diff options
Diffstat (limited to 'editors/code/src/commands')
-rw-r--r-- | editors/code/src/commands/runnables.ts | 4 | ||||
-rw-r--r-- | editors/code/src/commands/syntax_tree.ts | 111 |
2 files changed, 98 insertions, 17 deletions
diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/commands/runnables.ts index 357155163..2635a1440 100644 --- a/editors/code/src/commands/runnables.ts +++ b/editors/code/src/commands/runnables.ts | |||
@@ -66,6 +66,10 @@ export function debugSingle(ctx: Ctx): Cmd { | |||
66 | return async (config: ra.Runnable) => { | 66 | return async (config: ra.Runnable) => { |
67 | const editor = ctx.activeRustEditor; | 67 | const editor = ctx.activeRustEditor; |
68 | if (!editor) return; | 68 | if (!editor) return; |
69 | if (!vscode.extensions.getExtension("vadimcn.vscode-lldb")) { | ||
70 | vscode.window.showErrorMessage("Install `vadimcn.vscode-lldb` extension for debugging"); | ||
71 | return; | ||
72 | } | ||
69 | 73 | ||
70 | const debugConfig = { | 74 | const debugConfig = { |
71 | type: "lldb", | 75 | type: "lldb", |
diff --git a/editors/code/src/commands/syntax_tree.ts b/editors/code/src/commands/syntax_tree.ts index 996c7a716..b7a397414 100644 --- a/editors/code/src/commands/syntax_tree.ts +++ b/editors/code/src/commands/syntax_tree.ts | |||
@@ -15,6 +15,9 @@ export function syntaxTree(ctx: Ctx): Cmd { | |||
15 | void new AstInspector(ctx); | 15 | void new AstInspector(ctx); |
16 | 16 | ||
17 | ctx.pushCleanup(vscode.workspace.registerTextDocumentContentProvider(AST_FILE_SCHEME, tdcp)); | 17 | ctx.pushCleanup(vscode.workspace.registerTextDocumentContentProvider(AST_FILE_SCHEME, tdcp)); |
18 | ctx.pushCleanup(vscode.languages.setLanguageConfiguration("ra_syntax_tree", { | ||
19 | brackets: [["[", ")"]], | ||
20 | })); | ||
18 | 21 | ||
19 | return async () => { | 22 | return async () => { |
20 | const editor = vscode.window.activeTextEditor; | 23 | const editor = vscode.window.activeTextEditor; |
@@ -36,7 +39,7 @@ export function syntaxTree(ctx: Ctx): Cmd { | |||
36 | } | 39 | } |
37 | 40 | ||
38 | class TextDocumentContentProvider implements vscode.TextDocumentContentProvider { | 41 | class TextDocumentContentProvider implements vscode.TextDocumentContentProvider { |
39 | readonly uri = vscode.Uri.parse('rust-analyzer://syntaxtree'); | 42 | readonly uri = vscode.Uri.parse('rust-analyzer://syntaxtree/tree.rast'); |
40 | readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>(); | 43 | readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>(); |
41 | 44 | ||
42 | 45 | ||
@@ -79,16 +82,41 @@ class TextDocumentContentProvider implements vscode.TextDocumentContentProvider | |||
79 | 82 | ||
80 | // FIXME: consider implementing this via the Tree View API? | 83 | // FIXME: consider implementing this via the Tree View API? |
81 | // https://code.visualstudio.com/api/extension-guides/tree-view | 84 | // https://code.visualstudio.com/api/extension-guides/tree-view |
82 | class AstInspector implements vscode.HoverProvider, Disposable { | 85 | class AstInspector implements vscode.HoverProvider, vscode.DefinitionProvider, Disposable { |
83 | private static readonly astDecorationType = vscode.window.createTextEditorDecorationType({ | 86 | private readonly astDecorationType = vscode.window.createTextEditorDecorationType({ |
84 | fontStyle: "normal", | 87 | borderColor: new vscode.ThemeColor('rust_analyzer.syntaxTreeBorder'), |
85 | border: "#ffffff 1px solid", | 88 | borderStyle: "solid", |
89 | borderWidth: "2px", | ||
90 | |||
86 | }); | 91 | }); |
87 | private rustEditor: undefined | RustEditor; | 92 | private rustEditor: undefined | RustEditor; |
88 | 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 | const buf: [vscode.Range, vscode.Range][] = []; | ||
100 | for (let i = 0; i < astEditor.document.lineCount; ++i) { | ||
101 | const astLine = astEditor.document.lineAt(i); | ||
102 | |||
103 | // Heuristically look for nodes with quoted text (which are token nodes) | ||
104 | const isTokenNode = astLine.text.lastIndexOf('"') >= 0; | ||
105 | if (!isTokenNode) continue; | ||
106 | |||
107 | const rustRange = this.parseRustTextRange(this.rustEditor.document, astLine.text); | ||
108 | if (!rustRange) continue; | ||
109 | |||
110 | buf.push([rustRange, this.findAstNodeRange(astLine)]); | ||
111 | } | ||
112 | return buf; | ||
113 | }); | ||
114 | |||
89 | constructor(ctx: Ctx) { | 115 | constructor(ctx: Ctx) { |
90 | ctx.pushCleanup(vscode.languages.registerHoverProvider({ scheme: AST_FILE_SCHEME }, this)); | 116 | ctx.pushCleanup(vscode.languages.registerHoverProvider({ scheme: AST_FILE_SCHEME }, this)); |
117 | ctx.pushCleanup(vscode.languages.registerDefinitionProvider({ language: "rust" }, this)); | ||
91 | vscode.workspace.onDidCloseTextDocument(this.onDidCloseTextDocument, this, ctx.subscriptions); | 118 | vscode.workspace.onDidCloseTextDocument(this.onDidCloseTextDocument, this, ctx.subscriptions); |
119 | vscode.workspace.onDidChangeTextDocument(this.onDidChangeTextDocument, this, ctx.subscriptions); | ||
92 | vscode.window.onDidChangeVisibleTextEditors(this.onDidChangeVisibleTextEditors, this, ctx.subscriptions); | 120 | vscode.window.onDidChangeVisibleTextEditors(this.onDidChangeVisibleTextEditors, this, ctx.subscriptions); |
93 | 121 | ||
94 | ctx.pushCleanup(this); | 122 | ctx.pushCleanup(this); |
@@ -97,6 +125,12 @@ class AstInspector implements vscode.HoverProvider, Disposable { | |||
97 | this.setRustEditor(undefined); | 125 | this.setRustEditor(undefined); |
98 | } | 126 | } |
99 | 127 | ||
128 | private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) { | ||
129 | if (this.rustEditor && event.document.uri.toString() === this.rustEditor.document.uri.toString()) { | ||
130 | this.rust2Ast.reset(); | ||
131 | } | ||
132 | } | ||
133 | |||
100 | private onDidCloseTextDocument(doc: vscode.TextDocument) { | 134 | private onDidCloseTextDocument(doc: vscode.TextDocument) { |
101 | if (this.rustEditor && doc.uri.toString() === this.rustEditor.document.uri.toString()) { | 135 | if (this.rustEditor && doc.uri.toString() === this.rustEditor.document.uri.toString()) { |
102 | this.setRustEditor(undefined); | 136 | this.setRustEditor(undefined); |
@@ -104,38 +138,67 @@ class AstInspector implements vscode.HoverProvider, Disposable { | |||
104 | } | 138 | } |
105 | 139 | ||
106 | private onDidChangeVisibleTextEditors(editors: vscode.TextEditor[]) { | 140 | private onDidChangeVisibleTextEditors(editors: vscode.TextEditor[]) { |
107 | if (editors.every(suspect => suspect.document.uri.scheme !== AST_FILE_SCHEME)) { | 141 | if (!this.findAstTextEditor()) { |
108 | this.setRustEditor(undefined); | 142 | this.setRustEditor(undefined); |
109 | return; | 143 | return; |
110 | } | 144 | } |
111 | this.setRustEditor(editors.find(isRustEditor)); | 145 | this.setRustEditor(editors.find(isRustEditor)); |
112 | } | 146 | } |
113 | 147 | ||
148 | private findAstTextEditor(): undefined | vscode.TextEditor { | ||
149 | return vscode.window.visibleTextEditors.find(it => it.document.uri.scheme === AST_FILE_SCHEME); | ||
150 | } | ||
151 | |||
114 | private setRustEditor(newRustEditor: undefined | RustEditor) { | 152 | private setRustEditor(newRustEditor: undefined | RustEditor) { |
115 | if (newRustEditor !== this.rustEditor) { | 153 | if (this.rustEditor && this.rustEditor !== newRustEditor) { |
116 | this.rustEditor?.setDecorations(AstInspector.astDecorationType, []); | 154 | this.rustEditor.setDecorations(this.astDecorationType, []); |
155 | this.rust2Ast.reset(); | ||
117 | } | 156 | } |
118 | this.rustEditor = newRustEditor; | 157 | this.rustEditor = newRustEditor; |
119 | } | 158 | } |
120 | 159 | ||
160 | // additional positional params are omitted | ||
161 | provideDefinition(doc: vscode.TextDocument, pos: vscode.Position): vscode.ProviderResult<vscode.DefinitionLink[]> { | ||
162 | if (!this.rustEditor || doc.uri.toString() !== this.rustEditor.document.uri.toString()) return; | ||
163 | |||
164 | const astEditor = this.findAstTextEditor(); | ||
165 | if (!astEditor) return; | ||
166 | |||
167 | const rust2AstRanges = this.rust2Ast.get()?.find(([rustRange, _]) => rustRange.contains(pos)); | ||
168 | if (!rust2AstRanges) return; | ||
169 | |||
170 | const [rustFileRange, astFileRange] = rust2AstRanges; | ||
171 | |||
172 | astEditor.revealRange(astFileRange); | ||
173 | astEditor.selection = new vscode.Selection(astFileRange.start, astFileRange.end); | ||
174 | |||
175 | return [{ | ||
176 | targetRange: astFileRange, | ||
177 | targetUri: astEditor.document.uri, | ||
178 | originSelectionRange: rustFileRange, | ||
179 | targetSelectionRange: astFileRange, | ||
180 | }]; | ||
181 | } | ||
182 | |||
183 | // additional positional params are omitted | ||
121 | provideHover(doc: vscode.TextDocument, hoverPosition: vscode.Position): vscode.ProviderResult<vscode.Hover> { | 184 | provideHover(doc: vscode.TextDocument, hoverPosition: vscode.Position): vscode.ProviderResult<vscode.Hover> { |
122 | if (!this.rustEditor) return; | 185 | if (!this.rustEditor) return; |
123 | 186 | ||
124 | const astTextLine = doc.lineAt(hoverPosition.line); | 187 | const astFileLine = doc.lineAt(hoverPosition.line); |
125 | 188 | ||
126 | const rustTextRange = this.parseRustTextRange(this.rustEditor.document, astTextLine.text); | 189 | const rustFileRange = this.parseRustTextRange(this.rustEditor.document, astFileLine.text); |
127 | if (!rustTextRange) return; | 190 | if (!rustFileRange) return; |
128 | 191 | ||
129 | this.rustEditor.setDecorations(AstInspector.astDecorationType, [rustTextRange]); | 192 | this.rustEditor.setDecorations(this.astDecorationType, [rustFileRange]); |
130 | this.rustEditor.revealRange(rustTextRange); | 193 | this.rustEditor.revealRange(rustFileRange); |
131 | 194 | ||
132 | const rustSourceCode = this.rustEditor.document.getText(rustTextRange); | 195 | const rustSourceCode = this.rustEditor.document.getText(rustFileRange); |
133 | const astTextRange = this.findAstRange(astTextLine); | 196 | const astFileRange = this.findAstNodeRange(astFileLine); |
134 | 197 | ||
135 | return new vscode.Hover(["```rust\n" + rustSourceCode + "\n```"], astTextRange); | 198 | return new vscode.Hover(["```rust\n" + rustSourceCode + "\n```"], astFileRange); |
136 | } | 199 | } |
137 | 200 | ||
138 | private findAstRange(astLine: vscode.TextLine) { | 201 | private findAstNodeRange(astLine: vscode.TextLine) { |
139 | const lineOffset = astLine.range.start; | 202 | const lineOffset = astLine.range.start; |
140 | const begin = lineOffset.translate(undefined, astLine.firstNonWhitespaceCharacterIndex); | 203 | const begin = lineOffset.translate(undefined, astLine.firstNonWhitespaceCharacterIndex); |
141 | const end = lineOffset.translate(undefined, astLine.text.trimEnd().length); | 204 | const end = lineOffset.translate(undefined, astLine.text.trimEnd().length); |
@@ -151,3 +214,17 @@ class AstInspector implements vscode.HoverProvider, Disposable { | |||
151 | return new vscode.Range(begin, end); | 214 | return new vscode.Range(begin, end); |
152 | } | 215 | } |
153 | } | 216 | } |
217 | |||
218 | class Lazy<T> { | ||
219 | val: undefined | T; | ||
220 | |||
221 | constructor(private readonly compute: () => undefined | T) { } | ||
222 | |||
223 | get() { | ||
224 | return this.val ?? (this.val = this.compute()); | ||
225 | } | ||
226 | |||
227 | reset() { | ||
228 | this.val = undefined; | ||
229 | } | ||
230 | } | ||