diff options
Diffstat (limited to 'editors/code/src')
-rw-r--r-- | editors/code/src/commands/cargo_watch.ts | 168 | ||||
-rw-r--r-- | editors/code/src/commands/runnables.ts | 29 | ||||
-rw-r--r-- | editors/code/src/extension.ts | 4 |
3 files changed, 177 insertions, 24 deletions
diff --git a/editors/code/src/commands/cargo_watch.ts b/editors/code/src/commands/cargo_watch.ts new file mode 100644 index 000000000..55a1909cb --- /dev/null +++ b/editors/code/src/commands/cargo_watch.ts | |||
@@ -0,0 +1,168 @@ | |||
1 | import * as child_process from 'child_process'; | ||
2 | import * as path from 'path'; | ||
3 | import * as vscode from 'vscode'; | ||
4 | import { setInterval } from 'timers'; | ||
5 | |||
6 | const spinnerFrames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']; | ||
7 | |||
8 | class StatusDisplay { | ||
9 | private i = 0; | ||
10 | private statusBarItem: vscode.StatusBarItem; | ||
11 | private timer?: NodeJS.Timeout; | ||
12 | |||
13 | constructor(subscriptions: vscode.Disposable[]) { | ||
14 | this.statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 10); | ||
15 | subscriptions.push(this.statusBarItem); | ||
16 | this.statusBarItem.hide(); | ||
17 | } | ||
18 | |||
19 | public show() { | ||
20 | this.timer = this.timer || setInterval(() => { | ||
21 | this.statusBarItem!.text = "cargo check " + this.frame(); | ||
22 | }, 300); | ||
23 | |||
24 | this.statusBarItem!.show(); | ||
25 | } | ||
26 | |||
27 | public hide() { | ||
28 | if(this.timer) { | ||
29 | clearInterval(this.timer); | ||
30 | this.timer = undefined; | ||
31 | } | ||
32 | |||
33 | this.statusBarItem!.hide(); | ||
34 | } | ||
35 | |||
36 | frame() { | ||
37 | return spinnerFrames[this.i = ++this.i % spinnerFrames.length]; | ||
38 | } | ||
39 | } | ||
40 | |||
41 | export class CargoWatchProvider { | ||
42 | private diagnosticCollection?: vscode.DiagnosticCollection; | ||
43 | private cargoProcess?: child_process.ChildProcess; | ||
44 | private outBuffer: string = ""; | ||
45 | private statusDisplay? : StatusDisplay; | ||
46 | |||
47 | constructor() { | ||
48 | } | ||
49 | |||
50 | public activate(subscriptions: vscode.Disposable[]) { | ||
51 | subscriptions.push(this); | ||
52 | this.diagnosticCollection = vscode.languages.createDiagnosticCollection("rustc"); | ||
53 | |||
54 | this.statusDisplay = new StatusDisplay(subscriptions); | ||
55 | |||
56 | // Start the cargo watch with json message | ||
57 | this.cargoProcess = child_process.spawn('cargo', | ||
58 | ["watch", "-x", "\"check --message-format json\""], | ||
59 | { | ||
60 | // stdio: ['ignore', 'pipe', 'ignore'], | ||
61 | shell: true, | ||
62 | cwd: vscode.workspace.rootPath, | ||
63 | }); | ||
64 | |||
65 | |||
66 | this.cargoProcess.stdout.on('data', (s: string) => { | ||
67 | this.processOutput(s); | ||
68 | }); | ||
69 | |||
70 | this.cargoProcess.stderr.on('data', (s: string) => { | ||
71 | console.error('Error on cargo watch : ' + s); | ||
72 | }); | ||
73 | |||
74 | this.cargoProcess.on('error', (err: Error) => { | ||
75 | console.error('Error on spawn cargo process : ' + err); | ||
76 | }); | ||
77 | } | ||
78 | |||
79 | public dispose(): void { | ||
80 | if (this.diagnosticCollection) { | ||
81 | this.diagnosticCollection.clear(); | ||
82 | this.diagnosticCollection.dispose(); | ||
83 | } | ||
84 | |||
85 | if (this.cargoProcess) { | ||
86 | this.cargoProcess.kill(); | ||
87 | } | ||
88 | } | ||
89 | |||
90 | parseLine(line: string) { | ||
91 | if (line.startsWith("[Running")) { | ||
92 | this.diagnosticCollection!.clear(); | ||
93 | this.statusDisplay!.show(); | ||
94 | } | ||
95 | |||
96 | if (line.startsWith("[Finished running")) { | ||
97 | this.statusDisplay!.hide(); | ||
98 | } | ||
99 | |||
100 | function getLevel(s: string): vscode.DiagnosticSeverity { | ||
101 | if (s === "error") | ||
102 | return vscode.DiagnosticSeverity.Error; | ||
103 | |||
104 | if (s.startsWith("warn")) | ||
105 | return vscode.DiagnosticSeverity.Warning; | ||
106 | |||
107 | return vscode.DiagnosticSeverity.Information; | ||
108 | } | ||
109 | |||
110 | // cargo-watch itself output non json format | ||
111 | // Ignore these lines | ||
112 | let data = null; | ||
113 | try { | ||
114 | data = JSON.parse(line.trim()); | ||
115 | } catch (error) { | ||
116 | return; | ||
117 | } | ||
118 | |||
119 | // Only handle compiler-message now | ||
120 | if (data.reason !== "compiler-message") { | ||
121 | return; | ||
122 | } | ||
123 | |||
124 | let spans: any[] = data.message.spans; | ||
125 | spans = spans.filter(o => o.is_primary); | ||
126 | let file_name = null; | ||
127 | |||
128 | // We only handle primary span right now. | ||
129 | if (spans.length > 0) { | ||
130 | let o = spans[0]; | ||
131 | |||
132 | console.log("o", o); | ||
133 | let rendered = data.message.rendered; | ||
134 | let level = getLevel(data.message.level); | ||
135 | let range = new vscode.Range( | ||
136 | new vscode.Position(o.line_start - 1, o.column_start - 1), | ||
137 | new vscode.Position(o.line_end - 1, o.column_end - 1) | ||
138 | ); | ||
139 | |||
140 | file_name = path.join(vscode.workspace.rootPath!, o.file_name); | ||
141 | const diagnostic = new vscode.Diagnostic(range, rendered, level); | ||
142 | |||
143 | diagnostic.source = 'rustc'; | ||
144 | diagnostic.code = data.message.code.code; | ||
145 | diagnostic.relatedInformation = []; | ||
146 | |||
147 | let fileUrl = vscode.Uri.file(file_name!); | ||
148 | |||
149 | let diagnostics: vscode.Diagnostic[] = [...(this.diagnosticCollection!.get(fileUrl) || [])]; | ||
150 | diagnostics.push(diagnostic); | ||
151 | |||
152 | this.diagnosticCollection!.set(fileUrl, diagnostics); | ||
153 | } | ||
154 | } | ||
155 | |||
156 | processOutput(chunk: string) { | ||
157 | // The stdout is not line based, convert it to line based for proceess. | ||
158 | this.outBuffer += chunk; | ||
159 | let eolIndex; | ||
160 | while ((eolIndex = this.outBuffer.indexOf('\n')) >= 0) { | ||
161 | // line includes the EOL | ||
162 | const line = this.outBuffer.slice(0, eolIndex + 1); | ||
163 | this.parseLine(line); | ||
164 | this.outBuffer = this.outBuffer.slice(eolIndex + 1); | ||
165 | } | ||
166 | } | ||
167 | |||
168 | } | ||
diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/commands/runnables.ts index 4187ef4d1..722db158a 100644 --- a/editors/code/src/commands/runnables.ts +++ b/editors/code/src/commands/runnables.ts | |||
@@ -1,9 +1,11 @@ | |||
1 | import * as child_process from 'child_process'; | 1 | import * as child_process from 'child_process'; |
2 | |||
2 | import * as util from 'util'; | 3 | import * as util from 'util'; |
3 | import * as vscode from 'vscode'; | 4 | import * as vscode from 'vscode'; |
4 | import * as lc from 'vscode-languageclient'; | 5 | import * as lc from 'vscode-languageclient'; |
5 | 6 | ||
6 | import { Server } from '../server'; | 7 | import { Server } from '../server'; |
8 | import { CargoWatchProvider } from './cargo_watch'; | ||
7 | 9 | ||
8 | interface RunnablesParams { | 10 | interface RunnablesParams { |
9 | textDocument: lc.TextDocumentIdentifier; | 11 | textDocument: lc.TextDocumentIdentifier; |
@@ -127,32 +129,13 @@ export async function handleSingle(runnable: Runnable) { | |||
127 | return vscode.tasks.executeTask(task); | 129 | return vscode.tasks.executeTask(task); |
128 | } | 130 | } |
129 | 131 | ||
130 | export const autoCargoWatchTask: vscode.Task = { | ||
131 | name: 'cargo watch', | ||
132 | source: 'rust-analyzer', | ||
133 | definition: { | ||
134 | type: 'watch' | ||
135 | }, | ||
136 | execution: new vscode.ShellExecution('cargo', ['watch'], { cwd: '.' }), | ||
137 | |||
138 | isBackground: true, | ||
139 | problemMatchers: ['$rustc-watch'], | ||
140 | presentationOptions: { | ||
141 | clear: true | ||
142 | }, | ||
143 | // Not yet exposed in the vscode.d.ts | ||
144 | // https://github.com/Microsoft/vscode/blob/ea7c31d770e04b51d586b0d3944f3a7feb03afb9/src/vs/workbench/contrib/tasks/common/tasks.ts#L444-L456 | ||
145 | runOptions: ({ | ||
146 | runOn: 2 // RunOnOptions.folderOpen | ||
147 | } as unknown) as vscode.RunOptions | ||
148 | }; | ||
149 | |||
150 | /** | 132 | /** |
151 | * Interactively asks the user whether we should run `cargo check` in order to | 133 | * Interactively asks the user whether we should run `cargo check` in order to |
152 | * provide inline diagnostics; the user is met with a series of dialog boxes | 134 | * provide inline diagnostics; the user is met with a series of dialog boxes |
153 | * that, when accepted, allow us to `cargo install cargo-watch` and then run it. | 135 | * that, when accepted, allow us to `cargo install cargo-watch` and then run it. |
154 | */ | 136 | */ |
155 | export async function interactivelyStartCargoWatch() { | 137 | export async function interactivelyStartCargoWatch(context: vscode.ExtensionContext) { |
138 | |||
156 | if (Server.config.enableCargoWatchOnStartup === 'disabled') { | 139 | if (Server.config.enableCargoWatchOnStartup === 'disabled') { |
157 | return; | 140 | return; |
158 | } | 141 | } |
@@ -212,5 +195,7 @@ export async function interactivelyStartCargoWatch() { | |||
212 | } | 195 | } |
213 | } | 196 | } |
214 | 197 | ||
215 | vscode.tasks.executeTask(autoCargoWatchTask); | 198 | |
199 | let validater = new CargoWatchProvider(); | ||
200 | validater.activate(context.subscriptions); | ||
216 | } | 201 | } |
diff --git a/editors/code/src/extension.ts b/editors/code/src/extension.ts index 2e13c87de..5cbf285e5 100644 --- a/editors/code/src/extension.ts +++ b/editors/code/src/extension.ts | |||
@@ -2,7 +2,7 @@ import * as vscode from 'vscode'; | |||
2 | import * as lc from 'vscode-languageclient'; | 2 | import * as lc from 'vscode-languageclient'; |
3 | 3 | ||
4 | import * as commands from './commands'; | 4 | import * as commands from './commands'; |
5 | import { interactivelyStartCargoWatch } from './commands/runnables'; | 5 | import { interactivelyStartCargoWatch} from './commands/runnables'; |
6 | import { SyntaxTreeContentProvider } from './commands/syntaxTree'; | 6 | import { SyntaxTreeContentProvider } from './commands/syntaxTree'; |
7 | import * as events from './events'; | 7 | import * as events from './events'; |
8 | import * as notifications from './notifications'; | 8 | import * as notifications from './notifications'; |
@@ -121,7 +121,7 @@ export function activate(context: vscode.ExtensionContext) { | |||
121 | ); | 121 | ); |
122 | 122 | ||
123 | // Executing `cargo watch` provides us with inline diagnostics on save | 123 | // Executing `cargo watch` provides us with inline diagnostics on save |
124 | interactivelyStartCargoWatch(); | 124 | interactivelyStartCargoWatch(context); |
125 | 125 | ||
126 | // Start the language server, finally! | 126 | // Start the language server, finally! |
127 | Server.start(allNotifications); | 127 | Server.start(allNotifications); |