@@ -11,6 +11,7 @@ import {
11
11
} from '@pandacss/types'
12
12
import { CallExpression , Identifier , JsxOpeningElement , JsxSelfClosingElement , Node , SourceFile , ts } from 'ts-morph'
13
13
14
+ import { type PandaVSCodeSettings } from '@pandacss/extension-shared'
14
15
import {
15
16
BoxNodeArray ,
16
17
BoxNodeLiteral ,
@@ -28,15 +29,17 @@ import {
28
29
type Unboxed ,
29
30
} from '@pandacss/extractor'
30
31
import { type PandaContext } from '@pandacss/node'
31
- import { walkObject } from '@pandacss/shared'
32
32
import { type ParserResult } from '@pandacss/parser'
33
+ import { walkObject } from '@pandacss/shared'
33
34
import { type Token } from '@pandacss/token-dictionary'
34
35
import { Bool } from 'lil-fp'
35
- import { type PandaVSCodeSettings } from '@pandacss/extension-shared'
36
36
import { match } from 'ts-pattern'
37
37
import { color2kToVsCodeColor } from './color2k-to-vscode-color'
38
38
import { expandTokenFn , extractTokenPaths } from './expand-token-fn'
39
39
import { isColor } from './is-color'
40
+ import { makeColorTile , makeTable } from './metadata'
41
+ import { getSortText } from './sort-text'
42
+ import { traverse } from './traverse'
40
43
import { getMarkdownCss , isObjectLike , nodeRangeToVsCodeRange , printTokenValue } from './utils'
41
44
42
45
type ClosestMatch = {
@@ -48,12 +51,14 @@ type ClosestTokenMatch = ClosestMatch & {
48
51
kind : 'token'
49
52
token : Token
50
53
propValue : PrimitiveType
54
+ shorthand : string
51
55
}
52
56
53
57
type ClosestConditionMatch = ClosestMatch & {
54
58
kind : 'condition'
55
59
condition : RawCondition
56
60
propValue : Unboxed [ 'raw' ]
61
+ shorthand : never
57
62
}
58
63
type ClosestToken = ClosestTokenMatch | ClosestConditionMatch
59
64
@@ -165,14 +170,14 @@ export function setupTokensHelpers(setup: PandaExtensionSetup) {
165
170
const propNode = box . isArray ( boxNode )
166
171
? boxNode . value . find ( ( node ) => box . isMap ( node ) && getNestedBoxProp ( node , paths ) )
167
172
: getNestedBoxProp ( boxNode , paths )
168
- if ( ! box . isLiteral ( propNode ) ) return
173
+ if ( ! box . isLiteral ( propNode ) || ! prop ) return
169
174
170
175
const propName = ctx . utility . resolveShorthand ( prop )
171
176
const token = getTokenFromPropValue ( ctx , propName , value )
172
177
if ( ! token ) return
173
178
174
179
const range = nodeRangeToVsCodeRange ( propNode . getRange ( ) )
175
- onToken ?.( { kind : 'token' , token, range, propName, propValue : value , propNode } )
180
+ onToken ?.( { kind : 'token' , token, range, propName, propValue : value , propNode, shorthand : prop } )
176
181
} )
177
182
} )
178
183
}
@@ -296,7 +301,7 @@ export function setupTokensHelpers(setup: PandaExtensionSetup) {
296
301
const findClosestToken = < Return > (
297
302
node : Node ,
298
303
stack : Node [ ] ,
299
- onFoundToken : ( args : Pick < ClosestToken , 'propName' | 'propNode' > ) => Return ,
304
+ onFoundToken : ( args : Pick < ClosestToken , 'propName' | 'propNode' | 'shorthand' > ) => Return ,
300
305
) => {
301
306
const ctx = setup . getContext ( )
302
307
if ( ! ctx ) return
@@ -317,7 +322,7 @@ export function setupTokensHelpers(setup: PandaExtensionSetup) {
317
322
318
323
const propName = ctx . utility . resolveShorthand ( name )
319
324
320
- return onFoundToken ( { propName, propNode } )
325
+ return onFoundToken ( { propName, propNode, shorthand : name } )
321
326
} ,
322
327
)
323
328
. when (
@@ -333,7 +338,7 @@ export function setupTokensHelpers(setup: PandaExtensionSetup) {
333
338
334
339
const propName = ctx . utility . resolveShorthand ( name )
335
340
336
- return onFoundToken ( { propName, propNode : attrBox } )
341
+ return onFoundToken ( { propName, propNode : attrBox , shorthand : name } )
337
342
} ,
338
343
)
339
344
. otherwise ( ( ) => {
@@ -411,10 +416,71 @@ export function setupTokensHelpers(setup: PandaExtensionSetup) {
411
416
const settings = await setup . getPandaSettings ( )
412
417
const { node, stack } = match
413
418
414
- return findClosestToken ( node , stack , ( { propName, propNode } ) => {
415
- if ( ! box . isLiteral ( propNode ) ) return undefined
416
- return getCompletionFor ( ctx , propName , propNode , settings )
417
- } )
419
+ try {
420
+ return await findClosestToken ( node , stack , ( { propName, propNode, shorthand } ) => {
421
+ if ( ! box . isLiteral ( propNode ) ) return undefined
422
+ return getCompletionFor ( { ctx, propName, propNode, settings, shorthand } )
423
+ } )
424
+ } catch ( err ) {
425
+ console . error ( err )
426
+ console . trace ( )
427
+ return
428
+ }
429
+ }
430
+
431
+ const getCompletionDetails = async ( item : CompletionItem ) => {
432
+ const ctx = setup . getContext ( )
433
+ if ( ! ctx ) return
434
+
435
+ const settings = await setup . getPandaSettings ( )
436
+ const { propName, token, shorthand } = ( item . data ?? { } ) as { propName : string ; token : Token ; shorthand : string }
437
+ const markdownCss = getMarkdownCss ( ctx , { [ propName ] : token . value } , settings )
438
+
439
+ const markdown = [ markdownCss . withCss ]
440
+ if ( shorthand !== propName ) {
441
+ markdown . push ( `\`${ shorthand } \` is shorthand for \`${ propName } \`` )
442
+ }
443
+
444
+ const conditions = token . extensions . conditions ?? { base : token . value }
445
+ if ( conditions ) {
446
+ const separator = '[___]'
447
+ const table = [ { color : ' ' , theme : 'Condition' , value : 'Value' } ]
448
+
449
+ const tab = ' '
450
+ traverse (
451
+ conditions ,
452
+ ( { key : cond , value, depth } ) => {
453
+ if ( ! ctx . conditions . get ( cond ) && cond !== 'base' ) return
454
+
455
+ const indent = depth > 0 ? tab . repeat ( depth ) + '├ ' : ''
456
+
457
+ if ( typeof value === 'object' ) {
458
+ table . push ( {
459
+ color : '' ,
460
+ theme : `${ indent } **${ cond } **` ,
461
+ value : '─────' ,
462
+ } )
463
+ return
464
+ }
465
+
466
+ const [ tokenRef ] = ctx . tokens . getReferences ( value )
467
+ const color = tokenRef ?. value ?? value
468
+ if ( ! color ) return
469
+
470
+ table . push ( {
471
+ color : makeColorTile ( color ) ,
472
+ theme : `${ indent } **${ cond } **` ,
473
+ value : `\`${ color } \`` ,
474
+ } )
475
+ } ,
476
+ { separator } ,
477
+ )
478
+
479
+ markdown . push ( makeTable ( table ) )
480
+ markdown . push ( `\n${ tab } ` )
481
+ }
482
+
483
+ item . documentation = { kind : 'markdown' , value : markdown . join ( '\n' ) }
418
484
}
419
485
420
486
return {
@@ -425,6 +491,7 @@ export function setupTokensHelpers(setup: PandaExtensionSetup) {
425
491
getClosestToken,
426
492
getClosestInstance,
427
493
getClosestCompletionList,
494
+ getCompletionDetails,
428
495
}
429
496
}
430
497
@@ -516,15 +583,19 @@ const getTokenFromPropValue = (ctx: PandaContext, prop: string, value: string):
516
583
return token
517
584
}
518
585
519
- const completionCache = new Map < string , CompletionItem [ ] > ( )
520
- const itemCache = new Map < string , CompletionItem > ( )
521
-
522
- const getCompletionFor = (
523
- ctx : PandaContext ,
524
- propName : string ,
525
- propNode : BoxNodeLiteral ,
526
- settings : PandaVSCodeSettings ,
527
- ) => {
586
+ const getCompletionFor = ( {
587
+ ctx,
588
+ propName,
589
+ shorthand,
590
+ propNode,
591
+ settings,
592
+ } : {
593
+ ctx : PandaContext
594
+ propName : string
595
+ shorthand ?: string
596
+ propNode : BoxNodeLiteral
597
+ settings : PandaVSCodeSettings
598
+ } ) => {
528
599
const propValue = propNode . value
529
600
530
601
let str = String ( propValue )
@@ -552,10 +623,6 @@ const getCompletionFor = (
552
623
category = split [ 0 ]
553
624
}
554
625
555
- const cachePath = propName + '.' + str
556
- const cachedList = completionCache . get ( cachePath )
557
- if ( cachedList ) return cachedList
558
-
559
626
// token(colors.red.300) -> category = "colors"
560
627
// color="red.300" -> no category, need to find it
561
628
if ( ! category ) {
@@ -574,21 +641,15 @@ const getCompletionFor = (
574
641
values . forEach ( ( token , name ) => {
575
642
if ( str && ! name . includes ( str ) ) return
576
643
577
- const tokenPath = token . name
578
- const cachedItem = itemCache . get ( tokenPath )
579
- if ( cachedItem ) {
580
- items . push ( cachedItem )
581
- return
582
- }
583
-
584
644
const isColor = token . extensions . category === 'colors'
645
+
585
646
const completionItem = {
647
+ data : { propName, token, shorthand } ,
586
648
label : name ,
587
649
kind : isColor ? CompletionItemKind . Color : CompletionItemKind . EnumMember ,
588
- documentation : { kind : 'markdown' , value : getMarkdownCss ( ctx , { [ propName ] : token . value } , settings ) . withCss } ,
589
650
labelDetails : { description : printTokenValue ( token , settings ) , detail : ` ${ token . extensions . varRef } ` } ,
590
- sortText : '-' + name ,
591
- preselect : true ,
651
+ sortText : '-' + getSortText ( name ) ,
652
+ preselect : false ,
592
653
} as CompletionItem
593
654
594
655
if ( isColor ) {
@@ -597,10 +658,8 @@ const getCompletionFor = (
597
658
}
598
659
599
660
items . push ( completionItem )
600
- itemCache . set ( tokenPath , completionItem )
601
661
} )
602
662
603
- completionCache . set ( cachePath , items )
604
663
return items
605
664
}
606
665
@@ -611,6 +670,6 @@ const getFirstAncestorMatching = <Ancestor extends Node>(
611
670
) => {
612
671
for ( let i = stack . length - 1 ; i >= 0 ; i -- ) {
613
672
const parent = stack [ i ]
614
- if ( callback ( parent , i ) ) return parent
673
+ if ( parent && callback ( parent , i ) ) return parent
615
674
}
616
675
}
0 commit comments