improve support for tailwindcss v2

master
Brad Cornes 2020-11-19 17:34:59 +00:00
parent 0cce870c13
commit 2c05f5df3a
11 changed files with 24389 additions and 95 deletions

File diff suppressed because it is too large Load Diff

View File

@ -5,15 +5,15 @@
"preview": true,
"author": "Brad Cornes <hello@bradley.dev>",
"license": "MIT",
"version": "0.4.3",
"homepage": "https://github.com/tailwindcss/intellisense",
"version": "0.5.0",
"homepage": "https://github.com/tailwindlabs/tailwindcss-intellisense",
"bugs": {
"url": "https://github.com/tailwindcss/intellisense/issues",
"url": "https://github.com/tailwindlabs/tailwindcss-intellisense/issues",
"email": "hello@bradley.dev"
},
"repository": {
"type": "git",
"url": "https://github.com/tailwindcss/intellisense.git"
"url": "https://github.com/tailwindlabs/tailwindcss-intellisense.git"
},
"publisher": "bradlc",
"keywords": [
@ -177,6 +177,7 @@
"@types/node": "^13.9.3",
"@types/vscode": "^1.32.0",
"@zeit/ncc": "^0.22.0",
"bufferutil": "^4.0.2",
"callsite": "^1.0.0",
"chokidar": "^3.3.1",
"debounce": "^1.2.0",
@ -196,11 +197,12 @@
"rimraf": "^3.0.2",
"semver": "^7.3.2",
"stack-trace": "0.0.10",
"tailwindcss-language-service": "0.0.3",
"tailwindcss-language-service": "0.0.4",
"terser": "^4.6.12",
"tiny-invariant": "^1.1.0",
"tslint": "^5.16.0",
"typescript": "^3.8.3",
"utf-8-validate": "^5.0.3",
"vsce": "^1.76.1",
"vscode-languageclient": "^6.1.3",
"vscode-languageserver": "^6.1.1",

View File

@ -1,6 +1,4 @@
import selectorParser from 'postcss-selector-parser'
import fs from 'fs'
import path from 'path'
import dset from 'dset'
import dlv from 'dlv'
@ -83,48 +81,43 @@ async function process(groups) {
const contextKeys = baseKeys.slice(0, baseKeys.length - 1)
const index = []
const existing = dlv(tree, baseKeys)
const existing = dlv(tree, [...baseKeys, '__info'])
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)
}
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)
}
dset(tree, [...baseKeys, '__info'], [existing])
index.push(1)
}
}
if (classNames[i].__rule) {
dset(tree, [...baseKeys, ...index, '__rule'], true)
dset(tree, [...baseKeys, ...index, '__source'], group.source)
dset(tree, [...baseKeys, '__info', ...index, '__rule'], true)
dset(
tree,
[...baseKeys, '__info', ...index, '__source'],
group.source
)
dsetEach(tree, [...baseKeys, ...index], decls)
dsetEach(tree, [...baseKeys, '__info', ...index], decls)
}
dset(tree, [...baseKeys, ...index, '__pseudo'], classNames[i].__pseudo)
dset(tree, [...baseKeys, ...index, '__scope'], classNames[i].scope)
dset(
tree,
[...baseKeys, ...index, '__context'],
[...baseKeys, '__info', ...index, '__pseudo'],
classNames[i].__pseudo
)
dset(
tree,
[...baseKeys, '__info', ...index, '__scope'],
classNames[i].scope
)
dset(
tree,
[...baseKeys, '__info', ...index, '__context'],
context.concat([]).reverse()
)
// common context
context.push(...classNames[i].__pseudo)
context.push(...classNames[i].__pseudo.map((x) => `&${x}`))
for (let i = 0; i < contextKeys.length; i++) {
if (typeof commonContext[contextKeys[i]] === 'undefined') {

View File

@ -1,11 +1,11 @@
{
"name": "tailwindcss-language-service",
"version": "0.0.3",
"version": "0.0.4",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"version": "0.0.3",
"version": "0.0.4",
"dependencies": {
"@ctrl/tinycolor": "^3.1.4",
"@types/moo": "^0.5.3",

View File

@ -1,6 +1,6 @@
{
"name": "tailwindcss-language-service",
"version": "0.0.3",
"version": "0.0.4",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"files": [

View File

@ -57,7 +57,10 @@ function completionsFromClassList(
for (let i = parts.length - 1; i > 0; i--) {
let keys = parts.slice(0, i).filter(Boolean)
subset = dlv(state.classNames.classNames, keys)
if (typeof subset !== 'undefined' && typeof subset.__rule === 'undefined') {
if (
typeof subset !== 'undefined' &&
typeof dlv(subset, ['__info', '__rule']) === 'undefined'
) {
isSubset = true
subsetKey = keys
replacementRange = {
@ -74,50 +77,75 @@ function completionsFromClassList(
}
}
// console.log(Object.keys(isSubset ? subset : state.classNames.classNames))
return {
isIncomplete: false,
items: Object.keys(isSubset ? subset : state.classNames.classNames)
.map((className, index) => {
let label = className
let kind: CompletionItemKind = 21
let documentation: string = null
let command: any
let sortText = naturalExpand(index)
if (isContextItem(state, [...subsetKey, className])) {
kind = 9
command = { title: '', command: 'editor.action.triggerSuggest' }
label += sep
sortText = '-' + sortText // move to top
} else {
const color = getColor(state, [className])
if (color !== null) {
kind = 16
if (typeof color !== 'string' && color.a !== 0) {
documentation = color.toRgbString()
}
.filter((k) => k !== '__info')
.filter((className) => isContextItem(state, [...subsetKey, className]))
.map(
(className, index): CompletionItem => {
return {
label: className + sep,
kind: 9,
documentation: null,
command: {
title: '',
command: 'editor.action.triggerSuggest',
},
sortText: '-' + naturalExpand(index),
data: [...subsetKey, className],
textEdit: {
newText: className + sep,
range: replacementRange,
},
}
}
)
.concat(
Object.keys(isSubset ? subset : state.classNames.classNames)
.filter((className) =>
dlv(state.classNames.classNames, [
...subsetKey,
className,
'__info',
])
)
.map((className, index) => {
let kind: CompletionItemKind = 21
let documentation: string = null
const item = {
label,
kind,
documentation,
command,
sortText,
data: [...subsetKey, className],
textEdit: {
newText: label,
range: replacementRange,
},
const color = getColor(state, [className])
if (color !== null) {
kind = 16
if (typeof color !== 'string' && color.a !== 0) {
documentation = color.toRgbString()
}
}
return {
label: className,
kind,
documentation,
sortText: naturalExpand(index),
data: [...subsetKey, className],
textEdit: {
newText: className,
range: replacementRange,
},
}
})
)
.filter((item) => {
if (item === null) {
return false
}
if (filter && !filter(item)) {
return null
return false
}
return item
})
.filter((item) => item !== null),
return true
}),
}
}
@ -199,7 +227,10 @@ function provideAtApplyCompletions(
},
(item) => {
if (item.kind === 9) {
return semver.gte(state.version, '2.0.0-alpha.1') || flagEnabled(state, 'applyComplexClasses')
return (
semver.gte(state.version, '2.0.0-alpha.1') ||
flagEnabled(state, 'applyComplexClasses')
)
}
let validated = validateApply(state, item.data)
return validated !== null && validated.isApplyable === true
@ -708,8 +739,8 @@ export function resolveCompletionItem(
return item
}
const className = dlv(state.classNames.classNames, item.data)
if (isContextItem(state, item.data)) {
const className = dlv(state.classNames.classNames, [...item.data, '__info'])
if (item.kind === 9) {
item.detail = state.classNames.context[
item.data[item.data.length - 1]
].join(', ')
@ -729,13 +760,19 @@ export function resolveCompletionItem(
}
function isContextItem(state: State, keys: string[]): boolean {
const item = dlv(state.classNames.classNames, keys)
return Boolean(
isObject(item) &&
!item.__rule &&
!Array.isArray(item) &&
state.classNames.context[keys[keys.length - 1]]
)
const item = dlv(state.classNames.classNames, [keys])
if (!isObject(item)) {
return false
}
if (!state.classNames.context[keys[keys.length - 1]]) {
return false
}
if (Object.keys(item).filter((x) => x !== '__info').length > 0) {
return true
}
return isObject(item.__info) && !item.__info.__rule
}
function stringifyDecls(obj: any): string {

View File

@ -89,13 +89,17 @@ function provideClassNameHover(
}
}
const css = stringifyCss(
className.className,
dlv(state.classNames.classNames, [...parts, '__info'])
)
if (!css) return null
return {
contents: {
language: 'css',
value: stringifyCss(
className.className,
dlv(state.classNames.classNames, parts)
),
value: css,
},
range: className.range,
}

View File

@ -29,7 +29,7 @@ export function getColor(
state: State,
keys: string[]
): TinyColor | string | null {
const item = dlv(state.classNames.classNames, keys)
const item = dlv(state.classNames.classNames, [...keys, '__info'])
if (!item.__rule) return null
const props = Object.keys(removeMeta(item))
if (props.length === 0) return null

View File

@ -8,8 +8,9 @@ export function getClassNameParts(state: State, className: string): string[] {
let parts: string[] = className.split(separator)
if (parts.length === 1) {
return dlv(state.classNames.classNames, [className, '__rule']) === true ||
Array.isArray(dlv(state.classNames.classNames, [className]))
return dlv(state.classNames.classNames, [className, '__info', '__rule']) ===
true ||
Array.isArray(dlv(state.classNames.classNames, [className, '__info']))
? [className]
: null
}
@ -34,8 +35,8 @@ export function getClassNameParts(state: State, className: string): string[] {
return possibilities.find((key) => {
if (
dlv(state.classNames.classNames, [...key, '__rule']) === true ||
Array.isArray(dlv(state.classNames.classNames, [...key]))
dlv(state.classNames.classNames, [...key, '__info', '__rule']) === true ||
Array.isArray(dlv(state.classNames.classNames, [...key, '__info']))
) {
return true
}

View File

@ -10,7 +10,7 @@ export function getClassNameDecls(
const parts = getClassNameParts(state, className)
if (!parts) return null
const info = dlv(state.classNames.classNames, parts)
const info = dlv(state.classNames.classNames, [...parts, '__info'])
if (Array.isArray(info)) {
return info.map(removeMeta)

View File

@ -10,7 +10,7 @@ export function getClassNameMeta(
? classNameOrParts
: getClassNameParts(state, classNameOrParts)
if (!parts) return null
const info = dlv(state.classNames.classNames, parts)
const info = dlv(state.classNames.classNames, [...parts, '__info'])
if (Array.isArray(info)) {
return info.map((i) => ({