aboutsummaryrefslogtreecommitdiff
path: root/editors
diff options
context:
space:
mode:
Diffstat (limited to 'editors')
-rw-r--r--editors/code/package-lock.json51
-rw-r--r--editors/code/package.json38
-rw-r--r--editors/code/src/commands/cargo_watch.ts235
-rw-r--r--editors/code/src/commands/line_buffer.ts16
-rw-r--r--editors/code/src/commands/runnables.ts33
-rw-r--r--editors/code/src/commands/watch_status.ts51
-rw-r--r--editors/code/src/config.ts34
-rw-r--r--editors/code/src/extension.ts2
-rw-r--r--editors/code/src/utils/processes.ts51
-rw-r--r--editors/code/src/utils/terminateProcess.sh12
-rw-r--r--editors/emacs/ra-emacs-lsp.el2
-rw-r--r--editors/emacs/ra.el128
12 files changed, 480 insertions, 173 deletions
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json
index 5a0d21e78..008df6f52 100644
--- a/editors/code/package-lock.json
+++ b/editors/code/package-lock.json
@@ -607,6 +607,12 @@
607 "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", 607 "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==",
608 "dev": true 608 "dev": true
609 }, 609 },
610 "es6-object-assign": {
611 "version": "1.1.0",
612 "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz",
613 "integrity": "sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=",
614 "dev": true
615 },
610 "escape-string-regexp": { 616 "escape-string-regexp": {
611 "version": "1.0.5", 617 "version": "1.0.5",
612 "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 618 "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
@@ -1121,6 +1127,12 @@
1121 "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", 1127 "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
1122 "dev": true 1128 "dev": true
1123 }, 1129 },
1130 "interpret": {
1131 "version": "1.2.0",
1132 "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz",
1133 "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==",
1134 "dev": true
1135 },
1124 "is": { 1136 "is": {
1125 "version": "3.3.0", 1137 "version": "3.3.0",
1126 "resolved": "https://registry.npmjs.org/is/-/is-3.3.0.tgz", 1138 "resolved": "https://registry.npmjs.org/is/-/is-3.3.0.tgz",
@@ -1791,6 +1803,15 @@
1791 "util-deprecate": "^1.0.1" 1803 "util-deprecate": "^1.0.1"
1792 } 1804 }
1793 }, 1805 },
1806 "rechoir": {
1807 "version": "0.6.2",
1808 "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
1809 "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=",
1810 "dev": true,
1811 "requires": {
1812 "resolve": "^1.1.6"
1813 }
1814 },
1794 "remove-bom-buffer": { 1815 "remove-bom-buffer": {
1795 "version": "3.0.0", 1816 "version": "3.0.0",
1796 "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz", 1817 "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz",
@@ -1902,6 +1923,36 @@
1902 "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", 1923 "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
1903 "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==" 1924 "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg=="
1904 }, 1925 },
1926 "shelljs": {
1927 "version": "0.8.3",
1928 "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.3.tgz",
1929 "integrity": "sha512-fc0BKlAWiLpwZljmOvAOTE/gXawtCoNrP5oaY7KIaQbbyHeQVg01pSEuEGvGh3HEdBU4baCD7wQBwADmM/7f7A==",
1930 "dev": true,
1931 "requires": {
1932 "glob": "^7.0.0",
1933 "interpret": "^1.0.0",
1934 "rechoir": "^0.6.2"
1935 }
1936 },
1937 "shx": {
1938 "version": "0.3.2",
1939 "resolved": "https://registry.npmjs.org/shx/-/shx-0.3.2.tgz",
1940 "integrity": "sha512-aS0mWtW3T2sHAenrSrip2XGv39O9dXIFUqxAEWHEOS1ePtGIBavdPJY1kE2IHl14V/4iCbUiNDPGdyYTtmhSoA==",
1941 "dev": true,
1942 "requires": {
1943 "es6-object-assign": "^1.0.3",
1944 "minimist": "^1.2.0",
1945 "shelljs": "^0.8.1"
1946 },
1947 "dependencies": {
1948 "minimist": {
1949 "version": "1.2.0",
1950 "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
1951 "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
1952 "dev": true
1953 }
1954 }
1955 },
1905 "source-map": { 1956 "source-map": {
1906 "version": "0.6.1", 1957 "version": "0.6.1",
1907 "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 1958 "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
diff --git a/editors/code/package.json b/editors/code/package.json
index facb633d9..1c8caaa60 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -18,7 +18,7 @@
18 "scripts": { 18 "scripts": {
19 "vscode:prepublish": "npm run compile", 19 "vscode:prepublish": "npm run compile",
20 "package": "vsce package", 20 "package": "vsce package",
21 "compile": "tsc -p ./", 21 "compile": "tsc -p ./ && shx cp src/utils/terminateProcess.sh out/utils/terminateProcess.sh",
22 "watch": "tsc -watch -p ./", 22 "watch": "tsc -watch -p ./",
23 "postinstall": "node ./node_modules/vscode/bin/install", 23 "postinstall": "node ./node_modules/vscode/bin/install",
24 "fix": "prettier **/*.{json,ts} --write && tslint --project . --fix", 24 "fix": "prettier **/*.{json,ts} --write && tslint --project . --fix",
@@ -41,7 +41,8 @@
41 "tslint-config-prettier": "^1.18.0", 41 "tslint-config-prettier": "^1.18.0",
42 "typescript": "^3.3.1", 42 "typescript": "^3.3.1",
43 "vsce": "^1.57.0", 43 "vsce": "^1.57.0",
44 "vscode": "^1.1.29" 44 "vscode": "^1.1.29",
45 "shx": "^0.3.1"
45 }, 46 },
46 "activationEvents": [ 47 "activationEvents": [
47 "onLanguage:rust", 48 "onLanguage:rust",
@@ -183,6 +184,11 @@
183 ], 184 ],
184 "description": "Whether to run `cargo watch` on startup" 185 "description": "Whether to run `cargo watch` on startup"
185 }, 186 },
187 "rust-analyzer.cargo-watch.check-arguments": {
188 "type": "string",
189 "description": "`cargo-watch` check arguments. (e.g: `--features=\"shumway,pdf\"` will run as `cargo watch -x \"check --features=\"shumway,pdf\"\"` )",
190 "default": ""
191 },
186 "rust-analyzer.trace.server": { 192 "rust-analyzer.trace.server": {
187 "type": "string", 193 "type": "string",
188 "scope": "window", 194 "scope": "window",
@@ -191,8 +197,24 @@
191 "messages", 197 "messages",
192 "verbose" 198 "verbose"
193 ], 199 ],
200 "enumDescriptions": [
201 "No traces",
202 "Error only",
203 "Full log"
204 ],
194 "default": "off", 205 "default": "off",
195 "description": "Trace requests to the ra_lsp_server" 206 "description": "Trace requests to the ra_lsp_server"
207 },
208 "rust-analyzer.trace.cargo-watch": {
209 "type": "string",
210 "scope": "window",
211 "enum": [
212 "off",
213 "error",
214 "verbose"
215 ],
216 "default": "off",
217 "description": "Trace output of cargo-watch"
196 } 218 }
197 } 219 }
198 }, 220 },
@@ -223,18 +245,6 @@
223 "${workspaceRoot}" 245 "${workspaceRoot}"
224 ], 246 ],
225 "pattern": "$rustc" 247 "pattern": "$rustc"
226 },
227 {
228 "name": "rustc-watch",
229 "fileLocation": [
230 "relative",
231 "${workspaceRoot}"
232 ],
233 "background": {
234 "beginsPattern": "^\\[Running\\b",
235 "endsPattern": "^\\[Finished running\\b"
236 },
237 "pattern": "$rustc"
238 } 248 }
239 ] 249 ]
240 } 250 }
diff --git a/editors/code/src/commands/cargo_watch.ts b/editors/code/src/commands/cargo_watch.ts
new file mode 100644
index 000000000..d45d0e7d1
--- /dev/null
+++ b/editors/code/src/commands/cargo_watch.ts
@@ -0,0 +1,235 @@
1import * as child_process from 'child_process';
2import * as fs from 'fs';
3import * as path from 'path';
4import * as vscode from 'vscode';
5import { Server } from '../server';
6import { terminate } from '../utils/processes';
7import { LineBuffer } from './line_buffer';
8import { StatusDisplay } from './watch_status';
9
10export class CargoWatchProvider {
11 private diagnosticCollection?: vscode.DiagnosticCollection;
12 private cargoProcess?: child_process.ChildProcess;
13 private outBuffer: string = '';
14 private statusDisplay?: StatusDisplay;
15 private outputChannel?: vscode.OutputChannel;
16
17 public activate(subscriptions: vscode.Disposable[]) {
18 let cargoExists = false;
19 const cargoTomlFile = path.join(
20 vscode.workspace.rootPath!,
21 'Cargo.toml'
22 );
23 // Check if the working directory is valid cargo root path
24 try {
25 if (fs.existsSync(cargoTomlFile)) {
26 cargoExists = true;
27 }
28 } catch (err) {
29 cargoExists = false;
30 }
31
32 if (!cargoExists) {
33 vscode.window.showErrorMessage(
34 `Couldn\'t find \'Cargo.toml\' in ${cargoTomlFile}`
35 );
36 return;
37 }
38
39 subscriptions.push(this);
40 this.diagnosticCollection = vscode.languages.createDiagnosticCollection(
41 'rustc'
42 );
43
44 this.statusDisplay = new StatusDisplay(subscriptions);
45 this.outputChannel = vscode.window.createOutputChannel(
46 'Cargo Watch Trace'
47 );
48
49 let args = '"check --message-format json';
50 if (Server.config.cargoWatchOptions.checkArguments.length > 0) {
51 // Excape the double quote string:
52 args += ' ' + Server.config.cargoWatchOptions.checkArguments;
53 }
54 args += '"';
55
56 // Start the cargo watch with json message
57 this.cargoProcess = child_process.spawn(
58 'cargo',
59 ['watch', '-x', args],
60 {
61 stdio: ['ignore', 'pipe', 'pipe'],
62 cwd: vscode.workspace.rootPath,
63 windowsVerbatimArguments: true
64 }
65 );
66
67 const stdoutData = new LineBuffer();
68 this.cargoProcess.stdout.on('data', (s: string) => {
69 stdoutData.processOutput(s, line => {
70 this.logInfo(line);
71 try {
72 this.parseLine(line);
73 } catch (err) {
74 this.logError(`Failed to parse: ${err}, content : ${line}`);
75 }
76 });
77 });
78
79 const stderrData = new LineBuffer();
80 this.cargoProcess.stderr.on('data', (s: string) => {
81 stderrData.processOutput(s, line => {
82 this.logError('Error on cargo-watch : {\n' + line + '}\n');
83 });
84 });
85
86 this.cargoProcess.on('error', (err: Error) => {
87 this.logError(
88 'Error on cargo-watch process : {\n' + err.message + '}\n'
89 );
90 });
91
92 this.logInfo('cargo-watch started.');
93 }
94
95 public dispose(): void {
96 if (this.diagnosticCollection) {
97 this.diagnosticCollection.clear();
98 this.diagnosticCollection.dispose();
99 }
100
101 if (this.cargoProcess) {
102 this.cargoProcess.kill();
103 terminate(this.cargoProcess);
104 }
105
106 if (this.outputChannel) {
107 this.outputChannel.dispose();
108 }
109 }
110
111 private logInfo(line: string) {
112 if (Server.config.cargoWatchOptions.trace === 'verbose') {
113 this.outputChannel!.append(line);
114 }
115 }
116
117 private logError(line: string) {
118 if (
119 Server.config.cargoWatchOptions.trace === 'error' ||
120 Server.config.cargoWatchOptions.trace === 'verbose'
121 ) {
122 this.outputChannel!.append(line);
123 }
124 }
125
126 private parseLine(line: string) {
127 if (line.startsWith('[Running')) {
128 this.diagnosticCollection!.clear();
129 this.statusDisplay!.show();
130 }
131
132 if (line.startsWith('[Finished running')) {
133 this.statusDisplay!.hide();
134 }
135
136 function getLevel(s: string): vscode.DiagnosticSeverity {
137 if (s === 'error') {
138 return vscode.DiagnosticSeverity.Error;
139 }
140 if (s.startsWith('warn')) {
141 return vscode.DiagnosticSeverity.Warning;
142 }
143 return vscode.DiagnosticSeverity.Information;
144 }
145
146 // Reference:
147 // https://github.com/rust-lang/rust/blob/master/src/libsyntax/json.rs
148 interface RustDiagnosticSpan {
149 line_start: number;
150 line_end: number;
151 column_start: number;
152 column_end: number;
153 is_primary: boolean;
154 file_name: string;
155 }
156
157 interface RustDiagnostic {
158 spans: RustDiagnosticSpan[];
159 rendered: string;
160 level: string;
161 code?: {
162 code: string;
163 };
164 }
165
166 interface CargoArtifact {
167 reason: string;
168 package_id: string;
169 }
170
171 // https://github.com/rust-lang/cargo/blob/master/src/cargo/util/machine_message.rs
172 interface CargoMessage {
173 reason: string;
174 package_id: string;
175 message: RustDiagnostic;
176 }
177
178 // cargo-watch itself output non json format
179 // Ignore these lines
180 let data: CargoMessage;
181 try {
182 data = JSON.parse(line.trim());
183 } catch (error) {
184 this.logError(`Fail to parse to json : { ${error} }`);
185 return;
186 }
187
188 if (data.reason === 'compiler-artifact') {
189 const msg = data as CargoArtifact;
190
191 // The format of the package_id is "{name} {version} ({source_id})",
192 // https://github.com/rust-lang/cargo/blob/37ad03f86e895bb80b474c1c088322634f4725f5/src/cargo/core/package_id.rs#L53
193 this.statusDisplay!.packageName = msg.package_id.split(' ')[0];
194 } else if (data.reason === 'compiler-message') {
195 const msg = data.message as RustDiagnostic;
196
197 const spans = msg.spans.filter(o => o.is_primary);
198
199 // We only handle primary span right now.
200 if (spans.length > 0) {
201 const o = spans[0];
202
203 const rendered = msg.rendered;
204 const level = getLevel(msg.level);
205 const range = new vscode.Range(
206 new vscode.Position(o.line_start - 1, o.column_start - 1),
207 new vscode.Position(o.line_end - 1, o.column_end - 1)
208 );
209
210 const fileName = path.join(
211 vscode.workspace.rootPath!,
212 o.file_name
213 );
214 const diagnostic = new vscode.Diagnostic(
215 range,
216 rendered,
217 level
218 );
219
220 diagnostic.source = 'rustc';
221 diagnostic.code = msg.code ? msg.code.code : undefined;
222 diagnostic.relatedInformation = [];
223
224 const fileUrl = vscode.Uri.file(fileName!);
225
226 const diagnostics: vscode.Diagnostic[] = [
227 ...(this.diagnosticCollection!.get(fileUrl) || [])
228 ];
229 diagnostics.push(diagnostic);
230
231 this.diagnosticCollection!.set(fileUrl, diagnostics);
232 }
233 }
234 }
235}
diff --git a/editors/code/src/commands/line_buffer.ts b/editors/code/src/commands/line_buffer.ts
new file mode 100644
index 000000000..fb5b9f7f2
--- /dev/null
+++ b/editors/code/src/commands/line_buffer.ts
@@ -0,0 +1,16 @@
1export class LineBuffer {
2 private outBuffer: string = '';
3
4 public processOutput(chunk: string, cb: (line: string) => void) {
5 this.outBuffer += chunk;
6 let eolIndex = this.outBuffer.indexOf('\n');
7 while (eolIndex >= 0) {
8 // line includes the EOL
9 const line = this.outBuffer.slice(0, eolIndex + 1);
10 cb(line);
11 this.outBuffer = this.outBuffer.slice(eolIndex + 1);
12
13 eolIndex = this.outBuffer.indexOf('\n');
14 }
15 }
16}
diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/commands/runnables.ts
index 4187ef4d1..3589edcee 100644
--- a/editors/code/src/commands/runnables.ts
+++ b/editors/code/src/commands/runnables.ts
@@ -1,9 +1,11 @@
1import * as child_process from 'child_process'; 1import * as child_process from 'child_process';
2
2import * as util from 'util'; 3import * as util from 'util';
3import * as vscode from 'vscode'; 4import * as vscode from 'vscode';
4import * as lc from 'vscode-languageclient'; 5import * as lc from 'vscode-languageclient';
5 6
6import { Server } from '../server'; 7import { Server } from '../server';
8import { CargoWatchProvider } from './cargo_watch';
7 9
8interface RunnablesParams { 10interface RunnablesParams {
9 textDocument: lc.TextDocumentIdentifier; 11 textDocument: lc.TextDocumentIdentifier;
@@ -127,37 +129,19 @@ export async function handleSingle(runnable: Runnable) {
127 return vscode.tasks.executeTask(task); 129 return vscode.tasks.executeTask(task);
128} 130}
129 131
130export 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 */
155export async function interactivelyStartCargoWatch() { 137export async function interactivelyStartCargoWatch(
156 if (Server.config.enableCargoWatchOnStartup === 'disabled') { 138 context: vscode.ExtensionContext
139) {
140 if (Server.config.cargoWatchOptions.enableOnStartup === 'disabled') {
157 return; 141 return;
158 } 142 }
159 143
160 if (Server.config.enableCargoWatchOnStartup === 'ask') { 144 if (Server.config.cargoWatchOptions.enableOnStartup === 'ask') {
161 const watch = await vscode.window.showInformationMessage( 145 const watch = await vscode.window.showInformationMessage(
162 'Start watching changes with cargo? (Executes `cargo watch`, provides inline diagnostics)', 146 'Start watching changes with cargo? (Executes `cargo watch`, provides inline diagnostics)',
163 'yes', 147 'yes',
@@ -212,5 +196,6 @@ export async function interactivelyStartCargoWatch() {
212 } 196 }
213 } 197 }
214 198
215 vscode.tasks.executeTask(autoCargoWatchTask); 199 const validater = new CargoWatchProvider();
200 validater.activate(context.subscriptions);
216} 201}
diff --git a/editors/code/src/commands/watch_status.ts b/editors/code/src/commands/watch_status.ts
new file mode 100644
index 000000000..86ae821de
--- /dev/null
+++ b/editors/code/src/commands/watch_status.ts
@@ -0,0 +1,51 @@
1import * as vscode from 'vscode';
2
3const spinnerFrames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
4
5export class StatusDisplay {
6 public packageName?: string;
7
8 private i = 0;
9 private statusBarItem: vscode.StatusBarItem;
10 private timer?: NodeJS.Timeout;
11
12 constructor(subscriptions: vscode.Disposable[]) {
13 this.statusBarItem = vscode.window.createStatusBarItem(
14 vscode.StatusBarAlignment.Left,
15 10
16 );
17 subscriptions.push(this.statusBarItem);
18 this.statusBarItem.hide();
19 }
20
21 public show() {
22 this.packageName = undefined;
23
24 this.timer =
25 this.timer ||
26 setInterval(() => {
27 if (this.packageName) {
28 this.statusBarItem!.text = `cargo check [${
29 this.packageName
30 }] ${this.frame()}`;
31 } else {
32 this.statusBarItem!.text = `cargo check ${this.frame()}`;
33 }
34 }, 300);
35
36 this.statusBarItem!.show();
37 }
38
39 public hide() {
40 if (this.timer) {
41 clearInterval(this.timer);
42 this.timer = undefined;
43 }
44
45 this.statusBarItem!.hide();
46 }
47
48 private frame() {
49 return spinnerFrames[(this.i = ++this.i % spinnerFrames.length)];
50 }
51}
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index 420589068..481a5e5f1 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -4,14 +4,25 @@ import { Server } from './server';
4 4
5const RA_LSP_DEBUG = process.env.__RA_LSP_SERVER_DEBUG; 5const RA_LSP_DEBUG = process.env.__RA_LSP_SERVER_DEBUG;
6 6
7export type CargoWatchOptions = 'ask' | 'enabled' | 'disabled'; 7export type CargoWatchStartupOptions = 'ask' | 'enabled' | 'disabled';
8export type CargoWatchTraceOptions = 'off' | 'error' | 'verbose';
9
10export interface CargoWatchOptions {
11 enableOnStartup: CargoWatchStartupOptions;
12 checkArguments: string;
13 trace: CargoWatchTraceOptions;
14}
8 15
9export class Config { 16export class Config {
10 public highlightingOn = true; 17 public highlightingOn = true;
11 public enableEnhancedTyping = true; 18 public enableEnhancedTyping = true;
12 public raLspServerPath = RA_LSP_DEBUG || 'ra_lsp_server'; 19 public raLspServerPath = RA_LSP_DEBUG || 'ra_lsp_server';
13 public showWorkspaceLoadedNotification = true; 20 public showWorkspaceLoadedNotification = true;
14 public enableCargoWatchOnStartup: CargoWatchOptions = 'ask'; 21 public cargoWatchOptions: CargoWatchOptions = {
22 enableOnStartup: 'ask',
23 trace: 'off',
24 checkArguments: ''
25 };
15 26
16 private prevEnhancedTyping: null | boolean = null; 27 private prevEnhancedTyping: null | boolean = null;
17 28
@@ -73,9 +84,22 @@ export class Config {
73 } 84 }
74 85
75 if (config.has('enableCargoWatchOnStartup')) { 86 if (config.has('enableCargoWatchOnStartup')) {
76 this.enableCargoWatchOnStartup = config.get<CargoWatchOptions>( 87 this.cargoWatchOptions.enableOnStartup = config.get<
77 'enableCargoWatchOnStartup', 88 CargoWatchStartupOptions
78 'ask' 89 >('enableCargoWatchOnStartup', 'ask');
90 }
91
92 if (config.has('trace.cargo-watch')) {
93 this.cargoWatchOptions.trace = config.get<CargoWatchTraceOptions>(
94 'trace.cargo-watch',
95 'off'
96 );
97 }
98
99 if (config.has('cargo-watch.check-arguments')) {
100 this.cargoWatchOptions.checkArguments = config.get<string>(
101 'cargo-watch.check-arguments',
102 ''
79 ); 103 );
80 } 104 }
81 } 105 }
diff --git a/editors/code/src/extension.ts b/editors/code/src/extension.ts
index 2e13c87de..ef83c0b8b 100644
--- a/editors/code/src/extension.ts
+++ b/editors/code/src/extension.ts
@@ -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);
diff --git a/editors/code/src/utils/processes.ts b/editors/code/src/utils/processes.ts
new file mode 100644
index 000000000..da8be9eb1
--- /dev/null
+++ b/editors/code/src/utils/processes.ts
@@ -0,0 +1,51 @@
1'use strict';
2
3import * as cp from 'child_process';
4import ChildProcess = cp.ChildProcess;
5
6import { join } from 'path';
7
8const isWindows = process.platform === 'win32';
9const isMacintosh = process.platform === 'darwin';
10const isLinux = process.platform === 'linux';
11
12// this is very complex, but is basically copy-pased from VSCode implementation here:
13// https://github.com/Microsoft/vscode-languageserver-node/blob/dbfd37e35953ad0ee14c4eeced8cfbc41697b47e/client/src/utils/processes.ts#L15
14
15// And see discussion at
16// https://github.com/rust-analyzer/rust-analyzer/pull/1079#issuecomment-478908109
17
18export function terminate(process: ChildProcess, cwd?: string): boolean {
19 if (isWindows) {
20 try {
21 // This we run in Atom execFileSync is available.
22 // Ignore stderr since this is otherwise piped to parent.stderr
23 // which might be already closed.
24 const options: any = {
25 stdio: ['pipe', 'pipe', 'ignore']
26 };
27 if (cwd) {
28 options.cwd = cwd;
29 }
30 cp.execFileSync(
31 'taskkill',
32 ['/T', '/F', '/PID', process.pid.toString()],
33 options
34 );
35 return true;
36 } catch (err) {
37 return false;
38 }
39 } else if (isLinux || isMacintosh) {
40 try {
41 const cmd = join(__dirname, 'terminateProcess.sh');
42 const result = cp.spawnSync(cmd, [process.pid.toString()]);
43 return result.error ? false : true;
44 } catch (err) {
45 return false;
46 }
47 } else {
48 process.kill('SIGKILL');
49 return true;
50 }
51}
diff --git a/editors/code/src/utils/terminateProcess.sh b/editors/code/src/utils/terminateProcess.sh
new file mode 100644
index 000000000..2ec9e1c2e
--- /dev/null
+++ b/editors/code/src/utils/terminateProcess.sh
@@ -0,0 +1,12 @@
1#!/bin/bash
2
3terminateTree() {
4 for cpid in $(pgrep -P $1); do
5 terminateTree $cpid
6 done
7 kill -9 $1 > /dev/null 2>&1
8}
9
10for pid in $*; do
11 terminateTree $pid
12done \ No newline at end of file
diff --git a/editors/emacs/ra-emacs-lsp.el b/editors/emacs/ra-emacs-lsp.el
index 955703edb..84c018b66 100644
--- a/editors/emacs/ra-emacs-lsp.el
+++ b/editors/emacs/ra-emacs-lsp.el
@@ -159,7 +159,7 @@
159 (interactive (list (rust-analyzer--select-runnable))) 159 (interactive (list (rust-analyzer--select-runnable)))
160 (-let (((&hash "env" "bin" "args" "label") runnable)) 160 (-let (((&hash "env" "bin" "args" "label") runnable))
161 (compilation-start 161 (compilation-start
162 (string-join (cons bin args) " ") 162 (string-join (append (list bin) args '()) " ")
163 ;; cargo-process-mode is nice, but try to work without it... 163 ;; cargo-process-mode is nice, but try to work without it...
164 (if (functionp 'cargo-process-mode) 'cargo-process-mode nil) 164 (if (functionp 'cargo-process-mode) 'cargo-process-mode nil)
165 (lambda (_) (concat "*" label "*"))) 165 (lambda (_) (concat "*" label "*")))
diff --git a/editors/emacs/ra.el b/editors/emacs/ra.el
deleted file mode 100644
index fb73451c1..000000000
--- a/editors/emacs/ra.el
+++ /dev/null
@@ -1,128 +0,0 @@
1;;; ra.el --- Rust analyzer emacs bindings -*- lexical-binding: t; -*-
2;;; Commentary:
3;;; Small utilities for interacting with Rust analyzer.
4;;; Run
5;;; cargo install --git https://github.com/matklad/rust-analyzer/ ra_cli
6;;; to install the analyzer binary. Then copy-paste the bellow code to
7;;; your `.init.el` and use `ra-extend-selection` and
8;;; `ra-shrink-selection` functions.
9;;; Code:
10
11
12(defvar ra--selections-cache '(0 0 ()))
13(defun ra--cache-tick ()
14 "Get buffer modification count for cache."
15 (nth 0 ra--selections-cache))
16(defun ra--cache-sel ()
17 "Get current selection for cache."
18 (nth 1 ra--selections-cache))
19(defun ra--cache-nth-sel (n)
20 "Get Nth selection."
21 (nth n (nth 2 ra--selections-cache)))
22(defun ra--cache-set-nth-sel (n)
23 "Get Nth selection."
24 (setf (nth 1 ra--selections-cache) n)
25 (nth n (nth 2 ra--selections-cache)))
26
27
28(defun ra-extend-selection ()
29 "Extend START END region to contain the encompassing syntactic construct."
30 (interactive)
31 (let* ((p (point))
32 (m (or (and mark-active (mark)) p))
33 (start (min p m))
34 (end (max p m)))
35 (ra--extend-selection start end)))
36
37
38(defun ra-shrink-selection (start end)
39 "Shrink START END region to contain previous selection."
40 (interactive "r")
41 (ra--freshen-cache start end)
42 (let ((sel-id (ra--cache-sel)))
43 (if (not (= 0 sel-id))
44 (let* ((r (ra--cache-set-nth-sel (- sel-id 1))))
45 (push-mark (nth 0 r) t t)
46 (goto-char (nth 1 r))
47 (setq deactivate-mark nil)))))
48
49; Add this to setup keybinding
50; (require 'rust-mode)
51; (define-key rust-mode-map (kbd "C-w") 'ra-extend-selection)
52; (define-key rust-mode-map (kbd "C-S-w") 'ra-shrink-selection)
53
54
55
56(defun ra--extend-selection (start end)
57 "Extend START END region to contain the encompassing syntactic construct."
58 (ra--freshen-cache start end)
59 (let* ((next-sel-idx (+ 1 (ra--cache-sel)))
60 (r (ra--cache-set-nth-sel next-sel-idx)))
61 (push-mark (nth 0 r) t t)
62 (goto-char (nth 1 r))
63 (setq deactivate-mark nil)))
64
65(defun ra--selections (start end)
66 "Get list of selections for START END from Rust analyzer."
67 (read (with-output-to-string
68 (call-process-region
69 (point-min) (point-max)
70 "ra_cli" nil standard-output nil
71 "extend-selection"
72 (number-to-string start)
73 (number-to-string end)))))
74
75(defun ra--freshen-cache (start end)
76 "Make selection cache up-to-date for current buffer state and START END."
77 (if (not (and
78 (= (buffer-modified-tick) (ra--cache-tick))
79 (equal `(,start ,end) (ra--cache-nth-sel (ra--cache-sel)))))
80 (ra--set-cache start end)))
81
82(defun ra--set-cache (start end)
83 "Set selections cache for current buffer state and START END."
84 (setq ra--selections-cache `(,(buffer-modified-tick) 0 ,(ra--selections start end))))
85
86
87(require 'eglot)
88(require 'ivy)
89(require 'counsel)
90
91
92(defun workspace-symbols ()
93 (interactive)
94 (let ((buf (current-buffer)))
95 (ivy-read "Symbol name: "
96 (lambda (str)
97 (with-current-buffer buf
98 (let ((backend (eglot-xref-backend)))
99 (mapcar
100 (lambda (xref)
101 (let ((loc (xref-item-location xref)))
102 (propertize
103 (concat
104 (when (xref-file-location-p loc)
105 (with-slots (file line column) loc
106 (format "%s:%s:%s:"
107 (propertize (file-relative-name file)
108 'face 'compilation-info)
109 (propertize (format "%s" line)
110 'face 'compilation-line
111 )
112 column)))
113 (xref-item-summary xref))
114 'xref xref)))
115 (xref-backend-apropos backend str))
116 )))
117 :dynamic-collection t
118 :action (lambda (item)
119 (xref--pop-to-location (get-text-property 0 'xref item))))))
120
121(add-to-list 'eglot-server-programs '(rust-mode . ("ra_lsp_server")))
122
123; (require 'rust-mode)
124; (define-key rust-mode-map (kbd "C-n") 'workspace-symbols)
125
126(define-key)
127(provide 'ra)
128;;; ra.el ends here