From b5021411a84822cb3f1e3aeffad9550dd15bdeb6 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 16 Sep 2018 12:54:24 +0300 Subject: rename all things --- editors/code/src/extension.ts | 400 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 400 insertions(+) create mode 100644 editors/code/src/extension.ts (limited to 'editors/code/src') diff --git a/editors/code/src/extension.ts b/editors/code/src/extension.ts new file mode 100644 index 000000000..2c42c07fe --- /dev/null +++ b/editors/code/src/extension.ts @@ -0,0 +1,400 @@ +'use strict'; +import * as vscode from 'vscode'; +import * as lc from 'vscode-languageclient' +import { DH_UNABLE_TO_CHECK_GENERATOR } from 'constants'; + + +let client: lc.LanguageClient; + +let uris = { + syntaxTree: vscode.Uri.parse('ra-lsp://syntaxtree') +} + + +export function activate(context: vscode.ExtensionContext) { + let textDocumentContentProvider = new TextDocumentContentProvider() + let dispose = (disposable: vscode.Disposable) => { + context.subscriptions.push(disposable); + } + let registerCommand = (name: string, f: any) => { + dispose(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) + + let doc = await vscode.workspace.openTextDocument(uri) + let e = await vscode.window.showTextDocument(doc) + 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( + '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 (!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) +} + +export function deactivate(): Thenable { + if (!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 (editor == null) 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 + } +} + + +const decorations: { [index: string]: vscode.TextEditorDecorationType } = (() => { + const decor = (obj: any) => vscode.window.createTextEditorDecorationType({ color: obj }) + return { + 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 setHighlights( + editor: vscode.TextEditor, + highlihgs: Array +) { + let byTag: Map = new Map() + for (let tag in decorations) { + byTag.set(tag, []) + } + + for (let d of highlihgs) { + 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) + } +} -- cgit v1.2.3