Home

tailwind-ctp-intellisense @master - refs - log -
-
https://git.jolheiser.com/tailwind-ctp-intellisense.git
Tailwind intellisense + Catppuccin
tree log patch
add includeLanguages setting and remove default language client
Brad Cornes <brad@parall.ax>
4 years ago
10 changed files, 175 additions(+), 97 deletions(-)
M package.json -> package.json
diff --git a/package.json b/package.json
index 1e563a3008546894cbba855f1937c547abe85504..916beadc59c8d1402921342a378c73e4831b8d06 100755
--- a/package.json
+++ b/package.json
@@ -55,6 +55,10 @@         "tailwindCSS.emmetCompletions": {
           "type": "boolean",
           "default": false,
           "description": ""
+        },
+        "tailwindCSS.includeLanguages": {
+          "type": "object",
+          "default": {}
         }
       }
     }
M src/extension.ts -> src/extension.ts
diff --git a/src/extension.ts b/src/extension.ts
index 0adfcd1d8374b5cf8dd5dde3b92b091e3caffdf0..2497372df113002f18b7bc1ec6a65a46d054c62e 100755
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -18,10 +18,15 @@   LanguageClientOptions,
   TransportKind,
 } from 'vscode-languageclient'
 import { registerConfigErrorHandler } from './lib/registerConfigErrorHandler'
-import { LANGUAGES } from './lib/languages'
+import { DEFAULT_LANGUAGES } from './lib/languages'
+import isObject from './util/isObject'
+import { dedupe, equal } from './util/array'
+
+const CLIENT_ID = 'tailwindcss-intellisense'
+const CLIENT_NAME = 'Tailwind CSS IntelliSense'
 
-let defaultClient: LanguageClient
 let clients: Map<string, LanguageClient> = new Map()
+let languages: Map<string, string[]> = new Map()
 
 let _sortedWorkspaceFolders: string[] | undefined
 function sortedWorkspaceFolders(): string[] {
@@ -60,48 +65,102 @@   }
   return folder
 }
 
+function getUserLanguages(folder?: WorkspaceFolder): Record<string, string> {
+  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(
-    'lsp-multi-server-example'
-  )
+  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))
+
+      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 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')
-    ) {
+  function bootWorkspaceClient(folder: WorkspaceFolder) {
+    if (clients.has(folder.uri.toString())) {
       return
     }
 
-    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',
+    // 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',
           language,
+          pattern: `${folder.uri.fsPath}/**/*`,
         })),
-        diagnosticCollectionName: 'lsp-multi-server-example',
-        outputChannel: outputChannel,
-      }
-      defaultClient = new LanguageClient(
-        'lsp-multi-server-example',
-        'LSP Multi Server Example',
-        serverOptions,
-        clientOptions
-      )
-      defaultClient.start()
+      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') {
       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.
@@ -111,39 +170,14 @@     }
     // If we have nested workspace folders we only start a server on the outer most workspace folder.
     folder = getOuterMostWorkspaceFolder(folder)
 
-    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
+    if (!languages.has(folder.uri.toString())) {
+      languages.set(
+        folder.uri.toString(),
+        dedupe([...DEFAULT_LANGUAGES, ...Object.keys(getUserLanguages())])
       )
+    }
 
-      client.onReady().then(() => {
-        registerConfigErrorHandler(client)
-      })
-
-      client.start()
-      clients.set(folder.uri.toString(), client)
-    }
+    bootWorkspaceClient(folder)
   }
 
   Workspace.onDidOpenTextDocument(didOpenTextDocument)
@@ -161,9 +195,6 @@ }
 
 export function deactivate(): Thenable<void> {
   let promises: Thenable<void>[] = []
-  if (defaultClient) {
-    promises.push(defaultClient.stop())
-  }
   for (let client of clients.values()) {
     promises.push(client.stop())
   }
M src/lib/languages.ts -> src/lib/languages.ts
diff --git a/src/lib/languages.ts b/src/lib/languages.ts
index 8e1b859d9d8ed6f99e6bdc74ff43d75551faed42..b9238edde46be72d6b7a5db044b063e57666efff 100644
--- a/src/lib/languages.ts
+++ b/src/lib/languages.ts
@@ -1,4 +1,4 @@
-export const LANGUAGES = [
+export const DEFAULT_LANGUAGES = [
   // html
   'aspnetcorerazor',
   'blade',
M src/lsp/providers/completionProvider.ts -> src/lsp/providers/completionProvider.ts
diff --git a/src/lsp/providers/completionProvider.ts b/src/lsp/providers/completionProvider.ts
index f1e9130abb7d978a454c163c3d289daa4184f067..4dc374518e5e6b8e9830031c19a26afb1cbb5cb6 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(doc, params.position) ||
-    isJsContext(doc, params.position)
+    isHtmlContext(state, doc, params.position) ||
+    isJsContext(state, doc, params.position)
   ) {
     return provideClassAttributeCompletions(state, params)
   }
 
-  if (isCssContext(doc, params.position)) {
+  if (isCssContext(state, 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(doc, position)) {
+  if (!isCssContext(state, doc, position)) {
     return null
   }
 
@@ -318,7 +318,7 @@   { position, textDocument }: CompletionParams
 ): CompletionList {
   let doc = state.editor.documents.get(textDocument.uri)
 
-  if (!isCssContext(doc, position)) {
+  if (!isCssContext(state, doc, position)) {
     return null
   }
 
@@ -409,7 +409,7 @@   { position, textDocument }: CompletionParams
 ): CompletionList {
   let doc = state.editor.documents.get(textDocument.uri)
 
-  if (!isCssContext(doc, position)) {
+  if (!isCssContext(state, doc, position)) {
     return null
   }
 
@@ -457,7 +457,7 @@   { position, textDocument }: CompletionParams
 ): CompletionList {
   let doc = state.editor.documents.get(textDocument.uri)
 
-  if (!isCssContext(doc, position)) {
+  if (!isCssContext(state, doc, position)) {
     return null
   }
 
@@ -505,7 +505,7 @@   { position, textDocument }: CompletionParams
 ): CompletionList {
   let doc = state.editor.documents.get(textDocument.uri)
 
-  if (!isCssContext(doc, position)) {
+  if (!isCssContext(state, 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(doc, position)
+  const syntax = isHtmlContext(state, doc, position)
     ? 'html'
-    : isJsContext(doc, position)
+    : isJsContext(state, doc, position)
     ? 'jsx'
     : null
 
M src/lsp/providers/hoverProvider.ts -> src/lsp/providers/hoverProvider.ts
diff --git a/src/lsp/providers/hoverProvider.ts b/src/lsp/providers/hoverProvider.ts
index ce213758a18f24ae45a5f72ea00346e33b91d888..7e27d9ac9833ec166a6332607b38562ec0b03f80 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(doc, position)) return null
+  if (!isCssContext(state, doc, position)) return null
 
   const line = doc.getText({
     start: { line: position.line, character: 0 },
@@ -81,7 +81,11 @@   { textDocument, position }: TextDocumentPositionParams
 ): Hover {
   let doc = state.editor.documents.get(textDocument.uri)
 
-  if (!isHtmlContext(doc, position) && !isJsContext(doc, position)) return null
+  if (
+    !isHtmlContext(state, doc, position) &&
+    !isJsContext(state, doc, position)
+  )
+    return null
 
   let hovered = getClassNameAtPosition(doc, position)
   if (!hovered) return null
@@ -111,7 +115,7 @@   { textDocument, position }: TextDocumentPositionParams
 ): Hover {
   let doc = state.editor.documents.get(textDocument.uri)
 
-  if (!isCssContext(doc, position)) return null
+  if (!isCssContext(state, doc, position)) return null
 
   const classNames = findClassNamesInRange(doc, {
     start: { line: Math.max(position.line - 10, 0), character: 0 },
M src/lsp/server.ts -> src/lsp/server.ts
diff --git a/src/lsp/server.ts b/src/lsp/server.ts
index 3315853a892e72680b786a48e45987b70dc13f58..b28b1bad9d226222f5d9e24472c9d8d698df6187 100644
--- a/src/lsp/server.ts
+++ b/src/lsp/server.ts
@@ -32,7 +32,10 @@ let connection = createConnection(ProposedFeatures.all)
 let documents = new TextDocuments()
 let workspaceFolder: string | null
 
-const defaultSettings: Settings = { emmetCompletions: false }
+const defaultSettings: Settings = {
+  emmetCompletions: false,
+  includeLanguages: {},
+}
 let globalSettings: Settings = defaultSettings
 let documentSettings: Map<string, Settings> = new Map()
 
@@ -53,6 +56,11 @@       connection,
       documents,
       documentSettings,
       globalSettings,
+      userLanguages:
+        params.initializationOptions &&
+        params.initializationOptions.userLanguages
+          ? params.initializationOptions.userLanguages
+          : {},
       capabilities: {
         configuration:
           capabilities.workspace && !!capabilities.workspace.configuration,
M src/lsp/util/css.ts -> src/lsp/util/css.ts
diff --git a/src/lsp/util/css.ts b/src/lsp/util/css.ts
index fb4f90a86a88412678b90e801df0d47764dbe4b1..84b788889c876707b4201ba3d2093b8bb472dd2f 100644
--- a/src/lsp/util/css.ts
+++ b/src/lsp/util/css.ts
@@ -1,5 +1,6 @@
 import { TextDocument, Position } from 'vscode-languageserver'
 import { isInsideTag, isVueDoc, isSvelteDoc } from './html'
+import { State } from './state'
 
 export const CSS_LANGUAGES = [
   'css',
@@ -10,12 +11,20 @@   'scss',
   'stylus',
 ]
 
-function isCssDoc(doc: TextDocument): boolean {
-  return CSS_LANGUAGES.indexOf(doc.languageId) !== -1
+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
 }
 
-export function isCssContext(doc: TextDocument, position: Position): boolean {
-  if (isCssDoc(doc)) {
+export function isCssContext(
+  state: State,
+  doc: TextDocument,
+  position: Position
+): boolean {
+  if (isCssDoc(state, doc)) {
     return true
   }
 
M src/lsp/util/html.ts -> src/lsp/util/html.ts
diff --git a/src/lsp/util/html.ts b/src/lsp/util/html.ts
index d0f5743546bb04975746961bc9419d7fbf35e130..8808141906f9545b6ad1c62f838acf6f52f0f88a 100644
--- a/src/lsp/util/html.ts
+++ b/src/lsp/util/html.ts
@@ -1,4 +1,5 @@
 import { TextDocument, Position } from 'vscode-languageserver'
+import { State } from './state'
 
 export const HTML_LANGUAGES = [
   'aspnetcorerazor',
@@ -27,8 +28,14 @@   'slim',
   'twig',
 ]
 
-export function isHtmlDoc(doc: TextDocument): boolean {
-  return HTML_LANGUAGES.indexOf(doc.languageId) !== -1
+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 isVueDoc(doc: TextDocument): boolean {
@@ -39,13 +46,17 @@ export function isSvelteDoc(doc: TextDocument): boolean {
   return doc.languageId === 'svelte'
 }
 
-export function isHtmlContext(doc: TextDocument, position: Position): boolean {
+export function isHtmlContext(
+  state: State,
+  doc: TextDocument,
+  position: Position
+): boolean {
   let str = doc.getText({
     start: { line: 0, character: 0 },
     end: position,
   })
 
-  if (isHtmlDoc(doc) && !isInsideTag(str, ['script', 'style'])) {
+  if (isHtmlDoc(state, doc) && !isInsideTag(str, ['script', 'style'])) {
     return true
   }
 
M src/lsp/util/js.ts -> src/lsp/util/js.ts
diff --git a/src/lsp/util/js.ts b/src/lsp/util/js.ts
index 48107f079ef9569635bf1f87a246175c41bbad14..8a62a5ff69c8ff4869e8541c6d0eac40a9b1744e 100644
--- a/src/lsp/util/js.ts
+++ b/src/lsp/util/js.ts
@@ -1,5 +1,6 @@
 import { TextDocument, Position } from 'vscode-languageserver'
 import { isHtmlDoc, isInsideTag, isVueDoc, isSvelteDoc } from './html'
+import { State } from './state'
 
 export const JS_LANGUAGES = [
   'javascript',
@@ -8,12 +9,20 @@   'reason',
   'typescriptreact',
 ]
 
-export function isJsDoc(doc: TextDocument): boolean {
-  return JS_LANGUAGES.indexOf(doc.languageId) !== -1
+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 isJsContext(doc: TextDocument, position: Position): boolean {
-  if (isJsDoc(doc)) {
+export function isJsContext(
+  state: State,
+  doc: TextDocument,
+  position: Position
+): boolean {
+  if (isJsDoc(state, doc)) {
     return true
   }
 
@@ -22,7 +31,7 @@     start: { line: 0, character: 0 },
     end: position,
   })
 
-  if (isHtmlDoc(doc) && isInsideTag(str, ['script'])) {
+  if (isHtmlDoc(state, doc) && isInsideTag(str, ['script'])) {
     return true
   }
 
M src/lsp/util/state.ts -> src/lsp/util/state.ts
diff --git a/src/lsp/util/state.ts b/src/lsp/util/state.ts
index 401cedfeb2252fe7752dc2768aa7f880f44c21fa..daaa0501c05f20e0e88dc85c4357533d26e4a74d 100644
--- a/src/lsp/util/state.ts
+++ b/src/lsp/util/state.ts
@@ -18,6 +18,7 @@   connection: Connection
   documents: TextDocuments
   documentSettings: Map<string, Settings>
   globalSettings: Settings
+  userLanguages: Record<string, string>
   capabilities: {
     configuration: boolean
   }
@@ -25,6 +26,7 @@ }
 
 export type Settings = {
   emmetCompletions: boolean
+  includeLanguages: Record<string, string>
 }
 
 export type State = null | {