未验证 提交 71bd9c68 编写于 作者: S Sandeep Somavarapu 提交者: GitHub

Adopt settings and keybindings to use user data filesystem pro… (#76379)

Adopt settings and keybindings to use user data filesystem provider
......@@ -337,11 +337,13 @@ export class WindowsService extends Disposable implements IWindowsService, IURLH
console[severity].apply(console, ...messages);
}
async showItemInFolder(path: URI): Promise<void> {
async showItemInFolder(resource: URI): Promise<void> {
this.logService.trace('windowsService#showItemInFolder');
if (path.scheme === Schemas.file) {
shell.showItemInFolder(path.fsPath);
if (resource.scheme === Schemas.file) {
shell.showItemInFolder(resource.fsPath);
} else if (resource.scheme === Schemas.userData) {
shell.showItemInFolder(resource.path);
}
}
......
......@@ -200,6 +200,7 @@ const copyRelativePathCommand = {
appendEditorTitleContextMenuItem(COPY_PATH_COMMAND_ID, copyPathCommand.title, ResourceContextKey.IsFileSystemResource, '1_cutcopypaste');
appendEditorTitleContextMenuItem(COPY_RELATIVE_PATH_COMMAND_ID, copyRelativePathCommand.title, ResourceContextKey.IsFileSystemResource, '1_cutcopypaste');
appendEditorTitleContextMenuItem(REVEAL_IN_OS_COMMAND_ID, REVEAL_IN_OS_LABEL, ResourceContextKey.Scheme.isEqualTo(Schemas.file));
appendEditorTitleContextMenuItem(REVEAL_IN_OS_COMMAND_ID, REVEAL_IN_OS_LABEL, ContextKeyExpr.and(IsWebContext.toNegated(), ResourceContextKey.Scheme.isEqualTo(Schemas.userData)));
appendEditorTitleContextMenuItem(REVEAL_IN_EXPLORER_COMMAND_ID, nls.localize('revealInSideBar', "Reveal in Side Bar"), ResourceContextKey.IsFileSystemResource);
function appendEditorTitleContextMenuItem(id: string, title: string, when: ContextKeyExpr, group?: string): void {
......
......@@ -9,7 +9,7 @@ import { Event, Emitter } from 'vs/base/common/event';
import * as errors from 'vs/base/common/errors';
import { Disposable, IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
import { RunOnceScheduler } from 'vs/base/common/async';
import { FileChangeType, FileChangesEvent } from 'vs/platform/files/common/files';
import { FileChangeType, FileChangesEvent, IFileService } from 'vs/platform/files/common/files';
import { ConfigurationModel, ConfigurationModelParser } from 'vs/platform/configuration/common/configurationModels';
import { WorkspaceConfigurationModelParser, StandaloneConfigurationModelParser } from 'vs/workbench/services/configuration/common/configurationModels';
import { FOLDER_SETTINGS_PATH, TASKS_CONFIGURATION_KEY, FOLDER_SETTINGS_NAME, LAUNCH_CONFIGURATION_KEY, IConfigurationCache, ConfigurationKey, REMOTE_MACHINE_SCOPES, FOLDER_SCOPES, WORKSPACE_SCOPES, ConfigurationFileService } from 'vs/workbench/services/configuration/common/configuration';
......@@ -24,11 +24,51 @@ import { IConfigurationModel } from 'vs/platform/configuration/common/configurat
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { hash } from 'vs/base/common/hash';
export class UserConfiguration extends Disposable {
private readonly parser: ConfigurationModelParser;
private readonly reloadConfigurationScheduler: RunOnceScheduler;
protected readonly _onDidChangeConfiguration: Emitter<ConfigurationModel> = this._register(new Emitter<ConfigurationModel>());
readonly onDidChangeConfiguration: Event<ConfigurationModel> = this._onDidChangeConfiguration.event;
constructor(
private readonly userSettingsResource: URI,
private readonly scopes: ConfigurationScope[] | undefined,
private readonly fileService: IFileService
) {
super();
this.parser = new ConfigurationModelParser(this.userSettingsResource.toString(), this.scopes);
this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this.reload().then(configurationModel => this._onDidChangeConfiguration.fire(configurationModel)), 50));
this._register(this.fileService.watch(this.userSettingsResource));
this._register(Event.filter(this.fileService.onFileChanges, e => e.contains(this.userSettingsResource))(() => this.reloadConfigurationScheduler.schedule()));
}
async initialize(): Promise<ConfigurationModel> {
return this.reload();
}
async reload(): Promise<ConfigurationModel> {
try {
const content = await this.fileService.readFile(this.userSettingsResource);
this.parser.parseContent(content.value.toString() || '{}');
return this.parser.configurationModel;
} catch (e) {
return new ConfigurationModel();
}
}
reprocess(): ConfigurationModel {
this.parser.parse();
return this.parser.configurationModel;
}
}
export class RemoteUserConfiguration extends Disposable {
private readonly _cachedConfiguration: CachedRemoteUserConfiguration;
private readonly _configurationFileService: ConfigurationFileService;
private _userConfiguration: UserConfiguration | CachedRemoteUserConfiguration;
private _userConfiguration: FileServiceBasedRemoteUserConfiguration | CachedRemoteUserConfiguration;
private _userConfigurationInitializationPromise: Promise<ConfigurationModel> | null = null;
private readonly _onDidChangeConfiguration: Emitter<ConfigurationModel> = this._register(new Emitter<ConfigurationModel>());
......@@ -45,7 +85,7 @@ export class RemoteUserConfiguration extends Disposable {
this._userConfiguration = this._cachedConfiguration = new CachedRemoteUserConfiguration(remoteAuthority, configurationCache);
remoteAgentService.getEnvironment().then(async environment => {
if (environment) {
const userConfiguration = this._register(new UserConfiguration(environment.settingsPath, REMOTE_MACHINE_SCOPES, this._configurationFileService));
const userConfiguration = this._register(new FileServiceBasedRemoteUserConfiguration(environment.settingsPath, REMOTE_MACHINE_SCOPES, this._configurationFileService));
this._register(userConfiguration.onDidChangeConfiguration(configurationModel => this.onDidUserConfigurationChange(configurationModel)));
this._userConfigurationInitializationPromise = userConfiguration.initialize();
const configurationModel = await this._userConfigurationInitializationPromise;
......@@ -57,7 +97,7 @@ export class RemoteUserConfiguration extends Disposable {
}
async initialize(): Promise<ConfigurationModel> {
if (this._userConfiguration instanceof UserConfiguration) {
if (this._userConfiguration instanceof FileServiceBasedRemoteUserConfiguration) {
return this._userConfiguration.initialize();
}
......@@ -90,7 +130,7 @@ export class RemoteUserConfiguration extends Disposable {
}
}
export class UserConfiguration extends Disposable {
class FileServiceBasedRemoteUserConfiguration extends Disposable {
private readonly parser: ConfigurationModelParser;
private readonly reloadConfigurationScheduler: RunOnceScheduler;
......
......@@ -81,7 +81,7 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
this.configurationFileService = new ConfigurationFileService(fileService);
this._configuration = new Configuration(this.defaultConfiguration, new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ResourceMap(), new ConfigurationModel(), new ResourceMap<ConfigurationModel>(), this.workspace);
this.cachedFolderConfigs = new ResourceMap<FolderConfiguration>();
this.localUserConfiguration = this._register(new UserConfiguration(environmentService.settingsResource, remoteAuthority ? LOCAL_MACHINE_SCOPES : undefined, this.configurationFileService));
this.localUserConfiguration = this._register(new UserConfiguration(environmentService.settingsResource, remoteAuthority ? LOCAL_MACHINE_SCOPES : undefined, fileService));
this._register(this.localUserConfiguration.onDidChangeConfiguration(userConfiguration => this.onLocalUserConfigurationChanged(userConfiguration)));
if (remoteAuthority) {
this.remoteUserConfiguration = this._register(new RemoteUserConfiguration(remoteAuthority, configurationCache, this.configurationFileService, remoteAgentService));
......
......@@ -71,9 +71,9 @@ export class BrowserWorkbenchEnvironmentService implements IEnvironmentService {
this.configuration.remoteAuthority = configuration.remoteAuthority;
if (remoteUserDataUri) {
this.appSettingsHome = remoteUserDataUri || URI.file('/User').with({ scheme: Schemas.userData });
this.settingsResource = joinPath(this.appSettingsHome, 'settings.json');
this.keybindingsResource = joinPath(this.appSettingsHome, 'keybindings.json');
this.appSettingsHome = remoteUserDataUri;
this.settingsResource = joinPath(this.appSettingsHome, 'settings.json').with({ scheme: Schemas.userData });
this.keybindingsResource = joinPath(this.appSettingsHome, 'keybindings.json').with({ scheme: Schemas.userData });
} else {
const appSettingsHome = URI.file('/User').with({ scheme: Schemas.userData });
this.settingsResource = joinPath(appSettingsHome, 'settings.json');
......
......@@ -6,6 +6,10 @@
import { EnvironmentService } from 'vs/platform/environment/node/environmentService';
import { IWindowConfiguration } from 'vs/platform/windows/common/windows';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { memoize } from 'vs/base/common/decorators';
import { URI } from 'vs/base/common/uri';
import { joinPath } from 'vs/base/common/resources';
import { Schemas } from 'vs/base/common/network';
export class WorkbenchEnvironmentService extends EnvironmentService implements IWorkbenchEnvironmentService {
......@@ -21,4 +25,10 @@ export class WorkbenchEnvironmentService extends EnvironmentService implements I
get configuration(): IWindowConfiguration {
return this._configuration;
}
@memoize
get settingsResource(): URI { return joinPath(this.appSettingsHome, 'settings.json').with({ scheme: Schemas.userData }); }
@memoize
get keybindingsResource(): URI { return joinPath(this.appSettingsHome, 'keybindings.json').with({ scheme: Schemas.userData }); }
}
......@@ -36,11 +36,10 @@ import { MenuRegistry } from 'vs/platform/actions/common/actions';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
// tslint:disable-next-line: import-patterns
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 { 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 { IFileService } from 'vs/platform/files/common/files';
import { parse } from 'vs/base/common/json';
import * as objects from 'vs/base/common/objects';
import { IKeymapService } from 'vs/workbench/services/keybinding/common/keymapInfo';
......@@ -560,12 +559,11 @@ class UserKeybindings extends Disposable {
private _keybindings: IUserFriendlyKeybinding[] = [];
get keybindings(): IUserFriendlyKeybinding[] { return this._keybindings; }
private readonly reloadConfigurationScheduler: RunOnceScheduler;
protected readonly _onDidChange: Emitter<void> = this._register(new Emitter<void>());
readonly onDidChange: Event<void> = this._onDidChange.event;
private fileWatcherDisposable: IDisposable = Disposable.None;
private directoryWatcherDisposable: IDisposable = Disposable.None;
private readonly _onDidChange: Emitter<void> = this._register(new Emitter<void>());
readonly onDidChange: Event<void> = this._onDidChange.event;
constructor(
private readonly keybindingsResource: URI,
......@@ -573,40 +571,16 @@ class UserKeybindings extends Disposable {
) {
super();
this._register(fileService.onFileChanges(e => this.handleFileEvents(e)));
this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this.reload().then(changed => {
if (changed) {
this._onDidChange.fire();
}
}), 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;
this._register(this.fileService.watch(this.keybindingsResource));
this._register(Event.filter(this.fileService.onFileChanges, e => e.contains(this.keybindingsResource))(() => this.reloadConfigurationScheduler.schedule()));
}
async initialize(): Promise<void> {
const exists = await this.fileService.exists(this.keybindingsResource);
this.onResourceExists(exists);
await this.reload();
}
......@@ -621,39 +595,6 @@ class UserKeybindings extends Disposable {
}
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';
......
......@@ -10,6 +10,7 @@ import { IFileService, FileChangesEvent } from 'vs/platform/files/common/files';
import { URI } from 'vs/base/common/uri';
import * as resources from 'vs/base/common/resources';
import { VSBuffer } from 'vs/base/common/buffer';
import { startsWith } from 'vs/base/common/strings';
export class FileUserDataProvider extends Disposable implements IUserDataProvider {
......@@ -28,17 +29,17 @@ export class FileUserDataProvider extends Disposable implements IUserDataProvide
}
private handleFileChanges(event: FileChangesEvent): void {
const changedKeys: string[] = [];
const changedPaths: string[] = [];
for (const change of event.changes) {
if (change.resource.scheme === this.userDataHome.scheme) {
const key = this.toKey(change.resource);
if (key) {
changedKeys.push(key);
const path = this.toPath(change.resource);
if (path) {
changedPaths.push(path);
}
}
}
if (changedKeys.length) {
this._onDidChangeFile.fire(changedKeys);
if (changedPaths.length) {
this._onDidChangeFile.fire(changedPaths);
}
}
......@@ -62,18 +63,20 @@ export class FileUserDataProvider extends Disposable implements IUserDataProvide
async listFiles(path: string): Promise<string[]> {
const result = await this.fileService.resolve(this.toResource(path));
return result.children ? result.children.map(c => this.toKey(c.resource)!) : [];
return result.children ? result.children.map(c => this.toPath(c.resource)!) : [];
}
deleteFile(path: string): Promise<void> {
return this.fileService.del(this.toResource(path));
}
private toResource(key: string): URI {
return resources.joinPath(this.userDataHome, ...key.split('/'));
private toResource(path: string): URI {
return resources.joinPath(this.userDataHome, path);
}
private toKey(resource: URI): string | undefined {
return resources.relativePath(this.userDataHome, resource);
private toPath(resource: URI): string | undefined {
const resourcePath = resource.toString();
const userDataHomePath = this.userDataHome.toString();
return startsWith(resourcePath, userDataHomePath) ? resourcePath.substr(userDataHomePath.length + 1) : undefined;
}
}
\ No newline at end of file
......@@ -8,8 +8,8 @@ import { FileSystemProviderCapabilities, FileWriteOptions, IStat, FileType, File
import { IUserDataProvider } from 'vs/workbench/services/userData/common/userData';
import { URI } from 'vs/base/common/uri';
import { Event, Emitter } from 'vs/base/common/event';
import * as resources from 'vs/base/common/resources';
import { TernarySearchTree } from 'vs/base/common/map';
import { startsWith } from 'vs/base/common/strings';
export class UserDataFileSystemProvider extends Disposable implements IFileSystemProviderWithFileReadWriteCapability {
......@@ -85,7 +85,9 @@ export class UserDataFileSystemProvider extends Disposable implements IFileSyste
}
private toPath(resource: URI): string | undefined {
return resources.relativePath(this.userDataHome, resource);
const resourcePath = resource.toString();
const userDataHomePath = this.userDataHome.toString();
return startsWith(resourcePath, userDataHomePath) ? resourcePath.substr(userDataHomePath.length + 1) : undefined;
}
}
......@@ -105,8 +107,8 @@ class UserDataChangesEvent {
return this._pathsTree;
}
contains(keyOrSegment: string): boolean {
return this.pathsTree.findSubstr(keyOrSegment) !== undefined;
contains(pathOrSegment: string): boolean {
return this.pathsTree.findSubstr(pathOrSegment) !== undefined;
}
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册