summaryrefslogtreecommitdiff
path: root/tools/vscode/src
diff options
context:
space:
mode:
authorGravatar Nate Moore <natemoo-re@users.noreply.github.com> 2021-06-16 13:20:29 -0500
committerGravatar GitHub <noreply@github.com> 2021-06-16 13:20:29 -0500
commit0dd278810e4353799c7239463f156b358ea30871 (patch)
tree964a62a13467950509b446ebeaa9b668e5874665 /tools/vscode/src
parent382868abfc4b46a0c582ec4bf868719d4e87bbd2 (diff)
downloadastro-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.ts89
-rw-r--r--tools/vscode/src/index.ts70
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;
+}