1
0
mirror of https://github.com/chylex/IntelliJ-Rainbow-Brackets.git synced 2026-02-19 12:46:36 +01:00

Compare commits

6 Commits

Author SHA1 Message Date
a02e7f0916 Release 1.3.0 2025-10-30 07:59:12 +01:00
0d8e2c9e16 Update Gradle to 8.14.3 and IntelliJ Platform plugin to 2.9.0 2025-10-29 21:38:00 +01:00
3007bdce44 Fix missing read lock in indent guide renderer
Closes #5
2025-10-29 08:09:19 +01:00
74ac5273b9 Release 1.2.0 2024-12-09 13:25:18 +01:00
b732020017 Address several deprecation warnings 2024-12-09 13:19:38 +01:00
93f5911faf Fix exception when opening certain diff editors
Closes #2
2024-12-09 12:42:22 +01:00
34 changed files with 206 additions and 224 deletions

1
.gitignore vendored
View File

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

8
api/build.gradle.kts Normal file
View File

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

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")
private val bracePairs = lazy { val bracePairs = lazy {
Language.getRegisteredLanguages() Language.getRegisteredLanguages()
.map { language -> .map { language ->
if (language is CompositeLanguage) { if (language is CompositeLanguage) {
@@ -74,15 +74,13 @@ object BracePairs {
.toMap() .toMap()
} }
fun getBracePairs(language: Language): MutableMap<String, MutableList<BracePair>>? = bracePairs.value[language.id] private fun getBraceTypeSetOf(language: Language): Set<IElementType> = language.bracePairs?.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()
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,6 +1,7 @@
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
@@ -137,7 +138,7 @@ object RainbowHighlighter {
} }
private fun genByOption(option: String, rainbowName: String, level: Int) = private fun genByOption(option: String, rainbowName: String, level: Int) =
com.chylex.intellij.coloredbrackets.util.create( create(
"$rainbowName-$level", "$rainbowName-$level",
TextAttributes(randomColor(option), null, null, null, 0) TextAttributes(randomColor(option), null, null, null, 0)
) )

View File

@@ -2,6 +2,7 @@ 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
@@ -11,7 +12,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 com.chylex.intellij.coloredbrackets.color.randomColor( return 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

@@ -5,7 +5,7 @@ import com.intellij.openapi.components.PersistentStateComponent
import com.intellij.openapi.components.SettingsCategory 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.copyBean import com.intellij.util.xmlb.XmlSerializerUtil
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")], category = SettingsCategory.UI)
@@ -51,7 +51,7 @@ class RainbowSettings : PersistentStateComponent<RainbowSettings> {
override fun getState() = this override fun getState() = this
override fun loadState(state: RainbowSettings) { override fun loadState(state: RainbowSettings) {
copyBean(state, this) XmlSerializerUtil.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] ?: run { m.putSafely(k, k(f)) } operator fun invoke(k: K): R = m[k] ?: 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

@@ -96,24 +96,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 {
fun PsiElement.getLineCount(): Int { val doc = containingFile?.let { PsiDocumentManager.getInstance(project).getDocument(it) }
try { if (doc != null) {
val doc = containingFile?.let { PsiDocumentManager.getInstance(project).getDocument(it) } val spaceRange = textRange ?: TextRange.EMPTY_RANGE
if (doc != null) {
val spaceRange = textRange ?: TextRange.EMPTY_RANGE if (spaceRange.endOffset <= doc.textLength && spaceRange.startOffset < spaceRange.endOffset) {
val startLine = doc.getLineNumber(spaceRange.startOffset)
if (spaceRange.endOffset <= doc.textLength && spaceRange.startOffset < spaceRange.endOffset) { val endLine = doc.getLineNumber(spaceRange.endOffset - 1)
val startLine = doc.getLineNumber(spaceRange.startOffset)
val endLine = doc.getLineNumber(spaceRange.endOffset - 1) return endLine - startLine + 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

@@ -1,129 +1,103 @@
@file:Suppress("ConvertLambdaToReference") @file:Suppress("ConvertLambdaToReference")
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.jetbrains.intellij.platform.gradle.TestFrameworkType
import kotlin.io.path.Path
plugins { plugins {
kotlin("jvm") kotlin("jvm")
id("org.jetbrains.intellij") id("org.jetbrains.intellij.platform")
} }
group = "com.chylex.intellij.coloredbrackets" group = "com.chylex.intellij.coloredbrackets"
version = "1.1.0" version = "1.3.0"
val ideaVersion = "2023.3"
allprojects { allprojects {
apply(plugin = "org.jetbrains.kotlin.jvm") apply(plugin = "org.jetbrains.kotlin.jvm")
apply(plugin = "org.jetbrains.intellij") apply(plugin = "org.jetbrains.intellij.platform")
ext {
set("ideaVersion", ideaVersion)
}
repositories { repositories {
mavenCentral() mavenCentral()
intellijPlatform {
defaultRepositories()
}
} }
intellij { intellijPlatform {
version.set("2023.3") pluginConfiguration {
updateSinceUntilBuild.set(false) ideaVersion {
sinceBuild.set("233")
untilBuild.set(provider { null })
}
}
} }
kotlin { kotlin {
jvmToolchain(17) jvmToolchain(17)
}
compilerOptions {
tasks.withType<KotlinCompile> { freeCompilerArgs = listOf(
kotlinOptions.freeCompilerArgs = listOf( "-X" + "jvm-default=all",
"-Xjvm-default=all" )
) }
} }
} }
subprojects { subprojects {
tasks.buildSearchableOptions { intellijPlatform {
enabled = false buildSearchableOptions = 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,13 +1,12 @@
intellij { val ideaVersion: String by project
type.set("CL")
plugins.set(listOf(
// Built-in
"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.
))
}
dependencies { dependencies {
implementation(rootProject) implementation(project(":api"))
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 +1,2 @@
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.5-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
networkTimeout=10000 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME

View File

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

View File

@@ -1,6 +1,6 @@
package com.chylex.intellij.coloredbrackets.visitor package com.chylex.intellij.coloredbrackets.visitor
import com.chylex.intellij.coloredbrackets.bracePairs import com.chylex.intellij.coloredbrackets.BracePairs.bracePairs
import com.chylex.intellij.coloredbrackets.settings.RainbowSettings import com.chylex.intellij.coloredbrackets.settings.RainbowSettings
import com.intellij.lang.BracePair import com.intellij.lang.BracePair
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement

View File

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

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") val label: Ref<HyperlinkLabel> = Ref.create()
label.set(createActionLabel(labelText) {
fun EditorNotificationPanel.createComponentActionLabel(labelText: String, callback: (HyperlinkLabel) -> Unit) { callback(label.get())
val label: Ref<HyperlinkLabel> = Ref.create() })
label.set(createActionLabel(labelText) {
callback(label.get())
})
}
} }
} }

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) .range(element as PsiElement) // Cast necessary due to overload conflict in Kotlin 2 compiler.
.textAttributes( .textAttributes(
com.chylex.intellij.coloredbrackets.util.create( com.chylex.intellij.coloredbrackets.util.create(
"rainbow-kotlin-arrow", "rainbow-kotlin-arrow",

View File

@@ -7,6 +7,7 @@ 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
@@ -165,24 +166,28 @@ 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
var element = try {
psiFile.findElementAt(highlighter.endOffset)?.parent ?: return null
} catch (e: Throwable) {
return null
}
var rainbowInfo = RainbowInfo.RAINBOW_INFO_KEY[element] return ReadAction.compute<RainbowInfo, Throwable> {
if (rainbowInfo == null && psiFile is XmlFile && element !is XmlTag) { val psiFile = PsiManager.getInstance(project).findFile(virtualFile) ?: return@compute null
element = PsiTreeUtil.findFirstParent(element, true, XML_TAG_PARENT_CONDITION) ?: return null var element = try {
rainbowInfo = RainbowInfo.RAINBOW_INFO_KEY[element] ?: return null psiFile.findElementAt(highlighter.endOffset)?.parent ?: return@compute 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
}
} }
if (!element.isValid || !checkBoundary(document, element, highlighter)) {
return null
}
return rainbowInfo
} }
/*** /***

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

View File

@@ -18,18 +18,27 @@
]]></description> ]]></description>
<change-notes><![CDATA[ <change-notes><![CDATA[
<b>1.1.0</b> <b>Version 1.3.0</b>
<ul> <ul>
<li>Added support for C++ in Rider and CLion Nova</li> <li>Fixed assertion error caused by missing read lock in indent guide renderer.</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> </ul>
<b>1.0.0</b> <b>Version 1.2.0</b>
<ul> <ul>
<li>Restored 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>