Skip to content

Commit

Permalink
feat: add workspace implicit project defaults configuration (#605)
Browse files Browse the repository at this point in the history
Also update implicit project configuration defaults to match vscode.
  • Loading branch information
rchl committed Oct 10, 2022
1 parent 8cf4381 commit c6b3947
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 19 deletions.
37 changes: 36 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,42 @@ completions.completeFunctionCalls: boolean;
// Diagnostics code to be omitted when reporting diagnostics.
// See https://github.com/microsoft/TypeScript/blob/master/src/compiler/diagnosticMessages.json for a full list of valid codes.
diagnostics.ignoredCodes: number[];

/**
* Enable/disable semantic checking of JavaScript files. Existing `jsconfig.json` or `tsconfig.json` files override this setting.
*
* @default false
*/
implicitProjectConfiguration.checkJs: boolean;
/**
* Enable/disable `experimentalDecorators` in JavaScript files that are not part of a project. Existing `jsconfig.json` or `tsconfig.json` files override this setting.
*
* @default false
*/
implicitProjectConfiguration.experimentalDecorators: boolean;
/**
* Sets the module system for the program. See more: https://www.typescriptlang.org/tsconfig#module.
*
* @default 'ESNext'
*/
implicitProjectConfiguration.module: string;
/**
* Enable/disable [strict function types](https://www.typescriptlang.org/tsconfig#strictFunctionTypes) in JavaScript and TypeScript files that are not part of a project. Existing `jsconfig.json` or `tsconfig.json` files override this setting.
*
* @default true
*/
implicitProjectConfiguration.strictFunctionTypes: boolean;
/**
* Enable/disable [strict null checks](https://www.typescriptlang.org/tsconfig#strictNullChecks) in JavaScript and TypeScript files that are not part of a project. Existing `jsconfig.json` or `tsconfig.json` files override this setting.
*
* @default true
*/
implicitProjectConfiguration.strictNullChecks: boolean;
/**
* Set target JavaScript language version for emitted JavaScript and include library declarations. See more: https://www.typescriptlang.org/tsconfig#target.
*
* @default 'ES2020'
*/
implicitProjectConfiguration.target: string;
```

## Code actions on save
Expand Down
33 changes: 28 additions & 5 deletions src/configuration-manager.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import deepmerge from 'deepmerge';
import path from 'node:path';
import type * as lsp from 'vscode-languageserver';
import type tsp from 'typescript/lib/protocol.d.js';
import tsp from 'typescript/lib/protocol.d.js';
import { LspDocuments } from './document.js';
import { CommandTypes } from './tsp-command-types.js';
import type { TypeScriptInitializationOptions } from './ts-protocol.js';
Expand Down Expand Up @@ -42,18 +42,41 @@ const DEFAULT_TSSERVER_PREFERENCES: Required<tsp.UserPreferences> = {
useLabelDetailsInCompletionEntries: true,
};

const DEFAULT_IMPLICIT_PROJECT_CONFIGURATION: Required<WorkspaceConfigurationImplicitProjectConfigurationOptions> = {
checkJs: false,
experimentalDecorators: false,
module: tsp.ModuleKind.ESNext,
strictFunctionTypes: true,
strictNullChecks: true,
target: tsp.ScriptTarget.ES2020,
};

const DEFAULT_WORKSPACE_CONFIGURATION: WorkspaceConfiguration = {
implicitProjectConfiguration: DEFAULT_IMPLICIT_PROJECT_CONFIGURATION,
};

export interface WorkspaceConfiguration {
javascript?: WorkspaceConfigurationLanguageOptions;
typescript?: WorkspaceConfigurationLanguageOptions;
completions?: WorkspaceConfigurationCompletionOptions;
diagnostics?: WorkspaceConfigurationDiagnosticsOptions;
implicitProjectConfiguration?: WorkspaceConfigurationImplicitProjectConfigurationOptions;
}

export interface WorkspaceConfigurationLanguageOptions {
format?: tsp.FormatCodeSettings;
inlayHints?: TypeScriptInlayHintsPreferences;
}

export interface WorkspaceConfigurationImplicitProjectConfigurationOptions {
checkJs?: boolean;
experimentalDecorators?: boolean;
module?: string;
strictFunctionTypes?: boolean;
strictNullChecks?: boolean;
target?: string;
}

/* eslint-disable @typescript-eslint/indent */
export type TypeScriptInlayHintsPreferences = Pick<
tsp.UserPreferences,
Expand All @@ -78,7 +101,7 @@ export interface WorkspaceConfigurationCompletionOptions {

export class ConfigurationManager {
public tsPreferences: Required<tsp.UserPreferences> = deepmerge({}, DEFAULT_TSSERVER_PREFERENCES);
public workspaceConfiguration: WorkspaceConfiguration = {};
public workspaceConfiguration: WorkspaceConfiguration = deepmerge({}, DEFAULT_WORKSPACE_CONFIGURATION);
private tspClient: TspClient | null = null;

constructor(private readonly documents: LspDocuments) {}
Expand All @@ -88,10 +111,10 @@ export class ConfigurationManager {
}

public setWorkspaceConfiguration(configuration: WorkspaceConfiguration): void {
this.workspaceConfiguration = deepmerge({}, configuration);
this.workspaceConfiguration = deepmerge(DEFAULT_WORKSPACE_CONFIGURATION, configuration);
}

public async setAndConfigureTspClient(workspaceFolder: string | undefined, client: TspClient, hostInfo?: TypeScriptInitializationOptions['hostInfo']): Promise<void> {
public setAndConfigureTspClient(workspaceFolder: string | undefined, client: TspClient, hostInfo?: TypeScriptInitializationOptions['hostInfo']): void {
this.tspClient = client;
const formatOptions: tsp.FormatCodeSettings = {
// We can use \n here since the editor should normalize later on to its line endings.
Expand All @@ -105,7 +128,7 @@ export class ConfigurationManager {
autoImportFileExcludePatterns: this.getAutoImportFileExcludePatternsPreference(workspaceFolder),
},
};
await this.tspClient?.request(CommandTypes.Configure, args);
client.executeWithoutWaitingForResponse(CommandTypes.Configure, args);
}

public async configureGloballyFromDocument(filename: string, formattingOptions?: lsp.FormattingOptions): Promise<void> {
Expand Down
30 changes: 17 additions & 13 deletions src/lsp-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import { SourceDefinitionCommand } from './features/source-definition.js';
import { LogDirectoryProvider } from './tsServer/logDirectoryProvider.js';
import { Trace } from './tsServer/tracer.js';
import { TypeScriptVersion, TypeScriptVersionProvider } from './tsServer/versionProvider.js';
import { getInferredProjectCompilerOptions } from './utils/tsconfig.js';
import { Position, Range } from './utils/typeConverters.js';
import { CodeActionKind } from './utils/types.js';
import { ConfigurationManager } from './configuration-manager.js';
Expand Down Expand Up @@ -200,19 +201,8 @@ export class LspServer {

this.typeScriptAutoFixProvider = new TypeScriptAutoFixProvider(this.tspClient);

await Promise.all([
this.configurationManager.setAndConfigureTspClient(this.workspaceRoot, this._tspClient, hostInfo),
this.tspClient.request(CommandTypes.CompilerOptionsForInferredProjects, {
options: {
module: tsp.ModuleKind.CommonJS,
target: tsp.ScriptTarget.ES2016,
jsx: tsp.JsxEmit.Preserve,
allowJs: true,
allowSyntheticDefaultImports: true,
allowNonTsExtensions: true,
},
}),
]);
this.configurationManager.setAndConfigureTspClient(this.workspaceRoot, this._tspClient, hostInfo);
this.setCompilerOptionsForInferredProjects();

const initializeResult: lsp.InitializeResult = {
capabilities: {
Expand Down Expand Up @@ -303,8 +293,22 @@ export class LspServer {
return undefined;
}

private setCompilerOptionsForInferredProjects(): void {
const args: tsp.SetCompilerOptionsForInferredProjectsArgs = {
options: {
...getInferredProjectCompilerOptions(this.configurationManager.workspaceConfiguration.implicitProjectConfiguration!),
allowJs: true,
allowNonTsExtensions: true,
allowSyntheticDefaultImports: true,
resolveJsonModule: true,
},
};
this.tspClient.executeWithoutWaitingForResponse(CommandTypes.CompilerOptionsForInferredProjects, args);
}

didChangeConfiguration(params: lsp.DidChangeConfigurationParams): void {
this.configurationManager.setWorkspaceConfiguration(params.settings || {});
this.setCompilerOptionsForInferredProjects();
const ignoredDiagnosticCodes = this.configurationManager.workspaceConfiguration.diagnostics?.ignoredCodes || [];
this.diagnosticQueue?.updateIgnoredDiagnosticCodes(ignoredDiagnosticCodes);
}
Expand Down
54 changes: 54 additions & 0 deletions src/utils/tsconfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/*
* Copyright (C) 2022 TypeFox and others.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*/

import tsp from 'typescript/lib/protocol.d.js';
import type { WorkspaceConfigurationImplicitProjectConfigurationOptions } from '../configuration-manager.js';

const DEFAULT_PROJECT_CONFIG: tsp.ExternalProjectCompilerOptions = Object.freeze({
module: tsp.ModuleKind.ESNext,
moduleResolution: tsp.ModuleResolutionKind.Node,
target: tsp.ScriptTarget.ES2020,
jsx: tsp.JsxEmit.React,
});

export function getInferredProjectCompilerOptions(
workspaceConfig: WorkspaceConfigurationImplicitProjectConfigurationOptions,
): tsp.ExternalProjectCompilerOptions {
const projectConfig = { ...DEFAULT_PROJECT_CONFIG };

if (workspaceConfig.checkJs) {
projectConfig.checkJs = true;
}

if (workspaceConfig.experimentalDecorators) {
projectConfig.experimentalDecorators = true;
}

if (workspaceConfig.strictNullChecks) {
projectConfig.strictNullChecks = true;
}

if (workspaceConfig.strictFunctionTypes) {
projectConfig.strictFunctionTypes = true;
}

if (workspaceConfig.module) {
projectConfig.module = workspaceConfig.module as tsp.ModuleKind;
}

if (workspaceConfig.target) {
projectConfig.target = workspaceConfig.target as tsp.ScriptTarget;
}

projectConfig.sourceMap = true;

return projectConfig;
}

0 comments on commit c6b3947

Please sign in to comment.