diff --git a/extensions/json-language-features/client/src/jsonClient.ts b/extensions/json-language-features/client/src/jsonClient.ts index f892664d917..90aafc89b84 100644 --- a/extensions/json-language-features/client/src/jsonClient.ts +++ b/extensions/json-language-features/client/src/jsonClient.ts @@ -8,7 +8,8 @@ export type JSONLanguageStatus = { schemas: string[] }; import { workspace, window, languages, commands, LogOutputChannel, ExtensionContext, extensions, Uri, ColorInformation, Diagnostic, StatusBarAlignment, TextEditor, TextDocument, FormattingOptions, CancellationToken, FoldingRange, - ProviderResult, TextEdit, Range, Position, Disposable, CompletionItem, CompletionList, CompletionContext, Hover, MarkdownString, FoldingContext, DocumentSymbol, SymbolInformation, l10n + ProviderResult, TextEdit, Range, Position, Disposable, CompletionItem, CompletionList, CompletionContext, Hover, MarkdownString, FoldingContext, DocumentSymbol, SymbolInformation, l10n, + RelativePattern } from 'vscode'; import { LanguageClientOptions, RequestType, NotificationType, FormattingOptions as LSPFormattingOptions, DocumentDiagnosticReportKind, @@ -360,18 +361,29 @@ async function startClientWithParticipants(context: ExtensionContext, languagePa const schemaDocuments: { [uri: string]: boolean } = {}; // handle content request - client.onRequest(VSCodeContentRequest.type, (uriPath: string) => { + client.onRequest(VSCodeContentRequest.type, async (uriPath: string) => { const uri = Uri.parse(uriPath); + const uriString = uri.toString(); if (uri.scheme === 'untitled') { - return Promise.reject(new ResponseError(3, l10n.t('Unable to load {0}', uri.toString()))); + throw new ResponseError(3, l10n.t('Unable to load {0}', uriString)); } - if (uri.scheme !== 'http' && uri.scheme !== 'https') { - return workspace.openTextDocument(uri).then(doc => { - schemaDocuments[uri.toString()] = true; - return doc.getText(); - }, error => { - return Promise.reject(new ResponseError(2, error.toString())); - }); + if (uri.scheme === 'vscode') { + try { + runtime.logOutputChannel.info('read schema from vscode: ' + uriString); + ensureFilesystemWatcherInstalled(uri); + const content = await workspace.fs.readFile(uri); + return new TextDecoder().decode(content); + } catch (e) { + throw new ResponseError(5, e.toString(), e); + } + } else if (uri.scheme !== 'http' && uri.scheme !== 'https') { + try { + const document = await workspace.openTextDocument(uri); + schemaDocuments[uriString] = true; + return document.getText(); + } catch (e) { + throw new ResponseError(2, e.toString(), e); + } } else if (schemaDownloadEnabled) { if (runtime.telemetry && uri.authority === 'schema.management.azure.com') { /* __GDPR__ @@ -381,13 +393,15 @@ async function startClientWithParticipants(context: ExtensionContext, languagePa "schemaURL" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The azure schema URL that was requested." } } */ - runtime.telemetry.sendTelemetryEvent('json.schema', { schemaURL: uriPath }); + runtime.telemetry.sendTelemetryEvent('json.schema', { schemaURL: uriString }); + } + try { + return await runtime.schemaRequests.getContent(uriString); + } catch (e) { + throw new ResponseError(4, e.toString()); } - return runtime.schemaRequests.getContent(uriPath).catch(e => { - return Promise.reject(new ResponseError(4, e.toString())); - }); } else { - return Promise.reject(new ResponseError(1, l10n.t('Downloading schemas is disabled through setting \'{0}\'', SettingIds.enableSchemaDownload))); + throw new ResponseError(1, l10n.t('Downloading schemas is disabled through setting \'{0}\'', SettingIds.enableSchemaDownload)); } }); @@ -415,15 +429,50 @@ async function startClientWithParticipants(context: ExtensionContext, languagePa schemaResolutionErrorStatusBarItem.hide(); } }; - - toDispose.push(workspace.onDidChangeTextDocument(e => handleContentChange(e.document.uri.toString()))); - toDispose.push(workspace.onDidCloseTextDocument(d => { - const uriString = d.uri.toString(); + const handleContentClosed = (uriString: string) => { if (handleContentChange(uriString)) { delete schemaDocuments[uriString]; } fileSchemaErrors.delete(uriString); + }; + + const watchers: Map = new Map(); + toDispose.push(new Disposable(() => { + for (const d of watchers.values()) { + d.dispose(); + } })); + + + const ensureFilesystemWatcherInstalled = (uri: Uri) => { + + const uriString = uri.toString(); + if (!watchers.has(uriString)) { + try { + const watcher = workspace.createFileSystemWatcher(new RelativePattern(uri, '*')); + const handleChange = (uri: Uri) => { + runtime.logOutputChannel.info('schema change detected ' + uri.toString()); + client.sendNotification(SchemaContentChangeNotification.type, uriString); + }; + const createListener = watcher.onDidCreate(handleChange); + const changeListener = watcher.onDidChange(handleChange); + const deleteListener = watcher.onDidDelete(() => { + const watcher = watchers.get(uriString); + if (watcher) { + watcher.dispose(); + watchers.delete(uriString); + } + }); + watchers.set(uriString, Disposable.from(watcher, createListener, changeListener, deleteListener)); + } catch { + runtime.logOutputChannel.info('Problem installing a file system watcher for ' + uriString); + } + } + }; + + toDispose.push(workspace.onDidChangeTextDocument(e => handleContentChange(e.document.uri.toString()))); + toDispose.push(workspace.onDidCloseTextDocument(d => handleContentClosed(d.uri.toString()))); + toDispose.push(window.onDidChangeActiveTextEditor(handleActiveEditorChange)); const handleRetryResolveSchemaCommand = () => { diff --git a/src/vs/platform/jsonschemas/common/jsonContributionRegistry.ts b/src/vs/platform/jsonschemas/common/jsonContributionRegistry.ts index 08322db2bb6..039aaa6cc9b 100644 --- a/src/vs/platform/jsonschemas/common/jsonContributionRegistry.ts +++ b/src/vs/platform/jsonschemas/common/jsonContributionRegistry.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Emitter, Event } from 'vs/base/common/event'; -import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { getCompressedContent, IJSONSchema } from 'vs/base/common/jsonSchema'; import * as platform from 'vs/platform/registry/common/platform'; export const Extensions = { @@ -35,6 +35,18 @@ export interface IJSONContributionRegistry { * Get all schemas */ getSchemaContributions(): ISchemaContributions; + + /** + * Gets the (compressed) content of the schema with the given schema ID (if any) + * @param uri The id of the schema + */ + getSchemaContent(uri: string): string | undefined; + + /** + * Returns true if there's a schema that matches the given schema ID + * @param uri The id of the schema + */ + hasSchemaContent(uri: string): boolean; } @@ -74,6 +86,15 @@ class JSONContributionRegistry implements IJSONContributionRegistry { }; } + public getSchemaContent(uri: string): string | undefined { + const schema = this.schemasById[uri]; + return schema ? getCompressedContent(schema) : undefined; + } + + public hasSchemaContent(uri: string): boolean { + return !!this.schemasById[uri]; + } + } const jsonContributionRegistry = new JSONContributionRegistry(); diff --git a/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts b/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts index 958b8443d70..587d76eb1bc 100644 --- a/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts +++ b/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts @@ -3,17 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; import { isEqual } from 'vs/base/common/resources'; -import { URI } from 'vs/base/common/uri'; -import { ITextModel } from 'vs/editor/common/model'; -import { IModelService } from 'vs/editor/common/services/model'; -import { ILanguageService } from 'vs/editor/common/languages/language'; -import { ITextModelService } from 'vs/editor/common/services/resolverService'; import * as nls from 'vs/nls'; import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ConfigurationScope, Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; -import * as JSONContributionRegistry from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration'; @@ -24,38 +18,36 @@ import { RegisteredEditorPriority, IEditorResolverService } from 'vs/workbench/s import { ITextEditorService } from 'vs/workbench/services/textfile/common/textEditorService'; import { DEFAULT_SETTINGS_EDITOR_SETTING, FOLDER_SETTINGS_PATH, IPreferencesService, USE_SPLIT_JSON_SETTING } from 'vs/workbench/services/preferences/common/preferences'; import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; -import { getCompressedContent, IJSONSchema } from 'vs/base/common/jsonSchema'; -import { ILogService, LogLevel } from 'vs/platform/log/common/log'; +import { IFileService } from 'vs/platform/files/common/files'; +import { SettingsFileSystemProvider } from 'vs/workbench/contrib/preferences/common/settingsFilesystemProvider'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -const schemaRegistry = Registry.as(JSONContributionRegistry.Extensions.JSONContribution); - -export class PreferencesContribution implements IWorkbenchContribution { +export class PreferencesContribution extends Disposable implements IWorkbenchContribution { static readonly ID = 'workbench.contrib.preferences'; private editorOpeningListener: IDisposable | undefined; - private settingsListener: IDisposable; constructor( - @IModelService private readonly modelService: IModelService, - @ITextModelService private readonly textModelResolverService: ITextModelService, + @IFileService fileService: IFileService, + @IInstantiationService private readonly instantiationService: IInstantiationService, @IPreferencesService private readonly preferencesService: IPreferencesService, - @ILanguageService private readonly languageService: ILanguageService, @IUserDataProfileService private readonly userDataProfileService: IUserDataProfileService, @IWorkspaceContextService private readonly workspaceService: IWorkspaceContextService, @IConfigurationService private readonly configurationService: IConfigurationService, @IEditorResolverService private readonly editorResolverService: IEditorResolverService, @ITextEditorService private readonly textEditorService: ITextEditorService, - @ILogService private readonly logService: ILogService, ) { - this.settingsListener = this.configurationService.onDidChangeConfiguration(e => { + super(); + this._register(this.configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration(USE_SPLIT_JSON_SETTING) || e.affectsConfiguration(DEFAULT_SETTINGS_EDITOR_SETTING)) { this.handleSettingsEditorRegistration(); } - }); + })); this.handleSettingsEditorRegistration(); - this.start(); + const fileSystemProvider = this._register(this.instantiationService.createInstance(SettingsFileSystemProvider)); + this._register(fileService.registerProvider(SettingsFileSystemProvider.SCHEMA, fileSystemProvider)); } private handleSettingsEditorRegistration(): void { @@ -105,52 +97,9 @@ export class PreferencesContribution implements IWorkbenchContribution { ); } } - - private start(): void { - - this.textModelResolverService.registerTextModelContentProvider('vscode', { - provideTextContent: async (uri: URI): Promise => { - if (uri.scheme !== 'vscode') { - return null; - } - if (uri.authority === 'schemas') { - return this.getSchemaModel(uri); - } - return this.preferencesService.resolveModel(uri); - } - }); - } - - private getSchemaModel(uri: URI): ITextModel { - let schema = schemaRegistry.getSchemaContributions().schemas[uri.toString()] ?? {} /* Use empty schema if not yet registered */; - const modelContent = this.getSchemaContent(uri, schema); - const languageSelection = this.languageService.createById('jsonc'); - const model = this.modelService.createModel(modelContent, languageSelection, uri); - const disposables = new DisposableStore(); - disposables.add(schemaRegistry.onDidChangeSchema(schemaUri => { - if (schemaUri === uri.toString()) { - schema = schemaRegistry.getSchemaContributions().schemas[uri.toString()]; - model.setValue(this.getSchemaContent(uri, schema)); - } - })); - disposables.add(model.onWillDispose(() => disposables.dispose())); - return model; - } - - private getSchemaContent(uri: URI, schema: IJSONSchema): string { - const startTime = Date.now(); - const content = getCompressedContent(schema); - if (this.logService.getLevel() === LogLevel.Debug) { - const endTime = Date.now(); - const uncompressed = JSON.stringify(schema); - this.logService.debug(`${uri.path}: ${uncompressed.length} -> ${content.length} (${Math.round((uncompressed.length - content.length) / uncompressed.length * 100)}%) Took ${endTime - startTime}ms`); - } - return content; - } - - dispose(): void { + override dispose(): void { dispose(this.editorOpeningListener); - dispose(this.settingsListener); + super.dispose(); } } diff --git a/src/vs/workbench/contrib/preferences/common/settingsFilesystemProvider.ts b/src/vs/workbench/contrib/preferences/common/settingsFilesystemProvider.ts new file mode 100644 index 00000000000..f0a80db832c --- /dev/null +++ b/src/vs/workbench/contrib/preferences/common/settingsFilesystemProvider.ts @@ -0,0 +1,98 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { NotSupportedError } from 'vs/base/common/errors'; +import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; +import { Schemas } from 'vs/base/common/network'; +import { URI } from 'vs/base/common/uri'; +import { FileChangeType, FilePermission, FileSystemProviderCapabilities, FileSystemProviderErrorCode, FileType, IFileChange, IFileDeleteOptions, IFileOverwriteOptions, IFileSystemProviderWithFileReadWriteCapability, IStat, IWatchOptions } from 'vs/platform/files/common/files'; +import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; +import { Event, Emitter } from 'vs/base/common/event'; +import { Registry } from 'vs/platform/registry/common/platform'; +import * as JSONContributionRegistry from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; +import { VSBuffer } from 'vs/base/common/buffer'; +import { ILogService, LogLevel } from 'vs/platform/log/common/log'; + +const schemaRegistry = Registry.as(JSONContributionRegistry.Extensions.JSONContribution); + + +export class SettingsFileSystemProvider extends Disposable implements IFileSystemProviderWithFileReadWriteCapability { + + static readonly SCHEMA = Schemas.vscode; + + protected readonly _onDidChangeFile = this._register(new Emitter()); + readonly onDidChangeFile = this._onDidChangeFile.event; + + constructor( + @IPreferencesService private readonly preferencesService: IPreferencesService, + @ILogService private readonly logService: ILogService + ) { + super(); + this._register(schemaRegistry.onDidChangeSchema(schemaUri => { + this._onDidChangeFile.fire([{ resource: URI.parse(schemaUri), type: FileChangeType.UPDATED }]); + })); + this._register(preferencesService.onDidDefaultSettingsContentChanged(uri => { + this._onDidChangeFile.fire([{ resource: uri, type: FileChangeType.UPDATED }]); + })); + } + + readonly capabilities: FileSystemProviderCapabilities = FileSystemProviderCapabilities.Readonly + FileSystemProviderCapabilities.FileReadWrite; + + async readFile(uri: URI): Promise { + if (uri.scheme !== SettingsFileSystemProvider.SCHEMA) { + throw new NotSupportedError(); + } + let content: string | undefined; + if (uri.authority === 'schemas') { + content = this.getSchemaContent(uri); + } else if (uri.authority === 'defaultsettings') { + content = this.preferencesService.getDefaultSettingsContent(uri); + } + if (content) { + return VSBuffer.fromString(content).buffer; + } + throw FileSystemProviderErrorCode.FileNotFound; + } + + async stat(uri: URI): Promise { + if (schemaRegistry.hasSchemaContent(uri.toString()) || this.preferencesService.hasDefaultSettingsContent(uri)) { + const currentTime = Date.now(); + return { + type: FileType.File, + permissions: FilePermission.Readonly, + mtime: currentTime, + ctime: currentTime, + size: 0 + }; + } + throw FileSystemProviderErrorCode.FileNotFound; + } + + readonly onDidChangeCapabilities = Event.None; + + watch(resource: URI, opts: IWatchOptions): IDisposable { return Disposable.None; } + + async mkdir(resource: URI): Promise { } + async readdir(resource: URI): Promise<[string, FileType][]> { return []; } + + async rename(from: URI, to: URI, opts: IFileOverwriteOptions): Promise { } + async delete(resource: URI, opts: IFileDeleteOptions): Promise { } + + async writeFile() { + throw new NotSupportedError(); + } + + private getSchemaContent(uri: URI): string { + const startTime = Date.now(); + const content = schemaRegistry.getSchemaContent(uri.toString()) ?? '{}' /* Use empty schema if not yet registered */; + const logLevel = this.logService.getLevel(); + if (logLevel === LogLevel.Debug || logLevel === LogLevel.Trace) { + const endTime = Date.now(); + const uncompressed = JSON.stringify(schemaRegistry.getSchemaContributions().schemas[uri.toString()]); + this.logService.debug(`${uri.toString()}: ${uncompressed.length} -> ${content.length} (${Math.round((uncompressed.length - content.length) / uncompressed.length * 100)}%) Took ${endTime - startTime}ms`); + } + return content; + } +} diff --git a/src/vs/workbench/services/preferences/browser/preferencesService.ts b/src/vs/workbench/services/preferences/browser/preferencesService.ts index d12424fba94..530aa235183 100644 --- a/src/vs/workbench/services/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/services/preferences/browser/preferencesService.ts @@ -12,9 +12,7 @@ import { URI } from 'vs/base/common/uri'; import { CoreEditingCommands } from 'vs/editor/browser/coreCommands'; import { getCodeEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IPosition } from 'vs/editor/common/core/position'; -import { ITextModel } from 'vs/editor/common/model'; import { IModelService } from 'vs/editor/common/services/model'; -import { ILanguageService } from 'vs/editor/common/languages/language'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import * as nls from 'vs/nls'; import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -30,7 +28,6 @@ import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/ import { DEFAULT_EDITOR_ASSOCIATION, IEditorPane } from 'vs/workbench/common/editor'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { SideBySideEditorInput } from 'vs/workbench/common/editor/sideBySideEditorInput'; -import { TextResourceEditorInput } from 'vs/workbench/common/editor/textResourceEditorInput'; import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing'; import { GroupDirection, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService, SIDE_GROUP, SIDE_GROUP_TYPE } from 'vs/workbench/services/editor/common/editorService'; @@ -45,6 +42,8 @@ import { isObject } from 'vs/base/common/types'; import { SuggestController } from 'vs/editor/contrib/suggest/browser/suggestController'; import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { ResourceSet } from 'vs/base/common/map'; +import { isEqual } from 'vs/base/common/resources'; import { IURLService } from 'vs/platform/url/common/url'; const emptyEditableSettingsContent = '{\n}'; @@ -55,13 +54,21 @@ export class PreferencesService extends Disposable implements IPreferencesServic private readonly _onDispose = this._register(new Emitter()); + private readonly _onDidDefaultSettingsContentChanged = this._register(new Emitter()); + readonly onDidDefaultSettingsContentChanged = this._onDidDefaultSettingsContentChanged.event; + private _defaultUserSettingsContentModel: DefaultSettings | undefined; private _defaultWorkspaceSettingsContentModel: DefaultSettings | undefined; private _defaultFolderSettingsContentModel: DefaultSettings | undefined; + private _defaultRawSettingsEditorModel: DefaultRawSettingsEditorModel | undefined; + + private readonly _requestedDefaultSettings = new ResourceSet(); + private _settingsGroups: ISettingsGroup[] | undefined = undefined; private _defaultSettings: DefaultSettings | undefined = undefined; + constructor( @IEditorService private readonly editorService: IEditorService, @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, @@ -74,9 +81,8 @@ export class PreferencesService extends Disposable implements IPreferencesServic @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, @ITextModelService private readonly textModelResolverService: ITextModelService, @IKeybindingService keybindingService: IKeybindingService, - @IModelService private readonly modelService: IModelService, + @IModelService modelService: IModelService, @IJSONEditingService private readonly jsonEditingService: IJSONEditingService, - @ILanguageService private readonly languageService: ILanguageService, @ILabelService private readonly labelService: ILabelService, @IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService, @ITextEditorService private readonly textEditorService: ITextEditorService, @@ -121,52 +127,39 @@ export class PreferencesService extends Disposable implements IPreferencesServic return folder ? folder.toResource(FOLDER_SETTINGS_PATH) : null; } - resolveModel(uri: URI): ITextModel | null { + hasDefaultSettingsContent(uri: URI): boolean { + return this.isDefaultSettingsResource(uri) || isEqual(uri, this.defaultSettingsRawResource) || isEqual(uri, this.defaultKeybindingsResource); + } + + getDefaultSettingsContent(uri: URI): string | undefined { if (this.isDefaultSettingsResource(uri)) { // We opened a split json editor in this case, // and this half shows the default settings. + const target = this.getConfigurationTargetFromDefaultSettingsResource(uri); - const languageSelection = this.languageService.createById('jsonc'); - const model = this._register(this.modelService.createModel('', languageSelection, uri)); + const defaultSettings = this.getDefaultSettings(target); - let defaultSettings: DefaultSettings | undefined; - this.configurationService.onDidChangeConfiguration(e => { - if (e.source === ConfigurationTarget.DEFAULT) { - const model = this.modelService.getModel(uri); - if (!model) { - // model has not been given out => nothing to do - return; - } - defaultSettings = this.getDefaultSettings(target); - this.modelService.updateModel(model, defaultSettings.getContentWithoutMostCommonlyUsed(true)); - defaultSettings._onDidChange.fire(); - } - }); - - // Check if Default settings is already created and updated in above promise - if (!defaultSettings) { - defaultSettings = this.getDefaultSettings(target); - this.modelService.updateModel(model, defaultSettings.getContentWithoutMostCommonlyUsed(true)); + if (!this._requestedDefaultSettings.has(uri)) { + this._register(defaultSettings.onDidChange(() => this._onDidDefaultSettingsContentChanged.fire(uri))); + this._requestedDefaultSettings.add(uri); } - - return model; + return defaultSettings.getContentWithoutMostCommonlyUsed(true); } - if (this.defaultSettingsRawResource.toString() === uri.toString()) { - const defaultRawSettingsEditorModel = this.instantiationService.createInstance(DefaultRawSettingsEditorModel, this.getDefaultSettings(ConfigurationTarget.USER_LOCAL)); - const languageSelection = this.languageService.createById('jsonc'); - const model = this._register(this.modelService.createModel(defaultRawSettingsEditorModel.content, languageSelection, uri)); - return model; + if (isEqual(uri, this.defaultSettingsRawResource)) { + if (!this._defaultRawSettingsEditorModel) { + this._defaultRawSettingsEditorModel = this._register(this.instantiationService.createInstance(DefaultRawSettingsEditorModel, this.getDefaultSettings(ConfigurationTarget.USER_LOCAL))); + this._register(this._defaultRawSettingsEditorModel.onDidContentChanged(() => this._onDidDefaultSettingsContentChanged.fire(uri))); + } + return this._defaultRawSettingsEditorModel.content; } - if (this.defaultKeybindingsResource.toString() === uri.toString()) { + if (isEqual(uri, this.defaultKeybindingsResource)) { const defaultKeybindingsEditorModel = this.instantiationService.createInstance(DefaultKeybindingsEditorModel, uri); - const languageSelection = this.languageService.createById('jsonc'); - const model = this._register(this.modelService.createModel(defaultKeybindingsEditorModel.content, languageSelection, uri)); - return model; + return defaultKeybindingsEditorModel.content; } - return null; + return undefined; } public async createPreferencesEditorModel(uri: URI): Promise | null> { @@ -381,7 +374,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic public createSplitJsonEditorInput(configurationTarget: ConfigurationTarget, resource: URI): EditorInput { const editableSettingsEditorInput = this.textEditorService.createTextEditor({ resource }); - const defaultPreferencesEditorInput = this.instantiationService.createInstance(TextResourceEditorInput, this.getDefaultSettingsResource(configurationTarget), undefined, undefined, undefined, undefined); + const defaultPreferencesEditorInput = this.textEditorService.createTextEditor({ resource: this.getDefaultSettingsResource(configurationTarget) }); return this.instantiationService.createInstance(SideBySideEditorInput, editableSettingsEditorInput.getName(), undefined, defaultPreferencesEditorInput, editableSettingsEditorInput); } diff --git a/src/vs/workbench/services/preferences/common/preferences.ts b/src/vs/workbench/services/preferences/common/preferences.ts index f45dc085c29..f6a278621e4 100644 --- a/src/vs/workbench/services/preferences/common/preferences.ts +++ b/src/vs/workbench/services/preferences/common/preferences.ts @@ -11,7 +11,6 @@ import { ResolvedKeybinding } from 'vs/base/common/keybindings'; import { URI } from 'vs/base/common/uri'; import { IRange } from 'vs/editor/common/core/range'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; -import { ITextModel } from 'vs/editor/common/model'; import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { ConfigurationDefaultValueSource, ConfigurationScope, EditPresentationTypes, IExtensionInfo } from 'vs/platform/configuration/common/configurationRegistry'; import { IEditorOptions } from 'vs/platform/editor/common/editor'; @@ -238,12 +237,15 @@ export const IPreferencesService = createDecorator('prefere export interface IPreferencesService { readonly _serviceBrand: undefined; + readonly onDidDefaultSettingsContentChanged: Event; + userSettingsResource: URI; workspaceSettingsResource: URI | null; getFolderSettingsResource(resource: URI): URI | null; createPreferencesEditorModel(uri: URI): Promise | null>; - resolveModel(uri: URI): ITextModel | null; + getDefaultSettingsContent(uri: URI): string | undefined; + hasDefaultSettingsContent(uri: URI): boolean; createSettings2EditorModel(): Settings2EditorModel; // TODO openRawDefaultSettings(): Promise; diff --git a/src/vs/workbench/services/preferences/common/preferencesModels.ts b/src/vs/workbench/services/preferences/common/preferencesModels.ts index f116a7b0067..29009d0710e 100644 --- a/src/vs/workbench/services/preferences/common/preferencesModels.ts +++ b/src/vs/workbench/services/preferences/common/preferencesModels.ts @@ -1125,9 +1125,15 @@ export class DefaultRawSettingsEditorModel extends Disposable { private _content: string | null = null; + private readonly _onDidContentChanged = this._register(new Emitter()); + readonly onDidContentChanged = this._onDidContentChanged.event; + constructor(private defaultSettings: DefaultSettings) { super(); - this._register(defaultSettings.onDidChange(() => this._content = null)); + this._register(defaultSettings.onDidChange(() => { + this._content = null; + this._onDidContentChanged.fire(); + })); } get content(): string {