提交 c5bd62f0 编写于 作者: S Sandeep Somavarapu

adopt keybindings to user data service

上级 f75bbdbc
...@@ -97,7 +97,6 @@ export interface IEnvironmentService { ...@@ -97,7 +97,6 @@ export interface IEnvironmentService {
appNameLong: string; appNameLong: string;
appQuality?: string; appQuality?: string;
appSettingsHome: URI; appSettingsHome: URI;
keybindingsResource: URI;
keyboardLayoutResource: URI; keyboardLayoutResource: URI;
machineSettingsHome: URI; machineSettingsHome: URI;
......
...@@ -132,9 +132,6 @@ export class EnvironmentService implements IEnvironmentService { ...@@ -132,9 +132,6 @@ export class EnvironmentService implements IEnvironmentService {
@memoize @memoize
get settingsSearchUrl(): string | undefined { return product.settingsSearchUrl; } get settingsSearchUrl(): string | undefined { return product.settingsSearchUrl; }
@memoize
get keybindingsResource(): URI { return resources.joinPath(this.appSettingsHome, 'keybindings.json'); }
@memoize @memoize
get keyboardLayoutResource(): URI { return resources.joinPath(this.appSettingsHome, 'keyboardLayout.json'); } get keyboardLayoutResource(): URI { return resources.joinPath(this.appSettingsHome, 'keyboardLayout.json'); }
......
...@@ -10,6 +10,8 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' ...@@ -10,6 +10,8 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'
import { IResolveResult } from 'vs/platform/keybinding/common/keybindingResolver'; import { IResolveResult } from 'vs/platform/keybinding/common/keybindingResolver';
import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem';
export const USER_KEYBINDINGS_KEY = 'keybindings.json';
export interface IUserFriendlyKeybinding { export interface IUserFriendlyKeybinding {
key: string; key: string;
command: string; command: string;
......
...@@ -40,6 +40,7 @@ import { ILabelService } from 'vs/platform/label/common/label'; ...@@ -40,6 +40,7 @@ import { ILabelService } from 'vs/platform/label/common/label';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IKeybindingEditingService } from 'vs/workbench/services/keybinding/common/keybindingEditing';
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor( Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
new EditorDescriptor( new EditorDescriptor(
...@@ -371,6 +372,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon ...@@ -371,6 +372,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon
constructor( constructor(
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
@IKeybindingEditingService keybindingEditingService: IKeybindingEditingService,
@IConfigurationService configurationService: IConfigurationService, @IConfigurationService configurationService: IConfigurationService,
@IPreferencesService private readonly preferencesService: IPreferencesService, @IPreferencesService private readonly preferencesService: IPreferencesService,
@IWorkspaceContextService private readonly workpsaceContextService: IWorkspaceContextService, @IWorkspaceContextService private readonly workpsaceContextService: IWorkspaceContextService,
...@@ -387,7 +389,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon ...@@ -387,7 +389,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon
dark: URI.parse(require.toUrl(`vs/workbench/contrib/preferences/browser/media/preferences-editor-inverse.svg`)) dark: URI.parse(require.toUrl(`vs/workbench/contrib/preferences/browser/media/preferences-editor-inverse.svg`))
} }
}, },
when: ResourceContextKey.Resource.isEqualTo(environmentService.keybindingsResource.toString()), when: ResourceContextKey.Resource.isEqualTo(keybindingEditingService.userKeybindingsResource.toString()),
group: 'navigation', group: 'navigation',
order: 1 order: 1
}); });
......
...@@ -71,7 +71,6 @@ export class BrowserWorkbenchEnvironmentService implements IEnvironmentService { ...@@ -71,7 +71,6 @@ export class BrowserWorkbenchEnvironmentService implements IEnvironmentService {
this.configuration.remoteAuthority = configuration.remoteAuthority; this.configuration.remoteAuthority = configuration.remoteAuthority;
this.appSettingsHome = joinPath(URI.revive(JSON.parse(document.getElementById('vscode-remote-user-data-uri')!.getAttribute('data-settings')!)), 'User'); this.appSettingsHome = joinPath(URI.revive(JSON.parse(document.getElementById('vscode-remote-user-data-uri')!.getAttribute('data-settings')!)), 'User');
this.keybindingsResource = joinPath(this.appSettingsHome, 'keybindings.json');
this.keyboardLayoutResource = joinPath(this.appSettingsHome, 'keyboardLayout.json'); this.keyboardLayoutResource = joinPath(this.appSettingsHome, 'keyboardLayout.json');
this.logsPath = '/web/logs'; this.logsPath = '/web/logs';
...@@ -96,7 +95,6 @@ export class BrowserWorkbenchEnvironmentService implements IEnvironmentService { ...@@ -96,7 +95,6 @@ export class BrowserWorkbenchEnvironmentService implements IEnvironmentService {
appNameLong: string; appNameLong: string;
appQuality?: string; appQuality?: string;
appSettingsHome: URI; appSettingsHome: URI;
keybindingsResource: URI;
keyboardLayoutResource: URI; keyboardLayoutResource: URI;
machineSettingsHome: URI; machineSettingsHome: URI;
machineSettingsResource: URI; machineSettingsResource: URI;
......
...@@ -19,7 +19,7 @@ import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/commo ...@@ -19,7 +19,7 @@ import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/commo
import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { Extensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { Extensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
import { AbstractKeybindingService } from 'vs/platform/keybinding/common/abstractKeybindingService'; import { AbstractKeybindingService } from 'vs/platform/keybinding/common/abstractKeybindingService';
import { IKeyboardEvent, IUserFriendlyKeybinding, KeybindingSource, IKeybindingService, IKeybindingEvent } from 'vs/platform/keybinding/common/keybinding'; import { IKeyboardEvent, IUserFriendlyKeybinding, KeybindingSource, IKeybindingService, IKeybindingEvent, USER_KEYBINDINGS_KEY } from 'vs/platform/keybinding/common/keybinding';
import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingResolver'; import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingResolver';
import { IKeybindingItem, IKeybindingRule2, KeybindingWeight, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IKeybindingItem, IKeybindingRule2, KeybindingWeight, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem';
...@@ -36,17 +36,15 @@ import { MenuRegistry } from 'vs/platform/actions/common/actions'; ...@@ -36,17 +36,15 @@ import { MenuRegistry } from 'vs/platform/actions/common/actions';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
// tslint:disable-next-line: import-patterns // tslint:disable-next-line: import-patterns
import { commandsExtensionPoint } from 'vs/workbench/api/common/menusExtensionPoint'; import { commandsExtensionPoint } from 'vs/workbench/api/common/menusExtensionPoint';
import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { Disposable } from 'vs/base/common/lifecycle';
import { RunOnceScheduler } from 'vs/base/common/async'; import { RunOnceScheduler } from 'vs/base/common/async';
import { URI } from 'vs/base/common/uri';
import { IFileService, FileChangesEvent, FileChangeType } from 'vs/platform/files/common/files';
import { dirname, isEqual } from 'vs/base/common/resources';
import { parse } from 'vs/base/common/json'; import { parse } from 'vs/base/common/json';
import * as objects from 'vs/base/common/objects'; import * as objects from 'vs/base/common/objects';
import { IKeymapService } from 'vs/workbench/services/keybinding/common/keymapInfo'; import { IKeymapService } from 'vs/workbench/services/keybinding/common/keymapInfo';
import { getDispatchConfig } from 'vs/workbench/services/keybinding/common/dispatchConfig'; import { getDispatchConfig } from 'vs/workbench/services/keybinding/common/dispatchConfig';
import { isArray } from 'vs/base/common/types'; import { isArray } from 'vs/base/common/types';
import { INavigatorWithKeyboard } from 'vs/workbench/services/keybinding/common/navigatorKeyboard'; import { INavigatorWithKeyboard } from 'vs/workbench/services/keybinding/common/navigatorKeyboard';
import { IUserDataService } from 'vs/workbench/services/userData/common/userData';
interface ContributedKeyBinding { interface ContributedKeyBinding {
command: string; command: string;
...@@ -158,8 +156,8 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { ...@@ -158,8 +156,8 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService {
@IConfigurationService configurationService: IConfigurationService, @IConfigurationService configurationService: IConfigurationService,
@IWindowService private readonly windowService: IWindowService, @IWindowService private readonly windowService: IWindowService,
@IExtensionService extensionService: IExtensionService, @IExtensionService extensionService: IExtensionService,
@IFileService fileService: IFileService, @IKeymapService private readonly keymapService: IKeymapService,
@IKeymapService private readonly keymapService: IKeymapService @IUserDataService userDataService: IUserDataService,
) { ) {
super(contextKeyService, commandService, telemetryService, notificationService); super(contextKeyService, commandService, telemetryService, notificationService);
...@@ -185,7 +183,7 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { ...@@ -185,7 +183,7 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService {
this._cachedResolver = null; this._cachedResolver = null;
this.userKeybindings = this._register(new UserKeybindings(environmentService.keybindingsResource, fileService)); this.userKeybindings = this._register(new UserKeybindings(userDataService));
this.userKeybindings.initialize().then(() => { this.userKeybindings.initialize().then(() => {
if (this.userKeybindings.keybindings.length) { if (this.userKeybindings.keybindings.length) {
this.updateResolver({ source: KeybindingSource.User }); this.updateResolver({ source: KeybindingSource.User });
...@@ -552,100 +550,40 @@ class UserKeybindings extends Disposable { ...@@ -552,100 +550,40 @@ class UserKeybindings extends Disposable {
private _keybindings: IUserFriendlyKeybinding[] = []; private _keybindings: IUserFriendlyKeybinding[] = [];
get keybindings(): IUserFriendlyKeybinding[] { return this._keybindings; } get keybindings(): IUserFriendlyKeybinding[] { return this._keybindings; }
private readonly reloadConfigurationScheduler: RunOnceScheduler; private readonly reloadConfigurationScheduler: RunOnceScheduler;
protected readonly _onDidChange: Emitter<void> = this._register(new Emitter<void>()); protected readonly _onDidChange: Emitter<void> = this._register(new Emitter<void>());
readonly onDidChange: Event<void> = this._onDidChange.event; readonly onDidChange: Event<void> = this._onDidChange.event;
private fileWatcherDisposable: IDisposable = Disposable.None;
private directoryWatcherDisposable: IDisposable = Disposable.None;
constructor( constructor(
private readonly keybindingsResource: URI, private readonly userDataService: IUserDataService
private readonly fileService: IFileService
) { ) {
super(); super();
this._register(fileService.onFileChanges(e => this.handleFileEvents(e))); this._register(Event.filter(this.userDataService.onDidChange, e => e.contains(USER_KEYBINDINGS_KEY))(() => this.reloadConfigurationScheduler.schedule()));
this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this.reload().then(changed => { this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this.reload().then(changed => {
if (changed) { if (changed) {
this._onDidChange.fire(); this._onDidChange.fire();
} }
}), 50)); }), 50));
this._register(toDisposable(() => {
this.stopWatchingResource();
this.stopWatchingDirectory();
}));
}
private watchResource(): void {
this.fileWatcherDisposable = this.fileService.watch(this.keybindingsResource);
}
private stopWatchingResource(): void {
this.fileWatcherDisposable.dispose();
this.fileWatcherDisposable = Disposable.None;
}
private watchDirectory(): void {
const directory = dirname(this.keybindingsResource);
this.directoryWatcherDisposable = this.fileService.watch(directory);
}
private stopWatchingDirectory(): void {
this.directoryWatcherDisposable.dispose();
this.directoryWatcherDisposable = Disposable.None;
} }
async initialize(): Promise<void> { async initialize(): Promise<void> {
const exists = await this.fileService.exists(this.keybindingsResource);
this.onResourceExists(exists);
await this.reload(); await this.reload();
} }
private async reload(): Promise<boolean> { private async reload(): Promise<boolean> {
const existing = this._keybindings; const existing = this._keybindings;
try { try {
const content = await this.fileService.readFile(this.keybindingsResource); const content = (await this.userDataService.read(USER_KEYBINDINGS_KEY)) || '[]';
const value = parse(content.value.toString()); const value = parse(content);
this._keybindings = isArray(value) ? value : []; this._keybindings = isArray(value) ? value : [];
} catch (e) { } catch (e) {
this._keybindings = []; this._keybindings = [];
} }
return existing ? !objects.equals(existing, this._keybindings) : true; return existing ? !objects.equals(existing, this._keybindings) : true;
} }
private async handleFileEvents(event: FileChangesEvent): Promise<void> {
const events = event.changes;
let affectedByChanges = false;
// Find changes that affect the resource
for (const event of events) {
affectedByChanges = isEqual(this.keybindingsResource, event.resource);
if (affectedByChanges) {
if (event.type === FileChangeType.ADDED) {
this.onResourceExists(true);
} else if (event.type === FileChangeType.DELETED) {
this.onResourceExists(false);
}
break;
}
}
if (affectedByChanges) {
this.reloadConfigurationScheduler.schedule();
}
}
private onResourceExists(exists: boolean): void {
if (exists) {
this.stopWatchingDirectory();
this.watchResource();
} else {
this.stopWatchingResource();
this.watchDirectory();
}
}
} }
let schemaId = 'vscode://schemas/keybindings'; let schemaId = 'vscode://schemas/keybindings';
......
...@@ -8,23 +8,24 @@ import { Queue } from 'vs/base/common/async'; ...@@ -8,23 +8,24 @@ import { Queue } from 'vs/base/common/async';
import * as json from 'vs/base/common/json'; import * as json from 'vs/base/common/json';
import { setProperty } from 'vs/base/common/jsonEdit'; import { setProperty } from 'vs/base/common/jsonEdit';
import { Edit } from 'vs/base/common/jsonFormatter'; import { Edit } from 'vs/base/common/jsonFormatter';
import { Disposable, IReference } from 'vs/base/common/lifecycle'; import { Disposable } from 'vs/base/common/lifecycle';
import { isArray } from 'vs/base/common/types'; import { isArray } from 'vs/base/common/types';
import { URI } from 'vs/base/common/uri';
import { EditOperation } from 'vs/editor/common/core/editOperation'; import { EditOperation } from 'vs/editor/common/core/editOperation';
import { Range } from 'vs/editor/common/core/range'; import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection'; import { Selection } from 'vs/editor/common/core/selection';
import { ITextModel } from 'vs/editor/common/model'; import { ITextModel } from 'vs/editor/common/model';
import { ITextModelService, IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IFileService } from 'vs/platform/files/common/files';
import { ServiceIdentifier, createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ServiceIdentifier, createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IUserFriendlyKeybinding } from 'vs/platform/keybinding/common/keybinding'; import { IUserFriendlyKeybinding, USER_KEYBINDINGS_KEY } from 'vs/platform/keybinding/common/keybinding';
import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IUserDataService } from 'vs/workbench/services/userData/common/userData';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IModelService } from 'vs/editor/common/services/modelService';
import { Emitter } from 'vs/base/common/event';
import { LanguageIdentifier } from 'vs/editor/common/modes';
import { Schemas } from 'vs/base/common/network';
import { URI } from 'vs/base/common/uri';
export const IKeybindingEditingService = createDecorator<IKeybindingEditingService>('keybindingEditingService'); export const IKeybindingEditingService = createDecorator<IKeybindingEditingService>('keybindingEditingService');
...@@ -32,6 +33,8 @@ export interface IKeybindingEditingService { ...@@ -32,6 +33,8 @@ export interface IKeybindingEditingService {
_serviceBrand: ServiceIdentifier<any>; _serviceBrand: ServiceIdentifier<any>;
userKeybindingsResource: URI;
editKeybinding(keybindingItem: ResolvedKeybindingItem, key: string, when: string | undefined): Promise<void>; editKeybinding(keybindingItem: ResolvedKeybindingItem, key: string, when: string | undefined): Promise<void>;
removeKeybinding(keybindingItem: ResolvedKeybindingItem): Promise<void>; removeKeybinding(keybindingItem: ResolvedKeybindingItem): Promise<void>;
...@@ -42,18 +45,17 @@ export interface IKeybindingEditingService { ...@@ -42,18 +45,17 @@ export interface IKeybindingEditingService {
export class KeybindingsEditingService extends Disposable implements IKeybindingEditingService { export class KeybindingsEditingService extends Disposable implements IKeybindingEditingService {
public _serviceBrand: any; public _serviceBrand: any;
private queue: Queue<void>;
private resource: URI = this.environmentService.keybindingsResource; readonly userKeybindingsResource: URI;
private queue: Queue<void>;
constructor( constructor(
@ITextModelService private readonly textModelResolverService: ITextModelService, @IUserDataService private readonly userDataService: IUserDataService,
@ITextFileService private readonly textFileService: ITextFileService, @IModeService private readonly modeService: IModeService,
@IFileService private readonly fileService: IFileService, @IModelService private readonly modelService: IModelService
@IConfigurationService private readonly configurationService: IConfigurationService,
@IEnvironmentService private readonly environmentService: IEnvironmentService
) { ) {
super(); super();
this.userKeybindingsResource = userDataService.toResource(USER_KEYBINDINGS_KEY);
this.queue = new Queue<void>(); this.queue = new Queue<void>();
} }
...@@ -71,45 +73,44 @@ export class KeybindingsEditingService extends Disposable implements IKeybinding ...@@ -71,45 +73,44 @@ export class KeybindingsEditingService extends Disposable implements IKeybinding
private doEditKeybinding(keybindingItem: ResolvedKeybindingItem, key: string, when: string | undefined): Promise<void> { private doEditKeybinding(keybindingItem: ResolvedKeybindingItem, key: string, when: string | undefined): Promise<void> {
return this.resolveAndValidate() return this.resolveAndValidate()
.then(reference => { .then(model => {
const model = reference.object.textEditorModel;
const userKeybindingEntries = <IUserFriendlyKeybinding[]>json.parse(model.getValue()); const userKeybindingEntries = <IUserFriendlyKeybinding[]>json.parse(model.getValue());
const userKeybindingEntryIndex = this.findUserKeybindingEntryIndex(keybindingItem, userKeybindingEntries); const userKeybindingEntryIndex = this.findUserKeybindingEntryIndex(keybindingItem, userKeybindingEntries);
this.updateKeybinding(keybindingItem, key, when, model, userKeybindingEntryIndex); this.updateKeybinding(keybindingItem, key, when, model, userKeybindingEntryIndex);
if (keybindingItem.isDefault && keybindingItem.resolvedKeybinding) { if (keybindingItem.isDefault && keybindingItem.resolvedKeybinding) {
this.removeDefaultKeybinding(keybindingItem, model); this.removeDefaultKeybinding(keybindingItem, model);
} }
return this.save().then(() => reference.dispose()); return this.save(model);
}); });
} }
private doRemoveKeybinding(keybindingItem: ResolvedKeybindingItem): Promise<void> { private doRemoveKeybinding(keybindingItem: ResolvedKeybindingItem): Promise<void> {
return this.resolveAndValidate() return this.resolveAndValidate()
.then(reference => { .then(model => {
const model = reference.object.textEditorModel;
if (keybindingItem.isDefault) { if (keybindingItem.isDefault) {
this.removeDefaultKeybinding(keybindingItem, model); this.removeDefaultKeybinding(keybindingItem, model);
} else { } else {
this.removeUserKeybinding(keybindingItem, model); this.removeUserKeybinding(keybindingItem, model);
} }
return this.save().then(() => reference.dispose()); return this.save(model);
}); });
} }
private doResetKeybinding(keybindingItem: ResolvedKeybindingItem): Promise<void> { private doResetKeybinding(keybindingItem: ResolvedKeybindingItem): Promise<void> {
return this.resolveAndValidate() return this.resolveAndValidate()
.then(reference => { .then(model => {
const model = reference.object.textEditorModel;
if (!keybindingItem.isDefault) { if (!keybindingItem.isDefault) {
this.removeUserKeybinding(keybindingItem, model); this.removeUserKeybinding(keybindingItem, model);
this.removeUnassignedDefaultKeybinding(keybindingItem, model); this.removeUnassignedDefaultKeybinding(keybindingItem, model);
} }
return this.save().then(() => reference.dispose()); return this.save(model);
}); });
} }
private save(): Promise<any> { private async save(model: ITextModel): Promise<void> {
return this.textFileService.save(this.resource); await this.userDataService.write(USER_KEYBINDINGS_KEY, model.getValue());
model.dispose();
this.modelService.destroyModel(model.uri);
} }
private updateKeybinding(keybindingItem: ResolvedKeybindingItem, newKey: string, when: string | undefined, model: ITextModel, userKeybindingEntryIndex: number): void { private updateKeybinding(keybindingItem: ResolvedKeybindingItem, newKey: string, when: string | undefined, model: ITextModel, userKeybindingEntryIndex: number): void {
...@@ -207,45 +208,33 @@ export class KeybindingsEditingService extends Disposable implements IKeybinding ...@@ -207,45 +208,33 @@ export class KeybindingsEditingService extends Disposable implements IKeybinding
} }
private resolveModelReference(): Promise<IReference<IResolvedTextEditorModel>> { private async resolveModel(): Promise<ITextModel> {
return this.fileService.exists(this.resource) const content = (await this.userDataService.read(USER_KEYBINDINGS_KEY)) || '[]';
.then(exists => { const languageIdentifier = this.modeService.getLanguageIdentifier('jsonc');
const EOL = this.configurationService.getValue<{}>('files', { overrideIdentifier: 'json' })['eol']; return this.modelService.createModel(content, languageIdentifier ? { languageIdentifier, onDidChange: new Emitter<LanguageIdentifier>().event, dispose: () => { } } : null, this.userKeybindingsResource.with({ scheme: Schemas.vscode }));
const result: Promise<any> = exists ? Promise.resolve(null) : this.textFileService.write(this.resource, this.getEmptyContent(EOL), { encoding: 'utf8' });
return result.then(() => this.textModelResolverService.createModelReference(this.resource));
});
} }
private resolveAndValidate(): Promise<IReference<IResolvedTextEditorModel>> { private async resolveAndValidate(): Promise<ITextModel> {
const model = await this.resolveModel();
// Target cannot be dirty if not writing into buffer const EOL = model.getEOL();
if (this.textFileService.isDirty(this.resource)) { if (model.getValue()) {
return Promise.reject(new Error(localize('errorKeybindingsFileDirty', "Unable to write because the keybindings configuration file is dirty. Please save it first and then try again."))); const parsed = this.parse(model);
} if (parsed.parseErrors.length) {
return Promise.reject<any>(new Error(localize('parseErrors', "Unable to write to the keybindings configuration file. Please open it to correct errors/warnings in the file and try again.")));
return this.resolveModelReference() }
.then(reference => { if (parsed.result) {
const model = reference.object.textEditorModel; if (!isArray(parsed.result)) {
const EOL = model.getEOL(); return Promise.reject<any>(new Error(localize('errorInvalidConfiguration', "Unable to write to the keybindings configuration file. It has an object which is not of type Array. Please open the file to clean up and try again.")));
if (model.getValue()) {
const parsed = this.parse(model);
if (parsed.parseErrors.length) {
return Promise.reject<any>(new Error(localize('parseErrors', "Unable to write to the keybindings configuration file. Please open it to correct errors/warnings in the file and try again.")));
}
if (parsed.result) {
if (!isArray(parsed.result)) {
return Promise.reject<any>(new Error(localize('errorInvalidConfiguration', "Unable to write to the keybindings configuration file. It has an object which is not of type Array. Please open the file to clean up and try again.")));
}
} else {
const content = EOL + '[]';
this.applyEditsToBuffer({ content, length: content.length, offset: model.getValue().length }, model);
}
} else {
const content = this.getEmptyContent(EOL);
this.applyEditsToBuffer({ content, length: content.length, offset: 0 }, model);
} }
return reference; } else {
}); const content = EOL + '[]';
this.applyEditsToBuffer({ content, length: content.length, offset: model.getValue().length }, model);
}
} else {
const content = this.getEmptyContent(EOL);
this.applyEditsToBuffer({ content, length: content.length, offset: 0 }, model);
}
return model;
} }
private parse(model: ITextModel): { result: IUserFriendlyKeybinding[], parseErrors: json.ParseError[] } { private parse(model: ITextModel): { result: IUserFriendlyKeybinding[], parseErrors: json.ParseError[] } {
......
...@@ -21,7 +21,6 @@ import { ITextResourcePropertiesService } from 'vs/editor/common/services/resour ...@@ -21,7 +21,6 @@ import { ITextResourcePropertiesService } from 'vs/editor/common/services/resour
import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ConfigurationService } from 'vs/platform/configuration/node/configurationService'; import { ConfigurationService } from 'vs/platform/configuration/node/configurationService';
import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IFileService } from 'vs/platform/files/common/files'; import { IFileService } from 'vs/platform/files/common/files';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { IUserFriendlyKeybinding } from 'vs/platform/keybinding/common/keybinding'; import { IUserFriendlyKeybinding } from 'vs/platform/keybinding/common/keybinding';
...@@ -45,6 +44,8 @@ import { FileService } from 'vs/workbench/services/files/common/fileService'; ...@@ -45,6 +44,8 @@ import { FileService } from 'vs/workbench/services/files/common/fileService';
import { Schemas } from 'vs/base/common/network'; import { Schemas } from 'vs/base/common/network';
import { DiskFileSystemProvider } from 'vs/workbench/services/files/node/diskFileSystemProvider'; import { DiskFileSystemProvider } from 'vs/workbench/services/files/node/diskFileSystemProvider';
import { URI } from 'vs/base/common/uri'; import { URI } from 'vs/base/common/uri';
import { IUserDataService } from 'vs/workbench/services/userData/common/userData';
import { FileUserDataService } from 'vs/workbench/services/userData/common/fileUserDataService';
interface Modifiers { interface Modifiers {
metaKey?: boolean; metaKey?: boolean;
...@@ -66,7 +67,6 @@ suite('KeybindingsEditing', () => { ...@@ -66,7 +67,6 @@ suite('KeybindingsEditing', () => {
instantiationService = new TestInstantiationService(); instantiationService = new TestInstantiationService();
instantiationService.stub(IEnvironmentService, <IEnvironmentService>{ keybindingsResource: URI.file(keybindingsFile) });
instantiationService.stub(IConfigurationService, ConfigurationService); instantiationService.stub(IConfigurationService, ConfigurationService);
instantiationService.stub(IConfigurationService, 'getValue', { 'eol': '\n' }); instantiationService.stub(IConfigurationService, 'getValue', { 'eol': '\n' });
instantiationService.stub(IConfigurationService, 'onDidUpdateConfiguration', () => { }); instantiationService.stub(IConfigurationService, 'onDidUpdateConfiguration', () => { });
...@@ -85,6 +85,7 @@ suite('KeybindingsEditing', () => { ...@@ -85,6 +85,7 @@ suite('KeybindingsEditing', () => {
const fileService = new FileService(new NullLogService()); const fileService = new FileService(new NullLogService());
fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(new NullLogService())); fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(new NullLogService()));
instantiationService.stub(IFileService, fileService); instantiationService.stub(IFileService, fileService);
instantiationService.stub(IUserDataService, new FileUserDataService(URI.file(testDir), fileService));
instantiationService.stub(IUntitledEditorService, instantiationService.createInstance(UntitledEditorService)); instantiationService.stub(IUntitledEditorService, instantiationService.createInstance(UntitledEditorService));
instantiationService.stub(ITextFileService, instantiationService.createInstance(TestTextFileService)); instantiationService.stub(ITextFileService, instantiationService.createInstance(TestTextFileService));
instantiationService.stub(ITextModelService, <ITextModelService>instantiationService.createInstance(TextModelResolverService)); instantiationService.stub(ITextModelService, <ITextModelService>instantiationService.createInstance(TextModelResolverService));
...@@ -144,16 +145,6 @@ suite('KeybindingsEditing', () => { ...@@ -144,16 +145,6 @@ suite('KeybindingsEditing', () => {
.then(() => assert.deepEqual(getUserKeybindings(), expected)); .then(() => assert.deepEqual(getUserKeybindings(), expected));
}); });
test('edit a default keybinding to a non existing keybindings file', () => {
keybindingsFile = path.join(testDir, 'nonExistingFile.json');
instantiationService.get(IEnvironmentService).keybindingsResource = URI.file(keybindingsFile);
testObject = instantiationService.createInstance(KeybindingsEditingService);
const expected: IUserFriendlyKeybinding[] = [{ key: 'alt+c', command: 'a' }, { key: 'escape', command: '-a' }];
return testObject.editKeybinding(aResolvedKeybindingItem({ firstPart: { keyCode: KeyCode.Escape }, command: 'a' }), 'alt+c', undefined)
.then(() => assert.deepEqual(getUserKeybindings(), expected));
});
test('edit a default keybinding to an empty array', () => { test('edit a default keybinding to an empty array', () => {
writeToKeybindingsFile(); writeToKeybindingsFile();
const expected: IUserFriendlyKeybinding[] = [{ key: 'alt+c', command: 'a' }, { key: 'escape', command: '-a' }]; const expected: IUserFriendlyKeybinding[] = [{ key: 'alt+c', command: 'a' }, { key: 'escape', command: '-a' }];
......
...@@ -20,7 +20,6 @@ import { ITextModelService } from 'vs/editor/common/services/resolverService'; ...@@ -20,7 +20,6 @@ import { ITextModelService } from 'vs/editor/common/services/resolverService';
import * as nls from 'vs/nls'; import * as nls from 'vs/nls';
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IEditorOptions } from 'vs/platform/editor/common/editor'; import { IEditorOptions } from 'vs/platform/editor/common/editor';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { FileOperationError, FileOperationResult } from 'vs/platform/files/common/files'; import { FileOperationError, FileOperationResult } from 'vs/platform/files/common/files';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
...@@ -38,6 +37,7 @@ import { defaultKeybindingsContents, DefaultKeybindingsEditorModel, DefaultSetti ...@@ -38,6 +37,7 @@ import { defaultKeybindingsContents, DefaultKeybindingsEditorModel, DefaultSetti
import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IKeybindingEditingService } from '../../keybinding/common/keybindingEditing';
const emptyEditableSettingsContent = '{\n}'; const emptyEditableSettingsContent = '{\n}';
...@@ -64,7 +64,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic ...@@ -64,7 +64,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic
@INotificationService private readonly notificationService: INotificationService, @INotificationService private readonly notificationService: INotificationService,
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
@IInstantiationService private readonly instantiationService: IInstantiationService, @IInstantiationService private readonly instantiationService: IInstantiationService,
@IEnvironmentService private readonly environmentService: IEnvironmentService, @IKeybindingEditingService private readonly keybindingEditingService: IKeybindingEditingService,
@ITelemetryService private readonly telemetryService: ITelemetryService, @ITelemetryService private readonly telemetryService: ITelemetryService,
@ITextModelService private readonly textModelResolverService: ITextModelService, @ITextModelService private readonly textModelResolverService: ITextModelService,
@IKeybindingService keybindingService: IKeybindingService, @IKeybindingService keybindingService: IKeybindingService,
...@@ -273,7 +273,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic ...@@ -273,7 +273,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic
this.telemetryService.publicLog('openKeybindings', { textual }); this.telemetryService.publicLog('openKeybindings', { textual });
if (textual) { if (textual) {
const emptyContents = '// ' + nls.localize('emptyKeybindingsHeader', "Place your key bindings in this file to override the defaults") + '\n[\n]'; const emptyContents = '// ' + nls.localize('emptyKeybindingsHeader', "Place your key bindings in this file to override the defaults") + '\n[\n]';
const editableKeybindings = this.environmentService.keybindingsResource; const editableKeybindings = this.keybindingEditingService.userKeybindingsResource;
const openDefaultKeybindings = !!this.configurationService.getValue('workbench.settings.openDefaultKeybindings'); const openDefaultKeybindings = !!this.configurationService.getValue('workbench.settings.openDefaultKeybindings');
// Create as needed and open in editor // Create as needed and open in editor
......
...@@ -31,6 +31,7 @@ import { isEqual, isEqualOrParent, extname, basename, joinPath } from 'vs/base/c ...@@ -31,6 +31,7 @@ import { isEqual, isEqualOrParent, extname, basename, joinPath } from 'vs/base/c
import { onUnexpectedError } from 'vs/base/common/errors'; import { onUnexpectedError } from 'vs/base/common/errors';
import { Schemas } from 'vs/base/common/network'; import { Schemas } from 'vs/base/common/network';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IKeybindingEditingService } from 'vs/workbench/services/keybinding/common/keybindingEditing';
export interface IBackupMetaData { export interface IBackupMetaData {
mtime: number; mtime: number;
...@@ -106,7 +107,8 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil ...@@ -106,7 +107,8 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
@IEnvironmentService private readonly environmentService: IEnvironmentService, @IEnvironmentService private readonly environmentService: IEnvironmentService,
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
@ILogService private readonly logService: ILogService, @ILogService private readonly logService: ILogService,
@IConfigurationService private readonly configurationService: IConfigurationService @IConfigurationService private readonly configurationService: IConfigurationService,
@IKeybindingEditingService private readonly keybindingEditingService: IKeybindingEditingService
) { ) {
super(modelService, modeService); super(modelService, modeService);
...@@ -782,7 +784,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil ...@@ -782,7 +784,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
} }
// Check for keybindings file // Check for keybindings file
if (isEqual(this.resource, this.environmentService.keybindingsResource, !isLinux)) { if (isEqual(this.resource, this.keybindingEditingService.userKeybindingsResource, !isLinux)) {
return 'keybindings'; return 'keybindings';
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册