@@ -36,13 +36,8 @@ import pkgUp from 'pkg-up'
3636import stackTrace from 'stack-trace'
3737import extractClassNames from './lib/extractClassNames'
3838import { klona } from 'klona/full'
39- import { doHover } from '@tailwindcss/language-service/src/hoverProvider'
40- import { getCodeLens } from '@tailwindcss/language-service/src/codeLensProvider'
39+ import { createLanguageService } from '@tailwindcss/language-service/src/service'
4140import { Resolver } from './resolver'
42- import {
43- doComplete ,
44- resolveCompletionItem ,
45- } from '@tailwindcss/language-service/src/completionProvider'
4641import type {
4742 State ,
4843 FeatureFlags ,
@@ -52,17 +47,12 @@ import type {
5247 ClassEntry ,
5348} from '@tailwindcss/language-service/src/util/state'
5449import { provideDiagnostics } from './lsp/diagnosticsProvider'
55- import { doCodeActions } from '@tailwindcss/language-service/src/codeActions/codeActionProvider'
56- import { getDocumentColors } from '@tailwindcss/language-service/src/documentColorProvider'
57- import { getDocumentLinks } from '@tailwindcss/language-service/src/documentLinksProvider'
5850import { debounce } from 'debounce'
5951import { getModuleDependencies } from './util/getModuleDependencies'
6052import assert from 'node:assert'
6153// import postcssLoadConfig from 'postcss-load-config'
6254import { bigSign } from '@tailwindcss/language-service/src/util/jit'
6355import { getColor } from '@tailwindcss/language-service/src/util/color'
64- import * as culori from 'culori'
65- import namedColors from 'color-name'
6656import tailwindPlugins from './lib/plugins'
6757import isExcluded from './util/isExcluded'
6858import { getFileFsPath } from './util/uri'
@@ -72,7 +62,6 @@ import {
7262 firstOptional ,
7363 withoutLogs ,
7464 clearRequireCache ,
75- withFallback ,
7665 isObject ,
7766 pathToFileURL ,
7867 changeAffectsFile ,
@@ -85,8 +74,7 @@ import { supportedFeatures } from '@tailwindcss/language-service/src/features'
8574import { loadDesignSystem } from './util/v4'
8675import { readCssFile } from './util/css'
8776import type { DesignSystem } from '@tailwindcss/language-service/src/util/v4'
88-
89- const colorNames = Object . keys ( namedColors )
77+ import { File , FileType } from '@tailwindcss/language-service/src/fs'
9078
9179function getConfigId ( configPath : string , configDependencies : string [ ] ) : string {
9280 return JSON . stringify (
@@ -233,36 +221,71 @@ export async function createProjectService(
233221 getDocumentSymbols : ( uri : string ) => {
234222 return connection . sendRequest ( '@/tailwindCSS/getDocumentSymbols' , { uri } )
235223 } ,
236- async readDirectory ( document , directory ) {
224+ async readDirectory ( ) {
225+ // NOTE: This is overwritten in `createLanguageDocument`
226+ throw new Error ( 'Not implemented' )
227+ } ,
228+ } ,
229+ }
230+
231+ let service = createLanguageService ( {
232+ state : ( ) => state ,
233+ fs : {
234+ async document ( uri : string ) {
235+ return documentService . getDocument ( uri )
236+ } ,
237+ async resolve ( document : TextDocument , relativePath : string ) : Promise < string | null > {
238+ let documentPath = URI . parse ( document . uri ) . fsPath
239+ let baseDir = path . dirname ( documentPath )
240+
241+ let resolved = await resolver . substituteId ( relativePath , baseDir )
242+ resolved ??= relativePath
243+
244+ return URI . file ( path . resolve ( baseDir , resolved ) ) . toString ( )
245+ } ,
246+
247+ async readDirectory ( document : TextDocument , filepath : string ) : Promise < File [ ] > {
237248 try {
238249 let baseDir = path . dirname ( getFileFsPath ( document . uri ) )
239- directory = await resolver . substituteId ( `${ directory } /` , baseDir )
240- directory = path . resolve ( baseDir , directory )
241-
242- let dirents = await fs . promises . readdir ( directory , { withFileTypes : true } )
243-
244- let result : Array < [ string , { isDirectory : boolean } ] | null > = await Promise . all (
245- dirents . map ( async ( dirent ) => {
246- let isDirectory = dirent . isDirectory ( )
247- let shouldRemove = await isExcluded (
248- state ,
249- document ,
250- path . join ( directory , dirent . name , isDirectory ? '/' : '' ) ,
251- )
250+ filepath = await resolver . substituteId ( `${ filepath } /` , baseDir )
251+ filepath = path . resolve ( baseDir , filepath )
252252
253- if ( shouldRemove ) return null
253+ let dirents = await fs . promises . readdir ( filepath , { withFileTypes : true } )
254254
255- return [ dirent . name , { isDirectory } ]
256- } ) ,
257- )
255+ let results : File [ ] = [ ]
256+
257+ for ( let dirent of dirents ) {
258+ let isDirectory = dirent . isDirectory ( )
259+ let shouldRemove = await isExcluded (
260+ state ,
261+ document ,
262+ path . join ( filepath , dirent . name , isDirectory ? '/' : '' ) ,
263+ )
264+ if ( shouldRemove ) continue
265+
266+ let type : FileType = 'unknown'
258267
259- return result . filter ( ( item ) => item !== null )
268+ if ( dirent . isFile ( ) ) {
269+ type = 'file'
270+ } else if ( dirent . isDirectory ( ) ) {
271+ type = 'directory'
272+ } else if ( dirent . isSymbolicLink ( ) ) {
273+ type = 'symbolic-link'
274+ }
275+
276+ results . push ( {
277+ name : dirent . name ,
278+ type,
279+ } )
280+ }
281+
282+ return results
260283 } catch {
261284 return [ ]
262285 }
263286 } ,
264287 } ,
265- }
288+ } )
266289
267290 if ( projectConfig . configPath && projectConfig . config . source === 'js' ) {
268291 let deps = [ ]
@@ -1171,139 +1194,79 @@ export async function createProjectService(
11711194 } ,
11721195 onFileEvents,
11731196 async onHover ( params : TextDocumentPositionParams ) : Promise < Hover > {
1174- return withFallback ( async ( ) => {
1175- if ( ! state . enabled ) return null
1176- let document = documentService . getDocument ( params . textDocument . uri )
1177- if ( ! document ) return null
1178- let settings = await state . editor . getConfiguration ( document . uri )
1179- if ( ! settings . tailwindCSS . hovers ) return null
1180- if ( await isExcluded ( state , document ) ) return null
1181- return doHover ( state , document , params . position )
1182- } , null )
1197+ try {
1198+ let doc = await service . open ( params . textDocument . uri )
1199+ if ( ! doc ) return null
1200+ return doc . hover ( params . position )
1201+ } catch {
1202+ return null
1203+ }
11831204 } ,
11841205 async onCodeLens ( params : CodeLensParams ) : Promise < CodeLens [ ] > {
1185- return withFallback ( async ( ) => {
1186- if ( ! state . enabled ) return null
1187- let document = documentService . getDocument ( params . textDocument . uri )
1188- if ( ! document ) return null
1189- let settings = await state . editor . getConfiguration ( document . uri )
1190- if ( ! settings . tailwindCSS . codeLens ) return null
1191- if ( await isExcluded ( state , document ) ) return null
1192- return getCodeLens ( state , document )
1193- } , null )
1206+ try {
1207+ let doc = await service . open ( params . textDocument . uri )
1208+ if ( ! doc ) return null
1209+ return doc . codeLenses ( )
1210+ } catch {
1211+ return [ ]
1212+ }
11941213 } ,
11951214 async onCompletion ( params : CompletionParams ) : Promise < CompletionList > {
1196- return withFallback ( async ( ) => {
1197- if ( ! state . enabled ) return null
1198- let document = documentService . getDocument ( params . textDocument . uri )
1199- if ( ! document ) return null
1200- let settings = await state . editor . getConfiguration ( document . uri )
1201- if ( ! settings . tailwindCSS . suggestions ) return null
1202- if ( await isExcluded ( state , document ) ) return null
1203- return doComplete ( state , document , params . position , params . context )
1204- } , null )
1215+ try {
1216+ let doc = await service . open ( params . textDocument . uri )
1217+ if ( ! doc ) return null
1218+ return doc . completions ( params . position )
1219+ } catch {
1220+ return null
1221+ }
12051222 } ,
1206- onCompletionResolve ( item : CompletionItem ) : Promise < CompletionItem > {
1207- return withFallback ( ( ) => {
1208- if ( ! state . enabled ) return null
1209- return resolveCompletionItem ( state , item )
1210- } , null )
1223+ async onCompletionResolve ( item : CompletionItem ) : Promise < CompletionItem > {
1224+ try {
1225+ return await service . resolveCompletion ( item )
1226+ } catch {
1227+ return null
1228+ }
12111229 } ,
12121230 async onCodeAction ( params : CodeActionParams ) : Promise < CodeAction [ ] > {
1213- return withFallback ( async ( ) => {
1214- if ( ! state . enabled ) return null
1215- let document = documentService . getDocument ( params . textDocument . uri )
1216- if ( ! document ) return null
1217- let settings = await state . editor . getConfiguration ( document . uri )
1218- if ( ! settings . tailwindCSS . codeActions ) return null
1219- return doCodeActions ( state , params , document )
1220- } , null )
1231+ try {
1232+ let doc = await service . open ( params . textDocument . uri )
1233+ if ( ! doc ) return null
1234+ return doc . codeActions ( params . range , params . context )
1235+ } catch {
1236+ return [ ]
1237+ }
12211238 } ,
1222- onDocumentLinks ( params : DocumentLinkParams ) : Promise < DocumentLink [ ] > {
1223- if ( ! state . enabled ) return null
1224- let document = documentService . getDocument ( params . textDocument . uri )
1225- if ( ! document ) return null
1226-
1227- let documentPath = URI . parse ( document . uri ) . fsPath
1228- let baseDir = path . dirname ( documentPath )
1229-
1230- async function resolveTarget ( linkPath : string ) {
1231- linkPath = ( await resolver . substituteId ( linkPath , baseDir ) ) ?? linkPath
1232-
1233- return URI . file ( path . resolve ( baseDir , linkPath ) ) . toString ( )
1239+ async onDocumentLinks ( params : DocumentLinkParams ) : Promise < DocumentLink [ ] > {
1240+ try {
1241+ let doc = await service . open ( params . textDocument . uri )
1242+ if ( ! doc ) return null
1243+ return doc . documentLinks ( )
1244+ } catch {
1245+ return [ ]
12341246 }
1235-
1236- return getDocumentLinks ( state , document , resolveTarget )
12371247 } ,
12381248 provideDiagnostics : debounce (
1239- ( document : TextDocument ) => {
1240- if ( ! state . enabled ) return
1241- provideDiagnostics ( state , document )
1242- } ,
1249+ ( document ) => provideDiagnostics ( service , state , document ) ,
12431250 params . initializationOptions ?. testMode ? 0 : 500 ,
12441251 ) ,
1245- provideDiagnosticsForce : ( document : TextDocument ) => {
1246- if ( ! state . enabled ) return
1247- provideDiagnostics ( state , document )
1248- } ,
1252+ provideDiagnosticsForce : ( document ) => provideDiagnostics ( service , state , document ) ,
12491253 async onDocumentColor ( params : DocumentColorParams ) : Promise < ColorInformation [ ] > {
1250- return withFallback ( async ( ) => {
1251- if ( ! state . enabled ) return [ ]
1252- let document = documentService . getDocument ( params . textDocument . uri )
1253- if ( ! document ) return [ ]
1254- if ( await isExcluded ( state , document ) ) return null
1255- return getDocumentColors ( state , document )
1256- } , null )
1254+ try {
1255+ let doc = await service . open ( params . textDocument . uri )
1256+ if ( ! doc ) return null
1257+ return doc . documentColors ( )
1258+ } catch {
1259+ return [ ]
1260+ }
12571261 } ,
12581262 async onColorPresentation ( params : ColorPresentationParams ) : Promise < ColorPresentation [ ] > {
1259- let document = documentService . getDocument ( params . textDocument . uri )
1260- if ( ! document ) return [ ]
1261- let className = document . getText ( params . range )
1262- let match = className . match (
1263- new RegExp ( `-\\[(${ colorNames . join ( '|' ) } |(?:(?:#|rgba?\\(|hsla?\\())[^\\]]+)\\]$` , 'i' ) ,
1264- )
1265- // let match = className.match(/-\[((?:#|rgba?\(|hsla?\()[^\]]+)\]$/i)
1266- if ( match === null ) return [ ]
1267-
1268- let currentColor = match [ 1 ]
1269-
1270- let isNamedColor = colorNames . includes ( currentColor )
1271-
1272- let color : culori . Color = {
1273- mode : 'rgb' ,
1274- r : params . color . red ,
1275- g : params . color . green ,
1276- b : params . color . blue ,
1277- alpha : params . color . alpha ,
1278- }
1279-
1280- let hexValue = culori . formatHex8 ( color )
1281-
1282- if ( ! isNamedColor && ( currentColor . length === 4 || currentColor . length === 5 ) ) {
1283- let [ , ...chars ] =
1284- hexValue . match ( / ^ # ( [ a - f \d ] ) \1( [ a - f \d ] ) \2( [ a - f \d ] ) \3(?: ( [ a - f \d ] ) \4) ? $ / i) ?? [ ]
1285- if ( chars . length ) {
1286- hexValue = `#${ chars . filter ( Boolean ) . join ( '' ) } `
1287- }
1288- }
1289-
1290- if ( hexValue . length === 5 ) {
1291- hexValue = hexValue . replace ( / f $ / , '' )
1292- } else if ( hexValue . length === 9 ) {
1293- hexValue = hexValue . replace ( / f f $ / , '' )
1263+ try {
1264+ let doc = await service . open ( params . textDocument . uri )
1265+ if ( ! doc ) return null
1266+ return doc . colorPresentation ( params . color , params . range )
1267+ } catch {
1268+ return [ ]
12941269 }
1295-
1296- let prefix = className . substr ( 0 , match . index )
1297-
1298- return [
1299- hexValue ,
1300- culori . formatRgb ( color ) . replace ( / / g, '' ) ,
1301- culori
1302- . formatHsl ( color )
1303- . replace ( / / g, '' )
1304- // round numbers
1305- . replace ( / \d + \. \d + ( % ? ) / g, ( value , suffix ) => `${ Math . round ( parseFloat ( value ) ) } ${ suffix } ` ) ,
1306- ] . map ( ( value ) => ( { label : `${ prefix } -[${ value } ]` } ) )
13071270 } ,
13081271 sortClassLists ( classLists : string [ ] ) : string [ ] {
13091272 if ( ! state . jit ) {
0 commit comments