From 69de7e2fd71c3a808f0ac856d7b105eeb210f169 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adolfo=20Ochagav=C3=ADa?= Date: Sun, 7 Oct 2018 22:44:25 +0200 Subject: [PATCH 1/4] Refactor vscode extension --- editors/code/src/commands.ts | 17 + .../code/src/commands/apply_source_change.ts | 58 +++ editors/code/src/commands/extend_selection.ts | 29 ++ editors/code/src/commands/join_lines.ts | 21 + editors/code/src/commands/matching_brace.ts | 27 ++ editors/code/src/commands/parent_module.ts | 22 + editors/code/src/commands/runnables.ts | 88 ++++ editors/code/src/commands/syntaxTree.ts | 38 ++ editors/code/src/events.ts | 7 + .../src/events/change_active_text_editor.ts | 14 + .../code/src/events/change_text_document.ts | 19 + editors/code/src/extension.ts | 441 ++---------------- editors/code/src/highlighting.ts | 78 ++++ editors/code/src/server.ts | 74 +++ 14 files changed, 518 insertions(+), 415 deletions(-) create mode 100644 editors/code/src/commands.ts create mode 100644 editors/code/src/commands/apply_source_change.ts create mode 100644 editors/code/src/commands/extend_selection.ts create mode 100644 editors/code/src/commands/join_lines.ts create mode 100644 editors/code/src/commands/matching_brace.ts create mode 100644 editors/code/src/commands/parent_module.ts create mode 100644 editors/code/src/commands/runnables.ts create mode 100644 editors/code/src/commands/syntaxTree.ts create mode 100644 editors/code/src/events.ts create mode 100644 editors/code/src/events/change_active_text_editor.ts create mode 100644 editors/code/src/events/change_text_document.ts create mode 100644 editors/code/src/highlighting.ts create mode 100644 editors/code/src/server.ts diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts new file mode 100644 index 00000000000..99cac3379e4 --- /dev/null +++ b/editors/code/src/commands.ts @@ -0,0 +1,17 @@ +import * as applySourceChange from './commands/apply_source_change'; +import * as extendSelection from './commands/extend_selection'; +import * as joinLines from './commands/join_lines'; +import * as matchingBrace from './commands/matching_brace'; +import * as parentModule from './commands/parent_module'; +import * as runnables from './commands/runnables'; +import * as syntaxTree from './commands/syntaxTree'; + +export { + applySourceChange, + extendSelection, + joinLines, + matchingBrace, + parentModule, + runnables, + syntaxTree +} 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 00000000000..dcbbb2b0984 --- /dev/null +++ b/editors/code/src/commands/apply_source_change.ts @@ -0,0 +1,58 @@ +import * as vscode from 'vscode'; +import * as lc from 'vscode-languageclient' + +import { Server } from '../server'; + +interface FileSystemEdit { + type: string; + uri?: string; + src?: string; + dst?: string; +} + +export interface SourceChange { + label: string, + sourceFileEdits: lc.TextDocumentEdit[], + fileSystemEdits: FileSystemEdit[], + cursorPosition?: lc.TextDocumentPositionParams, +} + +export async function handle(change: SourceChange) { + console.log(`applySOurceChange ${JSON.stringify(change)}`) + let wsEdit = new vscode.WorkspaceEdit() + for (let sourceEdit of change.sourceFileEdits) { + let uri = Server.client.protocol2CodeConverter.asUri(sourceEdit.textDocument.uri) + let edits = Server.client.protocol2CodeConverter.asTextEdits(sourceEdit.edits) + wsEdit.set(uri, edits) + } + let created; + let moved; + for (let fsEdit of change.fileSystemEdits) { + if (fsEdit.type == "createFile") { + let uri = vscode.Uri.parse(fsEdit.uri!) + wsEdit.createFile(uri) + created = uri + } else if (fsEdit.type == "moveFile") { + let src = vscode.Uri.parse(fsEdit.src!) + let dst = vscode.Uri.parse(fsEdit.dst!) + wsEdit.renameFile(src, dst) + moved = dst + } else { + console.error(`unknown op: ${JSON.stringify(fsEdit)}`) + } + } + let toOpen = created || moved + let toReveal = change.cursorPosition + await vscode.workspace.applyEdit(wsEdit) + if (toOpen) { + let doc = await vscode.workspace.openTextDocument(toOpen) + await vscode.window.showTextDocument(doc) + } else if (toReveal) { + let uri = Server.client.protocol2CodeConverter.asUri(toReveal.textDocument.uri) + let position = Server.client.protocol2CodeConverter.asPosition(toReveal.position) + let editor = vscode.window.activeTextEditor; + if (!editor || editor.document.uri.toString() != uri.toString()) return + if (!editor.selection.isEmpty) return + editor!.selection = new vscode.Selection(position, position) + } +} diff --git a/editors/code/src/commands/extend_selection.ts b/editors/code/src/commands/extend_selection.ts new file mode 100644 index 00000000000..b90828ba9ba --- /dev/null +++ b/editors/code/src/commands/extend_selection.ts @@ -0,0 +1,29 @@ +import * as vscode from 'vscode'; + +import { TextDocumentIdentifier, Range } from "vscode-languageclient"; +import { Server } from '../server'; + +interface ExtendSelectionParams { + textDocument: TextDocumentIdentifier; + selections: Range[]; +} + +interface ExtendSelectionResult { + selections: Range[]; +} + +export async function handle() { + let editor = vscode.window.activeTextEditor + if (editor == null || editor.document.languageId != "rust") return + let request: ExtendSelectionParams = { + textDocument: { uri: editor.document.uri.toString() }, + selections: editor.selections.map((s) => { + return Server.client.code2ProtocolConverter.asRange(s) + }) + } + let response = await Server.client.sendRequest("m/extendSelection", request) + editor.selections = response.selections.map((range: Range) => { + let r = Server.client.protocol2CodeConverter.asRange(range) + return new vscode.Selection(r.start, r.end) + }) +} diff --git a/editors/code/src/commands/join_lines.ts b/editors/code/src/commands/join_lines.ts new file mode 100644 index 00000000000..7ae7b9d7601 --- /dev/null +++ b/editors/code/src/commands/join_lines.ts @@ -0,0 +1,21 @@ +import * as vscode from 'vscode'; + +import { TextDocumentIdentifier, Range } from "vscode-languageclient"; +import { Server } from '../server'; +import { handle as applySourceChange, SourceChange } from './apply_source_change'; + +interface JoinLinesParams { + textDocument: TextDocumentIdentifier; + range: Range; +} + +export async function handle() { + let editor = vscode.window.activeTextEditor + if (editor == null || editor.document.languageId != "rust") return + let request: JoinLinesParams = { + textDocument: { uri: editor.document.uri.toString() }, + range: Server.client.code2ProtocolConverter.asRange(editor.selection), + } + let change = await Server.client.sendRequest("m/joinLines", request) + await applySourceChange(change) +} diff --git a/editors/code/src/commands/matching_brace.ts b/editors/code/src/commands/matching_brace.ts new file mode 100644 index 00000000000..572c15ce830 --- /dev/null +++ b/editors/code/src/commands/matching_brace.ts @@ -0,0 +1,27 @@ +import * as vscode from 'vscode'; + +import { TextDocumentIdentifier, Position } from "vscode-languageclient"; +import { Server } from '../server'; + +interface FindMatchingBraceParams { + textDocument: TextDocumentIdentifier; + offsets: Position[]; +} + +export async function handle() { + let editor = vscode.window.activeTextEditor + if (editor == null || editor.document.languageId != "rust") return + let request: FindMatchingBraceParams = { + textDocument: { uri: editor.document.uri.toString() }, + offsets: editor.selections.map((s) => { + return Server.client.code2ProtocolConverter.asPosition(s.active) + }) + } + let response = await Server.client.sendRequest("m/findMatchingBrace", request) + editor.selections = editor.selections.map((sel, idx) => { + let active = Server.client.protocol2CodeConverter.asPosition(response[idx]) + let anchor = sel.isEmpty ? active : sel.anchor + return new vscode.Selection(anchor, active) + }) + editor.revealRange(editor.selection) +}; diff --git a/editors/code/src/commands/parent_module.ts b/editors/code/src/commands/parent_module.ts new file mode 100644 index 00000000000..dae60bfb4a3 --- /dev/null +++ b/editors/code/src/commands/parent_module.ts @@ -0,0 +1,22 @@ +import * as vscode from 'vscode'; + +import { TextDocumentIdentifier, Location } from "vscode-languageclient"; +import { Server } from '../server'; + +export async function handle() { + let editor = vscode.window.activeTextEditor + if (editor == null || editor.document.languageId != "rust") return + let request: TextDocumentIdentifier = { + uri: editor.document.uri.toString() + } + let response = await Server.client.sendRequest("m/parentModule", request) + let loc = response[0] + if (loc == null) return + let uri = Server.client.protocol2CodeConverter.asUri(loc.uri) + let range = Server.client.protocol2CodeConverter.asRange(loc.range) + + let doc = await vscode.workspace.openTextDocument(uri) + let e = await vscode.window.showTextDocument(doc) + e.selection = new vscode.Selection(range.start, range.start) + e.revealRange(range, vscode.TextEditorRevealType.InCenter) +} diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/commands/runnables.ts new file mode 100644 index 00000000000..45c16497dbe --- /dev/null +++ b/editors/code/src/commands/runnables.ts @@ -0,0 +1,88 @@ +import * as vscode from 'vscode'; +import * as lc from 'vscode-languageclient' +import { Server } from '../server'; + +interface RunnablesParams { + textDocument: lc.TextDocumentIdentifier, + position?: lc.Position, +} + +interface Runnable { + range: lc.Range; + label: string; + bin: string; + args: string[]; + env: { [index: string]: string }, +} + +class RunnableQuickPick implements vscode.QuickPickItem { + label: string; + description?: string | undefined; + detail?: string | undefined; + picked?: boolean | undefined; + + constructor(public runnable: Runnable) { + this.label = runnable.label + } +} + +interface CargoTaskDefinition extends vscode.TaskDefinition { + type: 'cargo'; + label: string; + command: string; + args: Array; + env?: { [key: string]: string }; +} + +function createTask(spec: Runnable): vscode.Task { + const TASK_SOURCE = 'Rust'; + let definition: CargoTaskDefinition = { + type: 'cargo', + label: 'cargo', + command: spec.bin, + args: spec.args, + env: spec.env + } + + let execCmd = `${definition.command} ${definition.args.join(' ')}`; + let execOption: vscode.ShellExecutionOptions = { + cwd: '.', + env: definition.env, + }; + let exec = new vscode.ShellExecution(`clear; ${execCmd}`, execOption); + + let f = vscode.workspace.workspaceFolders![0] + let t = new vscode.Task(definition, f, definition.label, TASK_SOURCE, exec, ['$rustc']); + return t; +} + +let prevRunnable: RunnableQuickPick | undefined = undefined +export async function handle() { + let editor = vscode.window.activeTextEditor + if (editor == null || editor.document.languageId != "rust") return + let textDocument: lc.TextDocumentIdentifier = { + uri: editor.document.uri.toString() + } + let params: RunnablesParams = { + textDocument, + position: Server.client.code2ProtocolConverter.asPosition(editor.selection.active) + } + let runnables = await Server.client.sendRequest('m/runnables', params) + let items: RunnableQuickPick[] = [] + if (prevRunnable) { + items.push(prevRunnable) + } + for (let r of runnables) { + if (prevRunnable && JSON.stringify(prevRunnable.runnable) == JSON.stringify(r)) { + continue + } + items.push(new RunnableQuickPick(r)) + } + let item = await vscode.window.showQuickPick(items) + if (item) { + item.detail = "rerun" + prevRunnable = item + let task = createTask(item.runnable) + return await vscode.tasks.executeTask(task) + } +} diff --git a/editors/code/src/commands/syntaxTree.ts b/editors/code/src/commands/syntaxTree.ts new file mode 100644 index 00000000000..d5daa9302c6 --- /dev/null +++ b/editors/code/src/commands/syntaxTree.ts @@ -0,0 +1,38 @@ +import * as vscode from 'vscode'; +import { TextDocumentIdentifier } from 'vscode-languageclient'; + +import { Server } from '../server'; + +export const syntaxTreeUri = vscode.Uri.parse('ra-lsp://syntaxtree'); + +export class TextDocumentContentProvider implements vscode.TextDocumentContentProvider { + public eventEmitter = new vscode.EventEmitter() + public syntaxTree: string = "Not available" + + public provideTextDocumentContent(uri: vscode.Uri): vscode.ProviderResult { + let editor = vscode.window.activeTextEditor; + if (editor == null) return "" + let request: SyntaxTreeParams = { + textDocument: { uri: editor.document.uri.toString() } + }; + return Server.client.sendRequest("m/syntaxTree", request); + } + + get onDidChange(): vscode.Event { + return this.eventEmitter.event + } +} + +interface SyntaxTreeParams { + textDocument: TextDocumentIdentifier; +} + +type SyntaxTreeResult = string; + +// Opens the virtual file that will show the syntax tree +// +// The contents of the file come from the `TextDocumentContentProvider` +export async function handle() { + let document = await vscode.workspace.openTextDocument(syntaxTreeUri) + return vscode.window.showTextDocument(document, vscode.ViewColumn.Two, true) +} diff --git a/editors/code/src/events.ts b/editors/code/src/events.ts new file mode 100644 index 00000000000..b143bb2565c --- /dev/null +++ b/editors/code/src/events.ts @@ -0,0 +1,7 @@ +import * as changeActiveTextEditor from './events/change_active_text_editor' +import * as changeTextDocument from './events/change_text_document'; + +export { + changeActiveTextEditor, + changeTextDocument +} \ No newline at end of file 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 00000000000..bbdd5309869 --- /dev/null +++ b/editors/code/src/events/change_active_text_editor.ts @@ -0,0 +1,14 @@ +import { TextEditor } from "vscode"; +import { TextDocumentIdentifier } from "vscode-languageclient"; + +import { Server } from "../server"; +import { Decoration } from "../highlighting"; + +export async function handle(editor: TextEditor | undefined) { + if (!Server.config.highlightingOn || !editor || editor.document.languageId != 'rust') return + let params: TextDocumentIdentifier = { + uri: editor.document.uri.toString() + } + let decorations = await Server.client.sendRequest("m/decorationsRequest", params) + Server.highlighter.setHighlights(editor, decorations) +} \ No newline at end of file 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 00000000000..83ee6c9ee99 --- /dev/null +++ b/editors/code/src/events/change_text_document.ts @@ -0,0 +1,19 @@ +import * as vscode from 'vscode'; + +import { syntaxTreeUri, TextDocumentContentProvider } from '../commands/syntaxTree'; + +export function createHandler(textDocumentContentProvider: TextDocumentContentProvider) { + return (event: vscode.TextDocumentChangeEvent) => { + let doc = event.document + if (doc.languageId != "rust") return + afterLs(() => { + textDocumentContentProvider.eventEmitter.fire(syntaxTreeUri); + }) + } +} + +// We need to order this after LS updates, but there's no API for that. +// Hence, good old setTimeout. +function afterLs(f: () => any) { + setTimeout(f, 10) +} diff --git a/editors/code/src/extension.ts b/editors/code/src/extension.ts index fde6a480d85..595fb98fe3f 100644 --- a/editors/code/src/extension.ts +++ b/editors/code/src/extension.ts @@ -1,434 +1,45 @@ -'use strict'; import * as vscode from 'vscode'; -import * as lc from 'vscode-languageclient' -let client: lc.LanguageClient; - -let uris = { - syntaxTree: vscode.Uri.parse('ra-lsp://syntaxtree') -} - -let highlightingOn = true; +import * as commands from './commands' +import * as events from './events' +import { Server } from './server'; +import { TextDocumentContentProvider } from './commands/syntaxTree'; export function activate(context: vscode.ExtensionContext) { - let applyHighlightingOn = () => { - let config = vscode.workspace.getConfiguration('ra-lsp'); - if (config.has('highlightingOn')) { - highlightingOn = config.get('highlightingOn') as boolean; - }; - - if (!highlightingOn) { - removeHighlights(); - } - }; - - // Apply the highlightingOn config now and whenever the config changes - applyHighlightingOn(); - vscode.workspace.onDidChangeConfiguration(_ => { - applyHighlightingOn(); - }); - - let textDocumentContentProvider = new TextDocumentContentProvider() - let dispose = (disposable: vscode.Disposable) => { + function disposeOnDeactivation(disposable: vscode.Disposable) { context.subscriptions.push(disposable); } - let registerCommand = (name: string, f: any) => { - dispose(vscode.commands.registerCommand(name, f)) + + function registerCommand(name: string, f: any) { + disposeOnDeactivation(vscode.commands.registerCommand(name, f)) } - registerCommand('ra-lsp.syntaxTree', () => openDoc(uris.syntaxTree)) - registerCommand('ra-lsp.extendSelection', async () => { - let editor = vscode.window.activeTextEditor - if (editor == null || editor.document.languageId != "rust") return - let request: ExtendSelectionParams = { - textDocument: { uri: editor.document.uri.toString() }, - selections: editor.selections.map((s) => { - return client.code2ProtocolConverter.asRange(s) - }) - } - let response = await client.sendRequest("m/extendSelection", request) - editor.selections = response.selections.map((range) => { - let r = client.protocol2CodeConverter.asRange(range) - return new vscode.Selection(r.start, r.end) - }) - }) - registerCommand('ra-lsp.matchingBrace', async () => { - let editor = vscode.window.activeTextEditor - if (editor == null || editor.document.languageId != "rust") return - let request: FindMatchingBraceParams = { - textDocument: { uri: editor.document.uri.toString() }, - offsets: editor.selections.map((s) => { - return client.code2ProtocolConverter.asPosition(s.active) - }) - } - let response = await client.sendRequest("m/findMatchingBrace", request) - editor.selections = editor.selections.map((sel, idx) => { - let active = client.protocol2CodeConverter.asPosition(response[idx]) - let anchor = sel.isEmpty ? active : sel.anchor - return new vscode.Selection(anchor, active) - }) - editor.revealRange(editor.selection) - }) - registerCommand('ra-lsp.joinLines', async () => { - let editor = vscode.window.activeTextEditor - if (editor == null || editor.document.languageId != "rust") return - let request: JoinLinesParams = { - textDocument: { uri: editor.document.uri.toString() }, - range: client.code2ProtocolConverter.asRange(editor.selection), - } - let change = await client.sendRequest("m/joinLines", request) - await applySourceChange(change) - }) - registerCommand('ra-lsp.parentModule', async () => { - let editor = vscode.window.activeTextEditor - if (editor == null || editor.document.languageId != "rust") return - let request: lc.TextDocumentIdentifier = { - uri: editor.document.uri.toString() - } - let response = await client.sendRequest("m/parentModule", request) - let loc = response[0] - if (loc == null) return - let uri = client.protocol2CodeConverter.asUri(loc.uri) - let range = client.protocol2CodeConverter.asRange(loc.range) + registerCommand('ra-lsp.syntaxTree', commands.syntaxTree.handle) + registerCommand('ra-lsp.extendSelection', commands.extendSelection.handle); + registerCommand('ra-lsp.matchingBrace', commands.matchingBrace.handle); + registerCommand('ra-lsp.joinLines', commands.joinLines.handle); + registerCommand('ra-lsp.parentModule', commands.parentModule.handle); + registerCommand('ra-lsp.run', commands.runnables.handle); + registerCommand('ra-lsp.applySourceChange', commands.applySourceChange.handle); - let doc = await vscode.workspace.openTextDocument(uri) - let e = await vscode.window.showTextDocument(doc) - e.selection = new vscode.Selection(range.start, range.start) - e.revealRange(range, vscode.TextEditorRevealType.InCenter) - }) - - let prevRunnable: RunnableQuickPick | undefined = undefined - registerCommand('ra-lsp.run', async () => { - let editor = vscode.window.activeTextEditor - if (editor == null || editor.document.languageId != "rust") return - let textDocument: lc.TextDocumentIdentifier = { - uri: editor.document.uri.toString() - } - let params: RunnablesParams = { - textDocument, - position: client.code2ProtocolConverter.asPosition(editor.selection.active) - } - let runnables = await client.sendRequest('m/runnables', params) - let items: RunnableQuickPick[] = [] - if (prevRunnable) { - items.push(prevRunnable) - } - for (let r of runnables) { - if (prevRunnable && JSON.stringify(prevRunnable.runnable) == JSON.stringify(r)) { - continue - } - items.push(new RunnableQuickPick(r)) - } - let item = await vscode.window.showQuickPick(items) - if (item) { - item.detail = "rerun" - prevRunnable = item - let task = createTask(item.runnable) - return await vscode.tasks.executeTask(task) - } - }) - registerCommand('ra-lsp.applySourceChange', applySourceChange) - - dispose(vscode.workspace.registerTextDocumentContentProvider( + let textDocumentContentProvider = new TextDocumentContentProvider() + disposeOnDeactivation(vscode.workspace.registerTextDocumentContentProvider( 'ra-lsp', textDocumentContentProvider )) - startServer() - vscode.workspace.onDidChangeTextDocument((event: vscode.TextDocumentChangeEvent) => { - let doc = event.document - if (doc.languageId != "rust") return - afterLs(() => { - textDocumentContentProvider.eventEmitter.fire(uris.syntaxTree) - }) - }, null, context.subscriptions) - vscode.window.onDidChangeActiveTextEditor(async (editor) => { - if (!highlightingOn || !editor || editor.document.languageId != 'rust') return - let params: lc.TextDocumentIdentifier = { - uri: editor.document.uri.toString() - } - let decorations = await client.sendRequest("m/decorationsRequest", params) - setHighlights(editor, decorations) - }) -} -// We need to order this after LS updates, but there's no API for that. -// Hence, good old setTimeout. -function afterLs(f: () => any) { - setTimeout(f, 10) + Server.start() + + vscode.workspace.onDidChangeTextDocument( + events.changeTextDocument.createHandler(textDocumentContentProvider), + null, + context.subscriptions) + vscode.window.onDidChangeActiveTextEditor(events.changeActiveTextEditor.handle) } export function deactivate(): Thenable { - if (!client) { + if (!Server.client) { return Promise.resolve(); } - return client.stop(); -} - -function startServer() { - let run: lc.Executable = { - command: "ra_lsp_server", - options: { cwd: "." } - } - let serverOptions: lc.ServerOptions = { - run, - debug: run - }; - - let clientOptions: lc.LanguageClientOptions = { - documentSelector: [{ scheme: 'file', language: 'rust' }], - }; - - client = new lc.LanguageClient( - 'ra-lsp', - 'rust-analyzer languge server', - serverOptions, - clientOptions, - ); - client.onReady().then(() => { - client.onNotification( - "m/publishDecorations", - (params: PublishDecorationsParams) => { - let editor = vscode.window.visibleTextEditors.find( - (editor) => editor.document.uri.toString() == params.uri - ) - if (!highlightingOn || !editor) return; - setHighlights( - editor, - params.decorations, - ) - } - ) - }) - client.start(); -} - -async function openDoc(uri: vscode.Uri) { - let document = await vscode.workspace.openTextDocument(uri) - return vscode.window.showTextDocument(document, vscode.ViewColumn.Two, true) -} - -class TextDocumentContentProvider implements vscode.TextDocumentContentProvider { - public eventEmitter = new vscode.EventEmitter() - public syntaxTree: string = "Not available" - - public provideTextDocumentContent(uri: vscode.Uri): vscode.ProviderResult { - let editor = vscode.window.activeTextEditor; - if (editor == null) return "" - let request: SyntaxTreeParams = { - textDocument: { uri: editor.document.uri.toString() } - }; - return client.sendRequest("m/syntaxTree", request); - } - - get onDidChange(): vscode.Event { - return this.eventEmitter.event - } -} - -let decorations: { [index: string]: vscode.TextEditorDecorationType } = {}; - -function initDecorations() { - const decor = (obj: any) => vscode.window.createTextEditorDecorationType({ color: obj }) - decorations = { - background: decor("#3F3F3F"), - error: vscode.window.createTextEditorDecorationType({ - borderColor: "red", - borderStyle: "none none dashed none", - }), - comment: decor("#7F9F7F"), - string: decor("#CC9393"), - keyword: decor("#F0DFAF"), - function: decor("#93E0E3"), - parameter: decor("#94BFF3"), - builtin: decor("#DD6718"), - text: decor("#DCDCCC"), - attribute: decor("#BFEBBF"), - literal: decor("#DFAF8F"), - } -} - -function removeHighlights() { - for (let tag in decorations) { - decorations[tag].dispose(); - } - - decorations = {}; -} - -function setHighlights( - editor: vscode.TextEditor, - highlights: Array -) { - // Initialize decorations if necessary - // - // Note: decoration objects need to be kept around so we can dispose them - // if the user disables syntax highlighting - if (Object.keys(decorations).length === 0) { - initDecorations(); - } - - let byTag: Map = new Map() - for (let tag in decorations) { - byTag.set(tag, []) - } - - for (let d of highlights) { - if (!byTag.get(d.tag)) { - console.log(`unknown tag ${d.tag}`) - continue - } - byTag.get(d.tag)!.push( - client.protocol2CodeConverter.asRange(d.range) - ) - } - - for (let tag of byTag.keys()) { - let dec: vscode.TextEditorDecorationType = decorations[tag] - let ranges = byTag.get(tag)! - editor.setDecorations(dec, ranges) - } -} - -interface SyntaxTreeParams { - textDocument: lc.TextDocumentIdentifier; -} - -type SyntaxTreeResult = string - -interface ExtendSelectionParams { - textDocument: lc.TextDocumentIdentifier; - selections: lc.Range[]; -} - -interface ExtendSelectionResult { - selections: lc.Range[]; -} - -interface FindMatchingBraceParams { - textDocument: lc.TextDocumentIdentifier; - offsets: lc.Position[]; -} - -interface JoinLinesParams { - textDocument: lc.TextDocumentIdentifier; - range: lc.Range; -} - -interface PublishDecorationsParams { - uri: string, - decorations: Decoration[], -} - -interface RunnablesParams { - textDocument: lc.TextDocumentIdentifier, - position?: lc.Position, -} - -interface Runnable { - range: lc.Range; - label: string; - bin: string; - args: string[]; - env: { [index: string]: string }, -} - -class RunnableQuickPick implements vscode.QuickPickItem { - label: string; - description?: string | undefined; - detail?: string | undefined; - picked?: boolean | undefined; - - constructor(public runnable: Runnable) { - this.label = runnable.label - } -} - -interface Decoration { - range: lc.Range, - tag: string, -} - - -interface CargoTaskDefinition extends vscode.TaskDefinition { - type: 'cargo'; - label: string; - command: string; - args: Array; - env?: { [key: string]: string }; -} - -function createTask(spec: Runnable): vscode.Task { - const TASK_SOURCE = 'Rust'; - let definition: CargoTaskDefinition = { - type: 'cargo', - label: 'cargo', - command: spec.bin, - args: spec.args, - env: spec.env - } - - let execCmd = `${definition.command} ${definition.args.join(' ')}`; - let execOption: vscode.ShellExecutionOptions = { - cwd: '.', - env: definition.env, - }; - let exec = new vscode.ShellExecution(`clear; ${execCmd}`, execOption); - - let f = vscode.workspace.workspaceFolders![0] - let t = new vscode.Task(definition, f, definition.label, TASK_SOURCE, exec, ['$rustc']); - return t; -} - -interface FileSystemEdit { - type: string; - uri?: string; - src?: string; - dst?: string; -} - -interface SourceChange { - label: string, - sourceFileEdits: lc.TextDocumentEdit[], - fileSystemEdits: FileSystemEdit[], - cursorPosition?: lc.TextDocumentPositionParams, -} - -async function applySourceChange(change: SourceChange) { - console.log(`applySOurceChange ${JSON.stringify(change)}`) - let wsEdit = new vscode.WorkspaceEdit() - for (let sourceEdit of change.sourceFileEdits) { - let uri = client.protocol2CodeConverter.asUri(sourceEdit.textDocument.uri) - let edits = client.protocol2CodeConverter.asTextEdits(sourceEdit.edits) - wsEdit.set(uri, edits) - } - let created; - let moved; - for (let fsEdit of change.fileSystemEdits) { - if (fsEdit.type == "createFile") { - let uri = vscode.Uri.parse(fsEdit.uri!) - wsEdit.createFile(uri) - created = uri - } else if (fsEdit.type == "moveFile") { - let src = vscode.Uri.parse(fsEdit.src!) - let dst = vscode.Uri.parse(fsEdit.dst!) - wsEdit.renameFile(src, dst) - moved = dst - } else { - console.error(`unknown op: ${JSON.stringify(fsEdit)}`) - } - } - let toOpen = created || moved - let toReveal = change.cursorPosition - await vscode.workspace.applyEdit(wsEdit) - if (toOpen) { - let doc = await vscode.workspace.openTextDocument(toOpen) - await vscode.window.showTextDocument(doc) - } else if (toReveal) { - let uri = client.protocol2CodeConverter.asUri(toReveal.textDocument.uri) - let position = client.protocol2CodeConverter.asPosition(toReveal.position) - let editor = vscode.window.activeTextEditor; - if (!editor || editor.document.uri.toString() != uri.toString()) return - if (!editor.selection.isEmpty) return - editor!.selection = new vscode.Selection(position, position) - } + return Server.client.stop(); } diff --git a/editors/code/src/highlighting.ts b/editors/code/src/highlighting.ts new file mode 100644 index 00000000000..169ddb0df08 --- /dev/null +++ b/editors/code/src/highlighting.ts @@ -0,0 +1,78 @@ +import * as vscode from 'vscode'; +import * as lc from 'vscode-languageclient' + +import { Server } from './server'; + +export interface Decoration { + range: lc.Range, + tag: string, +} + +export class Highlighter { + private decorations: { [index: string]: vscode.TextEditorDecorationType }; + constructor() { + this.decorations = {}; + } + + removeHighlights() { + for (let tag in this.decorations) { + this.decorations[tag].dispose(); + } + + this.decorations = {}; + } + + setHighlights( + editor: vscode.TextEditor, + highlights: Array + ) { + // Initialize decorations if necessary + // + // Note: decoration objects need to be kept around so we can dispose them + // if the user disables syntax highlighting + if (Object.keys(this.decorations).length === 0) { + this.initDecorations(); + } + + let byTag: Map = new Map() + for (let tag in this.decorations) { + byTag.set(tag, []) + } + + for (let d of highlights) { + if (!byTag.get(d.tag)) { + console.log(`unknown tag ${d.tag}`) + continue + } + byTag.get(d.tag)!.push( + Server.client.protocol2CodeConverter.asRange(d.range) + ) + } + + for (let tag of byTag.keys()) { + let dec: vscode.TextEditorDecorationType = this.decorations[tag] + let ranges = byTag.get(tag)! + editor.setDecorations(dec, ranges) + } + } + + private initDecorations() { + const decor = (obj: any) => vscode.window.createTextEditorDecorationType({ color: obj }) + this.decorations = { + background: decor("#3F3F3F"), + error: vscode.window.createTextEditorDecorationType({ + borderColor: "red", + borderStyle: "none none dashed none", + }), + comment: decor("#7F9F7F"), + string: decor("#CC9393"), + keyword: decor("#F0DFAF"), + function: decor("#93E0E3"), + parameter: decor("#94BFF3"), + builtin: decor("#DD6718"), + text: decor("#DCDCCC"), + attribute: decor("#BFEBBF"), + literal: decor("#DFAF8F"), + } + } +} diff --git a/editors/code/src/server.ts b/editors/code/src/server.ts new file mode 100644 index 00000000000..c1c95e00842 --- /dev/null +++ b/editors/code/src/server.ts @@ -0,0 +1,74 @@ +import * as vscode from 'vscode'; +import * as lc from 'vscode-languageclient' + +import { Highlighter, Decoration } from './highlighting'; + +export class Config { + highlightingOn = true; + + constructor() { + vscode.workspace.onDidChangeConfiguration(_ => this.userConfigChanged()); + this.userConfigChanged(); + } + + userConfigChanged() { + let config = vscode.workspace.getConfiguration('ra-lsp'); + if (config.has('highlightingOn')) { + this.highlightingOn = config.get('highlightingOn') as boolean; + }; + + if (!this.highlightingOn) { + Server.highlighter.removeHighlights(); + } + } +} + +export class Server { + static highlighter = new Highlighter(); + static config = new Config(); + static client: lc.LanguageClient; + + + static start() { + let run: lc.Executable = { + command: "ra_lsp_server", + options: { cwd: "." } + } + let serverOptions: lc.ServerOptions = { + run, + debug: run + }; + + let clientOptions: lc.LanguageClientOptions = { + documentSelector: [{ scheme: 'file', language: 'rust' }], + }; + + Server.client = new lc.LanguageClient( + 'ra-lsp', + 'rust-analyzer languge server', + serverOptions, + clientOptions, + ); + Server.client.onReady().then(() => { + Server.client.onNotification( + "m/publishDecorations", + (params: PublishDecorationsParams) => { + let editor = vscode.window.visibleTextEditors.find( + (editor) => editor.document.uri.toString() == params.uri + ) + if (!Server.config.highlightingOn || !editor) return; + Server.highlighter.setHighlights( + editor, + params.decorations, + ) + } + ) + }) + Server.client.start(); + } +} + +interface PublishDecorationsParams { + uri: string, + decorations: Decoration[], +} From 4d62cfccbb8281f33b6f894df07e7316a9d45bfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adolfo=20Ochagav=C3=ADa?= Date: Sun, 7 Oct 2018 22:59:02 +0200 Subject: [PATCH 2/4] Apply tslint suggestions, round one --- editors/code/src/commands.ts | 4 +- .../code/src/commands/apply_source_change.ts | 66 ++++++++-------- editors/code/src/commands/extend_selection.ts | 24 +++--- editors/code/src/commands/join_lines.ts | 16 ++-- editors/code/src/commands/matching_brace.ts | 28 +++---- editors/code/src/commands/parent_module.ts | 30 ++++---- editors/code/src/commands/runnables.ts | 76 +++++++++---------- editors/code/src/commands/syntaxTree.ts | 20 ++--- editors/code/src/config.ts | 23 ++++++ editors/code/src/events.ts | 6 +- .../src/events/change_active_text_editor.ts | 22 +++--- .../code/src/events/change_text_document.ts | 10 +-- editors/code/src/extension.ts | 22 +++--- editors/code/src/highlighting.ts | 66 ++++++++-------- editors/code/src/server.ts | 72 +++++++----------- editors/code/tslint.json | 13 ++++ 16 files changed, 257 insertions(+), 241 deletions(-) create mode 100644 editors/code/src/config.ts create mode 100644 editors/code/tslint.json diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index 99cac3379e4..c7e27781e06 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts @@ -13,5 +13,5 @@ export { matchingBrace, parentModule, runnables, - syntaxTree -} + syntaxTree, +}; diff --git a/editors/code/src/commands/apply_source_change.ts b/editors/code/src/commands/apply_source_change.ts index dcbbb2b0984..f011cbe12e9 100644 --- a/editors/code/src/commands/apply_source_change.ts +++ b/editors/code/src/commands/apply_source_change.ts @@ -1,5 +1,5 @@ import * as vscode from 'vscode'; -import * as lc from 'vscode-languageclient' +import * as lc from 'vscode-languageclient'; import { Server } from '../server'; @@ -11,48 +11,48 @@ interface FileSystemEdit { } export interface SourceChange { - label: string, - sourceFileEdits: lc.TextDocumentEdit[], - fileSystemEdits: FileSystemEdit[], - cursorPosition?: lc.TextDocumentPositionParams, + label: string; + sourceFileEdits: lc.TextDocumentEdit[]; + fileSystemEdits: FileSystemEdit[]; + cursorPosition?: lc.TextDocumentPositionParams; } export async function handle(change: SourceChange) { - console.log(`applySOurceChange ${JSON.stringify(change)}`) - let wsEdit = new vscode.WorkspaceEdit() - for (let sourceEdit of change.sourceFileEdits) { - let uri = Server.client.protocol2CodeConverter.asUri(sourceEdit.textDocument.uri) - let edits = Server.client.protocol2CodeConverter.asTextEdits(sourceEdit.edits) - wsEdit.set(uri, edits) + console.log(`applySOurceChange ${JSON.stringify(change)}`); + const wsEdit = new vscode.WorkspaceEdit(); + for (const sourceEdit of change.sourceFileEdits) { + const uri = Server.client.protocol2CodeConverter.asUri(sourceEdit.textDocument.uri); + const edits = Server.client.protocol2CodeConverter.asTextEdits(sourceEdit.edits); + wsEdit.set(uri, edits); } let created; let moved; - for (let fsEdit of change.fileSystemEdits) { - if (fsEdit.type == "createFile") { - let uri = vscode.Uri.parse(fsEdit.uri!) - wsEdit.createFile(uri) - created = uri - } else if (fsEdit.type == "moveFile") { - let src = vscode.Uri.parse(fsEdit.src!) - let dst = vscode.Uri.parse(fsEdit.dst!) - wsEdit.renameFile(src, dst) - moved = dst + for (const fsEdit of change.fileSystemEdits) { + if (fsEdit.type == 'createFile') { + const uri = vscode.Uri.parse(fsEdit.uri!); + wsEdit.createFile(uri); + created = uri; + } else if (fsEdit.type == 'moveFile') { + const src = vscode.Uri.parse(fsEdit.src!); + const dst = vscode.Uri.parse(fsEdit.dst!); + wsEdit.renameFile(src, dst); + moved = dst; } else { - console.error(`unknown op: ${JSON.stringify(fsEdit)}`) + console.error(`unknown op: ${JSON.stringify(fsEdit)}`); } } - let toOpen = created || moved - let toReveal = change.cursorPosition - await vscode.workspace.applyEdit(wsEdit) + const toOpen = created || moved; + const toReveal = change.cursorPosition; + await vscode.workspace.applyEdit(wsEdit); if (toOpen) { - let doc = await vscode.workspace.openTextDocument(toOpen) - await vscode.window.showTextDocument(doc) + const doc = await vscode.workspace.openTextDocument(toOpen); + await vscode.window.showTextDocument(doc); } else if (toReveal) { - let uri = Server.client.protocol2CodeConverter.asUri(toReveal.textDocument.uri) - let position = Server.client.protocol2CodeConverter.asPosition(toReveal.position) - let editor = vscode.window.activeTextEditor; - if (!editor || editor.document.uri.toString() != uri.toString()) return - if (!editor.selection.isEmpty) return - editor!.selection = new vscode.Selection(position, position) + const uri = Server.client.protocol2CodeConverter.asUri(toReveal.textDocument.uri); + const position = Server.client.protocol2CodeConverter.asPosition(toReveal.position); + const editor = vscode.window.activeTextEditor; + if (!editor || editor.document.uri.toString() != uri.toString()) { return; } + if (!editor.selection.isEmpty) { return; } + editor!.selection = new vscode.Selection(position, position); } } diff --git a/editors/code/src/commands/extend_selection.ts b/editors/code/src/commands/extend_selection.ts index b90828ba9ba..b722ac17229 100644 --- a/editors/code/src/commands/extend_selection.ts +++ b/editors/code/src/commands/extend_selection.ts @@ -1,6 +1,6 @@ import * as vscode from 'vscode'; -import { TextDocumentIdentifier, Range } from "vscode-languageclient"; +import { Range, TextDocumentIdentifier } from 'vscode-languageclient'; import { Server } from '../server'; interface ExtendSelectionParams { @@ -13,17 +13,17 @@ interface ExtendSelectionResult { } export async function handle() { - let editor = vscode.window.activeTextEditor - if (editor == null || editor.document.languageId != "rust") return - let request: ExtendSelectionParams = { - textDocument: { uri: editor.document.uri.toString() }, + const editor = vscode.window.activeTextEditor; + if (editor == null || editor.document.languageId != 'rust') { return; } + const request: ExtendSelectionParams = { selections: editor.selections.map((s) => { - return Server.client.code2ProtocolConverter.asRange(s) - }) - } - let response = await Server.client.sendRequest("m/extendSelection", request) + return Server.client.code2ProtocolConverter.asRange(s); + }), + textDocument: { uri: editor.document.uri.toString() }, + }; + const response = await Server.client.sendRequest('m/extendSelection', request); editor.selections = response.selections.map((range: Range) => { - let r = Server.client.protocol2CodeConverter.asRange(range) - return new vscode.Selection(r.start, r.end) - }) + const r = Server.client.protocol2CodeConverter.asRange(range); + return new vscode.Selection(r.start, r.end); + }); } diff --git a/editors/code/src/commands/join_lines.ts b/editors/code/src/commands/join_lines.ts index 7ae7b9d7601..80ad4460ba1 100644 --- a/editors/code/src/commands/join_lines.ts +++ b/editors/code/src/commands/join_lines.ts @@ -1,6 +1,6 @@ import * as vscode from 'vscode'; -import { TextDocumentIdentifier, Range } from "vscode-languageclient"; +import { Range, TextDocumentIdentifier } from 'vscode-languageclient'; import { Server } from '../server'; import { handle as applySourceChange, SourceChange } from './apply_source_change'; @@ -10,12 +10,12 @@ interface JoinLinesParams { } export async function handle() { - let editor = vscode.window.activeTextEditor - if (editor == null || editor.document.languageId != "rust") return - let request: JoinLinesParams = { - textDocument: { uri: editor.document.uri.toString() }, + const editor = vscode.window.activeTextEditor; + if (editor == null || editor.document.languageId != 'rust') { return; } + const request: JoinLinesParams = { range: Server.client.code2ProtocolConverter.asRange(editor.selection), - } - let change = await Server.client.sendRequest("m/joinLines", request) - await applySourceChange(change) + textDocument: { uri: editor.document.uri.toString() }, + }; + const change = await Server.client.sendRequest('m/joinLines', request); + await applySourceChange(change); } diff --git a/editors/code/src/commands/matching_brace.ts b/editors/code/src/commands/matching_brace.ts index 572c15ce830..cf7f6bf8fac 100644 --- a/editors/code/src/commands/matching_brace.ts +++ b/editors/code/src/commands/matching_brace.ts @@ -1,6 +1,6 @@ import * as vscode from 'vscode'; -import { TextDocumentIdentifier, Position } from "vscode-languageclient"; +import { Position, TextDocumentIdentifier } from 'vscode-languageclient'; import { Server } from '../server'; interface FindMatchingBraceParams { @@ -9,19 +9,19 @@ interface FindMatchingBraceParams { } export async function handle() { - let editor = vscode.window.activeTextEditor - if (editor == null || editor.document.languageId != "rust") return - let request: FindMatchingBraceParams = { + const editor = vscode.window.activeTextEditor; + if (editor == null || editor.document.languageId != 'rust') { return; } + const request: FindMatchingBraceParams = { textDocument: { uri: editor.document.uri.toString() }, offsets: editor.selections.map((s) => { - return Server.client.code2ProtocolConverter.asPosition(s.active) - }) - } - let response = await Server.client.sendRequest("m/findMatchingBrace", request) + return Server.client.code2ProtocolConverter.asPosition(s.active); + }), + }; + const response = await Server.client.sendRequest('m/findMatchingBrace', request); editor.selections = editor.selections.map((sel, idx) => { - let active = Server.client.protocol2CodeConverter.asPosition(response[idx]) - let anchor = sel.isEmpty ? active : sel.anchor - return new vscode.Selection(anchor, active) - }) - editor.revealRange(editor.selection) -}; + const active = Server.client.protocol2CodeConverter.asPosition(response[idx]); + const anchor = sel.isEmpty ? active : sel.anchor; + return new vscode.Selection(anchor, active); + }); + editor.revealRange(editor.selection); +} diff --git a/editors/code/src/commands/parent_module.ts b/editors/code/src/commands/parent_module.ts index dae60bfb4a3..7d413c27a65 100644 --- a/editors/code/src/commands/parent_module.ts +++ b/editors/code/src/commands/parent_module.ts @@ -1,22 +1,22 @@ import * as vscode from 'vscode'; -import { TextDocumentIdentifier, Location } from "vscode-languageclient"; +import { Location, TextDocumentIdentifier } from 'vscode-languageclient'; import { Server } from '../server'; export async function handle() { - let editor = vscode.window.activeTextEditor - if (editor == null || editor.document.languageId != "rust") return - let request: TextDocumentIdentifier = { - uri: editor.document.uri.toString() - } - let response = await Server.client.sendRequest("m/parentModule", request) - let loc = response[0] - if (loc == null) return - let uri = Server.client.protocol2CodeConverter.asUri(loc.uri) - let range = Server.client.protocol2CodeConverter.asRange(loc.range) + const editor = vscode.window.activeTextEditor; + if (editor == null || editor.document.languageId != 'rust') { return; } + const request: TextDocumentIdentifier = { + uri: editor.document.uri.toString(), + }; + const response = await Server.client.sendRequest('m/parentModule', request); + const loc = response[0]; + if (loc == null) { return; } + const uri = Server.client.protocol2CodeConverter.asUri(loc.uri); + const range = Server.client.protocol2CodeConverter.asRange(loc.range); - let doc = await vscode.workspace.openTextDocument(uri) - let e = await vscode.window.showTextDocument(doc) - e.selection = new vscode.Selection(range.start, range.start) - e.revealRange(range, vscode.TextEditorRevealType.InCenter) + const doc = await vscode.workspace.openTextDocument(uri); + const e = await vscode.window.showTextDocument(doc); + e.selection = new vscode.Selection(range.start, range.start); + e.revealRange(range, vscode.TextEditorRevealType.InCenter); } diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/commands/runnables.ts index 45c16497dbe..37db6ea10ac 100644 --- a/editors/code/src/commands/runnables.ts +++ b/editors/code/src/commands/runnables.ts @@ -1,10 +1,10 @@ import * as vscode from 'vscode'; -import * as lc from 'vscode-languageclient' +import * as lc from 'vscode-languageclient'; import { Server } from '../server'; interface RunnablesParams { - textDocument: lc.TextDocumentIdentifier, - position?: lc.Position, + textDocument: lc.TextDocumentIdentifier; + position?: lc.Position; } interface Runnable { @@ -12,17 +12,17 @@ interface Runnable { label: string; bin: string; args: string[]; - env: { [index: string]: string }, + env: { [index: string]: string }; } class RunnableQuickPick implements vscode.QuickPickItem { - label: string; - description?: string | undefined; - detail?: string | undefined; - picked?: boolean | undefined; + public label: string; + public description?: string | undefined; + public detail?: string | undefined; + public picked?: boolean | undefined; constructor(public runnable: Runnable) { - this.label = runnable.label + this.label = runnable.label; } } @@ -30,59 +30,59 @@ interface CargoTaskDefinition extends vscode.TaskDefinition { type: 'cargo'; label: string; command: string; - args: Array; + args: string[]; env?: { [key: string]: string }; } function createTask(spec: Runnable): vscode.Task { const TASK_SOURCE = 'Rust'; - let definition: CargoTaskDefinition = { + const definition: CargoTaskDefinition = { type: 'cargo', label: 'cargo', command: spec.bin, args: spec.args, - env: spec.env - } + env: spec.env, + }; - let execCmd = `${definition.command} ${definition.args.join(' ')}`; - let execOption: vscode.ShellExecutionOptions = { + const execCmd = `${definition.command} ${definition.args.join(' ')}`; + const execOption: vscode.ShellExecutionOptions = { cwd: '.', env: definition.env, }; - let exec = new vscode.ShellExecution(`clear; ${execCmd}`, execOption); + const exec = new vscode.ShellExecution(`clear; ${execCmd}`, execOption); - let f = vscode.workspace.workspaceFolders![0] - let t = new vscode.Task(definition, f, definition.label, TASK_SOURCE, exec, ['$rustc']); + const f = vscode.workspace.workspaceFolders![0]; + const t = new vscode.Task(definition, f, definition.label, TASK_SOURCE, exec, ['$rustc']); return t; } -let prevRunnable: RunnableQuickPick | undefined = undefined +let prevRunnable: RunnableQuickPick | undefined; export async function handle() { - let editor = vscode.window.activeTextEditor - if (editor == null || editor.document.languageId != "rust") return - let textDocument: lc.TextDocumentIdentifier = { - uri: editor.document.uri.toString() - } - let params: RunnablesParams = { + const editor = vscode.window.activeTextEditor; + if (editor == null || editor.document.languageId != 'rust') { return; } + const textDocument: lc.TextDocumentIdentifier = { + uri: editor.document.uri.toString(), + }; + const params: RunnablesParams = { textDocument, - position: Server.client.code2ProtocolConverter.asPosition(editor.selection.active) - } - let runnables = await Server.client.sendRequest('m/runnables', params) - let items: RunnableQuickPick[] = [] + position: Server.client.code2ProtocolConverter.asPosition(editor.selection.active), + }; + const runnables = await Server.client.sendRequest('m/runnables', params); + const items: RunnableQuickPick[] = []; if (prevRunnable) { - items.push(prevRunnable) + items.push(prevRunnable); } - for (let r of runnables) { + for (const r of runnables) { if (prevRunnable && JSON.stringify(prevRunnable.runnable) == JSON.stringify(r)) { - continue + continue; } - items.push(new RunnableQuickPick(r)) + items.push(new RunnableQuickPick(r)); } - let item = await vscode.window.showQuickPick(items) + const item = await vscode.window.showQuickPick(items); if (item) { - item.detail = "rerun" - prevRunnable = item - let task = createTask(item.runnable) - return await vscode.tasks.executeTask(task) + item.detail = 'rerun'; + prevRunnable = item; + const task = createTask(item.runnable); + return await vscode.tasks.executeTask(task); } } diff --git a/editors/code/src/commands/syntaxTree.ts b/editors/code/src/commands/syntaxTree.ts index d5daa9302c6..dcb721eee59 100644 --- a/editors/code/src/commands/syntaxTree.ts +++ b/editors/code/src/commands/syntaxTree.ts @@ -6,20 +6,20 @@ import { Server } from '../server'; export const syntaxTreeUri = vscode.Uri.parse('ra-lsp://syntaxtree'); export class TextDocumentContentProvider implements vscode.TextDocumentContentProvider { - public eventEmitter = new vscode.EventEmitter() - public syntaxTree: string = "Not available" + public eventEmitter = new vscode.EventEmitter(); + public syntaxTree: string = 'Not available'; public provideTextDocumentContent(uri: vscode.Uri): vscode.ProviderResult { - let editor = vscode.window.activeTextEditor; - if (editor == null) return "" - let request: SyntaxTreeParams = { - textDocument: { uri: editor.document.uri.toString() } + const editor = vscode.window.activeTextEditor; + if (editor == null) { return ''; } + const request: SyntaxTreeParams = { + textDocument: { uri: editor.document.uri.toString() }, }; - return Server.client.sendRequest("m/syntaxTree", request); + return Server.client.sendRequest('m/syntaxTree', request); } get onDidChange(): vscode.Event { - return this.eventEmitter.event + return this.eventEmitter.event; } } @@ -33,6 +33,6 @@ type SyntaxTreeResult = string; // // The contents of the file come from the `TextDocumentContentProvider` export async function handle() { - let document = await vscode.workspace.openTextDocument(syntaxTreeUri) - return vscode.window.showTextDocument(document, vscode.ViewColumn.Two, true) + const document = await vscode.workspace.openTextDocument(syntaxTreeUri); + return vscode.window.showTextDocument(document, vscode.ViewColumn.Two, true); } diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts new file mode 100644 index 00000000000..740b5be206a --- /dev/null +++ b/editors/code/src/config.ts @@ -0,0 +1,23 @@ +import * as vscode from 'vscode'; + +import { Server } from './server'; + +export class Config { + public highlightingOn = true; + + constructor() { + vscode.workspace.onDidChangeConfiguration((_) => this.userConfigChanged()); + this.userConfigChanged(); + } + + public userConfigChanged() { + const config = vscode.workspace.getConfiguration('ra-lsp'); + if (config.has('highlightingOn')) { + this.highlightingOn = config.get('highlightingOn') as boolean; + } + + if (!this.highlightingOn && Server) { + Server.highlighter.removeHighlights(); + } + } +} diff --git a/editors/code/src/events.ts b/editors/code/src/events.ts index b143bb2565c..8e2ac4a469a 100644 --- a/editors/code/src/events.ts +++ b/editors/code/src/events.ts @@ -1,7 +1,7 @@ -import * as changeActiveTextEditor from './events/change_active_text_editor' +import * as changeActiveTextEditor from './events/change_active_text_editor'; import * as changeTextDocument from './events/change_text_document'; export { changeActiveTextEditor, - changeTextDocument -} \ No newline at end of file + changeTextDocument, +}; diff --git a/editors/code/src/events/change_active_text_editor.ts b/editors/code/src/events/change_active_text_editor.ts index bbdd5309869..96d61126c6e 100644 --- a/editors/code/src/events/change_active_text_editor.ts +++ b/editors/code/src/events/change_active_text_editor.ts @@ -1,14 +1,14 @@ -import { TextEditor } from "vscode"; -import { TextDocumentIdentifier } from "vscode-languageclient"; +import { TextEditor } from 'vscode'; +import { TextDocumentIdentifier } from 'vscode-languageclient'; -import { Server } from "../server"; -import { Decoration } from "../highlighting"; +import { Decoration } from '../highlighting'; +import { Server } from '../server'; export async function handle(editor: TextEditor | undefined) { - if (!Server.config.highlightingOn || !editor || editor.document.languageId != 'rust') return - let params: TextDocumentIdentifier = { - uri: editor.document.uri.toString() - } - let decorations = await Server.client.sendRequest("m/decorationsRequest", params) - Server.highlighter.setHighlights(editor, decorations) -} \ No newline at end of file + if (!Server.config.highlightingOn || !editor || editor.document.languageId != 'rust') { return; } + const params: TextDocumentIdentifier = { + uri: editor.document.uri.toString(), + }; + const decorations = await Server.client.sendRequest('m/decorationsRequest', params); + Server.highlighter.setHighlights(editor, decorations); +} diff --git a/editors/code/src/events/change_text_document.ts b/editors/code/src/events/change_text_document.ts index 83ee6c9ee99..192fb1e8a8b 100644 --- a/editors/code/src/events/change_text_document.ts +++ b/editors/code/src/events/change_text_document.ts @@ -4,16 +4,16 @@ import { syntaxTreeUri, TextDocumentContentProvider } from '../commands/syntaxTr export function createHandler(textDocumentContentProvider: TextDocumentContentProvider) { return (event: vscode.TextDocumentChangeEvent) => { - let doc = event.document - if (doc.languageId != "rust") return + const doc = event.document; + if (doc.languageId != 'rust') { return; } afterLs(() => { textDocumentContentProvider.eventEmitter.fire(syntaxTreeUri); - }) - } + }); + }; } // We need to order this after LS updates, but there's no API for that. // Hence, good old setTimeout. function afterLs(f: () => any) { - setTimeout(f, 10) + setTimeout(f, 10); } diff --git a/editors/code/src/extension.ts b/editors/code/src/extension.ts index 595fb98fe3f..f1bc0b457b3 100644 --- a/editors/code/src/extension.ts +++ b/editors/code/src/extension.ts @@ -1,9 +1,9 @@ import * as vscode from 'vscode'; -import * as commands from './commands' -import * as events from './events' -import { Server } from './server'; +import * as commands from './commands'; import { TextDocumentContentProvider } from './commands/syntaxTree'; +import * as events from './events'; +import { Server } from './server'; export function activate(context: vscode.ExtensionContext) { function disposeOnDeactivation(disposable: vscode.Disposable) { @@ -11,10 +11,10 @@ export function activate(context: vscode.ExtensionContext) { } function registerCommand(name: string, f: any) { - disposeOnDeactivation(vscode.commands.registerCommand(name, f)) + disposeOnDeactivation(vscode.commands.registerCommand(name, f)); } - registerCommand('ra-lsp.syntaxTree', commands.syntaxTree.handle) + registerCommand('ra-lsp.syntaxTree', commands.syntaxTree.handle); registerCommand('ra-lsp.extendSelection', commands.extendSelection.handle); registerCommand('ra-lsp.matchingBrace', commands.matchingBrace.handle); registerCommand('ra-lsp.joinLines', commands.joinLines.handle); @@ -22,19 +22,19 @@ export function activate(context: vscode.ExtensionContext) { registerCommand('ra-lsp.run', commands.runnables.handle); registerCommand('ra-lsp.applySourceChange', commands.applySourceChange.handle); - let textDocumentContentProvider = new TextDocumentContentProvider() + const textDocumentContentProvider = new TextDocumentContentProvider(); disposeOnDeactivation(vscode.workspace.registerTextDocumentContentProvider( 'ra-lsp', - textDocumentContentProvider - )) + textDocumentContentProvider, + )); - Server.start() + Server.start(); vscode.workspace.onDidChangeTextDocument( events.changeTextDocument.createHandler(textDocumentContentProvider), null, - context.subscriptions) - vscode.window.onDidChangeActiveTextEditor(events.changeActiveTextEditor.handle) + context.subscriptions); + vscode.window.onDidChangeActiveTextEditor(events.changeActiveTextEditor.handle); } export function deactivate(): Thenable { diff --git a/editors/code/src/highlighting.ts b/editors/code/src/highlighting.ts index 169ddb0df08..71f8e5baa18 100644 --- a/editors/code/src/highlighting.ts +++ b/editors/code/src/highlighting.ts @@ -1,11 +1,11 @@ import * as vscode from 'vscode'; -import * as lc from 'vscode-languageclient' +import * as lc from 'vscode-languageclient'; import { Server } from './server'; export interface Decoration { - range: lc.Range, - tag: string, + range: lc.Range; + tag: string; } export class Highlighter { @@ -14,17 +14,17 @@ export class Highlighter { this.decorations = {}; } - removeHighlights() { - for (let tag in this.decorations) { + public removeHighlights() { + for (const tag in this.decorations) { this.decorations[tag].dispose(); } this.decorations = {}; } - setHighlights( + public setHighlights( editor: vscode.TextEditor, - highlights: Array + highlights: Decoration[], ) { // Initialize decorations if necessary // @@ -34,45 +34,45 @@ export class Highlighter { this.initDecorations(); } - let byTag: Map = new Map() - for (let tag in this.decorations) { - byTag.set(tag, []) + const byTag: Map = new Map(); + for (const tag in this.decorations) { + byTag.set(tag, []); } - for (let d of highlights) { + for (const d of highlights) { if (!byTag.get(d.tag)) { - console.log(`unknown tag ${d.tag}`) - continue + console.log(`unknown tag ${d.tag}`); + continue; } byTag.get(d.tag)!.push( - Server.client.protocol2CodeConverter.asRange(d.range) - ) + Server.client.protocol2CodeConverter.asRange(d.range), + ); } - for (let tag of byTag.keys()) { - let dec: vscode.TextEditorDecorationType = this.decorations[tag] - let ranges = byTag.get(tag)! - editor.setDecorations(dec, ranges) + for (const tag of byTag.keys()) { + const dec: vscode.TextEditorDecorationType = this.decorations[tag]; + const ranges = byTag.get(tag)!; + editor.setDecorations(dec, ranges); } } private initDecorations() { - const decor = (obj: any) => vscode.window.createTextEditorDecorationType({ color: obj }) + const decor = (obj: any) => vscode.window.createTextEditorDecorationType({ color: obj }); this.decorations = { - background: decor("#3F3F3F"), + background: decor('#3F3F3F'), error: vscode.window.createTextEditorDecorationType({ - borderColor: "red", - borderStyle: "none none dashed none", + borderColor: 'red', + borderStyle: 'none none dashed none', }), - comment: decor("#7F9F7F"), - string: decor("#CC9393"), - keyword: decor("#F0DFAF"), - function: decor("#93E0E3"), - parameter: decor("#94BFF3"), - builtin: decor("#DD6718"), - text: decor("#DCDCCC"), - attribute: decor("#BFEBBF"), - literal: decor("#DFAF8F"), - } + comment: decor('#7F9F7F'), + string: decor('#CC9393'), + keyword: decor('#F0DFAF'), + function: decor('#93E0E3'), + parameter: decor('#94BFF3'), + builtin: decor('#DD6718'), + text: decor('#DCDCCC'), + attribute: decor('#BFEBBF'), + literal: decor('#DFAF8F'), + }; } } diff --git a/editors/code/src/server.ts b/editors/code/src/server.ts index c1c95e00842..3857b00a535 100644 --- a/editors/code/src/server.ts +++ b/editors/code/src/server.ts @@ -1,45 +1,25 @@ import * as vscode from 'vscode'; -import * as lc from 'vscode-languageclient' +import * as lc from 'vscode-languageclient'; -import { Highlighter, Decoration } from './highlighting'; - -export class Config { - highlightingOn = true; - - constructor() { - vscode.workspace.onDidChangeConfiguration(_ => this.userConfigChanged()); - this.userConfigChanged(); - } - - userConfigChanged() { - let config = vscode.workspace.getConfiguration('ra-lsp'); - if (config.has('highlightingOn')) { - this.highlightingOn = config.get('highlightingOn') as boolean; - }; - - if (!this.highlightingOn) { - Server.highlighter.removeHighlights(); - } - } -} +import { Config } from './config'; +import { Decoration, Highlighter } from './highlighting'; export class Server { - static highlighter = new Highlighter(); - static config = new Config(); - static client: lc.LanguageClient; + public static highlighter = new Highlighter(); + public static config = new Config(); + public static client: lc.LanguageClient; - - static start() { - let run: lc.Executable = { - command: "ra_lsp_server", - options: { cwd: "." } - } - let serverOptions: lc.ServerOptions = { + public static start() { + const run: lc.Executable = { + command: 'ra_lsp_server', + options: { cwd: '.' }, + }; + const serverOptions: lc.ServerOptions = { run, - debug: run + debug: run, }; - let clientOptions: lc.LanguageClientOptions = { + const clientOptions: lc.LanguageClientOptions = { documentSelector: [{ scheme: 'file', language: 'rust' }], }; @@ -51,24 +31,24 @@ export class Server { ); Server.client.onReady().then(() => { Server.client.onNotification( - "m/publishDecorations", + 'm/publishDecorations', (params: PublishDecorationsParams) => { - let editor = vscode.window.visibleTextEditors.find( - (editor) => editor.document.uri.toString() == params.uri - ) - if (!Server.config.highlightingOn || !editor) return; + const targetEditor = vscode.window.visibleTextEditors.find( + (editor) => editor.document.uri.toString() == params.uri, + ); + if (!Server.config.highlightingOn || !targetEditor) { return; } Server.highlighter.setHighlights( - editor, + targetEditor, params.decorations, - ) - } - ) - }) + ); + }, + ); + }); Server.client.start(); } } interface PublishDecorationsParams { - uri: string, - decorations: Decoration[], + uri: string; + decorations: Decoration[]; } diff --git a/editors/code/tslint.json b/editors/code/tslint.json new file mode 100644 index 00000000000..466e1fa2035 --- /dev/null +++ b/editors/code/tslint.json @@ -0,0 +1,13 @@ +{ + "defaultSeverity": "warning", + "extends": [ + "tslint:recommended" + ], + "jsRules": {}, + "rules": { + "quotemark": [true, "single"], + "interface-name": false, + "object-literal-sort-keys": false + }, + "rulesDirectory": [] +} From 62b1b05a0d9dd021f98352b6229e48e0d8b94f78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adolfo=20Ochagav=C3=ADa?= Date: Mon, 8 Oct 2018 20:18:55 +0200 Subject: [PATCH 3/4] Fix remaining tslint suggestions --- editors/code/src/commands.ts | 17 ----- .../code/src/commands/apply_source_change.ts | 26 ++++---- editors/code/src/commands/extend_selection.ts | 2 +- editors/code/src/commands/index.ts | 17 +++++ editors/code/src/commands/join_lines.ts | 2 +- editors/code/src/commands/matching_brace.ts | 2 +- editors/code/src/commands/parent_module.ts | 2 +- editors/code/src/commands/runnables.ts | 4 +- editors/code/src/events.ts | 7 -- .../src/events/change_active_text_editor.ts | 2 +- .../code/src/events/change_text_document.ts | 2 +- editors/code/src/events/index.ts | 7 ++ editors/code/src/highlighting.ts | 66 ++++++++++--------- editors/code/src/server.ts | 2 +- editors/code/tslint.json | 2 +- 15 files changed, 82 insertions(+), 78 deletions(-) delete mode 100644 editors/code/src/commands.ts create mode 100644 editors/code/src/commands/index.ts delete mode 100644 editors/code/src/events.ts create mode 100644 editors/code/src/events/index.ts diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts deleted file mode 100644 index c7e27781e06..00000000000 --- a/editors/code/src/commands.ts +++ /dev/null @@ -1,17 +0,0 @@ -import * as applySourceChange from './commands/apply_source_change'; -import * as extendSelection from './commands/extend_selection'; -import * as joinLines from './commands/join_lines'; -import * as matchingBrace from './commands/matching_brace'; -import * as parentModule from './commands/parent_module'; -import * as runnables from './commands/runnables'; -import * as syntaxTree from './commands/syntaxTree'; - -export { - applySourceChange, - extendSelection, - joinLines, - matchingBrace, - parentModule, - runnables, - syntaxTree, -}; diff --git a/editors/code/src/commands/apply_source_change.ts b/editors/code/src/commands/apply_source_change.ts index f011cbe12e9..67765e5a3ca 100644 --- a/editors/code/src/commands/apply_source_change.ts +++ b/editors/code/src/commands/apply_source_change.ts @@ -18,7 +18,6 @@ export interface SourceChange { } export async function handle(change: SourceChange) { - console.log(`applySOurceChange ${JSON.stringify(change)}`); const wsEdit = new vscode.WorkspaceEdit(); for (const sourceEdit of change.sourceFileEdits) { const uri = Server.client.protocol2CodeConverter.asUri(sourceEdit.textDocument.uri); @@ -28,17 +27,18 @@ export async function handle(change: SourceChange) { let created; let moved; for (const fsEdit of change.fileSystemEdits) { - if (fsEdit.type == 'createFile') { - const uri = vscode.Uri.parse(fsEdit.uri!); - wsEdit.createFile(uri); - created = uri; - } else if (fsEdit.type == 'moveFile') { - const src = vscode.Uri.parse(fsEdit.src!); - const dst = vscode.Uri.parse(fsEdit.dst!); - wsEdit.renameFile(src, dst); - moved = dst; - } else { - console.error(`unknown op: ${JSON.stringify(fsEdit)}`); + switch (fsEdit.type) { + case 'createFile': + const uri = vscode.Uri.parse(fsEdit.uri!); + wsEdit.createFile(uri); + created = uri; + break; + case 'moveFile': + const src = vscode.Uri.parse(fsEdit.src!); + const dst = vscode.Uri.parse(fsEdit.dst!); + wsEdit.renameFile(src, dst); + moved = dst; + break; } } const toOpen = created || moved; @@ -51,7 +51,7 @@ export async function handle(change: SourceChange) { const uri = Server.client.protocol2CodeConverter.asUri(toReveal.textDocument.uri); const position = Server.client.protocol2CodeConverter.asPosition(toReveal.position); const editor = vscode.window.activeTextEditor; - if (!editor || editor.document.uri.toString() != uri.toString()) { return; } + if (!editor || editor.document.uri.toString() !== uri.toString()) { return; } if (!editor.selection.isEmpty) { return; } editor!.selection = new vscode.Selection(position, position); } diff --git a/editors/code/src/commands/extend_selection.ts b/editors/code/src/commands/extend_selection.ts index b722ac17229..cdc3d10fb3f 100644 --- a/editors/code/src/commands/extend_selection.ts +++ b/editors/code/src/commands/extend_selection.ts @@ -14,7 +14,7 @@ interface ExtendSelectionResult { export async function handle() { const editor = vscode.window.activeTextEditor; - if (editor == null || editor.document.languageId != 'rust') { return; } + if (editor == null || editor.document.languageId !== 'rust') { return; } const request: ExtendSelectionParams = { selections: editor.selections.map((s) => { return Server.client.code2ProtocolConverter.asRange(s); diff --git a/editors/code/src/commands/index.ts b/editors/code/src/commands/index.ts new file mode 100644 index 00000000000..dfdcd64545b --- /dev/null +++ b/editors/code/src/commands/index.ts @@ -0,0 +1,17 @@ +import * as applySourceChange from './apply_source_change'; +import * as extendSelection from './extend_selection'; +import * as joinLines from './join_lines'; +import * as matchingBrace from './matching_brace'; +import * as parentModule from './parent_module'; +import * as runnables from './runnables'; +import * as syntaxTree from './syntaxTree'; + +export { + applySourceChange, + extendSelection, + joinLines, + matchingBrace, + parentModule, + runnables, + syntaxTree, +}; diff --git a/editors/code/src/commands/join_lines.ts b/editors/code/src/commands/join_lines.ts index 80ad4460ba1..526b698ccd2 100644 --- a/editors/code/src/commands/join_lines.ts +++ b/editors/code/src/commands/join_lines.ts @@ -11,7 +11,7 @@ interface JoinLinesParams { export async function handle() { const editor = vscode.window.activeTextEditor; - if (editor == null || editor.document.languageId != 'rust') { return; } + if (editor == null || editor.document.languageId !== 'rust') { return; } const request: JoinLinesParams = { range: Server.client.code2ProtocolConverter.asRange(editor.selection), textDocument: { uri: editor.document.uri.toString() }, diff --git a/editors/code/src/commands/matching_brace.ts b/editors/code/src/commands/matching_brace.ts index cf7f6bf8fac..a80446a8f58 100644 --- a/editors/code/src/commands/matching_brace.ts +++ b/editors/code/src/commands/matching_brace.ts @@ -10,7 +10,7 @@ interface FindMatchingBraceParams { export async function handle() { const editor = vscode.window.activeTextEditor; - if (editor == null || editor.document.languageId != 'rust') { return; } + if (editor == null || editor.document.languageId !== 'rust') { return; } const request: FindMatchingBraceParams = { textDocument: { uri: editor.document.uri.toString() }, offsets: editor.selections.map((s) => { diff --git a/editors/code/src/commands/parent_module.ts b/editors/code/src/commands/parent_module.ts index 7d413c27a65..d66fb30264d 100644 --- a/editors/code/src/commands/parent_module.ts +++ b/editors/code/src/commands/parent_module.ts @@ -5,7 +5,7 @@ import { Server } from '../server'; export async function handle() { const editor = vscode.window.activeTextEditor; - if (editor == null || editor.document.languageId != 'rust') { return; } + if (editor == null || editor.document.languageId !== 'rust') { return; } const request: TextDocumentIdentifier = { uri: editor.document.uri.toString(), }; diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/commands/runnables.ts index 37db6ea10ac..40f590dceb8 100644 --- a/editors/code/src/commands/runnables.ts +++ b/editors/code/src/commands/runnables.ts @@ -59,7 +59,7 @@ function createTask(spec: Runnable): vscode.Task { let prevRunnable: RunnableQuickPick | undefined; export async function handle() { const editor = vscode.window.activeTextEditor; - if (editor == null || editor.document.languageId != 'rust') { return; } + if (editor == null || editor.document.languageId !== 'rust') { return; } const textDocument: lc.TextDocumentIdentifier = { uri: editor.document.uri.toString(), }; @@ -73,7 +73,7 @@ export async function handle() { items.push(prevRunnable); } for (const r of runnables) { - if (prevRunnable && JSON.stringify(prevRunnable.runnable) == JSON.stringify(r)) { + if (prevRunnable && JSON.stringify(prevRunnable.runnable) === JSON.stringify(r)) { continue; } items.push(new RunnableQuickPick(r)); diff --git a/editors/code/src/events.ts b/editors/code/src/events.ts deleted file mode 100644 index 8e2ac4a469a..00000000000 --- a/editors/code/src/events.ts +++ /dev/null @@ -1,7 +0,0 @@ -import * as changeActiveTextEditor from './events/change_active_text_editor'; -import * as changeTextDocument from './events/change_text_document'; - -export { - changeActiveTextEditor, - changeTextDocument, -}; diff --git a/editors/code/src/events/change_active_text_editor.ts b/editors/code/src/events/change_active_text_editor.ts index 96d61126c6e..3440aa0c375 100644 --- a/editors/code/src/events/change_active_text_editor.ts +++ b/editors/code/src/events/change_active_text_editor.ts @@ -5,7 +5,7 @@ import { Decoration } from '../highlighting'; import { Server } from '../server'; export async function handle(editor: TextEditor | undefined) { - if (!Server.config.highlightingOn || !editor || editor.document.languageId != 'rust') { return; } + if (!Server.config.highlightingOn || !editor || editor.document.languageId !== 'rust') { return; } const params: TextDocumentIdentifier = { uri: editor.document.uri.toString(), }; diff --git a/editors/code/src/events/change_text_document.ts b/editors/code/src/events/change_text_document.ts index 192fb1e8a8b..b3000e02618 100644 --- a/editors/code/src/events/change_text_document.ts +++ b/editors/code/src/events/change_text_document.ts @@ -5,7 +5,7 @@ import { syntaxTreeUri, TextDocumentContentProvider } from '../commands/syntaxTr export function createHandler(textDocumentContentProvider: TextDocumentContentProvider) { return (event: vscode.TextDocumentChangeEvent) => { const doc = event.document; - if (doc.languageId != 'rust') { return; } + if (doc.languageId !== 'rust') { return; } afterLs(() => { textDocumentContentProvider.eventEmitter.fire(syntaxTreeUri); }); diff --git a/editors/code/src/events/index.ts b/editors/code/src/events/index.ts new file mode 100644 index 00000000000..b570a7a926a --- /dev/null +++ b/editors/code/src/events/index.ts @@ -0,0 +1,7 @@ +import * as changeActiveTextEditor from './change_active_text_editor'; +import * as changeTextDocument from './change_text_document'; + +export { + changeActiveTextEditor, + changeTextDocument, +}; diff --git a/editors/code/src/highlighting.ts b/editors/code/src/highlighting.ts index 71f8e5baa18..e2ac4d62907 100644 --- a/editors/code/src/highlighting.ts +++ b/editors/code/src/highlighting.ts @@ -9,17 +9,42 @@ export interface Decoration { } export class Highlighter { - private decorations: { [index: string]: vscode.TextEditorDecorationType }; - constructor() { - this.decorations = {}; + private static initDecorations(): Map { + const decor = (color: string) => vscode.window.createTextEditorDecorationType({ color }); + + const decorations: Iterable<[string, vscode.TextEditorDecorationType]> = [ + ['background', decor('#3F3F3F')], + ['error', vscode.window.createTextEditorDecorationType({ + borderColor: 'red', + borderStyle: 'none none dashed none', + })], + ['comment', decor('#7F9F7F')], + ['string', decor('#CC9393')], + ['keyword', decor('#F0DFAF')], + ['function', decor('#93E0E3')], + ['parameter', decor('#94BFF3')], + ['builtin', decor('#DD6718')], + ['text', decor('#DCDCCC')], + ['attribute', decor('#BFEBBF')], + ['literal', decor('#DFAF8F')], + ]; + + return new Map(decorations); } + private decorations: (Map | null) = null; + public removeHighlights() { - for (const tag in this.decorations) { - this.decorations[tag].dispose(); + if (this.decorations == null) { + return; } - this.decorations = {}; + // Decorations are removed when the object is disposed + for (const decoration of this.decorations.values()) { + decoration.dispose(); + } + + this.decorations = null; } public setHighlights( @@ -30,18 +55,17 @@ export class Highlighter { // // Note: decoration objects need to be kept around so we can dispose them // if the user disables syntax highlighting - if (Object.keys(this.decorations).length === 0) { - this.initDecorations(); + if (this.decorations == null) { + this.decorations = Highlighter.initDecorations(); } const byTag: Map = new Map(); - for (const tag in this.decorations) { + for (const tag of this.decorations.keys()) { byTag.set(tag, []); } for (const d of highlights) { if (!byTag.get(d.tag)) { - console.log(`unknown tag ${d.tag}`); continue; } byTag.get(d.tag)!.push( @@ -50,29 +74,9 @@ export class Highlighter { } for (const tag of byTag.keys()) { - const dec: vscode.TextEditorDecorationType = this.decorations[tag]; + const dec = this.decorations.get(tag) as vscode.TextEditorDecorationType; const ranges = byTag.get(tag)!; editor.setDecorations(dec, ranges); } } - - private initDecorations() { - const decor = (obj: any) => vscode.window.createTextEditorDecorationType({ color: obj }); - this.decorations = { - background: decor('#3F3F3F'), - error: vscode.window.createTextEditorDecorationType({ - borderColor: 'red', - borderStyle: 'none none dashed none', - }), - comment: decor('#7F9F7F'), - string: decor('#CC9393'), - keyword: decor('#F0DFAF'), - function: decor('#93E0E3'), - parameter: decor('#94BFF3'), - builtin: decor('#DD6718'), - text: decor('#DCDCCC'), - attribute: decor('#BFEBBF'), - literal: decor('#DFAF8F'), - }; - } } diff --git a/editors/code/src/server.ts b/editors/code/src/server.ts index 3857b00a535..325023e36af 100644 --- a/editors/code/src/server.ts +++ b/editors/code/src/server.ts @@ -34,7 +34,7 @@ export class Server { 'm/publishDecorations', (params: PublishDecorationsParams) => { const targetEditor = vscode.window.visibleTextEditors.find( - (editor) => editor.document.uri.toString() == params.uri, + (editor) => editor.document.uri.toString() === params.uri, ); if (!Server.config.highlightingOn || !targetEditor) { return; } Server.highlighter.setHighlights( diff --git a/editors/code/tslint.json b/editors/code/tslint.json index 466e1fa2035..ce48dfc95e1 100644 --- a/editors/code/tslint.json +++ b/editors/code/tslint.json @@ -1,5 +1,5 @@ { - "defaultSeverity": "warning", + "defaultSeverity": "error", "extends": [ "tslint:recommended" ], From bbf38b9e722e8d6455828ff22242c92219da346d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adolfo=20Ochagav=C3=ADa?= Date: Mon, 8 Oct 2018 20:55:22 +0200 Subject: [PATCH 4/4] Add some comments --- editors/code/src/extension.ts | 17 +++++++++--- editors/code/src/notifications/index.ts | 5 ++++ .../src/notifications/publish_decorations.ts | 20 ++++++++++++++ editors/code/src/server.ts | 27 ++++--------------- 4 files changed, 44 insertions(+), 25 deletions(-) create mode 100644 editors/code/src/notifications/index.ts create mode 100644 editors/code/src/notifications/publish_decorations.ts diff --git a/editors/code/src/extension.ts b/editors/code/src/extension.ts index f1bc0b457b3..44e74f4cc9d 100644 --- a/editors/code/src/extension.ts +++ b/editors/code/src/extension.ts @@ -1,8 +1,10 @@ import * as vscode from 'vscode'; +import * as lc from 'vscode-languageclient'; import * as commands from './commands'; import { TextDocumentContentProvider } from './commands/syntaxTree'; import * as events from './events'; +import * as notifications from './notifications'; import { Server } from './server'; export function activate(context: vscode.ExtensionContext) { @@ -14,6 +16,7 @@ export function activate(context: vscode.ExtensionContext) { disposeOnDeactivation(vscode.commands.registerCommand(name, f)); } + // Commands are requests from vscode to the language server registerCommand('ra-lsp.syntaxTree', commands.syntaxTree.handle); registerCommand('ra-lsp.extendSelection', commands.extendSelection.handle); registerCommand('ra-lsp.matchingBrace', commands.matchingBrace.handle); @@ -22,19 +25,27 @@ export function activate(context: vscode.ExtensionContext) { registerCommand('ra-lsp.run', commands.runnables.handle); registerCommand('ra-lsp.applySourceChange', commands.applySourceChange.handle); + // Notifications are events triggered by the language server + const allNotifications: Iterable<[string, lc.GenericNotificationHandler]> = [ + ['m/publishDecorations', notifications.publishDecorations.handle], + ]; + + // The events below are plain old javascript events, triggered and handled by vscode + vscode.window.onDidChangeActiveTextEditor(events.changeActiveTextEditor.handle); + const textDocumentContentProvider = new TextDocumentContentProvider(); disposeOnDeactivation(vscode.workspace.registerTextDocumentContentProvider( 'ra-lsp', textDocumentContentProvider, )); - Server.start(); - vscode.workspace.onDidChangeTextDocument( events.changeTextDocument.createHandler(textDocumentContentProvider), null, context.subscriptions); - vscode.window.onDidChangeActiveTextEditor(events.changeActiveTextEditor.handle); + + // Start the language server, finally! + Server.start(allNotifications); } export function deactivate(): Thenable { diff --git a/editors/code/src/notifications/index.ts b/editors/code/src/notifications/index.ts new file mode 100644 index 00000000000..c5657686588 --- /dev/null +++ b/editors/code/src/notifications/index.ts @@ -0,0 +1,5 @@ +import * as publishDecorations from './publish_decorations'; + +export { + publishDecorations, +}; diff --git a/editors/code/src/notifications/publish_decorations.ts b/editors/code/src/notifications/publish_decorations.ts new file mode 100644 index 00000000000..d8790386b1a --- /dev/null +++ b/editors/code/src/notifications/publish_decorations.ts @@ -0,0 +1,20 @@ +import * as vscode from 'vscode'; + +import { Decoration } from '../highlighting'; +import { Server } from '../server'; + +export interface PublishDecorationsParams { + uri: string; + decorations: Decoration[]; +} + +export function handle(params: PublishDecorationsParams) { + const targetEditor = vscode.window.visibleTextEditors.find( + (editor) => editor.document.uri.toString() === params.uri, + ); + if (!Server.config.highlightingOn || !targetEditor) { return; } + Server.highlighter.setHighlights( + targetEditor, + params.decorations, + ); +} diff --git a/editors/code/src/server.ts b/editors/code/src/server.ts index 325023e36af..01fd80756fd 100644 --- a/editors/code/src/server.ts +++ b/editors/code/src/server.ts @@ -1,15 +1,14 @@ -import * as vscode from 'vscode'; import * as lc from 'vscode-languageclient'; import { Config } from './config'; -import { Decoration, Highlighter } from './highlighting'; +import { Highlighter } from './highlighting'; export class Server { public static highlighter = new Highlighter(); public static config = new Config(); public static client: lc.LanguageClient; - public static start() { + public static start(notificationHandlers: Iterable<[string, lc.GenericNotificationHandler]>) { const run: lc.Executable = { command: 'ra_lsp_server', options: { cwd: '.' }, @@ -18,7 +17,6 @@ export class Server { run, debug: run, }; - const clientOptions: lc.LanguageClientOptions = { documentSelector: [{ scheme: 'file', language: 'rust' }], }; @@ -30,25 +28,10 @@ export class Server { clientOptions, ); Server.client.onReady().then(() => { - Server.client.onNotification( - 'm/publishDecorations', - (params: PublishDecorationsParams) => { - const targetEditor = vscode.window.visibleTextEditors.find( - (editor) => editor.document.uri.toString() === params.uri, - ); - if (!Server.config.highlightingOn || !targetEditor) { return; } - Server.highlighter.setHighlights( - targetEditor, - params.decorations, - ); - }, - ); + for (const [type, handler] of notificationHandlers) { + Server.client.onNotification(type, handler); + } }); Server.client.start(); } } - -interface PublishDecorationsParams { - uri: string; - decorations: Decoration[]; -}