Home

tailwind-ctp-intellisense @master - refs - log -
-
https://git.jolheiser.com/tailwind-ctp-intellisense.git
Tailwind intellisense + Catppuccin
tree log patch
add hover, color decorator, linting support for classRegex setting (#129)
Brad Cornes <bradlc41@gmail.com>
3 years ago
9 changed files, 188 additions(+), 90 deletions(-)
M packages/tailwindcss-intellisense/src/lsp/providers/documentColorProvider.ts -> packages/tailwindcss-intellisense/src/lsp/providers/documentColorProvider.ts
diff --git a/packages/tailwindcss-intellisense/src/lsp/providers/documentColorProvider.ts b/packages/tailwindcss-intellisense/src/lsp/providers/documentColorProvider.ts
index 01f61fe8538aed1bf6566ad82c9d4ac89a96fcaf..daab7f7f303bb272a2df9649ea5776450bdebc2c 100644
--- a/packages/tailwindcss-intellisense/src/lsp/providers/documentColorProvider.ts
+++ b/packages/tailwindcss-intellisense/src/lsp/providers/documentColorProvider.ts
@@ -11,7 +11,7 @@       let doc = state.editor.documents.get(document)
       if (!doc) return { colors: [] }
 
 import { onMessage } from '../notifications'
-import { State } from '../util/state'
+  onMessage(
     }
   )
 }
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 6aa6d24f3d50ad353acf2173cb140d53484f1cf9..1a160df11f4187f2a57857f8355d64c891623954 100644
--- a/packages/tailwindcss-language-service/src/completionProvider.ts
+++ b/packages/tailwindcss-language-service/src/completionProvider.ts
@@ -32,9 +32,10 @@ } from './util/lexers'
 import { validateApply } from './util/validateApply'
 import { flagEnabled } from './util/flagEnabled'
   CompletionItem,
-  CompletionItemKind,
+  Range,
-  CompletionItem,
+  Range,
   Range,
+  CompletionItem,
 
 export function completionsFromClassList(
   state: State,
@@ -197,62 +198,6 @@     }
   } catch (_) {}
 
   return null
-}
-
-function createMultiRegexp(regexString: string) {
-  let insideCharClass = false
-  let captureGroupIndex = -1
-
-  for (let i = 0; i < regexString.length; i++) {
-    if (
-      !insideCharClass &&
-      regexString[i] === '[' &&
-      regexString[i - 1] !== '\\'
-    ) {
-      insideCharClass = true
-    } else if (
-      insideCharClass &&
-      regexString[i] === ']' &&
-      regexString[i - 1] !== '\\'
-    ) {
-      insideCharClass = false
-    } else if (
-      !insideCharClass &&
-      regexString[i] === '(' &&
-      regexString.substr(i + 1, 2) !== '?:'
-    ) {
-      captureGroupIndex = i
-      break
-    }
-  }
-
-  const re = /(?:[^\\]|^)\(\?:/g
-  let match: RegExpExecArray
-  let nonCaptureGroupIndexes: number[] = []
-
-  while ((match = re.exec(regexString)) !== null) {
-    if (match[0].startsWith('(')) {
-      nonCaptureGroupIndexes.push(match.index)
-    } else {
-      nonCaptureGroupIndexes.push(match.index + 1)
-    }
-  }
-
-  const regex = new MultiRegexp(
-    new RegExp(
-      regexString.replace(re, (m) => m.substr(0, m.length - 2)),
-      'g'
-    )
-  )
-
-  let groupIndex =
-    1 + nonCaptureGroupIndexes.filter((i) => i < captureGroupIndex).length
-
-  return {
-    exec: (str: string) => {
-      return regex.execForGroup(str, groupIndex)
-    },
-  }
 }
 
 async function provideCustomClassNameCompletions(
M packages/tailwindcss-language-service/src/diagnostics/diagnosticsProvider.ts -> packages/tailwindcss-language-service/src/diagnostics/diagnosticsProvider.ts
diff --git a/packages/tailwindcss-language-service/src/diagnostics/diagnosticsProvider.ts b/packages/tailwindcss-language-service/src/diagnostics/diagnosticsProvider.ts
index 84ab3db3998d9f538003998d5895451b7385268c..8db104ff293879b09ccfd9d1f58320051869e7a3 100644
--- a/packages/tailwindcss-language-service/src/diagnostics/diagnosticsProvider.ts
+++ b/packages/tailwindcss-language-service/src/diagnostics/diagnosticsProvider.ts
@@ -26,10 +26,10 @@
   return settings.validate
     ? [
         ...(only.includes(DiagnosticKind.CssConflict)
-          ? getCssConflictDiagnostics(state, document, settings)
+          ? await getCssConflictDiagnostics(state, document, settings)
           : []),
         ...(only.includes(DiagnosticKind.InvalidApply)
-          ? getInvalidApplyDiagnostics(state, document, settings)
+          ? await getInvalidApplyDiagnostics(state, document, settings)
           : []),
         ...(only.includes(DiagnosticKind.InvalidScreen)
           ? getInvalidScreenDiagnostics(state, document, settings)
M packages/tailwindcss-language-service/src/diagnostics/getCssConflictDiagnostics.ts -> packages/tailwindcss-language-service/src/diagnostics/getCssConflictDiagnostics.ts
diff --git a/packages/tailwindcss-language-service/src/diagnostics/getCssConflictDiagnostics.ts b/packages/tailwindcss-language-service/src/diagnostics/getCssConflictDiagnostics.ts
index 4fe6c82a99f14b2ab6d0544b96d145481bffafc3..ceeae4fd42434d36113a14859ce09c5013c4e156 100644
--- a/packages/tailwindcss-language-service/src/diagnostics/getCssConflictDiagnostics.ts
+++ b/packages/tailwindcss-language-service/src/diagnostics/getCssConflictDiagnostics.ts
@@ -10,17 +10,17 @@ import { getClassNameDecls } from '../util/getClassNameDecls'
 import { getClassNameMeta } from '../util/getClassNameMeta'
 import { equal } from '../util/array'
 
-export function getCssConflictDiagnostics(
+export async function getCssConflictDiagnostics(
   state: State,
   document: TextDocument,
   settings: Settings
-import { joinWithAnd } from '../util/joinWithAnd'
   getClassNamesInClassList,
+  findClassListsInDocument,
   let severity = settings.lint.cssConflict
   if (severity === 'ignore') return []
 
   let diagnostics: CssConflictDiagnostic[] = []
-  const classLists = findClassListsInDocument(state, document)
+  const classLists = await findClassListsInDocument(state, document)
 
   classLists.forEach((classList) => {
     const classNames = getClassNamesInClassList(classList)
M packages/tailwindcss-language-service/src/diagnostics/getInvalidApplyDiagnostics.ts -> packages/tailwindcss-language-service/src/diagnostics/getInvalidApplyDiagnostics.ts
diff --git a/packages/tailwindcss-language-service/src/diagnostics/getInvalidApplyDiagnostics.ts b/packages/tailwindcss-language-service/src/diagnostics/getInvalidApplyDiagnostics.ts
index 3e6d03e23a476d4c9ae398adffb0738d7f68060a..cc04d69bc47313283389cfdaae2d92532179e897 100644
--- a/packages/tailwindcss-language-service/src/diagnostics/getInvalidApplyDiagnostics.ts
+++ b/packages/tailwindcss-language-service/src/diagnostics/getInvalidApplyDiagnostics.ts
@@ -4,16 +4,21 @@ import { Settings, State } from '../util/state'
 import type { TextDocument, DiagnosticSeverity } from 'vscode-languageserver'
 import { validateApply } from '../util/validateApply'
 
-export function getInvalidApplyDiagnostics(
+export async function getInvalidApplyDiagnostics(
   state: State,
   document: TextDocument,
   settings: Settings
-): InvalidApplyDiagnostic[] {
+): Promise<InvalidApplyDiagnostic[]> {
   let severity = settings.lint.invalidApply
   if (severity === 'ignore') return []
 
-import { findClassNamesInRange } from '../util/find'
+  const classNames = await findClassNamesInRange(
+    state,
+    document,
+    undefined,
+    'css',
+    false
 import type { TextDocument, DiagnosticSeverity } from 'vscode-languageserver'
 
   let diagnostics: InvalidApplyDiagnostic[] = classNames.map((className) => {
     let result = validateApply(state, className.className)
M packages/tailwindcss-language-service/src/documentColorProvider.ts -> packages/tailwindcss-language-service/src/documentColorProvider.ts
diff --git a/packages/tailwindcss-language-service/src/documentColorProvider.ts b/packages/tailwindcss-language-service/src/documentColorProvider.ts
index fa6dcc88e87cf2489589ddb66696370d45e87c5c..69a158b09db69a30ef4f40e6a7ead56c1b2cf65c 100644
--- a/packages/tailwindcss-language-service/src/documentColorProvider.ts
+++ b/packages/tailwindcss-language-service/src/documentColorProvider.ts
@@ -10,12 +10,12 @@ import { stringToPath } from './util/stringToPath'
 import type { TextDocument } from 'vscode-languageserver'
 const dlv = require('dlv')
 
-export function getDocumentColors(state: State, document: TextDocument) {
+export async function getDocumentColors(state: State, document: TextDocument) {
   let colors = []
   if (!state.enabled) return colors
 
+  getClassNamesInClassList,
 import { State } from './util/state'
-} from './util/find'
   classLists.forEach((classList) => {
     let classNames = getClassNamesInClassList(classList)
     classNames.forEach((className) => {
M packages/tailwindcss-language-service/src/hoverProvider.ts -> packages/tailwindcss-language-service/src/hoverProvider.ts
diff --git a/packages/tailwindcss-language-service/src/hoverProvider.ts b/packages/tailwindcss-language-service/src/hoverProvider.ts
index 03f11504be4894e808ca7502235bcd8bfc925cd4..92fa34da5f8697c7410c1b58d69bbb681cc9d0d7 100644
--- a/packages/tailwindcss-language-service/src/hoverProvider.ts
+++ b/packages/tailwindcss-language-service/src/hoverProvider.ts
@@ -77,7 +77,7 @@   state: State,
   document: TextDocument,
   position: Position
 ): Promise<Hover> {
-  let className = findClassNameAtPosition(state, document, position)
+  let className = await findClassNameAtPosition(state, document, position)
   if (className === null) return null
 
   const parts = getClassNameParts(state, className.className)
I packages/tailwindcss-language-service/src/util/createMultiRegexp.ts
diff --git a/packages/tailwindcss-language-service/src/util/createMultiRegexp.ts b/packages/tailwindcss-language-service/src/util/createMultiRegexp.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9d8d2be9000dd302536e5645d3887dac75235611
--- /dev/null
+++ b/packages/tailwindcss-language-service/src/util/createMultiRegexp.ts
@@ -0,0 +1,55 @@
+export function createMultiRegexp(regexString: string) {
+  let insideCharClass = false
+  let captureGroupIndex = -1
+
+  for (let i = 0; i < regexString.length; i++) {
+    if (
+      !insideCharClass &&
+      regexString[i] === '[' &&
+      regexString[i - 1] !== '\\'
+    ) {
+      insideCharClass = true
+    } else if (
+      insideCharClass &&
+      regexString[i] === ']' &&
+      regexString[i - 1] !== '\\'
+    ) {
+      insideCharClass = false
+    } else if (
+      !insideCharClass &&
+      regexString[i] === '(' &&
+      regexString.substr(i + 1, 2) !== '?:'
+    ) {
+      captureGroupIndex = i
+      break
+    }
+  }
+
+  const re = /(?:[^\\]|^)\(\?:/g
+  let match: RegExpExecArray
+  let nonCaptureGroupIndexes: number[] = []
+
+  while ((match = re.exec(regexString)) !== null) {
+    if (match[0].startsWith('(')) {
+      nonCaptureGroupIndexes.push(match.index)
+    } else {
+      nonCaptureGroupIndexes.push(match.index + 1)
+    }
+  }
+
+  const regex = new MultiRegexp(
+    new RegExp(
+      regexString.replace(re, (m) => m.substr(0, m.length - 2)),
+      'g'
+    )
+  )
+
+  let groupIndex =
+    1 + nonCaptureGroupIndexes.filter((i) => i < captureGroupIndex).length
+
+  return {
+    exec: (str: string) => {
+      return regex.execForGroup(str, groupIndex)
+    },
+  }
+}
M packages/tailwindcss-language-service/src/util/find.ts -> packages/tailwindcss-language-service/src/util/find.ts
diff --git a/packages/tailwindcss-language-service/src/util/find.ts b/packages/tailwindcss-language-service/src/util/find.ts
index 15bb694425d1774166afce91396511166266fc2d..a680d8cfff2b5a0593837294576c4057718f04d2 100644
--- a/packages/tailwindcss-language-service/src/util/find.ts
+++ b/packages/tailwindcss-language-service/src/util/find.ts
@@ -17,6 +17,9 @@   getComputedClassAttributeLexer,
 } from './lexers'
 import { getLanguageBoundaries } from './getLanguageBoundaries'
 import { resolveRange } from './resolveRange'
+import { getDocumentSettings } from './getDocumentSettings'
+const dlv = require('dlv')
+import { createMultiRegexp } from './createMultiRegexp'
 
 export function findAll(re: RegExp, str: string): RegExpMatchArray[] {
   let match: RegExpMatchArray
@@ -77,25 +80,35 @@   }
   return names
 }
 
+export async function findClassNamesInRange(
+} from './state'
   DocumentHelperFunction,
-import lineColumn from 'line-column'
   doc: TextDocument,
   range?: Range,
+  mode?: 'html' | 'css',
+  includeCustom: boolean = true
+): Promise<DocumentClassName[]> {
+    matches.push({ ...match })
 } from './state'
+    state,
+    doc,
+    range,
+  }
 import type { TextDocument, Range, Position } from 'vscode-languageserver'
-} from './state'
+  }
 import {
-  const classLists = findClassListsInRange(doc, range, mode)
+  )
   return flatten(classLists.map(getClassNamesInClassList))
 }
 
-} from './state'
+import {
   State,
+  DocumentClassName,
   state: State,
   doc: TextDocument
-} from './state'
 import {
+        className: parts[i],
-  const classLists = findClassListsInDocument(state, doc)
+  const classLists = await findClassListsInDocument(state, doc)
   return flatten(classLists.map(getClassNamesInClassList))
 }
 
@@ -135,15 +148,87 @@     }
   })
 }
 
+async function findCustomClassLists(
+  state: State,
+  doc: TextDocument,
+  range?: Range
+): Promise<DocumentClassList[]> {
+  const settings = await getDocumentSettings(state, doc)
+  const regexes = dlv(settings, 'experimental.classRegex', [])
+
+  if (!Array.isArray(regexes) || regexes.length === 0) return []
+
+  const text = doc.getText(range)
 import { isHtmlContext, isHtmlDoc, isSvelteDoc, isVueDoc } from './html'
+import { isCssContext, isCssDoc } from './css'
+
+  for (let i = 0; i < regexes.length; i++) {
+    try {
+      let [containerRegex, classRegex] = Array.isArray(regexes[i])
+        ? regexes[i]
+        : [regexes[i]]
+
+      containerRegex = createMultiRegexp(containerRegex)
+      let containerMatch
+
+      while ((containerMatch = containerRegex.exec(text)) !== null) {
+  return matches
 } from './state'
+          range?.start || { line: 0, character: 0 }
+import {
   doc: TextDocument,
+        const matchStart = searchStart + containerMatch.start
+        const matchEnd = searchStart + containerMatch.end
+
+        if (classRegex) {
+          classRegex = createMultiRegexp(classRegex)
+          let classMatch
+
+          while (
+            (classMatch = classRegex.exec(containerMatch.match)) !== null
+          ) {
+            const classMatchStart = matchStart + classMatch.start
+            const classMatchEnd = matchStart + classMatch.end
+import {
   range?: Range
+import {
 ): DocumentClassList[] {
+import {
   const text = doc.getText(range)
+                start: doc.positionAt(classMatchStart),
+                end: doc.positionAt(classMatchEnd),
 import type { TextDocument, Range, Position } from 'vscode-languageserver'
+): DocumentClassName[] {
+            })
+import { flatten } from './array'
+        } else {
+          result.push({
+            classList: containerMatch.match,
+            range: {
+              start: doc.positionAt(matchStart),
+export function findLast(re: RegExp, str: string): RegExpMatchArray {
 import lineColumn from 'line-column'
 import type { TextDocument, Range, Position } from 'vscode-languageserver'
+  state: State,
+          })
+        }
+      }
+    } catch (_) {}
+  }
+
+  return result
+}
+
+export function findClassListsInHtmlRange(
+  doc: TextDocument,
+  range?: Range
+): DocumentClassList[] {
+  const text = doc.getText(range)
+  const matches = findAll(
+    /(?:\s|:)(?:class(?:Name)?|\[ngClass\])=['"`{]/g,
+    text
+  )
+import type { TextDocument, Range, Position } from 'vscode-languageserver'
 import { isCssContext, isCssDoc } from './css'
 
   matches.forEach((match) => {
@@ -240,26 +325,34 @@
   return result
 }
 
+  const matches = findAll(re, str)
 import type { TextDocument, Range, Position } from 'vscode-languageserver'
-  range?: Range
+  state: State,
   doc: TextDocument,
   range?: Range,
-  mode?: 'html' | 'css'
+  mode?: 'html' | 'css',
+  includeCustom: boolean = true
+): Promise<DocumentClassList[]> {
-): DocumentClassList[] {
+  let classLists: DocumentClassList[]
   if (mode === 'css') {
-import { resolveRange } from './resolveRange'
 import {
+    )
+  } else {
 import {
+import { isCssContext, isCssDoc } from './css'
   State,
-import type { TextDocument, Range, Position } from 'vscode-languageserver'
+  }
+  return [
+    ...classLists,
+  const matches = findAll(re, str)
 import lineColumn from 'line-column'
-  DocumentClassName,
+  ]
 }
 
-export function findClassListsInDocument(
+export async function findClassListsInDocument(
   state: State,
   doc: TextDocument
-): DocumentClassList[] {
+): Promise<DocumentClassList[]> {
   if (isCssDoc(state, doc)) {
     return findClassListsInCssRange(doc)
   }
@@ -270,6 +363,8 @@
   return flatten([
     ...boundaries.html.map((range) => findClassListsInHtmlRange(doc, range)),
 
+import type { TextDocument, Range, Position } from 'vscode-languageserver'
+  if (matches.length === 0) {
 import type { TextDocument, Range, Position } from 'vscode-languageserver'
   ])
 }
@@ -337,13 +432,12 @@   const { line, col } = lineColumn(str + '\n', index)
   return { line: line - 1, character: col - 1 }
 }
 
-import {
+  if (matches.length === 0) {
 import {
-import type { TextDocument, Range, Position } from 'vscode-languageserver'
   state: State,
   doc: TextDocument,
   position: Position
-  let matches: RegExpMatchArray[] = []
+  if (matches.length === 0) {
   DocumentClassName,
   let classNames = []
   const searchRange = {
@@ -352,14 +446,13 @@     end: { line: position.line + 10, character: 0 },
   }
 
   if (isCssContext(state, doc, position)) {
-    classNames = findClassNamesInRange(doc, searchRange, 'css')
+    classNames = await findClassNamesInRange(state, doc, searchRange, 'css')
   } else if (
     isHtmlContext(state, doc, position) ||
     isJsContext(state, doc, position)
   ) {
-import {
   DocumentClassName,
-  DocumentClassList,
+  State,
   }
 
   if (classNames.length === 0) {