aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/user/README.md2
-rw-r--r--editors/code/package.json10
-rw-r--r--editors/code/src/commands/cargo_watch.ts78
-rw-r--r--editors/code/src/commands/line_buffer.ts16
-rw-r--r--editors/code/src/config.ts39
-rw-r--r--editors/code/src/utils/processes.ts68
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';
3import * as vscode from 'vscode'; 3import * as vscode from 'vscode';
4import { Server } from '../server'; 4import { Server } from '../server';
5import { terminate } from '../utils/processes'; 5import { terminate } from '../utils/processes';
6import { LineBuffer } from './line_buffer';
6import { StatusDisplay } from './watch_status'; 7import { StatusDisplay } from './watch_status';
7 8
8
9export class CargoWatchProvider { 9export 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 @@
1export 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';
8export type CargoWatchTraceOptions = 'off' | 'error' | 'verbose'; 8export type CargoWatchTraceOptions = 'off' | 'error' | 'verbose';
9 9
10export interface CargoWatchOptions { 10export interface CargoWatchOptions {
11 enableOnStartup: CargoWatchStartupOptions, 11 enableOnStartup: CargoWatchStartupOptions;
12 trace: CargoWatchTraceOptions, 12 checkArguments: string;
13}; 13 trace: CargoWatchTraceOptions;
14}
14 15
15export class Config { 16export 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
6import { join } from 'path'; 6import { join } from 'path';
7 7
8const isWindows = (process.platform === 'win32'); 8const isWindows = process.platform === 'win32';
9const isMacintosh = (process.platform === 'darwin'); 9const isMacintosh = process.platform === 'darwin';
10const isLinux = (process.platform === 'linux'); 10const isLinux = process.platform === 'linux';
11export function terminate(process: ChildProcess, cwd?: string): boolean { 11export 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}