about summary refs log tree commit diff
path: root/editors/code
diff options
context:
space:
mode:
Diffstat (limited to 'editors/code')
-rw-r--r--editors/code/package.json12
-rw-r--r--editors/code/src/commands/cargo_watch.ts168
-rw-r--r--editors/code/src/commands/runnables.ts29
-rw-r--r--editors/code/src/extension.ts4
4 files changed, 177 insertions, 36 deletions
diff --git a/editors/code/package.json b/editors/code/package.json
index facb633d9ad..240aff6c9bc 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -223,18 +223,6 @@
                     "${workspaceRoot}"
                 ],
                 "pattern": "$rustc"
-            },
-            {
-                "name": "rustc-watch",
-                "fileLocation": [
-                    "relative",
-                    "${workspaceRoot}"
-                ],
-                "background": {
-                    "beginsPattern": "^\\[Running\\b",
-                    "endsPattern": "^\\[Finished running\\b"
-                },
-                "pattern": "$rustc"
             }
         ]
     }
diff --git a/editors/code/src/commands/cargo_watch.ts b/editors/code/src/commands/cargo_watch.ts
new file mode 100644
index 00000000000..55a1909cb8f
--- /dev/null
+++ b/editors/code/src/commands/cargo_watch.ts
@@ -0,0 +1,168 @@
+import * as child_process from 'child_process';
+import * as path from 'path';
+import * as vscode from 'vscode';
+import { setInterval } from 'timers';
+
+const spinnerFrames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
+
+class StatusDisplay {
+    private i = 0;    
+    private statusBarItem: vscode.StatusBarItem;
+    private timer?: NodeJS.Timeout;
+
+    constructor(subscriptions: vscode.Disposable[]) {
+        this.statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 10);
+        subscriptions.push(this.statusBarItem);
+        this.statusBarItem.hide();
+    }
+
+    public show() {
+        this.timer = this.timer || setInterval(() => {
+            this.statusBarItem!.text = "cargo check " + this.frame();
+        }, 300);        
+        
+        this.statusBarItem!.show();            
+    }
+
+    public hide() {
+        if(this.timer) {
+            clearInterval(this.timer);
+            this.timer = undefined;
+        }
+
+        this.statusBarItem!.hide();                        
+    }
+
+    frame() {
+        return spinnerFrames[this.i = ++this.i % spinnerFrames.length];
+    }
+}
+
+export class CargoWatchProvider {
+    private diagnosticCollection?: vscode.DiagnosticCollection;
+    private cargoProcess?: child_process.ChildProcess;
+    private outBuffer: string = "";    
+    private statusDisplay? : StatusDisplay;
+
+    constructor() {
+    }
+
+    public activate(subscriptions: vscode.Disposable[]) {
+        subscriptions.push(this);
+        this.diagnosticCollection = vscode.languages.createDiagnosticCollection("rustc");
+
+        this.statusDisplay = new StatusDisplay(subscriptions);
+
+        // Start the cargo watch with json message 
+        this.cargoProcess = child_process.spawn('cargo',
+            ["watch", "-x", "\"check --message-format json\""],
+            {
+                // stdio: ['ignore', 'pipe', 'ignore'], 
+                shell: true,
+                cwd: vscode.workspace.rootPath,
+            });
+
+
+        this.cargoProcess.stdout.on('data', (s: string) => {
+            this.processOutput(s);
+        });
+
+        this.cargoProcess.stderr.on('data', (s: string) => {
+            console.error('Error on cargo watch : ' + s);
+        });
+
+        this.cargoProcess.on('error', (err: Error) => {
+            console.error('Error on spawn cargo process : ' + err);
+        });
+    }
+
+    public dispose(): void {
+        if (this.diagnosticCollection) {
+            this.diagnosticCollection.clear();
+            this.diagnosticCollection.dispose();
+        }
+
+        if (this.cargoProcess) {
+            this.cargoProcess.kill();
+        }
+    }
+
+    parseLine(line: string) {
+        if (line.startsWith("[Running")) {
+            this.diagnosticCollection!.clear();
+            this.statusDisplay!.show();
+        }
+
+        if (line.startsWith("[Finished running")) {
+            this.statusDisplay!.hide();
+        }
+
+        function getLevel(s: string): vscode.DiagnosticSeverity {
+            if (s === "error")
+                return vscode.DiagnosticSeverity.Error;
+
+            if (s.startsWith("warn"))
+                return vscode.DiagnosticSeverity.Warning;
+
+            return vscode.DiagnosticSeverity.Information;
+        }
+
+        // cargo-watch itself output non json format
+        // Ignore these lines
+        let data = null;
+        try {
+            data = JSON.parse(line.trim());
+        } catch (error) {
+            return;
+        }
+
+        // Only handle compiler-message now
+        if (data.reason !== "compiler-message") {
+            return;
+        }
+
+        let spans: any[] = data.message.spans;
+        spans = spans.filter(o => o.is_primary);
+        let file_name = null;
+
+        // We only handle primary span right now.
+        if (spans.length > 0) {
+            let o = spans[0];
+
+            console.log("o", o);
+            let rendered = data.message.rendered;
+            let level = getLevel(data.message.level);
+            let range = new vscode.Range(
+                new vscode.Position(o.line_start - 1, o.column_start - 1),
+                new vscode.Position(o.line_end - 1, o.column_end - 1)
+            );
+
+            file_name = path.join(vscode.workspace.rootPath!, o.file_name);
+            const diagnostic = new vscode.Diagnostic(range, rendered, level);
+
+            diagnostic.source = 'rustc';
+            diagnostic.code = data.message.code.code;
+            diagnostic.relatedInformation = [];
+
+            let fileUrl = vscode.Uri.file(file_name!);
+
+            let diagnostics: vscode.Diagnostic[] = [...(this.diagnosticCollection!.get(fileUrl) || [])];
+            diagnostics.push(diagnostic);
+
+            this.diagnosticCollection!.set(fileUrl, diagnostics);
+        }
+    }
+
+    processOutput(chunk: string) {
+        // The stdout is not line based, convert it to line based for proceess.
+        this.outBuffer += chunk;
+        let eolIndex;
+        while ((eolIndex = this.outBuffer.indexOf('\n')) >= 0) {
+            // line includes the EOL
+            const line = this.outBuffer.slice(0, eolIndex + 1);
+            this.parseLine(line);
+            this.outBuffer = this.outBuffer.slice(eolIndex + 1);
+        }
+    }
+
+}
diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/commands/runnables.ts
index 4187ef4d157..722db158aa0 100644
--- a/editors/code/src/commands/runnables.ts
+++ b/editors/code/src/commands/runnables.ts
@@ -1,9 +1,11 @@
 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 } from './cargo_watch';
 
 interface RunnablesParams {
     textDocument: lc.TextDocumentIdentifier;
@@ -127,32 +129,13 @@ export async function handleSingle(runnable: Runnable) {
     return vscode.tasks.executeTask(task);
 }
 
-export const autoCargoWatchTask: vscode.Task = {
-    name: 'cargo watch',
-    source: 'rust-analyzer',
-    definition: {
-        type: 'watch'
-    },
-    execution: new vscode.ShellExecution('cargo', ['watch'], { cwd: '.' }),
-
-    isBackground: true,
-    problemMatchers: ['$rustc-watch'],
-    presentationOptions: {
-        clear: true
-    },
-    // Not yet exposed in the vscode.d.ts
-    // https://github.com/Microsoft/vscode/blob/ea7c31d770e04b51d586b0d3944f3a7feb03afb9/src/vs/workbench/contrib/tasks/common/tasks.ts#L444-L456
-    runOptions: ({
-        runOn: 2 // RunOnOptions.folderOpen
-    } as unknown) as vscode.RunOptions
-};
-
 /**
  * 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() {
+export async function interactivelyStartCargoWatch(context: vscode.ExtensionContext) {
+
     if (Server.config.enableCargoWatchOnStartup === 'disabled') {
         return;
     }
@@ -212,5 +195,7 @@ export async function interactivelyStartCargoWatch() {
         }
     }
 
-    vscode.tasks.executeTask(autoCargoWatchTask);
+
+    let validater = new CargoWatchProvider();
+    validater.activate(context.subscriptions);    
 }
diff --git a/editors/code/src/extension.ts b/editors/code/src/extension.ts
index 2e13c87de16..5cbf285e58d 100644
--- a/editors/code/src/extension.ts
+++ b/editors/code/src/extension.ts
@@ -2,7 +2,7 @@ import * as vscode from 'vscode';
 import * as lc from 'vscode-languageclient';
 
 import * as commands from './commands';
-import { interactivelyStartCargoWatch } from './commands/runnables';
+import { interactivelyStartCargoWatch} from './commands/runnables';
 import { SyntaxTreeContentProvider } from './commands/syntaxTree';
 import * as events from './events';
 import * as notifications from './notifications';
@@ -121,7 +121,7 @@ export function activate(context: vscode.ExtensionContext) {
     );
 
     // Executing `cargo watch` provides us with inline diagnostics on save
-    interactivelyStartCargoWatch();
+    interactivelyStartCargoWatch(context);
 
     // Start the language server, finally!
     Server.start(allNotifications);