aboutsummaryrefslogtreecommitdiff
path: root/editors/code/src/commands
diff options
context:
space:
mode:
authorbors[bot] <bors[bot]@users.noreply.github.com>2018-10-08 20:39:52 +0100
committerbors[bot] <bors[bot]@users.noreply.github.com>2018-10-08 20:39:52 +0100
commitf4ad36e972989c3feed8671d6d6fca0aed37cd8f (patch)
treef60e1aa4703c3e176315ecd886206848028b8cbf /editors/code/src/commands
parenta05e09e9c514878148ddf26aa76d6b9183583d0f (diff)
parentbbf38b9e722e8d6455828ff22242c92219da346d (diff)
Merge #103
103: WIP: refactor vscode extension r=aochagavia a=aochagavia Todo: - [x] Add more comments, so other people can find their way in the codebase - [x] Resolve remaining tslint suggestions - [ ] Integrate with CI @matklad The standard configuration of tslint forbids using `console.log` and `console.error`. Is there any reason we are using those or can I remove them? If they are used for debugging purposes I would prefer to remove them and rely on vscode's excellent debugger. Co-authored-by: Adolfo OchagavĂ­a <[email protected]>
Diffstat (limited to 'editors/code/src/commands')
-rw-r--r--editors/code/src/commands/apply_source_change.ts58
-rw-r--r--editors/code/src/commands/extend_selection.ts29
-rw-r--r--editors/code/src/commands/index.ts17
-rw-r--r--editors/code/src/commands/join_lines.ts21
-rw-r--r--editors/code/src/commands/matching_brace.ts27
-rw-r--r--editors/code/src/commands/parent_module.ts22
-rw-r--r--editors/code/src/commands/runnables.ts88
-rw-r--r--editors/code/src/commands/syntaxTree.ts38
8 files changed, 300 insertions, 0 deletions
diff --git a/editors/code/src/commands/apply_source_change.ts b/editors/code/src/commands/apply_source_change.ts
new file mode 100644
index 000000000..67765e5a3
--- /dev/null
+++ b/editors/code/src/commands/apply_source_change.ts
@@ -0,0 +1,58 @@
1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient';
3
4import { Server } from '../server';
5
6interface FileSystemEdit {
7 type: string;
8 uri?: string;
9 src?: string;
10 dst?: string;
11}
12
13export interface SourceChange {
14 label: string;
15 sourceFileEdits: lc.TextDocumentEdit[];
16 fileSystemEdits: FileSystemEdit[];
17 cursorPosition?: lc.TextDocumentPositionParams;
18}
19
20export async function handle(change: SourceChange) {
21 const wsEdit = new vscode.WorkspaceEdit();
22 for (const sourceEdit of change.sourceFileEdits) {
23 const uri = Server.client.protocol2CodeConverter.asUri(sourceEdit.textDocument.uri);
24 const edits = Server.client.protocol2CodeConverter.asTextEdits(sourceEdit.edits);
25 wsEdit.set(uri, edits);
26 }
27 let created;
28 let moved;
29 for (const fsEdit of change.fileSystemEdits) {
30 switch (fsEdit.type) {
31 case 'createFile':
32 const uri = vscode.Uri.parse(fsEdit.uri!);
33 wsEdit.createFile(uri);
34 created = uri;
35 break;
36 case 'moveFile':
37 const src = vscode.Uri.parse(fsEdit.src!);
38 const dst = vscode.Uri.parse(fsEdit.dst!);
39 wsEdit.renameFile(src, dst);
40 moved = dst;
41 break;
42 }
43 }
44 const toOpen = created || moved;
45 const toReveal = change.cursorPosition;
46 await vscode.workspace.applyEdit(wsEdit);
47 if (toOpen) {
48 const doc = await vscode.workspace.openTextDocument(toOpen);
49 await vscode.window.showTextDocument(doc);
50 } else if (toReveal) {
51 const uri = Server.client.protocol2CodeConverter.asUri(toReveal.textDocument.uri);
52 const position = Server.client.protocol2CodeConverter.asPosition(toReveal.position);
53 const editor = vscode.window.activeTextEditor;
54 if (!editor || editor.document.uri.toString() !== uri.toString()) { return; }
55 if (!editor.selection.isEmpty) { return; }
56 editor!.selection = new vscode.Selection(position, position);
57 }
58}
diff --git a/editors/code/src/commands/extend_selection.ts b/editors/code/src/commands/extend_selection.ts
new file mode 100644
index 000000000..cdc3d10fb
--- /dev/null
+++ b/editors/code/src/commands/extend_selection.ts
@@ -0,0 +1,29 @@
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') { return; }
18 const request: ExtendSelectionParams = {
19 selections: editor.selections.map((s) => {
20 return Server.client.code2ProtocolConverter.asRange(s);
21 }),
22 textDocument: { uri: editor.document.uri.toString() },
23 };
24 const response = await Server.client.sendRequest<ExtendSelectionResult>('m/extendSelection', request);
25 editor.selections = response.selections.map((range: Range) => {
26 const r = Server.client.protocol2CodeConverter.asRange(range);
27 return new vscode.Selection(r.start, r.end);
28 });
29}
diff --git a/editors/code/src/commands/index.ts b/editors/code/src/commands/index.ts
new file mode 100644
index 000000000..dfdcd6454
--- /dev/null
+++ b/editors/code/src/commands/index.ts
@@ -0,0 +1,17 @@
1import * as applySourceChange from './apply_source_change';
2import * as extendSelection from './extend_selection';
3import * as joinLines from './join_lines';
4import * as matchingBrace from './matching_brace';
5import * as parentModule from './parent_module';
6import * as runnables from './runnables';
7import * as syntaxTree from './syntaxTree';
8
9export {
10 applySourceChange,
11 extendSelection,
12 joinLines,
13 matchingBrace,
14 parentModule,
15 runnables,
16 syntaxTree,
17};
diff --git a/editors/code/src/commands/join_lines.ts b/editors/code/src/commands/join_lines.ts
new file mode 100644
index 000000000..526b698cc
--- /dev/null
+++ b/editors/code/src/commands/join_lines.ts
@@ -0,0 +1,21 @@
1import * as vscode from 'vscode';
2
3import { Range, TextDocumentIdentifier } from 'vscode-languageclient';
4import { Server } from '../server';
5import { handle as applySourceChange, SourceChange } from './apply_source_change';
6
7interface JoinLinesParams {
8 textDocument: TextDocumentIdentifier;
9 range: Range;
10}
11
12export async function handle() {
13 const editor = vscode.window.activeTextEditor;
14 if (editor == null || editor.document.languageId !== 'rust') { return; }
15 const request: JoinLinesParams = {
16 range: Server.client.code2ProtocolConverter.asRange(editor.selection),
17 textDocument: { uri: editor.document.uri.toString() },
18 };
19 const change = await Server.client.sendRequest<SourceChange>('m/joinLines', request);
20 await applySourceChange(change);
21}
diff --git a/editors/code/src/commands/matching_brace.ts b/editors/code/src/commands/matching_brace.ts
new file mode 100644
index 000000000..a80446a8f
--- /dev/null
+++ b/editors/code/src/commands/matching_brace.ts
@@ -0,0 +1,27 @@
1import * as vscode from 'vscode';
2
3import { Position, TextDocumentIdentifier } from 'vscode-languageclient';
4import { Server } from '../server';
5
6interface FindMatchingBraceParams {
7 textDocument: TextDocumentIdentifier;
8 offsets: Position[];
9}
10
11export async function handle() {
12 const editor = vscode.window.activeTextEditor;
13 if (editor == null || editor.document.languageId !== 'rust') { return; }
14 const request: FindMatchingBraceParams = {
15 textDocument: { uri: editor.document.uri.toString() },
16 offsets: editor.selections.map((s) => {
17 return Server.client.code2ProtocolConverter.asPosition(s.active);
18 }),
19 };
20 const response = await Server.client.sendRequest<Position[]>('m/findMatchingBrace', request);
21 editor.selections = editor.selections.map((sel, idx) => {
22 const active = Server.client.protocol2CodeConverter.asPosition(response[idx]);
23 const anchor = sel.isEmpty ? active : sel.anchor;
24 return new vscode.Selection(anchor, active);
25 });
26 editor.revealRange(editor.selection);
27}
diff --git a/editors/code/src/commands/parent_module.ts b/editors/code/src/commands/parent_module.ts
new file mode 100644
index 000000000..d66fb3026
--- /dev/null
+++ b/editors/code/src/commands/parent_module.ts
@@ -0,0 +1,22 @@
1import * as vscode from 'vscode';
2
3import { Location, TextDocumentIdentifier } from 'vscode-languageclient';
4import { Server } from '../server';
5
6export async function handle() {
7 const editor = vscode.window.activeTextEditor;
8 if (editor == null || editor.document.languageId !== 'rust') { return; }
9 const request: TextDocumentIdentifier = {
10 uri: editor.document.uri.toString(),
11 };
12 const response = await Server.client.sendRequest<Location[]>('m/parentModule', request);
13 const loc = response[0];
14 if (loc == null) { return; }
15 const uri = Server.client.protocol2CodeConverter.asUri(loc.uri);
16 const range = Server.client.protocol2CodeConverter.asRange(loc.range);
17
18 const doc = await vscode.workspace.openTextDocument(uri);
19 const e = await vscode.window.showTextDocument(doc);
20 e.selection = new vscode.Selection(range.start, range.start);
21 e.revealRange(range, vscode.TextEditorRevealType.InCenter);
22}
diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/commands/runnables.ts
new file mode 100644
index 000000000..40f590dce
--- /dev/null
+++ b/editors/code/src/commands/runnables.ts
@@ -0,0 +1,88 @@
1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient';
3import { Server } from '../server';
4
5interface RunnablesParams {
6 textDocument: lc.TextDocumentIdentifier;
7 position?: lc.Position;
8}
9
10interface Runnable {
11 range: lc.Range;
12 label: string;
13 bin: string;
14 args: string[];
15 env: { [index: string]: string };
16}
17
18class RunnableQuickPick implements vscode.QuickPickItem {
19 public label: string;
20 public description?: string | undefined;
21 public detail?: string | undefined;
22 public picked?: boolean | undefined;
23
24 constructor(public runnable: Runnable) {
25 this.label = runnable.label;
26 }
27}
28
29interface CargoTaskDefinition extends vscode.TaskDefinition {
30 type: 'cargo';
31 label: string;
32 command: string;
33 args: string[];
34 env?: { [key: string]: string };
35}
36
37function createTask(spec: Runnable): vscode.Task {
38 const TASK_SOURCE = 'Rust';
39 const definition: CargoTaskDefinition = {
40 type: 'cargo',
41 label: 'cargo',
42 command: spec.bin,
43 args: spec.args,
44 env: spec.env,
45 };
46
47 const execCmd = `${definition.command} ${definition.args.join(' ')}`;
48 const execOption: vscode.ShellExecutionOptions = {
49 cwd: '.',
50 env: definition.env,
51 };
52 const exec = new vscode.ShellExecution(`clear; ${execCmd}`, execOption);
53
54 const f = vscode.workspace.workspaceFolders![0];
55 const t = new vscode.Task(definition, f, definition.label, TASK_SOURCE, exec, ['$rustc']);
56 return t;
57}
58
59let prevRunnable: RunnableQuickPick | undefined;
60export async function handle() {
61 const editor = vscode.window.activeTextEditor;
62 if (editor == null || editor.document.languageId !== 'rust') { return; }
63 const textDocument: lc.TextDocumentIdentifier = {
64 uri: editor.document.uri.toString(),
65 };
66 const params: RunnablesParams = {
67 textDocument,
68 position: Server.client.code2ProtocolConverter.asPosition(editor.selection.active),
69 };
70 const runnables = await Server.client.sendRequest<Runnable[]>('m/runnables', params);
71 const items: RunnableQuickPick[] = [];
72 if (prevRunnable) {
73 items.push(prevRunnable);
74 }
75 for (const r of runnables) {
76 if (prevRunnable && JSON.stringify(prevRunnable.runnable) === JSON.stringify(r)) {
77 continue;
78 }
79 items.push(new RunnableQuickPick(r));
80 }
81 const item = await vscode.window.showQuickPick(items);
82 if (item) {
83 item.detail = 'rerun';
84 prevRunnable = item;
85 const task = createTask(item.runnable);
86 return await vscode.tasks.executeTask(task);
87 }
88}
diff --git a/editors/code/src/commands/syntaxTree.ts b/editors/code/src/commands/syntaxTree.ts
new file mode 100644
index 000000000..dcb721eee
--- /dev/null
+++ b/editors/code/src/commands/syntaxTree.ts
@@ -0,0 +1,38 @@
1import * as vscode from 'vscode';
2import { TextDocumentIdentifier } from 'vscode-languageclient';
3
4import { Server } from '../server';
5
6export const syntaxTreeUri = vscode.Uri.parse('ra-lsp://syntaxtree');
7
8export class TextDocumentContentProvider implements vscode.TextDocumentContentProvider {
9 public eventEmitter = new vscode.EventEmitter<vscode.Uri>();
10 public syntaxTree: string = 'Not available';
11
12 public provideTextDocumentContent(uri: vscode.Uri): vscode.ProviderResult<string> {
13 const editor = vscode.window.activeTextEditor;
14 if (editor == null) { return ''; }
15 const request: SyntaxTreeParams = {
16 textDocument: { uri: editor.document.uri.toString() },
17 };
18 return Server.client.sendRequest<SyntaxTreeResult>('m/syntaxTree', request);
19 }
20
21 get onDidChange(): vscode.Event<vscode.Uri> {
22 return this.eventEmitter.event;
23 }
24}
25
26interface SyntaxTreeParams {
27 textDocument: TextDocumentIdentifier;
28}
29
30type SyntaxTreeResult = string;
31
32// Opens the virtual file that will show the syntax tree
33//
34// The contents of the file come from the `TextDocumentContentProvider`
35export async function handle() {
36 const document = await vscode.workspace.openTextDocument(syntaxTreeUri);
37 return vscode.window.showTextDocument(document, vscode.ViewColumn.Two, true);
38}