diff options
author | 2021-06-16 13:20:29 -0500 | |
---|---|---|
committer | 2021-06-16 13:20:29 -0500 | |
commit | 0dd278810e4353799c7239463f156b358ea30871 (patch) | |
tree | 964a62a13467950509b446ebeaa9b668e5874665 /tools/vscode/src | |
parent | 382868abfc4b46a0c582ec4bf868719d4e87bbd2 (diff) | |
download | astro-0dd278810e4353799c7239463f156b358ea30871.tar.gz astro-0dd278810e4353799c7239463f156b358ea30871.tar.zst astro-0dd278810e4353799c7239463f156b358ea30871.zip |
Fix VS Code extension (#467)
* chore: astro-languageserver => @astrojs/language-server
* chore: astro-vscode => vscode
* chore: move devDeps to deps
* chore: bump language-server to 0.5.0-next.0
* chore: remove astro-docs
* chore: update changelog
* fix: expose `astro-ls` bin
* fix: vscode extension
* chore: update changelog
Diffstat (limited to 'tools/vscode/src')
-rw-r--r-- | tools/vscode/src/html/autoClose.ts | 89 | ||||
-rw-r--r-- | tools/vscode/src/index.ts | 70 |
2 files changed, 159 insertions, 0 deletions
diff --git a/tools/vscode/src/html/autoClose.ts b/tools/vscode/src/html/autoClose.ts new file mode 100644 index 000000000..a3b90f615 --- /dev/null +++ b/tools/vscode/src/html/autoClose.ts @@ -0,0 +1,89 @@ +// Original source: https://github.com/Microsoft/vscode/blob/master/extensions/html-language-features/client/src/tagClosing.ts + +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { window, workspace, Disposable, TextDocument, Position, SnippetString } from 'vscode'; + +import { TextDocumentContentChangeEvent } from 'vscode-languageserver-protocol'; + +/** */ +export function activateTagClosing( + tagProvider: (document: TextDocument, position: Position) => Thenable<string>, + supportedLanguages: { [id: string]: boolean }, + configName: string +): Disposable { + const disposables: Disposable[] = []; + workspace.onDidChangeTextDocument((event) => onDidChangeTextDocument(event.document, event.contentChanges), null, disposables); + + let isEnabled = false; + updateEnabledState(); + window.onDidChangeActiveTextEditor(updateEnabledState, null, disposables); + + let timeout: NodeJS.Timer | undefined = void 0; + + /** Check if this feature is enabled */ + function updateEnabledState() { + isEnabled = false; + const editor = window.activeTextEditor; + if (!editor) { + return; + } + const document = editor.document; + if (!supportedLanguages[document.languageId]) { + return; + } + if (!workspace.getConfiguration(void 0, document.uri).get<boolean>(configName)) { + return; + } + isEnabled = true; + } + + /** Handle text document changes */ + function onDidChangeTextDocument(document: TextDocument, changes: readonly TextDocumentContentChangeEvent[]) { + if (!isEnabled) { + return; + } + const activeDocument = window.activeTextEditor && window.activeTextEditor.document; + if (document !== activeDocument || changes.length === 0) { + return; + } + if (typeof timeout !== 'undefined') { + clearTimeout(timeout); + } + const lastChange = changes[changes.length - 1]; + const lastCharacter = lastChange.text[lastChange.text.length - 1]; + if (('range' in lastChange && (lastChange.rangeLength ?? 0) > 0) || (lastCharacter !== '>' && lastCharacter !== '/')) { + return; + } + const rangeStart = 'range' in lastChange ? lastChange.range.start : new Position(0, document.getText().length); + const version = document.version; + timeout = setTimeout(() => { + const position = new Position(rangeStart.line, rangeStart.character + lastChange.text.length); + tagProvider(document, position).then((text) => { + if (text && isEnabled) { + const activeEditor = window.activeTextEditor; + if (activeEditor) { + const activeDocument = activeEditor.document; + if (document === activeDocument && activeDocument.version === version) { + const selections = activeEditor.selections; + if (selections.length && selections.some((s) => s.active.isEqual(position))) { + activeEditor.insertSnippet( + new SnippetString(text), + selections.map((s) => s.active) + ); + } else { + activeEditor.insertSnippet(new SnippetString(text), position); + } + } + } + } + }); + timeout = void 0; + }, 100); + } + return Disposable.from(...disposables); +} diff --git a/tools/vscode/src/index.ts b/tools/vscode/src/index.ts new file mode 100644 index 000000000..287e3f119 --- /dev/null +++ b/tools/vscode/src/index.ts @@ -0,0 +1,70 @@ +import * as vscode from 'vscode'; +import * as lsp from 'vscode-languageclient/node.js'; +import { activateTagClosing } from './html/autoClose.js'; + +let docClient: lsp.LanguageClient; + +const TagCloseRequest: lsp.RequestType<lsp.TextDocumentPositionParams, string, any> = new lsp.RequestType('html/tag'); + +/** */ +export async function activate(context: vscode.ExtensionContext) { + docClient = createLanguageService(context, 'doc', 'astro', 'Astro', 6040); + + await docClient.onReady(); +} + +/** */ +function createLanguageService(context: vscode.ExtensionContext, mode: 'doc', id: string, name: string, port: number) { + const { workspace } = vscode; + const serverModule = require.resolve('@astrojs/language-server/bin/server.js'); + const debugOptions = { execArgv: ['--nolazy', '--inspect=' + port] }; + const serverOptions: lsp.ServerOptions = { + run: { module: serverModule, transport: lsp.TransportKind.ipc }, + debug: { + module: serverModule, + transport: lsp.TransportKind.ipc, + options: debugOptions, + }, + }; + const serverInitOptions: any = { + mode: mode, + appRoot: vscode.env.appRoot, + language: vscode.env.language, + }; + const clientOptions: lsp.LanguageClientOptions = { + documentSelector: [{ scheme: 'file', language: 'astro' }], + synchronize: { + configurationSection: ['javascript', 'typescript', 'prettier'], + fileEvents: workspace.createFileSystemWatcher('{**/*.js,**/*.ts}', false, false, false), + }, + initializationOptions: { + ...serverInitOptions, + configuration: { + prettier: workspace.getConfiguration('prettier'), + emmet: workspace.getConfiguration('emmet'), + typescript: workspace.getConfiguration('typescript'), + javascript: workspace.getConfiguration('javascript'), + }, + dontFilterIncompleteCompletions: true, // VSCode filters client side and is smarter at it than us + }, + }; + const client = new lsp.LanguageClient(id, name, serverOptions, clientOptions); + + context.subscriptions.push(client.start()); + + client + .onReady() + .then(() => { + const tagRequestor = (document: vscode.TextDocument, position: vscode.Position) => { + const param = client.code2ProtocolConverter.asTextDocumentPositionParams(document, position); + return client.sendRequest(TagCloseRequest, param); + }; + const disposable = activateTagClosing(tagRequestor, { astro: true }, 'html.autoClosingTags'); + context.subscriptions.push(disposable); + }) + .catch((err) => { + console.error('Astro, unable to load language server.', err); + }); + + return client; +} |