Skip to content

Commit 8df88a5

Browse files
committed
feat: incremental parse
1 parent 5ea698a commit 8df88a5

File tree

4 files changed

+62
-11
lines changed

4 files changed

+62
-11
lines changed

packages/language-server/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"tree-sitter": "^0.22.4",
1010
"tree-sitter-rust": "^0.23.2",
1111
"volar-service-typescript": "^0.0.62",
12+
"vscode-languageserver-textdocument": "^1.0.12",
1213
"vscode-uri": "^3.1.0"
1314
},
1415
"devDependencies": {

packages/language-server/src/languagePlugin.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ export async function createJsrsLanguagePlugin(_params: InitializeParams): Promi
1818
return new JsrsVirtualCode(snapshot);
1919
}
2020
},
21+
updateVirtualCode(uri, virtualCode, newSnapshot, _ctx) {
22+
if (virtualCode instanceof JsrsVirtualCode) {
23+
virtualCode.update(newSnapshot);
24+
return virtualCode;
25+
}
26+
},
2127
typescript: {
2228
extraFileExtensions: [{ extension: "jsrs", isMixedContent: true, scriptKind: 7 satisfies ts.ScriptKind.Deferred }],
2329
getServiceScript() {
Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,35 @@
11
import type { CodeMapping, VirtualCode } from "@volar/language-core";
2-
import type { Language } from "tree-sitter";
2+
import type { Language, Tree } from "tree-sitter";
33
import type ts from "typescript";
4+
import type { Position } from "vscode-languageserver-textdocument";
45
import Parser from "tree-sitter";
56
import _Rust from "tree-sitter-rust";
7+
import { TextDocument } from "vscode-languageserver-textdocument";
68
import { generateRoot } from "./codegen";
79
import { resolveCodes } from "./utils/resolveCodes";
810

9-
// eslint-disable-next-line ts/no-require-imports
10-
const Rust: Language = typeof _Rust === "string" ? require(_Rust) : _Rust;
11-
1211
let parser: Parser | undefined;
12+
function getRustParser() {
13+
if (!parser) {
14+
// eslint-disable-next-line ts/no-require-imports
15+
const Rust: Language = typeof _Rust === "string" ? require(_Rust) : _Rust;
16+
parser = new Parser();
17+
parser.setLanguage(Rust);
18+
}
19+
return parser;
20+
}
1321

1422
export class JsrsVirtualCode implements VirtualCode {
1523
id = "root";
1624
languageId = "jsrs";
17-
embeddedCodes: VirtualCode[] = [];
25+
embeddedCodes!: VirtualCode[];
26+
snapshot: ts.IScriptSnapshot;
1827
mappings: CodeMapping[];
19-
tree: Parser.Tree;
28+
tree: Tree;
29+
document: TextDocument;
2030

21-
constructor(public snapshot: ts.IScriptSnapshot) {
31+
constructor(snapshot: ts.IScriptSnapshot) {
32+
this.snapshot = snapshot;
2233
this.mappings = [
2334
{
2435
sourceOffsets: [0],
@@ -35,14 +46,44 @@ export class JsrsVirtualCode implements VirtualCode {
3546
},
3647
];
3748

38-
parser ??= new Parser();
39-
parser.setLanguage(Rust);
40-
4149
const content = snapshot.getText(0, snapshot.getLength());
42-
this.tree = parser.parse(content);
50+
this.tree = getRustParser().parse(content);
51+
this.document = TextDocument.create("file://temp.jsrs", "jsrs", 0, content);
52+
this.updateEmbeddedCodes();
53+
}
54+
55+
update(newSnapshot: ts.IScriptSnapshot) {
56+
const newContent = newSnapshot.getText(0, newSnapshot.getLength());
57+
const changeRange = newSnapshot.getChangeRange(this.snapshot);
58+
this.snapshot = newSnapshot;
59+
if (changeRange) {
60+
const newDocument = TextDocument.create("file://temp.jsrs", "jsrs", 0, newContent);
61+
this.tree.edit({
62+
startIndex: changeRange.span.start,
63+
oldEndIndex: changeRange.span.start + changeRange.span.length,
64+
newEndIndex: changeRange.span.start + changeRange.newLength,
65+
startPosition: positionToPoint(this.document.positionAt(changeRange.span.start)),
66+
oldEndPosition: positionToPoint(this.document.positionAt(changeRange.span.start + changeRange.span.length)),
67+
newEndPosition: positionToPoint(newDocument.positionAt(changeRange.span.start + changeRange.newLength)),
68+
});
69+
this.tree = getRustParser().parse(newContent, this.tree);
70+
this.document = newDocument;
71+
}
72+
else {
73+
// Cannot be incrementally updated, just recreate the tree
74+
this.tree = getRustParser().parse(newContent);
75+
this.document = TextDocument.create("file://temp.jsrs", "jsrs", 0, newContent);
76+
}
77+
this.updateEmbeddedCodes();
78+
}
4379

80+
updateEmbeddedCodes() {
4481
this.embeddedCodes = [
4582
resolveCodes("jsrs", "typescript", generateRoot(this.tree.rootNode)),
4683
];
4784
}
4885
}
86+
87+
function positionToPoint(position: Position) {
88+
return { row: position.line, column: position.character };
89+
}

pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)