Skip to content

Commit 0f4d93b

Browse files
committed
add classAttributes setting
1 parent 80e2e5a commit 0f4d93b

File tree

5 files changed

+53
-14
lines changed

5 files changed

+53
-14
lines changed

packages/tailwindcss-language-service/src/completionProvider.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import removeMeta from './util/removeMeta'
1414
import { getColor, getColorFromValue } from './util/color'
1515
import { isHtmlContext } from './util/html'
1616
import { isCssContext } from './util/css'
17-
import { findLast } from './util/find'
17+
import { findLast, matchClassAttributes } from './util/find'
1818
import { stringifyConfigValue, stringifyCss } from './util/stringify'
1919
import { stringifyScreen, Screen } from './util/screens'
2020
import isObject from './util/isObject'
@@ -327,25 +327,30 @@ export function completionsFromClassList(
327327
}
328328
}
329329

330-
function provideClassAttributeCompletions(
330+
async function provideClassAttributeCompletions(
331331
state: State,
332332
document: TextDocument,
333333
position: Position,
334334
context?: CompletionContext
335-
): CompletionList {
335+
): Promise<CompletionList> {
336336
let str = document.getText({
337337
start: document.positionAt(Math.max(0, document.offsetAt(position) - 500)),
338338
end: position,
339339
})
340340

341-
const match = findLast(/(?:\s|:|\()(?:class(?:Name)?|\[ngClass\])\s*=\s*['"`{]/gi, str)
341+
let matches = matchClassAttributes(
342+
str,
343+
(await state.editor.getConfiguration(document.uri)).tailwindCSS.classAttributes
344+
)
342345

343-
if (match === null) {
346+
if (matches.length === 0) {
344347
return null
345348
}
346349

350+
let match = matches[matches.length - 1]
351+
347352
const lexer =
348-
match[0][0] === ':' || match[0].trim().startsWith('[ngClass]')
353+
match[0][0] === ':' || (match[1].startsWith('[') && match[1].endsWith(']'))
349354
? getComputedClassAttributeLexer()
350355
: getClassAttributeLexer()
351356
lexer.reset(str.substr(match.index + match[0].length - 1))
@@ -490,12 +495,12 @@ function provideAtApplyCompletions(
490495
)
491496
}
492497

493-
function provideClassNameCompletions(
498+
async function provideClassNameCompletions(
494499
state: State,
495500
document: TextDocument,
496501
position: Position,
497502
context?: CompletionContext
498-
): CompletionList {
503+
): Promise<CompletionList> {
499504
if (isCssContext(state, document, position)) {
500505
return provideAtApplyCompletions(state, document, position)
501506
}
@@ -1035,7 +1040,7 @@ export async function doComplete(
10351040
if (state === null) return { items: [], isIncomplete: false }
10361041

10371042
const result =
1038-
provideClassNameCompletions(state, document, position, context) ||
1043+
(await provideClassNameCompletions(state, document, position, context)) ||
10391044
provideCssHelperCompletions(state, document, position) ||
10401045
provideCssDirectiveCompletions(state, document, position) ||
10411046
provideScreenDirectiveCompletions(state, document, position) ||

packages/tailwindcss-language-service/src/util/find.ts

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -172,16 +172,31 @@ async function findCustomClassLists(
172172
return result
173173
}
174174

175-
export function findClassListsInHtmlRange(doc: TextDocument, range?: Range): DocumentClassList[] {
175+
export function matchClassAttributes(text: string, attributes: string[]): RegExpMatchArray[] {
176+
const attrs = attributes.filter((x) => typeof x === 'string').flatMap((a) => [a, `\\[${a}\\]`])
177+
const re = /(?:\s|:|\()(ATTRS)\s*=\s*['"`{]/
178+
return findAll(new RegExp(re.source.replace('ATTRS', attrs.join('|')), 'gi'), text)
179+
}
180+
181+
export async function findClassListsInHtmlRange(
182+
state: State,
183+
doc: TextDocument,
184+
range?: Range
185+
): Promise<DocumentClassList[]> {
176186
const text = doc.getText(range)
177-
const matches = findAll(/(?:\s|:|\()(?:class(?:Name)?|\[ngClass\])\s*=\s*['"`{]/gi, text)
187+
188+
const matches = matchClassAttributes(
189+
text,
190+
(await state.editor.getConfiguration(doc.uri)).tailwindCSS.classAttributes
191+
)
192+
178193
const result: DocumentClassList[] = []
179194

180195
matches.forEach((match) => {
181196
const subtext = text.substr(match.index + match[0].length - 1)
182197

183198
let lexer =
184-
match[0][0] === ':' || match[0].trim().startsWith('[ngClass]')
199+
match[0][0] === ':' || (match[1].startsWith('[') && match[1].endsWith(']'))
185200
? getComputedClassAttributeLexer()
186201
: getClassAttributeLexer()
187202
lexer.reset(subtext)
@@ -273,7 +288,7 @@ export async function findClassListsInRange(
273288
if (mode === 'css') {
274289
classLists = findClassListsInCssRange(doc, range)
275290
} else {
276-
classLists = findClassListsInHtmlRange(doc, range)
291+
classLists = await findClassListsInHtmlRange(state, doc, range)
277292
}
278293
return [...classLists, ...(includeCustom ? await findCustomClassLists(state, doc, range) : [])]
279294
}
@@ -290,7 +305,9 @@ export async function findClassListsInDocument(
290305
if (!boundaries) return []
291306

292307
return flatten([
293-
...boundaries.html.map((range) => findClassListsInHtmlRange(doc, range)),
308+
...(await Promise.all(
309+
boundaries.html.map((range) => findClassListsInHtmlRange(state, doc, range))
310+
)),
294311
...boundaries.css.map((range) => findClassListsInCssRange(doc, range)),
295312
await findCustomClassLists(state, doc),
296313
])

packages/tailwindcss-language-service/src/util/state.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export type Settings = {
3939
tailwindCSS: {
4040
emmetCompletions: boolean
4141
includeLanguages: Record<string, string>
42+
classAttributes: string[]
4243
validate: boolean
4344
showPixelEquivalents: boolean
4445
rootFontSize: number

packages/vscode-tailwindcss/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ This setting allows you to add additional language support. The key of each entr
6666

6767
Enable completions when using [Emmet](https://emmet.io/)-style syntax, for example `div.bg-red-500.uppercase`. **Default: `false`**
6868

69+
### `tailwindCSS.classAttributes`
70+
71+
The HTML attributes for which to provide class completions, hover previews, linting etc. **Default: `class`, `className`, `ngClass`**
72+
6973
### `tailwindCSS.colorDecorators`
7074

7175
Controls whether the editor should render inline color decorators for Tailwind CSS classes and helper functions. **Default: `true`**

packages/vscode-tailwindcss/package.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,18 @@
8181
"default": {},
8282
"markdownDescription": "Enable features in languages that are not supported by default. Add a mapping here between the new language and an already supported language.\n E.g.: `{\"plaintext\": \"html\"}`"
8383
},
84+
"tailwindCSS.classAttributes": {
85+
"type": "array",
86+
"items": {
87+
"type": "string"
88+
},
89+
"default": [
90+
"class",
91+
"className",
92+
"ngClass"
93+
],
94+
"markdownDescription": "The HTML attributes for which to provide class completions, hover previews, linting etc."
95+
},
8496
"tailwindCSS.colorDecorators": {
8597
"type": "boolean",
8698
"default": true,

0 commit comments

Comments
 (0)