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>
4 years ago
9 changed files, 178 additions(+), 88 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
@@ -10,7 +10,7 @@     async ({ document }) => {
       let doc = state.editor.documents.get(document)
       if (!doc) return { colors: [] }
 
-      return { colors: getDocumentColors(state, doc) }
+      return { colors: await getDocumentColors(state, doc) }
     }
   )
 }
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
@@ -31,8 +31,8 @@   getComputedClassAttributeLexer,
 } from './util/lexers'
 import { validateApply } from './util/validateApply'
 import { flagEnabled } from './util/flagEnabled'
-import MultiRegexp from 'multi-regexp2'
 import { remToPx } from './util/remToPx'
+import { createMultiRegexp } from './util/createMultiRegexp'
 
 export function completionsFromClassList(
   state: State,
@@ -195,62 +195,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,16 +10,16 @@ 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
-): CssConflictDiagnostic[] {
+): Promise<CssConflictDiagnostic[]> {
   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,15 +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 []
 
-  const classNames = findClassNamesInRange(document, undefined, 'css')
+  const classNames = await findClassNamesInRange(
+    state,
+    document,
+    undefined,
+    'css',
+    false
+  )
 
   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,11 +10,11 @@ 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
 
-  let classLists = findClassListsInDocument(state, document)
+  let classLists = await findClassListsInDocument(state, document)
   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,20 +80,28 @@   }
   return names
 }
 
-export function findClassNamesInRange(
+export async function findClassNamesInRange(
+  state: State,
   doc: TextDocument,
   range?: Range,
-  mode?: 'html' | 'css'
-): DocumentClassName[] {
-  const classLists = findClassListsInRange(doc, range, mode)
+  mode?: 'html' | 'css',
+  includeCustom: boolean = true
+): Promise<DocumentClassName[]> {
+  const classLists = await findClassListsInRange(
+    state,
+    doc,
+    range,
+    mode,
+    includeCustom
+  )
   return flatten(classLists.map(getClassNamesInClassList))
 }
 
-export function findClassNamesInDocument(
+export async function findClassNamesInDocument(
   state: State,
   doc: TextDocument
-): DocumentClassName[] {
-  const classLists = findClassListsInDocument(state, doc)
+): Promise<DocumentClassName[]> {
+  const classLists = await findClassListsInDocument(state, doc)
   return flatten(classLists.map(getClassNamesInClassList))
 }
 
@@ -130,12 +141,77 @@     }
   })
 }
 
+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)
+  const result: DocumentClassList[] = []
+
+  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) {
+        const searchStart = doc.offsetAt(
+          range?.start || { line: 0, character: 0 }
+        )
+        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
+            result.push({
+              classList: classMatch.match,
+              range: {
+                start: doc.positionAt(classMatchStart),
+                end: doc.positionAt(classMatchEnd),
+              },
+            })
+          }
+        } else {
+          result.push({
+            classList: containerMatch.match,
+            range: {
+              start: doc.positionAt(matchStart),
+              end: doc.positionAt(matchEnd),
+            },
+          })
+        }
+      }
+    } 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)
+  const matches = findAll(
+    /(?:\s|:)(?:class(?:Name)?|\[ngClass\])=['"`{]/g,
+    text
+  )
   const result: DocumentClassList[] = []
 
   matches.forEach((match) => {
@@ -232,21 +308,29 @@
   return result
 }
 
-export function findClassListsInRange(
+export async function findClassListsInRange(
+  state: State,
   doc: TextDocument,
   range?: Range,
-  mode?: 'html' | 'css'
-): DocumentClassList[] {
+  mode?: 'html' | 'css',
+  includeCustom: boolean = true
+): Promise<DocumentClassList[]> {
+  let classLists: DocumentClassList[]
   if (mode === 'css') {
-    return findClassListsInCssRange(doc, range)
+    classLists = findClassListsInCssRange(doc, range)
+  } else {
+    classLists = findClassListsInHtmlRange(doc, range)
   }
-  return findClassListsInHtmlRange(doc, range)
+  return [
+    ...classLists,
+    ...(includeCustom ? await findCustomClassLists(state, doc, range) : []),
+  ]
 }
 
-export function findClassListsInDocument(
+export async function findClassListsInDocument(
   state: State,
   doc: TextDocument
-): DocumentClassList[] {
+): Promise<DocumentClassList[]> {
   if (isCssDoc(state, doc)) {
     return findClassListsInCssRange(doc)
   }
@@ -257,6 +341,7 @@
   return flatten([
     ...boundaries.html.map((range) => findClassListsInHtmlRange(doc, range)),
     ...boundaries.css.map((range) => findClassListsInCssRange(doc, range)),
+    await findCustomClassLists(state, doc),
   ])
 }
 
@@ -323,11 +408,11 @@   const { line, col } = lineColumn(str + '\n', index)
   return { line: line - 1, character: col - 1 }
 }
 
-export function findClassNameAtPosition(
+export async function findClassNameAtPosition(
   state: State,
   doc: TextDocument,
   position: Position
-): DocumentClassName {
+): Promise<DocumentClassName> {
   let classNames = []
   const searchRange = {
     start: { line: Math.max(position.line - 10, 0), character: 0 },
@@ -335,12 +420,12 @@     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)
   ) {
-    classNames = findClassNamesInRange(doc, searchRange, 'html')
+    classNames = await findClassNamesInRange(state, doc, searchRange, 'html')
   }
 
   if (classNames.length === 0) {