mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-13 05:38:46 +00:00
Add cargo-watch.check-arguments
This commit is contained in:
parent
ee05eafe6c
commit
02e450f354
6 changed files with 139 additions and 74 deletions
|
@ -59,6 +59,8 @@ for details.
|
|||
* `rust-analyzer.raLspServerPath`: path to `ra_lsp_server` executable
|
||||
* `rust-analyzer.enableCargoWatchOnStartup`: prompt to install & enable `cargo
|
||||
watch` for live error highlighting (note, this **does not** use rust-analyzer)
|
||||
* `rust-analyzer.cargo-watch.check-arguments`: cargo-watch check arguments.
|
||||
(e.g: `--features="shumway,pdf"` will run as `cargo watch -x "check --features="shumway,pdf""` )
|
||||
* `rust-analyzer.trace.server`: enables internal logging
|
||||
* `rust-analyzer.trace.cargo-watch`: enables cargo-watch logging
|
||||
|
||||
|
|
|
@ -184,6 +184,11 @@
|
|||
],
|
||||
"description": "Whether to run `cargo watch` on startup"
|
||||
},
|
||||
"rust-analyzer.cargo-watch.check-arguments": {
|
||||
"type": "string",
|
||||
"description": "`cargo-watch` check arguments. (e.g: `--features=\"shumway,pdf\"` will run as `cargo watch -x \"check --features=\"shumway,pdf\"\"` )",
|
||||
"default": ""
|
||||
},
|
||||
"rust-analyzer.trace.server": {
|
||||
"type": "string",
|
||||
"scope": "window",
|
||||
|
@ -192,6 +197,11 @@
|
|||
"messages",
|
||||
"verbose"
|
||||
],
|
||||
"enumDescriptions": [
|
||||
"No traces",
|
||||
"Error only",
|
||||
"Full log"
|
||||
],
|
||||
"default": "off",
|
||||
"description": "Trace requests to the ra_lsp_server"
|
||||
},
|
||||
|
|
|
@ -3,9 +3,9 @@ import * as path from 'path';
|
|||
import * as vscode from 'vscode';
|
||||
import { Server } from '../server';
|
||||
import { terminate } from '../utils/processes';
|
||||
import { LineBuffer } from './line_buffer';
|
||||
import { StatusDisplay } from './watch_status';
|
||||
|
||||
|
||||
export class CargoWatchProvider {
|
||||
private diagnosticCollection?: vscode.DiagnosticCollection;
|
||||
private cargoProcess?: child_process.ChildProcess;
|
||||
|
@ -23,33 +23,44 @@ export class CargoWatchProvider {
|
|||
this.outputChannel = vscode.window.createOutputChannel(
|
||||
'Cargo Watch Trace'
|
||||
);
|
||||
|
||||
|
||||
let args = '"check --message-format json';
|
||||
if (Server.config.cargoWatchOptions.checkArguments.length > 0) {
|
||||
// Excape the double quote string:
|
||||
args += ' ' + Server.config.cargoWatchOptions.checkArguments;
|
||||
}
|
||||
args += '"';
|
||||
|
||||
// Start the cargo watch with json message
|
||||
this.cargoProcess = child_process.spawn(
|
||||
'cargo',
|
||||
['watch', '-x', '\"check --message-format json\"'],
|
||||
['watch', '-x', args],
|
||||
{
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
cwd: vscode.workspace.rootPath,
|
||||
windowsVerbatimArguments: true,
|
||||
windowsVerbatimArguments: true
|
||||
}
|
||||
);
|
||||
|
||||
const stdoutData = new LineBuffer();
|
||||
this.cargoProcess.stdout.on('data', (s: string) => {
|
||||
this.processOutput(s, (line) => {
|
||||
stdoutData.processOutput(s, line => {
|
||||
this.logInfo(line);
|
||||
this.parseLine(line);
|
||||
});
|
||||
});
|
||||
|
||||
const stderrData = new LineBuffer();
|
||||
this.cargoProcess.stderr.on('data', (s: string) => {
|
||||
this.processOutput(s, (line) => {
|
||||
this.logError('Error on cargo-watch : {\n' + line + '}\n' );
|
||||
stderrData.processOutput(s, line => {
|
||||
this.logError('Error on cargo-watch : {\n' + line + '}\n');
|
||||
});
|
||||
});
|
||||
|
||||
this.cargoProcess.on('error', (err: Error) => {
|
||||
this.logError('Error on cargo-watch process : {\n' + err.message + '}\n');
|
||||
this.logError(
|
||||
'Error on cargo-watch process : {\n' + err.message + '}\n'
|
||||
);
|
||||
});
|
||||
|
||||
this.logInfo('cargo-watch started.');
|
||||
|
@ -66,7 +77,7 @@ export class CargoWatchProvider {
|
|||
terminate(this.cargoProcess);
|
||||
}
|
||||
|
||||
if(this.outputChannel) {
|
||||
if (this.outputChannel) {
|
||||
this.outputChannel.dispose();
|
||||
}
|
||||
}
|
||||
|
@ -74,13 +85,16 @@ export class CargoWatchProvider {
|
|||
private logInfo(line: string) {
|
||||
if (Server.config.cargoWatchOptions.trace === 'verbose') {
|
||||
this.outputChannel!.append(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private logError(line: string) {
|
||||
if (Server.config.cargoWatchOptions.trace === 'error' || Server.config.cargoWatchOptions.trace === 'verbose' ) {
|
||||
if (
|
||||
Server.config.cargoWatchOptions.trace === 'error' ||
|
||||
Server.config.cargoWatchOptions.trace === 'verbose'
|
||||
) {
|
||||
this.outputChannel!.append(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private parseLine(line: string) {
|
||||
|
@ -105,12 +119,32 @@ export class CargoWatchProvider {
|
|||
return vscode.DiagnosticSeverity.Information;
|
||||
}
|
||||
|
||||
interface ErrorSpan {
|
||||
line_start: number;
|
||||
line_end: number;
|
||||
column_start: number;
|
||||
column_end: number;
|
||||
}
|
||||
|
||||
interface ErrorMessage {
|
||||
reason: string;
|
||||
message: {
|
||||
spans: ErrorSpan[];
|
||||
rendered: string;
|
||||
level: string;
|
||||
code?: {
|
||||
code: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
// cargo-watch itself output non json format
|
||||
// Ignore these lines
|
||||
let data = null;
|
||||
let data: ErrorMessage;
|
||||
try {
|
||||
data = JSON.parse(line.trim());
|
||||
} catch (error) {
|
||||
this.logError(`Fail to pass to json : { ${error} }`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -137,7 +171,9 @@ export class CargoWatchProvider {
|
|||
const diagnostic = new vscode.Diagnostic(range, rendered, level);
|
||||
|
||||
diagnostic.source = 'rustc';
|
||||
diagnostic.code = data.message.code.code;
|
||||
diagnostic.code = data.message.code
|
||||
? data.message.code.code
|
||||
: undefined;
|
||||
diagnostic.relatedInformation = [];
|
||||
|
||||
const fileUrl = vscode.Uri.file(fileName!);
|
||||
|
@ -150,18 +186,4 @@ export class CargoWatchProvider {
|
|||
this.diagnosticCollection!.set(fileUrl, diagnostics);
|
||||
}
|
||||
}
|
||||
|
||||
private processOutput(chunk: string, cb: (line: string) => void ) {
|
||||
// The stdout is not line based, convert it to line based for proceess.
|
||||
this.outBuffer += chunk;
|
||||
let eolIndex = this.outBuffer.indexOf('\n');
|
||||
while (eolIndex >= 0) {
|
||||
// line includes the EOL
|
||||
const line = this.outBuffer.slice(0, eolIndex + 1);
|
||||
cb(line);
|
||||
this.outBuffer = this.outBuffer.slice(eolIndex + 1);
|
||||
|
||||
eolIndex = this.outBuffer.indexOf('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
16
editors/code/src/commands/line_buffer.ts
Normal file
16
editors/code/src/commands/line_buffer.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
export class LineBuffer {
|
||||
private outBuffer: string = '';
|
||||
|
||||
public processOutput(chunk: string, cb: (line: string) => void) {
|
||||
this.outBuffer += chunk;
|
||||
let eolIndex = this.outBuffer.indexOf('\n');
|
||||
while (eolIndex >= 0) {
|
||||
// line includes the EOL
|
||||
const line = this.outBuffer.slice(0, eolIndex + 1);
|
||||
cb(line);
|
||||
this.outBuffer = this.outBuffer.slice(eolIndex + 1);
|
||||
|
||||
eolIndex = this.outBuffer.indexOf('\n');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,16 +8,21 @@ export type CargoWatchStartupOptions = 'ask' | 'enabled' | 'disabled';
|
|||
export type CargoWatchTraceOptions = 'off' | 'error' | 'verbose';
|
||||
|
||||
export interface CargoWatchOptions {
|
||||
enableOnStartup: CargoWatchStartupOptions,
|
||||
trace: CargoWatchTraceOptions,
|
||||
};
|
||||
enableOnStartup: CargoWatchStartupOptions;
|
||||
checkArguments: string;
|
||||
trace: CargoWatchTraceOptions;
|
||||
}
|
||||
|
||||
export class Config {
|
||||
public highlightingOn = true;
|
||||
public enableEnhancedTyping = true;
|
||||
public raLspServerPath = RA_LSP_DEBUG || 'ra_lsp_server';
|
||||
public showWorkspaceLoadedNotification = true;
|
||||
public cargoWatchOptions: CargoWatchOptions = { enableOnStartup: 'ask', trace: 'off' };
|
||||
public cargoWatchOptions: CargoWatchOptions = {
|
||||
enableOnStartup: 'ask',
|
||||
trace: 'off',
|
||||
checkArguments: ''
|
||||
};
|
||||
|
||||
private prevEnhancedTyping: null | boolean = null;
|
||||
|
||||
|
@ -79,17 +84,23 @@ export class Config {
|
|||
}
|
||||
|
||||
if (config.has('enableCargoWatchOnStartup')) {
|
||||
this.cargoWatchOptions.enableOnStartup =
|
||||
config.get<CargoWatchStartupOptions>(
|
||||
'enableCargoWatchOnStartup',
|
||||
'ask'
|
||||
);
|
||||
this.cargoWatchOptions.trace =
|
||||
config.get<CargoWatchTraceOptions>(
|
||||
'trace.cargo-watch',
|
||||
'off'
|
||||
);
|
||||
this.cargoWatchOptions.enableOnStartup = config.get<
|
||||
CargoWatchStartupOptions
|
||||
>('enableCargoWatchOnStartup', 'ask');
|
||||
}
|
||||
|
||||
if (config.has('trace.cargo-watch')) {
|
||||
this.cargoWatchOptions.trace = config.get<CargoWatchTraceOptions>(
|
||||
'trace.cargo-watch',
|
||||
'off'
|
||||
);
|
||||
}
|
||||
|
||||
if (config.has('cargo-watch.check-arguments')) {
|
||||
this.cargoWatchOptions.checkArguments = config.get<string>(
|
||||
'cargo-watch.check-arguments',
|
||||
''
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,36 +5,40 @@ import ChildProcess = cp.ChildProcess;
|
|||
|
||||
import { join } from 'path';
|
||||
|
||||
const isWindows = (process.platform === 'win32');
|
||||
const isMacintosh = (process.platform === 'darwin');
|
||||
const isLinux = (process.platform === 'linux');
|
||||
const isWindows = process.platform === 'win32';
|
||||
const isMacintosh = process.platform === 'darwin';
|
||||
const isLinux = process.platform === 'linux';
|
||||
export function terminate(process: ChildProcess, cwd?: string): boolean {
|
||||
if (isWindows) {
|
||||
try {
|
||||
// This we run in Atom execFileSync is available.
|
||||
// Ignore stderr since this is otherwise piped to parent.stderr
|
||||
// which might be already closed.
|
||||
const options: any = {
|
||||
stdio: ['pipe', 'pipe', 'ignore']
|
||||
};
|
||||
if (cwd) {
|
||||
options.cwd = cwd
|
||||
}
|
||||
(cp).execFileSync('taskkill', ['/T', '/F', '/PID', process.pid.toString()], options);
|
||||
return true;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
} else if (isLinux || isMacintosh) {
|
||||
try {
|
||||
const cmd = join(__dirname, 'terminateProcess.sh');
|
||||
const result = cp.spawnSync(cmd, [process.pid.toString()]);
|
||||
return result.error ? false : true;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
process.kill('SIGKILL');
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (isWindows) {
|
||||
try {
|
||||
// This we run in Atom execFileSync is available.
|
||||
// Ignore stderr since this is otherwise piped to parent.stderr
|
||||
// which might be already closed.
|
||||
const options: any = {
|
||||
stdio: ['pipe', 'pipe', 'ignore']
|
||||
};
|
||||
if (cwd) {
|
||||
options.cwd = cwd;
|
||||
}
|
||||
cp.execFileSync(
|
||||
'taskkill',
|
||||
['/T', '/F', '/PID', process.pid.toString()],
|
||||
options
|
||||
);
|
||||
return true;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
} else if (isLinux || isMacintosh) {
|
||||
try {
|
||||
const cmd = join(__dirname, 'terminateProcess.sh');
|
||||
const result = cp.spawnSync(cmd, [process.pid.toString()]);
|
||||
return result.error ? false : true;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
process.kill('SIGKILL');
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue