Skip to content

Commit 0ff508e

Browse files
committed
Suggest @slot when inside a long form @variant rule
1 parent 3462d58 commit 0ff508e

File tree

2 files changed

+62
-0
lines changed

2 files changed

+62
-0
lines changed

packages/tailwindcss-language-server/tests/completions/completions.test.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,36 @@ withFixture('v4/basic', (c) => {
477477
expect(result.items.filter((item) => item.label.startsWith('--')).length).toBe(23)
478478
})
479479

480+
test.concurrent('@slot is suggeted inside @variant', async ({ expect }) => {
481+
let result = await completion({
482+
lang: 'css',
483+
text: '@',
484+
position: { line: 0, character: 1 },
485+
})
486+
487+
// Make sure `@slot` is NOT suggested by default
488+
expect(result.items.length).toBe(10)
489+
expect(result.items).not.toEqual(
490+
expect.arrayContaining([
491+
expect.objectContaining({ kind: 14, label: '@slot', sortText: '-0000000' }),
492+
]),
493+
)
494+
495+
result = await completion({
496+
lang: 'css',
497+
text: '@variant foo {\n@',
498+
position: { line: 1, character: 1 },
499+
})
500+
501+
// Make sure `@slot` is suggested
502+
expect(result.items.length).toBe(11)
503+
expect(result.items).toEqual(
504+
expect.arrayContaining([
505+
expect.objectContaining({ kind: 14, label: '@slot', sortText: '-0000000' }),
506+
]),
507+
)
508+
})
509+
480510
test.concurrent('resolve', async ({ expect }) => {
481511
let result = await completion({
482512
text: '<div class="">',

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1540,6 +1540,23 @@ function provideCssDirectiveCompletions(
15401540
)})`,
15411541
},
15421542
})
1543+
1544+
// If we're inside an @variant directive, also add `@slot`
1545+
if (isInsideAtRule('variant', document, position)) {
1546+
items.push({
1547+
label: '@slot',
1548+
documentation: {
1549+
kind: 'markdown' as typeof MarkupKind.Markdown,
1550+
value: `Use the \`@slot\` directive to define where rules go in a custom variant.\n\n[Tailwind CSS Documentation](${docsUrl(
1551+
state.version,
1552+
'functions-and-directives/#slot',
1553+
)})`,
1554+
},
1555+
1556+
// Make sure this appears as the first at-rule
1557+
sortText: '-0000000',
1558+
})
1559+
}
15431560
}
15441561

15451562
return withDefaults(
@@ -1567,6 +1584,21 @@ function provideCssDirectiveCompletions(
15671584
)
15681585
}
15691586

1587+
function isInsideAtRule(name: string, document: TextDocument, position: Position) {
1588+
// 1. Get all text up to the current position
1589+
let text = document.getText({
1590+
start: { line: 0, character: 0 },
1591+
end: position,
1592+
})
1593+
1594+
// 2. Find the last instance of the at-rule
1595+
let block = text.lastIndexOf(`@${name}`)
1596+
if (block === -1) return false
1597+
1598+
// 4. Count the number of open and close braces following the rule to determine if we're inside it
1599+
return braceLevel(text.slice(block)) > 0
1600+
}
1601+
15701602
// Provide completions for directives that take file paths
15711603
async function provideFileDirectiveCompletions(
15721604
state: State,

0 commit comments

Comments
 (0)