Merge branch 'next' into diagnostics

master
Brad Cornes 2020-05-10 14:38:48 +01:00
commit 4d87c66139
4 changed files with 142 additions and 107 deletions

View File

@ -46,11 +46,12 @@ function getClassNamesFromSelector(selector) {
return classNames
}
async function process(ast) {
async function process(groups) {
const tree = {}
const commonContext = {}
ast.root.walkRules((rule) => {
groups.forEach((group) => {
group.root.walkRules((rule) => {
const classNames = getClassNamesFromSelector(rule.selector)
const decls = {}
@ -110,6 +111,7 @@ async function process(ast) {
}
if (classNames[i].__rule) {
dset(tree, [...baseKeys, ...index, '__rule'], true)
dset(tree, [...baseKeys, ...index, '__source'], group.source)
dsetEach(tree, [...baseKeys, ...index], decls)
}
@ -140,6 +142,7 @@ async function process(ast) {
}
}
})
})
return { classNames: tree, context: commonContext }
}

View File

@ -83,12 +83,16 @@ export default async function getClassNames(
}
hook.unwatch()
const ast = await postcss([tailwindcss(configPath)]).process(
`
@tailwind components;
@tailwind utilities;
`,
{ from: undefined }
const [base, components, utilities] = await Promise.all(
[
semver.gte(version, '0.99.0') ? 'base' : 'preflight',
'components',
'utilities',
].map((group) =>
postcss([tailwindcss(configPath)]).process(`@tailwind ${group};`, {
from: undefined,
})
)
)
hook.unhook()
@ -111,7 +115,11 @@ export default async function getClassNames(
configPath,
config: resolvedConfig,
separator: typeof userSeperator === 'undefined' ? ':' : userSeperator,
classNames: await extractClassNames(ast),
classNames: await extractClassNames([
{ root: base.root, source: 'base' },
{ root: components.root, source: 'components' },
{ root: utilities.root, source: 'utilities' },
]),
dependencies: hook.deps,
plugins: getPlugins(config),
variants: getVariants({ config, version, postcss, browserslist }),

View File

@ -28,7 +28,8 @@ import { ensureArray } from '../../util/array'
function completionsFromClassList(
state: State,
classList: string,
classListRange: Range
classListRange: Range,
filter?: (item: CompletionItem) => boolean
): CompletionList {
let classNames = classList.split(/[\s+]/)
const partialClassName = classNames[classNames.length - 1]
@ -68,8 +69,8 @@ function completionsFromClassList(
return {
isIncomplete: false,
items: Object.keys(isSubset ? subset : state.classNames.classNames).map(
(className, index) => {
items: Object.keys(isSubset ? subset : state.classNames.classNames)
.map((className, index) => {
let label = className
let kind: CompletionItemKind = CompletionItemKind.Constant
let documentation: string = null
@ -88,7 +89,7 @@ function completionsFromClassList(
}
}
return {
const item = {
label,
kind,
documentation,
@ -100,8 +101,14 @@ function completionsFromClassList(
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
return completionsFromClassList(state, classList, {
return completionsFromClassList(
state,
classList,
{
start: {
line: position.line,
character: position.character - classList.length,
},
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(

View File

@ -3,9 +3,10 @@ import isObject from '../../util/isObject'
export default function removeMeta(obj: any): any {
let result = {}
for (let key in obj) {
if (key.substr(0, 2) === '__') continue
if (isObject(obj[key])) {
result[key] = removeMeta(obj[key])
} else if (key.substr(0, 2) !== '__') {
} else {
result[key] = obj[key]
}
}