1- import { Range , Color , Location , Position } from ' vscode-languageserver/node' ;
2- import * as fs from 'fs' ;
3- import fastGlob from ' fast-glob' ;
4- import * as culori from ' culori' ;
5- import axios from ' axios' ;
6- import postcss from ' postcss' ;
7- import { pathToFileURL } from ' url' ;
8- import path from ' path' ;
9- import postcssSCSS from ' postcss-scss' ;
10- import postcssLESS from ' postcss-less' ;
11- import CacheManager from ' ./CacheManager' ;
12- import isColor from ' ./utils/isColor' ;
13- import { culoriColorToVscodeColor } from ' ./utils/culoriColorToVscodeColor' ;
1+ import { Range , Color , Location , Position } from " vscode-languageserver/node" ;
2+ import * as fs from "fs" ;
3+ import fastGlob from " fast-glob" ;
4+ import * as culori from " culori" ;
5+ import axios from " axios" ;
6+ import postcss from " postcss" ;
7+ import { pathToFileURL } from " url" ;
8+ import path from " path" ;
9+ import postcssSCSS from " postcss-scss" ;
10+ import postcssLESS from " postcss-less" ;
11+ import CacheManager from " ./CacheManager" ;
12+ import isColor from " ./utils/isColor" ;
13+ import { culoriColorToVscodeColor } from " ./utils/culoriColorToVscodeColor" ;
1414
1515export type CSSSymbol = {
16- name : string
17- value : string
18- }
16+ name : string ;
17+ value : string ;
18+ } ;
1919
2020export type CSSVariable = {
21- symbol : CSSSymbol
22- definition : Location
23- color ?: Color
24- }
21+ symbol : CSSSymbol ;
22+ definition : Location ;
23+ color ?: Color ;
24+ } ;
2525
2626export interface CSSVariablesSettings {
27- lookupFiles : string [ ]
28- blacklistFolders : string [ ]
27+ lookupFiles : string [ ] ;
28+ blacklistFolders : string [ ] ;
2929}
3030
3131// The global settings, used when the `workspace/configuration` request is not supported by the client.
3232// Please note that this is not the case when using this server with the client provided in this example
3333// but could happen with other clients.
3434export const defaultSettings : CSSVariablesSettings = {
35- lookupFiles : [ ' **/*.less' , ' **/*.scss' , ' **/*.sass' , ' **/*.css' ] ,
35+ lookupFiles : [ " **/*.less" , " **/*.scss" , " **/*.sass" , " **/*.css" ] ,
3636 blacklistFolders : [
37- ' **/.cache' ,
38- ' **/.DS_Store' ,
39- ' **/.git' ,
40- ' **/.hg' ,
41- ' **/.next' ,
42- ' **/.svn' ,
43- ' **/bower_components' ,
44- ' **/CVS' ,
45- ' **/dist' ,
46- ' **/node_modules' ,
47- ' **/tests' ,
48- ' **/tmp' ,
37+ " **/.cache" ,
38+ " **/.DS_Store" ,
39+ " **/.git" ,
40+ " **/.hg" ,
41+ " **/.next" ,
42+ " **/.svn" ,
43+ " **/bower_components" ,
44+ " **/CVS" ,
45+ " **/dist" ,
46+ " **/node_modules" ,
47+ " **/tests" ,
48+ " **/tmp" ,
4949 ] ,
5050} ;
5151
5252const getAST = ( filePath : string , content : string ) => {
5353 const fileExtension = path . extname ( filePath ) ;
5454
55- if ( fileExtension === ' .less' ) {
55+ if ( fileExtension === " .less" ) {
5656 return postcssLESS . parse ( content ) ;
5757 }
58-
59- if ( fileExtension === ' .scss' ) {
58+
59+ if ( fileExtension === " .scss" ) {
6060 return postcssSCSS . parse ( content ) ;
6161 }
6262
@@ -66,12 +66,72 @@ const getAST = (filePath: string, content: string) => {
6666export default class CSSVariableManager {
6767 private cacheManager = new CacheManager < CSSVariable > ( ) ;
6868
69+ private resolveCachedVariables = ( ) => {
70+ for ( const filePath of this . cacheManager . getFiles ( ) ) {
71+ this . cacheManager . getAll ( filePath ) . forEach ( ( variable , key ) => {
72+ this . setCssVariable (
73+ key ,
74+ this . resolveRecursiveVariables ( variable . symbol . value ) ,
75+ filePath ,
76+ variable . definition . range
77+ ) ;
78+ } ) ;
79+ }
80+ } ;
81+
82+ public resolveRecursiveVariables = ( value : string ) => {
83+ for ( let i = 0 ; i < 20 ; i ++ ) {
84+ const variableReference = value . match (
85+ / ^ v a r \( \s * ( [ a - z A - Z 0 - 9 - ] + ) \s * \) $ /
86+ ) ?. [ 1 ] ;
87+ if ( ! variableReference ) {
88+ break ;
89+ }
90+ const newValue = this . cacheManager . get ( variableReference ) ?. symbol ?. value ;
91+
92+ if ( newValue ) {
93+ value = newValue ;
94+ continue ;
95+ }
96+
97+ break ;
98+ }
99+ return value ;
100+ } ;
101+
102+ public setCssVariable = (
103+ prop : string ,
104+ value : string ,
105+ filePath : string ,
106+ range : Range
107+ ) => {
108+ const variable : CSSVariable = {
109+ symbol : {
110+ name : prop ,
111+ value : value ,
112+ } ,
113+ definition : {
114+ uri : filePath ,
115+ range : range ,
116+ } ,
117+ } ;
118+
119+ const culoriColor = culori . parse ( value ) ;
120+
121+ if ( culoriColor ) {
122+ variable . color = culoriColorToVscodeColor ( culoriColor ) ;
123+ }
124+
125+ // add to cache
126+ this . cacheManager . set ( filePath , prop , variable ) ;
127+ } ;
128+
69129 public parseCSSVariablesFromText = async ( {
70130 content,
71131 filePath,
72132 } : {
73- content : string
74- filePath : string
133+ content : string ;
134+ filePath : string ;
75135 } ) => {
76136 try {
77137 // reset cache for this file
@@ -82,7 +142,7 @@ export default class CSSVariableManager {
82142
83143 const importUrls = [ ] ;
84144 ast . walkAtRules ( ( atRule ) => {
85- if ( atRule . name === ' import' ) {
145+ if ( atRule . name === " import" ) {
86146 // only support absolute url for now
87147 const match = atRule . params . match (
88148 / [ ' " ] (?< protocol > h t t p | h t t p s ) : \/ \/ (?< url > .* ?) [ ' " ] /
@@ -100,7 +160,7 @@ export default class CSSVariableManager {
100160 importUrls . map ( async ( url ) => {
101161 try {
102162 const response = await axios ( url , {
103- responseType : ' text' ,
163+ responseType : " text" ,
104164 } ) ;
105165
106166 const cssText = await response . data ;
@@ -116,37 +176,25 @@ export default class CSSVariableManager {
116176 ) ;
117177
118178 ast . walkDecls ( ( decl ) => {
119- if ( decl . prop . startsWith ( '--' ) ) {
120- const variable : CSSVariable = {
121- symbol : {
122- name : decl . prop ,
123- value : decl . value ,
124- } ,
125- definition : {
126- uri : fileURI ,
127- range : Range . create (
128- Position . create (
129- decl . source . start . line - 1 ,
130- decl . source . start . column - 1
131- ) ,
132- Position . create (
133- decl . source . end . line - 1 ,
134- decl . source . end . column - 1
135- )
179+ if ( decl . prop . startsWith ( "--" ) ) {
180+ this . setCssVariable (
181+ decl . prop ,
182+ decl . value ,
183+ fileURI ,
184+ Range . create (
185+ Position . create (
186+ decl . source . start . line - 1 ,
187+ decl . source . start . column - 1
136188 ) ,
137- } ,
138- } ;
139-
140- const culoriColor = culori . parse ( decl . value ) ;
141-
142- if ( culoriColor ) {
143- variable . color = culoriColorToVscodeColor ( culoriColor ) ;
144- }
145-
146- // add to cache
147- this . cacheManager . set ( filePath , decl . prop , variable ) ;
189+ Position . create (
190+ decl . source . end . line - 1 ,
191+ decl . source . end . column - 1
192+ )
193+ )
194+ ) ;
148195 }
149196 } ) ;
197+ this . resolveCachedVariables ( ) ;
150198 } catch ( error ) {
151199 console . error ( filePath ) ;
152200 }
@@ -165,7 +213,7 @@ export default class CSSVariableManager {
165213 } ) . then ( ( files ) => {
166214 return Promise . all (
167215 files . map ( ( filePath ) => {
168- const content = fs . readFileSync ( filePath , ' utf8' ) ;
216+ const content = fs . readFileSync ( filePath , " utf8" ) ;
169217 return this . parseCSSVariablesFromText ( {
170218 content,
171219 filePath,
0 commit comments