aboutsummaryrefslogtreecommitdiff
path: root/editors/code/src
diff options
context:
space:
mode:
Diffstat (limited to 'editors/code/src')
-rw-r--r--editors/code/src/commands/cargo_watch.ts102
-rw-r--r--editors/code/src/commands/extend_selection.ts34
-rw-r--r--editors/code/src/commands/index.ts2
-rw-r--r--editors/code/src/commands/runnables.ts17
-rw-r--r--editors/code/src/commands/watch_status.ts18
-rw-r--r--editors/code/src/extension.ts29
-rw-r--r--editors/code/src/server.ts1
7 files changed, 108 insertions, 95 deletions
diff --git a/editors/code/src/commands/cargo_watch.ts b/editors/code/src/commands/cargo_watch.ts
index 32bd38a1c..1d939e28c 100644
--- a/editors/code/src/commands/cargo_watch.ts
+++ b/editors/code/src/commands/cargo_watch.ts
@@ -7,44 +7,55 @@ import { terminate } from '../utils/processes';
7import { LineBuffer } from './line_buffer'; 7import { LineBuffer } from './line_buffer';
8import { StatusDisplay } from './watch_status'; 8import { StatusDisplay } from './watch_status';
9 9
10export class CargoWatchProvider { 10export function registerCargoWatchProvider(
11 private diagnosticCollection?: vscode.DiagnosticCollection; 11 subscriptions: vscode.Disposable[]
12 private cargoProcess?: child_process.ChildProcess; 12): CargoWatchProvider | undefined {
13 private outBuffer: string = ''; 13 let cargoExists = false;
14 private statusDisplay?: StatusDisplay; 14 const cargoTomlFile = path.join(vscode.workspace.rootPath!, 'Cargo.toml');
15 private outputChannel?: vscode.OutputChannel; 15 // Check if the working directory is valid cargo root path
16 16 try {
17 public activate(subscriptions: vscode.Disposable[]) { 17 if (fs.existsSync(cargoTomlFile)) {
18 let cargoExists = false; 18 cargoExists = true;
19 const cargoTomlFile = path.join( 19 }
20 vscode.workspace.rootPath!, 20 } catch (err) {
21 'Cargo.toml' 21 cargoExists = false;
22 }
23
24 if (!cargoExists) {
25 vscode.window.showErrorMessage(
26 `Couldn\'t find \'Cargo.toml\' in ${cargoTomlFile}`
22 ); 27 );
23 // Check if the working directory is valid cargo root path 28 return;
24 try { 29 }
25 if (fs.existsSync(cargoTomlFile)) {
26 cargoExists = true;
27 }
28 } catch (err) {
29 cargoExists = false;
30 }
31 30
32 if (!cargoExists) { 31 const provider = new CargoWatchProvider();
33 vscode.window.showErrorMessage( 32 subscriptions.push(provider);
34 `Couldn\'t find \'Cargo.toml\' in ${cargoTomlFile}` 33 return provider;
35 ); 34}
36 return;
37 }
38 35
39 subscriptions.push(this); 36export class CargoWatchProvider implements vscode.Disposable {
37 private readonly diagnosticCollection: vscode.DiagnosticCollection;
38 private readonly statusDisplay: StatusDisplay;
39 private readonly outputChannel: vscode.OutputChannel;
40 private cargoProcess?: child_process.ChildProcess;
41
42 constructor() {
40 this.diagnosticCollection = vscode.languages.createDiagnosticCollection( 43 this.diagnosticCollection = vscode.languages.createDiagnosticCollection(
41 'rustc' 44 'rustc'
42 ); 45 );
43 46 this.statusDisplay = new StatusDisplay();
44 this.statusDisplay = new StatusDisplay(subscriptions);
45 this.outputChannel = vscode.window.createOutputChannel( 47 this.outputChannel = vscode.window.createOutputChannel(
46 'Cargo Watch Trace' 48 'Cargo Watch Trace'
47 ); 49 );
50 }
51
52 public start() {
53 if (this.cargoProcess) {
54 vscode.window.showInformationMessage(
55 'Cargo Watch is already running'
56 );
57 return;
58 }
48 59
49 let args = 'check --message-format json'; 60 let args = 'check --message-format json';
50 if (Server.config.cargoWatchOptions.checkArguments.length > 0) { 61 if (Server.config.cargoWatchOptions.checkArguments.length > 0) {
@@ -95,25 +106,28 @@ export class CargoWatchProvider {
95 this.logInfo('cargo-watch started.'); 106 this.logInfo('cargo-watch started.');
96 } 107 }
97 108
98 public dispose(): void { 109 public stop() {
99 if (this.diagnosticCollection) {
100 this.diagnosticCollection.clear();
101 this.diagnosticCollection.dispose();
102 }
103
104 if (this.cargoProcess) { 110 if (this.cargoProcess) {
105 this.cargoProcess.kill(); 111 this.cargoProcess.kill();
106 terminate(this.cargoProcess); 112 terminate(this.cargoProcess);
113 this.cargoProcess = undefined;
114 } else {
115 vscode.window.showInformationMessage('Cargo Watch is not running');
107 } 116 }
117 }
108 118
109 if (this.outputChannel) { 119 public dispose(): void {
110 this.outputChannel.dispose(); 120 this.stop();
111 } 121
122 this.diagnosticCollection.clear();
123 this.diagnosticCollection.dispose();
124 this.outputChannel.dispose();
125 this.statusDisplay.dispose();
112 } 126 }
113 127
114 private logInfo(line: string) { 128 private logInfo(line: string) {
115 if (Server.config.cargoWatchOptions.trace === 'verbose') { 129 if (Server.config.cargoWatchOptions.trace === 'verbose') {
116 this.outputChannel!.append(line); 130 this.outputChannel.append(line);
117 } 131 }
118 } 132 }
119 133
@@ -122,18 +136,18 @@ export class CargoWatchProvider {
122 Server.config.cargoWatchOptions.trace === 'error' || 136 Server.config.cargoWatchOptions.trace === 'error' ||
123 Server.config.cargoWatchOptions.trace === 'verbose' 137 Server.config.cargoWatchOptions.trace === 'verbose'
124 ) { 138 ) {
125 this.outputChannel!.append(line); 139 this.outputChannel.append(line);
126 } 140 }
127 } 141 }
128 142
129 private parseLine(line: string) { 143 private parseLine(line: string) {
130 if (line.startsWith('[Running')) { 144 if (line.startsWith('[Running')) {
131 this.diagnosticCollection!.clear(); 145 this.diagnosticCollection.clear();
132 this.statusDisplay!.show(); 146 this.statusDisplay.show();
133 } 147 }
134 148
135 if (line.startsWith('[Finished running')) { 149 if (line.startsWith('[Finished running')) {
136 this.statusDisplay!.hide(); 150 this.statusDisplay.hide();
137 } 151 }
138 152
139 function getLevel(s: string): vscode.DiagnosticSeverity { 153 function getLevel(s: string): vscode.DiagnosticSeverity {
@@ -193,7 +207,7 @@ export class CargoWatchProvider {
193 207
194 // The format of the package_id is "{name} {version} ({source_id})", 208 // The format of the package_id is "{name} {version} ({source_id})",
195 // https://github.com/rust-lang/cargo/blob/37ad03f86e895bb80b474c1c088322634f4725f5/src/cargo/core/package_id.rs#L53 209 // https://github.com/rust-lang/cargo/blob/37ad03f86e895bb80b474c1c088322634f4725f5/src/cargo/core/package_id.rs#L53
196 this.statusDisplay!.packageName = msg.package_id.split(' ')[0]; 210 this.statusDisplay.packageName = msg.package_id.split(' ')[0];
197 } else if (data.reason === 'compiler-message') { 211 } else if (data.reason === 'compiler-message') {
198 const msg = data.message as RustDiagnostic; 212 const msg = data.message as RustDiagnostic;
199 213
diff --git a/editors/code/src/commands/extend_selection.ts b/editors/code/src/commands/extend_selection.ts
deleted file mode 100644
index 6f4187d15..000000000
--- a/editors/code/src/commands/extend_selection.ts
+++ /dev/null
@@ -1,34 +0,0 @@
1import * as vscode from 'vscode';
2
3import { Range, TextDocumentIdentifier } from 'vscode-languageclient';
4import { Server } from '../server';
5
6interface ExtendSelectionParams {
7 textDocument: TextDocumentIdentifier;
8 selections: Range[];
9}
10
11interface ExtendSelectionResult {
12 selections: Range[];
13}
14
15export async function handle() {
16 const editor = vscode.window.activeTextEditor;
17 if (editor == null || editor.document.languageId !== 'rust') {
18 return;
19 }
20 const request: ExtendSelectionParams = {
21 selections: editor.selections.map(s =>
22 Server.client.code2ProtocolConverter.asRange(s)
23 ),
24 textDocument: { uri: editor.document.uri.toString() }
25 };
26 const response = await Server.client.sendRequest<ExtendSelectionResult>(
27 'rust-analyzer/extendSelection',
28 request
29 );
30 editor.selections = response.selections.map((range: Range) => {
31 const r = Server.client.protocol2CodeConverter.asRange(range);
32 return new vscode.Selection(r.start, r.end);
33 });
34}
diff --git a/editors/code/src/commands/index.ts b/editors/code/src/commands/index.ts
index f36c4b040..194658497 100644
--- a/editors/code/src/commands/index.ts
+++ b/editors/code/src/commands/index.ts
@@ -1,6 +1,5 @@
1import * as analyzerStatus from './analyzer_status'; 1import * as analyzerStatus from './analyzer_status';
2import * as applySourceChange from './apply_source_change'; 2import * as applySourceChange from './apply_source_change';
3import * as extendSelection from './extend_selection';
4import * as joinLines from './join_lines'; 3import * as joinLines from './join_lines';
5import * as matchingBrace from './matching_brace'; 4import * as matchingBrace from './matching_brace';
6import * as onEnter from './on_enter'; 5import * as onEnter from './on_enter';
@@ -11,7 +10,6 @@ import * as syntaxTree from './syntaxTree';
11export { 10export {
12 analyzerStatus, 11 analyzerStatus,
13 applySourceChange, 12 applySourceChange,
14 extendSelection,
15 joinLines, 13 joinLines,
16 matchingBrace, 14 matchingBrace,
17 parentModule, 15 parentModule,
diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/commands/runnables.ts
index c4df24c79..26372c1e8 100644
--- a/editors/code/src/commands/runnables.ts
+++ b/editors/code/src/commands/runnables.ts
@@ -5,7 +5,7 @@ import * as vscode from 'vscode';
5import * as lc from 'vscode-languageclient'; 5import * as lc from 'vscode-languageclient';
6 6
7import { Server } from '../server'; 7import { Server } from '../server';
8import { CargoWatchProvider } from './cargo_watch'; 8import { CargoWatchProvider, registerCargoWatchProvider } from './cargo_watch';
9 9
10interface RunnablesParams { 10interface RunnablesParams {
11 textDocument: lc.TextDocumentIdentifier; 11 textDocument: lc.TextDocumentIdentifier;
@@ -137,7 +137,7 @@ export async function handleSingle(runnable: Runnable) {
137 */ 137 */
138export async function interactivelyStartCargoWatch( 138export async function interactivelyStartCargoWatch(
139 context: vscode.ExtensionContext 139 context: vscode.ExtensionContext
140) { 140): Promise<CargoWatchProvider | undefined> {
141 if (Server.config.cargoWatchOptions.enableOnStartup === 'disabled') { 141 if (Server.config.cargoWatchOptions.enableOnStartup === 'disabled') {
142 return; 142 return;
143 } 143 }
@@ -153,6 +153,12 @@ export async function interactivelyStartCargoWatch(
153 } 153 }
154 } 154 }
155 155
156 return startCargoWatch(context);
157}
158
159export async function startCargoWatch(
160 context: vscode.ExtensionContext
161): Promise<CargoWatchProvider | undefined> {
156 const execPromise = util.promisify(child_process.exec); 162 const execPromise = util.promisify(child_process.exec);
157 163
158 const { stderr } = await execPromise('cargo watch --version').catch(e => e); 164 const { stderr } = await execPromise('cargo watch --version').catch(e => e);
@@ -197,6 +203,9 @@ export async function interactivelyStartCargoWatch(
197 } 203 }
198 } 204 }
199 205
200 const validater = new CargoWatchProvider(); 206 const provider = await registerCargoWatchProvider(context.subscriptions);
201 validater.activate(context.subscriptions); 207 if (provider) {
208 provider.start();
209 }
210 return provider;
202} 211}
diff --git a/editors/code/src/commands/watch_status.ts b/editors/code/src/commands/watch_status.ts
index 86ae821de..a3b0178f2 100644
--- a/editors/code/src/commands/watch_status.ts
+++ b/editors/code/src/commands/watch_status.ts
@@ -2,19 +2,18 @@ import * as vscode from 'vscode';
2 2
3const spinnerFrames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']; 3const spinnerFrames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
4 4
5export class StatusDisplay { 5export class StatusDisplay implements vscode.Disposable {
6 public packageName?: string; 6 public packageName?: string;
7 7
8 private i = 0; 8 private i = 0;
9 private statusBarItem: vscode.StatusBarItem; 9 private statusBarItem: vscode.StatusBarItem;
10 private timer?: NodeJS.Timeout; 10 private timer?: NodeJS.Timeout;
11 11
12 constructor(subscriptions: vscode.Disposable[]) { 12 constructor() {
13 this.statusBarItem = vscode.window.createStatusBarItem( 13 this.statusBarItem = vscode.window.createStatusBarItem(
14 vscode.StatusBarAlignment.Left, 14 vscode.StatusBarAlignment.Left,
15 10 15 10
16 ); 16 );
17 subscriptions.push(this.statusBarItem);
18 this.statusBarItem.hide(); 17 this.statusBarItem.hide();
19 } 18 }
20 19
@@ -33,7 +32,7 @@ export class StatusDisplay {
33 } 32 }
34 }, 300); 33 }, 300);
35 34
36 this.statusBarItem!.show(); 35 this.statusBarItem.show();
37 } 36 }
38 37
39 public hide() { 38 public hide() {
@@ -42,7 +41,16 @@ export class StatusDisplay {
42 this.timer = undefined; 41 this.timer = undefined;
43 } 42 }
44 43
45 this.statusBarItem!.hide(); 44 this.statusBarItem.hide();
45 }
46
47 public dispose() {
48 if (this.timer) {
49 clearInterval(this.timer);
50 this.timer = undefined;
51 }
52
53 this.statusBarItem.dispose();
46 } 54 }
47 55
48 private frame() { 56 private frame() {
diff --git a/editors/code/src/extension.ts b/editors/code/src/extension.ts
index 1073a36a0..c8c3004a7 100644
--- a/editors/code/src/extension.ts
+++ b/editors/code/src/extension.ts
@@ -2,7 +2,11 @@ import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient'; 2import * as lc from 'vscode-languageclient';
3 3
4import * as commands from './commands'; 4import * as commands from './commands';
5import { interactivelyStartCargoWatch } from './commands/runnables'; 5import { CargoWatchProvider } from './commands/cargo_watch';
6import {
7 interactivelyStartCargoWatch,
8 startCargoWatch
9} from './commands/runnables';
6import { SyntaxTreeContentProvider } from './commands/syntaxTree'; 10import { SyntaxTreeContentProvider } from './commands/syntaxTree';
7import * as events from './events'; 11import * as events from './events';
8import * as notifications from './notifications'; 12import * as notifications from './notifications';
@@ -54,10 +58,6 @@ export function activate(context: vscode.ExtensionContext) {
54 Server.client.sendRequest<null>('rust-analyzer/collectGarbage', null) 58 Server.client.sendRequest<null>('rust-analyzer/collectGarbage', null)
55 ); 59 );
56 registerCommand( 60 registerCommand(
57 'rust-analyzer.extendSelection',
58 commands.extendSelection.handle
59 );
60 registerCommand(
61 'rust-analyzer.matchingBrace', 61 'rust-analyzer.matchingBrace',
62 commands.matchingBrace.handle 62 commands.matchingBrace.handle
63 ); 63 );
@@ -126,7 +126,24 @@ export function activate(context: vscode.ExtensionContext) {
126 vscode.commands.registerCommand('rust-analyzer.reload', reloadCommand); 126 vscode.commands.registerCommand('rust-analyzer.reload', reloadCommand);
127 127
128 // Executing `cargo watch` provides us with inline diagnostics on save 128 // Executing `cargo watch` provides us with inline diagnostics on save
129 interactivelyStartCargoWatch(context); 129 let provider: CargoWatchProvider | undefined;
130 interactivelyStartCargoWatch(context).then(p => {
131 provider = p;
132 });
133 registerCommand('rust-analyzer.startCargoWatch', () => {
134 if (provider) {
135 provider.start();
136 } else {
137 startCargoWatch(context).then(p => {
138 provider = p;
139 });
140 }
141 });
142 registerCommand('rust-analyzer.stopCargoWatch', () => {
143 if (provider) {
144 provider.stop();
145 }
146 });
130 147
131 // Start the language server, finally! 148 // Start the language server, finally!
132 startServer(); 149 startServer();
diff --git a/editors/code/src/server.ts b/editors/code/src/server.ts
index 5e9a19340..81c2b3fff 100644
--- a/editors/code/src/server.ts
+++ b/editors/code/src/server.ts
@@ -74,6 +74,7 @@ export class Server {
74 } 74 }
75 } 75 }
76 }; 76 };
77 Server.client.registerProposedFeatures();
77 Server.client.onReady().then(() => { 78 Server.client.onReady().then(() => {
78 for (const [type, handler] of notificationHandlers) { 79 for (const [type, handler] of notificationHandlers) {
79 Server.client.onNotification(type, handler); 80 Server.client.onNotification(type, handler);