1
0
mirror of https://github.com/chylex/IntelliJ-Rainbow-Brackets.git synced 2025-11-03 16:40:19 +01:00

Compare commits

2 Commits

Author SHA1 Message Date
11754dea88 test 2024-12-01 02:23:41 +01:00
3fc524f2d8 Release 1.0.0 2024-11-30 23:36:08 +01:00
42 changed files with 432 additions and 520 deletions

1
.gitignore vendored
View File

@@ -2,5 +2,4 @@
!/.idea/runConfigurations !/.idea/runConfigurations
/.gradle/ /.gradle/
/.intellijPlatform/
/build/ /build/

24
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CommitMessageInspectionProfile">
<profile version="1.0">
<inspection_tool class="GrazieCommit" enabled="true" level="TYPO" enabled_by_default="true"/>
</profile>
</component>
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git"/>
</component>
<component name="IssueNavigationConfiguration">
<option name="links">
<list>
<IssueNavigationLink>
<option name="issueRegexp" value="#(\d+)"/>
<option name="linkRegexp" value="https://github.com//izhangzhihao/intellij-rainbow-brackets/issues/$1"/>
</IssueNavigationLink>
</list>
</option>
</component>
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git"/>
</component>
</project>

View File

@@ -4,9 +4,5 @@ This is a fork of the [🌈Rainbow Brackets](https://github.com/izhangzhihao/int
## Key Changes ## Key Changes
- Support for C# (Rider) - Support for CLion and Rider
- Support for C++ (Rider, CLion, CLion Nova)
- 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

@@ -1,8 +0,0 @@
val ideaVersion: String by project
dependencies {
intellijPlatform {
@Suppress("DEPRECATION")
intellijIdeaUltimate(ideaVersion)
}
}

View File

@@ -1,103 +1,131 @@
@file:Suppress("ConvertLambdaToReference") @file:Suppress("ConvertLambdaToReference")
import org.jetbrains.intellij.platform.gradle.TestFrameworkType import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import kotlin.io.path.Path
plugins { plugins {
kotlin("jvm") kotlin("jvm")
id("org.jetbrains.intellij.platform") id("org.jetbrains.intellij")
} }
group = "com.chylex.intellij.coloredbrackets" group = "com.chylex.intellij.coloredbrackets"
version = "1.3.0" version = "1.0.0"
val ideaVersion = "2023.3"
allprojects { allprojects {
apply(plugin = "org.jetbrains.kotlin.jvm") apply(plugin = "org.jetbrains.kotlin.jvm")
apply(plugin = "org.jetbrains.intellij.platform") apply(plugin = "org.jetbrains.intellij")
ext {
set("ideaVersion", ideaVersion)
}
repositories { repositories {
mavenCentral() mavenCentral()
intellijPlatform {
defaultRepositories()
}
} }
intellijPlatform { intellij {
pluginConfiguration { version.set("2023.3")
ideaVersion { updateSinceUntilBuild.set(false)
sinceBuild.set("233")
untilBuild.set(provider { null })
}
}
} }
kotlin { kotlin {
jvmToolchain(17) jvmToolchain(17)
}
compilerOptions {
freeCompilerArgs = listOf( tasks.withType<KotlinCompile> {
"-X" + "jvm-default=all", kotlinOptions.freeCompilerArgs = listOf(
) "-Xjvm-default=all"
} )
} }
} }
subprojects { subprojects {
intellijPlatform { tasks.buildSearchableOptions {
buildSearchableOptions = false enabled = false
} }
} }
idea { idea {
module { module {
excludeDirs.add(file("build"))
excludeDirs.add(file("gradle")) excludeDirs.add(file("gradle"))
} }
} }
intellij {
type.set("IU")
plugins.set(
listOf(
// Built-in
"Groovy",
"JavaScript",
"com.intellij.css",
"com.intellij.database",
"com.intellij.java",
"org.intellij.plugins.markdown",
"org.jetbrains.kotlin",
"org.jetbrains.plugins.yaml",
// Downloaded
"Dart:233.11799.172", // https://plugins.jetbrains.com/plugin/6351-dart/versions/stable
"Pythonid:233.11799.300", // https://plugins.jetbrains.com/plugin/631-python/versions
"com.jetbrains.php:233.11799.300", // https://plugins.jetbrains.com/plugin/6610-php/versions
"com.jetbrains.sh:233.11799.165", // https://plugins.jetbrains.com/plugin/13122-shell-script/versions
"org.intellij.scala:2023.3.19", // https://plugins.jetbrains.com/plugin/1347-scala/versions
"org.jetbrains.plugins.go-template:233.11799.172", // https://plugins.jetbrains.com/plugin/10581-go-template/versions
"org.jetbrains.plugins.ruby:233.11799.300", // https://plugins.jetbrains.com/plugin/1293-ruby/versions
)
)
}
dependencies { dependencies {
project(":api")
intellijPlatform {
@Suppress("DEPRECATION")
intellijIdeaUltimate(ideaVersion)
bundledPlugin("JavaScript")
bundledPlugin("com.intellij.css")
bundledPlugin("com.intellij.database")
bundledPlugin("com.intellij.java")
bundledPlugin("org.intellij.groovy")
bundledPlugin("org.intellij.plugins.markdown")
bundledPlugin("org.jetbrains.kotlin")
bundledPlugin("org.jetbrains.plugins.yaml")
plugin("Dart", "233.11799.172") // https://plugins.jetbrains.com/plugin/6351-dart/versions/stable
plugin("PythonCore", "233.11799.300") // https://plugins.jetbrains.com/plugin/631-python/versions
plugin("com.jetbrains.php", "233.11799.300") // https://plugins.jetbrains.com/plugin/6610-php/versions
plugin("com.jetbrains.sh", "233.11799.165") // https://plugins.jetbrains.com/plugin/13122-shell-script/versions
plugin("org.intellij.scala", "2023.3.19") // https://plugins.jetbrains.com/plugin/1347-scala/versions
plugin("org.jetbrains.plugins.go-template", "233.11799.172") // https://plugins.jetbrains.com/plugin/10581-go-template/versions
plugin("org.jetbrains.plugins.ruby", "233.11799.300") // https://plugins.jetbrains.com/plugin/1293-ruby/versions
testFramework(TestFrameworkType.Plugin.Java)
pluginComposedModule(implementation(project(":api")))
pluginComposedModule(implementation(project(":clion")))
pluginComposedModule(implementation(project(":rider")))
}
testImplementation("junit:junit:4.13.2") testImplementation("junit:junit:4.13.2")
testImplementation("io.kotest:kotest-assertions-core:5.8.0") { testImplementation("io.kotest:kotest-assertions-core:5.8.0") {
exclude(group = "org.jetbrains.kotlin") exclude(group = "org.jetbrains.kotlin")
} }
} }
tasks.patchPluginXml {
sinceBuild.set("233")
}
tasks.test { tasks.test {
useJUnit() useJUnit()
} }
tasks.buildPlugin {
val projectName = rootProject.name
val instrumentedJarName = "instrumented-$projectName-$version"
for (ide in listOf("clion", "rider")) {
val task = project(":$ide").tasks.buildPlugin
dependsOn(task)
from(task.map { it.outputs.files.map(::zipTree) }) {
include("$ide/lib/instrumented-$ide.jar")
into("lib")
eachFile {
val newName = name.replace("instrumented-", "${instrumentedJarName}-")
val newPath = relativePath.segments.dropLast(3).plus(newName)
relativePath = RelativePath(true, *newPath.toTypedArray())
}
includeEmptyDirs = false
}
}
doLast {
val expectedPaths = listOf(
Path(projectName, "lib", "instrumented-$projectName-$version-clion.jar"),
Path(projectName, "lib", "instrumented-$projectName-$version-rider.jar"),
Path(projectName, "lib", "instrumented-$projectName-$version.jar"),
Path(projectName, "lib", "searchableOptions-$version.jar"),
)
val jarFiles = zipTree(outputs.files.singleFile)
for (expectedPath in expectedPaths) {
val found = jarFiles.find { it.toPath().endsWith(expectedPath) }
checkNotNull(found) { "Expected path not found: $expectedPath" }
}
}
}

View File

@@ -1,12 +1,12 @@
val ideaVersion: String by project intellij {
type.set("CL")
plugins.set(listOf(
// Built-in
"cidr-base-plugin"
))
}
dependencies { dependencies {
implementation(project(":api")) implementation(rootProject)
intellijPlatform {
clion(ideaVersion)
bundledPlugin("com.intellij.clion")
// bundledPlugin("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

@@ -1,10 +0,0 @@
<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

@@ -1,2 +1 @@
kotlin.stdlib.default.dependency=false kotlin.stdlib.default.dependency=false
org.gradle.jvmargs=-Xmx1G

View File

@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
networkTimeout=10000 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME

View File

@@ -1,11 +1,7 @@
val ideaVersion: String by project intellij {
type.set("RD")
}
dependencies { dependencies {
implementation(project(":api")) implementation(rootProject)
intellijPlatform {
rider(ideaVersion) {
useInstaller = false
}
}
} }

View File

@@ -1,13 +0,0 @@
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,25 +2,130 @@ 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 : ReSharperRainbowVisitor("C#") { class CSharpRainbowVisitor : RainbowHighlightVisitor() {
override fun suitableForFile(file: PsiFile): Boolean { override fun suitableForFile(file: PsiFile)
return super.suitableForFile(file) && RainbowSettings.instance.isEnableRainbowAngleBrackets : Boolean = super.suitableForFile(file) &&
} RainbowSettings.instance.isEnableRainbowAngleBrackets &&
(file.language.id == "C#" ||
override fun isAllowedElementType(type: IElementType): Boolean { file.viewProvider.allFiles.any { it.language.id == "C#" }
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

@@ -1,20 +0,0 @@
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

@@ -1,120 +0,0 @@
package com.chylex.intellij.coloredbrackets.visitor
import com.chylex.intellij.coloredbrackets.BracePairs.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

@@ -1,10 +0,0 @@
<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

@@ -3,10 +3,9 @@ rootProject.name = "ColoredBrackets"
pluginManagement { pluginManagement {
plugins { plugins {
kotlin("jvm") version "1.9.21" kotlin("jvm") version "1.9.21"
id("org.jetbrains.intellij.platform") version "2.9.0" id("org.jetbrains.intellij") version "1.17.4"
} }
} }
include("api")
include("clion") include("clion")
include("rider") include("rider")

View File

@@ -16,7 +16,7 @@ object BracePairs {
private val providers = LanguageExtension<BracePairProvider>("com.chylex.coloredbrackets.bracePairProvider") private val providers = LanguageExtension<BracePairProvider>("com.chylex.coloredbrackets.bracePairProvider")
val bracePairs = lazy { private val bracePairs = lazy {
Language.getRegisteredLanguages() Language.getRegisteredLanguages()
.map { language -> .map { language ->
if (language is CompositeLanguage) { if (language is CompositeLanguage) {
@@ -69,18 +69,20 @@ object BracePairs {
} }
} }
language.id to braceMap language.displayName to braceMap
} }
.toMap() .toMap()
} }
private fun getBraceTypeSetOf(language: Language): Set<IElementType> = language.bracePairs?.values?.flatten()?.map { listOf(it.leftBraceType, it.rightBraceType) }?.flatten()?.toSet() ?: emptySet() fun getBracePairs(language: Language): MutableMap<String, MutableList<BracePair>>? = bracePairs.value[language.displayName]
private fun getBraceTypeSetOf(language: Language): Set<IElementType> = getBracePairs(language)?.values?.flatten()?.map { listOf(it.leftBraceType, it.rightBraceType) }?.flatten()?.toSet() ?: emptySet()
val braceTypeSet: (Language) -> Set<IElementType> = { language: Language -> getBraceTypeSetOf(language) }.memoize() val braceTypeSet: (Language) -> Set<IElementType> = { language: Language -> getBraceTypeSetOf(language) }.memoize()
inline val Language.bracePairs: MutableMap<String, MutableList<BracePair>>?
get() = BracePairs.bracePairs.value[this.id]
inline val Language.braceTypeSet: Set<IElementType>
get() = BracePairs.braceTypeSet(this)
} }
inline val Language.bracePairs: MutableMap<String, MutableList<BracePair>>?
get() = BracePairs.getBracePairs(this)
inline val Language.braceTypeSet: Set<IElementType>
get() = BracePairs.braceTypeSet(this)

View File

@@ -1,7 +1,6 @@
package com.chylex.intellij.coloredbrackets package com.chylex.intellij.coloredbrackets
import com.chylex.intellij.coloredbrackets.settings.RainbowSettings import com.chylex.intellij.coloredbrackets.settings.RainbowSettings
import com.chylex.intellij.coloredbrackets.util.create
import com.chylex.intellij.coloredbrackets.util.memoize import com.chylex.intellij.coloredbrackets.util.memoize
import com.intellij.codeInsight.daemon.impl.HighlightInfo import com.intellij.codeInsight.daemon.impl.HighlightInfo
import com.intellij.codeInsight.daemon.impl.HighlightInfoType import com.intellij.codeInsight.daemon.impl.HighlightInfoType
@@ -138,7 +137,7 @@ object RainbowHighlighter {
} }
private fun genByOption(option: String, rainbowName: String, level: Int) = private fun genByOption(option: String, rainbowName: String, level: Int) =
create( com.chylex.intellij.coloredbrackets.util.create(
"$rainbowName-$level", "$rainbowName-$level",
TextAttributes(randomColor(option), null, null, null, 0) TextAttributes(randomColor(option), null, null, null, 0)
) )

View File

@@ -9,27 +9,21 @@ 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 : EditorNotificationProvider { class RainbowifyBanner : EditorNotifications.Provider<EditorNotificationPanel>() {
override fun collectNotificationData(project: Project, file: VirtualFile): Function<in FileEditor, out JComponent?> { override fun getKey(): Key<EditorNotificationPanel> = KEY
return Function { createNotificationPanel(project, file) }
}
private fun createNotificationPanel(project: Project, file: VirtualFile): EditorNotificationPanel? { override fun createNotificationPanel(file: VirtualFile, fileEditor: FileEditor, project: Project): EditorNotificationPanel? {
val settings = RainbowSettings.instance val settings = RainbowSettings.instance
if (!settings.isRainbowEnabled) { if (!settings.isRainbowEnabled) {
if (settings.suppressDisabledCheck) { if (settings.suppressDisabledCheck) return null
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)
@@ -47,9 +41,7 @@ class RainbowifyBanner : EditorNotificationProvider {
val psiFile = file.toPsiFile(project) val psiFile = file.toPsiFile(project)
if (psiFile != null && !checkForBigFile(psiFile)) { if (psiFile != null && !checkForBigFile(psiFile)) {
if (settings.suppressBigFileCheck) { if (settings.suppressBigFileCheck) return null
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)
@@ -69,9 +61,7 @@ class RainbowifyBanner : EditorNotificationProvider {
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) { if (settings.suppressBlackListCheck) return null
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)
@@ -91,10 +81,14 @@ class RainbowifyBanner : EditorNotificationProvider {
return null return null
} }
private fun EditorNotificationPanel.createComponentActionLabel(labelText: String, callback: (HyperlinkLabel) -> Unit) { companion object {
val label: Ref<HyperlinkLabel> = Ref.create() private val KEY = Key.create<EditorNotificationPanel>("RainbowifyBanner")
label.set(createActionLabel(labelText) {
callback(label.get()) fun EditorNotificationPanel.createComponentActionLabel(labelText: String, callback: (HyperlinkLabel) -> Unit) {
}) val label: Ref<HyperlinkLabel> = Ref.create()
label.set(createActionLabel(labelText) {
callback(label.get())
})
}
} }
} }

View File

@@ -2,7 +2,6 @@ package com.chylex.intellij.coloredbrackets
import com.chylex.intellij.coloredbrackets.color.Luminosity import com.chylex.intellij.coloredbrackets.color.Luminosity
import com.chylex.intellij.coloredbrackets.color.fromString import com.chylex.intellij.coloredbrackets.color.fromString
import com.chylex.intellij.coloredbrackets.color.randomColor
import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue import com.fasterxml.jackson.module.kotlin.readValue
@@ -12,7 +11,7 @@ val mapper: ObjectMapper by lazy { jacksonObjectMapper() }
fun randomColor(options: String): Color { fun randomColor(options: String): Color {
val ops: Map<String, String> = mapper.readValue(options) val ops: Map<String, String> = mapper.readValue(options)
return randomColor( return com.chylex.intellij.coloredbrackets.color.randomColor(
fromString(ops.getOrDefault("hue", "random")), fromString(ops.getOrDefault("hue", "random")),
Luminosity.valueOf(ops.getOrDefault("luminosity", "random")) Luminosity.valueOf(ops.getOrDefault("luminosity", "random"))
) )

View File

@@ -16,7 +16,7 @@ class KotlinLambdaExpressionArrowAnnotator : Annotator {
if ((element as? LeafPsiElement)?.elementType == KtTokens.ARROW) { if ((element as? LeafPsiElement)?.elementType == KtTokens.ARROW) {
RainbowInfo.RAINBOW_INFO_KEY[element.parent]?.color?.let { RainbowInfo.RAINBOW_INFO_KEY[element.parent]?.color?.let {
holder.newSilentAnnotation(HighlightSeverity.INFORMATION) holder.newSilentAnnotation(HighlightSeverity.INFORMATION)
.range(element as PsiElement) // Cast necessary due to overload conflict in Kotlin 2 compiler. .range(element)
.textAttributes( .textAttributes(
com.chylex.intellij.coloredbrackets.util.create( com.chylex.intellij.coloredbrackets.util.create(
"rainbow-kotlin-arrow", "rainbow-kotlin-arrow",

View File

@@ -7,7 +7,6 @@ import com.chylex.intellij.coloredbrackets.util.findNextSibling
import com.chylex.intellij.coloredbrackets.util.findPrevSibling import com.chylex.intellij.coloredbrackets.util.findPrevSibling
import com.chylex.intellij.coloredbrackets.util.lineNumber import com.chylex.intellij.coloredbrackets.util.lineNumber
import com.chylex.intellij.coloredbrackets.util.startOffset import com.chylex.intellij.coloredbrackets.util.startOffset
import com.intellij.openapi.application.ReadAction
import com.intellij.openapi.editor.Document import com.intellij.openapi.editor.Document
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.SoftWrap import com.intellij.openapi.editor.SoftWrap
@@ -166,28 +165,24 @@ class RainbowIndentGuideRenderer : CustomHighlighterRenderer {
val virtualFile = editor.virtualFile?.takeIf { it.isValid } ?: return null val virtualFile = editor.virtualFile?.takeIf { it.isValid } ?: return null
val document = editor.document val document = editor.document
val project = editor.project ?: return null val project = editor.project ?: return null
val psiFile = PsiManager.getInstance(project).findFile(virtualFile) ?: return null
return ReadAction.compute<RainbowInfo, Throwable> { var element = try {
val psiFile = PsiManager.getInstance(project).findFile(virtualFile) ?: return@compute null psiFile.findElementAt(highlighter.endOffset)?.parent ?: return null
var element = try { } catch (e: Throwable) {
psiFile.findElementAt(highlighter.endOffset)?.parent ?: return@compute null return null
} catch (_: Throwable) {
return@compute null
}
var rainbowInfo = RainbowInfo.RAINBOW_INFO_KEY[element]
if (rainbowInfo == null && psiFile is XmlFile && element !is XmlTag) {
element = PsiTreeUtil.findFirstParent(element, true, XML_TAG_PARENT_CONDITION) ?: return@compute null
rainbowInfo = RainbowInfo.RAINBOW_INFO_KEY[element] ?: return@compute null
}
if (!element.isValid || !checkBoundary(document, element, highlighter)) {
null
}
else {
rainbowInfo
}
} }
var rainbowInfo = RainbowInfo.RAINBOW_INFO_KEY[element]
if (rainbowInfo == null && psiFile is XmlFile && element !is XmlTag) {
element = PsiTreeUtil.findFirstParent(element, true, XML_TAG_PARENT_CONDITION) ?: return null
rainbowInfo = RainbowInfo.RAINBOW_INFO_KEY[element] ?: return null
}
if (!element.isValid || !checkBoundary(document, element, highlighter)) {
return null
}
return rainbowInfo
} }
/*** /***

View File

@@ -8,6 +8,7 @@ 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
@@ -22,8 +23,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
@@ -34,9 +35,11 @@ import java.util.Collections
* */ * */
class RainbowIndentsPass internal constructor( class RainbowIndentsPass internal constructor(
project: Project, project: Project,
private val myEditor: Editor, editor: Editor,
private val myFile: PsiFile, private val myFile: PsiFile,
) : TextEditorHighlightingPass(project, myEditor.document, false), DumbAware { ) : TextEditorHighlightingPass(project, editor.document, false), DumbAware {
private val myEditor: EditorEx = editor as EditorEx
@Volatile @Volatile
private var myRanges = emptyList<TextRange>() private var myRanges = emptyList<TextRange>()
@@ -148,8 +151,8 @@ class RainbowIndentsPass internal constructor(
calculator.calculate() calculator.calculate()
val lineIndents = calculator.lineIndents val lineIndents = calculator.lineIndents
val lines = IntArrayList() val lines = IntStack()
val indents = IntArrayList() val indents = IntStack()
lines.push(0) lines.push(0)
indents.push(0) indents.push(0)
@@ -158,10 +161,10 @@ class RainbowIndentsPass internal constructor(
ProgressManager.checkCanceled() ProgressManager.checkCanceled()
val curIndent = abs(lineIndents[line]) val curIndent = abs(lineIndents[line])
while (!indents.isEmpty && curIndent <= indents.peekInt(0)) { while (!indents.empty() && curIndent <= indents.peek()) {
ProgressManager.checkCanceled() ProgressManager.checkCanceled()
val level = indents.popInt() val level = indents.pop()
val startLine = lines.popInt() val startLine = lines.pop()
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])) {
@@ -181,10 +184,10 @@ class RainbowIndentsPass internal constructor(
} }
} }
while (!indents.isEmpty) { while (!indents.empty()) {
ProgressManager.checkCanceled() ProgressManager.checkCanceled()
val level = indents.popInt() val level = indents.pop()
val startLine = lines.popInt() val startLine = lines.pop()
if (level > 0) { if (level > 0) {
descriptors.add(createDescriptor(level, startLine, document.lineCount, lineIndents)) descriptors.add(createDescriptor(level, startLine, document.lineCount, lineIndents))
} }
@@ -204,12 +207,38 @@ 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 = document.charsSequence val myChars: CharSequence
init {
myChars = document.charsSequence
}
/** /**
* Calculates line indents for the [target document][.myDocument]. * Calculates line indents for the [target document][.myDocument].

View File

@@ -1,22 +1,19 @@
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): RainbowIndentsPass? { override fun createHighlightingPass(file: PsiFile, editor: Editor): TextEditorHighlightingPass {
return when (editor) { return RainbowIndentsPass(file.project, editor, file)
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,10 +1,8 @@
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
@@ -47,10 +45,6 @@ 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

@@ -2,13 +2,12 @@ package com.chylex.intellij.coloredbrackets.settings
import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.components.PersistentStateComponent import com.intellij.openapi.components.PersistentStateComponent
import com.intellij.openapi.components.SettingsCategory
import com.intellij.openapi.components.State import com.intellij.openapi.components.State
import com.intellij.openapi.components.Storage import com.intellij.openapi.components.Storage
import com.intellij.util.xmlb.XmlSerializerUtil import com.intellij.util.xmlb.XmlSerializerUtil.copyBean
import org.jetbrains.annotations.Nullable import org.jetbrains.annotations.Nullable
@State(name = "ColoredBracketsSettings", storages = [Storage("colored_brackets.xml")], category = SettingsCategory.UI) @State(name = "ColoredBracketsSettings", storages = [Storage("colored_brackets.xml")])
class RainbowSettings : PersistentStateComponent<RainbowSettings> { class RainbowSettings : PersistentStateComponent<RainbowSettings> {
/** /**
* default value * default value
@@ -38,7 +37,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 = 100_000 var bigFilesLinesThreshold = 1000
var languageBlacklist: Set<String> = setOf("hocon", "mxml") var languageBlacklist: Set<String> = setOf("hocon", "mxml")
@@ -51,7 +50,7 @@ class RainbowSettings : PersistentStateComponent<RainbowSettings> {
override fun getState() = this override fun getState() = this
override fun loadState(state: RainbowSettings) { override fun loadState(state: RainbowSettings) {
XmlSerializerUtil.copyBean(state, this) copyBean(state, this)
} }
companion object { companion object {

View File

@@ -23,7 +23,7 @@ private interface MemoizedCall<in F, out R> {
private class MemoizedHandler<F, in K : MemoizedCall<F, R>, out R>(val f: F) { private class MemoizedHandler<F, in K : MemoizedCall<F, R>, out R>(val f: F) {
private val m = Platform.newConcurrentMap<K, R>() private val m = Platform.newConcurrentMap<K, R>()
operator fun invoke(k: K): R = m[k] ?: m.putSafely(k, k(f)) operator fun invoke(k: K): R = m[k] ?: run { m.putSafely(k, k(f)) }
} }
private data class MemoizeKey1<out A, R>(val a: A) : MemoizedCall<(A) -> R, R> { private data class MemoizeKey1<out A, R>(val a: A) : MemoizedCall<(A) -> R, R> {

View File

@@ -1,7 +1,7 @@
package com.chylex.intellij.coloredbrackets.visitor package com.chylex.intellij.coloredbrackets.visitor
import com.chylex.intellij.coloredbrackets.BracePairs.bracePairs import com.chylex.intellij.coloredbrackets.bracePairs
import com.chylex.intellij.coloredbrackets.BracePairs.braceTypeSet import com.chylex.intellij.coloredbrackets.braceTypeSet
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.lang.BracePair
@@ -9,31 +9,28 @@ 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 processor = processor!! val settings = RainbowSettings.instance
val processor = Processor(settings)
val matching = processor.filterPairs(type, element) ?: return val matching = processor.filterPairs(type, element) ?: return
val pair = when (matching.size) { val pair =
1 -> matching[0] if (matching.size == 1) {
else -> matching.find { processor.isValidBracket(element, it) } ?: return matching[0]
} }
else {
matching.find { processor.isValidBracket(element, it) }
} ?: return
val level = processor.getBracketLevel(element, pair) val level = processor.getBracketLevel(element, pair)
if (processor.settings.isDoNOTRainbowifyTheFirstLevel) { if (settings.isDoNOTRainbowifyTheFirstLevel) {
if (level >= 1) { if (level >= 1) {
rainbowPairs(element, pair, level) rainbowPairs(element, pair, level)
} }
@@ -45,35 +42,13 @@ 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(val settings: RainbowSettings) { private class Processor(private 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)
@@ -84,19 +59,17 @@ class DefaultRainbowVisitor : RainbowHighlightVisitor() {
var nextCount = count var nextCount = count
if (!settings.cycleCountOnAllBrackets) { if (!settings.cycleCountOnAllBrackets) {
if (element.hasBrackets( if (element.haveBrackets(
pair, { it.elementType() == pair.leftBraceType },
{ it.elementType == pair.leftBraceType }, { it.elementType() == pair.rightBraceType })
{ it.elementType == pair.rightBraceType })
) { ) {
nextCount++ nextCount++
} }
} }
else { else {
if (element.hasBrackets( if (element.haveBrackets(
pair, { element.language.braceTypeSet.contains(it.elementType()) },
{ element.language.braceTypeSet.contains(it.elementType) }, { element.language.braceTypeSet.contains(it.elementType()) })
{ element.language.braceTypeSet.contains(it.elementType) })
) { ) {
nextCount++ nextCount++
} }
@@ -105,59 +78,49 @@ class DefaultRainbowVisitor : RainbowHighlightVisitor() {
return iterateBracketParents(element.parent, pair, nextCount) return iterateBracketParents(element.parent, pair, nextCount)
} }
private inline fun PsiElement.hasBrackets( private inline fun PsiElement.haveBrackets(
pair: BracePair, checkLeft: (PsiElement) -> Boolean,
checkLeft: (LeafPsiElement) -> Boolean, checkRight: (PsiElement) -> Boolean,
checkRight: (LeafPsiElement) -> Boolean,
): Boolean { ): Boolean {
if (this is LeafPsiElement) { if (this is LeafPsiElement) {
return false return false
} }
val cacheKey = HasBracketsCacheKey(this, pair) var findLeftBracket = false
val cacheValue = hasBracketsCache.getByte(cacheKey) var findRightBracket = false
if (cacheValue != CACHE_MISS) { var left: PsiElement? = firstChild
return cacheValue == 1.toByte() var right: PsiElement? = lastChild
} while (left != right && (!findLeftBracket || !findRightBracket)) {
val needBreak = left == null || left.nextSibling == right
val hasBrackets = run {
var findLeftBracket = false
var findRightBracket = false
var left: PsiElement? = firstChild
var right: PsiElement? = lastChild
while (left != right && (!findLeftBracket || !findRightBracket)) { if (left is LeafPsiElement && checkLeft(left)) {
val needBreak = left == null || left.nextSibling == right findLeftBracket = true
if (left is LeafPsiElement && checkLeft(left)) {
findLeftBracket = true
}
else {
left = left?.nextSibling
}
if (right is LeafPsiElement && checkRight(right)) {
findRightBracket = true
}
else {
right = right?.prevSibling
}
if (needBreak) {
break
}
}
// For https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/830
if (settings.doNOTRainbowifyTemplateString && left?.prevSibling?.textMatches("$") == true) {
false
} }
else { else {
findLeftBracket && findRightBracket left = left?.nextSibling
}
if (right is LeafPsiElement && checkRight(right)) {
findRightBracket = true
}
else {
right = right?.prevSibling
}
if (needBreak) {
break
} }
} }
hasBracketsCache.put(cacheKey, if (hasBrackets) 1.toByte() else 0.toByte()) //For https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/830
return hasBrackets if (settings.doNOTRainbowifyTemplateString) {
if (left?.prevSibling?.textMatches("$") == true) return false
}
return findLeftBracket && findRightBracket
}
private fun PsiElement.elementType(): IElementType? {
return (this as? LeafPsiElement)?.elementType
} }
fun isValidBracket(element: LeafPsiElement, pair: BracePair): Boolean { fun isValidBracket(element: LeafPsiElement, pair: BracePair): Boolean {
@@ -197,21 +160,24 @@ 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() -> null filterBraceType.isNullOrEmpty() -> {
null
}
// https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/198 // https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/198
element.javaClass.simpleName == "OCMacroForeignLeafElement" -> null element.javaClass.simpleName == "OCMacroForeignLeafElement" -> {
null
}
settings.isDoNOTRainbowifyBracketsWithoutContent -> filterBraceType settings.isDoNOTRainbowifyBracketsWithoutContent -> {
.filterNot { it.leftBraceType == type && element.nextSibling?.elementType() == it.rightBraceType } filterBraceType
.filterNot { it.rightBraceType == type && element.prevSibling?.elementType() == it.leftBraceType } .filterNot { it.leftBraceType == type && element.nextSibling?.elementType() == it.rightBraceType }
.filterNot { it.rightBraceType == type && element.prevSibling?.elementType() == it.leftBraceType }
}
else -> filterBraceType else -> {
filterBraceType
}
} }
} }
private fun PsiElement.elementType(): IElementType? {
return (this as? LeafPsiElement)?.elementType
}
} }
} }

View File

@@ -35,6 +35,7 @@ abstract class RainbowHighlightVisitor : HighlightVisitor {
onBeforeAnalyze(file, updateWholeFile) onBeforeAnalyze(file, updateWholeFile)
try { try {
action.run() action.run()
} catch (e: Throwable) {
} finally { } finally {
onAfterAnalyze() onAfterAnalyze()
} }
@@ -96,24 +97,24 @@ abstract class RainbowHighlightVisitor : HighlightVisitor {
private fun fileIsNotHaskellOrIntelliJHaskellPluginNotEnabled(fileType: String) = private fun fileIsNotHaskellOrIntelliJHaskellPluginNotEnabled(fileType: String) =
fileType != "Haskell" || !isIntelliJHaskellEnabled fileType != "Haskell" || !isIntelliJHaskellEnabled
}
private fun PsiElement.getLineCount(): Int { }
try {
val doc = containingFile?.let { PsiDocumentManager.getInstance(project).getDocument(it) } fun PsiElement.getLineCount(): Int {
if (doc != null) { try {
val spaceRange = textRange ?: TextRange.EMPTY_RANGE val doc = containingFile?.let { PsiDocumentManager.getInstance(project).getDocument(it) }
if (doc != null) {
if (spaceRange.endOffset <= doc.textLength && spaceRange.startOffset < spaceRange.endOffset) { val spaceRange = textRange ?: TextRange.EMPTY_RANGE
val startLine = doc.getLineNumber(spaceRange.startOffset)
val endLine = doc.getLineNumber(spaceRange.endOffset - 1) if (spaceRange.endOffset <= doc.textLength && spaceRange.startOffset < spaceRange.endOffset) {
val startLine = doc.getLineNumber(spaceRange.startOffset)
return endLine - startLine + 1 val endLine = doc.getLineNumber(spaceRange.endOffset - 1)
}
} return endLine - startLine + 1
return StringUtil.getLineBreakCount(text ?: "") + 1 }
} catch (e: Throwable) { }
return 0 return StringUtil.getLineBreakCount(text ?: "") + 1
} } catch (e: Throwable) {
} return 0
} }
} }

View File

@@ -4,41 +4,14 @@
<vendor url="https://chylex.com">chylex</vendor> <vendor url="https://chylex.com">chylex</vendor>
<description><![CDATA[ <description><![CDATA[
Fork of the <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets">Rainbow Brackets</a> plugin by <a href="https://github.com/izhangzhihao">izhangzhihao</a>, based on version 6.26. Fork of the <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets">Rainbow Brackets</a> plugin by <a href="https://github.com/izhangzhihao">izhangzhihao</a>, based on version 6.26.
<br><br>
<b>Key Changes</b>
<ul>
<li>Support for C# (Rider)</li>
<li>Support for C++ (Rider, CLion, CLion Nova)</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>
</ul>
]]></description> ]]></description>
<change-notes><![CDATA[ <change-notes><![CDATA[
<b>Version 1.3.0</b> <p>1.0.0</p>
<ul> <ul>
<li>Fixed assertion error caused by missing read lock in indent guide renderer.</li> <li>Added support for CLion and Rider</li>
</ul> <li>Fixed service initialization warnings reported by 2024.2+</li>
<b>Version 1.2.0</b>
<ul>
<li>Fixed not re-highlighting open files after changing settings.</li>
<li>Fixed exception when opening certain diff editors.</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>
@@ -47,8 +20,7 @@
<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="cpp-nova-brackets.xml">org.jetbrains.plugins.clion.radler</depends> <!--<depends optional="true" config-file="csharp-annotator.xml">com.intellij.modules.rider</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>