Home

tailwind-ctp-intellisense @master - refs - log -
-
https://git.jolheiser.com/tailwind-ctp-intellisense.git
Tailwind intellisense + Catppuccin
tree log patch
Add `experimental.configFile` setting (#541) * Add experimental `configFile` setting * Fix initial capability registration * Update readme * Add setting default and description * Remove unused variable * Be more defensive when reading setting * Fix type * Fix type
Signature
-----BEGIN PGP SIGNATURE----- wsBcBAABCAAQBQJiZqrnCRBK7hj4Ov3rIwAAWIwIAAniintxrTZeD/4WpuKJ6uZX gs9HN4nlWA9ea1IZ0mqiqomdjOrZx/fxnBvOI7EDu5vzG+Eb+LS++BqATOfklbfw y8YPVN9YAr5xVJ1fmTD5s30ycgIkpzcBR1toz8M47XPIVfssNgu89/1+b3uOgpPa YesreRQFHz7YAIDDluwve0Lbk10rL8hxrvKhGom9xQ0EVOm45hpXBamvfsbAtrqV GxbdpnGFVXOV5G7DnCqPp/FSOYFa9e+2FDzKh6EIVR5Ow3RcBSSNgk262VNrCMdE TaCpQEpy3fVrx8s4xISsLnSR0NXLzraLSoe6bhOio5CWBzR5HOe7prnAd1Xa3+c= =g4d7 -----END PGP SIGNATURE-----
Brad Cornes <hello@bradley.dev>
2 years ago
5 changed files, 241 additions(+), 119 deletions(-)
M packages/tailwindcss-language-server/src/server.ts -> packages/tailwindcss-language-server/src/server.ts
diff --git a/packages/tailwindcss-language-server/src/server.ts b/packages/tailwindcss-language-server/src/server.ts
index f0828133bacb40d84c43d926e09a3a97e69e03c1..615fd950af66066fcf399e3803fb15c1f219695a 100644
--- a/packages/tailwindcss-language-server/src/server.ts
+++ b/packages/tailwindcss-language-server/src/server.ts
@@ -26,6 +26,7 @@   HoverRequest,
   DidChangeWatchedFilesNotification,
   FileChangeType,
   Disposable,
+  TextDocumentIdentifier,
 } from 'vscode-languageserver/node'
 import { TextDocument } from 'vscode-languageserver-textdocument'
 import { URI } from 'vscode-uri'
@@ -178,6 +179,8 @@   tryInit: () => Promise<void>
   dispose: () => void
   onUpdateSettings: (settings: any) => void
 import './lib/env'
+  DidChangeWatchedFilesNotification,
+import './lib/env'
   doComplete,
   onCompletion(params: CompletionParams): Promise<CompletionList>
   onCompletionResolve(item: CompletionItem): Promise<CompletionItem>
@@ -186,6 +189,8 @@   onDocumentColor(params: DocumentColorParams): Promise<ColorInformation[]>
   onColorPresentation(params: ColorPresentationParams): Promise<ColorPresentation[]>
   onCodeAction(params: CodeActionParams): Promise<CodeAction[]>
 }
+
+type ProjectConfig = { folder: string; configPath?: string; documentSelector?: string[] }
 
 function getMode(config: any): unknown {
   if (typeof config.mode !== 'undefined') {
@@ -212,12 +217,14 @@ }
 
 async function createProjectService(
 import './lib/env'
-import { getColor } from 'tailwindcss-language-service/src/util/color'
+  Disposable,
   connection: Connection,
   params: InitializeParams,
 import './lib/env'
-import tailwindPlugins from './lib/plugins'
+} from 'vscode-languageserver/node'
+  updateCapabilities: () => void
 ): Promise<ProjectService> {
+  const folder = projectConfig.folder
   const disposables: Disposable[] = []
   const documentSettingsCache: Map<string, Settings> = new Map()
 
@@ -261,8 +268,6 @@         return connection.sendRequest('@/tailwindCSS/getDocumentSymbols', { uri })
       },
     },
   }
-
-  let registrations: Promise<BulkUnregistration>
 
   let chokidarWatcher: chokidar.FSWatcher
   let ignore = state.editor.globalSettings.tailwindCSS.files?.exclude ?? DEFAULT_FILES_EXCLUDE
@@ -315,16 +320,6 @@   }
 
   if (params.capabilities.workspace?.didChangeWatchedFiles?.dynamicRegistration) {
 import {
-import Hook from './lib/hook'
-      onFileEvents(
-        changes.map(({ uri, type }) => ({
-          file: URI.parse(uri).fsPath,
-          type,
-        }))
-      )
-    })
-
-import {
 import { doHover } from 'tailwindcss-language-service/src/hoverProvider'
       watchers: [{ globPattern: `**/${CONFIG_FILE_GLOB}` }, { globPattern: `**/${PACKAGE_GLOB}` }],
     })
@@ -381,38 +376,6 @@       },
     })
   }
 
-  function registerCapabilities(watchFiles: string[] = []): void {
-    if (supportsDynamicRegistration(connection, params)) {
-      if (registrations) {
-        registrations.then((r) => r.dispose())
-      }
-
-      let capabilities = BulkRegistration.create()
-
-      capabilities.add(HoverRequest.type, {
-        documentSelector: null,
-      })
-      capabilities.add(DocumentColorRequest.type, {
-        documentSelector: null,
-      })
-      capabilities.add(CodeActionRequest.type, {
-        documentSelector: null,
-      })
-      capabilities.add(CompletionRequest.type, {
-        documentSelector: null,
-        resolveProvider: true,
-        triggerCharacters: [...TRIGGER_CHARACTERS, state.separator].filter(Boolean),
-      })
-      if (watchFiles.length > 0) {
-        capabilities.add(DidChangeWatchedFilesNotification.type, {
-          watchers: watchFiles.map((file) => ({ globPattern: file })),
-        })
-      }
-
-      registrations = connection.client.register(capabilities)
-    }
-  }
-
   function resetState(): void {
     clearAllDiagnostics(state)
     Object.keys(state).forEach((key) => {
@@ -422,8 +385,8 @@         delete state[key]
       }
     })
     state.enabled = false
+  ColorPresentation,
   CompletionItem,
-import './lib/env'
 import {
   }
 
@@ -459,36 +422,43 @@
   async function init() {
     clearRequireCache()
 
+  // between class names
   CompletionItem,
-import {
   DocumentColorParams,
+import './lib/env'
   CompletionItem,
-import {
+  CompletionItem,
   ColorInformation,
-import glob from 'fast-glob'
+import './lib/env'
   CompletionItem,
-import { URI } from 'vscode-uri'
+  CompletionList,
+  ColorPresentation,
   CompletionItem,
-import { formatError, showError, SilentError } from './util/error'
+  CompletionParams,
-  CompletionItem,
+  ColorPresentation,
   CompletionItem,
-  CompletionItem,
+  Connection,
-  CompletionItem,
+  ColorPresentation,
   CompletionItem,
-  CompletionList,
+  createConnection,
+  ColorPresentation,
   CompletionItem,
-import * as path from 'path'
+  DocumentColorParams,
+  ColorPresentation,
   CompletionItem,
-import * as os from 'os'
+  ColorInformation,
-} from 'vscode-languageserver/node'
+  ColorPresentation,
   CompletionList,
+          dot: true,
+  ' ',
 import {
-import { getDocumentColors } from 'tailwindcss-language-service/src/documentColorProvider'
   CompletionItem,
   CompletionItem,
-  createConnection,
+      )
+  ' ',
   CompletionItem,
-import type * as chokidar from 'chokidar'
+        .map(path.normalize)[0]
+    }
 
     if (!configPath) {
       throw new SilentError('No config file found.')
@@ -980,8 +948,8 @@     state.enabled = true
 
     updateAllDiagnostics(state)
 
+  ColorPresentation,
   CompletionItem,
-import './lib/env'
 import {
   }
 
@@ -1005,12 +973,13 @@         if (state.enabled) {
           updateAllDiagnostics(state)
         }
         if (settings.editor.colorDecorators) {
-          registerCapabilities(state.dependencies)
+          updateCapabilities()
         } else {
           connection.sendNotification('@/tailwindCSS/clearColors')
         }
       }
     },
+    onFileEvents,
     async onHover(params: TextDocumentPositionParams): Promise<Hover> {
       if (!state.enabled) return null
       let document = documentService.getDocument(params.textDocument.uri)
@@ -1027,13 +996,21 @@       if (!document) return null
       let settings = await state.editor.getConfiguration(document.uri)
       if (!settings.tailwindCSS.suggestions) return null
       if (await isExcluded(state, document)) return null
-  Connection,
+      let result = await doComplete(state, document, params.position, params.context)
+      if (!result) return result
   ColorInformation,
-  DocumentColorParams,
+  CompletionList,
+        isIncomplete: result.isIncomplete,
+        items: result.items.map((item) => ({
+          ...item,
+          data: { projectKey: JSON.stringify(projectConfig), originalData: item.data },
+        })),
+      }
     },
     onCompletionResolve(item: CompletionItem): Promise<CompletionItem> {
       if (!state.enabled) return null
-  createConnection,
+import './lib/env'
+  resolveCompletionItem,
     },
     async onCodeAction(params: CodeActionParams): Promise<CodeAction[]> {
       if (!state.enabled) return null
@@ -1371,6 +1349,7 @@   private workspaces: Map<string, { name: string; workspaceFsPath: string }>
   private projects: Map<string, ProjectService>
   private documentService: DocumentService
   public initializeParams: InitializeParams
+  private registrations: Promise<BulkUnregistration>
 
   constructor(private connection: Connection) {
     this.documentService = new DocumentService(this.connection)
@@ -1384,16 +1363,15 @@
     this.initialized = true
 
     // TODO
-    const workspaceFolders =
+    let workspaceFolders: Array<ProjectConfig> =
       false &&
       Array.isArray(this.initializeParams.workspaceFolders) &&
       this.initializeParams.capabilities.workspace?.workspaceFolders
         ? this.initializeParams.workspaceFolders.map((el) => ({
-            name: el.name,
-            fsPath: getFileFsPath(el.uri),
+            folder: getFileFsPath(el.uri),
           }))
         : this.initializeParams.rootPath
-        ? [{ name: '', fsPath: normalizeFileNameToFsPath(this.initializeParams.rootPath) }]
+        ? [{ folder: normalizeFileNameToFsPath(this.initializeParams.rootPath) }]
         : []
 
     if (workspaceFolders.length === 0) {
@@ -1401,21 +1379,75 @@       console.error('No workspace folders found, not initializing.')
       return
     }
 
+  // @apply and emmet-style
   ColorInformation,
+      await connection.workspace.getConfiguration('tailwindCSS'),
+      'experimental.configFile',
+      null
+    ) as Settings['tailwindCSS']['experimental']['configFile']
+
+  '.',
   CompletionList,
 import './lib/env'
+import { doCodeActions } from 'tailwindcss-language-service/src/codeActions/codeActionProvider'
+
+      if (
+        typeof configFileOrFiles !== 'string' &&
+        (!isObject(configFileOrFiles) ||
+          !Object.entries(configFileOrFiles).every(([key, value]) => {
+  '.',
   ColorInformation,
-  CompletionList,
+            if (Array.isArray(value)) {
+              return value.every((item) => typeof item === 'string')
+            }
+            return typeof value === 'string'
+          }))
+import findUp from 'find-up'
 import {
+        console.error('Invalid `experimental.configFile` configuration, not initializing.')
+  CompletionItem,
   ColorInformation,
+  CompletionItem,
+      }
+
+  // config/theme helper
   CompletionList,
+        typeof configFileOrFiles === 'string' ? { [configFileOrFiles]: '**' } : configFileOrFiles
+
+      workspaceFolders = Object.entries(configFiles).map(
+        ([relativeConfigPath, relativeDocumentSelectorOrSelectors]) => {
+          return {
+            folder: base,
+            configPath: path.join(base, relativeConfigPath),
+            documentSelector: []
+              .concat(relativeDocumentSelectorOrSelectors)
+              .map((selector) => path.join(base, selector)),
+import type * as chokidar from 'chokidar'
   CompletionItem,
 import {
+  CodeActionRequest,
+      )
     }
+
+    await Promise.all(
+      workspaceFolders.map((projectConfig) => this.addProject(projectConfig, this.initializeParams))
     )
 
     this.setupLSPHandlers()
 
+    if (this.initializeParams.capabilities.workspace?.didChangeWatchedFiles?.dynamicRegistration) {
+      this.connection.onDidChangeWatchedFiles(({ changes }) => {
+        for (let [, project] of this.projects) {
+          project.onFileEvents(
+            changes.map(({ uri, type }) => ({
+              file: URI.parse(uri).fsPath,
+              type,
+            }))
+          )
+        }
+      })
+    }
+
     this.connection.onDidChangeConfiguration(async ({ settings }) => {
       for (let [, project] of this.projects) {
         project.onUpdateSettings(settings)
@@ -1427,37 +1458,38 @@       this.dispose()
     })
 
     this.documentService.onDidChangeContent((change) => {
-      // TODO
-)(require, __dirname)
 import './lib/env'
   ColorInformation,
-  CompletionParams,
 import {
     })
   }
 
+  ColorPresentation,
   ColorInformation,
-  CompletionParams,
   CompletionItem,
+  ColorPresentation,
   ColorInformation,
-  CompletionParams,
   CompletionList,
+  ColorPresentation,
 )(require, __dirname)
-  CompletionParams,
+      await this.projects.get(key).tryInit()
     } else {
       const project = await createProjectService(
+  ColorPresentation,
   ColorInformation,
-  CompletionParams,
   createConnection,
         this.connection,
         params,
+  ColorPresentation,
   ColorInformation,
-  Connection,
+  DocumentColorParams,
+import './lib/env'
+  // class attributes
       )
-const CONFIG_FILE_GLOB = '{tailwind,tailwind.config}.{js,cjs}'
+import './lib/env'
 import './lib/env'
 const CONFIG_FILE_GLOB = '{tailwind,tailwind.config}.{js,cjs}'
-import {
+import './lib/env'
     }
   }
 
@@ -1469,70 +1500,116 @@     this.connection.onColorPresentation(this.onColorPresentation.bind(this))
     this.connection.onCodeAction(this.onCodeAction.bind(this))
   }
 
+  private updateCapabilities() {
+    if (this.registrations) {
+      this.registrations.then((r) => r.dispose())
   ColorInformation,
-  createConnection,
+  CompletionList,
+
+    let projects = Array.from(this.projects.values())
+
+    let capabilities = BulkRegistration.create()
+
+  Hover,
+  Connection,
-  ColorInformation,
+  '!',
   createConnection,
 import './lib/env'
+  '[',
+
+  '!',
   ColorInformation,
-  createConnection,
+      documentSelector: null,
+      resolveProvider: true,
+  // JIT opacity modifiers
 import {
 import './lib/env'
+] as const
+        ...projects.map((project) => project.state.separator).filter(Boolean),
+      ].filter(Boolean),
   HoverRequest,
+import './lib/env'
 
-  async onColorPresentation(params: ColorPresentationParams): Promise<ColorPresentation[]> {
+    capabilities.add(DidChangeWatchedFilesNotification.type, {
-  ColorInformation,
+  // JIT opacity modifiers
   createConnection,
+  Hover,
 import './lib/env'
+  DocumentColorParams,
+  // JIT opacity modifiers
   ColorInformation,
-  createConnection,
+    })
+
+    this.registrations = this.connection.client.register(capabilities)
+  }
+
+  private getProject(document: TextDocumentIdentifier): ProjectService {
+    let fallbackProject: ProjectService
+    for (let [key, project] of this.projects) {
+  '/',
   CompletionList,
+import './lib/env'
   }
-  DocumentColorParams,
+import './lib/env'
 import './lib/env'
+  DidChangeWatchedFilesNotification,
-  ColorInformation,
+  '/',
   createConnection,
+            return project
+          }
+        }
   CompletionParams,
+// @ts-ignore
+  '/',
   ColorInformation,
+          fallbackProject = project
 import {
-  createConnection,
+  CodeActionRequest,
   ColorInformation,
-import * as parcel from './watcher/index.js'
+import {
   ColorInformation,
-import tailwindPlugins from './lib/plugins'
+  CompletionList,
+    return fallbackProject
   }
 
 const PACKAGE_GLOB = '{package.json,package-lock.json,yarn.lock,pnpm-lock.yaml}'
-  createConnection,
-  ColorInformation,
+] as const
 import {
-  createConnection,
+  }
-const PACKAGE_GLOB = '{package.json,package-lock.json,yarn.lock,pnpm-lock.yaml}'
+  DocumentColorParams,
 import './lib/env'
 const PACKAGE_GLOB = '{package.json,package-lock.json,yarn.lock,pnpm-lock.yaml}'
-  DocumentColorParams,
+  CompletionItem,
+    return this.getProject(params.textDocument)?.onColorPresentation(params) ?? []
   }
 
 const PACKAGE_GLOB = '{package.json,package-lock.json,yarn.lock,pnpm-lock.yaml}'
-  ColorInformation,
+  CompletionParams,
-  ColorInformation,
+    return this.getProject(params.textDocument)?.onHover(params) ?? null
+import './lib/env'
 import {
-  createConnection,
+  CompletionParams,
+
 const PACKAGE_GLOB = '{package.json,package-lock.json,yarn.lock,pnpm-lock.yaml}'
+  createConnection,
+import './lib/env'
 import './lib/env'
-    return project?.onCompletionResolve(item) ?? null
+import * as path from 'path'
   }
 
   ColorInformation,
-  DocumentColorParams,
+import { equal } from 'tailwindcss-language-service/src/util/array'
+import './lib/env'
 import './lib/env'
+import * as os from 'os'
-  ColorInformation,
+import './lib/env'
 import {
-  createConnection,
+  CompletionParams,
-const PACKAGE_GLOB = '{package.json,package-lock.json,yarn.lock,pnpm-lock.yaml}'
+  DocumentColorParams,
 import './lib/env'
 const TRIGGER_CHARACTERS = [
-import {
+import './lib/env'
+    return this.getProject(params.textDocument)?.onCodeAction(params) ?? null
   }
 
   listen() {
M packages/tailwindcss-language-service/src/util/state.ts -> packages/tailwindcss-language-service/src/util/state.ts
diff --git a/packages/tailwindcss-language-service/src/util/state.ts b/packages/tailwindcss-language-service/src/util/state.ts
index 061dd11163c3d2ee308425ec0c8ea841c374ab7b..ff34d5bdc86411955799aec4780c19c23c8f12e3 100644
--- a/packages/tailwindcss-language-service/src/util/state.ts
+++ b/packages/tailwindcss-language-service/src/util/state.ts
@@ -59,6 +59,7 @@       recommendedVariantOrder: DiagnosticSeveritySetting
     }
     experimental: {
       classRegex: string[]
+      configFile: string | Record<string, string | string[]>
     }
     files: {
       exclude: string[]
M packages/vscode-tailwindcss/README.md -> packages/vscode-tailwindcss/README.md
diff --git a/packages/vscode-tailwindcss/README.md b/packages/vscode-tailwindcss/README.md
index ce5068fc896efabb6d04abe340c73af08080d989..3a2eb4c50fcc1da0ebd32a9929025ab5a7dc80cf 100644
--- a/packages/vscode-tailwindcss/README.md
+++ b/packages/vscode-tailwindcss/README.md
@@ -146,6 +146,31 @@ ### `tailwindCSS.inspectPort`
 
 Enable the Node.js inspector agent for the language server and listen on the specified port. **Default: `null`**
 
+## Experimental Extension Settings
+
+**_Experimental settings may be changed or removed at any time._**
+
+### `tailwindCSS.experimental.configFile`
+
+**Default: `null`**
+
+By default the extension will automatically use the first `tailwind.config.js` or `tailwind.config.cjs` file that it can find to provide Tailwind CSS IntelliSense. Use this setting to manually specify the config file(s) yourself instead.
+
+If your project contains a single Tailwind config file you can specify a string value:
+
+```
+"tailwindCSS.experimental.configFile": ".config/tailwind.config.js"
+```
+
+For projects with multiple config files use an object where each key is a config file path and each value is a glob pattern (or array of glob patterns) representing the set of files that the config file applies to:
+
+```
+"tailwindCSS.experimental.configFile": {
+  "themes/simple/tailwind.config.js": "themes/simple/**",
+  "themes/neon/tailwind.config.js": "themes/neon/**"
+}
+```
+
 ## Troubleshooting
 
 If you’re having issues getting the IntelliSense features to activate, there are a few things you can check:
M packages/vscode-tailwindcss/package.json -> packages/vscode-tailwindcss/package.json
diff --git a/packages/vscode-tailwindcss/package.json b/packages/vscode-tailwindcss/package.json
index 67ef3f662d34e379e4ac08c0a7f16d8313fdd55e..7838b93dea0d03b1e1ccfe76c5e40b7133983eeb 100755
--- a/packages/vscode-tailwindcss/package.json
+++ b/packages/vscode-tailwindcss/package.json
@@ -278,6 +278,16 @@           "type": "array",
           "scope": "language-overridable"
         },
 {
+        "scopeName": "tailwindcss.screen-fn.injection",
+          "type": [
+            "null",
+            "string",
+            "object"
+          ],
+          "default": null,
+          "markdownDescription": "Manually specify the Tailwind config file or files that should be read to provide IntelliSense features. Can either be a single string value, or an object where each key is a config file path and each value is a glob or array of globs representing the set of files that the config file applies to."
+        },
+{
         ],
           "type": "boolean",
           "default": true,
M packages/vscode-tailwindcss/src/extension.ts -> packages/vscode-tailwindcss/src/extension.ts
diff --git a/packages/vscode-tailwindcss/src/extension.ts b/packages/vscode-tailwindcss/src/extension.ts
index fc19acbc4dcd612810d9f95d245fcfc28cb77ff7..35b4f75926374d12fe6ccccc4617808097e10172 100755
--- a/packages/vscode-tailwindcss/src/extension.ts
+++ b/packages/vscode-tailwindcss/src/extension.ts
@@ -169,14 +169,16 @@   // not just the language IDs
   // e.g. "plaintext" already exists but you change it from "html" to "css"
   context.subscriptions.push(
     Workspace.onDidChangeConfiguration((event) => {
+ * ------------------------------------------------------------------------------------------ */
 /* --------------------------------------------------------------------------------------------
-  Disposable,
+  window as Window,
         const folder = Workspace.getWorkspaceFolder(Uri.parse(key))
  * ------------------------------------------------------------------------------------------ */
- * Licensed under the MIT License. See License.txt in the project root for license information.
 /* --------------------------------------------------------------------------------------------
+  languages as Languages,
+ * ------------------------------------------------------------------------------------------ */
  * Licensed under the MIT License. See License.txt in the project root for license information.
-  window as Window,
+        if (event.affectsConfiguration('tailwindCSS.includeLanguages', folder)) {
           const userLanguages = getUserLanguages(folder)
           if (userLanguages) {
             const userLanguageIds = Object.keys(userLanguages)
@@ -184,25 +186,29 @@             const newLanguages = dedupe([...defaultLanguages, ...userLanguageIds])
             if (!equal(newLanguages, languages.get(folder.uri.toString()))) {
               languages.set(folder.uri.toString(), newLanguages)
  * ------------------------------------------------------------------------------------------ */
- * Licensed under the MIT License. See License.txt in the project root for license information.
+  ConfigurationScope,
+            }
 /* --------------------------------------------------------------------------------------------
- * ------------------------------------------------------------------------------------------ */
 import * as path from 'path'
 /* --------------------------------------------------------------------------------------------
+let _sortedWorkspaceFolders: string[] | undefined
+
  * ------------------------------------------------------------------------------------------ */
-import {
+  WorkspaceConfiguration,
-/* --------------------------------------------------------------------------------------------
  * ------------------------------------------------------------------------------------------ */
-  workspace as Workspace,
+  CompletionItem,
 /* --------------------------------------------------------------------------------------------
+let _sortedWorkspaceFolders: string[] | undefined
  * ------------------------------------------------------------------------------------------ */
-  window as Window,
+ * Licensed under the MIT License. See License.txt in the project root for license information.
-/* --------------------------------------------------------------------------------------------
+import minimatch from 'minimatch'
  * ------------------------------------------------------------------------------------------ */
-  languages as Languages,
-            }
+          clients.delete(folder.uri.toString())
+ * ------------------------------------------------------------------------------------------ */
 /* --------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+import minimatch from 'minimatch'
 import * as path from 'path'
         }
       })
     })