@@ -21,6 +21,8 @@ import type {
2121 WorkspaceFolder ,
2222 CodeLensParams ,
2323 CodeLens ,
24+ ServerCapabilities ,
25+ ClientCapabilities ,
2426} from 'vscode-languageserver/node'
2527import {
2628 CompletionRequest ,
@@ -624,6 +626,8 @@ export class TW {
624626
625627 console . log ( `[Global] Initializing projects...` )
626628
629+ await this . updateCommonCapabilities ( )
630+
627631 // init projects for documents that are _already_ open
628632 let readyDocuments : string [ ] = [ ]
629633 let enabledProjectCount = 0
@@ -640,8 +644,6 @@ export class TW {
640644
641645 console . log ( `[Global] Initialized ${ enabledProjectCount } projects` )
642646
643- this . setupLSPHandlers ( )
644-
645647 this . disposables . push (
646648 this . connection . onDidChangeConfiguration ( async ( { settings } ) => {
647649 let previousExclude = globalSettings . tailwindCSS . files . exclude
@@ -763,7 +765,7 @@ export class TW {
763765 this . connection ,
764766 params ,
765767 this . documentService ,
766- ( ) => this . updateCapabilities ( ) ,
768+ ( ) => this . updateProjectCapabilities ( ) ,
767769 ( ) => {
768770 for ( let document of this . documentService . getAllDocuments ( ) ) {
769771 let project = this . getProject ( document )
@@ -810,9 +812,7 @@ export class TW {
810812 }
811813
812814 setupLSPHandlers ( ) {
813- if ( this . lspHandlersAdded ) {
814- return
815- }
815+ if ( this . lspHandlersAdded ) return
816816 this . lspHandlersAdded = true
817817
818818 this . connection . onHover ( this . onHover . bind ( this ) )
@@ -858,43 +858,84 @@ export class TW {
858858 }
859859 }
860860
861- private updateCapabilities ( ) {
862- if ( ! supportsDynamicRegistration ( this . initializeParams ) ) {
863- this . connection . client . register ( DidChangeConfigurationNotification . type , undefined )
864- return
861+ // Common capabilities are always supported by the language server and do not
862+ // require any project-specific information to know how to configure them.
863+ //
864+ // These capabilities will stay valid until/unless the server has to restart
865+ // in which case they'll be unregistered and then re-registered once project
866+ // discovery has completed
867+ private commonRegistrations : BulkUnregistration | undefined
868+ private async updateCommonCapabilities ( ) {
869+ let capabilities = BulkRegistration . create ( )
870+
871+ let client = this . initializeParams . capabilities
872+
873+ if ( client . textDocument ?. hover ?. dynamicRegistration ) {
874+ capabilities . add ( HoverRequest . type , { documentSelector : null } )
865875 }
866876
867- if ( this . registrations ) {
868- this . registrations . then ( ( r ) => r . dispose ( ) )
877+ if ( client . textDocument ?. colorProvider ?. dynamicRegistration ) {
878+ capabilities . add ( DocumentColorRequest . type , { documentSelector : null } )
869879 }
870880
871- let projects = Array . from ( this . projects . values ( ) )
881+ if ( client . textDocument ?. codeAction ?. dynamicRegistration ) {
882+ capabilities . add ( CodeActionRequest . type , { documentSelector : null } )
883+ }
872884
873- let capabilities = BulkRegistration . create ( )
885+ if ( client . textDocument ?. codeLens ?. dynamicRegistration ) {
886+ capabilities . add ( CodeLensRequest . type , { documentSelector : null } )
887+ }
888+
889+ if ( client . textDocument ?. documentLink ?. dynamicRegistration ) {
890+ capabilities . add ( DocumentLinkRequest . type , { documentSelector : null } )
891+ }
892+
893+ if ( client . workspace ?. didChangeConfiguration ?. dynamicRegistration ) {
894+ capabilities . add ( DidChangeConfigurationNotification . type , undefined )
895+ }
874896
875- // TODO: We should *not* be re-registering these capabilities
876- // IDEA: These should probably be static registrations up front
877- capabilities . add ( HoverRequest . type , { documentSelector : null } )
878- capabilities . add ( DocumentColorRequest . type , { documentSelector : null } )
879- capabilities . add ( CodeActionRequest . type , { documentSelector : null } )
880- capabilities . add ( CodeLensRequest . type , { documentSelector : null } )
881- capabilities . add ( DocumentLinkRequest . type , { documentSelector : null } )
882- capabilities . add ( DidChangeConfigurationNotification . type , undefined )
883-
884- // TODO: Only re-register this if trigger characters change
885- capabilities . add ( CompletionRequest . type , {
897+ this . commonRegistrations = await this . connection . client . register ( capabilities )
898+ }
899+
900+ // These capabilities depend on the projects we've found to appropriately
901+ // configure them. This may mean collecting information from all discovered
902+ // projects to determine what we can do and how
903+ private updateProjectCapabilities ( ) {
904+ this . updateTriggerCharacters ( )
905+ }
906+
907+ private lastTriggerCharacters : Set < string > | undefined
908+ private completionRegistration : Disposable | undefined
909+ private async updateTriggerCharacters ( ) {
910+ // If the client does not suppory dynamic registration of completions then
911+ // we cannot update the set of trigger characters
912+ let client = this . initializeParams . capabilities
913+ if ( ! client . textDocument ?. completion ?. dynamicRegistration ) return
914+
915+ // The new set of trigger characters is all the static ones plus
916+ // any characters from any separator in v3 config
917+ let chars = new Set < string > ( TRIGGER_CHARACTERS )
918+
919+ for ( let project of this . projects . values ( ) ) {
920+ let sep = project . state . separator
921+ if ( typeof sep !== 'string' ) continue
922+
923+ sep = sep . slice ( - 1 )
924+ if ( ! sep ) continue
925+
926+ chars . add ( sep )
927+ }
928+
929+ // If the trigger characters haven't changed then we don't need to do anything
930+ if ( equal ( Array . from ( chars ) , Array . from ( this . lastTriggerCharacters ?? [ ] ) ) ) return
931+ this . lastTriggerCharacters = chars
932+
933+ this . completionRegistration ?. dispose ( )
934+ this . completionRegistration = await this . connection . client . register ( CompletionRequest . type , {
886935 documentSelector : null ,
887936 resolveProvider : true ,
888- triggerCharacters : [
889- ...TRIGGER_CHARACTERS ,
890- ...projects
891- . map ( ( project ) => project . state . separator )
892- . filter ( ( sep ) => typeof sep === 'string' )
893- . map ( ( sep ) => sep . slice ( - 1 ) ) ,
894- ] . filter ( Boolean ) ,
937+ triggerCharacters : Array . from ( chars ) ,
895938 } )
896-
897- this . registrations = this . connection . client . register ( capabilities )
898939 }
899940
900941 private getProject ( document : TextDocumentIdentifier ) : ProjectService {
@@ -1016,47 +1057,58 @@ export class TW {
10161057 this . connection . onInitialize ( async ( params : InitializeParams ) : Promise < InitializeResult > => {
10171058 this . initializeParams = params
10181059
1019- if ( supportsDynamicRegistration ( params ) ) {
1020- return {
1021- capabilities : {
1022- textDocumentSync : TextDocumentSyncKind . Full ,
1023- workspace : {
1024- workspaceFolders : {
1025- changeNotifications : true ,
1026- } ,
1027- } ,
1028- } ,
1029- }
1030- }
1031-
10321060 this . setupLSPHandlers ( )
10331061
10341062 return {
1035- capabilities : {
1036- textDocumentSync : TextDocumentSyncKind . Full ,
1037- hoverProvider : true ,
1038- colorProvider : true ,
1039- codeActionProvider : true ,
1040- codeLensProvider : {
1041- resolveProvider : false ,
1042- } ,
1043- documentLinkProvider : { } ,
1044- completionProvider : {
1045- resolveProvider : true ,
1046- triggerCharacters : [ ...TRIGGER_CHARACTERS , ':' ] ,
1047- } ,
1048- workspace : {
1049- workspaceFolders : {
1050- changeNotifications : true ,
1051- } ,
1052- } ,
1053- } ,
1063+ capabilities : this . computeServerCapabilities ( params . capabilities ) ,
10541064 }
10551065 } )
10561066
10571067 this . connection . onInitialized ( ( ) => this . init ( ) )
10581068 }
10591069
1070+ computeServerCapabilities ( client : ClientCapabilities ) {
1071+ let capabilities : ServerCapabilities = {
1072+ textDocumentSync : TextDocumentSyncKind . Full ,
1073+ workspace : {
1074+ workspaceFolders : {
1075+ changeNotifications : true ,
1076+ } ,
1077+ } ,
1078+ }
1079+
1080+ if ( ! client . textDocument ?. hover ?. dynamicRegistration ) {
1081+ capabilities . hoverProvider = true
1082+ }
1083+
1084+ if ( ! client . textDocument ?. colorProvider ?. dynamicRegistration ) {
1085+ capabilities . colorProvider = true
1086+ }
1087+
1088+ if ( ! client . textDocument ?. codeAction ?. dynamicRegistration ) {
1089+ capabilities . codeActionProvider = true
1090+ }
1091+
1092+ if ( ! client . textDocument ?. codeLens ?. dynamicRegistration ) {
1093+ capabilities . codeLensProvider = {
1094+ resolveProvider : false ,
1095+ }
1096+ }
1097+
1098+ if ( ! client . textDocument ?. completion ?. dynamicRegistration ) {
1099+ capabilities . completionProvider = {
1100+ resolveProvider : true ,
1101+ triggerCharacters : [ ...TRIGGER_CHARACTERS , ':' ] ,
1102+ }
1103+ }
1104+
1105+ if ( ! client . textDocument ?. documentLink ?. dynamicRegistration ) {
1106+ capabilities . documentLinkProvider = { }
1107+ }
1108+
1109+ return capabilities
1110+ }
1111+
10601112 listen ( ) {
10611113 this . connection . listen ( )
10621114 }
@@ -1070,10 +1122,11 @@ export class TW {
10701122
10711123 this . refreshDiagnostics ( )
10721124
1073- if ( this . registrations ) {
1074- this . registrations . then ( ( r ) => r . dispose ( ) )
1075- this . registrations = undefined
1076- }
1125+ this . commonRegistrations ?. dispose ( )
1126+ this . commonRegistrations = undefined
1127+
1128+ this . completionRegistration ?. dispose ( )
1129+ this . completionRegistration = undefined
10771130
10781131 this . disposables . forEach ( ( d ) => d . dispose ( ) )
10791132 this . disposables . length = 0
@@ -1106,13 +1159,3 @@ export class TW {
11061159 }
11071160 }
11081161}
1109-
1110- function supportsDynamicRegistration ( params : InitializeParams ) : boolean {
1111- return (
1112- params . capabilities . textDocument ?. hover ?. dynamicRegistration &&
1113- params . capabilities . textDocument ?. colorProvider ?. dynamicRegistration &&
1114- params . capabilities . textDocument ?. codeAction ?. dynamicRegistration &&
1115- params . capabilities . textDocument ?. completion ?. dynamicRegistration &&
1116- params . capabilities . textDocument ?. documentLink ?. dynamicRegistration
1117- )
1118- }
0 commit comments