Basic support for TS preferences

Fixes #45948
This commit is contained in:
Matt Bierner 2018-05-02 16:37:26 -07:00
parent eedc1f58a5
commit e1a4a6812b
8 changed files with 114 additions and 47 deletions

View file

@ -12,9 +12,9 @@
"engines": {
"vscode": "*"
},
"categories": [
"Programming Languages"
],
"categories": [
"Programming Languages"
],
"dependencies": {
"semver": "4.3.6",
"vscode-extension-telemetry": "0.0.17",
@ -426,6 +426,26 @@
"default": true,
"description": "%typescript.suggestionActions.enabled%",
"scope": "window"
},
"javascript.preferences.quoteStyle": {
"type": "string",
"default": "double",
"enum": [
"single",
"double"
],
"description": "%typescript.preferences.quoteStyle%",
"scope": "window"
},
"typescript.preferences.quoteStyle": {
"type": "string",
"default": "double",
"enum": [
"single",
"double"
],
"description": "%typescript.preferences.quoteStyle%",
"scope": "window"
}
}
},

View file

@ -50,9 +50,10 @@
"typescript.quickSuggestionsForPaths": "Enable/disable quick suggestions when typing out an import path.",
"typescript.locale": "Sets the locale used to report TypeScript errors. Requires TypeScript >= 2.6.0. Default of 'null' uses VS Code's locale for TypeScript errors.",
"javascript.implicitProjectConfig.experimentalDecorators": "Enable/disable 'experimentalDecorators' for JavaScript files that are not part of a project. Existing jsconfig.json or tsconfig.json files override this setting. Requires TypeScript >=2.3.1.",
"typescript.autoImportSuggestions.enabled": "Enable/disable auto import suggestions. Requires TypeScript >=2.6.1",
"typescript.autoImportSuggestions.enabled": "Enable/disable auto import suggestions. Requires TypeScript >= 2.6.1",
"typescript.experimental.syntaxFolding": "Enables/disables syntax aware folding markers.",
"taskDefinition.tsconfig.description": "The tsconfig file that defines the TS build.",
"javascript.suggestionActions.enabled": "Enable/disable suggestion diagnostics for JavaScript files in the editor. Requires TypeScript >= 2.8",
"typescript.suggestionActions.enabled": "Enable/disable suggestion diagnostics for TypeScript files in the editor. Requires TypeScript >= 2.8."
"typescript.suggestionActions.enabled": "Enable/disable suggestion diagnostics for TypeScript files in the editor. Requires TypeScript >= 2.8.",
"typescript.preferences.quoteStyle": "Prefered quote style ('single' or 'double') to use for quick fixes. Requires TS >= 2.9.0"
}

View file

@ -16,6 +16,7 @@ import * as typeConverters from '../utils/typeConverters';
import * as nls from 'vscode-nls';
import { applyCodeAction } from '../utils/codeAction';
import { CommandManager, Command } from '../utils/commandManager';
import FileConfigurationManager from './fileConfigurationManager';
const localize = nls.loadMessageBundle();
@ -248,6 +249,7 @@ export default class TypeScriptCompletionItemProvider implements vscode.Completi
constructor(
private readonly client: ITypeScriptServiceClient,
private readonly typingsStatus: TypingsStatus,
private readonly fileConfigurationManager: FileConfigurationManager,
commandManager: CommandManager
) {
commandManager.register(new ApplyCompletionCodeActionCommand(this.client));
@ -282,6 +284,8 @@ export default class TypeScriptCompletionItemProvider implements vscode.Completi
return [];
}
await this.fileConfigurationManager.ensureConfigurationForDocument(document, token);
const args: Proto.CompletionsRequestArgs & { triggerCharacter?: string } = {
...typeConverters.Position.toFileLocationRequestArgs(file, position),
includeExternalModuleExports: completionConfiguration.autoImportSuggestions,

View file

@ -9,22 +9,36 @@ import * as Proto from '../protocol';
import { ITypeScriptServiceClient } from '../typescriptService';
import * as languageIds from '../utils/languageModeIds';
namespace FormattingConfiguration {
export function equals(a: Proto.FormatCodeSettings, b: Proto.FormatCodeSettings): boolean {
let keys = Object.keys(a);
for (let i = 0; i < keys.length; i++) {
let key = keys[i];
if ((a as any)[key] !== (b as any)[key]) {
return false;
}
// TODO: for TS 2.8
type UserPreferences = any;
function objsAreEqual<T>(a: T, b: T): boolean {
let keys = Object.keys(a);
for (let i = 0; i < keys.length; i++) {
let key = keys[i];
if ((a as any)[key] !== (b as any)[key]) {
return false;
}
return true;
}
return true;
}
export default class FormattingConfigurationManager {
interface FileConfiguration {
formatOptions: Proto.FormatCodeSettings;
preferences: UserPreferences;
}
function areFileConfigurationsEqual(a: FileConfiguration, b: FileConfiguration): boolean {
return (
objsAreEqual(a.formatOptions, b.formatOptions)
&& objsAreEqual(a.preferences, b.preferences)
);
}
export default class FileConfigurationManager {
private onDidCloseTextDocumentSub: Disposable | undefined;
private formatOptions: { [key: string]: Proto.FormatCodeSettings | undefined; } = Object.create(null);
private formatOptions: { [key: string]: FileConfiguration | undefined } = Object.create(null);
public constructor(
private readonly client: ITypeScriptServiceClient
@ -46,7 +60,7 @@ export default class FormattingConfigurationManager {
}
}
public async ensureFormatOptionsForDocument(
public async ensureConfigurationForDocument(
document: TextDocument,
token: CancellationToken | undefined
): Promise<void> {
@ -56,11 +70,11 @@ export default class FormattingConfigurationManager {
tabSize: editor.options.tabSize,
insertSpaces: editor.options.insertSpaces
} as FormattingOptions;
return this.ensureFormatOptions(document, formattingOptions, token);
return this.ensureConfigurationOptions(document, formattingOptions, token);
}
}
public async ensureFormatOptions(
public async ensureConfigurationOptions(
document: TextDocument,
options: FormattingOptions,
token: CancellationToken | undefined
@ -72,33 +86,43 @@ export default class FormattingConfigurationManager {
const key = document.uri.toString();
const cachedOptions = this.formatOptions[key];
const formatOptions = this.getFormatOptions(document, options);
const currentOptions = this.getFileOptions(document, options);
if (cachedOptions && FormattingConfiguration.equals(cachedOptions, formatOptions)) {
if (cachedOptions && areFileConfigurationsEqual(cachedOptions, currentOptions)) {
return;
}
const args: Proto.ConfigureRequestArguments = {
file: file,
formatOptions: formatOptions
};
const args = {
file,
...currentOptions
} as Proto.ConfigureRequestArguments;
await this.client.execute('configure', args, token);
this.formatOptions[key] = formatOptions;
this.formatOptions[key] = currentOptions;
}
public reset() {
this.formatOptions = Object.create(null);
}
private getFileOptions(
document: TextDocument,
options: FormattingOptions
): FileConfiguration {
return {
formatOptions: this.getFormatOptions(document, options),
preferences: this.getPreferences(document)
};
}
private getFormatOptions(
document: TextDocument,
options: FormattingOptions
): Proto.FormatCodeSettings {
const config = workspace.getConfiguration(
document.languageId === languageIds.typescript || document.languageId === languageIds.typescriptreact
? 'typescript.format'
: 'javascript.format',
isTypeScriptDocument(document) ? 'typescript.format' : 'javascript.format',
document.uri);
return {
tabSize: options.tabSize,
indentSize: options.tabSize,
@ -122,4 +146,22 @@ export default class FormattingConfigurationManager {
placeOpenBraceOnNewLineForControlBlocks: config.get<boolean>('placeOpenBraceOnNewLineForControlBlocks'),
};
}
private getPreferences(document: TextDocument): UserPreferences {
if (!this.client.apiVersion.has290Features()) {
return {};
}
const config = workspace.getConfiguration(
isTypeScriptDocument(document) ? 'typescript.preferences' : 'javascript.preferences',
document.uri);
return {
quotePreference: config.get<'single' | 'double' | undefined>('quoteStyle')
};
}
}
function isTypeScriptDocument(document: TextDocument) {
return document.languageId === languageIds.typescript || document.languageId === languageIds.typescriptreact;
}

View file

@ -8,14 +8,14 @@ import { DocumentRangeFormattingEditProvider, OnTypeFormattingEditProvider, Form
import * as Proto from '../protocol';
import { ITypeScriptServiceClient } from '../typescriptService';
import * as typeConverters from '../utils/typeConverters';
import FormattingConfigurationManager from './formattingConfigurationManager';
import FileConfigurationManager from './fileConfigurationManager';
export class TypeScriptFormattingProvider implements DocumentRangeFormattingEditProvider, OnTypeFormattingEditProvider {
private enabled: boolean = true;
public constructor(
private readonly client: ITypeScriptServiceClient,
private readonly formattingOptionsManager: FormattingConfigurationManager
private readonly formattingOptionsManager: FileConfigurationManager
) { }
public updateConfiguration(config: WorkspaceConfiguration): void {
@ -32,7 +32,7 @@ export class TypeScriptFormattingProvider implements DocumentRangeFormattingEdit
args: Proto.FormatRequestArgs,
token: CancellationToken
): Promise<TextEdit[]> {
await this.formattingOptionsManager.ensureFormatOptions(document, options, token);
await this.formattingOptionsManager.ensureConfigurationOptions(document, options, token);
try {
const response = await this.client.execute('format', args, token);
if (response.body) {
@ -76,7 +76,7 @@ export class TypeScriptFormattingProvider implements DocumentRangeFormattingEdit
return [];
}
await this.formattingOptionsManager.ensureFormatOptions(document, options, token);
await this.formattingOptionsManager.ensureConfigurationOptions(document, options, token);
const args: Proto.FormatOnKeyRequestArgs = {
file: filepath,

View file

@ -8,7 +8,7 @@ import * as vscode from 'vscode';
import * as Proto from '../protocol';
import { ITypeScriptServiceClient } from '../typescriptService';
import * as typeConverters from '../utils/typeConverters';
import FormattingConfigurationManager from './formattingConfigurationManager';
import FileConfigurationManager from './fileConfigurationManager';
import { getEditForCodeAction, applyCodeActionCommands } from '../utils/codeAction';
import { Command, CommandManager } from '../utils/commandManager';
import { DiagnosticsManager } from './diagnostics';
@ -131,7 +131,7 @@ export default class TypeScriptQuickFixProvider implements vscode.CodeActionProv
constructor(
private readonly client: ITypeScriptServiceClient,
private readonly formattingConfigurationManager: FormattingConfigurationManager,
private readonly formattingConfigurationManager: FileConfigurationManager,
commandManager: CommandManager,
private readonly diagnosticsManager: DiagnosticsManager,
private readonly bufferSyncSupport: BufferSyncSupport
@ -167,7 +167,7 @@ export default class TypeScriptQuickFixProvider implements vscode.CodeActionProv
return [];
}
await this.formattingConfigurationManager.ensureFormatOptionsForDocument(document, token);
await this.formattingConfigurationManager.ensureConfigurationForDocument(document, token);
const results: vscode.CodeAction[] = [];
for (const diagnostic of fixableDiagnostics) {

View file

@ -10,7 +10,7 @@ import * as vscode from 'vscode';
import * as Proto from '../protocol';
import { ITypeScriptServiceClient } from '../typescriptService';
import * as typeConverters from '../utils/typeConverters';
import FormattingOptionsManager from './formattingConfigurationManager';
import FormattingOptionsManager from './fileConfigurationManager';
import { CommandManager, Command } from '../utils/commandManager';
class ApplyRefactoringCommand implements Command {
@ -29,7 +29,7 @@ class ApplyRefactoringCommand implements Command {
action: string,
range: vscode.Range
): Promise<boolean> {
await this.formattingOptionsManager.ensureFormatOptionsForDocument(document, undefined);
await this.formattingOptionsManager.ensureConfigurationForDocument(document, undefined);
const args: Proto.GetEditsForRefactorRequestArgs = {
...typeConverters.Range.toFileRangeRequestArgs(file, range),

View file

@ -11,7 +11,7 @@ import TypeScriptServiceClient from './typescriptServiceClient';
import BufferSyncSupport from './features/bufferSyncSupport';
import TypingsStatus from './utils/typingsStatus';
import FormattingConfigurationManager from './features/formattingConfigurationManager';
import FileConfigurationManager from './features/fileConfigurationManager';
import * as languageConfigurations from './utils/languageConfigurations';
import { CommandManager } from './utils/commandManager';
import { DiagnosticsManager, DiagnosticKind } from './features/diagnostics';
@ -28,7 +28,7 @@ const foldingSetting = 'typescript.experimental.syntaxFolding';
export default class LanguageProvider {
private readonly diagnosticsManager: DiagnosticsManager;
private readonly bufferSyncSupport: BufferSyncSupport;
private readonly formattingOptionsManager: FormattingConfigurationManager;
private readonly fileConfigurationManager: FileConfigurationManager;
private readonly toUpdateOnConfigurationChanged: ({ updateConfiguration: () => void })[] = [];
@ -46,7 +46,7 @@ export default class LanguageProvider {
private readonly commandManager: CommandManager,
typingsStatus: TypingsStatus
) {
this.formattingOptionsManager = new FormattingConfigurationManager(client);
this.fileConfigurationManager = new FileConfigurationManager(client);
this.bufferSyncSupport = new BufferSyncSupport(client, description.modeIds, {
delete: (resource) => {
this.diagnosticsManager.delete(resource);
@ -70,7 +70,7 @@ export default class LanguageProvider {
this.diagnosticsManager.dispose();
this.bufferSyncSupport.dispose();
this.formattingOptionsManager.dispose();
this.fileConfigurationManager.dispose();
}
@memoize
@ -94,13 +94,13 @@ export default class LanguageProvider {
const TypeScriptCompletionItemProvider = (await import('./features/completionItemProvider')).default;
this.disposables.push(languages.registerCompletionItemProvider(selector,
new TypeScriptCompletionItemProvider(client, typingsStatus, commandManager),
new TypeScriptCompletionItemProvider(client, typingsStatus, this.fileConfigurationManager, commandManager),
...TypeScriptCompletionItemProvider.triggerCharacters));
this.disposables.push(languages.registerCompletionItemProvider(selector, new (await import('./features/directiveCommentCompletionProvider')).default(client), '@'));
const { TypeScriptFormattingProvider, FormattingProviderManager } = await import('./features/formattingProvider');
const formattingProvider = new TypeScriptFormattingProvider(client, this.formattingOptionsManager);
const formattingProvider = new TypeScriptFormattingProvider(client, this.fileConfigurationManager);
formattingProvider.updateConfiguration(config);
this.disposables.push(languages.registerOnTypeFormattingEditProvider(selector, formattingProvider, ';', '}', '\n'));
@ -119,9 +119,9 @@ export default class LanguageProvider {
this.disposables.push(languages.registerDocumentSymbolProvider(selector, new (await import('./features/documentSymbolProvider')).default(client)));
this.disposables.push(languages.registerSignatureHelpProvider(selector, new (await import('./features/signatureHelpProvider')).default(client), '(', ','));
this.disposables.push(languages.registerRenameProvider(selector, new (await import('./features/renameProvider')).default(client)));
this.disposables.push(languages.registerCodeActionsProvider(selector, new (await import('./features/quickFixProvider')).default(client, this.formattingOptionsManager, commandManager, this.diagnosticsManager, this.bufferSyncSupport)));
this.disposables.push(languages.registerCodeActionsProvider(selector, new (await import('./features/quickFixProvider')).default(client, this.fileConfigurationManager, commandManager, this.diagnosticsManager, this.bufferSyncSupport)));
const refactorProvider = new (await import('./features/refactorProvider')).default(client, this.formattingOptionsManager, commandManager);
const refactorProvider = new (await import('./features/refactorProvider')).default(client, this.fileConfigurationManager, commandManager);
this.disposables.push(languages.registerCodeActionsProvider(selector, refactorProvider, refactorProvider.metadata));
await this.initFoldingProvider();
@ -226,7 +226,7 @@ export default class LanguageProvider {
this.diagnosticsManager.reInitialize();
this.bufferSyncSupport.reOpenDocuments();
this.bufferSyncSupport.requestAllDiagnostics();
this.formattingOptionsManager.reset();
this.fileConfigurationManager.reset();
this.registerVersionDependentProviders();
}