11import type { State } from './util/state'
2- import type { Hover , Position } from 'vscode-languageserver'
2+ import type { Hover , MarkupContent , Position , Range } from 'vscode-languageserver'
33import { stringifyCss , stringifyConfigValue } from './util/stringify'
44import dlv from 'dlv'
55import { isCssContext } from './util/css'
6- import { findClassNameAtPosition , findHelperFunctionsInRange } from './util/find'
6+ import {
7+ findAll ,
8+ findClassNameAtPosition ,
9+ findHelperFunctionsInRange ,
10+ indexToPosition ,
11+ } from './util/find'
712import { validateApply } from './util/validateApply'
813import { getClassNameParts } from './util/getClassNameAtPosition'
914import * as jit from './util/jit'
1015import { validateConfigPath } from './diagnostics/getInvalidConfigPathDiagnostics'
1116import { isWithinRange } from './util/isWithinRange'
1217import type { TextDocument } from 'vscode-languageserver-textdocument'
1318import { addPixelEquivalentsToValue } from './util/pixelEquivalents'
19+ import { getTextWithoutComments } from './util/doc'
20+ import braces from 'braces'
21+ import { absoluteRange } from './util/absoluteRange'
1422
1523export async function doHover (
1624 state : State ,
@@ -19,7 +27,8 @@ export async function doHover(
1927) : Promise < Hover > {
2028 return (
2129 ( await provideClassNameHover ( state , document , position ) ) ||
22- ( await provideCssHelperHover ( state , document , position ) )
30+ ( await provideCssHelperHover ( state , document , position ) ) ||
31+ ( await provideSourceGlobHover ( state , document , position ) )
2332 )
2433}
2534
@@ -133,3 +142,64 @@ async function provideClassNameHover(
133142 range : className . range ,
134143 }
135144}
145+
146+ function markdown ( lines : string [ ] ) : MarkupContent {
147+ return {
148+ kind : 'markdown' ,
149+ value : lines . join ( '\n' ) ,
150+ }
151+ }
152+
153+ async function provideSourceGlobHover (
154+ state : State ,
155+ document : TextDocument ,
156+ position : Position ,
157+ ) : Promise < Hover > {
158+ if ( ! isCssContext ( state , document , position ) ) {
159+ return null
160+ }
161+
162+ let range = {
163+ start : { line : position . line , character : 0 } ,
164+ end : { line : position . line + 1 , character : 0 } ,
165+ }
166+
167+ let text = getTextWithoutComments ( document , 'css' , range )
168+
169+ let pattern = / @ s o u r c e \s * (?< path > ' [ ^ ' ] + ' | " [ ^ " ] + " ) / dg
170+
171+ for ( let match of findAll ( pattern , text ) ) {
172+ let path = match . groups . path . slice ( 1 , - 1 )
173+
174+ // Ignore paths that don't need brace expansion
175+ if ( ! path . includes ( '{' ) || ! path . includes ( '}' ) ) continue
176+
177+ // Ignore paths that don't contain the current position
178+ let slice : Range = absoluteRange (
179+ {
180+ start : indexToPosition ( text , match . indices . groups . path [ 0 ] ) ,
181+ end : indexToPosition ( text , match . indices . groups . path [ 1 ] ) ,
182+ } ,
183+ range ,
184+ )
185+
186+ if ( ! isWithinRange ( position , slice ) ) continue
187+
188+ // Perform brace expansion
189+ let paths = new Set ( braces . expand ( path ) )
190+ if ( paths . size < 2 ) continue
191+
192+ return {
193+ range : slice ,
194+ contents : markdown ( [
195+ //
196+ '**Expansion**' ,
197+ '```plaintext' ,
198+ ...Array . from ( paths , ( path ) => `- ${ path } ` ) ,
199+ '```' ,
200+ ] ) ,
201+ }
202+ }
203+
204+ return null
205+ }
0 commit comments