mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-13 04:34:21 -10:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f4f4ff3a44 |
No files matched your search
@@ -1,5 +1,6 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { Text } from "@codemirror/state";
|
||||
import { Tree } from "@lezer/common";
|
||||
import { semanticRanges } from "../index";
|
||||
|
||||
describe("Kotlin semantic tokens", () => {
|
||||
@@ -42,4 +43,42 @@ describe("Kotlin semantic tokens", () => {
|
||||
)[0]?.className,
|
||||
).toBe("cm-sem-property");
|
||||
});
|
||||
|
||||
it("colours annotation names as annotations, not as the constructor calls the server reports", () => {
|
||||
const lines = [
|
||||
'@Column(name = "x")',
|
||||
"@field:Inject @Outer.Inner",
|
||||
"list.forEach { return@forEach }",
|
||||
"[email protected]()",
|
||||
];
|
||||
const doc = Text.of(lines);
|
||||
const types = ["method", "class", "label"];
|
||||
const tok = (line: number, word: string, type: string) => [
|
||||
line,
|
||||
lines[line]!.lastIndexOf(word),
|
||||
word.length,
|
||||
types.indexOf(type),
|
||||
0,
|
||||
];
|
||||
const data = [
|
||||
tok(0, "Column", "method"),
|
||||
tok(1, "Inject", "class"),
|
||||
tok(1, "Outer", "class"),
|
||||
tok(1, "Inner", "class"),
|
||||
tok(2, "forEach", "label"),
|
||||
tok(3, "Outer", "class"),
|
||||
].flat();
|
||||
const tokens = { tokenTypes: types, tokenModifiers: [], data };
|
||||
const ranges = semanticRanges(doc, tokens, false, "kotlin", Tree.empty);
|
||||
const classOf = (line: number, word: string) => {
|
||||
const from = doc.line(line + 1).from + lines[line]!.lastIndexOf(word);
|
||||
return ranges.find((r) => r.from === from)?.className;
|
||||
};
|
||||
expect(classOf(0, "Column")).toBe("cm-sem-annotation");
|
||||
expect(classOf(1, "Inject")).toBe("cm-sem-annotation");
|
||||
expect(classOf(1, "Outer")).toBe("cm-sem-annotation");
|
||||
expect(classOf(1, "Inner")).toBe("cm-sem-annotation");
|
||||
expect(classOf(2, "forEach")).toBe("cm-sem-constant");
|
||||
expect(classOf(3, "Outer")).not.toBe("cm-sem-annotation");
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,49 @@
|
||||
import type { Text } from "@codemirror/state";
|
||||
import { registerLanguageSemantics, type LanguageSemantics } from "../languages";
|
||||
|
||||
const NAME = /[A-Za-z_][\w]*/y;
|
||||
|
||||
/**
|
||||
* The names of annotation usages (`@Column`, `@field:Column`, `@Outer.Inner`), as `"from:to"` → `"annotation"`.
|
||||
* An `@` right after a name is a label (`return@forEach`, `this@Outer`, `loop@`), not an annotation.
|
||||
*/
|
||||
function annotationNames(doc: Text): Map<string, string> {
|
||||
const out = new Map<string, string>();
|
||||
const text = doc.toString();
|
||||
for (let at = text.indexOf("@"); at >= 0; at = text.indexOf("@", at + 1)) {
|
||||
if (/[\w`]/.test(text[at - 1] ?? "")) continue;
|
||||
let pos = at + 1;
|
||||
const name = () => {
|
||||
NAME.lastIndex = pos;
|
||||
const m = NAME.exec(text);
|
||||
return m ? { from: pos, to: pos + m[0].length } : null;
|
||||
};
|
||||
let first = name();
|
||||
// A use-site target (`@field:`, `@get:`, `@file:`) precedes the annotation's name.
|
||||
if (first && text[first.to] === ":" && text[first.to + 1] !== ":") {
|
||||
pos = first.to + 1;
|
||||
first = name();
|
||||
}
|
||||
for (let part = first; part;) {
|
||||
out.set(`${part.from}:${part.to}`, "annotation");
|
||||
if (text[part.to] !== ".") break;
|
||||
pos = part.to + 1;
|
||||
part = name();
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
const kotlinSemantics: LanguageSemantics = {
|
||||
tokenClasses({ type, modifiers, classes }) {
|
||||
symbolKinds: (_tree, doc) => annotationNames(doc),
|
||||
tokenClasses({ type, modifiers, kind, classes }) {
|
||||
// JetBrains Kotlin LSP reports an annotation usage as a constructor call (`method`);
|
||||
// IDEA colours the name as an annotation (Metadata), like its `@`.
|
||||
if (kind === "annotation" && type !== "label") {
|
||||
classes.length = 0;
|
||||
classes.push("cm-sem-annotation");
|
||||
return;
|
||||
}
|
||||
if (type === "operator" && modifiers.includes("declaration")) classes.push("cm-sem-function");
|
||||
// JetBrains Kotlin LSP uses `modification` for mutable declarations and references.
|
||||
if ((type === "variable" || type === "property" || type === "parameter") && modifiers.includes("modification"))
|
||||
|
||||
Reference in new issue
Block a user