diff options
Diffstat (limited to 'editors')
-rw-r--r-- | editors/code/package-lock.json | 51 | ||||
-rw-r--r-- | editors/code/package.json | 38 | ||||
-rw-r--r-- | editors/code/src/commands/cargo_watch.ts | 238 | ||||
-rw-r--r-- | editors/code/src/commands/line_buffer.ts | 16 | ||||
-rw-r--r-- | editors/code/src/commands/runnables.ts | 33 | ||||
-rw-r--r-- | editors/code/src/commands/watch_status.ts | 51 | ||||
-rw-r--r-- | editors/code/src/config.ts | 34 | ||||
-rw-r--r-- | editors/code/src/extension.ts | 2 | ||||
-rw-r--r-- | editors/code/src/utils/processes.ts | 51 | ||||
-rw-r--r-- | editors/code/src/utils/terminateProcess.sh | 12 | ||||
-rw-r--r-- | editors/emacs/ra.el | 128 |
11 files changed, 482 insertions, 172 deletions
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json index 5a0d21e78..008df6f52 100644 --- a/editors/code/package-lock.json +++ b/editors/code/package-lock.json | |||
@@ -607,6 +607,12 @@ | |||
607 | "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", | 607 | "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", |
608 | "dev": true | 608 | "dev": true |
609 | }, | 609 | }, |
610 | "es6-object-assign": { | ||
611 | "version": "1.1.0", | ||
612 | "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", | ||
613 | "integrity": "sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=", | ||
614 | "dev": true | ||
615 | }, | ||
610 | "escape-string-regexp": { | 616 | "escape-string-regexp": { |
611 | "version": "1.0.5", | 617 | "version": "1.0.5", |
612 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", | 618 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", |
@@ -1121,6 +1127,12 @@ | |||
1121 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", | 1127 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", |
1122 | "dev": true | 1128 | "dev": true |
1123 | }, | 1129 | }, |
1130 | "interpret": { | ||
1131 | "version": "1.2.0", | ||
1132 | "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", | ||
1133 | "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==", | ||
1134 | "dev": true | ||
1135 | }, | ||
1124 | "is": { | 1136 | "is": { |
1125 | "version": "3.3.0", | 1137 | "version": "3.3.0", |
1126 | "resolved": "https://registry.npmjs.org/is/-/is-3.3.0.tgz", | 1138 | "resolved": "https://registry.npmjs.org/is/-/is-3.3.0.tgz", |
@@ -1791,6 +1803,15 @@ | |||
1791 | "util-deprecate": "^1.0.1" | 1803 | "util-deprecate": "^1.0.1" |
1792 | } | 1804 | } |
1793 | }, | 1805 | }, |
1806 | "rechoir": { | ||
1807 | "version": "0.6.2", | ||
1808 | "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", | ||
1809 | "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", | ||
1810 | "dev": true, | ||
1811 | "requires": { | ||
1812 | "resolve": "^1.1.6" | ||
1813 | } | ||
1814 | }, | ||
1794 | "remove-bom-buffer": { | 1815 | "remove-bom-buffer": { |
1795 | "version": "3.0.0", | 1816 | "version": "3.0.0", |
1796 | "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz", | 1817 | "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz", |
@@ -1902,6 +1923,36 @@ | |||
1902 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", | 1923 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", |
1903 | "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==" | 1924 | "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==" |
1904 | }, | 1925 | }, |
1926 | "shelljs": { | ||
1927 | "version": "0.8.3", | ||
1928 | "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.3.tgz", | ||
1929 | "integrity": "sha512-fc0BKlAWiLpwZljmOvAOTE/gXawtCoNrP5oaY7KIaQbbyHeQVg01pSEuEGvGh3HEdBU4baCD7wQBwADmM/7f7A==", | ||
1930 | "dev": true, | ||
1931 | "requires": { | ||
1932 | "glob": "^7.0.0", | ||
1933 | "interpret": "^1.0.0", | ||
1934 | "rechoir": "^0.6.2" | ||
1935 | } | ||
1936 | }, | ||
1937 | "shx": { | ||
1938 | "version": "0.3.2", | ||
1939 | "resolved": "https://registry.npmjs.org/shx/-/shx-0.3.2.tgz", | ||
1940 | "integrity": "sha512-aS0mWtW3T2sHAenrSrip2XGv39O9dXIFUqxAEWHEOS1ePtGIBavdPJY1kE2IHl14V/4iCbUiNDPGdyYTtmhSoA==", | ||
1941 | "dev": true, | ||
1942 | "requires": { | ||
1943 | "es6-object-assign": "^1.0.3", | ||
1944 | "minimist": "^1.2.0", | ||
1945 | "shelljs": "^0.8.1" | ||
1946 | }, | ||
1947 | "dependencies": { | ||
1948 | "minimist": { | ||
1949 | "version": "1.2.0", | ||
1950 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", | ||
1951 | "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", | ||
1952 | "dev": true | ||
1953 | } | ||
1954 | } | ||
1955 | }, | ||
1905 | "source-map": { | 1956 | "source-map": { |
1906 | "version": "0.6.1", | 1957 | "version": "0.6.1", |
1907 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", | 1958 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", |
diff --git a/editors/code/package.json b/editors/code/package.json index facb633d9..1c8caaa60 100644 --- a/editors/code/package.json +++ b/editors/code/package.json | |||
@@ -18,7 +18,7 @@ | |||
18 | "scripts": { | 18 | "scripts": { |
19 | "vscode:prepublish": "npm run compile", | 19 | "vscode:prepublish": "npm run compile", |
20 | "package": "vsce package", | 20 | "package": "vsce package", |
21 | "compile": "tsc -p ./", | 21 | "compile": "tsc -p ./ && shx cp src/utils/terminateProcess.sh out/utils/terminateProcess.sh", |
22 | "watch": "tsc -watch -p ./", | 22 | "watch": "tsc -watch -p ./", |
23 | "postinstall": "node ./node_modules/vscode/bin/install", | 23 | "postinstall": "node ./node_modules/vscode/bin/install", |
24 | "fix": "prettier **/*.{json,ts} --write && tslint --project . --fix", | 24 | "fix": "prettier **/*.{json,ts} --write && tslint --project . --fix", |
@@ -41,7 +41,8 @@ | |||
41 | "tslint-config-prettier": "^1.18.0", | 41 | "tslint-config-prettier": "^1.18.0", |
42 | "typescript": "^3.3.1", | 42 | "typescript": "^3.3.1", |
43 | "vsce": "^1.57.0", | 43 | "vsce": "^1.57.0", |
44 | "vscode": "^1.1.29" | 44 | "vscode": "^1.1.29", |
45 | "shx": "^0.3.1" | ||
45 | }, | 46 | }, |
46 | "activationEvents": [ | 47 | "activationEvents": [ |
47 | "onLanguage:rust", | 48 | "onLanguage:rust", |
@@ -183,6 +184,11 @@ | |||
183 | ], | 184 | ], |
184 | "description": "Whether to run `cargo watch` on startup" | 185 | "description": "Whether to run `cargo watch` on startup" |
185 | }, | 186 | }, |
187 | "rust-analyzer.cargo-watch.check-arguments": { | ||
188 | "type": "string", | ||
189 | "description": "`cargo-watch` check arguments. (e.g: `--features=\"shumway,pdf\"` will run as `cargo watch -x \"check --features=\"shumway,pdf\"\"` )", | ||
190 | "default": "" | ||
191 | }, | ||
186 | "rust-analyzer.trace.server": { | 192 | "rust-analyzer.trace.server": { |
187 | "type": "string", | 193 | "type": "string", |
188 | "scope": "window", | 194 | "scope": "window", |
@@ -191,8 +197,24 @@ | |||
191 | "messages", | 197 | "messages", |
192 | "verbose" | 198 | "verbose" |
193 | ], | 199 | ], |
200 | "enumDescriptions": [ | ||
201 | "No traces", | ||
202 | "Error only", | ||
203 | "Full log" | ||
204 | ], | ||
194 | "default": "off", | 205 | "default": "off", |
195 | "description": "Trace requests to the ra_lsp_server" | 206 | "description": "Trace requests to the ra_lsp_server" |
207 | }, | ||
208 | "rust-analyzer.trace.cargo-watch": { | ||
209 | "type": "string", | ||
210 | "scope": "window", | ||
211 | "enum": [ | ||
212 | "off", | ||
213 | "error", | ||
214 | "verbose" | ||
215 | ], | ||
216 | "default": "off", | ||
217 | "description": "Trace output of cargo-watch" | ||
196 | } | 218 | } |
197 | } | 219 | } |
198 | }, | 220 | }, |
@@ -223,18 +245,6 @@ | |||
223 | "${workspaceRoot}" | 245 | "${workspaceRoot}" |
224 | ], | 246 | ], |
225 | "pattern": "$rustc" | 247 | "pattern": "$rustc" |
226 | }, | ||
227 | { | ||
228 | "name": "rustc-watch", | ||
229 | "fileLocation": [ | ||
230 | "relative", | ||
231 | "${workspaceRoot}" | ||
232 | ], | ||
233 | "background": { | ||
234 | "beginsPattern": "^\\[Running\\b", | ||
235 | "endsPattern": "^\\[Finished running\\b" | ||
236 | }, | ||
237 | "pattern": "$rustc" | ||
238 | } | 248 | } |
239 | ] | 249 | ] |
240 | } | 250 | } |
diff --git a/editors/code/src/commands/cargo_watch.ts b/editors/code/src/commands/cargo_watch.ts new file mode 100644 index 000000000..32bd38a1c --- /dev/null +++ b/editors/code/src/commands/cargo_watch.ts | |||
@@ -0,0 +1,238 @@ | |||
1 | import * as child_process from 'child_process'; | ||
2 | import * as fs from 'fs'; | ||
3 | import * as path from 'path'; | ||
4 | import * as vscode from 'vscode'; | ||
5 | import { Server } from '../server'; | ||
6 | import { terminate } from '../utils/processes'; | ||
7 | import { LineBuffer } from './line_buffer'; | ||
8 | import { StatusDisplay } from './watch_status'; | ||
9 | |||
10 | export class CargoWatchProvider { | ||
11 | private diagnosticCollection?: vscode.DiagnosticCollection; | ||
12 | private cargoProcess?: child_process.ChildProcess; | ||
13 | private outBuffer: string = ''; | ||
14 | private statusDisplay?: StatusDisplay; | ||
15 | private outputChannel?: vscode.OutputChannel; | ||
16 | |||
17 | public activate(subscriptions: vscode.Disposable[]) { | ||
18 | let cargoExists = false; | ||
19 | const cargoTomlFile = path.join( | ||
20 | vscode.workspace.rootPath!, | ||
21 | 'Cargo.toml' | ||
22 | ); | ||
23 | // Check if the working directory is valid cargo root path | ||
24 | try { | ||
25 | if (fs.existsSync(cargoTomlFile)) { | ||
26 | cargoExists = true; | ||
27 | } | ||
28 | } catch (err) { | ||
29 | cargoExists = false; | ||
30 | } | ||
31 | |||
32 | if (!cargoExists) { | ||
33 | vscode.window.showErrorMessage( | ||
34 | `Couldn\'t find \'Cargo.toml\' in ${cargoTomlFile}` | ||
35 | ); | ||
36 | return; | ||
37 | } | ||
38 | |||
39 | subscriptions.push(this); | ||
40 | this.diagnosticCollection = vscode.languages.createDiagnosticCollection( | ||
41 | 'rustc' | ||
42 | ); | ||
43 | |||
44 | this.statusDisplay = new StatusDisplay(subscriptions); | ||
45 | this.outputChannel = vscode.window.createOutputChannel( | ||
46 | 'Cargo Watch Trace' | ||
47 | ); | ||
48 | |||
49 | let args = 'check --message-format json'; | ||
50 | if (Server.config.cargoWatchOptions.checkArguments.length > 0) { | ||
51 | // Excape the double quote string: | ||
52 | args += ' ' + Server.config.cargoWatchOptions.checkArguments; | ||
53 | } | ||
54 | // Windows handles arguments differently than the unix-likes, so we need to wrap the args in double quotes | ||
55 | if (process.platform === 'win32') { | ||
56 | args = '"' + args + '"'; | ||
57 | } | ||
58 | |||
59 | // Start the cargo watch with json message | ||
60 | this.cargoProcess = child_process.spawn( | ||
61 | 'cargo', | ||
62 | ['watch', '-x', args], | ||
63 | { | ||
64 | stdio: ['ignore', 'pipe', 'pipe'], | ||
65 | cwd: vscode.workspace.rootPath, | ||
66 | windowsVerbatimArguments: true | ||
67 | } | ||
68 | ); | ||
69 | |||
70 | const stdoutData = new LineBuffer(); | ||
71 | this.cargoProcess.stdout.on('data', (s: string) => { | ||
72 | stdoutData.processOutput(s, line => { | ||
73 | this.logInfo(line); | ||
74 | try { | ||
75 | this.parseLine(line); | ||
76 | } catch (err) { | ||
77 | this.logError(`Failed to parse: ${err}, content : ${line}`); | ||
78 | } | ||
79 | }); | ||
80 | }); | ||
81 | |||
82 | const stderrData = new LineBuffer(); | ||
83 | this.cargoProcess.stderr.on('data', (s: string) => { | ||
84 | stderrData.processOutput(s, line => { | ||
85 | this.logError('Error on cargo-watch : {\n' + line + '}\n'); | ||
86 | }); | ||
87 | }); | ||
88 | |||
89 | this.cargoProcess.on('error', (err: Error) => { | ||
90 | this.logError( | ||
91 | 'Error on cargo-watch process : {\n' + err.message + '}\n' | ||
92 | ); | ||
93 | }); | ||
94 | |||
95 | this.logInfo('cargo-watch started.'); | ||
96 | } | ||
97 | |||
98 | public dispose(): void { | ||
99 | if (this.diagnosticCollection) { | ||
100 | this.diagnosticCollection.clear(); | ||
101 | this.diagnosticCollection.dispose(); | ||
102 | } | ||
103 | |||
104 | if (this.cargoProcess) { | ||
105 | this.cargoProcess.kill(); | ||
106 | terminate(this.cargoProcess); | ||
107 | } | ||
108 | |||
109 | if (this.outputChannel) { | ||
110 | this.outputChannel.dispose(); | ||
111 | } | ||
112 | } | ||
113 | |||
114 | private logInfo(line: string) { | ||
115 | if (Server.config.cargoWatchOptions.trace === 'verbose') { | ||
116 | this.outputChannel!.append(line); | ||
117 | } | ||
118 | } | ||
119 | |||
120 | private logError(line: string) { | ||
121 | if ( | ||
122 | Server.config.cargoWatchOptions.trace === 'error' || | ||
123 | Server.config.cargoWatchOptions.trace === 'verbose' | ||
124 | ) { | ||
125 | this.outputChannel!.append(line); | ||
126 | } | ||
127 | } | ||
128 | |||
129 | private parseLine(line: string) { | ||
130 | if (line.startsWith('[Running')) { | ||
131 | this.diagnosticCollection!.clear(); | ||
132 | this.statusDisplay!.show(); | ||
133 | } | ||
134 | |||
135 | if (line.startsWith('[Finished running')) { | ||
136 | this.statusDisplay!.hide(); | ||
137 | } | ||
138 | |||
139 | function getLevel(s: string): vscode.DiagnosticSeverity { | ||
140 | if (s === 'error') { | ||
141 | return vscode.DiagnosticSeverity.Error; | ||
142 | } | ||
143 | if (s.startsWith('warn')) { | ||
144 | return vscode.DiagnosticSeverity.Warning; | ||
145 | } | ||
146 | return vscode.DiagnosticSeverity.Information; | ||
147 | } | ||
148 | |||
149 | // Reference: | ||
150 | // https://github.com/rust-lang/rust/blob/master/src/libsyntax/json.rs | ||
151 | interface RustDiagnosticSpan { | ||
152 | line_start: number; | ||
153 | line_end: number; | ||
154 | column_start: number; | ||
155 | column_end: number; | ||
156 | is_primary: boolean; | ||
157 | file_name: string; | ||
158 | } | ||
159 | |||
160 | interface RustDiagnostic { | ||
161 | spans: RustDiagnosticSpan[]; | ||
162 | rendered: string; | ||
163 | level: string; | ||
164 | code?: { | ||
165 | code: string; | ||
166 | }; | ||
167 | } | ||
168 | |||
169 | interface CargoArtifact { | ||
170 | reason: string; | ||
171 | package_id: string; | ||
172 | } | ||
173 | |||
174 | // https://github.com/rust-lang/cargo/blob/master/src/cargo/util/machine_message.rs | ||
175 | interface CargoMessage { | ||
176 | reason: string; | ||
177 | package_id: string; | ||
178 | message: RustDiagnostic; | ||
179 | } | ||
180 | |||
181 | // cargo-watch itself output non json format | ||
182 | // Ignore these lines | ||
183 | let data: CargoMessage; | ||
184 | try { | ||
185 | data = JSON.parse(line.trim()); | ||
186 | } catch (error) { | ||
187 | this.logError(`Fail to parse to json : { ${error} }`); | ||
188 | return; | ||
189 | } | ||
190 | |||
191 | if (data.reason === 'compiler-artifact') { | ||
192 | const msg = data as CargoArtifact; | ||
193 | |||
194 | // The format of the package_id is "{name} {version} ({source_id})", | ||
195 | // https://github.com/rust-lang/cargo/blob/37ad03f86e895bb80b474c1c088322634f4725f5/src/cargo/core/package_id.rs#L53 | ||
196 | this.statusDisplay!.packageName = msg.package_id.split(' ')[0]; | ||
197 | } else if (data.reason === 'compiler-message') { | ||
198 | const msg = data.message as RustDiagnostic; | ||
199 | |||
200 | const spans = msg.spans.filter(o => o.is_primary); | ||
201 | |||
202 | // We only handle primary span right now. | ||
203 | if (spans.length > 0) { | ||
204 | const o = spans[0]; | ||
205 | |||
206 | const rendered = msg.rendered; | ||
207 | const level = getLevel(msg.level); | ||
208 | const range = new vscode.Range( | ||
209 | new vscode.Position(o.line_start - 1, o.column_start - 1), | ||
210 | new vscode.Position(o.line_end - 1, o.column_end - 1) | ||
211 | ); | ||
212 | |||
213 | const fileName = path.join( | ||
214 | vscode.workspace.rootPath!, | ||
215 | o.file_name | ||
216 | ); | ||
217 | const diagnostic = new vscode.Diagnostic( | ||
218 | range, | ||
219 | rendered, | ||
220 | level | ||
221 | ); | ||
222 | |||
223 | diagnostic.source = 'rustc'; | ||
224 | diagnostic.code = msg.code ? msg.code.code : undefined; | ||
225 | diagnostic.relatedInformation = []; | ||
226 | |||
227 | const fileUrl = vscode.Uri.file(fileName!); | ||
228 | |||
229 | const diagnostics: vscode.Diagnostic[] = [ | ||
230 | ...(this.diagnosticCollection!.get(fileUrl) || []) | ||
231 | ]; | ||
232 | diagnostics.push(diagnostic); | ||
233 | |||
234 | this.diagnosticCollection!.set(fileUrl, diagnostics); | ||
235 | } | ||
236 | } | ||
237 | } | ||
238 | } | ||
diff --git a/editors/code/src/commands/line_buffer.ts b/editors/code/src/commands/line_buffer.ts new file mode 100644 index 000000000..fb5b9f7f2 --- /dev/null +++ b/editors/code/src/commands/line_buffer.ts | |||
@@ -0,0 +1,16 @@ | |||
1 | export class LineBuffer { | ||
2 | private outBuffer: string = ''; | ||
3 | |||
4 | public processOutput(chunk: string, cb: (line: string) => void) { | ||
5 | this.outBuffer += chunk; | ||
6 | let eolIndex = this.outBuffer.indexOf('\n'); | ||
7 | while (eolIndex >= 0) { | ||
8 | // line includes the EOL | ||
9 | const line = this.outBuffer.slice(0, eolIndex + 1); | ||
10 | cb(line); | ||
11 | this.outBuffer = this.outBuffer.slice(eolIndex + 1); | ||
12 | |||
13 | eolIndex = this.outBuffer.indexOf('\n'); | ||
14 | } | ||
15 | } | ||
16 | } | ||
diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/commands/runnables.ts index 4187ef4d1..3589edcee 100644 --- a/editors/code/src/commands/runnables.ts +++ b/editors/code/src/commands/runnables.ts | |||
@@ -1,9 +1,11 @@ | |||
1 | import * as child_process from 'child_process'; | 1 | import * as child_process from 'child_process'; |
2 | |||
2 | import * as util from 'util'; | 3 | import * as util from 'util'; |
3 | import * as vscode from 'vscode'; | 4 | import * as vscode from 'vscode'; |
4 | import * as lc from 'vscode-languageclient'; | 5 | import * as lc from 'vscode-languageclient'; |
5 | 6 | ||
6 | import { Server } from '../server'; | 7 | import { Server } from '../server'; |
8 | import { CargoWatchProvider } from './cargo_watch'; | ||
7 | 9 | ||
8 | interface RunnablesParams { | 10 | interface RunnablesParams { |
9 | textDocument: lc.TextDocumentIdentifier; | 11 | textDocument: lc.TextDocumentIdentifier; |
@@ -127,37 +129,19 @@ export async function handleSingle(runnable: Runnable) { | |||
127 | return vscode.tasks.executeTask(task); | 129 | return vscode.tasks.executeTask(task); |
128 | } | 130 | } |
129 | 131 | ||
130 | export const autoCargoWatchTask: vscode.Task = { | ||
131 | name: 'cargo watch', | ||
132 | source: 'rust-analyzer', | ||
133 | definition: { | ||
134 | type: 'watch' | ||
135 | }, | ||
136 | execution: new vscode.ShellExecution('cargo', ['watch'], { cwd: '.' }), | ||
137 | |||
138 | isBackground: true, | ||
139 | problemMatchers: ['$rustc-watch'], | ||
140 | presentationOptions: { | ||
141 | clear: true | ||
142 | }, | ||
143 | // Not yet exposed in the vscode.d.ts | ||
144 | // https://github.com/Microsoft/vscode/blob/ea7c31d770e04b51d586b0d3944f3a7feb03afb9/src/vs/workbench/contrib/tasks/common/tasks.ts#L444-L456 | ||
145 | runOptions: ({ | ||
146 | runOn: 2 // RunOnOptions.folderOpen | ||
147 | } as unknown) as vscode.RunOptions | ||
148 | }; | ||
149 | |||
150 | /** | 132 | /** |
151 | * Interactively asks the user whether we should run `cargo check` in order to | 133 | * Interactively asks the user whether we should run `cargo check` in order to |
152 | * provide inline diagnostics; the user is met with a series of dialog boxes | 134 | * provide inline diagnostics; the user is met with a series of dialog boxes |
153 | * that, when accepted, allow us to `cargo install cargo-watch` and then run it. | 135 | * that, when accepted, allow us to `cargo install cargo-watch` and then run it. |
154 | */ | 136 | */ |
155 | export async function interactivelyStartCargoWatch() { | 137 | export async function interactivelyStartCargoWatch( |
156 | if (Server.config.enableCargoWatchOnStartup === 'disabled') { | 138 | context: vscode.ExtensionContext |
139 | ) { | ||
140 | if (Server.config.cargoWatchOptions.enableOnStartup === 'disabled') { | ||
157 | return; | 141 | return; |
158 | } | 142 | } |
159 | 143 | ||
160 | if (Server.config.enableCargoWatchOnStartup === 'ask') { | 144 | if (Server.config.cargoWatchOptions.enableOnStartup === 'ask') { |
161 | const watch = await vscode.window.showInformationMessage( | 145 | const watch = await vscode.window.showInformationMessage( |
162 | 'Start watching changes with cargo? (Executes `cargo watch`, provides inline diagnostics)', | 146 | 'Start watching changes with cargo? (Executes `cargo watch`, provides inline diagnostics)', |
163 | 'yes', | 147 | 'yes', |
@@ -212,5 +196,6 @@ export async function interactivelyStartCargoWatch() { | |||
212 | } | 196 | } |
213 | } | 197 | } |
214 | 198 | ||
215 | vscode.tasks.executeTask(autoCargoWatchTask); | 199 | const validater = new CargoWatchProvider(); |
200 | validater.activate(context.subscriptions); | ||
216 | } | 201 | } |
diff --git a/editors/code/src/commands/watch_status.ts b/editors/code/src/commands/watch_status.ts new file mode 100644 index 000000000..86ae821de --- /dev/null +++ b/editors/code/src/commands/watch_status.ts | |||
@@ -0,0 +1,51 @@ | |||
1 | import * as vscode from 'vscode'; | ||
2 | |||
3 | const spinnerFrames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']; | ||
4 | |||
5 | export class StatusDisplay { | ||
6 | public packageName?: string; | ||
7 | |||
8 | private i = 0; | ||
9 | private statusBarItem: vscode.StatusBarItem; | ||
10 | private timer?: NodeJS.Timeout; | ||
11 | |||
12 | constructor(subscriptions: vscode.Disposable[]) { | ||
13 | this.statusBarItem = vscode.window.createStatusBarItem( | ||
14 | vscode.StatusBarAlignment.Left, | ||
15 | 10 | ||
16 | ); | ||
17 | subscriptions.push(this.statusBarItem); | ||
18 | this.statusBarItem.hide(); | ||
19 | } | ||
20 | |||
21 | public show() { | ||
22 | this.packageName = undefined; | ||
23 | |||
24 | this.timer = | ||
25 | this.timer || | ||
26 | setInterval(() => { | ||
27 | if (this.packageName) { | ||
28 | this.statusBarItem!.text = `cargo check [${ | ||
29 | this.packageName | ||
30 | }] ${this.frame()}`; | ||
31 | } else { | ||
32 | this.statusBarItem!.text = `cargo check ${this.frame()}`; | ||
33 | } | ||
34 | }, 300); | ||
35 | |||
36 | this.statusBarItem!.show(); | ||
37 | } | ||
38 | |||
39 | public hide() { | ||
40 | if (this.timer) { | ||
41 | clearInterval(this.timer); | ||
42 | this.timer = undefined; | ||
43 | } | ||
44 | |||
45 | this.statusBarItem!.hide(); | ||
46 | } | ||
47 | |||
48 | private frame() { | ||
49 | return spinnerFrames[(this.i = ++this.i % spinnerFrames.length)]; | ||
50 | } | ||
51 | } | ||
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 420589068..481a5e5f1 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts | |||
@@ -4,14 +4,25 @@ import { Server } from './server'; | |||
4 | 4 | ||
5 | const RA_LSP_DEBUG = process.env.__RA_LSP_SERVER_DEBUG; | 5 | const RA_LSP_DEBUG = process.env.__RA_LSP_SERVER_DEBUG; |
6 | 6 | ||
7 | export type CargoWatchOptions = 'ask' | 'enabled' | 'disabled'; | 7 | export type CargoWatchStartupOptions = 'ask' | 'enabled' | 'disabled'; |
8 | export type CargoWatchTraceOptions = 'off' | 'error' | 'verbose'; | ||
9 | |||
10 | export interface CargoWatchOptions { | ||
11 | enableOnStartup: CargoWatchStartupOptions; | ||
12 | checkArguments: string; | ||
13 | trace: CargoWatchTraceOptions; | ||
14 | } | ||
8 | 15 | ||
9 | export class Config { | 16 | export class Config { |
10 | public highlightingOn = true; | 17 | public highlightingOn = true; |
11 | public enableEnhancedTyping = true; | 18 | public enableEnhancedTyping = true; |
12 | public raLspServerPath = RA_LSP_DEBUG || 'ra_lsp_server'; | 19 | public raLspServerPath = RA_LSP_DEBUG || 'ra_lsp_server'; |
13 | public showWorkspaceLoadedNotification = true; | 20 | public showWorkspaceLoadedNotification = true; |
14 | public enableCargoWatchOnStartup: CargoWatchOptions = 'ask'; | 21 | public cargoWatchOptions: CargoWatchOptions = { |
22 | enableOnStartup: 'ask', | ||
23 | trace: 'off', | ||
24 | checkArguments: '' | ||
25 | }; | ||
15 | 26 | ||
16 | private prevEnhancedTyping: null | boolean = null; | 27 | private prevEnhancedTyping: null | boolean = null; |
17 | 28 | ||
@@ -73,9 +84,22 @@ export class Config { | |||
73 | } | 84 | } |
74 | 85 | ||
75 | if (config.has('enableCargoWatchOnStartup')) { | 86 | if (config.has('enableCargoWatchOnStartup')) { |
76 | this.enableCargoWatchOnStartup = config.get<CargoWatchOptions>( | 87 | this.cargoWatchOptions.enableOnStartup = config.get< |
77 | 'enableCargoWatchOnStartup', | 88 | CargoWatchStartupOptions |
78 | 'ask' | 89 | >('enableCargoWatchOnStartup', 'ask'); |
90 | } | ||
91 | |||
92 | if (config.has('trace.cargo-watch')) { | ||
93 | this.cargoWatchOptions.trace = config.get<CargoWatchTraceOptions>( | ||
94 | 'trace.cargo-watch', | ||
95 | 'off' | ||
96 | ); | ||
97 | } | ||
98 | |||
99 | if (config.has('cargo-watch.check-arguments')) { | ||
100 | this.cargoWatchOptions.checkArguments = config.get<string>( | ||
101 | 'cargo-watch.check-arguments', | ||
102 | '' | ||
79 | ); | 103 | ); |
80 | } | 104 | } |
81 | } | 105 | } |
diff --git a/editors/code/src/extension.ts b/editors/code/src/extension.ts index 2e13c87de..ef83c0b8b 100644 --- a/editors/code/src/extension.ts +++ b/editors/code/src/extension.ts | |||
@@ -121,7 +121,7 @@ export function activate(context: vscode.ExtensionContext) { | |||
121 | ); | 121 | ); |
122 | 122 | ||
123 | // Executing `cargo watch` provides us with inline diagnostics on save | 123 | // Executing `cargo watch` provides us with inline diagnostics on save |
124 | interactivelyStartCargoWatch(); | 124 | interactivelyStartCargoWatch(context); |
125 | 125 | ||
126 | // Start the language server, finally! | 126 | // Start the language server, finally! |
127 | Server.start(allNotifications); | 127 | Server.start(allNotifications); |
diff --git a/editors/code/src/utils/processes.ts b/editors/code/src/utils/processes.ts new file mode 100644 index 000000000..da8be9eb1 --- /dev/null +++ b/editors/code/src/utils/processes.ts | |||
@@ -0,0 +1,51 @@ | |||
1 | 'use strict'; | ||
2 | |||
3 | import * as cp from 'child_process'; | ||
4 | import ChildProcess = cp.ChildProcess; | ||
5 | |||
6 | import { join } from 'path'; | ||
7 | |||
8 | const isWindows = process.platform === 'win32'; | ||
9 | const isMacintosh = process.platform === 'darwin'; | ||
10 | const isLinux = process.platform === 'linux'; | ||
11 | |||
12 | // this is very complex, but is basically copy-pased from VSCode implementation here: | ||
13 | // https://github.com/Microsoft/vscode-languageserver-node/blob/dbfd37e35953ad0ee14c4eeced8cfbc41697b47e/client/src/utils/processes.ts#L15 | ||
14 | |||
15 | // And see discussion at | ||
16 | // https://github.com/rust-analyzer/rust-analyzer/pull/1079#issuecomment-478908109 | ||
17 | |||
18 | export function terminate(process: ChildProcess, cwd?: string): boolean { | ||
19 | if (isWindows) { | ||
20 | try { | ||
21 | // This we run in Atom execFileSync is available. | ||
22 | // Ignore stderr since this is otherwise piped to parent.stderr | ||
23 | // which might be already closed. | ||
24 | const options: any = { | ||
25 | stdio: ['pipe', 'pipe', 'ignore'] | ||
26 | }; | ||
27 | if (cwd) { | ||
28 | options.cwd = cwd; | ||
29 | } | ||
30 | cp.execFileSync( | ||
31 | 'taskkill', | ||
32 | ['/T', '/F', '/PID', process.pid.toString()], | ||
33 | options | ||
34 | ); | ||
35 | return true; | ||
36 | } catch (err) { | ||
37 | return false; | ||
38 | } | ||
39 | } else if (isLinux || isMacintosh) { | ||
40 | try { | ||
41 | const cmd = join(__dirname, 'terminateProcess.sh'); | ||
42 | const result = cp.spawnSync(cmd, [process.pid.toString()]); | ||
43 | return result.error ? false : true; | ||
44 | } catch (err) { | ||
45 | return false; | ||
46 | } | ||
47 | } else { | ||
48 | process.kill('SIGKILL'); | ||
49 | return true; | ||
50 | } | ||
51 | } | ||
diff --git a/editors/code/src/utils/terminateProcess.sh b/editors/code/src/utils/terminateProcess.sh new file mode 100644 index 000000000..2ec9e1c2e --- /dev/null +++ b/editors/code/src/utils/terminateProcess.sh | |||
@@ -0,0 +1,12 @@ | |||
1 | #!/bin/bash | ||
2 | |||
3 | terminateTree() { | ||
4 | for cpid in $(pgrep -P $1); do | ||
5 | terminateTree $cpid | ||
6 | done | ||
7 | kill -9 $1 > /dev/null 2>&1 | ||
8 | } | ||
9 | |||
10 | for pid in $*; do | ||
11 | terminateTree $pid | ||
12 | done \ No newline at end of file | ||
diff --git a/editors/emacs/ra.el b/editors/emacs/ra.el deleted file mode 100644 index fb73451c1..000000000 --- a/editors/emacs/ra.el +++ /dev/null | |||
@@ -1,128 +0,0 @@ | |||
1 | ;;; ra.el --- Rust analyzer emacs bindings -*- lexical-binding: t; -*- | ||
2 | ;;; Commentary: | ||
3 | ;;; Small utilities for interacting with Rust analyzer. | ||
4 | ;;; Run | ||
5 | ;;; cargo install --git https://github.com/matklad/rust-analyzer/ ra_cli | ||
6 | ;;; to install the analyzer binary. Then copy-paste the bellow code to | ||
7 | ;;; your `.init.el` and use `ra-extend-selection` and | ||
8 | ;;; `ra-shrink-selection` functions. | ||
9 | ;;; Code: | ||
10 | |||
11 | |||
12 | (defvar ra--selections-cache '(0 0 ())) | ||
13 | (defun ra--cache-tick () | ||
14 | "Get buffer modification count for cache." | ||
15 | (nth 0 ra--selections-cache)) | ||
16 | (defun ra--cache-sel () | ||
17 | "Get current selection for cache." | ||
18 | (nth 1 ra--selections-cache)) | ||
19 | (defun ra--cache-nth-sel (n) | ||
20 | "Get Nth selection." | ||
21 | (nth n (nth 2 ra--selections-cache))) | ||
22 | (defun ra--cache-set-nth-sel (n) | ||
23 | "Get Nth selection." | ||
24 | (setf (nth 1 ra--selections-cache) n) | ||
25 | (nth n (nth 2 ra--selections-cache))) | ||
26 | |||
27 | |||
28 | (defun ra-extend-selection () | ||
29 | "Extend START END region to contain the encompassing syntactic construct." | ||
30 | (interactive) | ||
31 | (let* ((p (point)) | ||
32 | (m (or (and mark-active (mark)) p)) | ||
33 | (start (min p m)) | ||
34 | (end (max p m))) | ||
35 | (ra--extend-selection start end))) | ||
36 | |||
37 | |||
38 | (defun ra-shrink-selection (start end) | ||
39 | "Shrink START END region to contain previous selection." | ||
40 | (interactive "r") | ||
41 | (ra--freshen-cache start end) | ||
42 | (let ((sel-id (ra--cache-sel))) | ||
43 | (if (not (= 0 sel-id)) | ||
44 | (let* ((r (ra--cache-set-nth-sel (- sel-id 1)))) | ||
45 | (push-mark (nth 0 r) t t) | ||
46 | (goto-char (nth 1 r)) | ||
47 | (setq deactivate-mark nil))))) | ||
48 | |||
49 | ; Add this to setup keybinding | ||
50 | ; (require 'rust-mode) | ||
51 | ; (define-key rust-mode-map (kbd "C-w") 'ra-extend-selection) | ||
52 | ; (define-key rust-mode-map (kbd "C-S-w") 'ra-shrink-selection) | ||
53 | |||
54 | |||
55 | |||
56 | (defun ra--extend-selection (start end) | ||
57 | "Extend START END region to contain the encompassing syntactic construct." | ||
58 | (ra--freshen-cache start end) | ||
59 | (let* ((next-sel-idx (+ 1 (ra--cache-sel))) | ||
60 | (r (ra--cache-set-nth-sel next-sel-idx))) | ||
61 | (push-mark (nth 0 r) t t) | ||
62 | (goto-char (nth 1 r)) | ||
63 | (setq deactivate-mark nil))) | ||
64 | |||
65 | (defun ra--selections (start end) | ||
66 | "Get list of selections for START END from Rust analyzer." | ||
67 | (read (with-output-to-string | ||
68 | (call-process-region | ||
69 | (point-min) (point-max) | ||
70 | "ra_cli" nil standard-output nil | ||
71 | "extend-selection" | ||
72 | (number-to-string start) | ||
73 | (number-to-string end))))) | ||
74 | |||
75 | (defun ra--freshen-cache (start end) | ||
76 | "Make selection cache up-to-date for current buffer state and START END." | ||
77 | (if (not (and | ||
78 | (= (buffer-modified-tick) (ra--cache-tick)) | ||
79 | (equal `(,start ,end) (ra--cache-nth-sel (ra--cache-sel))))) | ||
80 | (ra--set-cache start end))) | ||
81 | |||
82 | (defun ra--set-cache (start end) | ||
83 | "Set selections cache for current buffer state and START END." | ||
84 | (setq ra--selections-cache `(,(buffer-modified-tick) 0 ,(ra--selections start end)))) | ||
85 | |||
86 | |||
87 | (require 'eglot) | ||
88 | (require 'ivy) | ||
89 | (require 'counsel) | ||
90 | |||
91 | |||
92 | (defun workspace-symbols () | ||
93 | (interactive) | ||
94 | (let ((buf (current-buffer))) | ||
95 | (ivy-read "Symbol name: " | ||
96 | (lambda (str) | ||
97 | (with-current-buffer buf | ||
98 | (let ((backend (eglot-xref-backend))) | ||
99 | (mapcar | ||
100 | (lambda (xref) | ||
101 | (let ((loc (xref-item-location xref))) | ||
102 | (propertize | ||
103 | (concat | ||
104 | (when (xref-file-location-p loc) | ||
105 | (with-slots (file line column) loc | ||
106 | (format "%s:%s:%s:" | ||
107 | (propertize (file-relative-name file) | ||
108 | 'face 'compilation-info) | ||
109 | (propertize (format "%s" line) | ||
110 | 'face 'compilation-line | ||
111 | ) | ||
112 | column))) | ||
113 | (xref-item-summary xref)) | ||
114 | 'xref xref))) | ||
115 | (xref-backend-apropos backend str)) | ||
116 | ))) | ||
117 | :dynamic-collection t | ||
118 | :action (lambda (item) | ||
119 | (xref--pop-to-location (get-text-property 0 'xref item)))))) | ||
120 | |||
121 | (add-to-list 'eglot-server-programs '(rust-mode . ("ra_lsp_server"))) | ||
122 | |||
123 | ; (require 'rust-mode) | ||
124 | ; (define-key rust-mode-map (kbd "C-n") 'workspace-symbols) | ||
125 | |||
126 | (define-key) | ||
127 | (provide 'ra) | ||
128 | ;;; ra.el ends here | ||