diff --git a/distributables/lyng-idea-0.0.3-SNAPSHOT.zip b/distributables/lyng-idea-0.0.3-SNAPSHOT.zip index 63e417a..8acdbc3 100644 --- a/distributables/lyng-idea-0.0.3-SNAPSHOT.zip +++ b/distributables/lyng-idea-0.0.3-SNAPSHOT.zip @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:85b67c4dc4721072c63d99dc1ca1812037e7cd30cc347f3a0df806e1712fe366 -size 27200367 +oid sha256:e6ca5168a379e7f107f378f34ee433c7e1112bcddb5cd106faedb7ff69c8d024 +size 27204101 diff --git a/lyng-idea/src/main/kotlin/net/sergeych/lyng/idea/annotators/LyngExternalAnnotator.kt b/lyng-idea/src/main/kotlin/net/sergeych/lyng/idea/annotators/LyngExternalAnnotator.kt index 53d1d70..687cc9c 100644 --- a/lyng-idea/src/main/kotlin/net/sergeych/lyng/idea/annotators/LyngExternalAnnotator.kt +++ b/lyng-idea/src/main/kotlin/net/sergeych/lyng/idea/annotators/LyngExternalAnnotator.kt @@ -243,6 +243,21 @@ class LyngExternalAnnotator : ExternalAnnotator() try { diff --git a/lyng-idea/src/main/kotlin/net/sergeych/lyng/idea/editor/LyngEnterHandler.kt b/lyng-idea/src/main/kotlin/net/sergeych/lyng/idea/editor/LyngEnterHandler.kt index 9b3b054..2f5e115 100644 --- a/lyng-idea/src/main/kotlin/net/sergeych/lyng/idea/editor/LyngEnterHandler.kt +++ b/lyng-idea/src/main/kotlin/net/sergeych/lyng/idea/editor/LyngEnterHandler.kt @@ -35,7 +35,6 @@ import com.intellij.psi.codeStyle.CodeStyleManager import net.sergeych.lyng.format.LyngFormatConfig import net.sergeych.lyng.format.LyngFormatter import net.sergeych.lyng.idea.LyngLanguage -import net.sergeych.lyng.idea.settings.LyngFormatterSettings class LyngEnterHandler : EnterHandlerDelegate { private val log = Logger.getInstance(LyngEnterHandler::class.java) @@ -82,11 +81,9 @@ class LyngEnterHandler : EnterHandlerDelegate { // consider only code part before // comment val code = trimmed.substringBefore("//").trim() if (code == "}") { - // Optionally reindent the enclosed block without manually touching the '}' line. - val settings = LyngFormatterSettings.getInstance(project) - if (settings.reindentClosedBlockOnEnter) { - reindentClosedBlockAroundBrace(project, file, doc, prevLine) - } + // Previously we reindented the enclosed block on Enter after a lone '}'. + // Per new behavior, this action is now bound to typing '}' instead. + // Keep Enter flow limited to indenting the new line only. } } // Adjust indent for the current (new) line diff --git a/lyng-idea/src/main/kotlin/net/sergeych/lyng/idea/highlight/LyngColorSettingsPage.kt b/lyng-idea/src/main/kotlin/net/sergeych/lyng/idea/highlight/LyngColorSettingsPage.kt index f59fd42..9209506 100644 --- a/lyng-idea/src/main/kotlin/net/sergeych/lyng/idea/highlight/LyngColorSettingsPage.kt +++ b/lyng-idea/src/main/kotlin/net/sergeych/lyng/idea/highlight/LyngColorSettingsPage.kt @@ -57,6 +57,7 @@ class LyngColorSettingsPage : ColorSettingsPage { AttributesDescriptor("Identifier", LyngHighlighterColors.IDENTIFIER), AttributesDescriptor("Punctuation", LyngHighlighterColors.PUNCT), // Semantic + AttributesDescriptor("Annotation (semantic)", LyngHighlighterColors.ANNOTATION), AttributesDescriptor("Variable (semantic)", LyngHighlighterColors.VARIABLE), AttributesDescriptor("Value (semantic)", LyngHighlighterColors.VALUE), AttributesDescriptor("Function (semantic)", LyngHighlighterColors.FUNCTION), diff --git a/lyng-idea/src/main/kotlin/net/sergeych/lyng/idea/highlight/LyngHighlighterColors.kt b/lyng-idea/src/main/kotlin/net/sergeych/lyng/idea/highlight/LyngHighlighterColors.kt index 1142ff9..b5aa440 100644 --- a/lyng-idea/src/main/kotlin/net/sergeych/lyng/idea/highlight/LyngHighlighterColors.kt +++ b/lyng-idea/src/main/kotlin/net/sergeych/lyng/idea/highlight/LyngHighlighterColors.kt @@ -72,4 +72,9 @@ object LyngHighlighterColors { val PARAMETER: TextAttributesKey = TextAttributesKey.createTextAttributesKey( "LYNG_PARAMETER", DefaultLanguageHighlighterColors.PARAMETER ) + + // Annotations (@Something) — use Kotlin/Java metadata default color + val ANNOTATION: TextAttributesKey = TextAttributesKey.createTextAttributesKey( + "LYNG_ANNOTATION", DefaultLanguageHighlighterColors.METADATA + ) } diff --git a/lyng-idea/src/main/resources/META-INF/plugin.xml b/lyng-idea/src/main/resources/META-INF/plugin.xml index 487c4f1..3fc3c26 100644 --- a/lyng-idea/src/main/resources/META-INF/plugin.xml +++ b/lyng-idea/src/main/resources/META-INF/plugin.xml @@ -81,6 +81,9 @@ + + + diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/highlight/SimpleLyngHighlighter.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/highlight/SimpleLyngHighlighter.kt index 9a53d65..07faed0 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/highlight/SimpleLyngHighlighter.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/highlight/SimpleLyngHighlighter.kt @@ -140,16 +140,22 @@ class SimpleLyngHighlighter : LyngHighlighter { for (t in tokens) { val k = kindOf(t.type, t.value) ?: continue - val start = src.offsetOf(t.pos) + val start0 = src.offsetOf(t.pos) val range = when (t.type) { - Type.STRING, Type.STRING2 -> adjustQuoteSpan(start, '"') - Type.CHAR -> adjustQuoteSpan(start, '\'') + Type.STRING, Type.STRING2 -> adjustQuoteSpan(start0, '"') + Type.CHAR -> adjustQuoteSpan(start0, '\'') Type.HEX -> { // Parser returns HEX token value without the leading "0x"; include it in highlight span - val end = (start + 2 + t.value.length).coerceAtMost(text.length) - TextRange(start, end) + val end = (start0 + 2 + t.value.length).coerceAtMost(text.length) + TextRange(start0, end) } - else -> TextRange(start, (start + t.value.length).coerceAtMost(text.length)) + Type.ATLABEL -> { + // Parser returns value without leading '@'; token pos points at '@'. + // So we need to include '@' (1 char) + the identifier length. + val end = (start0 + 1 + t.value.length).coerceAtMost(text.length) + TextRange(start0, end) + } + else -> TextRange(start0, (start0 + t.value.length).coerceAtMost(text.length)) } if (range.endExclusive > range.start) raw += HighlightSpan(range, k) } diff --git a/lynglib/src/commonTest/kotlin/net/sergeych/lyng/highlight/HighlightMappingTest.kt b/lynglib/src/commonTest/kotlin/net/sergeych/lyng/highlight/HighlightMappingTest.kt index 7a2fe8b..579f53f 100644 --- a/lynglib/src/commonTest/kotlin/net/sergeych/lyng/highlight/HighlightMappingTest.kt +++ b/lynglib/src/commonTest/kotlin/net/sergeych/lyng/highlight/HighlightMappingTest.kt @@ -81,4 +81,44 @@ class HighlightMappingTest { assertTrue(labeled.any { it.first == "(" && it.second == HighlightKind.Punctuation }) assertTrue(labeled.any { it.first == ")" && it.second == HighlightKind.Punctuation }) } + + @Test + fun annotationsIncludeAtAndFullName() { + // Simple standalone annotation must include full token including '@' + run { + val text = "@Ann" + val spans = SimpleLyngHighlighter().highlight(text) + val annSpans = spans.filter { it.kind == HighlightKind.Label } + assertTrue(annSpans.size == 1) + val frag = text.substring(annSpans[0].range.start, annSpans[0].range.endExclusive) + assertTrue(frag == "@Ann") + } + // Qualified name: we at least must not drop the last character of the @segment + run { + val text = "@Qualified.Name" + val spans = SimpleLyngHighlighter().highlight(text) + val annSpans = spans.filter { it.kind == HighlightKind.Label } + assertTrue(annSpans.size == 1) + val s = annSpans[0] + val frag = text.substring(s.range.start, s.range.endExclusive) + assertTrue(frag.startsWith("@Qualified")) + // Ensure we did not miss the last char of this segment + assertTrue(frag.last() == 'd') + // Next token after the span should be '.' + assertTrue(text.getOrNull(s.range.endExclusive) == '.') + } + // Multiple annotations: every Label span must include '@' and end on an identifier boundary + run { + val text = "@Ann @Another(1) fun x() {}" + val spans = SimpleLyngHighlighter().highlight(text) + val annSpans = spans.filter { it.kind == HighlightKind.Label } + assertTrue(annSpans.size >= 2) + for (s in annSpans) { + val frag = text.substring(s.range.start, s.range.endExclusive) + assertTrue(frag.startsWith("@")) + // last char must be letter/digit/underscore/tilde/dollar per idNextChars + assertTrue(frag.last().isLetterOrDigit() || frag.last() == '_' || frag.last() == '$' || frag.last() == '~') + } + } + } } diff --git a/lyngweb/src/jsMain/kotlin/net/sergeych/lyngweb/Highlight.kt b/lyngweb/src/jsMain/kotlin/net/sergeych/lyngweb/Highlight.kt index 483eb96..7685e22 100644 --- a/lyngweb/src/jsMain/kotlin/net/sergeych/lyngweb/Highlight.kt +++ b/lyngweb/src/jsMain/kotlin/net/sergeych/lyngweb/Highlight.kt @@ -163,6 +163,7 @@ fun ensureLyngHighlightStyles() { .hl-op { color: #8250df; } .hl-punc{ color: #57606a; } .hl-lbl { color: #e36209; } + .hl-ann { color: #e36209; font-style: italic; } .hl-dir { color: #6f42c1; } .hl-err { color: #b31d28; text-decoration: underline wavy #b31d28; } @@ -183,6 +184,7 @@ fun ensureLyngHighlightStyles() { [data-bs-theme="dark"] .hl-rx { color: #7ee787; } [data-bs-theme="dark"] .hl-cmt { color: #8b949e; } [data-bs-theme="dark"] .hl-lbl { color: #ffa657; } + [data-bs-theme="dark"] .hl-ann { color: #ffa657; font-style: italic; } [data-bs-theme="dark"] .hl-dir { color: #d2a8ff; } [data-bs-theme="dark"] .hl-err { color: #ffa198; text-decoration-color: #ffa198; } """ @@ -316,6 +318,10 @@ fun applyLyngHighlightToText(text: String): String { if (s.range.start > pos) sb.append(htmlEscape(safeSubstring(pos, s.range.start))) val cls = when (s.kind) { HighlightKind.Identifier -> overrides[s.range.start to s.range.endExclusive] ?: cssClassForKind(s.kind) + HighlightKind.Label -> { + val lex = safeSubstring(s.range.start, s.range.endExclusive) + if (lex.startsWith("@")) "hl-ann" else cssClassForKind(s.kind) + } else -> cssClassForKind(s.kind) } sb.append('<').append("span class=\"").append(cls).append('\"').append('>')