diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts index ca900c6b6967725ca5cd4d23af3f6d37ec40ba85..465d12b5cd4388f9939db068d2eb58349381f7af 100644 --- a/src/vs/workbench/browser/parts/editor/editorStatus.ts +++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts @@ -1137,7 +1137,7 @@ export class ChangeModeAction extends Action { // User decided to configure settings for current language if (pick === configureModeSettings) { - this.preferencesService.configureSettingsForLanguage(withUndefinedAsNull(modeId)); + this.preferencesService.openGlobalSettings(true, { editSetting: `[${withUndefinedAsNull(modeId)}]` }); return; } diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesActions.ts b/src/vs/workbench/contrib/preferences/browser/preferencesActions.ts index b7742712238d239b2e9c44584ac1d07e53338367..386c1ce4998c67e0f07b93f6d10d7fcf9221fdb5 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesActions.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesActions.ts @@ -270,7 +270,7 @@ export class ConfigureLanguageBasedSettingsAction extends Action { if (pick) { const modeId = this.modeService.getModeIdForLanguageName(pick.label.toLowerCase()); if (typeof modeId === 'string') { - return this.preferencesService.configureSettingsForLanguage(modeId); + return this.preferencesService.openGlobalSettings(true, { editSetting: `[${modeId}]` }); } } return undefined; diff --git a/src/vs/workbench/services/configuration/common/jsonEditingService.ts b/src/vs/workbench/services/configuration/common/jsonEditingService.ts index 10f76510c7e41d4640e352aeea455da006606826..289c66fb2f88c73262e5fc5ec0dd757944ff8c78 100644 --- a/src/vs/workbench/services/configuration/common/jsonEditingService.ts +++ b/src/vs/workbench/services/configuration/common/jsonEditingService.ts @@ -41,18 +41,18 @@ export class JSONEditingService implements IJSONEditingService { private async doWriteConfiguration(resource: URI, values: IJSONValue[], save: boolean): Promise { const reference = await this.resolveAndValidate(resource, save); - await this.writeToBuffer(reference.object.textEditorModel, values); + await this.writeToBuffer(reference.object.textEditorModel, values, save); reference.dispose(); } - private async writeToBuffer(model: ITextModel, values: IJSONValue[]): Promise { + private async writeToBuffer(model: ITextModel, values: IJSONValue[], save: boolean): Promise { let hasEdits: boolean = false; for (const value of values) { const edit = this.getEdits(model, value)[0]; hasEdits = this.applyEditsToBuffer(edit, model); } - if (hasEdits) { + if (hasEdits && save) { return this.textFileService.save(model.uri); } } diff --git a/src/vs/workbench/services/preferences/browser/preferencesService.ts b/src/vs/workbench/services/preferences/browser/preferencesService.ts index d709b09b4126b93d0249cb18c85ac0774ad1a7f8..bc872a3633e89b10078fbacbe613a892afae3875 100644 --- a/src/vs/workbench/services/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/services/preferences/browser/preferencesService.ts @@ -8,11 +8,9 @@ import { parse } from 'vs/base/common/json'; import { Disposable } from 'vs/base/common/lifecycle'; import * as network from 'vs/base/common/network'; import { assign } from 'vs/base/common/objects'; -import * as strings from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; import { getCodeEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { EditOperation } from 'vs/editor/common/core/editOperation'; -import { IPosition, Position } from 'vs/editor/common/core/position'; +import { IPosition } from 'vs/editor/common/core/position'; import { ITextModel } from 'vs/editor/common/model'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService } from 'vs/editor/common/services/modeService'; @@ -39,6 +37,10 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { withNullAsUndefined } from 'vs/base/common/types'; +import { getDefaultValue, IConfigurationRegistry, Extensions, OVERRIDE_PROPERTY_PATTERN } from 'vs/platform/configuration/common/configurationRegistry'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { CoreEditingCommands } from 'vs/editor/browser/controller/coreCommands'; const emptyEditableSettingsContent = '{\n}'; @@ -73,7 +75,8 @@ export class PreferencesService extends Disposable implements IPreferencesServic @IJSONEditingService private readonly jsonEditingService: IJSONEditingService, @IModeService private readonly modeService: IModeService, @ILabelService private readonly labelService: ILabelService, - @IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService + @IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService, + @ICommandService private readonly commandService: ICommandService, ) { super(); // The default keybindings.json updates based on keyboard layouts, so here we make sure @@ -311,25 +314,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.editorService.openEditor({ resource: this.defaultKeybindingsResource, label: nls.localize('defaultKeybindings', "Default Keybindings") }); } - configureSettingsForLanguage(language: string): void { - this.openGlobalSettings(true) - .then(editor => this.createPreferencesEditorModel(this.userSettingsResource) - .then((settingsModel: IPreferencesEditorModel | null) => { - const codeEditor = editor ? getCodeEditor(editor.getControl()) : null; - if (codeEditor && settingsModel) { - this.addLanguageOverrideEntry(language, settingsModel, codeEditor) - .then(position => { - if (codeEditor && position) { - codeEditor.setPosition(position); - codeEditor.revealLine(position.lineNumber); - codeEditor.focus(); - } - }); - } - })); - } - - private openOrSwitchSettings(configurationTarget: ConfigurationTarget, resource: URI, options?: ISettingsEditorOptions, group: IEditorGroup = this.editorGroupService.activeGroup): Promise { + private async openOrSwitchSettings(configurationTarget: ConfigurationTarget, resource: URI, options?: ISettingsEditorOptions, group: IEditorGroup = this.editorGroupService.activeGroup): Promise { const editorInput = this.getActiveSettingsEditorInput(group); if (editorInput) { const editorInputResource = editorInput.master.resource; @@ -337,7 +322,11 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.doSwitchSettings(configurationTarget, resource, editorInput, group, options); } } - return this.doOpenSettings(configurationTarget, resource, options, group); + const editor = await this.doOpenSettings(configurationTarget, resource, options, group); + if (editor && options?.editSetting) { + await this.editSetting(options?.editSetting, editor, resource); + } + return editor; } private openOrSwitchSettings2(configurationTarget: ConfigurationTarget, folderUri?: URI, options?: ISettingsEditorOptions, group: IEditorGroup = this.editorGroupService.activeGroup): Promise { @@ -593,39 +582,62 @@ export class PreferencesService extends Disposable implements IPreferencesServic ]; } - private addLanguageOverrideEntry(language: string, settingsModel: IPreferencesEditorModel, codeEditor: ICodeEditor): Promise { - const languageKey = `[${language}]`; - let setting = settingsModel.getPreference(languageKey); + private async editSetting(settingKey: string, editor: IEditorPane, settingsResource: URI): Promise { + const codeEditor = editor ? getCodeEditor(editor.getControl()) : null; + if (!codeEditor) { + return; + } + const settingsModel = await this.createPreferencesEditorModel(settingsResource); + if (!settingsModel) { + return; + } + const position = await this.getPositionToEdit(settingKey, settingsModel, codeEditor); + if (position) { + codeEditor.setPosition(position); + codeEditor.revealPositionNearTop(position); + codeEditor.focus(); + await this.commandService.executeCommand('editor.action.triggerSuggest'); + } + } + + private async getPositionToEdit(settingKey: string, settingsModel: IPreferencesEditorModel, codeEditor: ICodeEditor): Promise { const model = codeEditor.getModel(); - if (model) { - const configuration = this.configurationService.getValue<{ editor: { tabSize: number; insertSpaces: boolean } }>(); - const eol = model.getEOL(); - if (setting) { - if (setting.overrides && setting.overrides.length) { - const lastSetting = setting.overrides[setting.overrides.length - 1]; - return Promise.resolve({ lineNumber: lastSetting.valueRange.endLineNumber, column: model.getLineMaxColumn(lastSetting.valueRange.endLineNumber) }); + if (!model) { + return null; + } + const schema = Registry.as(Extensions.Configuration).getConfigurationProperties()[settingKey]; + if (!schema && !OVERRIDE_PROPERTY_PATTERN.test(settingKey)) { + return null; + } + + let position = null; + const type = schema ? schema.type : 'object' /* Override Identifier */; + let setting = settingsModel.getPreference(settingKey); + if (!setting) { + const defaultValue = type === 'array' ? this.configurationService.inspect(settingKey).defaultValue : getDefaultValue(type); + if (defaultValue !== undefined) { + await this.jsonEditingService.write(settingsModel.uri!, [{ key: settingKey, value: defaultValue }], false); + setting = settingsModel.getPreference(settingKey); + } + } + + if (setting) { + position = { lineNumber: setting.valueRange.startLineNumber, column: setting.valueRange.startColumn + 1 }; + if (type === 'object' || type === 'array') { + codeEditor.setPosition(position); + await CoreEditingCommands.LineBreakInsert.runEditorCommand(null, codeEditor, null); + position = { lineNumber: position.lineNumber + 1, column: model.getLineMaxColumn(position.lineNumber + 1) }; + const firstNonWhiteSpaceColumn = model.getLineFirstNonWhitespaceColumn(position.lineNumber); + if (firstNonWhiteSpaceColumn) { + // Line has some text. Insert another new line. + codeEditor.setPosition({ lineNumber: position.lineNumber, column: firstNonWhiteSpaceColumn }); + await CoreEditingCommands.LineBreakInsert.runEditorCommand(null, codeEditor, null); + position = { lineNumber: position.lineNumber, column: model.getLineMaxColumn(position.lineNumber) }; } - return Promise.resolve({ lineNumber: setting.valueRange.startLineNumber, column: setting.valueRange.startColumn + 1 }); } - return this.configurationService.updateValue(languageKey, {}, ConfigurationTarget.USER) - .then(() => { - setting = settingsModel.getPreference(languageKey); - if (setting) { - let content = eol + this.spaces(2, configuration.editor) + eol + this.spaces(1, configuration.editor); - let editOperation = EditOperation.insert(new Position(setting.valueRange.endLineNumber, setting.valueRange.endColumn - 1), content); - model.pushEditOperations([], [editOperation], () => []); - let lineNumber = setting.valueRange.endLineNumber + 1; - settingsModel.dispose(); - return { lineNumber, column: model.getLineMaxColumn(lineNumber) }; - } - return null; - }); } - return Promise.resolve(null); - } - private spaces(count: number, { tabSize, insertSpaces }: { tabSize: number; insertSpaces: boolean }): string { - return insertSpaces ? strings.repeat(' ', tabSize * count) : strings.repeat('\t', count); + return position; } public dispose(): void { diff --git a/src/vs/workbench/services/preferences/common/preferences.ts b/src/vs/workbench/services/preferences/common/preferences.ts index 8e5e93efe4fb1367c2563322b65aa1a01e38c018..093f10ecaa5652ed2b30f805769fff4726e49634 100644 --- a/src/vs/workbench/services/preferences/common/preferences.ts +++ b/src/vs/workbench/services/preferences/common/preferences.ts @@ -155,6 +155,7 @@ export interface ISettingsEditorOptions extends IEditorOptions { target?: ConfigurationTarget; folderUri?: URI; query?: string; + editSetting?: string; } /** @@ -165,6 +166,7 @@ export class SettingsEditorOptions extends EditorOptions implements ISettingsEdi target?: ConfigurationTarget; folderUri?: URI; query?: string; + editSetting?: string; static create(settings: ISettingsEditorOptions): SettingsEditorOptions { const options = new SettingsEditorOptions(); @@ -173,6 +175,7 @@ export class SettingsEditorOptions extends EditorOptions implements ISettingsEdi options.target = settings.target; options.folderUri = settings.folderUri; options.query = settings.query; + options.editSetting = settings.editSetting; return options; } @@ -203,8 +206,6 @@ export interface IPreferencesService { switchSettings(target: ConfigurationTarget, resource: URI, jsonEditor?: boolean): Promise; openGlobalKeybindingSettings(textual: boolean): Promise; openDefaultKeybindingsFile(): Promise; - - configureSettingsForLanguage(language: string | null): void; } export function getSettingsTargetName(target: ConfigurationTarget, resource: URI, workspaceContextService: IWorkspaceContextService): string {