filter @apply completions
parent
ef1f922c86
commit
c9fea34deb
|
@ -46,99 +46,102 @@ function getClassNamesFromSelector(selector) {
|
||||||
return classNames
|
return classNames
|
||||||
}
|
}
|
||||||
|
|
||||||
async function process(ast) {
|
async function process(groups) {
|
||||||
const tree = {}
|
const tree = {}
|
||||||
const commonContext = {}
|
const commonContext = {}
|
||||||
|
|
||||||
ast.root.walkRules((rule) => {
|
groups.forEach((group) => {
|
||||||
const classNames = getClassNamesFromSelector(rule.selector)
|
group.root.walkRules((rule) => {
|
||||||
|
const classNames = getClassNamesFromSelector(rule.selector)
|
||||||
|
|
||||||
const decls = {}
|
const decls = {}
|
||||||
rule.walkDecls((decl) => {
|
rule.walkDecls((decl) => {
|
||||||
if (decls[decl.prop]) {
|
if (decls[decl.prop]) {
|
||||||
decls[decl.prop] = [
|
decls[decl.prop] = [
|
||||||
...(Array.isArray(decls[decl.prop])
|
...(Array.isArray(decls[decl.prop])
|
||||||
? decls[decl.prop]
|
? decls[decl.prop]
|
||||||
: [decls[decl.prop]]),
|
: [decls[decl.prop]]),
|
||||||
decl.value,
|
decl.value,
|
||||||
]
|
]
|
||||||
} else {
|
} else {
|
||||||
decls[decl.prop] = decl.value
|
decls[decl.prop] = decl.value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
let p = rule
|
||||||
|
const keys = []
|
||||||
|
while (p.parent.type !== 'root') {
|
||||||
|
p = p.parent
|
||||||
|
if (p.type === 'atrule') {
|
||||||
|
keys.push(`@${p.name} ${p.params}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < classNames.length; i++) {
|
||||||
|
const context = keys.concat([])
|
||||||
|
const baseKeys = classNames[i].className.split('__TAILWIND_SEPARATOR__')
|
||||||
|
const contextKeys = baseKeys.slice(0, baseKeys.length - 1)
|
||||||
|
const index = []
|
||||||
|
|
||||||
|
const existing = dlv(tree, baseKeys)
|
||||||
|
if (typeof existing !== 'undefined') {
|
||||||
|
if (Array.isArray(existing)) {
|
||||||
|
const scopeIndex = existing.findIndex(
|
||||||
|
(x) =>
|
||||||
|
x.__scope === classNames[i].scope &&
|
||||||
|
arraysEqual(existing.__context, context)
|
||||||
|
)
|
||||||
|
if (scopeIndex > -1) {
|
||||||
|
keys.unshift(scopeIndex)
|
||||||
|
index.push(scopeIndex)
|
||||||
|
} else {
|
||||||
|
keys.unshift(existing.length)
|
||||||
|
index.push(existing.length)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (
|
||||||
|
existing.__scope !== classNames[i].scope ||
|
||||||
|
!arraysEqual(existing.__context, context)
|
||||||
|
) {
|
||||||
|
dset(tree, baseKeys, [existing])
|
||||||
|
keys.unshift(1)
|
||||||
|
index.push(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (classNames[i].__rule) {
|
||||||
|
dset(tree, [...baseKeys, ...index, '__rule'], true)
|
||||||
|
dset(tree, [...baseKeys, ...index, '__source'], group.source)
|
||||||
|
|
||||||
|
dsetEach(tree, [...baseKeys, ...index], decls)
|
||||||
|
}
|
||||||
|
if (classNames[i].__pseudo) {
|
||||||
|
dset(tree, [...baseKeys, '__pseudo'], classNames[i].__pseudo)
|
||||||
|
}
|
||||||
|
dset(tree, [...baseKeys, ...index, '__scope'], classNames[i].scope)
|
||||||
|
dset(
|
||||||
|
tree,
|
||||||
|
[...baseKeys, ...index, '__context'],
|
||||||
|
context.concat([]).reverse()
|
||||||
|
)
|
||||||
|
|
||||||
|
// common context
|
||||||
|
if (classNames[i].__pseudo) {
|
||||||
|
context.push(...classNames[i].__pseudo)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < contextKeys.length; i++) {
|
||||||
|
if (typeof commonContext[contextKeys[i]] === 'undefined') {
|
||||||
|
commonContext[contextKeys[i]] = context
|
||||||
|
} else {
|
||||||
|
commonContext[contextKeys[i]] = intersection(
|
||||||
|
commonContext[contextKeys[i]],
|
||||||
|
context
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
let p = rule
|
|
||||||
const keys = []
|
|
||||||
while (p.parent.type !== 'root') {
|
|
||||||
p = p.parent
|
|
||||||
if (p.type === 'atrule') {
|
|
||||||
keys.push(`@${p.name} ${p.params}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < classNames.length; i++) {
|
|
||||||
const context = keys.concat([])
|
|
||||||
const baseKeys = classNames[i].className.split('__TAILWIND_SEPARATOR__')
|
|
||||||
const contextKeys = baseKeys.slice(0, baseKeys.length - 1)
|
|
||||||
const index = []
|
|
||||||
|
|
||||||
const existing = dlv(tree, baseKeys)
|
|
||||||
if (typeof existing !== 'undefined') {
|
|
||||||
if (Array.isArray(existing)) {
|
|
||||||
const scopeIndex = existing.findIndex(
|
|
||||||
(x) =>
|
|
||||||
x.__scope === classNames[i].scope &&
|
|
||||||
arraysEqual(existing.__context, context)
|
|
||||||
)
|
|
||||||
if (scopeIndex > -1) {
|
|
||||||
keys.unshift(scopeIndex)
|
|
||||||
index.push(scopeIndex)
|
|
||||||
} else {
|
|
||||||
keys.unshift(existing.length)
|
|
||||||
index.push(existing.length)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (
|
|
||||||
existing.__scope !== classNames[i].scope ||
|
|
||||||
!arraysEqual(existing.__context, context)
|
|
||||||
) {
|
|
||||||
dset(tree, baseKeys, [existing])
|
|
||||||
keys.unshift(1)
|
|
||||||
index.push(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (classNames[i].__rule) {
|
|
||||||
dset(tree, [...baseKeys, ...index, '__rule'], true)
|
|
||||||
|
|
||||||
dsetEach(tree, [...baseKeys, ...index], decls)
|
|
||||||
}
|
|
||||||
if (classNames[i].__pseudo) {
|
|
||||||
dset(tree, [...baseKeys, '__pseudo'], classNames[i].__pseudo)
|
|
||||||
}
|
|
||||||
dset(tree, [...baseKeys, ...index, '__scope'], classNames[i].scope)
|
|
||||||
dset(
|
|
||||||
tree,
|
|
||||||
[...baseKeys, ...index, '__context'],
|
|
||||||
context.concat([]).reverse()
|
|
||||||
)
|
|
||||||
|
|
||||||
// common context
|
|
||||||
if (classNames[i].__pseudo) {
|
|
||||||
context.push(...classNames[i].__pseudo)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < contextKeys.length; i++) {
|
|
||||||
if (typeof commonContext[contextKeys[i]] === 'undefined') {
|
|
||||||
commonContext[contextKeys[i]] = context
|
|
||||||
} else {
|
|
||||||
commonContext[contextKeys[i]] = intersection(
|
|
||||||
commonContext[contextKeys[i]],
|
|
||||||
context
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return { classNames: tree, context: commonContext }
|
return { classNames: tree, context: commonContext }
|
||||||
|
|
|
@ -83,12 +83,12 @@ export default async function getClassNames(
|
||||||
}
|
}
|
||||||
hook.unwatch()
|
hook.unwatch()
|
||||||
|
|
||||||
const ast = await postcss([tailwindcss(configPath)]).process(
|
const [components, utilities] = await Promise.all(
|
||||||
`
|
['components', 'utilities'].map((group) =>
|
||||||
@tailwind components;
|
postcss([tailwindcss(configPath)]).process(`@tailwind ${group};`, {
|
||||||
@tailwind utilities;
|
from: undefined,
|
||||||
`,
|
})
|
||||||
{ from: undefined }
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
hook.unhook()
|
hook.unhook()
|
||||||
|
@ -111,7 +111,10 @@ export default async function getClassNames(
|
||||||
configPath,
|
configPath,
|
||||||
config: resolvedConfig,
|
config: resolvedConfig,
|
||||||
separator: typeof userSeperator === 'undefined' ? ':' : userSeperator,
|
separator: typeof userSeperator === 'undefined' ? ':' : userSeperator,
|
||||||
classNames: await extractClassNames(ast),
|
classNames: await extractClassNames([
|
||||||
|
{ root: components.root, source: 'components' },
|
||||||
|
{ root: utilities.root, source: 'utilities' },
|
||||||
|
]),
|
||||||
dependencies: hook.deps,
|
dependencies: hook.deps,
|
||||||
plugins: getPlugins(config),
|
plugins: getPlugins(config),
|
||||||
variants: getVariants({ config, version, postcss, browserslist }),
|
variants: getVariants({ config, version, postcss, browserslist }),
|
||||||
|
|
|
@ -28,7 +28,8 @@ import { ensureArray } from '../../util/array'
|
||||||
function completionsFromClassList(
|
function completionsFromClassList(
|
||||||
state: State,
|
state: State,
|
||||||
classList: string,
|
classList: string,
|
||||||
classListRange: Range
|
classListRange: Range,
|
||||||
|
filter?: (item: CompletionItem) => boolean
|
||||||
): CompletionList {
|
): CompletionList {
|
||||||
let classNames = classList.split(/[\s+]/)
|
let classNames = classList.split(/[\s+]/)
|
||||||
const partialClassName = classNames[classNames.length - 1]
|
const partialClassName = classNames[classNames.length - 1]
|
||||||
|
@ -68,8 +69,8 @@ function completionsFromClassList(
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isIncomplete: false,
|
isIncomplete: false,
|
||||||
items: Object.keys(isSubset ? subset : state.classNames.classNames).map(
|
items: Object.keys(isSubset ? subset : state.classNames.classNames)
|
||||||
(className, index) => {
|
.map((className, index) => {
|
||||||
let label = className
|
let label = className
|
||||||
let kind: CompletionItemKind = CompletionItemKind.Constant
|
let kind: CompletionItemKind = CompletionItemKind.Constant
|
||||||
let documentation: string = null
|
let documentation: string = null
|
||||||
|
@ -88,7 +89,7 @@ function completionsFromClassList(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
const item = {
|
||||||
label,
|
label,
|
||||||
kind,
|
kind,
|
||||||
documentation,
|
documentation,
|
||||||
|
@ -100,8 +101,14 @@ function completionsFromClassList(
|
||||||
range: replacementRange,
|
range: replacementRange,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
|
||||||
),
|
if (filter && !filter(item)) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return item
|
||||||
|
})
|
||||||
|
.filter((item) => item !== null),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,13 +182,29 @@ function provideAtApplyCompletions(
|
||||||
|
|
||||||
const classList = match.groups.classList
|
const classList = match.groups.classList
|
||||||
|
|
||||||
return completionsFromClassList(state, classList, {
|
return completionsFromClassList(
|
||||||
start: {
|
state,
|
||||||
line: position.line,
|
classList,
|
||||||
character: position.character - classList.length,
|
{
|
||||||
|
start: {
|
||||||
|
line: position.line,
|
||||||
|
character: position.character - classList.length,
|
||||||
|
},
|
||||||
|
end: position,
|
||||||
},
|
},
|
||||||
end: position,
|
(item) => {
|
||||||
})
|
// TODO: first line excludes all subtrees but there could _technically_ be
|
||||||
|
// valid apply-able class names in there. Will be correct in 99% of cases
|
||||||
|
if (item.kind === CompletionItemKind.Module) return false
|
||||||
|
let info = dlv(state.classNames.classNames, item.data)
|
||||||
|
return (
|
||||||
|
!Array.isArray(info) &&
|
||||||
|
info.__source === 'utilities' &&
|
||||||
|
(info.__context || []).length === 0 &&
|
||||||
|
(info.__pseudo || []).length === 0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function provideClassNameCompletions(
|
function provideClassNameCompletions(
|
||||||
|
|
Loading…
Reference in New Issue