add `classAttributes` setting
parent
80e2e5aba5
commit
0f4d93b96e
|
@ -14,7 +14,7 @@ import removeMeta from './util/removeMeta'
|
||||||
import { getColor, getColorFromValue } from './util/color'
|
import { getColor, getColorFromValue } from './util/color'
|
||||||
import { isHtmlContext } from './util/html'
|
import { isHtmlContext } from './util/html'
|
||||||
import { isCssContext } from './util/css'
|
import { isCssContext } from './util/css'
|
||||||
import { findLast } from './util/find'
|
import { findLast, matchClassAttributes } from './util/find'
|
||||||
import { stringifyConfigValue, stringifyCss } from './util/stringify'
|
import { stringifyConfigValue, stringifyCss } from './util/stringify'
|
||||||
import { stringifyScreen, Screen } from './util/screens'
|
import { stringifyScreen, Screen } from './util/screens'
|
||||||
import isObject from './util/isObject'
|
import isObject from './util/isObject'
|
||||||
|
@ -327,25 +327,30 @@ export function completionsFromClassList(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function provideClassAttributeCompletions(
|
async function provideClassAttributeCompletions(
|
||||||
state: State,
|
state: State,
|
||||||
document: TextDocument,
|
document: TextDocument,
|
||||||
position: Position,
|
position: Position,
|
||||||
context?: CompletionContext
|
context?: CompletionContext
|
||||||
): CompletionList {
|
): Promise<CompletionList> {
|
||||||
let str = document.getText({
|
let str = document.getText({
|
||||||
start: document.positionAt(Math.max(0, document.offsetAt(position) - 500)),
|
start: document.positionAt(Math.max(0, document.offsetAt(position) - 500)),
|
||||||
end: position,
|
end: position,
|
||||||
})
|
})
|
||||||
|
|
||||||
const match = findLast(/(?:\s|:|\()(?:class(?:Name)?|\[ngClass\])\s*=\s*['"`{]/gi, str)
|
let matches = matchClassAttributes(
|
||||||
|
str,
|
||||||
|
(await state.editor.getConfiguration(document.uri)).tailwindCSS.classAttributes
|
||||||
|
)
|
||||||
|
|
||||||
if (match === null) {
|
if (matches.length === 0) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let match = matches[matches.length - 1]
|
||||||
|
|
||||||
const lexer =
|
const lexer =
|
||||||
match[0][0] === ':' || match[0].trim().startsWith('[ngClass]')
|
match[0][0] === ':' || (match[1].startsWith('[') && match[1].endsWith(']'))
|
||||||
? getComputedClassAttributeLexer()
|
? getComputedClassAttributeLexer()
|
||||||
: getClassAttributeLexer()
|
: getClassAttributeLexer()
|
||||||
lexer.reset(str.substr(match.index + match[0].length - 1))
|
lexer.reset(str.substr(match.index + match[0].length - 1))
|
||||||
|
@ -490,12 +495,12 @@ function provideAtApplyCompletions(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function provideClassNameCompletions(
|
async function provideClassNameCompletions(
|
||||||
state: State,
|
state: State,
|
||||||
document: TextDocument,
|
document: TextDocument,
|
||||||
position: Position,
|
position: Position,
|
||||||
context?: CompletionContext
|
context?: CompletionContext
|
||||||
): CompletionList {
|
): Promise<CompletionList> {
|
||||||
if (isCssContext(state, document, position)) {
|
if (isCssContext(state, document, position)) {
|
||||||
return provideAtApplyCompletions(state, document, position)
|
return provideAtApplyCompletions(state, document, position)
|
||||||
}
|
}
|
||||||
|
@ -1035,7 +1040,7 @@ export async function doComplete(
|
||||||
if (state === null) return { items: [], isIncomplete: false }
|
if (state === null) return { items: [], isIncomplete: false }
|
||||||
|
|
||||||
const result =
|
const result =
|
||||||
provideClassNameCompletions(state, document, position, context) ||
|
(await provideClassNameCompletions(state, document, position, context)) ||
|
||||||
provideCssHelperCompletions(state, document, position) ||
|
provideCssHelperCompletions(state, document, position) ||
|
||||||
provideCssDirectiveCompletions(state, document, position) ||
|
provideCssDirectiveCompletions(state, document, position) ||
|
||||||
provideScreenDirectiveCompletions(state, document, position) ||
|
provideScreenDirectiveCompletions(state, document, position) ||
|
||||||
|
|
|
@ -172,16 +172,31 @@ async function findCustomClassLists(
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
export function findClassListsInHtmlRange(doc: TextDocument, range?: Range): DocumentClassList[] {
|
export function matchClassAttributes(text: string, attributes: string[]): RegExpMatchArray[] {
|
||||||
|
const attrs = attributes.filter((x) => typeof x === 'string').flatMap((a) => [a, `\\[${a}\\]`])
|
||||||
|
const re = /(?:\s|:|\()(ATTRS)\s*=\s*['"`{]/
|
||||||
|
return findAll(new RegExp(re.source.replace('ATTRS', attrs.join('|')), 'gi'), text)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function findClassListsInHtmlRange(
|
||||||
|
state: State,
|
||||||
|
doc: TextDocument,
|
||||||
|
range?: Range
|
||||||
|
): Promise<DocumentClassList[]> {
|
||||||
const text = doc.getText(range)
|
const text = doc.getText(range)
|
||||||
const matches = findAll(/(?:\s|:|\()(?:class(?:Name)?|\[ngClass\])\s*=\s*['"`{]/gi, text)
|
|
||||||
|
const matches = matchClassAttributes(
|
||||||
|
text,
|
||||||
|
(await state.editor.getConfiguration(doc.uri)).tailwindCSS.classAttributes
|
||||||
|
)
|
||||||
|
|
||||||
const result: DocumentClassList[] = []
|
const result: DocumentClassList[] = []
|
||||||
|
|
||||||
matches.forEach((match) => {
|
matches.forEach((match) => {
|
||||||
const subtext = text.substr(match.index + match[0].length - 1)
|
const subtext = text.substr(match.index + match[0].length - 1)
|
||||||
|
|
||||||
let lexer =
|
let lexer =
|
||||||
match[0][0] === ':' || match[0].trim().startsWith('[ngClass]')
|
match[0][0] === ':' || (match[1].startsWith('[') && match[1].endsWith(']'))
|
||||||
? getComputedClassAttributeLexer()
|
? getComputedClassAttributeLexer()
|
||||||
: getClassAttributeLexer()
|
: getClassAttributeLexer()
|
||||||
lexer.reset(subtext)
|
lexer.reset(subtext)
|
||||||
|
@ -273,7 +288,7 @@ export async function findClassListsInRange(
|
||||||
if (mode === 'css') {
|
if (mode === 'css') {
|
||||||
classLists = findClassListsInCssRange(doc, range)
|
classLists = findClassListsInCssRange(doc, range)
|
||||||
} else {
|
} else {
|
||||||
classLists = findClassListsInHtmlRange(doc, range)
|
classLists = await findClassListsInHtmlRange(state, doc, range)
|
||||||
}
|
}
|
||||||
return [...classLists, ...(includeCustom ? await findCustomClassLists(state, doc, range) : [])]
|
return [...classLists, ...(includeCustom ? await findCustomClassLists(state, doc, range) : [])]
|
||||||
}
|
}
|
||||||
|
@ -290,7 +305,9 @@ export async function findClassListsInDocument(
|
||||||
if (!boundaries) return []
|
if (!boundaries) return []
|
||||||
|
|
||||||
return flatten([
|
return flatten([
|
||||||
...boundaries.html.map((range) => findClassListsInHtmlRange(doc, range)),
|
...(await Promise.all(
|
||||||
|
boundaries.html.map((range) => findClassListsInHtmlRange(state, doc, range))
|
||||||
|
)),
|
||||||
...boundaries.css.map((range) => findClassListsInCssRange(doc, range)),
|
...boundaries.css.map((range) => findClassListsInCssRange(doc, range)),
|
||||||
await findCustomClassLists(state, doc),
|
await findCustomClassLists(state, doc),
|
||||||
])
|
])
|
||||||
|
|
|
@ -39,6 +39,7 @@ export type Settings = {
|
||||||
tailwindCSS: {
|
tailwindCSS: {
|
||||||
emmetCompletions: boolean
|
emmetCompletions: boolean
|
||||||
includeLanguages: Record<string, string>
|
includeLanguages: Record<string, string>
|
||||||
|
classAttributes: string[]
|
||||||
validate: boolean
|
validate: boolean
|
||||||
showPixelEquivalents: boolean
|
showPixelEquivalents: boolean
|
||||||
rootFontSize: number
|
rootFontSize: number
|
||||||
|
|
|
@ -66,6 +66,10 @@ This setting allows you to add additional language support. The key of each entr
|
||||||
|
|
||||||
Enable completions when using [Emmet](https://emmet.io/)-style syntax, for example `div.bg-red-500.uppercase`. **Default: `false`**
|
Enable completions when using [Emmet](https://emmet.io/)-style syntax, for example `div.bg-red-500.uppercase`. **Default: `false`**
|
||||||
|
|
||||||
|
### `tailwindCSS.classAttributes`
|
||||||
|
|
||||||
|
The HTML attributes for which to provide class completions, hover previews, linting etc. **Default: `class`, `className`, `ngClass`**
|
||||||
|
|
||||||
### `tailwindCSS.colorDecorators`
|
### `tailwindCSS.colorDecorators`
|
||||||
|
|
||||||
Controls whether the editor should render inline color decorators for Tailwind CSS classes and helper functions. **Default: `true`**
|
Controls whether the editor should render inline color decorators for Tailwind CSS classes and helper functions. **Default: `true`**
|
||||||
|
|
|
@ -81,6 +81,18 @@
|
||||||
"default": {},
|
"default": {},
|
||||||
"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\"}`"
|
"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\"}`"
|
||||||
},
|
},
|
||||||
|
"tailwindCSS.classAttributes": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"default": [
|
||||||
|
"class",
|
||||||
|
"className",
|
||||||
|
"ngClass"
|
||||||
|
],
|
||||||
|
"markdownDescription": "The HTML attributes for which to provide class completions, hover previews, linting etc."
|
||||||
|
},
|
||||||
"tailwindCSS.colorDecorators": {
|
"tailwindCSS.colorDecorators": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": true,
|
"default": true,
|
||||||
|
|
Loading…
Reference in New Issue