Use `itemDefaults` to reduce size of completion lists (#706)

* Use completion list `itemDefaults`

* more defaults
master
Brad Cornes 2023-01-27 10:30:27 +00:00 committed by GitHub
parent 7235aeab48
commit 637f838725
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 520 additions and 422 deletions

View File

@ -378,8 +378,21 @@ async function createProjectService(
const disposables: Array<Disposable | Promise<Disposable>> = [] const disposables: Array<Disposable | Promise<Disposable>> = []
let documentSelector = projectConfig.documentSelector let documentSelector = projectConfig.documentSelector
let itemDefaults =
params.capabilities.textDocument?.completion?.completionList?.itemDefaults ?? []
// VS Code _does_ support `itemDefaults.data` since at least 1.67.0 (this extension's min version)
// but it doesn't advertise it in its capabilities. So we manually add it here.
// See also: https://github.com/microsoft/vscode-languageserver-node/issues/1181
if (params.clientInfo?.name === 'Visual Studio Code' && !itemDefaults.includes('data')) {
itemDefaults.push('data')
}
let state: State = { let state: State = {
enabled: false, enabled: false,
completionItemData: {
_projectKey: projectKey,
},
editor: { editor: {
connection, connection,
folder, folder,
@ -390,6 +403,7 @@ async function createProjectService(
capabilities: { capabilities: {
configuration: true, configuration: true,
diagnosticRelatedInformation: true, diagnosticRelatedInformation: true,
itemDefaults,
}, },
documents: documentService.documents, documents: documentService.documents,
getConfiguration, getConfiguration,
@ -1114,21 +1128,13 @@ async function createProjectService(
let settings = await state.editor.getConfiguration(document.uri) let settings = await state.editor.getConfiguration(document.uri)
if (!settings.tailwindCSS.suggestions) return null if (!settings.tailwindCSS.suggestions) return null
if (await isExcluded(state, document)) return null if (await isExcluded(state, document)) return null
let result = await doComplete(state, document, params.position, params.context) return doComplete(state, document, params.position, params.context)
if (!result) return result
return {
isIncomplete: result.isIncomplete,
items: result.items.map((item) => ({
...item,
data: { projectKey, originalData: item.data },
})),
}
}, null) }, null)
}, },
onCompletionResolve(item: CompletionItem): Promise<CompletionItem> { onCompletionResolve(item: CompletionItem): Promise<CompletionItem> {
return withFallback(() => { return withFallback(() => {
if (!state.enabled) return null if (!state.enabled) return null
return resolveCompletionItem(state, { ...item, data: item.data?.originalData }) return resolveCompletionItem(state, item)
}, null) }, null)
}, },
async onCodeAction(params: CodeActionParams): Promise<CodeAction[]> { async onCodeAction(params: CodeActionParams): Promise<CodeAction[]> {
@ -2162,7 +2168,7 @@ class TW {
} }
async onCompletionResolve(item: CompletionItem): Promise<CompletionItem> { async onCompletionResolve(item: CompletionItem): Promise<CompletionItem> {
return this.projects.get(item.data.projectKey)?.onCompletionResolve(item) ?? null return this.projects.get(item.data?._projectKey)?.onCompletionResolve(item) ?? null
} }
onCodeAction(params: CodeActionParams): Promise<CodeAction[]> { onCodeAction(params: CodeActionParams): Promise<CodeAction[]> {

View File

@ -97,12 +97,13 @@ export function completionsFromClassList(
} }
if (modifiers) { if (modifiers) {
return { return withDefaults(
{
isIncomplete: false, isIncomplete: false,
items: modifiers.map((modifier, index) => { items: modifiers.map((modifier, index) => {
let className = `${beforeSlash}/${modifier}` let className = `${beforeSlash}/${modifier}`
let kind: CompletionItemKind = 21 let kind: CompletionItemKind = 21
let documentation: string = null let documentation: string | undefined
const color = getColor(state, className) const color = getColor(state, className)
if (color !== null) { if (color !== null) {
@ -114,17 +115,18 @@ export function completionsFromClassList(
return { return {
label: className, label: className,
documentation, ...(documentation ? { documentation } : {}),
kind, kind,
sortText: naturalExpand(index), sortText: naturalExpand(index),
data: [className],
textEdit: {
newText: className,
range: replacementRange,
},
} }
}), }),
} },
{
range: replacementRange,
data: state.completionItemData,
},
state.editor.capabilities.itemDefaults
)
} }
} }
@ -141,13 +143,14 @@ export function completionsFromClassList(
let variantOrder = 0 let variantOrder = 0
function variantItem( function variantItem(
item: Omit<CompletionItem, 'kind' | 'data' | 'sortText' | 'textEdit'> & { item: Omit<CompletionItem, 'kind' | 'data' | 'command' | 'sortText' | 'textEdit'>
textEdit?: { newText: string; range?: Range }
}
): CompletionItem { ): CompletionItem {
return { return {
kind: 9, kind: 9,
data: 'variant', data: {
...(state.completionItemData ?? {}),
_type: 'variant',
},
command: command:
item.insertTextFormat === 2 // Snippet item.insertTextFormat === 2 // Snippet
? undefined ? undefined
@ -157,11 +160,6 @@ export function completionsFromClassList(
}, },
sortText: '-' + naturalExpand(variantOrder++), sortText: '-' + naturalExpand(variantOrder++),
...item, ...item,
textEdit: {
newText: item.label,
range: replacementRange,
...item.textEdit,
},
} }
} }
@ -174,9 +172,7 @@ export function completionsFromClassList(
variantItem({ variantItem({
label: `${variant.name}${variant.hasDash ? '-' : ''}[]${sep}`, label: `${variant.name}${variant.hasDash ? '-' : ''}[]${sep}`,
insertTextFormat: 2, insertTextFormat: 2,
textEdit: { textEditText: `${variant.name}${variant.hasDash ? '-' : ''}[\${1}]${sep}\${0}`,
newText: `${variant.name}${variant.hasDash ? '-' : ''}[\${1}]${sep}\${0}`,
},
// command: { // command: {
// title: '', // title: '',
// command: 'tailwindCSS.onInsertArbitraryVariantSnippet', // command: 'tailwindCSS.onInsertArbitraryVariantSnippet',
@ -199,9 +195,7 @@ export function completionsFromClassList(
variantItem({ variantItem({
label: `${variant.name}${sep}`, label: `${variant.name}${sep}`,
detail: variant.selectors().join(', '), detail: variant.selectors().join(', '),
textEdit: { textEditText: resultingVariants[resultingVariants.length - 1] + sep,
newText: resultingVariants[resultingVariants.length - 1] + sep,
},
additionalTextEdits: additionalTextEdits:
shouldSortVariants && resultingVariants.length > 1 shouldSortVariants && resultingVariants.length > 1
? [ ? [
@ -248,12 +242,13 @@ export function completionsFromClassList(
} }
if (state.classList) { if (state.classList) {
return { return withDefaults(
{
isIncomplete: false, isIncomplete: false,
items: items.concat( items: items.concat(
state.classList.map(([className, { color }], index) => { state.classList.map(([className, { color }], index) => {
let kind: CompletionItemKind = color ? 16 : 21 let kind: CompletionItemKind = color ? 16 : 21
let documentation = null let documentation: string | undefined
if (color && typeof color !== 'string') { if (color && typeof color !== 'string') {
documentation = culori.formatRgb(color) documentation = culori.formatRgb(color)
@ -262,20 +257,26 @@ export function completionsFromClassList(
return { return {
label: className, label: className,
kind, kind,
documentation, ...(documentation ? { documentation } : {}),
sortText: naturalExpand(index), sortText: naturalExpand(index, state.classList.length),
data: [...existingVariants, important ? `!${className}` : className],
textEdit: {
newText: className,
range: replacementRange,
},
} as CompletionItem } as CompletionItem
}) })
), ),
} },
{
data: {
...(state.completionItemData ?? {}),
...(important ? { important } : {}),
variants: existingVariants,
},
range: replacementRange,
},
state.editor.capabilities.itemDefaults
)
} }
return { return withDefaults(
{
isIncomplete: false, isIncomplete: false,
items: items items: items
.concat( .concat(
@ -287,9 +288,9 @@ export function completionsFromClassList(
} }
return item.__info && isUtil(item) return item.__info && isUtil(item)
}) })
.map((className, index) => { .map((className, index, classNames) => {
let kind: CompletionItemKind = 21 let kind: CompletionItemKind = 21
let documentation: string = null let documentation: string | undefined
const color = getColor(state, className) const color = getColor(state, className)
if (color !== null) { if (color !== null) {
@ -302,13 +303,8 @@ export function completionsFromClassList(
return { return {
label: className, label: className,
kind, kind,
documentation, ...(documentation ? { documentation } : {}),
sortText: naturalExpand(index), sortText: naturalExpand(index, classNames.length),
data: [...existingVariants, important ? `!${className}` : className],
textEdit: {
newText: className,
range: replacementRange,
},
} as CompletionItem } as CompletionItem
}) })
) )
@ -321,7 +317,17 @@ export function completionsFromClassList(
} }
return true return true
}), }),
} },
{
range: replacementRange,
data: {
...(state.completionItemData ?? {}),
variants: existingVariants,
...(important ? { important } : {}),
},
},
state.editor.capabilities.itemDefaults
)
} }
for (let i = parts.length - 1; i > 0; i--) { for (let i = parts.length - 1; i > 0; i--) {
@ -341,25 +347,25 @@ export function completionsFromClassList(
} }
} }
return { return withDefaults(
{
isIncomplete: false, isIncomplete: false,
items: Object.keys(isSubset ? subset : state.classNames.classNames) items: Object.keys(isSubset ? subset : state.classNames.classNames)
.filter((k) => k !== '__info') .filter((k) => k !== '__info')
.filter((className) => isContextItem(state, [...subsetKey, className])) .filter((className) => isContextItem(state, [...subsetKey, className]))
.map((className, index): CompletionItem => { .map((className, index, classNames): CompletionItem => {
return { return {
label: className + sep, label: className + sep,
kind: 9, kind: 9,
documentation: null,
command: { command: {
title: '', title: '',
command: 'editor.action.triggerSuggest', command: 'editor.action.triggerSuggest',
}, },
sortText: '-' + naturalExpand(index), sortText: '-' + naturalExpand(index, classNames.length),
data: [...subsetKey, className], data: {
textEdit: { ...(state.completionItemData ?? {}),
newText: className + sep, className,
range: replacementRange, variants: subsetKey,
}, },
} }
}) })
@ -368,9 +374,9 @@ export function completionsFromClassList(
.filter((className) => .filter((className) =>
dlv(state.classNames.classNames, [...subsetKey, className, '__info']) dlv(state.classNames.classNames, [...subsetKey, className, '__info'])
) )
.map((className, index) => { .map((className, index, classNames) => {
let kind: CompletionItemKind = 21 let kind: CompletionItemKind = 21
let documentation: string = null let documentation: string | undefined
const color = getColor(state, className) const color = getColor(state, className)
if (color !== null) { if (color !== null) {
@ -383,13 +389,8 @@ export function completionsFromClassList(
return { return {
label: className, label: className,
kind, kind,
documentation, ...(documentation ? { documentation } : {}),
sortText: naturalExpand(index), sortText: naturalExpand(index, classNames.length),
data: [...subsetKey, className],
textEdit: {
newText: className,
range: replacementRange,
},
} }
}) })
) )
@ -402,7 +403,16 @@ export function completionsFromClassList(
} }
return true return true
}), }),
} },
{
range: replacementRange,
data: {
...(state.completionItemData ?? {}),
variants: subsetKey,
},
},
state.editor.capabilities.itemDefaults
)
} }
async function provideClassAttributeCompletions( async function provideClassAttributeCompletions(
@ -569,7 +579,9 @@ function provideAtApplyCompletions(
semver.gte(state.version, '2.0.0-alpha.1') || flagEnabled(state, 'applyComplexClasses') semver.gte(state.version, '2.0.0-alpha.1') || flagEnabled(state, 'applyComplexClasses')
) )
} }
let validated = validateApply(state, item.data) let variants = item.data?.variants ?? []
let className = item.data?.className ?? item.label
let validated = validateApply(state, [...variants, className])
return validated !== null && validated.isApplyable === true return validated !== null && validated.isApplyable === true
} }
) )
@ -673,7 +685,8 @@ function provideCssHelperCompletions(
end: position, end: position,
} }
return { return withDefaults(
{
isIncomplete: false, isIncomplete: false,
items: Object.keys(obj) items: Object.keys(obj)
.sort((a, z) => { .sort((a, z) => {
@ -690,9 +703,10 @@ function provideCssHelperCompletions(
} }
return 0 return 0
}) })
.map((item, index) => { .map((item, index, items) => {
let color = getColorFromValue(obj[item]) let color = getColorFromValue(obj[item])
const replaceDot: boolean = item.indexOf('.') !== -1 && separator && separator.endsWith('.') const replaceDot: boolean =
item.indexOf('.') !== -1 && separator && separator.endsWith('.')
const insertClosingBrace: boolean = const insertClosingBrace: boolean =
text.charAt(text.length - 1) !== ']' && text.charAt(text.length - 1) !== ']' &&
(replaceDot || (separator && separator.endsWith('['))) (replaceDot || (separator && separator.endsWith('[')))
@ -700,21 +714,17 @@ function provideCssHelperCompletions(
return { return {
label: item, label: item,
sortText: naturalExpand(index), sortText: naturalExpand(index, items.length),
commitCharacters: [!item.includes('.') && '.', !item.includes('[') && '['].filter( commitCharacters: [!item.includes('.') && '.', !item.includes('[') && '['].filter(
Boolean Boolean
), ),
kind: color ? 16 : isObject(obj[item]) ? 9 : 10, kind: color ? 16 : isObject(obj[item]) ? 9 : 10,
// VS Code bug causes some values to not display in some cases // VS Code bug causes some values to not display in some cases
detail: detail === '0' || detail === 'transparent' ? `${detail} ` : detail, detail: detail === '0' || detail === 'transparent' ? `${detail} ` : detail,
documentation: ...(color && typeof color !== 'string' && (color.alpha ?? 1) !== 0
color && typeof color !== 'string' && (color.alpha ?? 1) !== 0 ? { documentation: culori.formatRgb(color) }
? culori.formatRgb(color) : {}),
: null, ...(insertClosingBrace ? { textEditText: `${item}]` } : {}),
textEdit: {
newText: `${item}${insertClosingBrace ? ']' : ''}`,
range: editRange,
},
additionalTextEdits: replaceDot additionalTextEdits: replaceDot
? [ ? [
{ {
@ -729,10 +739,18 @@ function provideCssHelperCompletions(
}, },
] ]
: [], : [],
data: 'helper',
} }
}), }),
} },
{
range: editRange,
data: {
...(state.completionItemData ?? {}),
_type: 'helper',
},
},
state.editor.capabilities.itemDefaults
)
} }
function provideTailwindDirectiveCompletions( function provideTailwindDirectiveCompletions(
@ -753,9 +771,7 @@ function provideTailwindDirectiveCompletions(
if (match === null) return null if (match === null) return null
return { let items = [
isIncomplete: false,
items: [
semver.gte(state.version, '1.0.0-beta.1') semver.gte(state.version, '1.0.0-beta.1')
? { ? {
label: 'base', label: 'base',
@ -818,12 +834,21 @@ function provideTailwindDirectiveCompletions(
)})`, )})`,
}, },
}, },
].map((item) => ({ ]
return withDefaults(
{
isIncomplete: false,
items: items.map((item) => ({
...item, ...item,
kind: 21, kind: 21,
data: '@tailwind', })),
textEdit: { },
newText: item.label, {
data: {
...(state.completionItemData ?? {}),
_type: '@tailwind',
},
range: { range: {
start: { start: {
line: position.line, line: position.line,
@ -832,8 +857,8 @@ function provideTailwindDirectiveCompletions(
end: position, end: position,
}, },
}, },
})), state.editor.capabilities.itemDefaults
} )
} }
function provideVariantsDirectiveCompletions( function provideVariantsDirectiveCompletions(
@ -877,19 +902,23 @@ function provideVariantsDirectiveCompletions(
possibleVariants = possibleVariants.filter((v) => !state.screens.includes(v)) possibleVariants = possibleVariants.filter((v) => !state.screens.includes(v))
} }
return { return withDefaults(
{
isIncomplete: false, isIncomplete: false,
items: possibleVariants items: possibleVariants
.filter((v) => existingVariants.indexOf(v) === -1) .filter((v) => existingVariants.indexOf(v) === -1)
.map((variant, index) => ({ .map((variant, index, variants) => ({
// TODO: detail // TODO: detail
label: variant, label: variant,
detail: state.variants[variant],
kind: 21, kind: 21,
data: 'variant', sortText: naturalExpand(index, variants.length),
sortText: naturalExpand(index), })),
textEdit: { },
newText: variant, {
data: {
...(state.completionItemData ?? {}),
_type: 'variant',
},
range: { range: {
start: { start: {
line: position.line, line: position.line,
@ -898,8 +927,8 @@ function provideVariantsDirectiveCompletions(
end: position, end: position,
}, },
}, },
})), state.editor.capabilities.itemDefaults
} )
} }
function provideLayerDirectiveCompletions( function provideLayerDirectiveCompletions(
@ -920,15 +949,20 @@ function provideLayerDirectiveCompletions(
if (match === null) return null if (match === null) return null
return { return withDefaults(
{
isIncomplete: false, isIncomplete: false,
items: ['base', 'components', 'utilities'].map((layer, index) => ({ items: ['base', 'components', 'utilities'].map((layer, index, layers) => ({
label: layer, label: layer,
kind: 21, kind: 21,
data: 'layer', sortText: naturalExpand(index, layers.length),
sortText: naturalExpand(index), })),
textEdit: { },
newText: layer, {
data: {
...(state.completionItemData ?? {}),
_type: 'layer',
},
range: { range: {
start: { start: {
line: position.line, line: position.line,
@ -937,6 +971,44 @@ function provideLayerDirectiveCompletions(
end: position, end: position,
}, },
}, },
state.editor.capabilities.itemDefaults
)
}
function withDefaults(
completionList: CompletionList,
defaults: Partial<{ data: any; range: Range }>,
supportedDefaults: string[]
): CompletionList {
let defaultData = supportedDefaults.includes('data')
let defaultRange = supportedDefaults.includes('editRange')
return {
...completionList,
...(defaultData || defaultRange
? {
itemDefaults: {
...(defaultData && defaults.data ? { data: defaults.data } : {}),
...(defaultRange && defaults.range ? { editRange: defaults.range } : {}),
},
}
: {}),
items:
defaultData && defaultRange
? completionList.items
: completionList.items.map(({ textEditText, ...item }) => ({
...item,
...(defaultData || !defaults.data || item.data ? {} : { data: defaults.data }),
...(defaultRange || !defaults.range
? textEditText
? { textEditText }
: {}
: {
textEdit: {
newText: textEditText ?? item.label,
range: defaults.range,
},
}),
})), })),
} }
} }
@ -963,15 +1035,20 @@ function provideScreenDirectiveCompletions(
if (!isObject(screens)) return null if (!isObject(screens)) return null
return { return withDefaults(
{
isIncomplete: false, isIncomplete: false,
items: Object.keys(screens).map((screen, index) => ({ items: Object.keys(screens).map((screen, index) => ({
label: screen, label: screen,
kind: 21, kind: 21,
data: 'screen',
sortText: naturalExpand(index), sortText: naturalExpand(index),
textEdit: { })),
newText: screen, },
{
data: {
...(state.completionItemData ?? {}),
_type: 'screen',
},
range: { range: {
start: { start: {
line: position.line, line: position.line,
@ -980,8 +1057,8 @@ function provideScreenDirectiveCompletions(
end: position, end: position,
}, },
}, },
})), state.editor.capabilities.itemDefaults
} )
} }
function provideCssDirectiveCompletions( function provideCssDirectiveCompletions(
@ -1089,14 +1166,19 @@ function provideCssDirectiveCompletions(
: []), : []),
] ]
return { return withDefaults(
{
isIncomplete: false, isIncomplete: false,
items: items.map((item) => ({ items: items.map((item) => ({
...item, ...item,
kind: 14, kind: 14,
data: 'directive', })),
textEdit: { },
newText: item.label, {
data: {
...(state.completionItemData ?? {}),
_type: 'directive',
},
range: { range: {
start: { start: {
line: position.line, line: position.line,
@ -1105,8 +1187,8 @@ function provideCssDirectiveCompletions(
end: position, end: position,
}, },
}, },
})), state.editor.capabilities.itemDefaults
} )
} }
async function provideConfigDirectiveCompletions( async function provideConfigDirectiveCompletions(
@ -1131,15 +1213,24 @@ async function provideConfigDirectiveCompletions(
let valueBeforeLastSlash = partial.substring(0, partial.lastIndexOf('/')) let valueBeforeLastSlash = partial.substring(0, partial.lastIndexOf('/'))
let valueAfterLastSlash = partial.substring(partial.lastIndexOf('/') + 1) let valueAfterLastSlash = partial.substring(partial.lastIndexOf('/') + 1)
return { return withDefaults(
{
isIncomplete: false, isIncomplete: false,
items: (await state.editor.readDirectory(document, valueBeforeLastSlash || '.')) items: (await state.editor.readDirectory(document, valueBeforeLastSlash || '.'))
.filter(([name, type]) => type.isDirectory || /\.c?js$/.test(name)) .filter(([name, type]) => type.isDirectory || /\.c?js$/.test(name))
.map(([name, type]) => ({ .map(([name, type]) => ({
label: type.isDirectory ? name + '/' : name, label: type.isDirectory ? name + '/' : name,
kind: type.isDirectory ? 19 : 17, kind: type.isDirectory ? 19 : 17,
textEdit: { command: type.isDirectory
newText: type.isDirectory ? name + '/' : name, ? { command: 'editor.action.triggerSuggest', title: '' }
: undefined,
})),
},
{
data: {
...(state.completionItemData ?? {}),
_type: 'filesystem',
},
range: { range: {
start: { start: {
line: position.line, line: position.line,
@ -1148,11 +1239,8 @@ async function provideConfigDirectiveCompletions(
end: position, end: position,
}, },
}, },
command: type.isDirectory state.editor.capabilities.itemDefaults
? { command: 'editor.action.triggerSuggest', title: '' } )
: undefined,
})),
}
} }
async function provideEmmetCompletions( async function provideEmmetCompletions(
@ -1255,25 +1343,31 @@ export async function resolveCompletionItem(
state: State, state: State,
item: CompletionItem item: CompletionItem
): Promise<CompletionItem> { ): Promise<CompletionItem> {
if (['helper', 'directive', 'variant', 'layer', '@tailwind'].includes(item.data)) { if (
['helper', 'directive', 'variant', 'layer', '@tailwind', 'filesystem'].includes(
item.data?._type
)
) {
return item return item
} }
if (item.data === 'screen') { if (item.data?._type === 'screen') {
let screens = dlv(state.config, ['theme', 'screens'], dlv(state.config, ['screens'], {})) let screens = dlv(state.config, ['theme', 'screens'], dlv(state.config, ['screens'], {}))
if (!isObject(screens)) screens = {} if (!isObject(screens)) screens = {}
item.detail = stringifyScreen(screens[item.label] as Screen) item.detail = stringifyScreen(screens[item.label] as Screen)
return item return item
} }
if (!Array.isArray(item.data)) { let className = item.data?.className ?? item.label
return item if (item.data?.important) {
className = `!${className}`
} }
let variants = item.data?.variants ?? []
if (state.jit) { if (state.jit) {
if (item.kind === 9) return item if (item.kind === 9) return item
if (item.detail && item.documentation) return item if (item.detail && item.documentation) return item
let { root, rules } = jit.generateRules(state, [item.data.join(state.separator)]) let { root, rules } = jit.generateRules(state, [[...variants, className].join(state.separator)])
if (rules.length === 0) return item if (rules.length === 0) return item
if (!item.detail) { if (!item.detail) {
if (rules.length === 1) { if (rules.length === 1) {
@ -1291,14 +1385,14 @@ export async function resolveCompletionItem(
return item return item
} }
const className = dlv(state.classNames.classNames, [...item.data, '__info']) const rules = dlv(state.classNames.classNames, [...variants, className, '__info'])
if (item.kind === 9) { if (item.kind === 9) {
item.detail = state.classNames.context[item.data[item.data.length - 1]].join(', ') item.detail = state.classNames.context[className].join(', ')
} else { } else {
item.detail = await getCssDetail(state, className) item.detail = await getCssDetail(state, rules)
if (!item.documentation) { if (!item.documentation) {
const settings = await state.editor.getConfiguration() const settings = await state.editor.getConfiguration()
const css = stringifyCss(item.data.join(':'), className, settings) const css = stringifyCss([...variants, className].join(':'), rules, settings)
if (css) { if (css) {
item.documentation = { item.documentation = {
kind: 'markdown' as typeof MarkupKind.Markdown, kind: 'markdown' as typeof MarkupKind.Markdown,

View File

@ -1,8 +1,4 @@
function pad(n: string): string { export function naturalExpand(value: number, total?: number): string {
return ('00000000' + n).substr(-8) let length = typeof total === 'number' ? total.toString().length : 8
} return ('0'.repeat(length) + value).slice(-length)
export function naturalExpand(value: number | string): string {
let str = typeof value === 'string' ? value : value.toString()
return str.replace(/\d+/g, pad)
} }

View File

@ -25,6 +25,7 @@ export type EditorState = {
capabilities: { capabilities: {
configuration: boolean configuration: boolean
diagnosticRelatedInformation: boolean diagnosticRelatedInformation: boolean
itemDefaults: string[]
} }
getConfiguration: (uri?: string) => Promise<Settings> getConfiguration: (uri?: string) => Promise<Settings>
getDocumentSymbols: (uri: string) => Promise<SymbolInformation[]> getDocumentSymbols: (uri: string) => Promise<SymbolInformation[]>
@ -118,6 +119,7 @@ export interface State {
jitContext?: any jitContext?: any
classList?: Array<[string, { color: culori.Color | KeywordColor | null; modifiers?: string[] }]> classList?: Array<[string, { color: culori.Color | KeywordColor | null; modifiers?: string[] }]>
pluginVersions?: string pluginVersions?: string
completionItemData?: Record<string, any>
// postcssPlugins?: { before: any[]; after: any[] } // postcssPlugins?: { before: any[]; after: any[] }
} }

View File

@ -24,7 +24,7 @@
"vscode" "vscode"
], ],
"engines": { "engines": {
"vscode": "^1.65.0" "vscode": "^1.67.0"
}, },
"categories": [ "categories": [
"Linters", "Linters",