annotation highlighting
This commit is contained in:
parent
d285335e1c
commit
834f3118c8
BIN
distributables/lyng-idea-0.0.3-SNAPSHOT.zip
(Stored with Git LFS)
BIN
distributables/lyng-idea-0.0.3-SNAPSHOT.zip
(Stored with Git LFS)
Binary file not shown.
@ -243,6 +243,21 @@ class LyngExternalAnnotator : ExternalAnnotator<LyngExternalAnnotator.Input, Lyn
|
|||||||
if (e is com.intellij.openapi.progress.ProcessCanceledException) throw e
|
if (e is com.intellij.openapi.progress.ProcessCanceledException) throw e
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add annotation coloring using token highlighter (treat @Label as annotation)
|
||||||
|
run {
|
||||||
|
val tokens = try { SimpleLyngHighlighter().highlight(text) } catch (_: Throwable) { emptyList() }
|
||||||
|
for (s in tokens) if (s.kind == HighlightKind.Label) {
|
||||||
|
val start = s.range.start
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Build spell index payload: identifiers from symbols + references; comments/strings from simple highlighter
|
// Build spell index payload: identifiers from symbols + references; comments/strings from simple highlighter
|
||||||
val idRanges = mutableSetOf<IntRange>()
|
val idRanges = mutableSetOf<IntRange>()
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -35,7 +35,6 @@ import com.intellij.psi.codeStyle.CodeStyleManager
|
|||||||
import net.sergeych.lyng.format.LyngFormatConfig
|
import net.sergeych.lyng.format.LyngFormatConfig
|
||||||
import net.sergeych.lyng.format.LyngFormatter
|
import net.sergeych.lyng.format.LyngFormatter
|
||||||
import net.sergeych.lyng.idea.LyngLanguage
|
import net.sergeych.lyng.idea.LyngLanguage
|
||||||
import net.sergeych.lyng.idea.settings.LyngFormatterSettings
|
|
||||||
|
|
||||||
class LyngEnterHandler : EnterHandlerDelegate {
|
class LyngEnterHandler : EnterHandlerDelegate {
|
||||||
private val log = Logger.getInstance(LyngEnterHandler::class.java)
|
private val log = Logger.getInstance(LyngEnterHandler::class.java)
|
||||||
@ -82,11 +81,9 @@ class LyngEnterHandler : EnterHandlerDelegate {
|
|||||||
// consider only code part before // comment
|
// consider only code part before // comment
|
||||||
val code = trimmed.substringBefore("//").trim()
|
val code = trimmed.substringBefore("//").trim()
|
||||||
if (code == "}") {
|
if (code == "}") {
|
||||||
// Optionally reindent the enclosed block without manually touching the '}' line.
|
// Previously we reindented the enclosed block on Enter after a lone '}'.
|
||||||
val settings = LyngFormatterSettings.getInstance(project)
|
// Per new behavior, this action is now bound to typing '}' instead.
|
||||||
if (settings.reindentClosedBlockOnEnter) {
|
// Keep Enter flow limited to indenting the new line only.
|
||||||
reindentClosedBlockAroundBrace(project, file, doc, prevLine)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Adjust indent for the current (new) line
|
// Adjust indent for the current (new) line
|
||||||
|
|||||||
@ -57,6 +57,7 @@ class LyngColorSettingsPage : ColorSettingsPage {
|
|||||||
AttributesDescriptor("Identifier", LyngHighlighterColors.IDENTIFIER),
|
AttributesDescriptor("Identifier", LyngHighlighterColors.IDENTIFIER),
|
||||||
AttributesDescriptor("Punctuation", LyngHighlighterColors.PUNCT),
|
AttributesDescriptor("Punctuation", LyngHighlighterColors.PUNCT),
|
||||||
// Semantic
|
// Semantic
|
||||||
|
AttributesDescriptor("Annotation (semantic)", LyngHighlighterColors.ANNOTATION),
|
||||||
AttributesDescriptor("Variable (semantic)", LyngHighlighterColors.VARIABLE),
|
AttributesDescriptor("Variable (semantic)", LyngHighlighterColors.VARIABLE),
|
||||||
AttributesDescriptor("Value (semantic)", LyngHighlighterColors.VALUE),
|
AttributesDescriptor("Value (semantic)", LyngHighlighterColors.VALUE),
|
||||||
AttributesDescriptor("Function (semantic)", LyngHighlighterColors.FUNCTION),
|
AttributesDescriptor("Function (semantic)", LyngHighlighterColors.FUNCTION),
|
||||||
|
|||||||
@ -72,4 +72,9 @@ object LyngHighlighterColors {
|
|||||||
val PARAMETER: TextAttributesKey = TextAttributesKey.createTextAttributesKey(
|
val PARAMETER: TextAttributesKey = TextAttributesKey.createTextAttributesKey(
|
||||||
"LYNG_PARAMETER", DefaultLanguageHighlighterColors.PARAMETER
|
"LYNG_PARAMETER", DefaultLanguageHighlighterColors.PARAMETER
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Annotations (@Something) — use Kotlin/Java metadata default color
|
||||||
|
val ANNOTATION: TextAttributesKey = TextAttributesKey.createTextAttributesKey(
|
||||||
|
"LYNG_ANNOTATION", DefaultLanguageHighlighterColors.METADATA
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -81,6 +81,9 @@
|
|||||||
<!-- Smart Enter handler -->
|
<!-- Smart Enter handler -->
|
||||||
<enterHandlerDelegate implementation="net.sergeych.lyng.idea.editor.LyngEnterHandler"/>
|
<enterHandlerDelegate implementation="net.sergeych.lyng.idea.editor.LyngEnterHandler"/>
|
||||||
|
|
||||||
|
<!-- Trigger reindent of enclosed block when typing a standalone '}' -->
|
||||||
|
<typedHandler implementation="net.sergeych.lyng.idea.editor.LyngTypedHandler"/>
|
||||||
|
|
||||||
<!-- Smart Backspace handler (deferred) -->
|
<!-- Smart Backspace handler (deferred) -->
|
||||||
<!-- <backspaceHandlerDelegate implementation="net.sergeych.lyng.idea.editor.LyngBackspaceHandler"/> -->
|
<!-- <backspaceHandlerDelegate implementation="net.sergeych.lyng.idea.editor.LyngBackspaceHandler"/> -->
|
||||||
|
|
||||||
|
|||||||
@ -140,16 +140,22 @@ class SimpleLyngHighlighter : LyngHighlighter {
|
|||||||
|
|
||||||
for (t in tokens) {
|
for (t in tokens) {
|
||||||
val k = kindOf(t.type, t.value) ?: continue
|
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) {
|
val range = when (t.type) {
|
||||||
Type.STRING, Type.STRING2 -> adjustQuoteSpan(start, '"')
|
Type.STRING, Type.STRING2 -> adjustQuoteSpan(start0, '"')
|
||||||
Type.CHAR -> adjustQuoteSpan(start, '\'')
|
Type.CHAR -> adjustQuoteSpan(start0, '\'')
|
||||||
Type.HEX -> {
|
Type.HEX -> {
|
||||||
// Parser returns HEX token value without the leading "0x"; include it in highlight span
|
// Parser returns HEX token value without the leading "0x"; include it in highlight span
|
||||||
val end = (start + 2 + t.value.length).coerceAtMost(text.length)
|
val end = (start0 + 2 + t.value.length).coerceAtMost(text.length)
|
||||||
TextRange(start, end)
|
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)
|
if (range.endExclusive > range.start) raw += HighlightSpan(range, k)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -81,4 +81,44 @@ class HighlightMappingTest {
|
|||||||
assertTrue(labeled.any { it.first == "(" && it.second == HighlightKind.Punctuation })
|
assertTrue(labeled.any { it.first == "(" && it.second == HighlightKind.Punctuation })
|
||||||
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() == '~')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -163,6 +163,7 @@ fun ensureLyngHighlightStyles() {
|
|||||||
.hl-op { color: #8250df; }
|
.hl-op { color: #8250df; }
|
||||||
.hl-punc{ color: #57606a; }
|
.hl-punc{ color: #57606a; }
|
||||||
.hl-lbl { color: #e36209; }
|
.hl-lbl { color: #e36209; }
|
||||||
|
.hl-ann { color: #e36209; font-style: italic; }
|
||||||
.hl-dir { color: #6f42c1; }
|
.hl-dir { color: #6f42c1; }
|
||||||
.hl-err { color: #b31d28; text-decoration: underline wavy #b31d28; }
|
.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-rx { color: #7ee787; }
|
||||||
[data-bs-theme="dark"] .hl-cmt { color: #8b949e; }
|
[data-bs-theme="dark"] .hl-cmt { color: #8b949e; }
|
||||||
[data-bs-theme="dark"] .hl-lbl { color: #ffa657; }
|
[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-dir { color: #d2a8ff; }
|
||||||
[data-bs-theme="dark"] .hl-err { color: #ffa198; text-decoration-color: #ffa198; }
|
[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)))
|
if (s.range.start > pos) sb.append(htmlEscape(safeSubstring(pos, s.range.start)))
|
||||||
val cls = when (s.kind) {
|
val cls = when (s.kind) {
|
||||||
HighlightKind.Identifier -> overrides[s.range.start to s.range.endExclusive] ?: cssClassForKind(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)
|
else -> cssClassForKind(s.kind)
|
||||||
}
|
}
|
||||||
sb.append('<').append("span class=\"").append(cls).append('\"').append('>')
|
sb.append('<').append("span class=\"").append(cls).append('\"').append('>')
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user