Skip to content

Commit d453281

Browse files
committed
vscode: add goto definition from rust file to syntax tree editor
1 parent 98f7842 commit d453281

File tree

1 file changed

+83
-5
lines changed

1 file changed

+83
-5
lines changed

editors/code/src/commands/syntax_tree.ts

Lines changed: 83 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,17 +83,43 @@ class TextDocumentContentProvider implements vscode.TextDocumentContentProvider
8383
// FIXME: consider implementing this via the Tree View API?
8484
// https://code.visualstudio.com/api/extension-guides/tree-view
8585
class AstInspector implements vscode.HoverProvider, Disposable {
86-
private static readonly astDecorationType = vscode.window.createTextEditorDecorationType({
86+
private readonly astDecorationType = vscode.window.createTextEditorDecorationType({
8787
borderColor: new vscode.ThemeColor('rust_analyzer.syntaxTreeBorder'),
8888
borderStyle: "solid",
8989
borderWidth: "2px",
9090

9191
});
9292
private rustEditor: undefined | RustEditor;
9393

94+
// Lazy rust token range -> syntax tree file range.
95+
private readonly rust2Ast = new Lazy(() => {
96+
const astEditor = this.findAstTextEditor();
97+
if (!this.rustEditor || !astEditor) return undefined;
98+
99+
console.time("Build goto def index");
100+
let buf: [vscode.Range, vscode.Range][] = [];
101+
for (let i = 0; i < astEditor.document.lineCount; ++i) {
102+
const astLine = astEditor.document.lineAt(i);
103+
104+
// Heuristically look for nodes with quoted text (which are token nodes)
105+
const isTokenNode = astLine.text.lastIndexOf('"') >= 0;
106+
if (!isTokenNode) continue;
107+
108+
const rustRange = this.parseRustTextRange(this.rustEditor.document, astLine.text);
109+
if (!rustRange) continue;
110+
111+
buf.push([rustRange, this.findAstRange(astLine)]);
112+
}
113+
114+
console.timeEnd("Build goto def index");
115+
return buf;
116+
});
117+
94118
constructor(ctx: Ctx) {
95119
ctx.pushCleanup(vscode.languages.registerHoverProvider({ scheme: AST_FILE_SCHEME }, this));
120+
ctx.pushCleanup(vscode.languages.registerDefinitionProvider({ language: "rust" }, this));
96121
vscode.workspace.onDidCloseTextDocument(this.onDidCloseTextDocument, this, ctx.subscriptions);
122+
vscode.workspace.onDidChangeTextDocument(this.onDidChangeTextDocument, this, ctx.subscriptions);
97123
vscode.window.onDidChangeVisibleTextEditors(this.onDidChangeVisibleTextEditors, this, ctx.subscriptions);
98124

99125
ctx.pushCleanup(this);
@@ -102,27 +128,64 @@ class AstInspector implements vscode.HoverProvider, Disposable {
102128
this.setRustEditor(undefined);
103129
}
104130

131+
private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) {
132+
if (this.rustEditor && event.document.uri.toString() === this.rustEditor.document.uri.toString()) {
133+
this.rust2Ast.reset();
134+
}
135+
}
136+
105137
private onDidCloseTextDocument(doc: vscode.TextDocument) {
106138
if (this.rustEditor && doc.uri.toString() === this.rustEditor.document.uri.toString()) {
107139
this.setRustEditor(undefined);
108140
}
109141
}
110142

111143
private onDidChangeVisibleTextEditors(editors: vscode.TextEditor[]) {
112-
if (editors.every(suspect => suspect.document.uri.scheme !== AST_FILE_SCHEME)) {
144+
if (!this.findAstTextEditor()) {
113145
this.setRustEditor(undefined);
114146
return;
115147
}
116148
this.setRustEditor(editors.find(isRustEditor));
117149
}
118150

151+
private findAstTextEditor(): undefined | vscode.TextEditor {
152+
return vscode.window.visibleTextEditors.find(it => it.document.uri.scheme === AST_FILE_SCHEME);
153+
}
154+
119155
private setRustEditor(newRustEditor: undefined | RustEditor) {
120-
if (newRustEditor !== this.rustEditor) {
121-
this.rustEditor?.setDecorations(AstInspector.astDecorationType, []);
156+
if (this.rustEditor && this.rustEditor !== newRustEditor) {
157+
this.rustEditor.setDecorations(this.astDecorationType, []);
158+
this.rust2Ast.reset();
122159
}
123160
this.rustEditor = newRustEditor;
124161
}
125162

163+
// additional positional params are omitted
164+
provideDefinition(doc: vscode.TextDocument, pos: vscode.Position): vscode.ProviderResult<vscode.DefinitionLink[]> {
165+
if (!this.rustEditor || doc.uri.toString() !== this.rustEditor.document.uri.toString()) return;
166+
167+
const astEditor = this.findAstTextEditor();
168+
if (!astEditor) return;
169+
170+
console.time("Goto def");
171+
const rust2AstRanges = this.rust2Ast.get()?.find(([rustRange, _]) => rustRange.contains(pos));
172+
console.timeEnd("Goto def");
173+
if (!rust2AstRanges) return;
174+
175+
const [rustFileRange, astFileRange] = rust2AstRanges;
176+
177+
astEditor.revealRange(astFileRange);
178+
astEditor.selection = new vscode.Selection(astFileRange.start, astFileRange.end);
179+
180+
return [{
181+
targetRange: astFileRange,
182+
targetUri: astEditor.document.uri,
183+
originSelectionRange: rustFileRange,
184+
targetSelectionRange: astFileRange,
185+
}];
186+
}
187+
188+
// additional positional params are omitted
126189
provideHover(doc: vscode.TextDocument, hoverPosition: vscode.Position): vscode.ProviderResult<vscode.Hover> {
127190
if (!this.rustEditor) return;
128191

@@ -131,7 +194,7 @@ class AstInspector implements vscode.HoverProvider, Disposable {
131194
const rustTextRange = this.parseRustTextRange(this.rustEditor.document, astTextLine.text);
132195
if (!rustTextRange) return;
133196

134-
this.rustEditor.setDecorations(AstInspector.astDecorationType, [rustTextRange]);
197+
this.rustEditor.setDecorations(this.astDecorationType, [rustTextRange]);
135198
this.rustEditor.revealRange(rustTextRange);
136199

137200
const rustSourceCode = this.rustEditor.document.getText(rustTextRange);
@@ -156,3 +219,18 @@ class AstInspector implements vscode.HoverProvider, Disposable {
156219
return new vscode.Range(begin, end);
157220
}
158221
}
222+
223+
class Lazy<T> {
224+
val: undefined | T;
225+
226+
constructor(private readonly compute: () => undefined | T) {}
227+
228+
get() {
229+
return this.val ?? (this.val = this.compute());
230+
}
231+
232+
reset() {
233+
this.val = undefined;
234+
}
235+
236+
}

0 commit comments

Comments
 (0)