Adopt `getVariants` API
parent
bf57dd14bc
commit
f59adbe35b
|
@ -63,6 +63,7 @@ import {
|
||||||
FeatureFlags,
|
FeatureFlags,
|
||||||
Settings,
|
Settings,
|
||||||
ClassNames,
|
ClassNames,
|
||||||
|
Variant,
|
||||||
} from 'tailwindcss-language-service/src/util/state'
|
} from 'tailwindcss-language-service/src/util/state'
|
||||||
import {
|
import {
|
||||||
provideDiagnostics,
|
provideDiagnostics,
|
||||||
|
@ -1181,19 +1182,29 @@ function isAtRule(node: Node): node is AtRule {
|
||||||
return node.type === 'atrule'
|
return node.type === 'atrule'
|
||||||
}
|
}
|
||||||
|
|
||||||
function getVariants(state: State): Record<string, string> {
|
function getVariants(state: State): Array<Variant> {
|
||||||
|
if (state.jitContext?.getVariants) {
|
||||||
|
return state.jitContext.getVariants()
|
||||||
|
}
|
||||||
|
|
||||||
if (state.jit) {
|
if (state.jit) {
|
||||||
|
let result: Array<Variant> = []
|
||||||
|
// [name, [sort, fn]]
|
||||||
|
// [name, [[sort, fn]]]
|
||||||
|
Array.from(state.jitContext.variantMap as Map<string, [any, any]>).forEach(
|
||||||
|
([variantName, variantFnOrFns]) => {
|
||||||
|
result.push({
|
||||||
|
name: variantName,
|
||||||
|
values: [],
|
||||||
|
isArbitrary: false,
|
||||||
|
hasDash: true,
|
||||||
|
selectors: () => {
|
||||||
function escape(className: string): string {
|
function escape(className: string): string {
|
||||||
let node = state.modules.postcssSelectorParser.module.className()
|
let node = state.modules.postcssSelectorParser.module.className()
|
||||||
node.value = className
|
node.value = className
|
||||||
return dlv(node, 'raws.value', node.value)
|
return dlv(node, 'raws.value', node.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = {}
|
|
||||||
// [name, [sort, fn]]
|
|
||||||
// [name, [[sort, fn]]]
|
|
||||||
Array.from(state.jitContext.variantMap as Map<string, [any, any]>).forEach(
|
|
||||||
([variantName, variantFnOrFns]) => {
|
|
||||||
let fns = (Array.isArray(variantFnOrFns[0]) ? variantFnOrFns : [variantFnOrFns]).map(
|
let fns = (Array.isArray(variantFnOrFns[0]) ? variantFnOrFns : [variantFnOrFns]).map(
|
||||||
([_sort, fn]) => fn
|
([_sort, fn]) => fn
|
||||||
)
|
)
|
||||||
|
@ -1279,7 +1290,9 @@ function getVariants(state: State): Record<string, string> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result[variantName] = definitions.join(', ') || null
|
return definitions
|
||||||
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1311,7 +1324,13 @@ function getVariants(state: State): Record<string, string> {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return variants.reduce((obj, variant) => ({ ...obj, [variant]: null }), {})
|
return variants.map((variant) => ({
|
||||||
|
name: variant,
|
||||||
|
values: [],
|
||||||
|
isArbitrary: false,
|
||||||
|
hasDash: true,
|
||||||
|
selectors: () => [],
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getPlugins(config: any) {
|
async function getPlugins(config: any) {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Settings, State } from './util/state'
|
import { Settings, State, Variant } from './util/state'
|
||||||
import type {
|
import type {
|
||||||
CompletionItem,
|
CompletionItem,
|
||||||
CompletionItemKind,
|
CompletionItemKind,
|
||||||
|
@ -110,7 +110,6 @@ export function completionsFromClassList(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let allVariants = Object.keys(state.variants)
|
|
||||||
let { variants: existingVariants, offset } = getVariantsFromClassName(state, partialClassName)
|
let { variants: existingVariants, offset } = getVariantsFromClassName(state, partialClassName)
|
||||||
|
|
||||||
replacementRange.start.character += offset
|
replacementRange.start.character += offset
|
||||||
|
@ -123,40 +122,77 @@ export function completionsFromClassList(
|
||||||
let items: CompletionItem[] = []
|
let items: CompletionItem[] = []
|
||||||
|
|
||||||
if (!important) {
|
if (!important) {
|
||||||
let shouldSortVariants = !semver.gte(state.version, '2.99.0')
|
let variantOrder = 0
|
||||||
|
|
||||||
|
function variantItem(
|
||||||
|
item: Omit<CompletionItem, 'kind' | 'data' | 'sortText' | 'textEdit'> & {
|
||||||
|
textEdit?: { newText: string; range?: Range }
|
||||||
|
}
|
||||||
|
): CompletionItem {
|
||||||
|
return {
|
||||||
|
kind: 9,
|
||||||
|
data: 'variant',
|
||||||
|
command:
|
||||||
|
item.insertTextFormat === 2 // Snippet
|
||||||
|
? undefined
|
||||||
|
: {
|
||||||
|
title: '',
|
||||||
|
command: 'editor.action.triggerSuggest',
|
||||||
|
},
|
||||||
|
sortText: '-' + naturalExpand(variantOrder++),
|
||||||
|
...item,
|
||||||
|
textEdit: {
|
||||||
|
newText: item.label,
|
||||||
|
range: replacementRange,
|
||||||
|
...item.textEdit,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
items.push(
|
items.push(
|
||||||
...Object.entries(state.variants)
|
...state.variants.flatMap((variant) => {
|
||||||
.filter(([variant]) => !existingVariants.includes(variant))
|
let items: CompletionItem[] = []
|
||||||
.map(([variant, definition], index) => {
|
|
||||||
let resultingVariants = [...existingVariants, variant]
|
if (variant.isArbitrary) {
|
||||||
|
items.push(
|
||||||
|
variantItem({
|
||||||
|
label: `${variant.name}${variant.hasDash ? '-' : ''}[]${sep}`,
|
||||||
|
insertTextFormat: 2,
|
||||||
|
textEdit: {
|
||||||
|
newText: `${variant.name}-[\${1:&}]${sep}\${0}`,
|
||||||
|
},
|
||||||
|
// command: {
|
||||||
|
// title: '',
|
||||||
|
// command: 'tailwindCSS.onInsertArbitraryVariantSnippet',
|
||||||
|
// arguments: [variant.name, replacementRange],
|
||||||
|
// },
|
||||||
|
})
|
||||||
|
)
|
||||||
|
} else if (!existingVariants.includes(variant.name)) {
|
||||||
|
let shouldSortVariants = !semver.gte(state.version, '2.99.0')
|
||||||
|
let resultingVariants = [...existingVariants, variant.name]
|
||||||
|
|
||||||
if (shouldSortVariants) {
|
if (shouldSortVariants) {
|
||||||
|
let allVariants = state.variants.map(({ name }) => name)
|
||||||
resultingVariants = resultingVariants.sort(
|
resultingVariants = resultingVariants.sort(
|
||||||
(a, b) => allVariants.indexOf(b) - allVariants.indexOf(a)
|
(a, b) => allVariants.indexOf(b) - allVariants.indexOf(a)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
items.push(
|
||||||
label: variant + sep,
|
variantItem({
|
||||||
kind: 9,
|
label: `${variant.name}${sep}`,
|
||||||
detail: definition,
|
detail: variant.selectors().join(', '),
|
||||||
data: 'variant',
|
|
||||||
command: {
|
|
||||||
title: '',
|
|
||||||
command: 'editor.action.triggerSuggest',
|
|
||||||
},
|
|
||||||
sortText: '-' + naturalExpand(index),
|
|
||||||
textEdit: {
|
textEdit: {
|
||||||
newText: resultingVariants[resultingVariants.length - 1] + sep,
|
newText: resultingVariants[resultingVariants.length - 1] + sep,
|
||||||
range: replacementRange,
|
|
||||||
},
|
},
|
||||||
additionalTextEdits:
|
additionalTextEdits:
|
||||||
shouldSortVariants && resultingVariants.length > 1
|
shouldSortVariants && resultingVariants.length > 1
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
newText:
|
newText:
|
||||||
resultingVariants.slice(0, resultingVariants.length - 1).join(sep) + sep,
|
resultingVariants.slice(0, resultingVariants.length - 1).join(sep) +
|
||||||
|
sep,
|
||||||
range: {
|
range: {
|
||||||
start: {
|
start: {
|
||||||
...classListRange.start,
|
...classListRange.start,
|
||||||
|
@ -170,7 +206,24 @@ export function completionsFromClassList(
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
: [],
|
: [],
|
||||||
} as CompletionItem
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (variant.values.length) {
|
||||||
|
items.push(
|
||||||
|
...variant.values
|
||||||
|
.filter((value) => !existingVariants.includes(`${variant.name}-${value}`))
|
||||||
|
.map((value) =>
|
||||||
|
variantItem({
|
||||||
|
label: `${variant.name}${variant.hasDash ? '-' : ''}${value}${sep}`,
|
||||||
|
detail: variant.selectors({ value }).join(', '),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return items
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -790,7 +843,12 @@ function provideVariantsDirectiveCompletions(
|
||||||
|
|
||||||
if (/\s+/.test(parts[parts.length - 1])) return null
|
if (/\s+/.test(parts[parts.length - 1])) return null
|
||||||
|
|
||||||
let possibleVariants = Object.keys(state.variants)
|
let possibleVariants = state.variants.flatMap((variant) => {
|
||||||
|
if (variant.values.length) {
|
||||||
|
return variant.values.map((value) => `${variant.name}${variant.hasDash ? '-' : ''}${value}`)
|
||||||
|
}
|
||||||
|
return [variant.name]
|
||||||
|
})
|
||||||
const existingVariants = parts.slice(0, parts.length - 1)
|
const existingVariants = parts.slice(0, parts.length - 1)
|
||||||
|
|
||||||
if (state.jit) {
|
if (state.jit) {
|
||||||
|
|
|
@ -32,7 +32,12 @@ export function getInvalidVariantDiagnostics(
|
||||||
ranges.push(...boundaries.filter((b) => b.type === 'css').map(({ range }) => range))
|
ranges.push(...boundaries.filter((b) => b.type === 'css').map(({ range }) => range))
|
||||||
}
|
}
|
||||||
|
|
||||||
let possibleVariants = Object.keys(state.variants)
|
let possibleVariants = state.variants.flatMap((variant) => {
|
||||||
|
if (variant.values.length) {
|
||||||
|
return variant.values.map((value) => `${variant.name}${variant.hasDash ? '-' : ''}${value}`)
|
||||||
|
}
|
||||||
|
return [variant.name]
|
||||||
|
})
|
||||||
if (state.jit) {
|
if (state.jit) {
|
||||||
possibleVariants.unshift('responsive')
|
possibleVariants.unshift('responsive')
|
||||||
possibleVariants = possibleVariants.filter((v) => !state.screens.includes(v))
|
possibleVariants = possibleVariants.filter((v) => !state.screens.includes(v))
|
||||||
|
|
|
@ -5,17 +5,25 @@ export function getVariantsFromClassName(
|
||||||
state: State,
|
state: State,
|
||||||
className: string
|
className: string
|
||||||
): { variants: string[]; offset: number } {
|
): { variants: string[]; offset: number } {
|
||||||
let allVariants = Object.keys(state.variants)
|
let allVariants = state.variants.flatMap((variant) => {
|
||||||
let parts = splitAtTopLevelOnly(className, state.separator).filter(Boolean)
|
if (variant.values.length) {
|
||||||
|
return variant.values.map((value) => `${variant.name}${variant.hasDash ? '-' : ''}${value}`)
|
||||||
|
}
|
||||||
|
return [variant.name]
|
||||||
|
})
|
||||||
let variants = new Set<string>()
|
let variants = new Set<string>()
|
||||||
let offset = 0
|
let offset = 0
|
||||||
|
let parts = splitAtTopLevelOnly(className, state.separator)
|
||||||
|
if (parts.length < 2) {
|
||||||
|
return { variants: Array.from(variants), offset }
|
||||||
|
}
|
||||||
|
parts = parts.filter(Boolean)
|
||||||
|
|
||||||
for (let part of parts) {
|
for (let part of parts) {
|
||||||
if (
|
if (
|
||||||
allVariants.includes(part) ||
|
allVariants.includes(part) ||
|
||||||
(state.jit &&
|
(state.jit &&
|
||||||
((part.includes('[') && part.endsWith(']')) ||
|
((part.includes('[') && part.endsWith(']')) || part.includes('/')) &&
|
||||||
(part.includes('<') && part.includes('>'))) &&
|
|
||||||
jit.generateRules(state, [`${part}${state.separator}[color:red]`]).rules.length > 0)
|
jit.generateRules(state, [`${part}${state.separator}[color:red]`]).rules.length > 0)
|
||||||
) {
|
) {
|
||||||
variants.add(part)
|
variants.add(part)
|
||||||
|
|
|
@ -80,6 +80,14 @@ export interface FeatureFlags {
|
||||||
experimental: string[]
|
experimental: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Variant {
|
||||||
|
name: string
|
||||||
|
values: string[]
|
||||||
|
isArbitrary: boolean
|
||||||
|
hasDash: boolean
|
||||||
|
selectors: (params?: { value?: string; label?: string }) => string[]
|
||||||
|
}
|
||||||
|
|
||||||
export interface State {
|
export interface State {
|
||||||
enabled: boolean
|
enabled: boolean
|
||||||
configPath?: string
|
configPath?: string
|
||||||
|
@ -90,7 +98,7 @@ export interface State {
|
||||||
dependencies?: string[]
|
dependencies?: string[]
|
||||||
plugins?: any
|
plugins?: any
|
||||||
screens?: string[]
|
screens?: string[]
|
||||||
variants?: Record<string, string | null>
|
variants?: Variant[]
|
||||||
corePlugins?: string[]
|
corePlugins?: string[]
|
||||||
modules?: {
|
modules?: {
|
||||||
tailwindcss?: { version: string; module: any }
|
tailwindcss?: { version: string; module: any }
|
||||||
|
|
|
@ -26,6 +26,7 @@ import {
|
||||||
ProviderResult,
|
ProviderResult,
|
||||||
SnippetString,
|
SnippetString,
|
||||||
TextEdit,
|
TextEdit,
|
||||||
|
TextEditorSelectionChangeKind,
|
||||||
} from 'vscode'
|
} from 'vscode'
|
||||||
import {
|
import {
|
||||||
LanguageClient,
|
LanguageClient,
|
||||||
|
@ -149,6 +150,62 @@ export async function activate(context: ExtensionContext) {
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// context.subscriptions.push(
|
||||||
|
// commands.registerCommand(
|
||||||
|
// 'tailwindCSS.onInsertArbitraryVariantSnippet',
|
||||||
|
// (
|
||||||
|
// variantName: string,
|
||||||
|
// range: {
|
||||||
|
// start: { line: number; character: number }
|
||||||
|
// end: { line: number; character: number }
|
||||||
|
// }
|
||||||
|
// ) => {
|
||||||
|
// let listener = Window.onDidChangeTextEditorSelection((event) => {
|
||||||
|
// if (event.selections.length !== 1) {
|
||||||
|
// listener.dispose()
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let document = event.textEditor.document
|
||||||
|
// let selection = event.selections[0]
|
||||||
|
|
||||||
|
// let line = document.lineAt(range.start.line)
|
||||||
|
// let lineRangeFromCompletion = new Range(
|
||||||
|
// range.start.line,
|
||||||
|
// range.start.character,
|
||||||
|
// line.range.end.line,
|
||||||
|
// line.range.end.character
|
||||||
|
// )
|
||||||
|
// let lineText = document.getText(lineRangeFromCompletion)
|
||||||
|
// let match = lineText.match(/^(\S+)]:/)
|
||||||
|
|
||||||
|
// if (!match) {
|
||||||
|
// listener.dispose()
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let arbitraryValueRange = new Range(
|
||||||
|
// lineRangeFromCompletion.start.translate(0, variantName.length + 2),
|
||||||
|
// lineRangeFromCompletion.start.translate(0, match[1].length)
|
||||||
|
// )
|
||||||
|
|
||||||
|
// if (!arbitraryValueRange.contains(selection)) {
|
||||||
|
// listener.dispose()
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (
|
||||||
|
// event.kind === TextEditorSelectionChangeKind.Command &&
|
||||||
|
// selection.isEmpty &&
|
||||||
|
// selection.start.isEqual(arbitraryValueRange.end.translate(0, 2))
|
||||||
|
// ) {
|
||||||
|
// commands.executeCommand('editor.action.triggerSuggest')
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// context.subscriptions.push(listener)
|
||||||
|
// }
|
||||||
|
// )
|
||||||
|
// )
|
||||||
|
|
||||||
let watcher = Workspace.createFileSystemWatcher(`**/${CONFIG_FILE_GLOB}`, false, true, true)
|
let watcher = Workspace.createFileSystemWatcher(`**/${CONFIG_FILE_GLOB}`, false, true, true)
|
||||||
|
|
||||||
watcher.onDidCreate((uri) => {
|
watcher.onDidCreate((uri) => {
|
||||||
|
|
Loading…
Reference in New Issue