Skip to content

Commit 8170e66

Browse files
Don't register ability to hover, request colors, etc… more than once (#1378)
We're currently re-registering "common" capabilities per workspace folder. This causes VSCode to make multiple requests to the server for things like hover causing higher CPU usage. This then results multiple hovers being displayed. What's worse is that for some scenarios where the server has to internally do a restart the old registrations weren't getting disposed of because of a race condition when calling it concurrently for multiple folders. I've done two things to address this: - Common capability registration will only happen after all project folders have been initialized — hopefully this doesn't cause any problems (though if it does it's revealing a bigger underlying one) - Common capabilities will be explicitly disposed before registering again This should mean that this only happens one time per server initialization. Fixes #1371
1 parent fa87e8a commit 8170e66

File tree

1 file changed

+24
-6
lines changed
  • packages/tailwindcss-language-server/src

1 file changed

+24
-6
lines changed

packages/tailwindcss-language-server/src/tw.ts

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,10 @@ export class TW {
174174
}
175175
}
176176

177+
if (results.some((result) => result.status === 'fulfilled')) {
178+
await this.updateCommonCapabilities()
179+
}
180+
177181
await this.listenForEvents()
178182
}
179183

@@ -628,8 +632,6 @@ export class TW {
628632

629633
console.log(`[Global] Initializing projects...`)
630634

631-
await this.updateCommonCapabilities()
632-
633635
// init projects for documents that are _already_ open
634636
let readyDocuments: string[] = []
635637
let enabledProjectCount = 0
@@ -896,6 +898,7 @@ export class TW {
896898
capabilities.add(DidChangeConfigurationNotification.type, undefined)
897899
}
898900

901+
this.commonRegistrations?.dispose()
899902
this.commonRegistrations = await this.connection.client.register(capabilities)
900903
}
901904

@@ -907,7 +910,7 @@ export class TW {
907910
}
908911

909912
private lastTriggerCharacters: Set<string> | undefined
910-
private completionRegistration: Disposable | undefined
913+
private completionRegistration: Promise<Disposable> | undefined
911914
private async updateTriggerCharacters() {
912915
// If the client does not suppory dynamic registration of completions then
913916
// we cannot update the set of trigger characters
@@ -938,12 +941,27 @@ export class TW {
938941

939942
this.lastTriggerCharacters = chars
940943

941-
this.completionRegistration?.dispose()
942-
this.completionRegistration = await this.connection.client.register(CompletionRequest.type, {
944+
let current = this.completionRegistration
945+
this.completionRegistration = this.connection.client.register(CompletionRequest.type, {
943946
documentSelector: null,
944947
resolveProvider: true,
945948
triggerCharacters: Array.from(chars),
946949
})
950+
951+
// NOTE:
952+
// This weird setup works around a race condition where multiple projects
953+
// with different separators update their capabilities at the same time. It
954+
// is extremely unlikely but it could cause `CompletionRequest` to be
955+
// registered more than once with the LSP client.
956+
//
957+
// We store the promises meaning everything up to this point is synchronous
958+
// so it should be fine but really the proper fix here is to:
959+
// - Refactor workspace folder initialization so discovery, initialization,
960+
// file events, config watchers, etc… are all shared.
961+
// - Remove the need for the "restart" concept in the server for as much as
962+
// possible. Each project should be capable of reloading its modules.
963+
await current?.then((r) => r.dispose())
964+
await this.completionRegistration
947965
}
948966

949967
private getProject(document: TextDocumentIdentifier): ProjectService {
@@ -1134,7 +1152,7 @@ export class TW {
11341152
this.commonRegistrations = undefined
11351153

11361154
this.lastTriggerCharacters.clear()
1137-
this.completionRegistration?.dispose()
1155+
this.completionRegistration?.then((r) => r.dispose())
11381156
this.completionRegistration = undefined
11391157

11401158
this.disposables.forEach((d) => d.dispose())

0 commit comments

Comments
 (0)