@@ -21,6 +21,8 @@ import type {
21
21
WorkspaceFolder ,
22
22
CodeLensParams ,
23
23
CodeLens ,
24
+ ServerCapabilities ,
25
+ ClientCapabilities ,
24
26
} from 'vscode-languageserver/node'
25
27
import {
26
28
CompletionRequest ,
@@ -624,6 +626,8 @@ export class TW {
624
626
625
627
console . log ( `[Global] Initializing projects...` )
626
628
629
+ await this . updateCommonCapabilities ( )
630
+
627
631
// init projects for documents that are _already_ open
628
632
let readyDocuments : string [ ] = [ ]
629
633
let enabledProjectCount = 0
@@ -640,8 +644,6 @@ export class TW {
640
644
641
645
console . log ( `[Global] Initialized ${ enabledProjectCount } projects` )
642
646
643
- this . setupLSPHandlers ( )
644
-
645
647
this . disposables . push (
646
648
this . connection . onDidChangeConfiguration ( async ( { settings } ) => {
647
649
let previousExclude = globalSettings . tailwindCSS . files . exclude
@@ -763,7 +765,7 @@ export class TW {
763
765
this . connection ,
764
766
params ,
765
767
this . documentService ,
766
- ( ) => this . updateCapabilities ( ) ,
768
+ ( ) => this . updateProjectCapabilities ( ) ,
767
769
( ) => {
768
770
for ( let document of this . documentService . getAllDocuments ( ) ) {
769
771
let project = this . getProject ( document )
@@ -810,9 +812,7 @@ export class TW {
810
812
}
811
813
812
814
setupLSPHandlers ( ) {
813
- if ( this . lspHandlersAdded ) {
814
- return
815
- }
815
+ if ( this . lspHandlersAdded ) return
816
816
this . lspHandlersAdded = true
817
817
818
818
this . connection . onHover ( this . onHover . bind ( this ) )
@@ -858,43 +858,84 @@ export class TW {
858
858
}
859
859
}
860
860
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 } )
865
875
}
866
876
867
- if ( this . registrations ) {
868
- this . registrations . then ( ( r ) => r . dispose ( ) )
877
+ if ( client . textDocument ?. colorProvider ?. dynamicRegistration ) {
878
+ capabilities . add ( DocumentColorRequest . type , { documentSelector : null } )
869
879
}
870
880
871
- let projects = Array . from ( this . projects . values ( ) )
881
+ if ( client . textDocument ?. codeAction ?. dynamicRegistration ) {
882
+ capabilities . add ( CodeActionRequest . type , { documentSelector : null } )
883
+ }
872
884
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
+ }
874
896
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 , {
886
935
documentSelector : null ,
887
936
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 ) ,
895
938
} )
896
-
897
- this . registrations = this . connection . client . register ( capabilities )
898
939
}
899
940
900
941
private getProject ( document : TextDocumentIdentifier ) : ProjectService {
@@ -1016,47 +1057,58 @@ export class TW {
1016
1057
this . connection . onInitialize ( async ( params : InitializeParams ) : Promise < InitializeResult > => {
1017
1058
this . initializeParams = params
1018
1059
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
-
1032
1060
this . setupLSPHandlers ( )
1033
1061
1034
1062
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 ) ,
1054
1064
}
1055
1065
} )
1056
1066
1057
1067
this . connection . onInitialized ( ( ) => this . init ( ) )
1058
1068
}
1059
1069
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
+
1060
1112
listen ( ) {
1061
1113
this . connection . listen ( )
1062
1114
}
@@ -1070,10 +1122,11 @@ export class TW {
1070
1122
1071
1123
this . refreshDiagnostics ( )
1072
1124
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
1077
1130
1078
1131
this . disposables . forEach ( ( d ) => d . dispose ( ) )
1079
1132
this . disposables . length = 0
@@ -1106,13 +1159,3 @@ export class TW {
1106
1159
}
1107
1160
}
1108
1161
}
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