diff options
-rw-r--r-- | docs/user/README.md | 2 | ||||
-rw-r--r-- | editors/code/package.json | 10 | ||||
-rw-r--r-- | editors/code/src/commands/cargo_watch.ts | 78 | ||||
-rw-r--r-- | editors/code/src/commands/line_buffer.ts | 16 | ||||
-rw-r--r-- | editors/code/src/config.ts | 39 | ||||
-rw-r--r-- | editors/code/src/utils/processes.ts | 68 |
6 files changed, 139 insertions, 74 deletions
diff --git a/docs/user/README.md b/docs/user/README.md index 722a86c9b..33dd4f995 100644 --- a/docs/user/README.md +++ b/docs/user/README.md | |||
@@ -59,6 +59,8 @@ for details. | |||
59 | * `rust-analyzer.raLspServerPath`: path to `ra_lsp_server` executable | 59 | * `rust-analyzer.raLspServerPath`: path to `ra_lsp_server` executable |
60 | * `rust-analyzer.enableCargoWatchOnStartup`: prompt to install & enable `cargo | 60 | * `rust-analyzer.enableCargoWatchOnStartup`: prompt to install & enable `cargo |
61 | watch` for live error highlighting (note, this **does not** use rust-analyzer) | 61 | watch` for live error highlighting (note, this **does not** use rust-analyzer) |
62 | * `rust-analyzer.cargo-watch.check-arguments`: cargo-watch check arguments. | ||
63 | (e.g: `--features="shumway,pdf"` will run as `cargo watch -x "check --features="shumway,pdf""` ) | ||
62 | * `rust-analyzer.trace.server`: enables internal logging | 64 | * `rust-analyzer.trace.server`: enables internal logging |
63 | * `rust-analyzer.trace.cargo-watch`: enables cargo-watch logging | 65 | * `rust-analyzer.trace.cargo-watch`: enables cargo-watch logging |
64 | 66 | ||
diff --git a/editors/code/package.json b/editors/code/package.json index cc364d478..1c8caaa60 100644 --- a/editors/code/package.json +++ b/editors/code/package.json | |||
@@ -184,6 +184,11 @@ | |||
184 | ], | 184 | ], |
185 | "description": "Whether to run `cargo watch` on startup" | 185 | "description": "Whether to run `cargo watch` on startup" |
186 | }, | 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 | }, | ||
187 | "rust-analyzer.trace.server": { | 192 | "rust-analyzer.trace.server": { |
188 | "type": "string", | 193 | "type": "string", |
189 | "scope": "window", | 194 | "scope": "window", |
@@ -192,6 +197,11 @@ | |||
192 | "messages", | 197 | "messages", |
193 | "verbose" | 198 | "verbose" |
194 | ], | 199 | ], |
200 | "enumDescriptions": [ | ||
201 | "No traces", | ||
202 | "Error only", | ||
203 | "Full log" | ||
204 | ], | ||
195 | "default": "off", | 205 | "default": "off", |
196 | "description": "Trace requests to the ra_lsp_server" | 206 | "description": "Trace requests to the ra_lsp_server" |
197 | }, | 207 | }, |
diff --git a/editors/code/src/commands/cargo_watch.ts b/editors/code/src/commands/cargo_watch.ts index 9864ce01a..fb8fcaeb3 100644 --- a/editors/code/src/commands/cargo_watch.ts +++ b/editors/code/src/commands/cargo_watch.ts | |||
@@ -3,9 +3,9 @@ import * as path from 'path'; | |||
3 | import * as vscode from 'vscode'; | 3 | import * as vscode from 'vscode'; |
4 | import { Server } from '../server'; | 4 | import { Server } from '../server'; |
5 | import { terminate } from '../utils/processes'; | 5 | import { terminate } from '../utils/processes'; |
6 | import { LineBuffer } from './line_buffer'; | ||
6 | import { StatusDisplay } from './watch_status'; | 7 | import { StatusDisplay } from './watch_status'; |
7 | 8 | ||
8 | |||
9 | export class CargoWatchProvider { | 9 | export class CargoWatchProvider { |
10 | private diagnosticCollection?: vscode.DiagnosticCollection; | 10 | private diagnosticCollection?: vscode.DiagnosticCollection; |
11 | private cargoProcess?: child_process.ChildProcess; | 11 | private cargoProcess?: child_process.ChildProcess; |
@@ -23,33 +23,44 @@ export class CargoWatchProvider { | |||
23 | this.outputChannel = vscode.window.createOutputChannel( | 23 | this.outputChannel = vscode.window.createOutputChannel( |
24 | 'Cargo Watch Trace' | 24 | 'Cargo Watch Trace' |
25 | ); | 25 | ); |
26 | 26 | ||
27 | let args = '"check --message-format json'; | ||
28 | if (Server.config.cargoWatchOptions.checkArguments.length > 0) { | ||
29 | // Excape the double quote string: | ||
30 | args += ' ' + Server.config.cargoWatchOptions.checkArguments; | ||
31 | } | ||
32 | args += '"'; | ||
33 | |||
27 | // Start the cargo watch with json message | 34 | // Start the cargo watch with json message |
28 | this.cargoProcess = child_process.spawn( | 35 | this.cargoProcess = child_process.spawn( |
29 | 'cargo', | 36 | 'cargo', |
30 | ['watch', '-x', '\"check --message-format json\"'], | 37 | ['watch', '-x', args], |
31 | { | 38 | { |
32 | stdio: ['ignore', 'pipe', 'pipe'], | 39 | stdio: ['ignore', 'pipe', 'pipe'], |
33 | cwd: vscode.workspace.rootPath, | 40 | cwd: vscode.workspace.rootPath, |
34 | windowsVerbatimArguments: true, | 41 | windowsVerbatimArguments: true |
35 | } | 42 | } |
36 | ); | 43 | ); |
37 | 44 | ||
45 | const stdoutData = new LineBuffer(); | ||
38 | this.cargoProcess.stdout.on('data', (s: string) => { | 46 | this.cargoProcess.stdout.on('data', (s: string) => { |
39 | this.processOutput(s, (line) => { | 47 | stdoutData.processOutput(s, line => { |
40 | this.logInfo(line); | 48 | this.logInfo(line); |
41 | this.parseLine(line); | 49 | this.parseLine(line); |
42 | }); | 50 | }); |
43 | }); | 51 | }); |
44 | 52 | ||
53 | const stderrData = new LineBuffer(); | ||
45 | this.cargoProcess.stderr.on('data', (s: string) => { | 54 | this.cargoProcess.stderr.on('data', (s: string) => { |
46 | this.processOutput(s, (line) => { | 55 | stderrData.processOutput(s, line => { |
47 | this.logError('Error on cargo-watch : {\n' + line + '}\n' ); | 56 | this.logError('Error on cargo-watch : {\n' + line + '}\n'); |
48 | }); | 57 | }); |
49 | }); | 58 | }); |
50 | 59 | ||
51 | this.cargoProcess.on('error', (err: Error) => { | 60 | this.cargoProcess.on('error', (err: Error) => { |
52 | this.logError('Error on cargo-watch process : {\n' + err.message + '}\n'); | 61 | this.logError( |
62 | 'Error on cargo-watch process : {\n' + err.message + '}\n' | ||
63 | ); | ||
53 | }); | 64 | }); |
54 | 65 | ||
55 | this.logInfo('cargo-watch started.'); | 66 | this.logInfo('cargo-watch started.'); |
@@ -66,7 +77,7 @@ export class CargoWatchProvider { | |||
66 | terminate(this.cargoProcess); | 77 | terminate(this.cargoProcess); |
67 | } | 78 | } |
68 | 79 | ||
69 | if(this.outputChannel) { | 80 | if (this.outputChannel) { |
70 | this.outputChannel.dispose(); | 81 | this.outputChannel.dispose(); |
71 | } | 82 | } |
72 | } | 83 | } |
@@ -74,13 +85,16 @@ export class CargoWatchProvider { | |||
74 | private logInfo(line: string) { | 85 | private logInfo(line: string) { |
75 | if (Server.config.cargoWatchOptions.trace === 'verbose') { | 86 | if (Server.config.cargoWatchOptions.trace === 'verbose') { |
76 | this.outputChannel!.append(line); | 87 | this.outputChannel!.append(line); |
77 | } | 88 | } |
78 | } | 89 | } |
79 | 90 | ||
80 | private logError(line: string) { | 91 | private logError(line: string) { |
81 | if (Server.config.cargoWatchOptions.trace === 'error' || Server.config.cargoWatchOptions.trace === 'verbose' ) { | 92 | if ( |
93 | Server.config.cargoWatchOptions.trace === 'error' || | ||
94 | Server.config.cargoWatchOptions.trace === 'verbose' | ||
95 | ) { | ||
82 | this.outputChannel!.append(line); | 96 | this.outputChannel!.append(line); |
83 | } | 97 | } |
84 | } | 98 | } |
85 | 99 | ||
86 | private parseLine(line: string) { | 100 | private parseLine(line: string) { |
@@ -105,12 +119,32 @@ export class CargoWatchProvider { | |||
105 | return vscode.DiagnosticSeverity.Information; | 119 | return vscode.DiagnosticSeverity.Information; |
106 | } | 120 | } |
107 | 121 | ||
122 | interface ErrorSpan { | ||
123 | line_start: number; | ||
124 | line_end: number; | ||
125 | column_start: number; | ||
126 | column_end: number; | ||
127 | } | ||
128 | |||
129 | interface ErrorMessage { | ||
130 | reason: string; | ||
131 | message: { | ||
132 | spans: ErrorSpan[]; | ||
133 | rendered: string; | ||
134 | level: string; | ||
135 | code?: { | ||
136 | code: string; | ||
137 | }; | ||
138 | }; | ||
139 | } | ||
140 | |||
108 | // cargo-watch itself output non json format | 141 | // cargo-watch itself output non json format |
109 | // Ignore these lines | 142 | // Ignore these lines |
110 | let data = null; | 143 | let data: ErrorMessage; |
111 | try { | 144 | try { |
112 | data = JSON.parse(line.trim()); | 145 | data = JSON.parse(line.trim()); |
113 | } catch (error) { | 146 | } catch (error) { |
147 | this.logError(`Fail to pass to json : { ${error} }`); | ||
114 | return; | 148 | return; |
115 | } | 149 | } |
116 | 150 | ||
@@ -137,7 +171,9 @@ export class CargoWatchProvider { | |||
137 | const diagnostic = new vscode.Diagnostic(range, rendered, level); | 171 | const diagnostic = new vscode.Diagnostic(range, rendered, level); |
138 | 172 | ||
139 | diagnostic.source = 'rustc'; | 173 | diagnostic.source = 'rustc'; |
140 | diagnostic.code = data.message.code.code; | 174 | diagnostic.code = data.message.code |
175 | ? data.message.code.code | ||
176 | : undefined; | ||
141 | diagnostic.relatedInformation = []; | 177 | diagnostic.relatedInformation = []; |
142 | 178 | ||
143 | const fileUrl = vscode.Uri.file(fileName!); | 179 | const fileUrl = vscode.Uri.file(fileName!); |
@@ -150,18 +186,4 @@ export class CargoWatchProvider { | |||
150 | this.diagnosticCollection!.set(fileUrl, diagnostics); | 186 | this.diagnosticCollection!.set(fileUrl, diagnostics); |
151 | } | 187 | } |
152 | } | 188 | } |
153 | |||
154 | private processOutput(chunk: string, cb: (line: string) => void ) { | ||
155 | // The stdout is not line based, convert it to line based for proceess. | ||
156 | this.outBuffer += chunk; | ||
157 | let eolIndex = this.outBuffer.indexOf('\n'); | ||
158 | while (eolIndex >= 0) { | ||
159 | // line includes the EOL | ||
160 | const line = this.outBuffer.slice(0, eolIndex + 1); | ||
161 | cb(line); | ||
162 | this.outBuffer = this.outBuffer.slice(eolIndex + 1); | ||
163 | |||
164 | eolIndex = this.outBuffer.indexOf('\n'); | ||
165 | } | ||
166 | } | ||
167 | } | 189 | } |
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/config.ts b/editors/code/src/config.ts index c95d13878..481a5e5f1 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts | |||
@@ -8,16 +8,21 @@ export type CargoWatchStartupOptions = 'ask' | 'enabled' | 'disabled'; | |||
8 | export type CargoWatchTraceOptions = 'off' | 'error' | 'verbose'; | 8 | export type CargoWatchTraceOptions = 'off' | 'error' | 'verbose'; |
9 | 9 | ||
10 | export interface CargoWatchOptions { | 10 | export interface CargoWatchOptions { |
11 | enableOnStartup: CargoWatchStartupOptions, | 11 | enableOnStartup: CargoWatchStartupOptions; |
12 | trace: CargoWatchTraceOptions, | 12 | checkArguments: string; |
13 | }; | 13 | trace: CargoWatchTraceOptions; |
14 | } | ||
14 | 15 | ||
15 | export class Config { | 16 | export class Config { |
16 | public highlightingOn = true; | 17 | public highlightingOn = true; |
17 | public enableEnhancedTyping = true; | 18 | public enableEnhancedTyping = true; |
18 | public raLspServerPath = RA_LSP_DEBUG || 'ra_lsp_server'; | 19 | public raLspServerPath = RA_LSP_DEBUG || 'ra_lsp_server'; |
19 | public showWorkspaceLoadedNotification = true; | 20 | public showWorkspaceLoadedNotification = true; |
20 | public cargoWatchOptions: CargoWatchOptions = { enableOnStartup: 'ask', trace: 'off' }; | 21 | public cargoWatchOptions: CargoWatchOptions = { |
22 | enableOnStartup: 'ask', | ||
23 | trace: 'off', | ||
24 | checkArguments: '' | ||
25 | }; | ||
21 | 26 | ||
22 | private prevEnhancedTyping: null | boolean = null; | 27 | private prevEnhancedTyping: null | boolean = null; |
23 | 28 | ||
@@ -79,17 +84,23 @@ export class Config { | |||
79 | } | 84 | } |
80 | 85 | ||
81 | if (config.has('enableCargoWatchOnStartup')) { | 86 | if (config.has('enableCargoWatchOnStartup')) { |
82 | this.cargoWatchOptions.enableOnStartup = | 87 | this.cargoWatchOptions.enableOnStartup = config.get< |
83 | config.get<CargoWatchStartupOptions>( | 88 | CargoWatchStartupOptions |
84 | 'enableCargoWatchOnStartup', | 89 | >('enableCargoWatchOnStartup', 'ask'); |
85 | 'ask' | 90 | } |
86 | ); | 91 | |
87 | this.cargoWatchOptions.trace = | 92 | if (config.has('trace.cargo-watch')) { |
88 | config.get<CargoWatchTraceOptions>( | 93 | this.cargoWatchOptions.trace = config.get<CargoWatchTraceOptions>( |
89 | 'trace.cargo-watch', | 94 | 'trace.cargo-watch', |
90 | 'off' | 95 | 'off' |
91 | ); | 96 | ); |
97 | } | ||
92 | 98 | ||
99 | if (config.has('cargo-watch.check-arguments')) { | ||
100 | this.cargoWatchOptions.checkArguments = config.get<string>( | ||
101 | 'cargo-watch.check-arguments', | ||
102 | '' | ||
103 | ); | ||
93 | } | 104 | } |
94 | } | 105 | } |
95 | } | 106 | } |
diff --git a/editors/code/src/utils/processes.ts b/editors/code/src/utils/processes.ts index 09fdf6e24..d4c2c8778 100644 --- a/editors/code/src/utils/processes.ts +++ b/editors/code/src/utils/processes.ts | |||
@@ -5,36 +5,40 @@ import ChildProcess = cp.ChildProcess; | |||
5 | 5 | ||
6 | import { join } from 'path'; | 6 | import { join } from 'path'; |
7 | 7 | ||
8 | const isWindows = (process.platform === 'win32'); | 8 | const isWindows = process.platform === 'win32'; |
9 | const isMacintosh = (process.platform === 'darwin'); | 9 | const isMacintosh = process.platform === 'darwin'; |
10 | const isLinux = (process.platform === 'linux'); | 10 | const isLinux = process.platform === 'linux'; |
11 | export function terminate(process: ChildProcess, cwd?: string): boolean { | 11 | export function terminate(process: ChildProcess, cwd?: string): boolean { |
12 | if (isWindows) { | 12 | if (isWindows) { |
13 | try { | 13 | try { |
14 | // This we run in Atom execFileSync is available. | 14 | // This we run in Atom execFileSync is available. |
15 | // Ignore stderr since this is otherwise piped to parent.stderr | 15 | // Ignore stderr since this is otherwise piped to parent.stderr |
16 | // which might be already closed. | 16 | // which might be already closed. |
17 | const options: any = { | 17 | const options: any = { |
18 | stdio: ['pipe', 'pipe', 'ignore'] | 18 | stdio: ['pipe', 'pipe', 'ignore'] |
19 | }; | 19 | }; |
20 | if (cwd) { | 20 | if (cwd) { |
21 | options.cwd = cwd | 21 | options.cwd = cwd; |
22 | } | 22 | } |
23 | (cp).execFileSync('taskkill', ['/T', '/F', '/PID', process.pid.toString()], options); | 23 | cp.execFileSync( |
24 | return true; | 24 | 'taskkill', |
25 | } catch (err) { | 25 | ['/T', '/F', '/PID', process.pid.toString()], |
26 | return false; | 26 | options |
27 | } | 27 | ); |
28 | } else if (isLinux || isMacintosh) { | 28 | return true; |
29 | try { | 29 | } catch (err) { |
30 | const cmd = join(__dirname, 'terminateProcess.sh'); | 30 | return false; |
31 | const result = cp.spawnSync(cmd, [process.pid.toString()]); | 31 | } |
32 | return result.error ? false : true; | 32 | } else if (isLinux || isMacintosh) { |
33 | } catch (err) { | 33 | try { |
34 | return false; | 34 | const cmd = join(__dirname, 'terminateProcess.sh'); |
35 | } | 35 | const result = cp.spawnSync(cmd, [process.pid.toString()]); |
36 | } else { | 36 | return result.error ? false : true; |
37 | process.kill('SIGKILL'); | 37 | } catch (err) { |
38 | return true; | 38 | return false; |
39 | } | 39 | } |
40 | } \ No newline at end of file | 40 | } else { |
41 | process.kill('SIGKILL'); | ||
42 | return true; | ||
43 | } | ||
44 | } | ||