Skip to content

Commit 5cf4b17

Browse files
committed
[JIT] add opacity modifier completions
1 parent 1d0af70 commit 5cf4b17

File tree

2 files changed

+77
-8
lines changed

2 files changed

+77
-8
lines changed

packages/tailwindcss-language-service/src/completionProvider.ts

Lines changed: 74 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import type {
77
CompletionList,
88
TextDocument,
99
Position,
10+
CompletionContext,
1011
} from 'vscode-languageserver'
1112
const dlv = require('dlv')
1213
import removeMeta from './util/removeMeta'
@@ -43,7 +44,8 @@ export function completionsFromClassList(
4344
classList: string,
4445
classListRange: Range,
4546
filter?: (item: CompletionItem) => boolean,
46-
document?: TextDocument
47+
document?: TextDocument,
48+
context?: CompletionContext
4749
): CompletionList {
4850
let classNames = classList.split(/[\s+]/)
4951
const partialClassName = classNames[classNames.length - 1]
@@ -62,6 +64,58 @@ export function completionsFromClassList(
6264
}
6365

6466
if (state.jit) {
67+
if (
68+
context &&
69+
(context.triggerKind === 1 ||
70+
(context.triggerKind === 2 && context.triggerCharacter === '/')) &&
71+
partialClassName.includes('/')
72+
) {
73+
let beforeSlash = partialClassName.split('/').slice(0, -1).join('/')
74+
let testClass = beforeSlash + '/[0]'
75+
let { rules } = jit.generateRules(state, [testClass])
76+
if (rules.length > 0) {
77+
let opacities = dlv(state.config, 'theme.opacity', {})
78+
if (!isObject(opacities)) {
79+
opacities = {}
80+
}
81+
return {
82+
isIncomplete: false,
83+
items: Object.keys(opacities).map((opacity, index) => {
84+
let className = `${beforeSlash}/${opacity}`
85+
let kind: CompletionItemKind = 21
86+
let documentation: string = null
87+
88+
const color = getColor(state, className)
89+
if (color !== null) {
90+
kind = 16
91+
if (typeof color !== 'string') {
92+
documentation = color.toRgbString().replace(/(^rgba\([^)]+) 0\)$/, '$1 0.001)')
93+
}
94+
}
95+
96+
return {
97+
label: opacity,
98+
detail: stringifyConfigValue(opacities[opacity]),
99+
documentation,
100+
kind,
101+
sortText: naturalExpand(index),
102+
data: [className],
103+
textEdit: {
104+
newText: opacity,
105+
range: {
106+
...replacementRange,
107+
start: {
108+
...replacementRange.start,
109+
character: replacementRange.start.character + beforeSlash.length + 1,
110+
},
111+
},
112+
},
113+
}
114+
}),
115+
}
116+
}
117+
}
118+
65119
let allVariants = Object.keys(state.variants)
66120
let { variants: existingVariants, offset } = getVariantsFromClassName(state, partialClassName)
67121

@@ -256,7 +310,8 @@ export function completionsFromClassList(
256310
function provideClassAttributeCompletions(
257311
state: State,
258312
document: TextDocument,
259-
position: Position
313+
position: Position,
314+
context?: CompletionContext
260315
): CompletionList {
261316
let str = document.getText({
262317
start: { line: Math.max(position.line - 10, 0), character: 0 },
@@ -299,7 +354,8 @@ function provideClassAttributeCompletions(
299354
end: position,
300355
},
301356
undefined,
302-
document
357+
document,
358+
context
303359
)
304360
}
305361
} catch (_) {}
@@ -417,10 +473,11 @@ function provideAtApplyCompletions(
417473
function provideClassNameCompletions(
418474
state: State,
419475
document: TextDocument,
420-
position: Position
476+
position: Position,
477+
context?: CompletionContext
421478
): CompletionList {
422479
if (isHtmlContext(state, document, position) || isJsContext(state, document, position)) {
423-
return provideClassAttributeCompletions(state, document, position)
480+
return provideClassAttributeCompletions(state, document, position, context)
424481
}
425482

426483
if (isCssContext(state, document, position)) {
@@ -925,11 +982,16 @@ async function provideEmmetCompletions(
925982
})
926983
}
927984

928-
export async function doComplete(state: State, document: TextDocument, position: Position) {
985+
export async function doComplete(
986+
state: State,
987+
document: TextDocument,
988+
position: Position,
989+
context?: CompletionContext
990+
) {
929991
if (state === null) return { items: [], isIncomplete: false }
930992

931993
const result =
932-
provideClassNameCompletions(state, document, position) ||
994+
provideClassNameCompletions(state, document, position, context) ||
933995
provideCssHelperCompletions(state, document, position) ||
934996
provideCssDirectiveCompletions(state, document, position) ||
935997
provideScreenDirectiveCompletions(state, document, position) ||
@@ -958,8 +1020,13 @@ export async function resolveCompletionItem(
9581020
return item
9591021
}
9601022

1023+
if (!Array.isArray(item.data)) {
1024+
return item
1025+
}
1026+
9611027
if (state.jit) {
9621028
if (item.kind === 9) return item
1029+
if (item.detail && item.documentation) return item
9631030
let { root, rules } = jit.generateRules(state, [item.data.join(state.separator)])
9641031
if (rules.length === 0) return item
9651032
if (!item.detail) {

src/server.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ const TRIGGER_CHARACTERS = [
8585
'[',
8686
// JIT "important" prefix
8787
'!',
88+
// JIT opacity modifiers
89+
'/',
8890
] as const
8991

9092
const colorNames = Object.keys(namedColors)
@@ -748,7 +750,7 @@ async function createProjectService(
748750
if (!state.enabled) return null
749751
let document = documentService.getDocument(params.textDocument.uri)
750752
if (!document) return null
751-
return doComplete(state, document, params.position)
753+
return doComplete(state, document, params.position, params.context)
752754
},
753755
onCompletionResolve(item: CompletionItem): Promise<CompletionItem> {
754756
if (!state.enabled) return null

0 commit comments

Comments
 (0)