Skip to content

Commit

Permalink
refactor: move ts config resolve to typescript-language-service
Browse files Browse the repository at this point in the history
close #1823
  • Loading branch information
johnsoncodehk committed Sep 6, 2022
1 parent 27a16ee commit b3c9155
Show file tree
Hide file tree
Showing 17 changed files with 191 additions and 193 deletions.
@@ -0,0 +1,38 @@
import * as vscode from 'vscode-languageserver-protocol';
import { isTypeScriptDocument } from './shared';

export async function getFormatCodeSettings(
getConfiguration: (section: string) => Promise<any>,
uri: string,
options?: vscode.FormattingOptions,
): Promise<ts.FormatCodeSettings> {

let config = await getConfiguration(isTypeScriptDocument(uri) ? 'typescript.format' : 'javascript.format');

config = config ?? {};

return {
convertTabsToSpaces: options?.insertSpaces,
tabSize: options?.tabSize,
indentSize: options?.tabSize,
indentStyle: 2 /** ts.IndentStyle.Smart */,
newLineCharacter: '\n',
insertSpaceAfterCommaDelimiter: config.insertSpaceAfterCommaDelimiter ?? true,
insertSpaceAfterConstructor: config.insertSpaceAfterConstructor ?? false,
insertSpaceAfterSemicolonInForStatements: config.insertSpaceAfterSemicolonInForStatements ?? true,
insertSpaceBeforeAndAfterBinaryOperators: config.insertSpaceBeforeAndAfterBinaryOperators ?? true,
insertSpaceAfterKeywordsInControlFlowStatements: config.insertSpaceAfterKeywordsInControlFlowStatements ?? true,
insertSpaceAfterFunctionKeywordForAnonymousFunctions: config.insertSpaceAfterFunctionKeywordForAnonymousFunctions ?? true,
insertSpaceBeforeFunctionParenthesis: config.insertSpaceBeforeFunctionParenthesis ?? false,
insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: config.insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis ?? false,
insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: config.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets ?? false,
insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: config.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces ?? true,
insertSpaceAfterOpeningAndBeforeClosingEmptyBraces: config.insertSpaceAfterOpeningAndBeforeClosingEmptyBraces ?? true,
insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: config.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces ?? false,
insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: config.insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces ?? false,
insertSpaceAfterTypeAssertion: config.insertSpaceAfterTypeAssertion ?? false,
placeOpenBraceOnNewLineForFunctions: config.placeOpenBraceOnNewLineForFunctions ?? false,
placeOpenBraceOnNewLineForControlBlocks: config.placeOpenBraceOnNewLineForControlBlocks ?? false,
semicolons: config.semicolons ?? 'ignore',
};
}
@@ -0,0 +1,81 @@
import { isTypeScriptDocument } from './shared';

export async function getUserPreferences(
getConfiguration: (section: string) => Promise<any>,
uri: string
): Promise<ts.UserPreferences> {

let config = await getConfiguration(isTypeScriptDocument(uri) ? 'typescript' : 'javascript');
let preferencesConfig = await getConfiguration(isTypeScriptDocument(uri) ? 'typescript.preferences' : 'javascript.preferences');

config = config ?? {};
preferencesConfig = preferencesConfig ?? {};

const preferences: ts.UserPreferences = {
quotePreference: getQuoteStylePreference(preferencesConfig),
importModuleSpecifierPreference: getImportModuleSpecifierPreference(preferencesConfig),
importModuleSpecifierEnding: getImportModuleSpecifierEndingPreference(preferencesConfig),
allowTextChangesInNewFiles: uri.startsWith('file://'),
providePrefixAndSuffixTextForRename: (preferencesConfig.renameShorthandProperties ?? true) === false ? false : (preferencesConfig.useAliasesForRenames ?? true),
// @ts-ignore
allowRenameOfImportPath: true,
includeAutomaticOptionalChainCompletions: config.suggest?.includeAutomaticOptionalChainCompletions ?? true,
provideRefactorNotApplicableReason: true,
// @ts-ignore
includeCompletionsForImportStatements: config.suggest?.includeCompletionsForImportStatements ?? true,
includeCompletionsWithSnippetText: config.suggest?.includeCompletionsWithSnippetText ?? true,
allowIncompleteCompletions: true,
// @ts-ignore
displayPartsForJSDoc: true,

// inlay hints
includeInlayParameterNameHints: getInlayParameterNameHintsPreference(config),
includeInlayParameterNameHintsWhenArgumentMatchesName: !(config.inlayHints?.parameterNames?.suppressWhenArgumentMatchesName ?? true),
includeInlayFunctionParameterTypeHints: config.inlayHints?.parameterTypes?.enabled ?? false,
includeInlayVariableTypeHints: config.inlayHints?.variableTypes?.enabled ?? false,
includeInlayPropertyDeclarationTypeHints: config.inlayHints?.propertyDeclarationTypes?.enabled ?? false,
includeInlayFunctionLikeReturnTypeHints: config.inlayHints?.functionLikeReturnTypes?.enabled ?? false,
includeInlayEnumMemberValueHints: config.inlayHints?.enumMemberValues?.enabled ?? false,

// custom
includeCompletionsForModuleExports: config.suggest?.autoImports ?? true,
};

return preferences;
}

function getQuoteStylePreference(config: any) {
switch (config.quoteStyle as string) {
case 'single': return 'single';
case 'double': return 'double';
default: return 'auto';
}
}

function getImportModuleSpecifierPreference(config: any) {
switch (config.importModuleSpecifier as string) {
case 'project-relative': return 'project-relative';
case 'relative': return 'relative';
case 'non-relative': return 'non-relative';
default: return undefined;
}
}

function getImportModuleSpecifierEndingPreference(config: any) {
switch (config.importModuleSpecifierEnding as string) {
case 'minimal': return 'minimal';
case 'index': return 'index';
case 'js': return 'js';
default: return 'minimal'; // fix https://github.com/johnsoncodehk/volar/issues/1667
// default: return 'auto';
}
}

function getInlayParameterNameHintsPreference(config: any) {
switch (config.inlayHints?.parameterNames?.enabled) {
case 'none': return 'none';
case 'literals': return 'literals';
case 'all': return 'all';
default: return undefined;
}
}
3 changes: 3 additions & 0 deletions packages/typescript-language-service/src/configs/shared.ts
@@ -0,0 +1,3 @@
export function isTypeScriptDocument(uri: string) {
return uri.endsWith('.ts') || uri.endsWith('.tsx');
}
29 changes: 15 additions & 14 deletions packages/typescript-language-service/src/index.ts
@@ -1,4 +1,3 @@
import * as vscode from 'vscode-languageserver-protocol';
import * as completions from './services/completions/basic';
import * as directiveCommentCompletions from './services/completions/directiveComment';
import * as jsDocCompletions from './services/completions/jsDoc';
Expand Down Expand Up @@ -29,20 +28,22 @@ import { TextDocument } from 'vscode-languageserver-textdocument';
import * as shared from '@volar/shared';
import type * as ts from 'typescript/lib/tsserverlibrary';
import { URI } from 'vscode-uri';
import * as _ from 'vscode-languageserver-protocol';

export interface LanguageService extends ReturnType<typeof createLanguageService> { }
export { getSemanticTokenLegend } from './services/semanticTokens';
export * from './configs/getFormatCodeSettings';
export * from './configs/getUserPreferences';

export interface Settings {
getFormatOptions?(uri: string, options?: vscode.FormattingOptions): Promise<ts.FormatCodeSettings>;
getPreferences?(uri: string): Promise<ts.UserPreferences>;
}
export interface GetConfiguration {
(section: string): Promise<any>;
};

export function createLanguageService(
ts: typeof import('typescript/lib/tsserverlibrary'),
host: ts.LanguageServiceHost,
languageService: ts.LanguageService,
settings: Settings,
getConfiguration: GetConfiguration,
rootUri: URI,
) {

Expand All @@ -55,21 +56,21 @@ export function createLanguageService(
findFileReferences: fileReferences.register(rootUri, languageService, getValidTextDocument, getTextDocument),
findImplementations: implementation.register(rootUri, languageService, getValidTextDocument, getTextDocument),
prepareRename: prepareRename.register(languageService, getValidTextDocument),
doRename: rename.register(rootUri, languageService, getValidTextDocument, settings),
getEditsForFileRename: fileRename.register(rootUri, languageService, getValidTextDocument, settings),
getCodeActions: codeActions.register(rootUri, languageService, getValidTextDocument, settings),
doCodeActionResolve: codeActionResolve.register(rootUri, languageService, getValidTextDocument, settings),
getInlayHints: inlayHints.register(languageService, getValidTextDocument, settings, ts),
doRename: rename.register(rootUri, languageService, getValidTextDocument, getConfiguration),
getEditsForFileRename: fileRename.register(rootUri, languageService, getValidTextDocument, getConfiguration),
getCodeActions: codeActions.register(rootUri, languageService, getValidTextDocument, getConfiguration),
doCodeActionResolve: codeActionResolve.register(rootUri, languageService, getValidTextDocument, getConfiguration),
getInlayHints: inlayHints.register(languageService, getValidTextDocument, getConfiguration, ts),

findDocumentHighlights: documentHighlight.register(languageService, getValidTextDocument, ts),
findDocumentSymbols: documentSymbol.register(languageService, getValidTextDocument),
findWorkspaceSymbols: workspaceSymbols.register(rootUri, languageService, getTextDocument),
doComplete: completions.register(languageService, getValidTextDocument, settings, ts),
doCompletionResolve: completionResolve.register(rootUri, languageService, getValidTextDocument, getTextDocument, settings),
doComplete: completions.register(languageService, getValidTextDocument, getConfiguration, ts),
doCompletionResolve: completionResolve.register(rootUri, languageService, getValidTextDocument, getTextDocument, getConfiguration),
doDirectiveCommentComplete: directiveCommentCompletions.register(getValidTextDocument),
doJsDocComplete: jsDocCompletions.register(languageService, getValidTextDocument),
doHover: hover.register(rootUri, languageService, getValidTextDocument, getTextDocument, ts),
doFormatting: formatting.register(languageService, getValidTextDocument, settings),
doFormatting: formatting.register(languageService, getValidTextDocument, getConfiguration),
getSignatureHelp: signatureHelp.register(languageService, getValidTextDocument, ts),
getSelectionRanges: selectionRanges.register(languageService, getValidTextDocument),
doValidation: diagnostics.register(rootUri, languageService, getValidTextDocument, ts),
Expand Down
10 changes: 6 additions & 4 deletions packages/typescript-language-service/src/services/codeAction.ts
Expand Up @@ -4,8 +4,10 @@ import * as shared from '@volar/shared';
import type { TextDocument } from 'vscode-languageserver-textdocument';
import { fileTextChangesToWorkspaceEdit } from './rename';
import * as fixNames from '../utils/fixNames';
import type { Settings } from '../';
import type { GetConfiguration } from '../';
import { URI } from 'vscode-uri';
import { getFormatCodeSettings } from '../configs/getFormatCodeSettings';
import { getUserPreferences } from '../configs/getUserPreferences';

export interface FixAllData {
type: 'fixAll',
Expand Down Expand Up @@ -34,16 +36,16 @@ export function register(
rootUri: URI,
languageService: ts.LanguageService,
getTextDocument: (uri: string) => TextDocument | undefined,
settings: Settings,
getConfiguration: GetConfiguration,
) {
return async (uri: string, range: vscode.Range, context: vscode.CodeActionContext) => {

const document = getTextDocument(uri);
if (!document) return;

const [formatOptions, preferences] = await Promise.all([
settings.getFormatOptions?.(document.uri) ?? {},
settings.getPreferences?.(document.uri) ?? {},
getFormatCodeSettings(getConfiguration, document.uri),
getUserPreferences(getConfiguration, document.uri),
]);

const fileName = shared.getPathOfUri(document.uri);
Expand Down
Expand Up @@ -3,22 +3,24 @@ import * as vscode from 'vscode-languageserver-protocol';
import type { TextDocument } from 'vscode-languageserver-textdocument';
import { fileTextChangesToWorkspaceEdit } from './rename';
import { Data } from './codeAction';
import type { Settings } from '../';
import type { GetConfiguration } from '../';
import { URI } from 'vscode-uri';
import { getFormatCodeSettings } from '../configs/getFormatCodeSettings';
import { getUserPreferences } from '../configs/getUserPreferences';

export function register(
rootUri: URI,
languageService: ts.LanguageService,
getTextDocument: (uri: string) => TextDocument | undefined,
settings: Settings,
getConfiguration: GetConfiguration,
) {
return async (codeAction: vscode.CodeAction) => {

const data: Data = codeAction.data;
const document = getTextDocument(data.uri);
const [formatOptions, preferences] = document ? await Promise.all([
settings.getFormatOptions?.(document.uri) ?? {},
settings.getPreferences?.(document.uri) ?? {},
getFormatCodeSettings(getConfiguration, document.uri),
getUserPreferences(getConfiguration, document.uri),
]) : [{}, {}];

if (data?.type === 'fixAll') {
Expand Down
Expand Up @@ -5,7 +5,8 @@ import type { TextDocument } from 'vscode-languageserver-textdocument';
import * as shared from '@volar/shared';
import * as semver from 'semver';
import { parseKindModifier } from '../../utils/modifiers';
import { Settings } from '../..';
import { GetConfiguration } from '../..';
import { getUserPreferences } from '../../configs/getUserPreferences';

export interface Data {
uri: string,
Expand All @@ -17,7 +18,7 @@ export interface Data {
export function register(
languageService: ts.LanguageService,
getTextDocument: (uri: string) => TextDocument | undefined,
settings: Settings,
getConfiguration: GetConfiguration,
ts: typeof import('typescript/lib/tsserverlibrary'),
) {

Expand All @@ -30,7 +31,7 @@ export function register(
if (!document)
return;

const preferences = await settings.getPreferences?.(document.uri) ?? {};
const preferences = await getUserPreferences(getConfiguration, document.uri);
const fileName = shared.getPathOfUri(document.uri);
const offset = document.offsetAt(position);

Expand Down
Expand Up @@ -6,15 +6,17 @@ import { handleKindModifiers } from './basic';
import type { Data } from './basic';
import * as previewer from '../../utils/previewer';
import * as shared from '@volar/shared';
import type { Settings } from '../..';
import type { GetConfiguration } from '../..';
import { URI } from 'vscode-uri';
import { getFormatCodeSettings } from '../../configs/getFormatCodeSettings';
import { getUserPreferences } from '../../configs/getUserPreferences';

export function register(
rootUri: URI,
languageService: ts.LanguageService,
getTextDocument: (uri: string) => TextDocument | undefined,
getTextDocument2: (uri: string) => TextDocument | undefined,
settings: Settings,
getConfiguration: GetConfiguration,
) {
return async (item: vscode.CompletionItem, newPosition?: vscode.Position): Promise<vscode.CompletionItem> => {

Expand All @@ -32,8 +34,8 @@ export function register(
}

const [formatOptions, preferences] = document ? await Promise.all([
settings.getFormatOptions?.(document.uri) ?? {},
settings.getPreferences?.(document.uri) ?? {},
getFormatCodeSettings(getConfiguration, document.uri),
getUserPreferences(getConfiguration, document.uri),
]) : [{}, {}];

let details: ts.CompletionEntryDetails | undefined;
Expand Down
10 changes: 6 additions & 4 deletions packages/typescript-language-service/src/services/fileRename.ts
Expand Up @@ -3,21 +3,23 @@ import type * as vscode from 'vscode-languageserver-protocol';
import type { TextDocument } from 'vscode-languageserver-textdocument';
import * as shared from '@volar/shared';
import { fileTextChangesToWorkspaceEdit } from './rename';
import type { Settings } from '../';
import type { GetConfiguration } from '../';
import { URI } from 'vscode-uri';
import { getFormatCodeSettings } from '../configs/getFormatCodeSettings';
import { getUserPreferences } from '../configs/getUserPreferences';

export function register(
rootUri: URI,
languageService: ts.LanguageService,
getTextDocument: (uri: string) => TextDocument | undefined,
settings: Settings,
getConfiguration: GetConfiguration,
) {
return async (oldUri: string, newUri: string): Promise<vscode.WorkspaceEdit | undefined> => {

const document = getTextDocument(oldUri);
const [formatOptions, preferences] = document ? await Promise.all([
settings.getFormatOptions?.(document.uri) ?? {},
settings.getPreferences?.(document.uri) ?? {},
getFormatCodeSettings(getConfiguration, document.uri),
getUserPreferences(getConfiguration, document.uri),
]) : [{}, {}];

const fileToRename = shared.getPathOfUri(oldUri);
Expand Down
11 changes: 6 additions & 5 deletions packages/typescript-language-service/src/services/formatting.ts
Expand Up @@ -2,12 +2,13 @@ import type * as ts from 'typescript/lib/tsserverlibrary';
import * as vscode from 'vscode-languageserver-protocol';
import * as shared from '@volar/shared';
import type { TextDocument } from 'vscode-languageserver-textdocument';
import type { Settings } from '../';
import type { GetConfiguration } from '../';
import { getFormatCodeSettings } from '../configs/getFormatCodeSettings';

export function register(
languageService: ts.LanguageService,
getTextDocument: (uri: string) => TextDocument | undefined,
settings: Settings
getConfiguration: GetConfiguration,
) {
return {
onRange: async (uri: string, options: vscode.FormattingOptions, range?: vscode.Range): Promise<vscode.TextEdit[]> => {
Expand All @@ -16,8 +17,8 @@ export function register(
if (!document) return [];

const fileName = shared.getPathOfUri(document.uri);
const tsOptions = await settings.getFormatOptions?.(document.uri, options) ?? options;
if (typeof(tsOptions.indentSize) === "boolean" || typeof(tsOptions.indentSize) === "string") {
const tsOptions = await getFormatCodeSettings(getConfiguration, document.uri, options);
if (typeof (tsOptions.indentSize) === "boolean" || typeof (tsOptions.indentSize) === "string") {
tsOptions.indentSize = undefined;
}

Expand Down Expand Up @@ -49,7 +50,7 @@ export function register(
if (!document) return [];

const fileName = shared.getPathOfUri(document.uri);
const tsOptions = await settings.getFormatOptions?.(document.uri, options) ?? options;
const tsOptions = await getFormatCodeSettings(getConfiguration, document.uri, options);

let scriptEdits: ReturnType<typeof languageService.getFormattingEditsForRange> | undefined;
try {
Expand Down
Expand Up @@ -2,20 +2,21 @@ import * as shared from '@volar/shared';
import type * as ts from 'typescript/lib/tsserverlibrary';
import * as vscode from 'vscode-languageserver-protocol';
import type { TextDocument } from 'vscode-languageserver-textdocument';
import type { Settings } from '..';
import type { GetConfiguration } from '..';
import { getUserPreferences } from '../configs/getUserPreferences';

export function register(
languageService: ts.LanguageService,
getTextDocument: (uri: string) => TextDocument | undefined,
settings: Settings,
getConfiguration: GetConfiguration,
ts: typeof import('typescript/lib/tsserverlibrary'),
) {
return async (uri: string, range: vscode.Range) => {

const document = getTextDocument(uri);
if (!document) return;

const preferences = await settings.getPreferences?.(document.uri) ?? {};
const preferences = await getUserPreferences(getConfiguration, document.uri);
const fileName = shared.getPathOfUri(document.uri);
const start = document.offsetAt(range.start);
const end = document.offsetAt(range.end);
Expand Down

0 comments on commit b3c9155

Please sign in to comment.