diff options
Diffstat (limited to 'vscode/packages/client/src')
-rw-r--r-- | vscode/packages/client/src/features/defaultSettings.ts | 30 | ||||
-rw-r--r-- | vscode/packages/client/src/html/autoClose.ts | 108 | ||||
-rw-r--r-- | vscode/packages/client/src/index.ts | 73 |
3 files changed, 138 insertions, 73 deletions
diff --git a/vscode/packages/client/src/features/defaultSettings.ts b/vscode/packages/client/src/features/defaultSettings.ts deleted file mode 100644 index ec3de70a1..000000000 --- a/vscode/packages/client/src/features/defaultSettings.ts +++ /dev/null @@ -1,30 +0,0 @@ -import * as vscode from 'vscode'; - -export async function activate() { - onConfigUpdated(); - - vscode.workspace.onDidChangeConfiguration(onConfigUpdated); - - function onConfigUpdated() { - const astro = vscode.extensions.getExtension('skypack.astro'); - if (!astro) { - return; - } - const emmet = vscode.extensions.getExtension('vscode.emmet'); - if (!emmet) { - return; - } - - const emmetIncludeLanguages = getEmmetIncludeLanguages(); - if (emmetIncludeLanguages && emmetIncludeLanguages['astro']) { - return; - } - setEmmetIncludeLanguages({ ...emmetIncludeLanguages, astro: 'html' }); - } - function getEmmetIncludeLanguages() { - return vscode.workspace.getConfiguration('emmet').get<Record<string, string>>('includeLanguages'); - } - function setEmmetIncludeLanguages(value: Record<string, string>) { - return vscode.workspace.getConfiguration('emmet').set('includeLanguages', value); - } -} diff --git a/vscode/packages/client/src/html/autoClose.ts b/vscode/packages/client/src/html/autoClose.ts new file mode 100644 index 000000000..0dbce66c8 --- /dev/null +++ b/vscode/packages/client/src/html/autoClose.ts @@ -0,0 +1,108 @@ +// 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/vscode/packages/client/src/index.ts b/vscode/packages/client/src/index.ts index c7233892b..9ed016eb0 100644 --- a/vscode/packages/client/src/index.ts +++ b/vscode/packages/client/src/index.ts @@ -1,21 +1,22 @@ import * as path from 'path'; import * as vscode from 'vscode'; import * as lsp from 'vscode-languageclient/node'; - -import * as defaultSettings from './features/defaultSettings.js'; +import { activateTagClosing } from './html/autoClose'; 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); - defaultSettings.activate(); - await docClient.onReady(); - startEmbeddedLanguageServices(); } +/** */ function createLanguageService(context: vscode.ExtensionContext, mode: 'doc', id: string, name: string, port: number) { + const { workspace } = vscode; const serverModule = context.asAbsolutePath(path.join('dist', 'server.js')); const debugOptions = { execArgv: ['--nolazy', '--inspect=' + port] }; const serverOptions: lsp.ServerOptions = { @@ -33,47 +34,33 @@ function createLanguageService(context: vscode.ExtensionContext, mode: 'doc', id }; const clientOptions: lsp.LanguageClientOptions = { documentSelector: [{ scheme: 'file', language: 'astro' }], - initializationOptions: serverInitOptions, + 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()); - return client; -} - -async function startEmbeddedLanguageServices() { - const ts = vscode.extensions.getExtension('vscode.typescript-language-features'); - const css = vscode.extensions.getExtension('vscode.css-language-features'); - const html = vscode.extensions.getExtension('vscode.html-language-features'); - - if (ts && !ts.isActive) { - await ts.activate(); - } - if (css && !css.isActive) { - await css.activate(); - } - if (html && !html.isActive) { - await html.activate(); - } + context.subscriptions.push(client.start()); - /* from html-language-features */ - const EMPTY_ELEMENTS: string[] = ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr']; - vscode.languages.setLanguageConfiguration('astro', { - indentationRules: { - increaseIndentPattern: /<(?!\?|(?:area|base|br|col|frame|hr|html|img|input|link|meta|param)\b|[^>]*\/>)([-_\.A-Za-z0-9]+)(?=\s|>)\b[^>]*>(?!.*<\/\1>)|<!--(?!.*-->)|\{[^}"']*$/, - decreaseIndentPattern: /^\s*(<\/(?!html)[-_\.A-Za-z0-9]+\b[^>]*>|-->|\})/, - }, - wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\@\$\^\&\*\(\)\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\s]+)/g, - onEnterRules: [ - { - beforeText: new RegExp(`<(?!(?:${EMPTY_ELEMENTS.join('|')}))([_:\\w][_:\\w-.\\d]*)([^/>]*(?!/)>)[^<]*$`, 'i'), - afterText: /^<\/([_:\w][_:\w-.\d]*)\s*>/i, - action: { indentAction: vscode.IndentAction.IndentOutdent }, - }, - { - beforeText: new RegExp(`<(?!(?:${EMPTY_ELEMENTS.join('|')}))(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$`, 'i'), - action: { indentAction: vscode.IndentAction.Indent }, - }, - ], + 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); }); + + return client; } |