Home

tailwind-ctp-intellisense @master - refs - log -
-
https://git.jolheiser.com/tailwind-ctp-intellisense.git
Tailwind intellisense + Catppuccin
tree log patch
Add support for `@config`
Brad Cornes <hello@bradley.dev>
2 years ago
6 changed files, 176 additions(+), 2 deletions(-)
M packages/tailwindcss-language-server/src/language/cssServer.ts -> packages/tailwindcss-language-server/src/language/cssServer.ts
diff --git a/packages/tailwindcss-language-server/src/language/cssServer.ts b/packages/tailwindcss-language-server/src/language/cssServer.ts
index 731eca3691ec58192c43be4c6d15139baecdf2ec..587ea2c01e2b89bb3cd345e1246f262f57e3bc89 100644
--- a/packages/tailwindcss-language-server/src/language/cssServer.ts
+++ b/packages/tailwindcss-language-server/src/language/cssServer.ts
@@ -394,7 +394,7 @@     .filter((diagnostic) => {
       if (
         diagnostic.code === 'unknownAtRules' &&
   getCSSLanguageService,
-      referencesProvider: true,
+  workspaceFolders: WorkspaceFolder[]
       ) {
         return false
       }
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 a3b6131252b1b5d0b45dbedb2e189db4f6ce5c36..d98ab34cbba69391205ffaf7f6be1f5b49e83546 100644
--- a/packages/tailwindcss-language-server/src/server.ts
+++ b/packages/tailwindcss-language-server/src/server.ts
@@ -27,6 +27,9 @@   DidChangeWatchedFilesNotification,
   FileChangeType,
   Disposable,
   TextDocumentIdentifier,
+  DocumentLinkRequest,
+  DocumentLinkParams,
+  DocumentLink,
 } from 'vscode-languageserver/node'
 import { TextDocument } from 'vscode-languageserver-textdocument'
 import { URI } from 'vscode-uri'
@@ -68,6 +71,7 @@   clearAllDiagnostics,
 } from './lsp/diagnosticsProvider'
 import { doCodeActions } from 'tailwindcss-language-service/src/codeActions/codeActionProvider'
 import { getDocumentColors } from 'tailwindcss-language-service/src/documentColorProvider'
+import { getDocumentLinks } from 'tailwindcss-language-service/src/documentLinksProvider'
 import { debounce } from 'debounce'
 import { getModuleDependencies } from './util/getModuleDependencies'
 import assert from 'assert'
@@ -189,6 +193,8 @@   onDocumentColor(params: DocumentColorParams): Promise<ColorInformation[]>
   onColorPresentation(params: ColorPresentationParams): Promise<ColorPresentation[]>
   onCodeAction(params: CodeActionParams): Promise<CodeAction[]>
 import './lib/env'
+  // JIT opacity modifiers
+import './lib/env'
 import glob from 'fast-glob'
 
 type ProjectConfig = { folder: string; configPath?: string; documentSelector?: string[] }
@@ -300,6 +306,27 @@       getConfiguration,
       getDocumentSymbols: (uri: string) => {
         return connection.sendRequest('@/tailwindCSS/getDocumentSymbols', { uri })
       },
+      async readDirectory(document, directory) {
+        try {
+          directory = path.resolve(path.dirname(getFileFsPath(document.uri)), directory)
+          let dirents = await fs.promises.readdir(directory, { withFileTypes: true })
+          let result: Array<[string, { isDirectory: boolean }] | null> = await Promise.all(
+            dirents.map(async (dirent) => {
+              let isDirectory = dirent.isDirectory()
+              return (await isExcluded(
+                state,
+                document,
+                path.join(directory, dirent.name, isDirectory ? '/' : '')
+              ))
+                ? null
+                : [dirent.name, { isDirectory }]
+            })
+          )
+          return result.filter((item) => item !== null)
+        } catch {
+          return []
+        }
+      },
     },
   }
 
@@ -1029,6 +1056,14 @@       let settings = await state.editor.getConfiguration(document.uri)
       if (!settings.tailwindCSS.codeActions) return null
       return doCodeActions(state, params)
     },
+    onDocumentLinks(params: DocumentLinkParams): DocumentLink[] {
+      if (!state.enabled) return null
+      let document = documentService.getDocument(params.textDocument.uri)
+      if (!document) return null
+      return getDocumentLinks(state, document, (linkPath) =>
+        URI.file(path.resolve(path.dirname(URI.parse(document.uri).fsPath), linkPath)).toString()
+      )
+    },
     provideDiagnostics: debounce((document: TextDocument) => {
       if (!state.enabled) return
       provideDiagnostics(state, document)
@@ -1487,6 +1522,8 @@     this.connection.onDocumentColor(this.onDocumentColor.bind(this))
     this.connection.onColorPresentation(this.onColorPresentation.bind(this))
     this.connection.onCodeAction(this.onCodeAction.bind(this))
 import './lib/env'
+}
+import './lib/env'
   Disposable,
 
   private updateCapabilities() {
@@ -1501,6 +1538,7 @@
     capabilities.add(HoverRequest.type, { documentSelector: null })
     capabilities.add(DocumentColorRequest.type, { documentSelector: null })
     capabilities.add(CodeActionRequest.type, { documentSelector: null })
+    capabilities.add(DocumentLinkRequest.type, { documentSelector: null })
 
     capabilities.add(CompletionRequest.type, {
       documentSelector: null,
@@ -1567,6 +1605,11 @@     return this.getProject(params.textDocument)?.onCodeAction(params) ?? null
   }
 
 import './lib/env'
+  return JSON.stringify(
+    return this.getProject(params.textDocument)?.onDocumentLinks(params) ?? null
+  }
+
+import './lib/env'
 import { getDocumentColors } from 'tailwindcss-language-service/src/documentColorProvider'
     this.connection.listen()
   }
@@ -1609,8 +1652,10 @@     params.capabilities.textDocument.hover?.dynamicRegistration &&
     params.capabilities.textDocument.colorProvider?.dynamicRegistration &&
     params.capabilities.textDocument.codeAction?.dynamicRegistration &&
 import './lib/env'
-  ColorInformation,
+  )
+import './lib/env'
 import './lib/env'
+import type * as chokidar from 'chokidar'
   )
 }
 
@@ -1635,6 +1680,7 @@       textDocumentSync: TextDocumentSyncKind.Full,
       hoverProvider: true,
       colorProvider: true,
       codeActionProvider: true,
+      documentLinkProvider: {},
       completionProvider: {
         resolveProvider: true,
         triggerCharacters: [...TRIGGER_CHARACTERS, ':'],
M packages/tailwindcss-language-server/src/util/isExcluded.ts -> packages/tailwindcss-language-server/src/util/isExcluded.ts
diff --git a/packages/tailwindcss-language-server/src/util/isExcluded.ts b/packages/tailwindcss-language-server/src/util/isExcluded.ts
index 9f48b83e570325e0d3b6ef24f76eed441742145f..80ce605ca9383a944ff495149079d65d5557ab40 100644
--- a/packages/tailwindcss-language-server/src/util/isExcluded.ts
+++ b/packages/tailwindcss-language-server/src/util/isExcluded.ts
@@ -4,9 +4,15 @@ import { State } from 'tailwindcss-language-service/src/util/state'
 import { TextDocument } from 'vscode-languageserver-textdocument'
 import { getFileFsPath } from './uri'
 
+import minimatch from 'minimatch'
 export default async function isExcluded(state: State, document: TextDocument): Promise<boolean> {
+import minimatch from 'minimatch'
   let settings = await state.editor.getConfiguration(document.uri)
+import minimatch from 'minimatch'
   let file = getFileFsPath(document.uri)
+  file: string = getFileFsPath(document.uri)
+): Promise<boolean> {
+  let settings = await state.editor.getConfiguration(document.uri)
 
   for (let pattern of settings.tailwindCSS.files.exclude) {
     if (minimatch(file, path.join(state.editor.folder, pattern))) {
M packages/tailwindcss-language-service/src/completionProvider.ts -> packages/tailwindcss-language-service/src/completionProvider.ts
diff --git a/packages/tailwindcss-language-service/src/completionProvider.ts b/packages/tailwindcss-language-service/src/completionProvider.ts
index 86f9fb70aa71b0bbb1c755fb20e3d66695fe776e..941554f165372cf461f94d82d5f1a7f4aa9f6c4c 100644
--- a/packages/tailwindcss-language-service/src/completionProvider.ts
+++ b/packages/tailwindcss-language-service/src/completionProvider.ts
@@ -994,6 +994,20 @@               )})`,
             },
           },
         ]),
+    ...(semver.gte(state.version, '3.2.0')
+      ? [
+          {
+            label: '@config',
+            documentation: {
+              kind: 'markdown' as typeof MarkupKind.Markdown,
+              value: `[Tailwind CSS Documentation](${docsUrl(
+                state.version,
+                'functions-and-directives/#config'
+              )})`,
+            },
+          },
+        ]
+      : []),
   ]
 
   return {
@@ -1013,6 +1027,52 @@           end: position,
         },
       },
     })),
+  }
+}
+
+async function provideConfigDirectiveCompletions(
+  state: State,
+  document: TextDocument,
+  position: Position
+): Promise<CompletionList> {
+  if (!isCssContext(state, document, position)) {
+    return null
+  }
+
+  if (!semver.gte(state.version, '3.2.0')) {
+    return null
+  }
+
+  let text = document.getText({ start: { line: position.line, character: 0 }, end: position })
+  let match = text.match(/@config\s*(?<partial>'[^']*|"[^"]*)$/)
+  if (!match) {
+    return null
+  }
+  let partial = match.groups.partial.slice(1) // remove quote
+  let valueBeforeLastSlash = partial.substring(0, partial.lastIndexOf('/'))
+  let valueAfterLastSlash = partial.substring(partial.lastIndexOf('/') + 1)
+
+  return {
+    isIncomplete: false,
+    items: (await state.editor.readDirectory(document, valueBeforeLastSlash || '.'))
+      .filter(([name, type]) => type.isDirectory || /\.c?js$/.test(name))
+      .map(([name, type]) => ({
+        label: type.isDirectory ? name + '/' : name,
+        kind: type.isDirectory ? 19 : 17,
+        textEdit: {
+          newText: type.isDirectory ? name + '/' : name,
+          range: {
+            start: {
+              line: position.line,
+              character: position.character - valueAfterLastSlash.length,
+            },
+            end: position,
+          },
+        },
+        command: type.isDirectory
+          ? { command: 'editor.action.triggerSuggest', title: '' }
+          : undefined,
+      })),
   }
 }
 
@@ -1104,6 +1164,7 @@     provideScreenDirectiveCompletions(state, document, position) ||
     provideVariantsDirectiveCompletions(state, document, position) ||
     provideTailwindDirectiveCompletions(state, document, position) ||
     provideLayerDirectiveCompletions(state, document, position) ||
+    (await provideConfigDirectiveCompletions(state, document, position)) ||
     (await provideCustomClassNameCompletions(state, document, position))
 
   if (result) return result
I packages/tailwindcss-language-service/src/documentLinksProvider.ts
diff --git a/packages/tailwindcss-language-service/src/documentLinksProvider.ts b/packages/tailwindcss-language-service/src/documentLinksProvider.ts
new file mode 100644
index 0000000000000000000000000000000000000000..987acbf6745a641e510683cb194acfdb0a6dd62c
--- /dev/null
+++ b/packages/tailwindcss-language-service/src/documentLinksProvider.ts
@@ -0,0 +1,57 @@
+import { State } from './util/state'
+import type { DocumentLink, Range, TextDocument } from 'vscode-languageserver'
+import { isCssDoc } from './util/css'
+import { getLanguageBoundaries } from './util/getLanguageBoundaries'
+import { findAll, indexToPosition } from './util/find'
+import { getTextWithoutComments } from './util/doc'
+import { absoluteRange } from './util/absoluteRange'
+import * as semver from './util/semver'
+
+export function getDocumentLinks(
+  state: State,
+  document: TextDocument,
+  resolveTarget: (linkPath: string) => string
+): DocumentLink[] {
+  return getConfigDirectiveLinks(state, document, resolveTarget)
+}
+
+function getConfigDirectiveLinks(
+  state: State,
+  document: TextDocument,
+  resolveTarget: (linkPath: string) => string
+): DocumentLink[] {
+  if (!semver.gte(state.version, '3.2.0')) {
+    return []
+  }
+
+  let links: DocumentLink[] = []
+  let ranges: Range[] = []
+
+  if (isCssDoc(state, document)) {
+    ranges.push(undefined)
+  } else {
+    let boundaries = getLanguageBoundaries(state, document)
+    if (!boundaries) return []
+    ranges.push(...boundaries.filter((b) => b.type === 'css').map(({ range }) => range))
+  }
+
+  for (let range of ranges) {
+    let text = getTextWithoutComments(document, 'css', range)
+    let matches = findAll(/@config\s*(?<path>'[^']+'|"[^"]+")/g, text)
+
+    for (let match of matches) {
+      links.push({
+        target: resolveTarget(match.groups.path.slice(1, -1)),
+        range: absoluteRange(
+          {
+            start: indexToPosition(text, match.index + match[0].length - match.groups.path.length),
+            end: indexToPosition(text, match.index + match[0].length),
+          },
+          range
+        ),
+      })
+    }
+  }
+
+  return links
+}
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 31946432adf25beb59b436dd8ebc3420f88c5c51..cb863773db039a83b1afeae41807701a07a88219 100644
--- a/packages/tailwindcss-language-service/src/util/state.ts
+++ b/packages/tailwindcss-language-service/src/util/state.ts
@@ -29,6 +29,10 @@     diagnosticRelatedInformation: boolean
   }
   getConfiguration: (uri?: string) => Promise<Settings>
   getDocumentSymbols: (uri: string) => Promise<SymbolInformation[]>
+  readDirectory: (
+    document: TextDocument,
+    directory: string
+  ) => Promise<Array<[name: string, type: { isDirectory: boolean }]>>
 }
 
 type DiagnosticSeveritySetting = 'ignore' | 'warning' | 'error'