aboutsummaryrefslogtreecommitdiff
path: root/editors/code/src
diff options
context:
space:
mode:
Diffstat (limited to 'editors/code/src')
-rw-r--r--editors/code/src/commands/syntax_tree.ts88
1 files changed, 83 insertions, 5 deletions
diff --git a/editors/code/src/commands/syntax_tree.ts b/editors/code/src/commands/syntax_tree.ts
index 8d71cb39e..c1af7122f 100644
--- a/editors/code/src/commands/syntax_tree.ts
+++ b/editors/code/src/commands/syntax_tree.ts
@@ -83,7 +83,7 @@ class TextDocumentContentProvider implements vscode.TextDocumentContentProvider
83// FIXME: consider implementing this via the Tree View API? 83// FIXME: consider implementing this via the Tree View API?
84// https://code.visualstudio.com/api/extension-guides/tree-view 84// https://code.visualstudio.com/api/extension-guides/tree-view
85class AstInspector implements vscode.HoverProvider, Disposable { 85class AstInspector implements vscode.HoverProvider, Disposable {
86 private static readonly astDecorationType = vscode.window.createTextEditorDecorationType({ 86 private readonly astDecorationType = vscode.window.createTextEditorDecorationType({
87 borderColor: new vscode.ThemeColor('rust_analyzer.syntaxTreeBorder'), 87 borderColor: new vscode.ThemeColor('rust_analyzer.syntaxTreeBorder'),
88 borderStyle: "solid", 88 borderStyle: "solid",
89 borderWidth: "2px", 89 borderWidth: "2px",
@@ -91,9 +91,35 @@ class AstInspector implements vscode.HoverProvider, Disposable {
91 }); 91 });
92 private rustEditor: undefined | RustEditor; 92 private rustEditor: undefined | RustEditor;
93 93
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
94 constructor(ctx: Ctx) { 118 constructor(ctx: Ctx) {
95 ctx.pushCleanup(vscode.languages.registerHoverProvider({ scheme: AST_FILE_SCHEME }, this)); 119 ctx.pushCleanup(vscode.languages.registerHoverProvider({ scheme: AST_FILE_SCHEME }, this));
120 ctx.pushCleanup(vscode.languages.registerDefinitionProvider({ language: "rust" }, this));
96 vscode.workspace.onDidCloseTextDocument(this.onDidCloseTextDocument, this, ctx.subscriptions); 121 vscode.workspace.onDidCloseTextDocument(this.onDidCloseTextDocument, this, ctx.subscriptions);
122 vscode.workspace.onDidChangeTextDocument(this.onDidChangeTextDocument, this, ctx.subscriptions);
97 vscode.window.onDidChangeVisibleTextEditors(this.onDidChangeVisibleTextEditors, this, ctx.subscriptions); 123 vscode.window.onDidChangeVisibleTextEditors(this.onDidChangeVisibleTextEditors, this, ctx.subscriptions);
98 124
99 ctx.pushCleanup(this); 125 ctx.pushCleanup(this);
@@ -102,6 +128,12 @@ class AstInspector implements vscode.HoverProvider, Disposable {
102 this.setRustEditor(undefined); 128 this.setRustEditor(undefined);
103 } 129 }
104 130
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
105 private onDidCloseTextDocument(doc: vscode.TextDocument) { 137 private onDidCloseTextDocument(doc: vscode.TextDocument) {
106 if (this.rustEditor && doc.uri.toString() === this.rustEditor.document.uri.toString()) { 138 if (this.rustEditor && doc.uri.toString() === this.rustEditor.document.uri.toString()) {
107 this.setRustEditor(undefined); 139 this.setRustEditor(undefined);
@@ -109,20 +141,51 @@ class AstInspector implements vscode.HoverProvider, Disposable {
109 } 141 }
110 142
111 private onDidChangeVisibleTextEditors(editors: vscode.TextEditor[]) { 143 private onDidChangeVisibleTextEditors(editors: vscode.TextEditor[]) {
112 if (editors.every(suspect => suspect.document.uri.scheme !== AST_FILE_SCHEME)) { 144 if (!this.findAstTextEditor()) {
113 this.setRustEditor(undefined); 145 this.setRustEditor(undefined);
114 return; 146 return;
115 } 147 }
116 this.setRustEditor(editors.find(isRustEditor)); 148 this.setRustEditor(editors.find(isRustEditor));
117 } 149 }
118 150
151 private findAstTextEditor(): undefined | vscode.TextEditor {
152 return vscode.window.visibleTextEditors.find(it => it.document.uri.scheme === AST_FILE_SCHEME);
153 }
154
119 private setRustEditor(newRustEditor: undefined | RustEditor) { 155 private setRustEditor(newRustEditor: undefined | RustEditor) {
120 if (newRustEditor !== this.rustEditor) { 156 if (this.rustEditor && this.rustEditor !== newRustEditor) {
121 this.rustEditor?.setDecorations(AstInspector.astDecorationType, []); 157 this.rustEditor.setDecorations(this.astDecorationType, []);
158 this.rust2Ast.reset();
122 } 159 }
123 this.rustEditor = newRustEditor; 160 this.rustEditor = newRustEditor;
124 } 161 }
125 162
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
126 provideHover(doc: vscode.TextDocument, hoverPosition: vscode.Position): vscode.ProviderResult<vscode.Hover> { 189 provideHover(doc: vscode.TextDocument, hoverPosition: vscode.Position): vscode.ProviderResult<vscode.Hover> {
127 if (!this.rustEditor) return; 190 if (!this.rustEditor) return;
128 191
@@ -131,7 +194,7 @@ class AstInspector implements vscode.HoverProvider, Disposable {
131 const rustTextRange = this.parseRustTextRange(this.rustEditor.document, astTextLine.text); 194 const rustTextRange = this.parseRustTextRange(this.rustEditor.document, astTextLine.text);
132 if (!rustTextRange) return; 195 if (!rustTextRange) return;
133 196
134 this.rustEditor.setDecorations(AstInspector.astDecorationType, [rustTextRange]); 197 this.rustEditor.setDecorations(this.astDecorationType, [rustTextRange]);
135 this.rustEditor.revealRange(rustTextRange); 198 this.rustEditor.revealRange(rustTextRange);
136 199
137 const rustSourceCode = this.rustEditor.document.getText(rustTextRange); 200 const rustSourceCode = this.rustEditor.document.getText(rustTextRange);
@@ -156,3 +219,18 @@ class AstInspector implements vscode.HoverProvider, Disposable {
156 return new vscode.Range(begin, end); 219 return new vscode.Range(begin, end);
157 } 220 }
158} 221}
222
223class 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}