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(-)
packages/tailwindcss-intellisense/src/lsp/providers/documentColorProvider.tspackages/tailwindcss-language-service/src/completionProvider.tspackages/tailwindcss-language-service/src/diagnostics/diagnosticsProvider.tspackages/tailwindcss-language-service/src/diagnostics/getCssConflictDiagnostics.tspackages/tailwindcss-language-service/src/diagnostics/getInvalidApplyDiagnostics.tspackages/tailwindcss-language-service/src/documentColorProvider.tspackages/tailwindcss-language-service/src/hoverProvider.tspackages/tailwindcss-language-service/src/util/createMultiRegexp.tspackages/tailwindcss-language-service/src/util/find.ts
M packages/tailwindcss-intellisense/src/lsp/providers/documentColorProvider.tspackages/tailwindcss-intellisense/src/lsp/providers/documentColorProvider.ts
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
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.tspackages/tailwindcss-language-service/src/completionProvider.ts
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
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.tspackages/tailwindcss-language-service/src/diagnostics/diagnosticsProvider.ts
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
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.tspackages/tailwindcss-language-service/src/diagnostics/getCssConflictDiagnostics.ts
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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.tspackages/tailwindcss-language-service/src/diagnostics/getInvalidApplyDiagnostics.ts
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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.tspackages/tailwindcss-language-service/src/documentColorProvider.ts
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
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.tspackages/tailwindcss-language-service/src/hoverProvider.ts
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
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
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
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.tspackages/tailwindcss-language-service/src/util/find.ts
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
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) {