diff options
Diffstat (limited to 'editors/code')
-rw-r--r-- | editors/code/package-lock.json | 59 | ||||
-rw-r--r-- | editors/code/package.json | 8 | ||||
-rw-r--r-- | editors/code/src/client.ts | 5 | ||||
-rw-r--r-- | editors/code/src/commands/syntax_tree.ts | 165 | ||||
-rw-r--r-- | editors/code/src/ctx.ts | 9 | ||||
-rw-r--r-- | editors/code/src/main.ts | 12 | ||||
-rw-r--r-- | editors/code/src/tasks.ts | 52 | ||||
-rw-r--r-- | editors/code/src/util.ts | 4 |
8 files changed, 226 insertions, 88 deletions
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json index b7220d8e2..955a05066 100644 --- a/editors/code/package-lock.json +++ b/editors/code/package-lock.json | |||
@@ -84,9 +84,9 @@ | |||
84 | "dev": true | 84 | "dev": true |
85 | }, | 85 | }, |
86 | "@types/node": { | 86 | "@types/node": { |
87 | "version": "12.12.30", | 87 | "version": "12.12.34", |
88 | "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.30.tgz", | 88 | "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.34.tgz", |
89 | "integrity": "sha512-sz9MF/zk6qVr3pAnM0BSQvYIBK44tS75QC5N+VbWSE4DjCV/pJ+UzCW/F+vVnl7TkOPcuwQureKNtSSwjBTaMg==", | 89 | "integrity": "sha512-BneGN0J9ke24lBRn44hVHNeDlrXRYF+VRp0HbSUNnEZahXGAysHZIqnf/hER6aabdBgzM4YOV4jrR8gj4Zfi0g==", |
90 | "dev": true | 90 | "dev": true |
91 | }, | 91 | }, |
92 | "@types/node-fetch": { | 92 | "@types/node-fetch": { |
@@ -115,45 +115,56 @@ | |||
115 | "dev": true | 115 | "dev": true |
116 | }, | 116 | }, |
117 | "@typescript-eslint/eslint-plugin": { | 117 | "@typescript-eslint/eslint-plugin": { |
118 | "version": "2.24.0", | 118 | "version": "2.26.0", |
119 | "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.24.0.tgz", | 119 | "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.26.0.tgz", |
120 | "integrity": "sha512-wJRBeaMeT7RLQ27UQkDFOu25MqFOBus8PtOa9KaT5ZuxC1kAsd7JEHqWt4YXuY9eancX0GK9C68i5OROnlIzBA==", | 120 | "integrity": "sha512-4yUnLv40bzfzsXcTAtZyTjbiGUXMrcIJcIMioI22tSOyAxpdXiZ4r7YQUU8Jj6XXrLz9d5aMHPQf5JFR7h27Nw==", |
121 | "dev": true, | 121 | "dev": true, |
122 | "requires": { | 122 | "requires": { |
123 | "@typescript-eslint/experimental-utils": "2.24.0", | 123 | "@typescript-eslint/experimental-utils": "2.26.0", |
124 | "eslint-utils": "^1.4.3", | ||
125 | "functional-red-black-tree": "^1.0.1", | 124 | "functional-red-black-tree": "^1.0.1", |
126 | "regexpp": "^3.0.0", | 125 | "regexpp": "^3.0.0", |
127 | "tsutils": "^3.17.1" | 126 | "tsutils": "^3.17.1" |
128 | } | 127 | } |
129 | }, | 128 | }, |
130 | "@typescript-eslint/experimental-utils": { | 129 | "@typescript-eslint/experimental-utils": { |
131 | "version": "2.24.0", | 130 | "version": "2.26.0", |
132 | "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.24.0.tgz", | 131 | "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.26.0.tgz", |
133 | "integrity": "sha512-DXrwuXTdVh3ycNCMYmWhUzn/gfqu9N0VzNnahjiDJvcyhfBy4gb59ncVZVxdp5XzBC77dCncu0daQgOkbvPwBw==", | 132 | "integrity": "sha512-RELVoH5EYd+JlGprEyojUv9HeKcZqF7nZUGSblyAw1FwOGNnmQIU8kxJ69fttQvEwCsX5D6ECJT8GTozxrDKVQ==", |
134 | "dev": true, | 133 | "dev": true, |
135 | "requires": { | 134 | "requires": { |
136 | "@types/json-schema": "^7.0.3", | 135 | "@types/json-schema": "^7.0.3", |
137 | "@typescript-eslint/typescript-estree": "2.24.0", | 136 | "@typescript-eslint/typescript-estree": "2.26.0", |
138 | "eslint-scope": "^5.0.0" | 137 | "eslint-scope": "^5.0.0", |
138 | "eslint-utils": "^2.0.0" | ||
139 | }, | ||
140 | "dependencies": { | ||
141 | "eslint-utils": { | ||
142 | "version": "2.0.0", | ||
143 | "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.0.0.tgz", | ||
144 | "integrity": "sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA==", | ||
145 | "dev": true, | ||
146 | "requires": { | ||
147 | "eslint-visitor-keys": "^1.1.0" | ||
148 | } | ||
149 | } | ||
139 | } | 150 | } |
140 | }, | 151 | }, |
141 | "@typescript-eslint/parser": { | 152 | "@typescript-eslint/parser": { |
142 | "version": "2.24.0", | 153 | "version": "2.26.0", |
143 | "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.24.0.tgz", | 154 | "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.26.0.tgz", |
144 | "integrity": "sha512-H2Y7uacwSSg8IbVxdYExSI3T7uM1DzmOn2COGtCahCC3g8YtM1xYAPi2MAHyfPs61VKxP/J/UiSctcRgw4G8aw==", | 155 | "integrity": "sha512-+Xj5fucDtdKEVGSh9353wcnseMRkPpEAOY96EEenN7kJVrLqy/EVwtIh3mxcUz8lsFXW1mT5nN5vvEam/a5HiQ==", |
145 | "dev": true, | 156 | "dev": true, |
146 | "requires": { | 157 | "requires": { |
147 | "@types/eslint-visitor-keys": "^1.0.0", | 158 | "@types/eslint-visitor-keys": "^1.0.0", |
148 | "@typescript-eslint/experimental-utils": "2.24.0", | 159 | "@typescript-eslint/experimental-utils": "2.26.0", |
149 | "@typescript-eslint/typescript-estree": "2.24.0", | 160 | "@typescript-eslint/typescript-estree": "2.26.0", |
150 | "eslint-visitor-keys": "^1.1.0" | 161 | "eslint-visitor-keys": "^1.1.0" |
151 | } | 162 | } |
152 | }, | 163 | }, |
153 | "@typescript-eslint/typescript-estree": { | 164 | "@typescript-eslint/typescript-estree": { |
154 | "version": "2.24.0", | 165 | "version": "2.26.0", |
155 | "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.24.0.tgz", | 166 | "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.26.0.tgz", |
156 | "integrity": "sha512-RJ0yMe5owMSix55qX7Mi9V6z2FDuuDpN6eR5fzRJrp+8in9UF41IGNQHbg5aMK4/PjVaEQksLvz0IA8n+Mr/FA==", | 167 | "integrity": "sha512-3x4SyZCLB4zsKsjuhxDLeVJN6W29VwBnYpCsZ7vIdPel9ZqLfIZJgJXO47MNUkurGpQuIBALdPQKtsSnWpE1Yg==", |
157 | "dev": true, | 168 | "dev": true, |
158 | "requires": { | 169 | "requires": { |
159 | "debug": "^4.1.1", | 170 | "debug": "^4.1.1", |
@@ -1388,9 +1399,9 @@ | |||
1388 | } | 1399 | } |
1389 | }, | 1400 | }, |
1390 | "rollup": { | 1401 | "rollup": { |
1391 | "version": "2.1.0", | 1402 | "version": "2.3.1", |
1392 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.1.0.tgz", | 1403 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.3.1.tgz", |
1393 | "integrity": "sha512-gfE1455AEazVVTJoeQtcOq/U6GSxwoj4XPSWVsuWmgIxj7sBQNLDOSA82PbdMe+cP8ql8fR1jogPFe8Wg8g4SQ==", | 1404 | "integrity": "sha512-BRjzOauORe+R0U0I6SkMTSG22nYmtztR/TaBl0SvbXgc3VAxBDrZoB6HROiK0S5px1pUBnLnjBkbzmVuwC9Q1Q==", |
1394 | "dev": true, | 1405 | "dev": true, |
1395 | "requires": { | 1406 | "requires": { |
1396 | "fsevents": "~2.1.2" | 1407 | "fsevents": "~2.1.2" |
diff --git a/editors/code/package.json b/editors/code/package.json index 37e083220..ba31c4e63 100644 --- a/editors/code/package.json +++ b/editors/code/package.json | |||
@@ -39,13 +39,13 @@ | |||
39 | "devDependencies": { | 39 | "devDependencies": { |
40 | "@rollup/plugin-commonjs": "^11.0.2", | 40 | "@rollup/plugin-commonjs": "^11.0.2", |
41 | "@rollup/plugin-node-resolve": "^7.1.1", | 41 | "@rollup/plugin-node-resolve": "^7.1.1", |
42 | "@types/node": "^12.12.30", | 42 | "@types/node": "^12.12.34", |
43 | "@types/node-fetch": "^2.5.5", | 43 | "@types/node-fetch": "^2.5.5", |
44 | "@types/vscode": "^1.43.0", | 44 | "@types/vscode": "^1.43.0", |
45 | "@typescript-eslint/eslint-plugin": "^2.24.0", | 45 | "@typescript-eslint/eslint-plugin": "^2.26.0", |
46 | "@typescript-eslint/parser": "^2.24.0", | 46 | "@typescript-eslint/parser": "^2.26.0", |
47 | "eslint": "^6.8.0", | 47 | "eslint": "^6.8.0", |
48 | "rollup": "^2.1.0", | 48 | "rollup": "^2.3.1", |
49 | "tslib": "^1.11.1", | 49 | "tslib": "^1.11.1", |
50 | "typescript": "^3.8.3", | 50 | "typescript": "^3.8.3", |
51 | "typescript-formatter": "^7.2.2", | 51 | "typescript-formatter": "^7.2.2", |
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index d72ecc58f..f909f8db2 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts | |||
@@ -30,15 +30,14 @@ export function configToServerOptions(config: Config) { | |||
30 | }; | 30 | }; |
31 | } | 31 | } |
32 | 32 | ||
33 | export async function createClient(config: Config, serverPath: string): Promise<lc.LanguageClient> { | 33 | export async function createClient(config: Config, serverPath: string, cwd: string): Promise<lc.LanguageClient> { |
34 | // '.' Is the fallback if no folder is open | 34 | // '.' Is the fallback if no folder is open |
35 | // TODO?: Workspace folders support Uri's (eg: file://test.txt). | 35 | // TODO?: Workspace folders support Uri's (eg: file://test.txt). |
36 | // It might be a good idea to test if the uri points to a file. | 36 | // It might be a good idea to test if the uri points to a file. |
37 | const workspaceFolderPath = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath ?? '.'; | ||
38 | 37 | ||
39 | const run: lc.Executable = { | 38 | const run: lc.Executable = { |
40 | command: serverPath, | 39 | command: serverPath, |
41 | options: { cwd: workspaceFolderPath }, | 40 | options: { cwd }, |
42 | }; | 41 | }; |
43 | const serverOptions: lc.ServerOptions = { | 42 | const serverOptions: lc.ServerOptions = { |
44 | run, | 43 | run, |
diff --git a/editors/code/src/commands/syntax_tree.ts b/editors/code/src/commands/syntax_tree.ts index 2e08e8f11..996c7a716 100644 --- a/editors/code/src/commands/syntax_tree.ts +++ b/editors/code/src/commands/syntax_tree.ts | |||
@@ -1,8 +1,10 @@ | |||
1 | import * as vscode from 'vscode'; | 1 | import * as vscode from 'vscode'; |
2 | import * as ra from '../rust-analyzer-api'; | 2 | import * as ra from '../rust-analyzer-api'; |
3 | 3 | ||
4 | import { Ctx, Cmd } from '../ctx'; | 4 | import { Ctx, Cmd, Disposable } from '../ctx'; |
5 | import { isRustDocument } from '../util'; | 5 | import { isRustDocument, RustEditor, isRustEditor, sleep } from '../util'; |
6 | |||
7 | const AST_FILE_SCHEME = "rust-analyzer"; | ||
6 | 8 | ||
7 | // Opens the virtual file that will show the syntax tree | 9 | // Opens the virtual file that will show the syntax tree |
8 | // | 10 | // |
@@ -10,35 +12,13 @@ import { isRustDocument } from '../util'; | |||
10 | export function syntaxTree(ctx: Ctx): Cmd { | 12 | export function syntaxTree(ctx: Ctx): Cmd { |
11 | const tdcp = new TextDocumentContentProvider(ctx); | 13 | const tdcp = new TextDocumentContentProvider(ctx); |
12 | 14 | ||
13 | ctx.pushCleanup( | 15 | void new AstInspector(ctx); |
14 | vscode.workspace.registerTextDocumentContentProvider( | 16 | |
15 | 'rust-analyzer', | 17 | ctx.pushCleanup(vscode.workspace.registerTextDocumentContentProvider(AST_FILE_SCHEME, tdcp)); |
16 | tdcp, | ||
17 | ), | ||
18 | ); | ||
19 | |||
20 | vscode.workspace.onDidChangeTextDocument( | ||
21 | (event: vscode.TextDocumentChangeEvent) => { | ||
22 | const doc = event.document; | ||
23 | if (!isRustDocument(doc)) return; | ||
24 | afterLs(() => tdcp.eventEmitter.fire(tdcp.uri)); | ||
25 | }, | ||
26 | null, | ||
27 | ctx.subscriptions, | ||
28 | ); | ||
29 | |||
30 | vscode.window.onDidChangeActiveTextEditor( | ||
31 | (editor: vscode.TextEditor | undefined) => { | ||
32 | if (!editor || !isRustDocument(editor.document)) return; | ||
33 | tdcp.eventEmitter.fire(tdcp.uri); | ||
34 | }, | ||
35 | null, | ||
36 | ctx.subscriptions, | ||
37 | ); | ||
38 | 18 | ||
39 | return async () => { | 19 | return async () => { |
40 | const editor = vscode.window.activeTextEditor; | 20 | const editor = vscode.window.activeTextEditor; |
41 | const rangeEnabled = !!(editor && !editor.selection.isEmpty); | 21 | const rangeEnabled = !!editor && !editor.selection.isEmpty; |
42 | 22 | ||
43 | const uri = rangeEnabled | 23 | const uri = rangeEnabled |
44 | ? vscode.Uri.parse(`${tdcp.uri.toString()}?range=true`) | 24 | ? vscode.Uri.parse(`${tdcp.uri.toString()}?range=true`) |
@@ -48,45 +28,126 @@ export function syntaxTree(ctx: Ctx): Cmd { | |||
48 | 28 | ||
49 | tdcp.eventEmitter.fire(uri); | 29 | tdcp.eventEmitter.fire(uri); |
50 | 30 | ||
51 | return vscode.window.showTextDocument( | 31 | void await vscode.window.showTextDocument(document, { |
52 | document, | 32 | viewColumn: vscode.ViewColumn.Two, |
53 | vscode.ViewColumn.Two, | 33 | preserveFocus: true |
54 | true, | 34 | }); |
55 | ); | ||
56 | }; | 35 | }; |
57 | } | 36 | } |
58 | 37 | ||
59 | // We need to order this after LS updates, but there's no API for that. | ||
60 | // Hence, good old setTimeout. | ||
61 | function afterLs(f: () => void) { | ||
62 | setTimeout(f, 10); | ||
63 | } | ||
64 | |||
65 | |||
66 | class TextDocumentContentProvider implements vscode.TextDocumentContentProvider { | 38 | class TextDocumentContentProvider implements vscode.TextDocumentContentProvider { |
67 | uri = vscode.Uri.parse('rust-analyzer://syntaxtree'); | 39 | readonly uri = vscode.Uri.parse('rust-analyzer://syntaxtree'); |
68 | eventEmitter = new vscode.EventEmitter<vscode.Uri>(); | 40 | readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>(); |
41 | |||
69 | 42 | ||
70 | constructor(private readonly ctx: Ctx) { | 43 | constructor(private readonly ctx: Ctx) { |
44 | vscode.workspace.onDidChangeTextDocument(this.onDidChangeTextDocument, this, ctx.subscriptions); | ||
45 | vscode.window.onDidChangeActiveTextEditor(this.onDidChangeActiveTextEditor, this, ctx.subscriptions); | ||
71 | } | 46 | } |
72 | 47 | ||
73 | provideTextDocumentContent(uri: vscode.Uri): vscode.ProviderResult<string> { | 48 | private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) { |
74 | const editor = vscode.window.activeTextEditor; | 49 | if (isRustDocument(event.document)) { |
75 | const client = this.ctx.client; | 50 | // We need to order this after language server updates, but there's no API for that. |
76 | if (!editor || !client) return ''; | 51 | // Hence, good old sleep(). |
52 | void sleep(10).then(() => this.eventEmitter.fire(this.uri)); | ||
53 | } | ||
54 | } | ||
55 | private onDidChangeActiveTextEditor(editor: vscode.TextEditor | undefined) { | ||
56 | if (editor && isRustEditor(editor)) { | ||
57 | this.eventEmitter.fire(this.uri); | ||
58 | } | ||
59 | } | ||
60 | |||
61 | provideTextDocumentContent(uri: vscode.Uri, ct: vscode.CancellationToken): vscode.ProviderResult<string> { | ||
62 | const rustEditor = this.ctx.activeRustEditor; | ||
63 | if (!rustEditor) return ''; | ||
77 | 64 | ||
78 | // When the range based query is enabled we take the range of the selection | 65 | // When the range based query is enabled we take the range of the selection |
79 | const range = uri.query === 'range=true' && !editor.selection.isEmpty | 66 | const range = uri.query === 'range=true' && !rustEditor.selection.isEmpty |
80 | ? client.code2ProtocolConverter.asRange(editor.selection) | 67 | ? this.ctx.client.code2ProtocolConverter.asRange(rustEditor.selection) |
81 | : null; | 68 | : null; |
82 | 69 | ||
83 | return client.sendRequest(ra.syntaxTree, { | 70 | const params = { textDocument: { uri: rustEditor.document.uri.toString() }, range, }; |
84 | textDocument: { uri: editor.document.uri.toString() }, | 71 | return this.ctx.client.sendRequest(ra.syntaxTree, params, ct); |
85 | range, | ||
86 | }); | ||
87 | } | 72 | } |
88 | 73 | ||
89 | get onDidChange(): vscode.Event<vscode.Uri> { | 74 | get onDidChange(): vscode.Event<vscode.Uri> { |
90 | return this.eventEmitter.event; | 75 | return this.eventEmitter.event; |
91 | } | 76 | } |
92 | } | 77 | } |
78 | |||
79 | |||
80 | // FIXME: consider implementing this via the Tree View API? | ||
81 | // https://code.visualstudio.com/api/extension-guides/tree-view | ||
82 | class AstInspector implements vscode.HoverProvider, Disposable { | ||
83 | private static readonly astDecorationType = vscode.window.createTextEditorDecorationType({ | ||
84 | fontStyle: "normal", | ||
85 | border: "#ffffff 1px solid", | ||
86 | }); | ||
87 | private rustEditor: undefined | RustEditor; | ||
88 | |||
89 | constructor(ctx: Ctx) { | ||
90 | ctx.pushCleanup(vscode.languages.registerHoverProvider({ scheme: AST_FILE_SCHEME }, this)); | ||
91 | vscode.workspace.onDidCloseTextDocument(this.onDidCloseTextDocument, this, ctx.subscriptions); | ||
92 | vscode.window.onDidChangeVisibleTextEditors(this.onDidChangeVisibleTextEditors, this, ctx.subscriptions); | ||
93 | |||
94 | ctx.pushCleanup(this); | ||
95 | } | ||
96 | dispose() { | ||
97 | this.setRustEditor(undefined); | ||
98 | } | ||
99 | |||
100 | private onDidCloseTextDocument(doc: vscode.TextDocument) { | ||
101 | if (this.rustEditor && doc.uri.toString() === this.rustEditor.document.uri.toString()) { | ||
102 | this.setRustEditor(undefined); | ||
103 | } | ||
104 | } | ||
105 | |||
106 | private onDidChangeVisibleTextEditors(editors: vscode.TextEditor[]) { | ||
107 | if (editors.every(suspect => suspect.document.uri.scheme !== AST_FILE_SCHEME)) { | ||
108 | this.setRustEditor(undefined); | ||
109 | return; | ||
110 | } | ||
111 | this.setRustEditor(editors.find(isRustEditor)); | ||
112 | } | ||
113 | |||
114 | private setRustEditor(newRustEditor: undefined | RustEditor) { | ||
115 | if (newRustEditor !== this.rustEditor) { | ||
116 | this.rustEditor?.setDecorations(AstInspector.astDecorationType, []); | ||
117 | } | ||
118 | this.rustEditor = newRustEditor; | ||
119 | } | ||
120 | |||
121 | provideHover(doc: vscode.TextDocument, hoverPosition: vscode.Position): vscode.ProviderResult<vscode.Hover> { | ||
122 | if (!this.rustEditor) return; | ||
123 | |||
124 | const astTextLine = doc.lineAt(hoverPosition.line); | ||
125 | |||
126 | const rustTextRange = this.parseRustTextRange(this.rustEditor.document, astTextLine.text); | ||
127 | if (!rustTextRange) return; | ||
128 | |||
129 | this.rustEditor.setDecorations(AstInspector.astDecorationType, [rustTextRange]); | ||
130 | this.rustEditor.revealRange(rustTextRange); | ||
131 | |||
132 | const rustSourceCode = this.rustEditor.document.getText(rustTextRange); | ||
133 | const astTextRange = this.findAstRange(astTextLine); | ||
134 | |||
135 | return new vscode.Hover(["```rust\n" + rustSourceCode + "\n```"], astTextRange); | ||
136 | } | ||
137 | |||
138 | private findAstRange(astLine: vscode.TextLine) { | ||
139 | const lineOffset = astLine.range.start; | ||
140 | const begin = lineOffset.translate(undefined, astLine.firstNonWhitespaceCharacterIndex); | ||
141 | const end = lineOffset.translate(undefined, astLine.text.trimEnd().length); | ||
142 | return new vscode.Range(begin, end); | ||
143 | } | ||
144 | |||
145 | private parseRustTextRange(doc: vscode.TextDocument, astLine: string): undefined | vscode.Range { | ||
146 | const parsedRange = /\[(\d+); (\d+)\)/.exec(astLine); | ||
147 | if (!parsedRange) return; | ||
148 | |||
149 | const [begin, end] = parsedRange.slice(1).map(off => doc.positionAt(+off)); | ||
150 | |||
151 | return new vscode.Range(begin, end); | ||
152 | } | ||
153 | } | ||
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index d2f49cd23..86b5f3629 100644 --- a/editors/code/src/ctx.ts +++ b/editors/code/src/ctx.ts | |||
@@ -15,8 +15,13 @@ export class Ctx { | |||
15 | 15 | ||
16 | } | 16 | } |
17 | 17 | ||
18 | static async create(config: Config, extCtx: vscode.ExtensionContext, serverPath: string): Promise<Ctx> { | 18 | static async create( |
19 | const client = await createClient(config, serverPath); | 19 | config: Config, |
20 | extCtx: vscode.ExtensionContext, | ||
21 | serverPath: string, | ||
22 | cwd: string, | ||
23 | ): Promise<Ctx> { | ||
24 | const client = await createClient(config, serverPath, cwd); | ||
20 | const res = new Ctx(config, extCtx, client, serverPath); | 25 | const res = new Ctx(config, extCtx, client, serverPath); |
21 | res.pushCleanup(client.start()); | 26 | res.pushCleanup(client.start()); |
22 | await client.onReady(); | 27 | await client.onReady(); |
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index a46dbde33..7ba16120c 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts | |||
@@ -13,6 +13,7 @@ import { log, assert } from './util'; | |||
13 | import { PersistentState } from './persistent_state'; | 13 | import { PersistentState } from './persistent_state'; |
14 | import { fetchRelease, download } from './net'; | 14 | import { fetchRelease, download } from './net'; |
15 | import { spawnSync } from 'child_process'; | 15 | import { spawnSync } from 'child_process'; |
16 | import { activateTaskProvider } from './tasks'; | ||
16 | 17 | ||
17 | let ctx: Ctx | undefined; | 18 | let ctx: Ctx | undefined; |
18 | 19 | ||
@@ -41,11 +42,18 @@ export async function activate(context: vscode.ExtensionContext) { | |||
41 | const state = new PersistentState(context.globalState); | 42 | const state = new PersistentState(context.globalState); |
42 | const serverPath = await bootstrap(config, state); | 43 | const serverPath = await bootstrap(config, state); |
43 | 44 | ||
45 | const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; | ||
46 | if (workspaceFolder === undefined) { | ||
47 | const err = "Cannot activate rust-analyzer when no folder is opened"; | ||
48 | void vscode.window.showErrorMessage(err); | ||
49 | throw new Error(err); | ||
50 | } | ||
51 | |||
44 | // Note: we try to start the server before we activate type hints so that it | 52 | // Note: we try to start the server before we activate type hints so that it |
45 | // registers its `onDidChangeDocument` handler before us. | 53 | // registers its `onDidChangeDocument` handler before us. |
46 | // | 54 | // |
47 | // This a horribly, horribly wrong way to deal with this problem. | 55 | // This a horribly, horribly wrong way to deal with this problem. |
48 | ctx = await Ctx.create(config, context, serverPath); | 56 | ctx = await Ctx.create(config, context, serverPath, workspaceFolder.uri.fsPath); |
49 | 57 | ||
50 | // Commands which invokes manually via command palette, shortcut, etc. | 58 | // Commands which invokes manually via command palette, shortcut, etc. |
51 | 59 | ||
@@ -85,6 +93,8 @@ export async function activate(context: vscode.ExtensionContext) { | |||
85 | ctx.registerCommand('applySourceChange', commands.applySourceChange); | 93 | ctx.registerCommand('applySourceChange', commands.applySourceChange); |
86 | ctx.registerCommand('selectAndApplySourceChange', commands.selectAndApplySourceChange); | 94 | ctx.registerCommand('selectAndApplySourceChange', commands.selectAndApplySourceChange); |
87 | 95 | ||
96 | ctx.pushCleanup(activateTaskProvider(workspaceFolder)); | ||
97 | |||
88 | activateStatusDisplay(ctx); | 98 | activateStatusDisplay(ctx); |
89 | 99 | ||
90 | if (!ctx.config.highlightingSemanticTokens) { | 100 | if (!ctx.config.highlightingSemanticTokens) { |
diff --git a/editors/code/src/tasks.ts b/editors/code/src/tasks.ts new file mode 100644 index 000000000..fa1c4a951 --- /dev/null +++ b/editors/code/src/tasks.ts | |||
@@ -0,0 +1,52 @@ | |||
1 | import * as vscode from 'vscode'; | ||
2 | |||
3 | // This ends up as the `type` key in tasks.json. RLS also uses `cargo` and | ||
4 | // our configuration should be compatible with it so use the same key. | ||
5 | const TASK_TYPE = 'cargo'; | ||
6 | |||
7 | export function activateTaskProvider(target: vscode.WorkspaceFolder): vscode.Disposable { | ||
8 | const provider: vscode.TaskProvider = { | ||
9 | // Detect Rust tasks. Currently we do not do any actual detection | ||
10 | // of tasks (e.g. aliases in .cargo/config) and just return a fixed | ||
11 | // set of tasks that always exist. These tasks cannot be removed in | ||
12 | // tasks.json - only tweaked. | ||
13 | provideTasks: () => getStandardCargoTasks(target), | ||
14 | |||
15 | // We don't need to implement this. | ||
16 | resolveTask: () => undefined, | ||
17 | }; | ||
18 | |||
19 | return vscode.tasks.registerTaskProvider(TASK_TYPE, provider); | ||
20 | } | ||
21 | |||
22 | function getStandardCargoTasks(target: vscode.WorkspaceFolder): vscode.Task[] { | ||
23 | return [ | ||
24 | { command: 'build', group: vscode.TaskGroup.Build }, | ||
25 | { command: 'check', group: vscode.TaskGroup.Build }, | ||
26 | { command: 'test', group: vscode.TaskGroup.Test }, | ||
27 | { command: 'clean', group: vscode.TaskGroup.Clean }, | ||
28 | { command: 'run', group: undefined }, | ||
29 | ] | ||
30 | .map(({ command, group }) => { | ||
31 | const vscodeTask = new vscode.Task( | ||
32 | // The contents of this object end up in the tasks.json entries. | ||
33 | { | ||
34 | type: TASK_TYPE, | ||
35 | command, | ||
36 | }, | ||
37 | // The scope of the task - workspace or specific folder (global | ||
38 | // is not supported). | ||
39 | target, | ||
40 | // The task name, and task source. These are shown in the UI as | ||
41 | // `${source}: ${name}`, e.g. `rust: cargo build`. | ||
42 | `cargo ${command}`, | ||
43 | 'rust', | ||
44 | // What to do when this command is executed. | ||
45 | new vscode.ShellExecution('cargo', [command]), | ||
46 | // Problem matchers. | ||
47 | ['$rustc'], | ||
48 | ); | ||
49 | vscodeTask.group = group; | ||
50 | return vscodeTask; | ||
51 | }); | ||
52 | } | ||
diff --git a/editors/code/src/util.ts b/editors/code/src/util.ts index 978a31751..6f91f81d6 100644 --- a/editors/code/src/util.ts +++ b/editors/code/src/util.ts | |||
@@ -65,12 +65,12 @@ export async function sendRequestWithRetry<TParam, TRet>( | |||
65 | throw 'unreachable'; | 65 | throw 'unreachable'; |
66 | } | 66 | } |
67 | 67 | ||
68 | function sleep(ms: number) { | 68 | export function sleep(ms: number) { |
69 | return new Promise(resolve => setTimeout(resolve, ms)); | 69 | return new Promise(resolve => setTimeout(resolve, ms)); |
70 | } | 70 | } |
71 | 71 | ||
72 | export type RustDocument = vscode.TextDocument & { languageId: "rust" }; | 72 | export type RustDocument = vscode.TextDocument & { languageId: "rust" }; |
73 | export type RustEditor = vscode.TextEditor & { document: RustDocument; id: string }; | 73 | export type RustEditor = vscode.TextEditor & { document: RustDocument }; |
74 | 74 | ||
75 | export function isRustDocument(document: vscode.TextDocument): document is RustDocument { | 75 | export function isRustDocument(document: vscode.TextDocument): document is RustDocument { |
76 | return document.languageId === 'rust' | 76 | return document.languageId === 'rust' |