@@ -1275,6 +1275,66 @@ function provideVariantsDirectiveCompletions(
12751275 )
12761276}
12771277
1278+ function provideVariantDirectiveCompletions (
1279+ state : State ,
1280+ document : TextDocument ,
1281+ position : Position ,
1282+ ) : CompletionList {
1283+ if ( ! state . v4 ) return null
1284+ if ( ! isCssContext ( state , document , position ) ) return null
1285+
1286+ let text = document . getText ( {
1287+ start : { line : position . line , character : 0 } ,
1288+ end : position ,
1289+ } )
1290+
1291+ let match = text . match ( / ^ \s * @ v a r i a n t \s + (?< partial > [ ^ } ] * ) $ / i)
1292+ if ( match === null ) return null
1293+
1294+ let partial = match . groups . partial . trim ( )
1295+
1296+ // We only allow one variant `@variant` call
1297+ if ( / \s / . test ( partial ) ) return null
1298+
1299+ // We don't allow applying stacked variants so don't suggest them
1300+ if ( / : / . test ( partial ) ) return null
1301+
1302+ let possibleVariants = state . variants . flatMap ( ( variant ) => {
1303+ if ( variant . values . length ) {
1304+ return variant . values . map ( ( value ) =>
1305+ value === 'DEFAULT' ? variant . name : `${ variant . name } ${ variant . hasDash ? '-' : '' } ${ value } ` ,
1306+ )
1307+ }
1308+
1309+ return [ variant . name ]
1310+ } )
1311+
1312+ return withDefaults (
1313+ {
1314+ isIncomplete : false ,
1315+ items : possibleVariants . map ( ( variant , index , variants ) => ( {
1316+ label : variant ,
1317+ kind : 21 ,
1318+ sortText : naturalExpand ( index , variants . length ) ,
1319+ } ) ) ,
1320+ } ,
1321+ {
1322+ data : {
1323+ ...( state . completionItemData ?? { } ) ,
1324+ _type : 'variant' ,
1325+ } ,
1326+ range : {
1327+ start : {
1328+ line : position . line ,
1329+ character : position . character ,
1330+ } ,
1331+ end : position ,
1332+ } ,
1333+ } ,
1334+ state . editor . capabilities . itemDefaults ,
1335+ )
1336+ }
1337+
12781338function provideLayerDirectiveCompletions (
12791339 state : State ,
12801340 document : TextDocument ,
@@ -1423,6 +1483,8 @@ function provideCssDirectiveCompletions(
14231483
14241484 if ( match === null ) return null
14251485
1486+ let isNested = isInsideNesting ( document , position )
1487+
14261488 let items : CompletionItem [ ] = [ ]
14271489
14281490 items . push ( {
@@ -1535,12 +1597,12 @@ function provideCssDirectiveCompletions(
15351597 } )
15361598
15371599 items . push ( {
1538- label : '@variant' ,
1600+ label : '@custom- variant' ,
15391601 documentation : {
15401602 kind : 'markdown' as typeof MarkupKind . Markdown ,
1541- value : `Use the \`@variant\` directive to define a custom variant or override an existing one.\n\n[Tailwind CSS Documentation](${ docsUrl (
1603+ value : `Use the \`@custom- variant\` directive to define a custom variant or override an existing one.\n\n[Tailwind CSS Documentation](${ docsUrl (
15421604 state . version ,
1543- 'functions-and-directives/#variant' ,
1605+ 'functions-and-directives/#custom- variant' ,
15441606 ) } )`,
15451607 } ,
15461608 } )
@@ -1566,9 +1628,22 @@ function provideCssDirectiveCompletions(
15661628 ) } )`,
15671629 } ,
15681630 } )
1631+ }
15691632
1570- // If we're inside an @variant directive, also add `@slot`
1571- if ( isInsideAtRule ( 'variant' , document , position ) ) {
1633+ if ( state . v4 && isNested ) {
1634+ items . push ( {
1635+ label : '@variant' ,
1636+ documentation : {
1637+ kind : 'markdown' as typeof MarkupKind . Markdown ,
1638+ value : `Use the \`@variant\` directive to use a variant in CSS.\n\n[Tailwind CSS Documentation](${ docsUrl (
1639+ state . version ,
1640+ 'functions-and-directives/variant' ,
1641+ ) } )`,
1642+ } ,
1643+ } )
1644+
1645+ // If we're inside an @custom-variant directive, also add `@slot`
1646+ if ( isInsideAtRule ( 'custom-variant' , document , position ) ) {
15721647 items . push ( {
15731648 label : '@slot' ,
15741649 documentation : {
@@ -1611,20 +1686,29 @@ function provideCssDirectiveCompletions(
16111686}
16121687
16131688function isInsideAtRule ( name : string , document : TextDocument , position : Position ) {
1614- // 1. Get all text up to the current position
16151689 let text = document . getText ( {
16161690 start : { line : 0 , character : 0 } ,
16171691 end : position ,
16181692 } )
16191693
1620- // 2. Find the last instance of the at-rule
1694+ // Find the last instance of the at-rule
16211695 let block = text . lastIndexOf ( `@${ name } ` )
16221696 if ( block === - 1 ) return false
16231697
1624- // 4. Count the number of open and close braces following the rule to determine if we're inside it
1698+ // Check if we're inside it by counting the number of still-open braces
16251699 return braceLevel ( text . slice ( block ) ) > 0
16261700}
16271701
1702+ function isInsideNesting ( document : TextDocument , position : Position ) {
1703+ let text = document . getText ( {
1704+ start : { line : 0 , character : 0 } ,
1705+ end : position ,
1706+ } )
1707+
1708+ // Check if we're inside a rule by counting the number of still-open braces
1709+ return braceLevel ( text ) > 0
1710+ }
1711+
16281712// Provide completions for directives that take file paths
16291713const PATTERN_AT_THEME = / @ (?< directive > t h e m e ) \s + (?: (?< parts > [ ^ { ] + ) \s $ | $ ) /
16301714const PATTERN_IMPORT_THEME = / @ (?< directive > i m p o r t ) \s * [ ^ ; ] + ?t h e m e \( (?: (?< parts > [ ^ ) ] + ) \s $ | $ ) /
@@ -1874,6 +1958,7 @@ export async function doComplete(
18741958 provideCssHelperCompletions ( state , document , position ) ||
18751959 provideCssDirectiveCompletions ( state , document , position ) ||
18761960 provideScreenDirectiveCompletions ( state , document , position ) ||
1961+ provideVariantDirectiveCompletions ( state , document , position ) ||
18771962 provideVariantsDirectiveCompletions ( state , document , position ) ||
18781963 provideTailwindDirectiveCompletions ( state , document , position ) ||
18791964 provideLayerDirectiveCompletions ( state , document , position ) ||
0 commit comments