mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 13:03:31 +00:00
Remove cargo-watch from vscode extension.
Still keeps tests around for reference when porting them to rust
This commit is contained in:
parent
66e8ef53a0
commit
41a1ec723c
6 changed files with 1 additions and 454 deletions
|
@ -18,7 +18,7 @@
|
|||
"scripts": {
|
||||
"vscode:prepublish": "npm run compile",
|
||||
"package": "vsce package",
|
||||
"compile": "rollup -c && shx cp src/utils/terminateProcess.sh bundle/terminateProcess.sh",
|
||||
"compile": "rollup -c",
|
||||
"watch": "tsc -watch -p ./",
|
||||
"fix": "prettier **/*.{json,ts} --write && tslint --project . --fix",
|
||||
"lint": "tslint --project .",
|
||||
|
@ -133,16 +133,6 @@
|
|||
"command": "rust-analyzer.reload",
|
||||
"title": "Restart server",
|
||||
"category": "Rust Analyzer"
|
||||
},
|
||||
{
|
||||
"command": "rust-analyzer.startCargoWatch",
|
||||
"title": "Start Cargo Watch",
|
||||
"category": "Rust Analyzer"
|
||||
},
|
||||
{
|
||||
"command": "rust-analyzer.stopCargoWatch",
|
||||
"title": "Stop Cargo Watch",
|
||||
"category": "Rust Analyzer"
|
||||
}
|
||||
],
|
||||
"keybindings": [
|
||||
|
|
|
@ -1,264 +0,0 @@
|
|||
import * as child_process from 'child_process';
|
||||
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';
|
||||
|
||||
import {
|
||||
mapRustDiagnosticToVsCode,
|
||||
RustDiagnostic,
|
||||
} from '../utils/diagnostics/rust';
|
||||
import SuggestedFixCollection from '../utils/diagnostics/SuggestedFixCollection';
|
||||
import { areDiagnosticsEqual } from '../utils/diagnostics/vscode';
|
||||
|
||||
export async function registerCargoWatchProvider(
|
||||
subscriptions: vscode.Disposable[],
|
||||
): Promise<CargoWatchProvider | undefined> {
|
||||
let cargoExists = false;
|
||||
|
||||
// Check if the working directory is valid cargo root path
|
||||
const cargoTomlPath = path.join(vscode.workspace.rootPath!, 'Cargo.toml');
|
||||
const cargoTomlUri = vscode.Uri.file(cargoTomlPath);
|
||||
const cargoTomlFileInfo = await vscode.workspace.fs.stat(cargoTomlUri);
|
||||
|
||||
if (cargoTomlFileInfo) {
|
||||
cargoExists = true;
|
||||
}
|
||||
|
||||
if (!cargoExists) {
|
||||
vscode.window.showErrorMessage(
|
||||
`Couldn\'t find \'Cargo.toml\' at ${cargoTomlPath}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const provider = new CargoWatchProvider();
|
||||
subscriptions.push(provider);
|
||||
return provider;
|
||||
}
|
||||
|
||||
export class CargoWatchProvider implements vscode.Disposable {
|
||||
private readonly diagnosticCollection: vscode.DiagnosticCollection;
|
||||
private readonly statusDisplay: StatusDisplay;
|
||||
private readonly outputChannel: vscode.OutputChannel;
|
||||
|
||||
private suggestedFixCollection: SuggestedFixCollection;
|
||||
private codeActionDispose: vscode.Disposable;
|
||||
|
||||
private cargoProcess?: child_process.ChildProcess;
|
||||
|
||||
constructor() {
|
||||
this.diagnosticCollection = vscode.languages.createDiagnosticCollection(
|
||||
'rustc',
|
||||
);
|
||||
this.statusDisplay = new StatusDisplay(
|
||||
Server.config.cargoWatchOptions.command,
|
||||
);
|
||||
this.outputChannel = vscode.window.createOutputChannel(
|
||||
'Cargo Watch Trace',
|
||||
);
|
||||
|
||||
// Track `rustc`'s suggested fixes so we can convert them to code actions
|
||||
this.suggestedFixCollection = new SuggestedFixCollection();
|
||||
this.codeActionDispose = vscode.languages.registerCodeActionsProvider(
|
||||
[{ scheme: 'file', language: 'rust' }],
|
||||
this.suggestedFixCollection,
|
||||
{
|
||||
providedCodeActionKinds:
|
||||
SuggestedFixCollection.PROVIDED_CODE_ACTION_KINDS,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
public start() {
|
||||
if (this.cargoProcess) {
|
||||
vscode.window.showInformationMessage(
|
||||
'Cargo Watch is already running',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let args =
|
||||
Server.config.cargoWatchOptions.command + ' --message-format json';
|
||||
if (Server.config.cargoWatchOptions.allTargets) {
|
||||
args += ' --all-targets';
|
||||
}
|
||||
if (Server.config.cargoWatchOptions.command.length > 0) {
|
||||
// Excape the double quote string:
|
||||
args += ' ' + Server.config.cargoWatchOptions.arguments;
|
||||
}
|
||||
// Windows handles arguments differently than the unix-likes, so we need to wrap the args in double quotes
|
||||
if (process.platform === 'win32') {
|
||||
args = '"' + args + '"';
|
||||
}
|
||||
|
||||
const ignoreFlags = Server.config.cargoWatchOptions.ignore.reduce(
|
||||
(flags, pattern) => [...flags, '--ignore', pattern],
|
||||
[] as string[],
|
||||
);
|
||||
|
||||
// Start the cargo watch with json message
|
||||
this.cargoProcess = child_process.spawn(
|
||||
'cargo',
|
||||
['watch', '-x', args, ...ignoreFlags],
|
||||
{
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
cwd: vscode.workspace.rootPath,
|
||||
windowsVerbatimArguments: true,
|
||||
},
|
||||
);
|
||||
|
||||
if (!this.cargoProcess) {
|
||||
vscode.window.showErrorMessage('Cargo Watch failed to start');
|
||||
return;
|
||||
}
|
||||
|
||||
const stdoutData = new LineBuffer();
|
||||
this.cargoProcess.stdout?.on('data', (s: string) => {
|
||||
stdoutData.processOutput(s, line => {
|
||||
this.logInfo(line);
|
||||
try {
|
||||
this.parseLine(line);
|
||||
} catch (err) {
|
||||
this.logError(`Failed to parse: ${err}, content : ${line}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const stderrData = new LineBuffer();
|
||||
this.cargoProcess.stderr?.on('data', (s: string) => {
|
||||
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.logInfo('cargo-watch started.');
|
||||
}
|
||||
|
||||
public stop() {
|
||||
if (this.cargoProcess) {
|
||||
this.cargoProcess.kill();
|
||||
terminate(this.cargoProcess);
|
||||
this.cargoProcess = undefined;
|
||||
} else {
|
||||
vscode.window.showInformationMessage('Cargo Watch is not running');
|
||||
}
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.stop();
|
||||
|
||||
this.diagnosticCollection.clear();
|
||||
this.diagnosticCollection.dispose();
|
||||
this.outputChannel.dispose();
|
||||
this.statusDisplay.dispose();
|
||||
this.codeActionDispose.dispose();
|
||||
}
|
||||
|
||||
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'
|
||||
) {
|
||||
this.outputChannel.append(line);
|
||||
}
|
||||
}
|
||||
|
||||
private parseLine(line: string) {
|
||||
if (line.startsWith('[Running')) {
|
||||
this.diagnosticCollection.clear();
|
||||
this.suggestedFixCollection.clear();
|
||||
this.statusDisplay.show();
|
||||
}
|
||||
|
||||
if (line.startsWith('[Finished running')) {
|
||||
this.statusDisplay.hide();
|
||||
}
|
||||
|
||||
interface CargoArtifact {
|
||||
reason: string;
|
||||
package_id: string;
|
||||
}
|
||||
|
||||
// https://github.com/rust-lang/cargo/blob/master/src/cargo/util/machine_message.rs
|
||||
interface CargoMessage {
|
||||
reason: string;
|
||||
package_id: string;
|
||||
message: RustDiagnostic;
|
||||
}
|
||||
|
||||
// cargo-watch itself output non json format
|
||||
// Ignore these lines
|
||||
let data: CargoMessage;
|
||||
try {
|
||||
data = JSON.parse(line.trim());
|
||||
} catch (error) {
|
||||
this.logError(`Fail to parse to json : { ${error} }`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.reason === 'compiler-artifact') {
|
||||
const msg = data as CargoArtifact;
|
||||
|
||||
// The format of the package_id is "{name} {version} ({source_id})",
|
||||
// https://github.com/rust-lang/cargo/blob/37ad03f86e895bb80b474c1c088322634f4725f5/src/cargo/core/package_id.rs#L53
|
||||
this.statusDisplay.packageName = msg.package_id.split(' ')[0];
|
||||
} else if (data.reason === 'compiler-message') {
|
||||
const msg = data.message as RustDiagnostic;
|
||||
|
||||
const mapResult = mapRustDiagnosticToVsCode(msg);
|
||||
if (!mapResult) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { location, diagnostic, suggestedFixes } = mapResult;
|
||||
const fileUri = location.uri;
|
||||
|
||||
const diagnostics: vscode.Diagnostic[] = [
|
||||
...(this.diagnosticCollection!.get(fileUri) || []),
|
||||
];
|
||||
|
||||
// If we're building multiple targets it's possible we've already seen this diagnostic
|
||||
const isDuplicate = diagnostics.some(d =>
|
||||
areDiagnosticsEqual(d, diagnostic),
|
||||
);
|
||||
if (isDuplicate) {
|
||||
return;
|
||||
}
|
||||
|
||||
diagnostics.push(diagnostic);
|
||||
this.diagnosticCollection!.set(fileUri, diagnostics);
|
||||
|
||||
if (suggestedFixes.length) {
|
||||
for (const suggestedFix of suggestedFixes) {
|
||||
this.suggestedFixCollection.addSuggestedFixForDiagnostic(
|
||||
suggestedFix,
|
||||
diagnostic,
|
||||
);
|
||||
}
|
||||
|
||||
// Have VsCode query us for the code actions
|
||||
vscode.commands.executeCommand(
|
||||
'vscode.executeCodeActionProvider',
|
||||
fileUri,
|
||||
diagnostic.range,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +1,7 @@
|
|||
import * as child_process from 'child_process';
|
||||
|
||||
import * as util from 'util';
|
||||
import * as vscode from 'vscode';
|
||||
import * as lc from 'vscode-languageclient';
|
||||
|
||||
import { Server } from '../server';
|
||||
import { CargoWatchProvider, registerCargoWatchProvider } from './cargo_watch';
|
||||
|
||||
interface RunnablesParams {
|
||||
textDocument: lc.TextDocumentIdentifier;
|
||||
|
@ -131,90 +127,3 @@ export async function handleSingle(runnable: Runnable) {
|
|||
|
||||
return vscode.tasks.executeTask(task);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interactively asks the user whether we should run `cargo check` in order to
|
||||
* provide inline diagnostics; the user is met with a series of dialog boxes
|
||||
* that, when accepted, allow us to `cargo install cargo-watch` and then run it.
|
||||
*/
|
||||
export async function interactivelyStartCargoWatch(
|
||||
context: vscode.ExtensionContext,
|
||||
): Promise<CargoWatchProvider | undefined> {
|
||||
if (Server.config.cargoWatchOptions.enableOnStartup === 'disabled') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Server.config.cargoWatchOptions.enableOnStartup === 'ask') {
|
||||
const watch = await vscode.window.showInformationMessage(
|
||||
'Start watching changes with cargo? (Executes `cargo watch`, provides inline diagnostics)',
|
||||
'yes',
|
||||
'no',
|
||||
);
|
||||
if (watch !== 'yes') {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return startCargoWatch(context);
|
||||
}
|
||||
|
||||
export async function startCargoWatch(
|
||||
context: vscode.ExtensionContext,
|
||||
): Promise<CargoWatchProvider | undefined> {
|
||||
const execPromise = util.promisify(child_process.exec);
|
||||
|
||||
const { stderr, code = 0 } = await execPromise(
|
||||
'cargo watch --version',
|
||||
).catch(e => e);
|
||||
|
||||
if (stderr.includes('no such subcommand: `watch`')) {
|
||||
const msg =
|
||||
'The `cargo-watch` subcommand is not installed. Install? (takes ~1-2 minutes)';
|
||||
const install = await vscode.window.showInformationMessage(
|
||||
msg,
|
||||
'yes',
|
||||
'no',
|
||||
);
|
||||
if (install !== 'yes') {
|
||||
return;
|
||||
}
|
||||
|
||||
const label = 'install-cargo-watch';
|
||||
const taskFinished = new Promise((resolve, _reject) => {
|
||||
const disposable = vscode.tasks.onDidEndTask(({ execution }) => {
|
||||
if (execution.task.name === label) {
|
||||
disposable.dispose();
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
vscode.tasks.executeTask(
|
||||
createTask({
|
||||
label,
|
||||
bin: 'cargo',
|
||||
args: ['install', 'cargo-watch'],
|
||||
env: {},
|
||||
}),
|
||||
);
|
||||
await taskFinished;
|
||||
const output = await execPromise('cargo watch --version').catch(e => e);
|
||||
if (output.stderr !== '') {
|
||||
vscode.window.showErrorMessage(
|
||||
`Couldn't install \`cargo-\`watch: ${output.stderr}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
} else if (code !== 0) {
|
||||
vscode.window.showErrorMessage(
|
||||
`\`cargo watch\` failed with ${code}: ${stderr}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const provider = await registerCargoWatchProvider(context.subscriptions);
|
||||
if (provider) {
|
||||
provider.start();
|
||||
}
|
||||
return provider;
|
||||
}
|
||||
|
|
|
@ -2,13 +2,8 @@ import * as vscode from 'vscode';
|
|||
import * as lc from 'vscode-languageclient';
|
||||
|
||||
import * as commands from './commands';
|
||||
import { CargoWatchProvider } from './commands/cargo_watch';
|
||||
import { ExpandMacroContentProvider } from './commands/expand_macro';
|
||||
import { HintsUpdater } from './commands/inlay_hints';
|
||||
import {
|
||||
interactivelyStartCargoWatch,
|
||||
startCargoWatch,
|
||||
} from './commands/runnables';
|
||||
import { SyntaxTreeContentProvider } from './commands/syntaxTree';
|
||||
import * as events from './events';
|
||||
import * as notifications from './notifications';
|
||||
|
@ -139,26 +134,6 @@ export async function activate(context: vscode.ExtensionContext) {
|
|||
|
||||
vscode.commands.registerCommand('rust-analyzer.reload', reloadCommand);
|
||||
|
||||
// Executing `cargo watch` provides us with inline diagnostics on save
|
||||
let provider: CargoWatchProvider | undefined;
|
||||
interactivelyStartCargoWatch(context).then(p => {
|
||||
provider = p;
|
||||
});
|
||||
registerCommand('rust-analyzer.startCargoWatch', () => {
|
||||
if (provider) {
|
||||
provider.start();
|
||||
} else {
|
||||
startCargoWatch(context).then(p => {
|
||||
provider = p;
|
||||
});
|
||||
}
|
||||
});
|
||||
registerCommand('rust-analyzer.stopCargoWatch', () => {
|
||||
if (provider) {
|
||||
provider.stop();
|
||||
}
|
||||
});
|
||||
|
||||
// Start the language server, finally!
|
||||
try {
|
||||
await startServer();
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
import * as cp from 'child_process';
|
||||
import ChildProcess = cp.ChildProcess;
|
||||
|
||||
import { join } from 'path';
|
||||
|
||||
const isWindows = process.platform === 'win32';
|
||||
const isMacintosh = process.platform === 'darwin';
|
||||
const isLinux = process.platform === 'linux';
|
||||
|
||||
// this is very complex, but is basically copy-pased from VSCode implementation here:
|
||||
// https://github.com/Microsoft/vscode-languageserver-node/blob/dbfd37e35953ad0ee14c4eeced8cfbc41697b47e/client/src/utils/processes.ts#L15
|
||||
|
||||
// And see discussion at
|
||||
// https://github.com/rust-analyzer/rust-analyzer/pull/1079#issuecomment-478908109
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
terminateTree() {
|
||||
for cpid in $(pgrep -P $1); do
|
||||
terminateTree $cpid
|
||||
done
|
||||
kill -9 $1 > /dev/null 2>&1
|
||||
}
|
||||
|
||||
for pid in $*; do
|
||||
terminateTree $pid
|
||||
done
|
Loading…
Reference in a new issue