🌐 AI搜索 & 代理 主页
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ The `$/typescriptVersion` notification params include two properties:

### Workspace Configuration request for formatting settings

Server asks the client for file-specific configuration options (`tabSize` and `insertSpaces`) that are required by `tsserver` to properly format file edits when for example using "Organize imports" or performing other file modifications. Those options have to be dynamically provided by the client/editor since the values can differ for each file. For this reason server sends a `workspace/configuration` request with `scopeUri` equal to file's URI and `section` equal to `formattingOptions`. The client is expected to return a configuration that includes the following properties:
Server asks the client (provided client supports `workspace/configuration` capability) for file-specific configuration options (`tabSize` and `insertSpaces`) that are required by `tsserver` to properly format file edits when for example using "Organize imports" or performing other file modifications. Those options have to be dynamically provided by the client/editor since the values can differ for each file. For this reason server sends a `workspace/configuration` request with `scopeUri` equal to file's URI and `section` equal to `formattingOptions`. The client is expected to return a configuration that includes the following properties:

```js
{
Expand Down
46 changes: 37 additions & 9 deletions src/features/fileConfigurationManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@

import path from 'node:path';
import deepmerge from 'deepmerge';
import type lsp from 'vscode-languageserver';
import lsp from 'vscode-languageserver';
import { URI } from 'vscode-uri';
import { CommandTypes, ModuleKind, ScriptTarget, type ts, type TypeScriptInitializationOptions } from '../ts-protocol.js';
import { CommandTypes, ModuleKind, ScriptTarget, type ts, type TypeScriptInitializationOptions, SupportedFeatures } from '../ts-protocol.js';
import { ITypeScriptServiceClient } from '../typescriptService.js';
import { isTypeScriptDocument } from '../configuration/languageIds.js';
import { LspDocument } from '../document.js';
Expand Down Expand Up @@ -144,22 +144,38 @@ function areFileConfigurationsEqual(a: FileConfiguration, b: FileConfiguration):
export default class FileConfigurationManager {
public tsPreferences: Required<ts.server.protocol.UserPreferences> = deepmerge({}, DEFAULT_TSSERVER_PREFERENCES);
public workspaceConfiguration: WorkspaceConfiguration = deepmerge({}, DEFAULT_WORKSPACE_CONFIGURATION);
private readonly formatOptions: ResourceMap<Promise<FileConfiguration | undefined>>;
private readonly fileOptionsCache: ResourceMap<Promise<FileConfiguration | undefined>>;
private readonly formatOptionsCache: ResourceMap<Partial<lsp.FormattingOptions>>;
private readonly initialConfigurationRequestsMap: ResourceMap<lsp.CancellationTokenSource>;

public constructor(
private readonly client: ITypeScriptServiceClient,
private readonly lspClient: LspClient,
private readonly features: SupportedFeatures,
onCaseInsensitiveFileSystem: boolean,
) {
this.formatOptions = new ResourceMap(undefined, { onCaseInsensitiveFileSystem });
this.fileOptionsCache = new ResourceMap(undefined, { onCaseInsensitiveFileSystem });
this.formatOptionsCache = new ResourceMap(undefined, { onCaseInsensitiveFileSystem });
this.initialConfigurationRequestsMap = new ResourceMap(undefined, { onCaseInsensitiveFileSystem });
}

public onDidOpenTextDocument(document: LspDocument): void {
const cancellation = new lsp.CancellationTokenSource();
this.initialConfigurationRequestsMap.set(document.uri, cancellation);
this.ensureConfigurationForDocument(document, cancellation.token)
.then(() => {
this.initialConfigurationRequestsMap.delete(document.uri);
})
.catch(() => {});
}

public onDidCloseTextDocument(documentUri: URI): void {
// When a document gets closed delete the cached formatting options.
// This is necessary since the tsserver now closed a project when its
// last file in it closes which drops the stored formatting options
// as well.
this.formatOptions.delete(documentUri);
this.fileOptionsCache.delete(documentUri);
this.initialConfigurationRequestsMap.get(documentUri)?.cancel();
}

public mergeTsPreferences(preferences: ts.server.protocol.UserPreferences): void {
Expand Down Expand Up @@ -215,7 +231,16 @@ export default class FileConfigurationManager {
}

private async getFormattingOptions(document: LspDocument): Promise<Partial<lsp.FormattingOptions>> {
const formatConfiguration = await this.lspClient.getWorkspaceConfiguration<Partial<lsp.FormattingOptions> | undefined>(document.uri.toString(), 'formattingOptions') || {};
const formatOptionsCached = this.formatOptionsCache.get(document.uri);
if (formatOptionsCached) {
return formatOptionsCached;
}

const formatConfiguration =
this.features.workspaceConfigurationSuppport
? await this.lspClient.getWorkspaceConfiguration<Partial<lsp.FormattingOptions> | undefined>(document.uri.toString(), 'formattingOptions') || {}
: {};

const options: Partial<lsp.FormattingOptions> = {};

if (typeof formatConfiguration.tabSize === 'number') {
Expand All @@ -225,6 +250,7 @@ export default class FileConfigurationManager {
options.insertSpaces = formatConfiguration.insertSpaces;
}

this.formatOptionsCache.set(document.uri, options);
return options;
}

Expand All @@ -234,7 +260,7 @@ export default class FileConfigurationManager {
token?: lsp.CancellationToken,
): Promise<void> {
const currentOptions = this.getFileOptions(document, options);
const cachedOptions = this.formatOptions.get(document.uri);
const cachedOptions = this.fileOptionsCache.get(document.uri);
if (cachedOptions) {
const cachedOptionsValue = await cachedOptions;
if (token?.isCancellationRequested) {
Expand All @@ -255,7 +281,7 @@ export default class FileConfigurationManager {
}
})();

this.formatOptions.set(document.uri, task);
this.fileOptionsCache.set(document.uri, task);

await task;
}
Expand All @@ -272,7 +298,9 @@ export default class FileConfigurationManager {
}

public reset(): void {
this.formatOptions.clear();
this.fileOptionsCache.clear();
this.formatOptionsCache.clear();
this.initialConfigurationRequestsMap.clear();
}

private getFileOptions(
Expand Down
20 changes: 15 additions & 5 deletions src/lsp-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export class LspServer {
constructor(private options: LspServerConfiguration) {
this.logger = new PrefixingLogger(options.logger, '[lspserver]');
this.tsClient = new TsClient(onCaseInsensitiveFileSystem(), this.logger, options.lspClient);
this.fileConfigurationManager = new FileConfigurationManager(this.tsClient, this.options.lspClient, onCaseInsensitiveFileSystem());
this.fileConfigurationManager = new FileConfigurationManager(this.tsClient, this.options.lspClient, this.features, onCaseInsensitiveFileSystem());
this.commandManager = new CommandManager();
this.diagnosticsManager = new DiagnosticsManager(
diagnostics => this.options.lspClient.publishDiagnostics(diagnostics),
Expand Down Expand Up @@ -116,7 +116,7 @@ export class LspServer {
this.features.moveToFileCodeActionSupport =
userInitializationOptions.supportsMoveToFileCodeAction &&
typescriptVersion.version?.gte(API.v520);
const { textDocument } = clientCapabilities;
const { textDocument, workspace } = clientCapabilities;
if (textDocument) {
const { codeAction, completion, definition, publishDiagnostics } = textDocument;
if (codeAction) {
Expand All @@ -140,6 +140,9 @@ export class LspServer {
this.features.diagnosticsSupport = Boolean(publishDiagnostics);
this.features.diagnosticsTagSupport = Boolean(publishDiagnostics?.tagSupport);
}
if (workspace?.configuration) {
this.features.workspaceConfigurationSuppport = true;
}

this.fileConfigurationManager.mergeTsPreferences({
useLabelDetailsInCompletionEntries: this.features.completionLabelDetails,
Expand Down Expand Up @@ -369,12 +372,19 @@ export class LspServer {
}

didOpenTextDocument(params: lsp.DidOpenTextDocumentParams): void {
if (this.tsClient.toOpenDocument(params.textDocument.uri, { suppressAlertOnFailure: true })) {
throw new Error(`Can't open already open document: ${params.textDocument.uri}`);
const { uri, languageId } = params.textDocument;

if (this.tsClient.toOpenDocument(uri, { suppressAlertOnFailure: true })) {
throw new Error(`Can't open already open document: ${uri}`);
}

if (!this.tsClient.openTextDocument(params.textDocument)) {
throw new Error(`Cannot open document '${params.textDocument.uri}' (languageId: ${params.textDocument.languageId}).`);
throw new Error(`Cannot open document '${uri}' (languageId: ${languageId}).`);
}

const document = this.tsClient.toOpenDocument(uri);
if (document) {
this.fileConfigurationManager.onDidOpenTextDocument(document);
}
}

Expand Down
1 change: 1 addition & 0 deletions src/ts-protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,7 @@ export interface SupportedFeatures {
diagnosticsSupport?: boolean;
diagnosticsTagSupport?: boolean;
moveToFileCodeActionSupport?: boolean;
workspaceConfigurationSuppport?: boolean;
}

export interface TypeScriptPlugin {
Expand Down