diff options
author | veetaha <[email protected]> | 2020-04-02 01:24:30 +0100 |
---|---|---|
committer | veetaha <[email protected]> | 2020-04-02 09:13:16 +0100 |
commit | d453281bb2b5608c5655f116b7722a2514063555 (patch) | |
tree | ab05167036b984972475265983e73f042eb55fa8 /editors/code/src | |
parent | 98f7842e408587fdaca3d15d8eda677b689a035a (diff) |
vscode: add goto definition from rust file to syntax tree editor
Diffstat (limited to 'editors/code/src')
-rw-r--r-- | editors/code/src/commands/syntax_tree.ts | 88 |
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 |
85 | class AstInspector implements vscode.HoverProvider, Disposable { | 85 | class 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 | |||
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 | } | ||