1
0
mirror of https://github.com/chylex/IntelliJ-Rainbow-Brackets.git synced 2025-09-18 00:24:47 +02:00

Compare commits

10 Commits

18 changed files with 360 additions and 253 deletions

View File

@@ -4,6 +4,9 @@ This is a fork of the [🌈Rainbow Brackets](https://github.com/izhangzhihao/int
## Key Changes ## Key Changes
- Support for CLion and Rider - Support for C# (Rider)
- Support for C++ (Rider, CLion, CLion Nova)
- Support for Settings Sync - Support for Settings Sync
- Improved highlighting performance
- Increased default setting for maximum line count from 1K to 100K
- Fixed service initialization warnings reported by 2024.2+ - Fixed service initialization warnings reported by 2024.2+

View File

@@ -9,7 +9,7 @@ plugins {
} }
group = "com.chylex.intellij.coloredbrackets" group = "com.chylex.intellij.coloredbrackets"
version = "1.0.0" version = "1.2.0"
allprojects { allprojects {
apply(plugin = "org.jetbrains.kotlin.jvm") apply(plugin = "org.jetbrains.kotlin.jvm")

View File

@@ -3,7 +3,8 @@ intellij {
plugins.set(listOf( plugins.set(listOf(
// Built-in // Built-in
"cidr-base-plugin" "cidr-base-plugin",
//"org.jetbrains.plugins.clion.radler" // Only in 2024.1 or newer. Worked around by only including the .xml file, and taking the implementation from Rider.
)) ))
} }

View File

@@ -0,0 +1,10 @@
<idea-plugin>
<extensions defaultExtensionNs="com.chylex.coloredbrackets">
<bracePairProvider language="C++"
implementationClass="com.chylex.intellij.coloredbrackets.provider.CppBracePairProvider" />
</extensions>
<extensions defaultExtensionNs="com.intellij">
<highlightVisitor implementation="com.chylex.intellij.coloredbrackets.visitor.CppRainbowVisitor" />
</extensions>
</idea-plugin>

View File

@@ -0,0 +1,13 @@
package com.chylex.intellij.coloredbrackets.provider
import com.intellij.lang.BracePair
import com.jetbrains.rider.cpp.fileType.lexer.CppTokenTypes
class CppBracePairProvider : BracePairProvider {
override fun pairs(): List<BracePair> = listOf(
BracePair(CppTokenTypes.LPAR, CppTokenTypes.RPAR, false),
BracePair(CppTokenTypes.LBRACE, CppTokenTypes.RBRACE, false),
BracePair(CppTokenTypes.LBRACKET, CppTokenTypes.RBRACKET, false),
BracePair(CppTokenTypes.LT, CppTokenTypes.GT, false),
)
}

View File

@@ -2,130 +2,25 @@ package com.chylex.intellij.coloredbrackets.visitor
import com.chylex.intellij.coloredbrackets.settings.RainbowSettings import com.chylex.intellij.coloredbrackets.settings.RainbowSettings
import com.intellij.codeInsight.daemon.impl.HighlightVisitor import com.intellij.codeInsight.daemon.impl.HighlightVisitor
import com.intellij.lang.BracePair
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile import com.intellij.psi.PsiFile
import com.intellij.psi.impl.source.tree.LeafPsiElement
import com.intellij.psi.tree.IElementType import com.intellij.psi.tree.IElementType
import com.jetbrains.rider.languages.fileTypes.csharp.kotoparser.lexer.CSharpTokenType import com.jetbrains.rider.languages.fileTypes.csharp.kotoparser.lexer.CSharpTokenType
import com.jetbrains.rider.languages.fileTypes.csharp.psi.CSharpDummyNode import com.jetbrains.rider.languages.fileTypes.csharp.psi.CSharpDummyNode
class CSharpRainbowVisitor : RainbowHighlightVisitor() { class CSharpRainbowVisitor : ReSharperRainbowVisitor("C#") {
override fun suitableForFile(file: PsiFile) override fun suitableForFile(file: PsiFile): Boolean {
: Boolean = super.suitableForFile(file) && return super.suitableForFile(file) && RainbowSettings.instance.isEnableRainbowAngleBrackets
RainbowSettings.instance.isEnableRainbowAngleBrackets && }
(file.language.id == "C#" ||
file.viewProvider.allFiles.any { it.language.id == "C#" } override fun isAllowedElementType(type: IElementType): Boolean {
) return type == CSharpTokenType.LPARENTH || type == CSharpTokenType.RPARENTH
}
override fun PsiElement.isDummyNode(): Boolean {
return this is CSharpDummyNode
}
override fun clone(): HighlightVisitor = CSharpRainbowVisitor() override fun clone(): HighlightVisitor = CSharpRainbowVisitor()
override fun visit(element: PsiElement) {
val type = (element as? LeafPsiElement)?.elementType ?: return
val pair = map[type]
if (pair != null) {
val level = element.getBracketLevel(pair, type)
if (RainbowSettings.instance.isDoNOTRainbowifyTheFirstLevel) {
if (level >= 1) {
rainbowPairs(element, pair, level)
}
}
else {
if (level >= 0) {
rainbowPairs(element, pair, level)
}
}
}
}
private fun rainbowPairs(element: LeafPsiElement, pair: BracePair, level: Int) {
val startElement = element.takeIf { it.elementType == pair.leftBraceType }
val endElement = element.takeIf { it.elementType == pair.rightBraceType }
element.setHighlightInfo(element.parent, level, startElement, endElement)
}
companion object {
val map = mapOf(
CSharpTokenType.LPARENTH to BracePair(CSharpTokenType.LPARENTH, CSharpTokenType.RPARENTH, true),
CSharpTokenType.RPARENTH to BracePair(CSharpTokenType.LPARENTH, CSharpTokenType.RPARENTH, true),
//LT to BracePair(LT, GT, true),
//GT to BracePair(LT, GT, true),
)
private fun LeafPsiElement.getBracketLevel(pair: BracePair, type: IElementType): Int = iterateBracketParents(this, pair, -1, type)
private tailrec fun iterateBracketParents(element: PsiElement?, pair: BracePair, count: Int, type: IElementType): Int {
if (element == null || element is PsiFile) {
return count
}
var nextCount = count
if (element is LeafPsiElement && type == pair.leftBraceType && element.elementType == pair.rightBraceType) {
nextCount--
}
if (element is LeafPsiElement && type == pair.rightBraceType && element.elementType == pair.leftBraceType) {
nextCount--
}
if (element is LeafPsiElement && element.elementType == type) {
nextCount++
}
return if (type == pair.leftBraceType) {
val prev = element.prevSibling
if (prev == null) {
iterateBracketParents(element.parent.prevSibling.iterForPreDummyNode()?.lastChild, pair, nextCount, type)
}
else {
iterateBracketParents(prev, pair, nextCount, type)
}
}
else {
val next = element.nextSibling
if (next == null) {
iterateBracketParents(element.parent.nextSibling.iterForNextDummyNode()?.firstChild, pair, nextCount, type)
}
else {
iterateBracketParents(next, pair, nextCount, type)
}
}
}
private tailrec fun PsiElement?.iterForNextDummyNode(): PsiElement? {
return if (this == null) {
null
}
else if (this is CSharpDummyNode) {
this
}
else {
if (this.nextSibling == null) {
null
}
else {
this.nextSibling.iterForNextDummyNode()
}
}
}
private tailrec fun PsiElement?.iterForPreDummyNode(): PsiElement? {
return if (this == null) {
null
}
else if (this is CSharpDummyNode) {
this
}
else {
if (this.prevSibling == null) {
null
}
else {
this.prevSibling.iterForPreDummyNode()
}
}
}
}
} }

View File

@@ -0,0 +1,20 @@
package com.chylex.intellij.coloredbrackets.visitor
import com.intellij.codeInsight.daemon.impl.HighlightVisitor
import com.intellij.psi.PsiElement
import com.intellij.psi.tree.IElementType
import com.jetbrains.rider.cpp.fileType.lexer.CppTokenTypes
import com.jetbrains.rider.cpp.fileType.psi.CppDummyNode
class CppRainbowVisitor : ReSharperRainbowVisitor("C++") {
override fun clone(): HighlightVisitor = CppRainbowVisitor()
override fun isAllowedElementType(type: IElementType): Boolean {
return type == CppTokenTypes.LPAR || type == CppTokenTypes.RPAR
}
override fun PsiElement.isDummyNode(): Boolean {
return this is CppDummyNode
}
}

View File

@@ -0,0 +1,120 @@
package com.chylex.intellij.coloredbrackets.visitor
import com.chylex.intellij.coloredbrackets.bracePairs
import com.chylex.intellij.coloredbrackets.settings.RainbowSettings
import com.intellij.lang.BracePair
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.impl.source.tree.LeafPsiElement
import com.intellij.psi.tree.IElementType
import com.intellij.psi.util.elementType
abstract class ReSharperRainbowVisitor(private val languageId: String) : RainbowHighlightVisitor() {
override fun suitableForFile(file: PsiFile): Boolean {
return super.suitableForFile(file) && (file.language.id == languageId || file.viewProvider.allFiles.any { it.language.id == languageId })
}
protected abstract fun isAllowedElementType(type: IElementType): Boolean
final override fun visit(element: PsiElement) {
val type = (element as? LeafPsiElement)?.elementType?.takeIf(::isAllowedElementType) ?: return
val pair = type.language.bracePairs?.getValue(type.toString())?.singleOrNull() ?: return
val settings = RainbowSettings.instance
if (settings.isDoNOTRainbowifyBracketsWithoutContent) {
if (pair.leftBraceType == type && element.nextSibling?.elementType == pair.rightBraceType) {
return
}
if (pair.rightBraceType == type && element.prevSibling?.elementType == pair.leftBraceType) {
return
}
}
val level = element.getBracketLevel(pair, type)
if (settings.isDoNOTRainbowifyTheFirstLevel) {
if (level >= 1) {
rainbowPairs(element, pair, level)
}
}
else {
if (level >= 0) {
rainbowPairs(element, pair, level)
}
}
}
private fun rainbowPairs(element: LeafPsiElement, pair: BracePair, level: Int) {
val startElement = element.takeIf { it.elementType == pair.leftBraceType }
val endElement = element.takeIf { it.elementType == pair.rightBraceType }
element.setHighlightInfo(element.parent, level, startElement, endElement)
}
private fun LeafPsiElement.getBracketLevel(pair: BracePair, type: IElementType): Int {
return iterateBracketParents(this, pair, -1, type)
}
private tailrec fun iterateBracketParents(element: PsiElement?, pair: BracePair, count: Int, type: IElementType): Int {
if (element == null || element is PsiFile) {
return count
}
var nextCount = count
if (element is LeafPsiElement) {
if (type == pair.leftBraceType && element.elementType == pair.rightBraceType) {
nextCount--
}
if (type == pair.rightBraceType && element.elementType == pair.leftBraceType) {
nextCount--
}
if (type == element.elementType) {
nextCount++
}
}
return if (type == pair.leftBraceType) {
val prev = element.prevSibling
if (prev == null) {
iterateBracketParents(element.parent.prevSibling.iterForPreDummyNode()?.lastChild, pair, nextCount, type)
}
else {
iterateBracketParents(prev, pair, nextCount, type)
}
}
else {
val next = element.nextSibling
if (next == null) {
iterateBracketParents(element.parent.nextSibling.iterForNextDummyNode()?.firstChild, pair, nextCount, type)
}
else {
iterateBracketParents(next, pair, nextCount, type)
}
}
}
protected abstract fun PsiElement.isDummyNode(): Boolean
private tailrec fun PsiElement?.iterForNextDummyNode(): PsiElement? {
return when {
this == null -> null
this.isDummyNode() -> this
this.nextSibling == null -> null
else -> this.nextSibling.iterForNextDummyNode()
}
}
private tailrec fun PsiElement?.iterForPreDummyNode(): PsiElement? {
return when {
this == null -> null
this.isDummyNode() -> this
this.prevSibling == null -> null
else -> this.prevSibling.iterForPreDummyNode()
}
}
}

View File

@@ -0,0 +1,10 @@
<idea-plugin>
<extensions defaultExtensionNs="com.chylex.coloredbrackets">
<bracePairProvider language="C++"
implementationClass="com.chylex.intellij.coloredbrackets.provider.CppBracePairProvider" />
</extensions>
<extensions defaultExtensionNs="com.intellij">
<highlightVisitor implementation="com.chylex.intellij.coloredbrackets.visitor.CppRainbowVisitor" />
</extensions>
</idea-plugin>

View File

@@ -69,12 +69,12 @@ object BracePairs {
} }
} }
language.displayName to braceMap language.id to braceMap
} }
.toMap() .toMap()
} }
fun getBracePairs(language: Language): MutableMap<String, MutableList<BracePair>>? = bracePairs.value[language.displayName] fun getBracePairs(language: Language): MutableMap<String, MutableList<BracePair>>? = bracePairs.value[language.id]
private fun getBraceTypeSetOf(language: Language): Set<IElementType> = getBracePairs(language)?.values?.flatten()?.map { listOf(it.leftBraceType, it.rightBraceType) }?.flatten()?.toSet() ?: emptySet() private fun getBraceTypeSetOf(language: Language): Set<IElementType> = getBracePairs(language)?.values?.flatten()?.map { listOf(it.leftBraceType, it.rightBraceType) }?.flatten()?.toSet() ?: emptySet()

View File

@@ -9,21 +9,27 @@ import com.intellij.icons.AllIcons
import com.intellij.ide.actions.ShowSettingsUtilImpl import com.intellij.ide.actions.ShowSettingsUtilImpl
import com.intellij.openapi.fileEditor.FileEditor import com.intellij.openapi.fileEditor.FileEditor
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Key
import com.intellij.openapi.util.Ref import com.intellij.openapi.util.Ref
import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.VirtualFile
import com.intellij.ui.EditorNotificationPanel import com.intellij.ui.EditorNotificationPanel
import com.intellij.ui.EditorNotificationProvider
import com.intellij.ui.EditorNotifications import com.intellij.ui.EditorNotifications
import com.intellij.ui.HyperlinkLabel import com.intellij.ui.HyperlinkLabel
import java.util.function.Function
import javax.swing.JComponent
class RainbowifyBanner : EditorNotifications.Provider<EditorNotificationPanel>() { class RainbowifyBanner : EditorNotificationProvider {
override fun getKey(): Key<EditorNotificationPanel> = KEY override fun collectNotificationData(project: Project, file: VirtualFile): Function<in FileEditor, out JComponent?> {
return Function { createNotificationPanel(project, file) }
}
override fun createNotificationPanel(file: VirtualFile, fileEditor: FileEditor, project: Project): EditorNotificationPanel? { private fun createNotificationPanel(project: Project, file: VirtualFile): EditorNotificationPanel? {
val settings = RainbowSettings.instance val settings = RainbowSettings.instance
if (!settings.isRainbowEnabled) { if (!settings.isRainbowEnabled) {
if (settings.suppressDisabledCheck) return null if (settings.suppressDisabledCheck) {
return null
}
return EditorNotificationPanel().apply { return EditorNotificationPanel().apply {
text("Colored Brackets is now disabled") text("Colored Brackets is now disabled")
icon(AllIcons.General.GearPlain) icon(AllIcons.General.GearPlain)
@@ -41,7 +47,9 @@ class RainbowifyBanner : EditorNotifications.Provider<EditorNotificationPanel>()
val psiFile = file.toPsiFile(project) val psiFile = file.toPsiFile(project)
if (psiFile != null && !checkForBigFile(psiFile)) { if (psiFile != null && !checkForBigFile(psiFile)) {
if (settings.suppressBigFileCheck) return null if (settings.suppressBigFileCheck) {
return null
}
return EditorNotificationPanel().apply { return EditorNotificationPanel().apply {
text("Rainbowify is disabled for files > " + settings.bigFilesLinesThreshold + " lines") text("Rainbowify is disabled for files > " + settings.bigFilesLinesThreshold + " lines")
icon(AllIcons.General.InspectionsEye) icon(AllIcons.General.InspectionsEye)
@@ -61,7 +69,9 @@ class RainbowifyBanner : EditorNotifications.Provider<EditorNotificationPanel>()
settings.languageBlacklist.contains(file.fileType.name) || settings.languageBlacklist.contains(file.fileType.name) ||
settings.languageBlacklist.contains(memoizedFileExtension(file.name)) settings.languageBlacklist.contains(memoizedFileExtension(file.name))
) { ) {
if (settings.suppressBlackListCheck) return null if (settings.suppressBlackListCheck) {
return null
}
return EditorNotificationPanel().apply { return EditorNotificationPanel().apply {
text("Rainbowify is disabled because the language/file extension is in the black list") text("Rainbowify is disabled because the language/file extension is in the black list")
icon(AllIcons.General.InspectionsEye) icon(AllIcons.General.InspectionsEye)
@@ -81,14 +91,10 @@ class RainbowifyBanner : EditorNotifications.Provider<EditorNotificationPanel>()
return null return null
} }
companion object { private fun EditorNotificationPanel.createComponentActionLabel(labelText: String, callback: (HyperlinkLabel) -> Unit) {
private val KEY = Key.create<EditorNotificationPanel>("RainbowifyBanner")
fun EditorNotificationPanel.createComponentActionLabel(labelText: String, callback: (HyperlinkLabel) -> Unit) {
val label: Ref<HyperlinkLabel> = Ref.create() val label: Ref<HyperlinkLabel> = Ref.create()
label.set(createActionLabel(labelText) { label.set(createActionLabel(labelText) {
callback(label.get()) callback(label.get())
}) })
} }
}
} }

View File

@@ -8,7 +8,6 @@ import com.intellij.ide.actions.ToggleZenModeAction
import com.intellij.lang.LanguageParserDefinitions import com.intellij.lang.LanguageParserDefinitions
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.IndentGuideDescriptor import com.intellij.openapi.editor.IndentGuideDescriptor
import com.intellij.openapi.editor.ex.EditorEx
import com.intellij.openapi.editor.ex.util.EditorUtil import com.intellij.openapi.editor.ex.util.EditorUtil
import com.intellij.openapi.editor.markup.HighlighterTargetArea import com.intellij.openapi.editor.markup.HighlighterTargetArea
import com.intellij.openapi.editor.markup.MarkupModel import com.intellij.openapi.editor.markup.MarkupModel
@@ -23,8 +22,8 @@ import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiFile import com.intellij.psi.PsiFile
import com.intellij.psi.tree.TokenSet import com.intellij.psi.tree.TokenSet
import com.intellij.util.DocumentUtil import com.intellij.util.DocumentUtil
import com.intellij.util.containers.IntStack
import com.intellij.util.text.CharArrayUtil import com.intellij.util.text.CharArrayUtil
import it.unimi.dsi.fastutil.ints.IntArrayList
import java.lang.StrictMath.abs import java.lang.StrictMath.abs
import java.lang.StrictMath.min import java.lang.StrictMath.min
import java.util.Collections import java.util.Collections
@@ -35,11 +34,9 @@ import java.util.Collections
* */ * */
class RainbowIndentsPass internal constructor( class RainbowIndentsPass internal constructor(
project: Project, project: Project,
editor: Editor, private val myEditor: Editor,
private val myFile: PsiFile, private val myFile: PsiFile,
) : TextEditorHighlightingPass(project, editor.document, false), DumbAware { ) : TextEditorHighlightingPass(project, myEditor.document, false), DumbAware {
private val myEditor: EditorEx = editor as EditorEx
@Volatile @Volatile
private var myRanges = emptyList<TextRange>() private var myRanges = emptyList<TextRange>()
@@ -151,8 +148,8 @@ class RainbowIndentsPass internal constructor(
calculator.calculate() calculator.calculate()
val lineIndents = calculator.lineIndents val lineIndents = calculator.lineIndents
val lines = IntStack() val lines = IntArrayList()
val indents = IntStack() val indents = IntArrayList()
lines.push(0) lines.push(0)
indents.push(0) indents.push(0)
@@ -161,10 +158,10 @@ class RainbowIndentsPass internal constructor(
ProgressManager.checkCanceled() ProgressManager.checkCanceled()
val curIndent = abs(lineIndents[line]) val curIndent = abs(lineIndents[line])
while (!indents.empty() && curIndent <= indents.peek()) { while (!indents.isEmpty && curIndent <= indents.peekInt(0)) {
ProgressManager.checkCanceled() ProgressManager.checkCanceled()
val level = indents.pop() val level = indents.popInt()
val startLine = lines.pop() val startLine = lines.popInt()
if (level > 0) { if (level > 0) {
for (i in startLine until line) { for (i in startLine until line) {
if (level != abs(lineIndents[i])) { if (level != abs(lineIndents[i])) {
@@ -184,10 +181,10 @@ class RainbowIndentsPass internal constructor(
} }
} }
while (!indents.empty()) { while (!indents.isEmpty) {
ProgressManager.checkCanceled() ProgressManager.checkCanceled()
val level = indents.pop() val level = indents.popInt()
val startLine = lines.pop() val startLine = lines.popInt()
if (level > 0) { if (level > 0) {
descriptors.add(createDescriptor(level, startLine, document.lineCount, lineIndents)) descriptors.add(createDescriptor(level, startLine, document.lineCount, lineIndents))
} }
@@ -207,38 +204,12 @@ class RainbowIndentsPass internal constructor(
return IndentGuideDescriptor(level, sLine, endLine) return IndentGuideDescriptor(level, sLine, endLine)
} }
/*
private fun findCodeConstructStart(startLine: Int): Int? {
val document = myEditor.document
val text = document.immutableCharSequence
val lineStartOffset = document.getLineStartOffset(startLine)
val firstNonWsOffset = CharArrayUtil.shiftForward(text, lineStartOffset, " \t")
val type = PsiUtilBase.getPsiFileAtOffset(myFile, firstNonWsOffset).fileType
val language = PsiUtilCore.getLanguageAtOffset(myFile, firstNonWsOffset)
val braceMatcher = BraceMatchingUtil.getBraceMatcher(type, language)
val iterator = myEditor.highlighter.createIterator(firstNonWsOffset)
return if (braceMatcher.isLBraceToken(iterator, text, type)) {
braceMatcher.getCodeConstructStart(myFile, firstNonWsOffset)
} else null
}
private fun findCodeConstructStartLine(startLine: Int): Int {
val codeConstructStart = findCodeConstructStart(startLine)
return if (codeConstructStart != null) myEditor.document.getLineNumber(codeConstructStart) else startLine
}
*/
private inner class IndentsCalculator { private inner class IndentsCalculator {
val myComments: MutableMap<String, TokenSet> = HashMap() val myComments: MutableMap<String, TokenSet> = HashMap()
val lineIndents = IntArray(document.lineCount) // negative value means the line is empty (or contains a comment) and indent val lineIndents = IntArray(document.lineCount) // negative value means the line is empty (or contains a comment) and indent
// (denoted by absolute value) was deduced from enclosing non-empty lines // (denoted by absolute value) was deduced from enclosing non-empty lines
val myChars: CharSequence val myChars = document.charsSequence
init {
myChars = document.charsSequence
}
/** /**
* Calculates line indents for the [target document][.myDocument]. * Calculates line indents for the [target document][.myDocument].

View File

@@ -1,19 +1,22 @@
package com.chylex.intellij.coloredbrackets.indents package com.chylex.intellij.coloredbrackets.indents
import com.intellij.codeHighlighting.Pass import com.intellij.codeHighlighting.Pass
import com.intellij.codeHighlighting.TextEditorHighlightingPass
import com.intellij.codeHighlighting.TextEditorHighlightingPassFactory import com.intellij.codeHighlighting.TextEditorHighlightingPassFactory
import com.intellij.codeHighlighting.TextEditorHighlightingPassFactoryRegistrar import com.intellij.codeHighlighting.TextEditorHighlightingPassFactoryRegistrar
import com.intellij.codeHighlighting.TextEditorHighlightingPassRegistrar import com.intellij.codeHighlighting.TextEditorHighlightingPassRegistrar
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.impl.ImaginaryEditor
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.psi.PsiFile import com.intellij.psi.PsiFile
class RainbowIndentsPassFactory : class RainbowIndentsPassFactory :
TextEditorHighlightingPassFactoryRegistrar, TextEditorHighlightingPassFactory { TextEditorHighlightingPassFactoryRegistrar, TextEditorHighlightingPassFactory {
override fun createHighlightingPass(file: PsiFile, editor: Editor): TextEditorHighlightingPass { override fun createHighlightingPass(file: PsiFile, editor: Editor): RainbowIndentsPass? {
return RainbowIndentsPass(file.project, editor, file) return when (editor) {
is ImaginaryEditor -> null
else -> RainbowIndentsPass(file.project, editor, file)
}
} }
override fun registerHighlightingPassFactory(registrar: TextEditorHighlightingPassRegistrar, project: Project) { override fun registerHighlightingPassFactory(registrar: TextEditorHighlightingPassRegistrar, project: Project) {

View File

@@ -1,8 +1,10 @@
package com.chylex.intellij.coloredbrackets.settings package com.chylex.intellij.coloredbrackets.settings
import com.chylex.intellij.coloredbrackets.settings.form.RainbowSettingsForm import com.chylex.intellij.coloredbrackets.settings.form.RainbowSettingsForm
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer
import com.intellij.openapi.options.ConfigurationException import com.intellij.openapi.options.ConfigurationException
import com.intellij.openapi.options.SearchableConfigurable import com.intellij.openapi.options.SearchableConfigurable
import com.intellij.openapi.project.ProjectManager
import org.jetbrains.annotations.Nls import org.jetbrains.annotations.Nls
import javax.swing.JComponent import javax.swing.JComponent
@@ -45,6 +47,10 @@ class RainbowConfigurable : SearchableConfigurable {
settings.doNOTRainbowifyBigFiles = settingsForm?.doNOTRainbowifyBigFiles() ?: true settings.doNOTRainbowifyBigFiles = settingsForm?.doNOTRainbowifyBigFiles() ?: true
settings.bigFilesLinesThreshold = settingsForm?.bigFilesLinesThreshold() ?: 1000 settings.bigFilesLinesThreshold = settingsForm?.bigFilesLinesThreshold() ?: 1000
settings.rainbowifyPythonKeywords = settingsForm?.rainbowifyPythonKeywords() ?: false settings.rainbowifyPythonKeywords = settingsForm?.rainbowifyPythonKeywords() ?: false
ProjectManager.getInstanceIfCreated()?.openProjects?.forEach {
DaemonCodeAnalyzer.getInstance(it).restart()
}
} }
override fun reset() { override fun reset() {

View File

@@ -38,7 +38,7 @@ class RainbowSettings : PersistentStateComponent<RainbowSettings> {
var rainbowifyTagNameInXML = false var rainbowifyTagNameInXML = false
var doNOTRainbowifyTemplateString = false var doNOTRainbowifyTemplateString = false
var doNOTRainbowifyBigFiles = true var doNOTRainbowifyBigFiles = true
var bigFilesLinesThreshold = 1000 var bigFilesLinesThreshold = 100_000
var languageBlacklist: Set<String> = setOf("hocon", "mxml") var languageBlacklist: Set<String> = setOf("hocon", "mxml")

View File

@@ -9,28 +9,31 @@ import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile import com.intellij.psi.PsiFile
import com.intellij.psi.impl.source.tree.LeafPsiElement import com.intellij.psi.impl.source.tree.LeafPsiElement
import com.intellij.psi.tree.IElementType import com.intellij.psi.tree.IElementType
import it.unimi.dsi.fastutil.objects.Object2ByteOpenHashMap
class DefaultRainbowVisitor : RainbowHighlightVisitor() { class DefaultRainbowVisitor : RainbowHighlightVisitor() {
override fun clone(): HighlightVisitor = DefaultRainbowVisitor() override fun clone(): HighlightVisitor = DefaultRainbowVisitor()
private var processor: Processor? = null
override fun onBeforeAnalyze(file: PsiFile, updateWholeFile: Boolean) {
processor = Processor(RainbowSettings.instance)
}
override fun visit(element: PsiElement) { override fun visit(element: PsiElement) {
val type = (element as? LeafPsiElement)?.elementType ?: return val type = (element as? LeafPsiElement)?.elementType ?: return
val settings = RainbowSettings.instance val processor = processor!!
val processor = Processor(settings)
val matching = processor.filterPairs(type, element) ?: return val matching = processor.filterPairs(type, element) ?: return
val pair = val pair = when (matching.size) {
if (matching.size == 1) { 1 -> matching[0]
matching[0] else -> matching.find { processor.isValidBracket(element, it) } ?: return
} }
else {
matching.find { processor.isValidBracket(element, it) }
} ?: return
val level = processor.getBracketLevel(element, pair) val level = processor.getBracketLevel(element, pair)
if (settings.isDoNOTRainbowifyTheFirstLevel) { if (processor.settings.isDoNOTRainbowifyTheFirstLevel) {
if (level >= 1) { if (level >= 1) {
rainbowPairs(element, pair, level) rainbowPairs(element, pair, level)
} }
@@ -42,13 +45,35 @@ class DefaultRainbowVisitor : RainbowHighlightVisitor() {
} }
} }
override fun onAfterAnalyze() {
processor = null
}
private fun rainbowPairs(element: LeafPsiElement, pair: BracePair, level: Int) { private fun rainbowPairs(element: LeafPsiElement, pair: BracePair, level: Int) {
val startElement = element.takeIf { it.elementType == pair.leftBraceType } val startElement = element.takeIf { it.elementType == pair.leftBraceType }
val endElement = element.takeIf { it.elementType == pair.rightBraceType } val endElement = element.takeIf { it.elementType == pair.rightBraceType }
element.setHighlightInfo(element.parent, level, startElement, endElement) element.setHighlightInfo(element.parent, level, startElement, endElement)
} }
private class Processor(private val settings: RainbowSettings) { private class Processor(val settings: RainbowSettings) {
private companion object {
private const val CACHE_MISS = (-1).toByte()
}
private data class HasBracketsCacheKey(val element: PsiElement, val pair: BracePair) {
private val hashCode = (31 * System.identityHashCode(element)) + System.identityHashCode(pair)
override fun equals(other: Any?): Boolean {
return other is HasBracketsCacheKey && element === other.element && pair === other.pair
}
override fun hashCode(): Int {
return hashCode
}
}
private val hasBracketsCache = Object2ByteOpenHashMap<HasBracketsCacheKey>().apply { defaultReturnValue(CACHE_MISS) }
fun getBracketLevel(element: LeafPsiElement, pair: BracePair): Int = iterateBracketParents(element.parent, pair, -1) fun getBracketLevel(element: LeafPsiElement, pair: BracePair): Int = iterateBracketParents(element.parent, pair, -1)
@@ -59,17 +84,19 @@ class DefaultRainbowVisitor : RainbowHighlightVisitor() {
var nextCount = count var nextCount = count
if (!settings.cycleCountOnAllBrackets) { if (!settings.cycleCountOnAllBrackets) {
if (element.haveBrackets( if (element.hasBrackets(
{ it.elementType() == pair.leftBraceType }, pair,
{ it.elementType() == pair.rightBraceType }) { it.elementType == pair.leftBraceType },
{ it.elementType == pair.rightBraceType })
) { ) {
nextCount++ nextCount++
} }
} }
else { else {
if (element.haveBrackets( if (element.hasBrackets(
{ element.language.braceTypeSet.contains(it.elementType()) }, pair,
{ element.language.braceTypeSet.contains(it.elementType()) }) { element.language.braceTypeSet.contains(it.elementType) },
{ element.language.braceTypeSet.contains(it.elementType) })
) { ) {
nextCount++ nextCount++
} }
@@ -78,18 +105,27 @@ class DefaultRainbowVisitor : RainbowHighlightVisitor() {
return iterateBracketParents(element.parent, pair, nextCount) return iterateBracketParents(element.parent, pair, nextCount)
} }
private inline fun PsiElement.haveBrackets( private inline fun PsiElement.hasBrackets(
checkLeft: (PsiElement) -> Boolean, pair: BracePair,
checkRight: (PsiElement) -> Boolean, checkLeft: (LeafPsiElement) -> Boolean,
checkRight: (LeafPsiElement) -> Boolean,
): Boolean { ): Boolean {
if (this is LeafPsiElement) { if (this is LeafPsiElement) {
return false return false
} }
val cacheKey = HasBracketsCacheKey(this, pair)
val cacheValue = hasBracketsCache.getByte(cacheKey)
if (cacheValue != CACHE_MISS) {
return cacheValue == 1.toByte()
}
val hasBrackets = run {
var findLeftBracket = false var findLeftBracket = false
var findRightBracket = false var findRightBracket = false
var left: PsiElement? = firstChild var left: PsiElement? = firstChild
var right: PsiElement? = lastChild var right: PsiElement? = lastChild
while (left != right && (!findLeftBracket || !findRightBracket)) { while (left != right && (!findLeftBracket || !findRightBracket)) {
val needBreak = left == null || left.nextSibling == right val needBreak = left == null || left.nextSibling == right
@@ -111,16 +147,17 @@ class DefaultRainbowVisitor : RainbowHighlightVisitor() {
} }
} }
//For https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/830 // For https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/830
if (settings.doNOTRainbowifyTemplateString) { if (settings.doNOTRainbowifyTemplateString && left?.prevSibling?.textMatches("$") == true) {
if (left?.prevSibling?.textMatches("$") == true) return false false
}
else {
findLeftBracket && findRightBracket
}
} }
return findLeftBracket && findRightBracket hasBracketsCache.put(cacheKey, if (hasBrackets) 1.toByte() else 0.toByte())
} return hasBrackets
private fun PsiElement.elementType(): IElementType? {
return (this as? LeafPsiElement)?.elementType
} }
fun isValidBracket(element: LeafPsiElement, pair: BracePair): Boolean { fun isValidBracket(element: LeafPsiElement, pair: BracePair): Boolean {
@@ -160,24 +197,21 @@ class DefaultRainbowVisitor : RainbowHighlightVisitor() {
val pairs = element.language.bracePairs ?: return null val pairs = element.language.bracePairs ?: return null
val filterBraceType = pairs[type.toString()] val filterBraceType = pairs[type.toString()]
return when { return when {
filterBraceType.isNullOrEmpty() -> { filterBraceType.isNullOrEmpty() -> null
null
}
// https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/198
element.javaClass.simpleName == "OCMacroForeignLeafElement" -> {
null
}
settings.isDoNOTRainbowifyBracketsWithoutContent -> { // https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/198
filterBraceType element.javaClass.simpleName == "OCMacroForeignLeafElement" -> null
settings.isDoNOTRainbowifyBracketsWithoutContent -> filterBraceType
.filterNot { it.leftBraceType == type && element.nextSibling?.elementType() == it.rightBraceType } .filterNot { it.leftBraceType == type && element.nextSibling?.elementType() == it.rightBraceType }
.filterNot { it.rightBraceType == type && element.prevSibling?.elementType() == it.leftBraceType } .filterNot { it.rightBraceType == type && element.prevSibling?.elementType() == it.leftBraceType }
else -> filterBraceType
}
} }
else -> { private fun PsiElement.elementType(): IElementType? {
filterBraceType return (this as? LeafPsiElement)?.elementType
}
}
} }
} }
} }

View File

@@ -35,7 +35,6 @@ abstract class RainbowHighlightVisitor : HighlightVisitor {
onBeforeAnalyze(file, updateWholeFile) onBeforeAnalyze(file, updateWholeFile)
try { try {
action.run() action.run()
} catch (e: Throwable) {
} finally { } finally {
onAfterAnalyze() onAfterAnalyze()
} }

View File

@@ -8,18 +8,33 @@
<br><br> <br><br>
<b>Key Changes</b> <b>Key Changes</b>
<ul> <ul>
<li>Support for CLion and Rider</li> <li>Support for C# (Rider)</li>
<li>Support for C++ (Rider, CLion, CLion Nova)</li>
<li>Support for Settings Sync</li> <li>Support for Settings Sync</li>
<li>Improved highlighting performance</li>
<li>Increased default setting for maximum line count from 1K to 100K</li>
<li>Fixed service initialization warnings reported by 2024.2+</li> <li>Fixed service initialization warnings reported by 2024.2+</li>
</ul> </ul>
]]></description> ]]></description>
<change-notes><![CDATA[ <change-notes><![CDATA[
<b>1.0.0</b> <b>Version 1.2.0</b>
<ul> <ul>
<li>Added support for CLion and Rider</li> <li>Fixed not re-highlighting open files after changing settings.</li>
<li>Added support for Settings Sync</li> <li>Fixed exception when opening certain diff editors.</li>
<li>Fixed service initialization warnings reported by IJ 2024.2+</li> </ul>
<b>Version 1.1.0</b>
<ul>
<li>Added support for C++ in Rider and CLion Nova.</li>
<li>Fixed broken option to not color parentheses without content in C#.</li>
<li>Improved highlighting performance.</li>
<li>Increased default setting for maximum line count from 1K to 100K.</li>
</ul>
<b>Version 1.0.0</b>
<ul>
<li>Restored support for CLion and Rider.</li>
<li>Added support for Settings Sync.</li>
<li>Fixed service initialization warnings reported by IJ 2024.2.</li>
</ul> </ul>
]]></change-notes> ]]></change-notes>
@@ -28,7 +43,8 @@
<depends optional="true" config-file="JSX.xml">JavaScript</depends> <depends optional="true" config-file="JSX.xml">JavaScript</depends>
<depends optional="true" config-file="dart-brackets.xml">Dart</depends> <depends optional="true" config-file="dart-brackets.xml">Dart</depends>
<depends optional="true" config-file="groovy-brackets.xml">org.intellij.groovy</depends> <depends optional="true" config-file="groovy-brackets.xml">org.intellij.groovy</depends>
<!--<depends optional="true" config-file="csharp-annotator.xml">com.intellij.modules.rider</depends>--> <depends optional="true" config-file="cpp-nova-brackets.xml">org.jetbrains.plugins.clion.radler</depends>
<depends optional="true" config-file="cpp-rider-brackets.xml">com.intellij.modules.rider</depends>
<depends optional="true" config-file="csharp-brackets.xml">com.intellij.modules.rider</depends> <depends optional="true" config-file="csharp-brackets.xml">com.intellij.modules.rider</depends>
<depends optional="true" config-file="intellij-haskell-annotator.xml">intellij.haskell</depends> <depends optional="true" config-file="intellij-haskell-annotator.xml">intellij.haskell</depends>
<depends optional="true" config-file="sql-brackets.xml">com.intellij.database</depends> <depends optional="true" config-file="sql-brackets.xml">com.intellij.database</depends>