added label syntax support and highlighting
This commit is contained in:
1 parent
660a80a26b
commit
26b8370b01
12 files changed
+122
-19
No files matched your search
+44
-13
@@ -94,6 +94,16 @@ class LyngExternalAnnotator : ExternalAnnotator<LyngExternalAnnotator.Input, Lyn
|
||||
|
||||
val out = ArrayList<Span>(256)
|
||||
|
||||
fun isFollowedByParenOrBlock(rangeEnd: Int): Boolean {
|
||||
var i = rangeEnd
|
||||
while (i < text.length) {
|
||||
val ch = text[i]
|
||||
if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') { i++; continue }
|
||||
return ch == '(' || ch == '{'
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun putRange(start: Int, end: Int, key: com.intellij.openapi.editor.colors.TextAttributesKey) {
|
||||
if (start in 0..end && end <= text.length && start < end) out += Span(start, end, key)
|
||||
}
|
||||
@@ -207,16 +217,6 @@ class LyngExternalAnnotator : ExternalAnnotator<LyngExternalAnnotator.Input, Lyn
|
||||
|
||||
val tokens = try { SimpleLyngHighlighter().highlight(text) } catch (_: Throwable) { emptyList() }
|
||||
|
||||
fun isFollowedByParenOrBlock(rangeEnd: Int): Boolean {
|
||||
var i = rangeEnd
|
||||
while (i < text.length) {
|
||||
val ch = text[i]
|
||||
if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') { i++; continue }
|
||||
return ch == '(' || ch == '{'
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Build simple name -> role map for top-level vals/vars and parameters
|
||||
val nameRole = HashMap<String, com.intellij.openapi.editor.colors.TextAttributesKey>(8)
|
||||
for (d in mini.declarations) when (d) {
|
||||
@@ -253,7 +253,7 @@ class LyngExternalAnnotator : ExternalAnnotator<LyngExternalAnnotator.Input, Lyn
|
||||
if (e is com.intellij.openapi.progress.ProcessCanceledException) throw e
|
||||
}
|
||||
|
||||
// Add annotation coloring using token highlighter (treat @Label as annotation)
|
||||
// Add annotation/label coloring using token highlighter
|
||||
run {
|
||||
val tokens = try { SimpleLyngHighlighter().highlight(text) } catch (_: Throwable) { emptyList() }
|
||||
for (s in tokens) if (s.kind == HighlightKind.Label) {
|
||||
@@ -261,8 +261,29 @@ class LyngExternalAnnotator : ExternalAnnotator<LyngExternalAnnotator.Input, Lyn
|
||||
val end = s.range.endExclusive
|
||||
if (start in 0..end && end <= text.length && start < end) {
|
||||
val lexeme = try { text.substring(start, end) } catch (_: Throwable) { null }
|
||||
if (lexeme != null && lexeme.startsWith("@")) {
|
||||
putRange(start, end, LyngHighlighterColors.ANNOTATION)
|
||||
if (lexeme != null) {
|
||||
// Heuristic: if it starts with @ and follows a control keyword, it's likely a label
|
||||
// Otherwise if it starts with @ it's an annotation.
|
||||
// If it ends with @ it's a loop label.
|
||||
when {
|
||||
lexeme.endsWith("@") -> putRange(start, end, LyngHighlighterColors.LABEL)
|
||||
lexeme.startsWith("@") -> {
|
||||
// Try to see if it's an exit label
|
||||
val prevNonWs = prevNonWs(text, start)
|
||||
val prevWord = if (prevNonWs >= 0) {
|
||||
var wEnd = prevNonWs + 1
|
||||
var wStart = prevNonWs
|
||||
while (wStart > 0 && text[wStart - 1].isLetter()) wStart--
|
||||
text.substring(wStart, wEnd)
|
||||
} else null
|
||||
|
||||
if (prevWord in setOf("return", "break", "continue") || isFollowedByParenOrBlock(end)) {
|
||||
putRange(start, end, LyngHighlighterColors.LABEL)
|
||||
} else {
|
||||
putRange(start, end, LyngHighlighterColors.ANNOTATION)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -364,6 +385,16 @@ class LyngExternalAnnotator : ExternalAnnotator<LyngExternalAnnotator.Input, Lyn
|
||||
private val CACHE_KEY: Key<Result> = Key.create("LYNG_SEMANTIC_CACHE")
|
||||
}
|
||||
|
||||
private fun prevNonWs(text: String, idxExclusive: Int): Int {
|
||||
var i = idxExclusive - 1
|
||||
while (i >= 0) {
|
||||
val ch = text[i]
|
||||
if (ch != ' ' && ch != '\t' && ch != '\n' && ch != '\r') return i
|
||||
i--
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the error highlight a bit wider than a single character so it is easier to see and click.
|
||||
* Strategy:
|
||||
|
||||
@@ -43,10 +43,15 @@ class LyngColorSettingsPage : ColorSettingsPage {
|
||||
}
|
||||
|
||||
var counter = 0
|
||||
counter = counter + 1
|
||||
outer@ while (counter < 10) {
|
||||
if (counter == 5) return@outer
|
||||
counter = counter + 1
|
||||
}
|
||||
""".trimIndent()
|
||||
|
||||
override fun getAdditionalHighlightingTagToDescriptorMap(): MutableMap<String, TextAttributesKey>? = null
|
||||
override fun getAdditionalHighlightingTagToDescriptorMap(): MutableMap<String, TextAttributesKey> = mutableMapOf(
|
||||
"label" to LyngHighlighterColors.LABEL
|
||||
)
|
||||
|
||||
override fun getAttributeDescriptors(): Array<AttributesDescriptor> = arrayOf(
|
||||
AttributesDescriptor("Keyword", LyngHighlighterColors.KEYWORD),
|
||||
@@ -58,6 +63,7 @@ class LyngColorSettingsPage : ColorSettingsPage {
|
||||
AttributesDescriptor("Punctuation", LyngHighlighterColors.PUNCT),
|
||||
// Semantic
|
||||
AttributesDescriptor("Annotation (semantic)", LyngHighlighterColors.ANNOTATION),
|
||||
AttributesDescriptor("Label (semantic)", LyngHighlighterColors.LABEL),
|
||||
AttributesDescriptor("Variable (semantic)", LyngHighlighterColors.VARIABLE),
|
||||
AttributesDescriptor("Value (semantic)", LyngHighlighterColors.VALUE),
|
||||
AttributesDescriptor("Function (semantic)", LyngHighlighterColors.FUNCTION),
|
||||
|
||||
@@ -82,4 +82,9 @@ object LyngHighlighterColors {
|
||||
val ENUM_CONSTANT: TextAttributesKey = TextAttributesKey.createTextAttributesKey(
|
||||
"LYNG_ENUM_CONSTANT", DefaultLanguageHighlighterColors.STATIC_FIELD
|
||||
)
|
||||
|
||||
// Labels (label@ or @label used as exit target)
|
||||
val LABEL: TextAttributesKey = TextAttributesKey.createTextAttributesKey(
|
||||
"LYNG_LABEL", DefaultLanguageHighlighterColors.LABEL
|
||||
)
|
||||
}
|
||||
@@ -133,10 +133,24 @@ class LyngLexer : LexerBase() {
|
||||
return
|
||||
}
|
||||
|
||||
// Identifier / keyword
|
||||
// Labels / Annotations: @label or label@
|
||||
if (ch == '@') {
|
||||
i++
|
||||
while (i < endOffset && (buffer[i].isIdentifierPart())) i++
|
||||
myTokenEnd = i
|
||||
myTokenType = LyngTokenTypes.LABEL
|
||||
return
|
||||
}
|
||||
|
||||
if (ch.isIdentifierStart()) {
|
||||
i++
|
||||
while (i < endOffset && buffer[i].isIdentifierPart()) i++
|
||||
if (i < endOffset && buffer[i] == '@') {
|
||||
i++
|
||||
myTokenEnd = i
|
||||
myTokenType = LyngTokenTypes.LABEL
|
||||
return
|
||||
}
|
||||
myTokenEnd = i
|
||||
val text = buffer.subSequence(myTokenStart, myTokenEnd).toString()
|
||||
myTokenType = if (text in keywords) LyngTokenTypes.KEYWORD else LyngTokenTypes.IDENTIFIER
|
||||
|
||||
@@ -33,6 +33,7 @@ class LyngSyntaxHighlighter : SyntaxHighlighter {
|
||||
LyngTokenTypes.BLOCK_COMMENT -> pack(LyngHighlighterColors.BLOCK_COMMENT)
|
||||
LyngTokenTypes.PUNCT -> pack(LyngHighlighterColors.PUNCT)
|
||||
LyngTokenTypes.IDENTIFIER -> pack(LyngHighlighterColors.IDENTIFIER)
|
||||
LyngTokenTypes.LABEL -> pack(LyngHighlighterColors.LABEL)
|
||||
else -> emptyArray()
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ object LyngTokenTypes {
|
||||
val NUMBER = LyngTokenType("NUMBER")
|
||||
val KEYWORD = LyngTokenType("KEYWORD")
|
||||
val IDENTIFIER = LyngTokenType("IDENTIFIER")
|
||||
val LABEL = LyngTokenType("LABEL")
|
||||
val PUNCT = LyngTokenType("PUNCT")
|
||||
val BAD_CHAR = LyngTokenType("BAD_CHAR")
|
||||
}
|
||||
Reference in new issue
Block a user