diff --git a/package.json b/package.json index 916beadc59c8d1402921342a378c73e4831b8d06..1e563a3008546894cbba855f1937c547abe85504 100755 --- a/package.json +++ b/package.json @@ -55,10 +55,6 @@ "tailwindCSS.emmetCompletions": { "type": "boolean", "default": false, "description": "" - }, - "tailwindCSS.includeLanguages": { - "type": "object", - "default": {} } } } diff --git a/src/extension.ts b/src/extension.ts index 2497372df113002f18b7bc1ec6a65a46d054c62e..0adfcd1d8374b5cf8dd5dde3b92b091e3caffdf0 100755 --- a/src/extension.ts +++ b/src/extension.ts @@ -18,15 +18,10 @@ LanguageClientOptions, TransportKind, } from 'vscode-languageclient' import { registerConfigErrorHandler } from './lib/registerConfigErrorHandler' -import { DEFAULT_LANGUAGES } from './lib/languages' -import isObject from './util/isObject' -import { dedupe, equal } from './util/array' +import { LANGUAGES } from './lib/languages' -const CLIENT_ID = 'tailwindcss-intellisense' -const CLIENT_NAME = 'Tailwind CSS IntelliSense' - +let defaultClient: LanguageClient let clients: Map = new Map() -let languages: Map = new Map() let _sortedWorkspaceFolders: string[] | undefined function sortedWorkspaceFolders(): string[] { @@ -65,102 +60,48 @@ } return folder } -function getUserLanguages(folder?: WorkspaceFolder): Record { - const langs = Workspace.getConfiguration('tailwindCSS', folder) - .includeLanguages - return isObject(langs) ? langs : {} -} - export function activate(context: ExtensionContext) { - let module = context.asAbsolutePath(path.join('dist', 'server', 'index.js')) - let outputChannel: OutputChannel = Window.createOutputChannel(CLIENT_ID) - - // TODO: check if the actual language MAPPING changed - // not just the language IDs - // e.g. "plaintext" already exists but you change it from "html" to "css" - Workspace.onDidChangeConfiguration((event) => { - clients.forEach((client, key) => { - const folder = Workspace.getWorkspaceFolder(Uri.parse(key)) + let module = context.asAbsolutePath( + path.join('dist', 'server', 'index.js') + ) + let outputChannel: OutputChannel = Window.createOutputChannel( + 'lsp-multi-server-example' + ) - if (event.affectsConfiguration('tailwindCSS', folder)) { - const userLanguages = getUserLanguages(folder) - if (userLanguages) { - const userLanguageIds = Object.keys(userLanguages) - const newLanguages = dedupe([ - ...DEFAULT_LANGUAGES, - ...userLanguageIds, - ]) - if (!equal(newLanguages, languages.get(folder.uri.toString()))) { - languages.set(folder.uri.toString(), newLanguages) - - if (client) { - clients.delete(folder.uri.toString()) - client.stop() - bootWorkspaceClient(folder) - } - } - } - } - }) - }) - - function bootWorkspaceClient(folder: WorkspaceFolder) { - if (clients.has(folder.uri.toString())) { + function didOpenTextDocument(document: TextDocument): void { + // We are only interested in language mode text + if ( + LANGUAGES.indexOf(document.languageId) === -1 || + (document.uri.scheme !== 'file' && document.uri.scheme !== 'untitled') + ) { return } - // placeholder so we don't boot another server before this one is ready - clients.set(folder.uri.toString(), null) - - let debugOptions = { - execArgv: ['--nolazy', `--inspect=${6011 + clients.size}`], - } - let serverOptions = { - run: { module, transport: TransportKind.ipc }, - debug: { - module, - transport: TransportKind.ipc, - options: debugOptions, - }, - } - let clientOptions: LanguageClientOptions = { - documentSelector: languages - .get(folder.uri.toString()) - .map((language) => ({ - scheme: 'file', + let uri = document.uri + // Untitled files go to a default client. + if (uri.scheme === 'untitled' && !defaultClient) { + let debugOptions = { execArgv: ['--nolazy', '--inspect=6010'] } + let serverOptions = { + run: { module, transport: TransportKind.ipc }, + debug: { module, transport: TransportKind.ipc, options: debugOptions }, + } + let clientOptions: LanguageClientOptions = { + documentSelector: LANGUAGES.map((language) => ({ + scheme: 'untitled', language, - pattern: `${folder.uri.fsPath}/**/*`, })), - diagnosticCollectionName: CLIENT_ID, - workspaceFolder: folder, - outputChannel: outputChannel, - middleware: {}, - initializationOptions: { - userLanguages: getUserLanguages(folder), - }, - } - let client = new LanguageClient( - CLIENT_ID, - CLIENT_NAME, - serverOptions, - clientOptions - ) - - client.onReady().then(() => { - registerConfigErrorHandler(client) - }) - - client.start() - clients.set(folder.uri.toString(), client) - } - - function didOpenTextDocument(document: TextDocument): void { - // We are only interested in language mode text - if (document.uri.scheme !== 'file') { + diagnosticCollectionName: 'lsp-multi-server-example', + outputChannel: outputChannel, + } + defaultClient = new LanguageClient( + 'lsp-multi-server-example', + 'LSP Multi Server Example', + serverOptions, + clientOptions + ) + defaultClient.start() return } - - let uri = document.uri let folder = Workspace.getWorkspaceFolder(uri) // Files outside a folder can't be handled. This might depend on the language. // Single file languages like JSON might handle files outside the workspace folders. @@ -170,14 +111,39 @@ } // If we have nested workspace folders we only start a server on the outer most workspace folder. folder = getOuterMostWorkspaceFolder(folder) - if (!languages.has(folder.uri.toString())) { - languages.set( - folder.uri.toString(), - dedupe([...DEFAULT_LANGUAGES, ...Object.keys(getUserLanguages())]) + if (!clients.has(folder.uri.toString())) { + let debugOptions = { + execArgv: ['--nolazy', `--inspect=${6011 + clients.size}`], + } + let serverOptions = { + run: { module, transport: TransportKind.ipc }, + debug: { module, transport: TransportKind.ipc, options: debugOptions }, + } + let clientOptions: LanguageClientOptions = { + documentSelector: LANGUAGES.map((language) => ({ + scheme: 'file', + language, + pattern: `${folder.uri.fsPath}/**/*`, + })), + diagnosticCollectionName: 'lsp-multi-server-example', + workspaceFolder: folder, + outputChannel: outputChannel, + middleware: {}, + } + let client = new LanguageClient( + 'lsp-multi-server-example', + 'LSP Multi Server Example', + serverOptions, + clientOptions ) + + client.onReady().then(() => { + registerConfigErrorHandler(client) + }) + + client.start() + clients.set(folder.uri.toString(), client) } - - bootWorkspaceClient(folder) } Workspace.onDidOpenTextDocument(didOpenTextDocument) @@ -195,6 +161,9 @@ } export function deactivate(): Thenable { let promises: Thenable[] = [] + if (defaultClient) { + promises.push(defaultClient.stop()) + } for (let client of clients.values()) { promises.push(client.stop()) } diff --git a/src/lib/languages.ts b/src/lib/languages.ts index b9238edde46be72d6b7a5db044b063e57666efff..8e1b859d9d8ed6f99e6bdc74ff43d75551faed42 100644 --- a/src/lib/languages.ts +++ b/src/lib/languages.ts @@ -1,4 +1,4 @@ -export const DEFAULT_LANGUAGES = [ +export const LANGUAGES = [ // html 'aspnetcorerazor', 'blade', diff --git a/src/lsp/providers/completionProvider.ts b/src/lsp/providers/completionProvider.ts index 4dc374518e5e6b8e9830031c19a26afb1cbb5cb6..f1e9130abb7d978a454c163c3d289daa4184f067 100644 --- a/src/lsp/providers/completionProvider.ts +++ b/src/lsp/providers/completionProvider.ts @@ -191,13 +191,13 @@ ): CompletionList { let doc = state.editor.documents.get(params.textDocument.uri) if ( - isHtmlContext(state, doc, params.position) || - isJsContext(state, doc, params.position) + isHtmlContext(doc, params.position) || + isJsContext(doc, params.position) ) { return provideClassAttributeCompletions(state, params) } - if (isCssContext(state, doc, params.position)) { + if (isCssContext(doc, params.position)) { return provideAtApplyCompletions(state, params) } @@ -210,7 +210,7 @@ { position, textDocument }: CompletionParams ): CompletionList { let doc = state.editor.documents.get(textDocument.uri) - if (!isCssContext(state, doc, position)) { + if (!isCssContext(doc, position)) { return null } @@ -318,7 +318,7 @@ { position, textDocument }: CompletionParams ): CompletionList { let doc = state.editor.documents.get(textDocument.uri) - if (!isCssContext(state, doc, position)) { + if (!isCssContext(doc, position)) { return null } @@ -409,7 +409,7 @@ { position, textDocument }: CompletionParams ): CompletionList { let doc = state.editor.documents.get(textDocument.uri) - if (!isCssContext(state, doc, position)) { + if (!isCssContext(doc, position)) { return null } @@ -457,7 +457,7 @@ { position, textDocument }: CompletionParams ): CompletionList { let doc = state.editor.documents.get(textDocument.uri) - if (!isCssContext(state, doc, position)) { + if (!isCssContext(doc, position)) { return null } @@ -505,7 +505,7 @@ { position, textDocument }: CompletionParams ): CompletionList { let doc = state.editor.documents.get(textDocument.uri) - if (!isCssContext(state, doc, position)) { + if (!isCssContext(doc, position)) { return null } @@ -600,9 +600,9 @@ if (settings.emmetCompletions !== true) return null let doc = state.editor.documents.get(textDocument.uri) - const syntax = isHtmlContext(state, doc, position) + const syntax = isHtmlContext(doc, position) ? 'html' - : isJsContext(state, doc, position) + : isJsContext(doc, position) ? 'jsx' : null diff --git a/src/lsp/providers/hoverProvider.ts b/src/lsp/providers/hoverProvider.ts index 7e27d9ac9833ec166a6332607b38562ec0b03f80..ce213758a18f24ae45a5f72ea00346e33b91d888 100644 --- a/src/lsp/providers/hoverProvider.ts +++ b/src/lsp/providers/hoverProvider.ts @@ -27,7 +27,7 @@ { textDocument, position }: TextDocumentPositionParams ): Hover { let doc = state.editor.documents.get(textDocument.uri) - if (!isCssContext(state, doc, position)) return null + if (!isCssContext(doc, position)) return null const line = doc.getText({ start: { line: position.line, character: 0 }, @@ -81,11 +81,7 @@ { textDocument, position }: TextDocumentPositionParams ): Hover { let doc = state.editor.documents.get(textDocument.uri) - if ( - !isHtmlContext(state, doc, position) && - !isJsContext(state, doc, position) - ) - return null + if (!isHtmlContext(doc, position) && !isJsContext(doc, position)) return null let hovered = getClassNameAtPosition(doc, position) if (!hovered) return null @@ -115,7 +111,7 @@ { textDocument, position }: TextDocumentPositionParams ): Hover { let doc = state.editor.documents.get(textDocument.uri) - if (!isCssContext(state, doc, position)) return null + if (!isCssContext(doc, position)) return null const classNames = findClassNamesInRange(doc, { start: { line: Math.max(position.line - 10, 0), character: 0 }, diff --git a/src/lsp/server.ts b/src/lsp/server.ts index b28b1bad9d226222f5d9e24472c9d8d698df6187..3315853a892e72680b786a48e45987b70dc13f58 100644 --- a/src/lsp/server.ts +++ b/src/lsp/server.ts @@ -32,10 +32,7 @@ let connection = createConnection(ProposedFeatures.all) let documents = new TextDocuments() let workspaceFolder: string | null -const defaultSettings: Settings = { - emmetCompletions: false, - includeLanguages: {}, -} +const defaultSettings: Settings = { emmetCompletions: false } let globalSettings: Settings = defaultSettings let documentSettings: Map = new Map() @@ -56,11 +53,6 @@ connection, documents, documentSettings, globalSettings, - userLanguages: - params.initializationOptions && - params.initializationOptions.userLanguages - ? params.initializationOptions.userLanguages - : {}, capabilities: { configuration: capabilities.workspace && !!capabilities.workspace.configuration, diff --git a/src/lsp/util/css.ts b/src/lsp/util/css.ts index 84b788889c876707b4201ba3d2093b8bb472dd2f..fb4f90a86a88412678b90e801df0d47764dbe4b1 100644 --- a/src/lsp/util/css.ts +++ b/src/lsp/util/css.ts @@ -1,6 +1,5 @@ import { TextDocument, Position } from 'vscode-languageserver' import { isInsideTag, isVueDoc, isSvelteDoc } from './html' -import { State } from './state' export const CSS_LANGUAGES = [ 'css', @@ -11,20 +10,12 @@ 'scss', 'stylus', ] -function isCssDoc(state: State, doc: TextDocument): boolean { - const userCssLanguages = Object.keys( - state.editor.userLanguages - ).filter((lang) => CSS_LANGUAGES.includes(state.editor.userLanguages[lang])) - - return [...CSS_LANGUAGES, ...userCssLanguages].indexOf(doc.languageId) !== -1 +function isCssDoc(doc: TextDocument): boolean { + return CSS_LANGUAGES.indexOf(doc.languageId) !== -1 } -export function isCssContext( - state: State, - doc: TextDocument, - position: Position -): boolean { - if (isCssDoc(state, doc)) { +export function isCssContext(doc: TextDocument, position: Position): boolean { + if (isCssDoc(doc)) { return true } diff --git a/src/lsp/util/html.ts b/src/lsp/util/html.ts index 8808141906f9545b6ad1c62f838acf6f52f0f88a..d0f5743546bb04975746961bc9419d7fbf35e130 100644 --- a/src/lsp/util/html.ts +++ b/src/lsp/util/html.ts @@ -1,5 +1,4 @@ import { TextDocument, Position } from 'vscode-languageserver' -import { State } from './state' export const HTML_LANGUAGES = [ 'aspnetcorerazor', @@ -28,14 +27,8 @@ 'slim', 'twig', ] -export function isHtmlDoc(state: State, doc: TextDocument): boolean { - const userHtmlLanguages = Object.keys( - state.editor.userLanguages - ).filter((lang) => HTML_LANGUAGES.includes(state.editor.userLanguages[lang])) - - return ( - [...HTML_LANGUAGES, ...userHtmlLanguages].indexOf(doc.languageId) !== -1 - ) +export function isHtmlDoc(doc: TextDocument): boolean { + return HTML_LANGUAGES.indexOf(doc.languageId) !== -1 } export function isVueDoc(doc: TextDocument): boolean { @@ -46,17 +39,13 @@ export function isSvelteDoc(doc: TextDocument): boolean { return doc.languageId === 'svelte' } -export function isHtmlContext( - state: State, - doc: TextDocument, - position: Position -): boolean { +export function isHtmlContext(doc: TextDocument, position: Position): boolean { let str = doc.getText({ start: { line: 0, character: 0 }, end: position, }) - if (isHtmlDoc(state, doc) && !isInsideTag(str, ['script', 'style'])) { + if (isHtmlDoc(doc) && !isInsideTag(str, ['script', 'style'])) { return true } diff --git a/src/lsp/util/js.ts b/src/lsp/util/js.ts index 8a62a5ff69c8ff4869e8541c6d0eac40a9b1744e..48107f079ef9569635bf1f87a246175c41bbad14 100644 --- a/src/lsp/util/js.ts +++ b/src/lsp/util/js.ts @@ -1,6 +1,5 @@ import { TextDocument, Position } from 'vscode-languageserver' import { isHtmlDoc, isInsideTag, isVueDoc, isSvelteDoc } from './html' -import { State } from './state' export const JS_LANGUAGES = [ 'javascript', @@ -9,20 +8,12 @@ 'reason', 'typescriptreact', ] -export function isJsDoc(state: State, doc: TextDocument): boolean { - const userJsLanguages = Object.keys( - state.editor.userLanguages - ).filter((lang) => JS_LANGUAGES.includes(state.editor.userLanguages[lang])) - - return [...JS_LANGUAGES, ...userJsLanguages].indexOf(doc.languageId) !== -1 +export function isJsDoc(doc: TextDocument): boolean { + return JS_LANGUAGES.indexOf(doc.languageId) !== -1 } -export function isJsContext( - state: State, - doc: TextDocument, - position: Position -): boolean { - if (isJsDoc(state, doc)) { +export function isJsContext(doc: TextDocument, position: Position): boolean { + if (isJsDoc(doc)) { return true } @@ -31,7 +22,7 @@ start: { line: 0, character: 0 }, end: position, }) - if (isHtmlDoc(state, doc) && isInsideTag(str, ['script'])) { + if (isHtmlDoc(doc) && isInsideTag(str, ['script'])) { return true } diff --git a/src/lsp/util/state.ts b/src/lsp/util/state.ts index daaa0501c05f20e0e88dc85c4357533d26e4a74d..401cedfeb2252fe7752dc2768aa7f880f44c21fa 100644 --- a/src/lsp/util/state.ts +++ b/src/lsp/util/state.ts @@ -18,7 +18,6 @@ connection: Connection documents: TextDocuments documentSettings: Map globalSettings: Settings - userLanguages: Record capabilities: { configuration: boolean } @@ -26,7 +25,6 @@ } export type Settings = { emmetCompletions: boolean - includeLanguages: Record } export type State = null | {