-
Notifications
You must be signed in to change notification settings - Fork 725
Created OnAutoInsertFeature to support dynamic capability registration #7033
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
ac85eb7
42b9352
70f1b0d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,134 @@ | ||
| /*--------------------------------------------------------------------------------------------- | ||
| * Copyright (c) Microsoft Corporation. All rights reserved. | ||
| * Licensed under the MIT License. See License.txt in the project root for license information. | ||
| *--------------------------------------------------------------------------------------------*/ | ||
|
|
||
| import { | ||
| languages as Languages, | ||
| workspace as Workspace, | ||
| DocumentSelector as VDocumentSelector, | ||
| TextDocument, | ||
| } from 'vscode'; | ||
|
|
||
| import { DynamicFeature, FeatureState, LanguageClient, RegistrationData, ensure } from 'vscode-languageclient/node'; | ||
|
|
||
| import { | ||
| ClientCapabilities, | ||
| DocumentSelector, | ||
| InitializeParams, | ||
| ProtocolRequestType, | ||
| RegistrationType, | ||
| ServerCapabilities, | ||
| } from 'vscode-languageserver-protocol'; | ||
|
|
||
| import * as RoslynProtocol from './roslynProtocol'; | ||
| import * as UUID from 'vscode-languageclient/lib/common/utils/uuid'; | ||
|
|
||
| export class OnAutoInsertFeature implements DynamicFeature<RoslynProtocol.OnAutoInsertRegistrationOptions> { | ||
| private readonly _client: LanguageClient; | ||
| private readonly _registrations: Map<string, RegistrationData<RoslynProtocol.OnAutoInsertRegistrationOptions>>; | ||
|
|
||
| constructor(client: LanguageClient) { | ||
| this._client = client; | ||
| this._registrations = new Map(); | ||
| this.registrationType = new ProtocolRequestType< | ||
| RoslynProtocol.OnAutoInsertParams, | ||
| RoslynProtocol.OnAutoInsertResponseItem | null, | ||
| never, | ||
| void, | ||
| RoslynProtocol.OnAutoInsertRegistrationOptions | ||
| >(RoslynProtocol.OnAutoInsertRequest.method); | ||
| } | ||
| fillInitializeParams?: ((params: InitializeParams) => void) | undefined; | ||
| preInitialize?: | ||
| | ((capabilities: ServerCapabilities<any>, documentSelector: DocumentSelector | undefined) => void) | ||
| | undefined; | ||
| registrationType: RegistrationType<RoslynProtocol.OnAutoInsertRegistrationOptions>; | ||
| register(data: RegistrationData<RoslynProtocol.OnAutoInsertRegistrationOptions>): void { | ||
| if (!data.registerOptions.documentSelector) { | ||
| return; | ||
| } | ||
| this._registrations.set(data.id, data); | ||
| } | ||
| unregister(id: string): void { | ||
| const registration = this._registrations.get(id); | ||
| if (registration !== undefined) { | ||
| this._registrations.delete(id); | ||
| } | ||
| } | ||
| dispose(): void { | ||
| this._registrations.clear(); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I assume VSCode calls this when the instance shuts down? We don't need to manually do anything right?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Documentation says this: /**
* Called when the client is stopped to dispose this feature. Usually a feature
* un-registers listeners registered hooked up with the VS Code extension host.
*/That said, it didnt' actually get called on shutdown. Perhaps there's a different way to trigger this. |
||
| } | ||
|
|
||
| public getState(): FeatureState { | ||
| const selectors = this.getDocumentSelectors(); | ||
|
|
||
| let count = 0; | ||
| for (const selector of selectors) { | ||
| count++; | ||
| for (const document of Workspace.textDocuments) { | ||
| if (Languages.match(selector, document) > 0) { | ||
| return { kind: 'document', id: this.registrationType.method, registrations: true, matches: true }; | ||
| } | ||
| } | ||
| } | ||
| const registrations = count > 0; | ||
| return { kind: 'document', id: this.registrationType.method, registrations, matches: false }; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wondering - could this implement DynamicDocumentFeature instead? Looks like it might already have this state management code This is also probably outside ofthe scope of this PR, but it seems like most vscode features implement TextDocumentLanguageFeature, and then also have the implementation of the feature inside that type.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I did go down that road initially. However, I wasn't able to get all the way there because it started making hardcoded assumptions about the list of language features. For example, it requires a client that implements FeatureClient<Middleware, LanguageClientOptions> but MiddleWare is hardcoded to this: https://github.com/microsoft/vscode-languageserver-node/blob/54dd4abae40251a18fbe263285e6e97f27457e62/client/src/common/client.ts#L338 |
||
| } | ||
|
|
||
| public fillClientCapabilities(capabilities: ClientCapabilities): void { | ||
| const textDocumentCapabilities: any = ensure(capabilities, 'textDocument')!; | ||
| if (textDocumentCapabilities['_vs_onAutoInsert'] === undefined) { | ||
| textDocumentCapabilities['_vs_onAutoInsert'] = {} as any; | ||
| } | ||
| const onAutoInsertCapability = textDocumentCapabilities['_vs_onAutoInsert']; | ||
| onAutoInsertCapability.dynamicRegistration = true; | ||
| } | ||
|
|
||
| public initialize(_capabilities: ServerCapabilities, documentSelector: DocumentSelector): void { | ||
| const capabilities: any = _capabilities; | ||
| const options = this.getRegistrationOptions(documentSelector, capabilities._vs_onAutoInsertProvider); | ||
| if (!options) { | ||
| return; | ||
| } | ||
| this.register({ | ||
| id: UUID.generateUuid(), | ||
| registerOptions: options, | ||
| }); | ||
| } | ||
|
|
||
| public getOptions(textDocument: TextDocument): RoslynProtocol.OnAutoInsertOptions | undefined { | ||
| for (const registration of this._registrations.values()) { | ||
| const selector = registration.registerOptions.documentSelector; | ||
| if ( | ||
| selector !== null && | ||
| Languages.match(this._client.protocol2CodeConverter.asDocumentSelector(selector), textDocument) > 0 | ||
| ) { | ||
| return registration.registerOptions; | ||
| } | ||
| } | ||
| return undefined; | ||
| } | ||
|
|
||
| private *getDocumentSelectors(): IterableIterator<VDocumentSelector> { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm learning so many new things in this PR...
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Me too. I haven't really written any TypeScript before. :) |
||
| for (const registration of this._registrations.values()) { | ||
| const selector = registration.registerOptions.documentSelector; | ||
| if (selector === null) { | ||
| continue; | ||
| } | ||
| yield this._client.protocol2CodeConverter.asDocumentSelector(selector); | ||
| } | ||
| } | ||
|
|
||
| private getRegistrationOptions( | ||
| documentSelector: DocumentSelector | undefined, | ||
| capability: undefined | RoslynProtocol.OnAutoInsertOptions | ||
| ): (RoslynProtocol.OnAutoInsertRegistrationOptions & { documentSelector: DocumentSelector }) | undefined { | ||
| if (!documentSelector || !capability) { | ||
| return undefined; | ||
| } | ||
| return Object.assign({}, capability, { documentSelector }) as RoslynProtocol.OnAutoInsertRegistrationOptions & { | ||
| documentSelector: DocumentSelector; | ||
| }; | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did not know this could be extended in this way. This is super useful!