diff options
-rw-r--r-- | editors/code/src/commands/apply_source_change.ts | 58 | ||||
-rw-r--r-- | editors/code/src/commands/extend_selection.ts | 29 | ||||
-rw-r--r-- | editors/code/src/commands/index.ts | 17 | ||||
-rw-r--r-- | editors/code/src/commands/join_lines.ts | 21 | ||||
-rw-r--r-- | editors/code/src/commands/matching_brace.ts | 27 | ||||
-rw-r--r-- | editors/code/src/commands/parent_module.ts | 22 | ||||
-rw-r--r-- | editors/code/src/commands/runnables.ts | 88 | ||||
-rw-r--r-- | editors/code/src/commands/syntaxTree.ts | 38 | ||||
-rw-r--r-- | editors/code/src/config.ts | 23 | ||||
-rw-r--r-- | editors/code/src/events/change_active_text_editor.ts | 14 | ||||
-rw-r--r-- | editors/code/src/events/change_text_document.ts | 19 | ||||
-rw-r--r-- | editors/code/src/events/index.ts | 7 | ||||
-rw-r--r-- | editors/code/src/extension.ts | 452 | ||||
-rw-r--r-- | editors/code/src/highlighting.ts | 82 | ||||
-rw-r--r-- | editors/code/src/notifications/index.ts | 5 | ||||
-rw-r--r-- | editors/code/src/notifications/publish_decorations.ts | 20 | ||||
-rw-r--r-- | editors/code/src/server.ts | 37 | ||||
-rw-r--r-- | editors/code/tslint.json | 13 |
18 files changed, 557 insertions, 415 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 @@ | |||
1 | import * as vscode from 'vscode'; | ||
2 | import * as lc from 'vscode-languageclient'; | ||
3 | |||
4 | import { Server } from '../server'; | ||
5 | |||
6 | interface FileSystemEdit { | ||
7 | type: string; | ||
8 | uri?: string; | ||
9 | src?: string; | ||
10 | dst?: string; | ||
11 | } | ||
12 | |||
13 | export interface SourceChange { | ||
14 | label: string; | ||
15 | sourceFileEdits: lc.TextDocumentEdit[]; | ||
16 | fileSystemEdits: FileSystemEdit[]; | ||
17 | cursorPosition?: lc.TextDocumentPositionParams; | ||
18 | } | ||
19 | |||
20 | export 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 @@ | |||
1 | import * as vscode from 'vscode'; | ||
2 | |||
3 | import { Range, TextDocumentIdentifier } from 'vscode-languageclient'; | ||
4 | import { Server } from '../server'; | ||
5 | |||
6 | interface ExtendSelectionParams { | ||
7 | textDocument: TextDocumentIdentifier; | ||
8 | selections: Range[]; | ||
9 | } | ||
10 | |||
11 | interface ExtendSelectionResult { | ||
12 | selections: Range[]; | ||
13 | } | ||
14 | |||
15 | export 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 @@ | |||
1 | import * as applySourceChange from './apply_source_change'; | ||
2 | import * as extendSelection from './extend_selection'; | ||
3 | import * as joinLines from './join_lines'; | ||
4 | import * as matchingBrace from './matching_brace'; | ||
5 | import * as parentModule from './parent_module'; | ||
6 | import * as runnables from './runnables'; | ||
7 | import * as syntaxTree from './syntaxTree'; | ||
8 | |||
9 | export { | ||
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 @@ | |||
1 | import * as vscode from 'vscode'; | ||
2 | |||
3 | import { Range, TextDocumentIdentifier } from 'vscode-languageclient'; | ||
4 | import { Server } from '../server'; | ||
5 | import { handle as applySourceChange, SourceChange } from './apply_source_change'; | ||
6 | |||
7 | interface JoinLinesParams { | ||
8 | textDocument: TextDocumentIdentifier; | ||
9 | range: Range; | ||
10 | } | ||
11 | |||
12 | export 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 @@ | |||
1 | import * as vscode from 'vscode'; | ||
2 | |||
3 | import { Position, TextDocumentIdentifier } from 'vscode-languageclient'; | ||
4 | import { Server } from '../server'; | ||
5 | |||
6 | interface FindMatchingBraceParams { | ||
7 | textDocument: TextDocumentIdentifier; | ||
8 | offsets: Position[]; | ||
9 | } | ||
10 | |||
11 | export 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 @@ | |||
1 | import * as vscode from 'vscode'; | ||
2 | |||
3 | import { Location, TextDocumentIdentifier } from 'vscode-languageclient'; | ||
4 | import { Server } from '../server'; | ||
5 | |||
6 | export 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 @@ | |||
1 | import * as vscode from 'vscode'; | ||
2 | import * as lc from 'vscode-languageclient'; | ||
3 | import { Server } from '../server'; | ||
4 | |||
5 | interface RunnablesParams { | ||
6 | textDocument: lc.TextDocumentIdentifier; | ||
7 | position?: lc.Position; | ||
8 | } | ||
9 | |||
10 | interface Runnable { | ||
11 | range: lc.Range; | ||
12 | label: string; | ||
13 | bin: string; | ||
14 | args: string[]; | ||
15 | env: { [index: string]: string }; | ||
16 | } | ||
17 | |||
18 | class 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 | |||
29 | interface CargoTaskDefinition extends vscode.TaskDefinition { | ||
30 | type: 'cargo'; | ||
31 | label: string; | ||
32 | command: string; | ||
33 | args: string[]; | ||
34 | env?: { [key: string]: string }; | ||
35 | } | ||
36 | |||
37 | function 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 | |||
59 | let prevRunnable: RunnableQuickPick | undefined; | ||
60 | export 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 @@ | |||
1 | import * as vscode from 'vscode'; | ||
2 | import { TextDocumentIdentifier } from 'vscode-languageclient'; | ||
3 | |||
4 | import { Server } from '../server'; | ||
5 | |||
6 | export const syntaxTreeUri = vscode.Uri.parse('ra-lsp://syntaxtree'); | ||
7 | |||
8 | export 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 | |||
26 | interface SyntaxTreeParams { | ||
27 | textDocument: TextDocumentIdentifier; | ||
28 | } | ||
29 | |||
30 | type 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` | ||
35 | export async function handle() { | ||
36 | const document = await vscode.workspace.openTextDocument(syntaxTreeUri); | ||
37 | return vscode.window.showTextDocument(document, vscode.ViewColumn.Two, true); | ||
38 | } | ||
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts new file mode 100644 index 000000000..740b5be20 --- /dev/null +++ b/editors/code/src/config.ts | |||
@@ -0,0 +1,23 @@ | |||
1 | import * as vscode from 'vscode'; | ||
2 | |||
3 | import { Server } from './server'; | ||
4 | |||
5 | export class Config { | ||
6 | public highlightingOn = true; | ||
7 | |||
8 | constructor() { | ||
9 | vscode.workspace.onDidChangeConfiguration((_) => this.userConfigChanged()); | ||
10 | this.userConfigChanged(); | ||
11 | } | ||
12 | |||
13 | public userConfigChanged() { | ||
14 | const config = vscode.workspace.getConfiguration('ra-lsp'); | ||
15 | if (config.has('highlightingOn')) { | ||
16 | this.highlightingOn = config.get('highlightingOn') as boolean; | ||
17 | } | ||
18 | |||
19 | if (!this.highlightingOn && Server) { | ||
20 | Server.highlighter.removeHighlights(); | ||
21 | } | ||
22 | } | ||
23 | } | ||
diff --git a/editors/code/src/events/change_active_text_editor.ts b/editors/code/src/events/change_active_text_editor.ts new file mode 100644 index 000000000..3440aa0c3 --- /dev/null +++ b/editors/code/src/events/change_active_text_editor.ts | |||
@@ -0,0 +1,14 @@ | |||
1 | import { TextEditor } from 'vscode'; | ||
2 | import { TextDocumentIdentifier } from 'vscode-languageclient'; | ||
3 | |||
4 | import { Decoration } from '../highlighting'; | ||
5 | import { Server } from '../server'; | ||
6 | |||
7 | export async function handle(editor: TextEditor | undefined) { | ||
8 | if (!Server.config.highlightingOn || !editor || editor.document.languageId !== 'rust') { return; } | ||
9 | const params: TextDocumentIdentifier = { | ||
10 | uri: editor.document.uri.toString(), | ||
11 | }; | ||
12 | const decorations = await Server.client.sendRequest<Decoration[]>('m/decorationsRequest', params); | ||
13 | Server.highlighter.setHighlights(editor, decorations); | ||
14 | } | ||
diff --git a/editors/code/src/events/change_text_document.ts b/editors/code/src/events/change_text_document.ts new file mode 100644 index 000000000..b3000e026 --- /dev/null +++ b/editors/code/src/events/change_text_document.ts | |||
@@ -0,0 +1,19 @@ | |||
1 | import * as vscode from 'vscode'; | ||
2 | |||
3 | import { syntaxTreeUri, TextDocumentContentProvider } from '../commands/syntaxTree'; | ||
4 | |||
5 | export function createHandler(textDocumentContentProvider: TextDocumentContentProvider) { | ||
6 | return (event: vscode.TextDocumentChangeEvent) => { | ||
7 | const doc = event.document; | ||
8 | if (doc.languageId !== 'rust') { return; } | ||
9 | afterLs(() => { | ||
10 | textDocumentContentProvider.eventEmitter.fire(syntaxTreeUri); | ||
11 | }); | ||
12 | }; | ||
13 | } | ||
14 | |||
15 | // We need to order this after LS updates, but there's no API for that. | ||
16 | // Hence, good old setTimeout. | ||
17 | function afterLs(f: () => any) { | ||
18 | setTimeout(f, 10); | ||
19 | } | ||
diff --git a/editors/code/src/events/index.ts b/editors/code/src/events/index.ts new file mode 100644 index 000000000..b570a7a92 --- /dev/null +++ b/editors/code/src/events/index.ts | |||
@@ -0,0 +1,7 @@ | |||
1 | import * as changeActiveTextEditor from './change_active_text_editor'; | ||
2 | import * as changeTextDocument from './change_text_document'; | ||
3 | |||
4 | export { | ||
5 | changeActiveTextEditor, | ||
6 | changeTextDocument, | ||
7 | }; | ||
diff --git a/editors/code/src/extension.ts b/editors/code/src/extension.ts index fde6a480d..44e74f4cc 100644 --- a/editors/code/src/extension.ts +++ b/editors/code/src/extension.ts | |||
@@ -1,434 +1,56 @@ | |||
1 | 'use strict'; | ||
2 | import * as vscode from 'vscode'; | 1 | import * as vscode from 'vscode'; |
3 | import * as lc from 'vscode-languageclient' | 2 | import * as lc from 'vscode-languageclient'; |
4 | 3 | ||
5 | let client: lc.LanguageClient; | 4 | import * as commands from './commands'; |
6 | 5 | import { TextDocumentContentProvider } from './commands/syntaxTree'; | |
7 | let uris = { | 6 | import * as events from './events'; |
8 | syntaxTree: vscode.Uri.parse('ra-lsp://syntaxtree') | 7 | import * as notifications from './notifications'; |
9 | } | 8 | import { Server } from './server'; |
10 | |||
11 | let highlightingOn = true; | ||
12 | 9 | ||
13 | export function activate(context: vscode.ExtensionContext) { | 10 | export function activate(context: vscode.ExtensionContext) { |
14 | let applyHighlightingOn = () => { | 11 | function disposeOnDeactivation(disposable: vscode.Disposable) { |
15 | let config = vscode.workspace.getConfiguration('ra-lsp'); | ||
16 | if (config.has('highlightingOn')) { | ||
17 | highlightingOn = config.get('highlightingOn') as boolean; | ||
18 | }; | ||
19 | |||
20 | if (!highlightingOn) { | ||
21 | removeHighlights(); | ||
22 | } | ||
23 | }; | ||
24 | |||
25 | // Apply the highlightingOn config now and whenever the config changes | ||
26 | applyHighlightingOn(); | ||
27 | vscode.workspace.onDidChangeConfiguration(_ => { | ||
28 | applyHighlightingOn(); | ||
29 | }); | ||
30 | |||
31 | let textDocumentContentProvider = new TextDocumentContentProvider() | ||
32 | let dispose = (disposable: vscode.Disposable) => { | ||
33 | context.subscriptions.push(disposable); | 12 | context.subscriptions.push(disposable); |
34 | } | 13 | } |
35 | let registerCommand = (name: string, f: any) => { | 14 | |
36 | dispose(vscode.commands.registerCommand(name, f)) | 15 | function registerCommand(name: string, f: any) { |
16 | disposeOnDeactivation(vscode.commands.registerCommand(name, f)); | ||
37 | } | 17 | } |
38 | 18 | ||
39 | registerCommand('ra-lsp.syntaxTree', () => openDoc(uris.syntaxTree)) | 19 | // Commands are requests from vscode to the language server |
40 | registerCommand('ra-lsp.extendSelection', async () => { | 20 | registerCommand('ra-lsp.syntaxTree', commands.syntaxTree.handle); |
41 | let editor = vscode.window.activeTextEditor | 21 | registerCommand('ra-lsp.extendSelection', commands.extendSelection.handle); |
42 | if (editor == null || editor.document.languageId != "rust") return | 22 | registerCommand('ra-lsp.matchingBrace', commands.matchingBrace.handle); |
43 | let request: ExtendSelectionParams = { | 23 | registerCommand('ra-lsp.joinLines', commands.joinLines.handle); |
44 | textDocument: { uri: editor.document.uri.toString() }, | 24 | registerCommand('ra-lsp.parentModule', commands.parentModule.handle); |
45 | selections: editor.selections.map((s) => { | 25 | registerCommand('ra-lsp.run', commands.runnables.handle); |
46 | return client.code2ProtocolConverter.asRange(s) | 26 | registerCommand('ra-lsp.applySourceChange', commands.applySourceChange.handle); |
47 | }) | ||
48 | } | ||
49 | let response = await client.sendRequest<ExtendSelectionResult>("m/extendSelection", request) | ||
50 | editor.selections = response.selections.map((range) => { | ||
51 | let r = client.protocol2CodeConverter.asRange(range) | ||
52 | return new vscode.Selection(r.start, r.end) | ||
53 | }) | ||
54 | }) | ||
55 | registerCommand('ra-lsp.matchingBrace', async () => { | ||
56 | let editor = vscode.window.activeTextEditor | ||
57 | if (editor == null || editor.document.languageId != "rust") return | ||
58 | let request: FindMatchingBraceParams = { | ||
59 | textDocument: { uri: editor.document.uri.toString() }, | ||
60 | offsets: editor.selections.map((s) => { | ||
61 | return client.code2ProtocolConverter.asPosition(s.active) | ||
62 | }) | ||
63 | } | ||
64 | let response = await client.sendRequest<lc.Position[]>("m/findMatchingBrace", request) | ||
65 | editor.selections = editor.selections.map((sel, idx) => { | ||
66 | let active = client.protocol2CodeConverter.asPosition(response[idx]) | ||
67 | let anchor = sel.isEmpty ? active : sel.anchor | ||
68 | return new vscode.Selection(anchor, active) | ||
69 | }) | ||
70 | editor.revealRange(editor.selection) | ||
71 | }) | ||
72 | registerCommand('ra-lsp.joinLines', async () => { | ||
73 | let editor = vscode.window.activeTextEditor | ||
74 | if (editor == null || editor.document.languageId != "rust") return | ||
75 | let request: JoinLinesParams = { | ||
76 | textDocument: { uri: editor.document.uri.toString() }, | ||
77 | range: client.code2ProtocolConverter.asRange(editor.selection), | ||
78 | } | ||
79 | let change = await client.sendRequest<SourceChange>("m/joinLines", request) | ||
80 | await applySourceChange(change) | ||
81 | }) | ||
82 | registerCommand('ra-lsp.parentModule', async () => { | ||
83 | let editor = vscode.window.activeTextEditor | ||
84 | if (editor == null || editor.document.languageId != "rust") return | ||
85 | let request: lc.TextDocumentIdentifier = { | ||
86 | uri: editor.document.uri.toString() | ||
87 | } | ||
88 | let response = await client.sendRequest<lc.Location[]>("m/parentModule", request) | ||
89 | let loc = response[0] | ||
90 | if (loc == null) return | ||
91 | let uri = client.protocol2CodeConverter.asUri(loc.uri) | ||
92 | let range = client.protocol2CodeConverter.asRange(loc.range) | ||
93 | 27 | ||
94 | let doc = await vscode.workspace.openTextDocument(uri) | 28 | // Notifications are events triggered by the language server |
95 | let e = await vscode.window.showTextDocument(doc) | 29 | const allNotifications: Iterable<[string, lc.GenericNotificationHandler]> = [ |
96 | e.selection = new vscode.Selection(range.start, range.start) | 30 | ['m/publishDecorations', notifications.publishDecorations.handle], |
97 | e.revealRange(range, vscode.TextEditorRevealType.InCenter) | 31 | ]; |
98 | }) | ||
99 | 32 | ||
100 | let prevRunnable: RunnableQuickPick | undefined = undefined | 33 | // The events below are plain old javascript events, triggered and handled by vscode |
101 | registerCommand('ra-lsp.run', async () => { | 34 | vscode.window.onDidChangeActiveTextEditor(events.changeActiveTextEditor.handle); |
102 | let editor = vscode.window.activeTextEditor | ||
103 | if (editor == null || editor.document.languageId != "rust") return | ||
104 | let textDocument: lc.TextDocumentIdentifier = { | ||
105 | uri: editor.document.uri.toString() | ||
106 | } | ||
107 | let params: RunnablesParams = { | ||
108 | textDocument, | ||
109 | position: client.code2ProtocolConverter.asPosition(editor.selection.active) | ||
110 | } | ||
111 | let runnables = await client.sendRequest<Runnable[]>('m/runnables', params) | ||
112 | let items: RunnableQuickPick[] = [] | ||
113 | if (prevRunnable) { | ||
114 | items.push(prevRunnable) | ||
115 | } | ||
116 | for (let r of runnables) { | ||
117 | if (prevRunnable && JSON.stringify(prevRunnable.runnable) == JSON.stringify(r)) { | ||
118 | continue | ||
119 | } | ||
120 | items.push(new RunnableQuickPick(r)) | ||
121 | } | ||
122 | let item = await vscode.window.showQuickPick(items) | ||
123 | if (item) { | ||
124 | item.detail = "rerun" | ||
125 | prevRunnable = item | ||
126 | let task = createTask(item.runnable) | ||
127 | return await vscode.tasks.executeTask(task) | ||
128 | } | ||
129 | }) | ||
130 | registerCommand('ra-lsp.applySourceChange', applySourceChange) | ||
131 | 35 | ||
132 | dispose(vscode.workspace.registerTextDocumentContentProvider( | 36 | const textDocumentContentProvider = new TextDocumentContentProvider(); |
37 | disposeOnDeactivation(vscode.workspace.registerTextDocumentContentProvider( | ||
133 | 'ra-lsp', | 38 | 'ra-lsp', |
134 | textDocumentContentProvider | 39 | textDocumentContentProvider, |
135 | )) | 40 | )); |
136 | startServer() | ||
137 | vscode.workspace.onDidChangeTextDocument((event: vscode.TextDocumentChangeEvent) => { | ||
138 | let doc = event.document | ||
139 | if (doc.languageId != "rust") return | ||
140 | afterLs(() => { | ||
141 | textDocumentContentProvider.eventEmitter.fire(uris.syntaxTree) | ||
142 | }) | ||
143 | }, null, context.subscriptions) | ||
144 | vscode.window.onDidChangeActiveTextEditor(async (editor) => { | ||
145 | if (!highlightingOn || !editor || editor.document.languageId != 'rust') return | ||
146 | let params: lc.TextDocumentIdentifier = { | ||
147 | uri: editor.document.uri.toString() | ||
148 | } | ||
149 | let decorations = await client.sendRequest<Decoration[]>("m/decorationsRequest", params) | ||
150 | setHighlights(editor, decorations) | ||
151 | }) | ||
152 | } | ||
153 | 41 | ||
154 | // We need to order this after LS updates, but there's no API for that. | 42 | vscode.workspace.onDidChangeTextDocument( |
155 | // Hence, good old setTimeout. | 43 | events.changeTextDocument.createHandler(textDocumentContentProvider), |
156 | function afterLs(f: () => any) { | 44 | null, |
157 | setTimeout(f, 10) | 45 | context.subscriptions); |
46 | |||
47 | // Start the language server, finally! | ||
48 | Server.start(allNotifications); | ||
158 | } | 49 | } |
159 | 50 | ||
160 | export function deactivate(): Thenable<void> { | 51 | export function deactivate(): Thenable<void> { |
161 | if (!client) { | 52 | if (!Server.client) { |
162 | return Promise.resolve(); | 53 | return Promise.resolve(); |
163 | } | 54 | } |
164 | return client.stop(); | 55 | return Server.client.stop(); |
165 | } | ||
166 | |||
167 | function startServer() { | ||
168 | let run: lc.Executable = { | ||
169 | command: "ra_lsp_server", | ||
170 | options: { cwd: "." } | ||
171 | } | ||
172 | let serverOptions: lc.ServerOptions = { | ||
173 | run, | ||
174 | debug: run | ||
175 | }; | ||
176 | |||
177 | let clientOptions: lc.LanguageClientOptions = { | ||
178 | documentSelector: [{ scheme: 'file', language: 'rust' }], | ||
179 | }; | ||
180 | |||
181 | client = new lc.LanguageClient( | ||
182 | 'ra-lsp', | ||
183 | 'rust-analyzer languge server', | ||
184 | serverOptions, | ||
185 | clientOptions, | ||
186 | ); | ||
187 | client.onReady().then(() => { | ||
188 | client.onNotification( | ||
189 | "m/publishDecorations", | ||
190 | (params: PublishDecorationsParams) => { | ||
191 | let editor = vscode.window.visibleTextEditors.find( | ||
192 | (editor) => editor.document.uri.toString() == params.uri | ||
193 | ) | ||
194 | if (!highlightingOn || !editor) return; | ||
195 | setHighlights( | ||
196 | editor, | ||
197 | params.decorations, | ||
198 | ) | ||
199 | } | ||
200 | ) | ||
201 | }) | ||
202 | client.start(); | ||
203 | } | ||
204 | |||
205 | async function openDoc(uri: vscode.Uri) { | ||
206 | let document = await vscode.workspace.openTextDocument(uri) | ||
207 | return vscode.window.showTextDocument(document, vscode.ViewColumn.Two, true) | ||
208 | } | ||
209 | |||
210 | class TextDocumentContentProvider implements vscode.TextDocumentContentProvider { | ||
211 | public eventEmitter = new vscode.EventEmitter<vscode.Uri>() | ||
212 | public syntaxTree: string = "Not available" | ||
213 | |||
214 | public provideTextDocumentContent(uri: vscode.Uri): vscode.ProviderResult<string> { | ||
215 | let editor = vscode.window.activeTextEditor; | ||
216 | if (editor == null) return "" | ||
217 | let request: SyntaxTreeParams = { | ||
218 | textDocument: { uri: editor.document.uri.toString() } | ||
219 | }; | ||
220 | return client.sendRequest<SyntaxTreeResult>("m/syntaxTree", request); | ||
221 | } | ||
222 | |||
223 | get onDidChange(): vscode.Event<vscode.Uri> { | ||
224 | return this.eventEmitter.event | ||
225 | } | ||
226 | } | ||
227 | |||
228 | let decorations: { [index: string]: vscode.TextEditorDecorationType } = {}; | ||
229 | |||
230 | function initDecorations() { | ||
231 | const decor = (obj: any) => vscode.window.createTextEditorDecorationType({ color: obj }) | ||
232 | decorations = { | ||
233 | background: decor("#3F3F3F"), | ||
234 | error: vscode.window.createTextEditorDecorationType({ | ||
235 | borderColor: "red", | ||
236 | borderStyle: "none none dashed none", | ||
237 | }), | ||
238 | comment: decor("#7F9F7F"), | ||
239 | string: decor("#CC9393"), | ||
240 | keyword: decor("#F0DFAF"), | ||
241 | function: decor("#93E0E3"), | ||
242 | parameter: decor("#94BFF3"), | ||
243 | builtin: decor("#DD6718"), | ||
244 | text: decor("#DCDCCC"), | ||
245 | attribute: decor("#BFEBBF"), | ||
246 | literal: decor("#DFAF8F"), | ||
247 | } | ||
248 | } | ||
249 | |||
250 | function removeHighlights() { | ||
251 | for (let tag in decorations) { | ||
252 | decorations[tag].dispose(); | ||
253 | } | ||
254 | |||
255 | decorations = {}; | ||
256 | } | ||
257 | |||
258 | function setHighlights( | ||
259 | editor: vscode.TextEditor, | ||
260 | highlights: Array<Decoration> | ||
261 | ) { | ||
262 | // Initialize decorations if necessary | ||
263 | // | ||
264 | // Note: decoration objects need to be kept around so we can dispose them | ||
265 | // if the user disables syntax highlighting | ||
266 | if (Object.keys(decorations).length === 0) { | ||
267 | initDecorations(); | ||
268 | } | ||
269 | |||
270 | let byTag: Map<string, vscode.Range[]> = new Map() | ||
271 | for (let tag in decorations) { | ||
272 | byTag.set(tag, []) | ||
273 | } | ||
274 | |||
275 | for (let d of highlights) { | ||
276 | if (!byTag.get(d.tag)) { | ||
277 | console.log(`unknown tag ${d.tag}`) | ||
278 | continue | ||
279 | } | ||
280 | byTag.get(d.tag)!.push( | ||
281 | client.protocol2CodeConverter.asRange(d.range) | ||
282 | ) | ||
283 | } | ||
284 | |||
285 | for (let tag of byTag.keys()) { | ||
286 | let dec: vscode.TextEditorDecorationType = decorations[tag] | ||
287 | let ranges = byTag.get(tag)! | ||
288 | editor.setDecorations(dec, ranges) | ||
289 | } | ||
290 | } | ||
291 | |||
292 | interface SyntaxTreeParams { | ||
293 | textDocument: lc.TextDocumentIdentifier; | ||
294 | } | ||
295 | |||
296 | type SyntaxTreeResult = string | ||
297 | |||
298 | interface ExtendSelectionParams { | ||
299 | textDocument: lc.TextDocumentIdentifier; | ||
300 | selections: lc.Range[]; | ||
301 | } | ||
302 | |||
303 | interface ExtendSelectionResult { | ||
304 | selections: lc.Range[]; | ||
305 | } | ||
306 | |||
307 | interface FindMatchingBraceParams { | ||
308 | textDocument: lc.TextDocumentIdentifier; | ||
309 | offsets: lc.Position[]; | ||
310 | } | ||
311 | |||
312 | interface JoinLinesParams { | ||
313 | textDocument: lc.TextDocumentIdentifier; | ||
314 | range: lc.Range; | ||
315 | } | ||
316 | |||
317 | interface PublishDecorationsParams { | ||
318 | uri: string, | ||
319 | decorations: Decoration[], | ||
320 | } | ||
321 | |||
322 | interface RunnablesParams { | ||
323 | textDocument: lc.TextDocumentIdentifier, | ||
324 | position?: lc.Position, | ||
325 | } | ||
326 | |||
327 | interface Runnable { | ||
328 | range: lc.Range; | ||
329 | label: string; | ||
330 | bin: string; | ||
331 | args: string[]; | ||
332 | env: { [index: string]: string }, | ||
333 | } | ||
334 | |||
335 | class RunnableQuickPick implements vscode.QuickPickItem { | ||
336 | label: string; | ||
337 | description?: string | undefined; | ||
338 | detail?: string | undefined; | ||
339 | picked?: boolean | undefined; | ||
340 | |||
341 | constructor(public runnable: Runnable) { | ||
342 | this.label = runnable.label | ||
343 | } | ||
344 | } | ||
345 | |||
346 | interface Decoration { | ||
347 | range: lc.Range, | ||
348 | tag: string, | ||
349 | } | ||
350 | |||
351 | |||
352 | interface CargoTaskDefinition extends vscode.TaskDefinition { | ||
353 | type: 'cargo'; | ||
354 | label: string; | ||
355 | command: string; | ||
356 | args: Array<string>; | ||
357 | env?: { [key: string]: string }; | ||
358 | } | ||
359 | |||
360 | function createTask(spec: Runnable): vscode.Task { | ||
361 | const TASK_SOURCE = 'Rust'; | ||
362 | let definition: CargoTaskDefinition = { | ||
363 | type: 'cargo', | ||
364 | label: 'cargo', | ||
365 | command: spec.bin, | ||
366 | args: spec.args, | ||
367 | env: spec.env | ||
368 | } | ||
369 | |||
370 | let execCmd = `${definition.command} ${definition.args.join(' ')}`; | ||
371 | let execOption: vscode.ShellExecutionOptions = { | ||
372 | cwd: '.', | ||
373 | env: definition.env, | ||
374 | }; | ||
375 | let exec = new vscode.ShellExecution(`clear; ${execCmd}`, execOption); | ||
376 | |||
377 | let f = vscode.workspace.workspaceFolders![0] | ||
378 | let t = new vscode.Task(definition, f, definition.label, TASK_SOURCE, exec, ['$rustc']); | ||
379 | return t; | ||
380 | } | ||
381 | |||
382 | interface FileSystemEdit { | ||
383 | type: string; | ||
384 | uri?: string; | ||
385 | src?: string; | ||
386 | dst?: string; | ||
387 | } | ||
388 | |||
389 | interface SourceChange { | ||
390 | label: string, | ||
391 | sourceFileEdits: lc.TextDocumentEdit[], | ||
392 | fileSystemEdits: FileSystemEdit[], | ||
393 | cursorPosition?: lc.TextDocumentPositionParams, | ||
394 | } | ||
395 | |||
396 | async function applySourceChange(change: SourceChange) { | ||
397 | console.log(`applySOurceChange ${JSON.stringify(change)}`) | ||
398 | let wsEdit = new vscode.WorkspaceEdit() | ||
399 | for (let sourceEdit of change.sourceFileEdits) { | ||
400 | let uri = client.protocol2CodeConverter.asUri(sourceEdit.textDocument.uri) | ||
401 | let edits = client.protocol2CodeConverter.asTextEdits(sourceEdit.edits) | ||
402 | wsEdit.set(uri, edits) | ||
403 | } | ||
404 | let created; | ||
405 | let moved; | ||
406 | for (let fsEdit of change.fileSystemEdits) { | ||
407 | if (fsEdit.type == "createFile") { | ||
408 | let uri = vscode.Uri.parse(fsEdit.uri!) | ||
409 | wsEdit.createFile(uri) | ||
410 | created = uri | ||
411 | } else if (fsEdit.type == "moveFile") { | ||
412 | let src = vscode.Uri.parse(fsEdit.src!) | ||
413 | let dst = vscode.Uri.parse(fsEdit.dst!) | ||
414 | wsEdit.renameFile(src, dst) | ||
415 | moved = dst | ||
416 | } else { | ||
417 | console.error(`unknown op: ${JSON.stringify(fsEdit)}`) | ||
418 | } | ||
419 | } | ||
420 | let toOpen = created || moved | ||
421 | let toReveal = change.cursorPosition | ||
422 | await vscode.workspace.applyEdit(wsEdit) | ||
423 | if (toOpen) { | ||
424 | let doc = await vscode.workspace.openTextDocument(toOpen) | ||
425 | await vscode.window.showTextDocument(doc) | ||
426 | } else if (toReveal) { | ||
427 | let uri = client.protocol2CodeConverter.asUri(toReveal.textDocument.uri) | ||
428 | let position = client.protocol2CodeConverter.asPosition(toReveal.position) | ||
429 | let editor = vscode.window.activeTextEditor; | ||
430 | if (!editor || editor.document.uri.toString() != uri.toString()) return | ||
431 | if (!editor.selection.isEmpty) return | ||
432 | editor!.selection = new vscode.Selection(position, position) | ||
433 | } | ||
434 | } | 56 | } |
diff --git a/editors/code/src/highlighting.ts b/editors/code/src/highlighting.ts new file mode 100644 index 000000000..e2ac4d629 --- /dev/null +++ b/editors/code/src/highlighting.ts | |||
@@ -0,0 +1,82 @@ | |||
1 | import * as vscode from 'vscode'; | ||
2 | import * as lc from 'vscode-languageclient'; | ||
3 | |||
4 | import { Server } from './server'; | ||
5 | |||
6 | export interface Decoration { | ||
7 | range: lc.Range; | ||
8 | tag: string; | ||
9 | } | ||
10 | |||
11 | export class Highlighter { | ||
12 | private static initDecorations(): Map<string, vscode.TextEditorDecorationType> { | ||
13 | const decor = (color: string) => vscode.window.createTextEditorDecorationType({ color }); | ||
14 | |||
15 | const decorations: Iterable<[string, vscode.TextEditorDecorationType]> = [ | ||
16 | ['background', decor('#3F3F3F')], | ||
17 | ['error', vscode.window.createTextEditorDecorationType({ | ||
18 | borderColor: 'red', | ||
19 | borderStyle: 'none none dashed none', | ||
20 | })], | ||
21 | ['comment', decor('#7F9F7F')], | ||
22 | ['string', decor('#CC9393')], | ||
23 | ['keyword', decor('#F0DFAF')], | ||
24 | ['function', decor('#93E0E3')], | ||
25 | ['parameter', decor('#94BFF3')], | ||
26 | ['builtin', decor('#DD6718')], | ||
27 | ['text', decor('#DCDCCC')], | ||
28 | ['attribute', decor('#BFEBBF')], | ||
29 | ['literal', decor('#DFAF8F')], | ||
30 | ]; | ||
31 | |||
32 | return new Map<string, vscode.TextEditorDecorationType>(decorations); | ||
33 | } | ||
34 | |||
35 | private decorations: (Map<string, vscode.TextEditorDecorationType> | null) = null; | ||
36 | |||
37 | public removeHighlights() { | ||
38 | if (this.decorations == null) { | ||
39 | return; | ||
40 | } | ||
41 | |||
42 | // Decorations are removed when the object is disposed | ||
43 | for (const decoration of this.decorations.values()) { | ||
44 | decoration.dispose(); | ||
45 | } | ||
46 | |||
47 | this.decorations = null; | ||
48 | } | ||
49 | |||
50 | public setHighlights( | ||
51 | editor: vscode.TextEditor, | ||
52 | highlights: Decoration[], | ||
53 | ) { | ||
54 | // Initialize decorations if necessary | ||
55 | // | ||
56 | // Note: decoration objects need to be kept around so we can dispose them | ||
57 | // if the user disables syntax highlighting | ||
58 | if (this.decorations == null) { | ||
59 | this.decorations = Highlighter.initDecorations(); | ||
60 | } | ||
61 | |||
62 | const byTag: Map<string, vscode.Range[]> = new Map(); | ||
63 | for (const tag of this.decorations.keys()) { | ||
64 | byTag.set(tag, []); | ||
65 | } | ||
66 | |||
67 | for (const d of highlights) { | ||
68 | if (!byTag.get(d.tag)) { | ||
69 | continue; | ||
70 | } | ||
71 | byTag.get(d.tag)!.push( | ||
72 | Server.client.protocol2CodeConverter.asRange(d.range), | ||
73 | ); | ||
74 | } | ||
75 | |||
76 | for (const tag of byTag.keys()) { | ||
77 | const dec = this.decorations.get(tag) as vscode.TextEditorDecorationType; | ||
78 | const ranges = byTag.get(tag)!; | ||
79 | editor.setDecorations(dec, ranges); | ||
80 | } | ||
81 | } | ||
82 | } | ||
diff --git a/editors/code/src/notifications/index.ts b/editors/code/src/notifications/index.ts new file mode 100644 index 000000000..c56576865 --- /dev/null +++ b/editors/code/src/notifications/index.ts | |||
@@ -0,0 +1,5 @@ | |||
1 | import * as publishDecorations from './publish_decorations'; | ||
2 | |||
3 | export { | ||
4 | publishDecorations, | ||
5 | }; | ||
diff --git a/editors/code/src/notifications/publish_decorations.ts b/editors/code/src/notifications/publish_decorations.ts new file mode 100644 index 000000000..d8790386b --- /dev/null +++ b/editors/code/src/notifications/publish_decorations.ts | |||
@@ -0,0 +1,20 @@ | |||
1 | import * as vscode from 'vscode'; | ||
2 | |||
3 | import { Decoration } from '../highlighting'; | ||
4 | import { Server } from '../server'; | ||
5 | |||
6 | export interface PublishDecorationsParams { | ||
7 | uri: string; | ||
8 | decorations: Decoration[]; | ||
9 | } | ||
10 | |||
11 | export function handle(params: PublishDecorationsParams) { | ||
12 | const targetEditor = vscode.window.visibleTextEditors.find( | ||
13 | (editor) => editor.document.uri.toString() === params.uri, | ||
14 | ); | ||
15 | if (!Server.config.highlightingOn || !targetEditor) { return; } | ||
16 | Server.highlighter.setHighlights( | ||
17 | targetEditor, | ||
18 | params.decorations, | ||
19 | ); | ||
20 | } | ||
diff --git a/editors/code/src/server.ts b/editors/code/src/server.ts new file mode 100644 index 000000000..01fd80756 --- /dev/null +++ b/editors/code/src/server.ts | |||
@@ -0,0 +1,37 @@ | |||
1 | import * as lc from 'vscode-languageclient'; | ||
2 | |||
3 | import { Config } from './config'; | ||
4 | import { Highlighter } from './highlighting'; | ||
5 | |||
6 | export class Server { | ||
7 | public static highlighter = new Highlighter(); | ||
8 | public static config = new Config(); | ||
9 | public static client: lc.LanguageClient; | ||
10 | |||
11 | public static start(notificationHandlers: Iterable<[string, lc.GenericNotificationHandler]>) { | ||
12 | const run: lc.Executable = { | ||
13 | command: 'ra_lsp_server', | ||
14 | options: { cwd: '.' }, | ||
15 | }; | ||
16 | const serverOptions: lc.ServerOptions = { | ||
17 | run, | ||
18 | debug: run, | ||
19 | }; | ||
20 | const clientOptions: lc.LanguageClientOptions = { | ||
21 | documentSelector: [{ scheme: 'file', language: 'rust' }], | ||
22 | }; | ||
23 | |||
24 | Server.client = new lc.LanguageClient( | ||
25 | 'ra-lsp', | ||
26 | 'rust-analyzer languge server', | ||
27 | serverOptions, | ||
28 | clientOptions, | ||
29 | ); | ||
30 | Server.client.onReady().then(() => { | ||
31 | for (const [type, handler] of notificationHandlers) { | ||
32 | Server.client.onNotification(type, handler); | ||
33 | } | ||
34 | }); | ||
35 | Server.client.start(); | ||
36 | } | ||
37 | } | ||
diff --git a/editors/code/tslint.json b/editors/code/tslint.json new file mode 100644 index 000000000..ce48dfc95 --- /dev/null +++ b/editors/code/tslint.json | |||
@@ -0,0 +1,13 @@ | |||
1 | { | ||
2 | "defaultSeverity": "error", | ||
3 | "extends": [ | ||
4 | "tslint:recommended" | ||
5 | ], | ||
6 | "jsRules": {}, | ||
7 | "rules": { | ||
8 | "quotemark": [true, "single"], | ||
9 | "interface-name": false, | ||
10 | "object-literal-sort-keys": false | ||
11 | }, | ||
12 | "rulesDirectory": [] | ||
13 | } | ||