From 4c2ad5ea5670a0071410d71ac1782c29f0bb90f8 Mon Sep 17 00:00:00 2001 From: Akos Kitta Date: Tue, 25 Feb 2020 13:42:40 +0100 Subject: [PATCH] Use the config dir for all settings and extensions Closes #4488. Signed-off-by: Akos Kitta --- packages/core/src/browser/label-provider.ts | 2 +- .../preferences/preference-configurations.ts | 13 +---- .../test/mock-preference-provider.ts | 5 -- .../browser/test/mock-env-variables-server.ts | 20 +++---- .../env-variables/env-variables-protocol.ts | 6 +- .../env-variables/env-variables-server.ts | 31 ++-------- packages/core/src/node/file-uri.ts | 3 - .../browser/debug-configuration-manager.ts | 2 +- packages/java/src/node/java-contribution.ts | 4 +- .../plugin-ext/src/common/plugin-api-rpc.ts | 9 ++- .../src/hosted/browser/hosted-plugin.ts | 28 ++++++--- .../plugin-ext/src/main/browser/env-main.ts | 16 +---- .../main/node/paths/plugin-paths-service.ts | 24 ++++---- .../main/node/plugins-key-value-storage.ts | 19 +++--- .../src/plugin/env-variables-server-ext.ts | 58 ------------------- .../browser/folders-preferences-provider.ts | 8 +-- .../src/browser/preferences-tree-widget.ts | 7 +-- .../src/browser/task-configuration-manager.ts | 2 +- .../task/src/browser/task-configurations.ts | 19 +----- .../user-storage-service-filesystem.spec.ts | 39 ++++++------- .../user-storage-service-filesystem.ts | 8 ++- .../src/browser/quick-open-workspace.ts | 9 ++- .../src/browser/workspace-service.spec.ts | 6 +- .../src/browser/workspace-service.ts | 4 +- packages/workspace/src/common/utils.ts | 13 ++--- .../src/node/default-workspace-server.ts | 26 +++++---- 26 files changed, 131 insertions(+), 250 deletions(-) delete mode 100644 packages/plugin-ext/src/plugin/env-variables-server-ext.ts diff --git a/packages/core/src/browser/label-provider.ts b/packages/core/src/browser/label-provider.ts index ec548bce8f89c..055dd4b843b31 100644 --- a/packages/core/src/browser/label-provider.ts +++ b/packages/core/src/browser/label-provider.ts @@ -78,7 +78,7 @@ export interface LabelProviderContribution { /** * Check whether the given element is affected by the given change event. * Contributions delegating to the label provider can use this hook - * to perfrom a recursive check. + * to perform a recursive check. */ affects?(element: object, event: DidChangeLabelEvent): boolean; diff --git a/packages/core/src/browser/preferences/preference-configurations.ts b/packages/core/src/browser/preferences/preference-configurations.ts index 8615e0ab56383..ada87a389c0c3 100644 --- a/packages/core/src/browser/preferences/preference-configurations.ts +++ b/packages/core/src/browser/preferences/preference-configurations.ts @@ -17,7 +17,6 @@ import { injectable, inject, named, interfaces } from 'inversify'; import URI from '../../common/uri'; import { ContributionProvider, bindContributionProvider } from '../../common/contribution-provider'; -import { EnvVariablesServer } from '../../common/env-variables'; export const PreferenceConfiguration = Symbol('PreferenceConfiguration'); export interface PreferenceConfiguration { @@ -35,12 +34,9 @@ export class PreferenceConfigurations { @inject(ContributionProvider) @named(PreferenceConfiguration) protected readonly provider: ContributionProvider; - @inject(EnvVariablesServer) - protected readonly envServer: EnvVariablesServer; - /* prefer Theia over VS Code by default */ - async getPaths(): Promise { - return [await this.envServer.getDataFolderName(), '.vscode']; + getPaths(): string[] { + return ['.theia', '.vscode']; } getConfigName(): string { @@ -75,10 +71,7 @@ export class PreferenceConfigurations { return configUri.parent.path.base; } - async createUri(folder: URI, configPath: string, configName: string = this.getConfigName()): Promise { - if (!configPath) { - configPath = (await this.getPaths())[0]; - } + createUri(folder: URI, configPath: string = this.getPaths()[0], configName: string = this.getConfigName()): URI { return folder.resolve(configPath).resolve(configName + '.json'); } diff --git a/packages/core/src/browser/preferences/test/mock-preference-provider.ts b/packages/core/src/browser/preferences/test/mock-preference-provider.ts index ef65b265e6878..c0c892167bfaa 100644 --- a/packages/core/src/browser/preferences/test/mock-preference-provider.ts +++ b/packages/core/src/browser/preferences/test/mock-preference-provider.ts @@ -20,8 +20,6 @@ import { interfaces } from 'inversify'; import { PreferenceProvider } from '../'; import { PreferenceScope } from '../preference-scope'; import { PreferenceProviderDataChanges, PreferenceProviderDataChange } from '../preference-provider'; -import { EnvVariablesServer } from '../../../common/env-variables/env-variables-protocol'; -import { MockEnvVariablesServerImpl } from '../../test/mock-env-variables-server'; export class MockPreferenceProvider extends PreferenceProvider { readonly prefs: { [p: string]: any } = {}; @@ -52,9 +50,6 @@ export class MockPreferenceProvider extends PreferenceProvider { export function bindMockPreferenceProviders(bind: interfaces.Bind, unbind: interfaces.Unbind): void { unbind(PreferenceProvider); - // Needed for PreferenceConfigurations in PreferenceSchemaProvider - bind(EnvVariablesServer).to(MockEnvVariablesServerImpl).inSingletonScope(); - bind(PreferenceProvider).toDynamicValue(ctx => new MockPreferenceProvider(PreferenceScope.User)).inSingletonScope().whenTargetNamed(PreferenceScope.User); bind(PreferenceProvider).toDynamicValue(ctx => new MockPreferenceProvider(PreferenceScope.Workspace)).inSingletonScope().whenTargetNamed(PreferenceScope.Workspace); bind(PreferenceProvider).toDynamicValue(ctx => new MockPreferenceProvider(PreferenceScope.Folder)).inSingletonScope().whenTargetNamed(PreferenceScope.Folder); diff --git a/packages/core/src/browser/test/mock-env-variables-server.ts b/packages/core/src/browser/test/mock-env-variables-server.ts index 7cf770a5ec0d4..350bb243e8806 100644 --- a/packages/core/src/browser/test/mock-env-variables-server.ts +++ b/packages/core/src/browser/test/mock-env-variables-server.ts @@ -14,33 +14,27 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { injectable } from 'inversify'; +import URI from '../../common/uri'; import { EnvVariablesServer, EnvVariable } from '../../common/env-variables'; -@injectable() export class MockEnvVariablesServerImpl implements EnvVariablesServer { - async getDataFolderName(): Promise { - return '.theia'; - } - async getUserHomeFolder(): Promise { - return 'file:///home/test'; - } + constructor(protected readonly configDirUri: URI) { } - async getUserDataFolder(): Promise { - return 'file:///home/test/.theia'; + async getConfigDirUri(): Promise { + return this.configDirUri.toString(); } getExecPath(): Promise { throw new Error('Method not implemented.'); } + getVariables(): Promise { throw new Error('Method not implemented.'); } + getValue(key: string): Promise { throw new Error('Method not implemented.'); } - getAppDataFolder(): Promise { - throw new Error('Method not implemented.'); - } + } diff --git a/packages/core/src/common/env-variables/env-variables-protocol.ts b/packages/core/src/common/env-variables/env-variables-protocol.ts index f60629f2dc56a..167f0b2a2e1e9 100644 --- a/packages/core/src/common/env-variables/env-variables-protocol.ts +++ b/packages/core/src/common/env-variables/env-variables-protocol.ts @@ -21,11 +21,7 @@ export interface EnvVariablesServer { getExecPath(): Promise getVariables(): Promise getValue(key: string): Promise - getUserHomeFolder(): Promise - getDataFolderName(): Promise - getUserDataFolder(): Promise - /** Windows specific. Returns system data folder of Theia. On other than Windows systems is the same as getUserDataFolder */ - getAppDataFolder(): Promise + getConfigDirUri(): Promise; } export interface EnvVariable { diff --git a/packages/core/src/node/env-variables/env-variables-server.ts b/packages/core/src/node/env-variables/env-variables-server.ts index 128e5792b9257..165fc05c3d2d9 100644 --- a/packages/core/src/node/env-variables/env-variables-server.ts +++ b/packages/core/src/node/env-variables/env-variables-server.ts @@ -14,21 +14,18 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import * as os from 'os'; +import { join } from 'path'; +import { homedir } from 'os'; import { injectable } from 'inversify'; import { EnvVariable, EnvVariablesServer } from '../../common/env-variables'; import { isWindows } from '../../common/os'; import { FileUri } from '../file-uri'; -const THEIA_DATA_FOLDER = '.theia'; - -const WINDOWS_APP_DATA_DIR = 'AppData'; -const WINDOWS_ROAMING_DIR = 'Roaming'; - @injectable() export class EnvVariablesServerImpl implements EnvVariablesServer { protected readonly envs: { [key: string]: EnvVariable } = {}; + protected readonly configDirUri = FileUri.create(join(homedir(), '.theia')).toString(); constructor() { const prEnv = process.env; @@ -52,26 +49,8 @@ export class EnvVariablesServerImpl implements EnvVariablesServer { return this.envs[key]; } - async getUserHomeFolder(): Promise { - return FileUri.create(os.homedir()).toString(); - } - - async getDataFolderName(): Promise { - return THEIA_DATA_FOLDER; - } - - async getUserDataFolder(): Promise { - return FileUri.create(await this.getUserHomeFolder()).resolve(await this.getDataFolderName()).toString(); - } - - async getAppDataFolder(): Promise { - const dataFolderUriBuilder = FileUri.create(await this.getUserHomeFolder()); - if (isWindows) { - dataFolderUriBuilder.resolve(WINDOWS_APP_DATA_DIR); - dataFolderUriBuilder.resolve(WINDOWS_ROAMING_DIR); - } - dataFolderUriBuilder.resolve(await this.getDataFolderName()); - return dataFolderUriBuilder.toString(); + async getConfigDirUri(): Promise { + return this.configDirUri; } } diff --git a/packages/core/src/node/file-uri.ts b/packages/core/src/node/file-uri.ts index 3915620d8ce47..8878c6ae27539 100644 --- a/packages/core/src/node/file-uri.ts +++ b/packages/core/src/node/file-uri.ts @@ -54,9 +54,6 @@ export namespace FileUri { return fsPathFromVsCodeUri + '\\'; } } - if (fsPathFromVsCodeUri.startsWith('/file:')) { - return fsPathFromVsCodeUri.substring('/file:'.length); - } return fsPathFromVsCodeUri; } } diff --git a/packages/debug/src/browser/debug-configuration-manager.ts b/packages/debug/src/browser/debug-configuration-manager.ts index 3aec103b5310e..ce25e327a44ac 100644 --- a/packages/debug/src/browser/debug-configuration-manager.ts +++ b/packages/debug/src/browser/debug-configuration-manager.ts @@ -268,7 +268,7 @@ export class DebugConfigurationManager { if (configUri && configUri.path.base === 'launch.json') { uri = configUri; } else { // fallback - uri = new URI(model.workspaceFolderUri).resolve((await this.preferenceConfigurations.getPaths())[0] + '/launch.json'); + uri = new URI(model.workspaceFolderUri).resolve(`${this.preferenceConfigurations.getPaths()[0]}/launch.json`); } const debugType = await this.selectDebugType(); const configurations = debugType ? await this.provideDebugConfigurations(debugType, model.workspaceFolderUri) : []; diff --git a/packages/java/src/node/java-contribution.ts b/packages/java/src/node/java-contribution.ts index 727856fa2b552..217a72f56b370 100644 --- a/packages/java/src/node/java-contribution.ts +++ b/packages/java/src/node/java-contribution.ts @@ -104,7 +104,9 @@ export class JavaContribution extends BaseLanguageServerContribution { this.activeDataFolders.add(dataFolderSuffix); clientConnection.onClose(() => this.activeDataFolders.delete(dataFolderSuffix)); - const workspacePath = path.resolve(FileUri.fsPath(await this.envServer.getUserDataFolder()), 'jdt.ls', '_ws_' + dataFolderSuffix); + const configDirUri = await this.envServer.getConfigDirUri(); + const configDirFsPath = FileUri.fsPath(configDirUri); + const workspacePath = path.resolve(configDirFsPath, 'jdt.ls', '_ws_' + dataFolderSuffix); const configuration = configurations.get(process.platform); if (!configuration) { throw new Error('Cannot find Java server configuration for ' + process.platform); diff --git a/packages/plugin-ext/src/common/plugin-api-rpc.ts b/packages/plugin-ext/src/common/plugin-api-rpc.ts index f83c55037757e..24a0dbe835be2 100644 --- a/packages/plugin-ext/src/common/plugin-api-rpc.ts +++ b/packages/plugin-ext/src/common/plugin-api-rpc.ts @@ -993,13 +993,12 @@ export interface DocumentsMain { export interface EnvMain { $getEnvVariable(envVarName: string): Promise; - $getAllEnvVariables(): Promise $getClientOperatingSystem(): Promise; +} +export interface EnvExt { $getExecPath(): Promise - $getUserHomeFolderPath(): Promise - $getDataFolderName(): Promise - $getUserDataFolderPath(): Promise - $getAppDataPath(): Promise + $getConfigDirUri(): Promise + $getAllEnvVariables(): Promise } export interface PreferenceRegistryMain { diff --git a/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts b/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts index 12a919fd8ed70..9eecff647dc2b 100644 --- a/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts +++ b/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts @@ -33,7 +33,7 @@ import { RPCProtocol, RPCProtocolImpl } from '../../common/rpc-protocol'; import { Disposable, DisposableCollection, ILogger, ContributionProvider, CommandRegistry, WillExecuteCommandEvent, - CancellationTokenSource, JsonRpcProxy, ProgressService, Path + CancellationTokenSource, JsonRpcProxy, ProgressService } from '@theia/core'; import { PreferenceServiceImpl, PreferenceProviderProvider } from '@theia/core/lib/browser/preferences'; import { WorkspaceService } from '@theia/workspace/lib/browser'; @@ -58,6 +58,7 @@ import { WebviewWidget } from '../../main/browser/webview/webview'; import { WidgetManager } from '@theia/core/lib/browser/widget-manager'; import { TerminalService } from '@theia/terminal/lib/browser/base/terminal-service'; import { EnvVariablesServer } from '@theia/core/lib/common/env-variables'; +import URI from '@theia/core/lib/common/uri'; export type PluginHost = 'frontend' | string; export type DebugActivationEvent = 'onDebugResolve' | 'onDebugInitialConfigurations' | 'onDebugAdapterProtocolTracker'; @@ -348,9 +349,9 @@ export class HostedPluginSupport { } const thenable: Promise[] = []; const configStorage: ConfigStorage = { - hostLogPath: hostLogPath!, - hostStoragePath: hostStoragePath, - hostGlobalStoragePath: hostGlobalStoragePath! + hostLogPath, + hostStoragePath, + hostGlobalStoragePath }; for (const [host, hostContributions] of contributionsByHost) { const manager = await this.obtainManager(host, hostContributions, toDisconnect); @@ -470,15 +471,24 @@ export class HostedPluginSupport { } protected async getHostGlobalStoragePath(): Promise { - const userDataFolderPath: string = (await this.fileSystem.getFsPath(await this.envServer.getUserDataFolder()))!; - const globalStorageFolderPath = new Path(userDataFolderPath).join('globalStorage').toString(); + const configDirUri = await this.envServer.getConfigDirUri(); + const globalStorageFolderUri = new URI(configDirUri).resolve('globalStorage').toString(); // Make sure that folder by the path exists - if (! await this.fileSystem.exists(globalStorageFolderPath)) { - await this.fileSystem.createFolder(globalStorageFolderPath); + if (!await this.fileSystem.exists(globalStorageFolderUri)) { + await this.fileSystem.createFolder(globalStorageFolderUri); } - return globalStorageFolderPath; + return new Promise((resolve, reject) => { + this.fileSystem.getFsPath(globalStorageFolderUri) + .then(fsPath => { + if (fsPath) { + resolve(fsPath); + } else { + reject(new Error(`Could not resolve the FS path for URI: ${globalStorageFolderUri}`)); + } + }); + }); } async activateByEvent(activationEvent: string): Promise { diff --git a/packages/plugin-ext/src/main/browser/env-main.ts b/packages/plugin-ext/src/main/browser/env-main.ts index 5bd05e7870581..35f8d09f3a05a 100644 --- a/packages/plugin-ext/src/main/browser/env-main.ts +++ b/packages/plugin-ext/src/main/browser/env-main.ts @@ -23,7 +23,6 @@ import { isWindows, isOSX } from '@theia/core'; import { OperatingSystem } from '../../plugin/types-impl'; export class EnvMainImpl implements EnvMain { - private envVariableServer: EnvVariablesServer; constructor(rpc: RPCProtocol, container: interfaces.Container) { @@ -52,21 +51,10 @@ export class EnvMainImpl implements EnvMain { return this.envVariableServer.getExecPath(); } - $getUserHomeFolderPath(): Promise { - return this.envVariableServer.getUserHomeFolder(); - } - - $getDataFolderName(): Promise { - return this.envVariableServer.getDataFolderName(); + $getConfigDirUri(): Promise { + return this.envVariableServer.getConfigDirUri(); } - $getUserDataFolderPath(): Promise { - return this.envVariableServer.getUserDataFolder(); - } - - $getAppDataPath(): Promise { - return this.envVariableServer.getAppDataFolder(); - } } /** diff --git a/packages/plugin-ext/src/main/node/paths/plugin-paths-service.ts b/packages/plugin-ext/src/main/node/paths/plugin-paths-service.ts index 8c8a15c45cd56..12532d521f7a1 100644 --- a/packages/plugin-ext/src/main/node/paths/plugin-paths-service.ts +++ b/packages/plugin-ext/src/main/node/paths/plugin-paths-service.ts @@ -20,8 +20,8 @@ import * as path from 'path'; import { readdir, remove } from 'fs-extra'; import * as crypto from 'crypto'; import URI from '@theia/core/lib/common/uri'; -import { FileUri } from '@theia/core/lib/node'; import { ILogger } from '@theia/core'; +import { FileUri } from '@theia/core/lib/node'; import { PluginPaths } from './const'; import { PluginPathsService } from '../../common/plugin-paths-protocol'; import { THEIA_EXT, VSCODE_EXT, getTemporaryWorkspaceFileUri } from '@theia/workspace/lib/common'; @@ -85,7 +85,7 @@ export class PluginPathsServiceImpl implements PluginPathsService { } protected async buildWorkspaceId(workspace: FileStat, roots: FileStat[]): Promise { - const untitledWorkspace = getTemporaryWorkspaceFileUri(await this.envServer.getUserDataFolder()); + const untitledWorkspace = await getTemporaryWorkspaceFileUri(this.envServer); if (untitledWorkspace.toString() === workspace.uri) { // if workspace is temporary @@ -104,16 +104,6 @@ export class PluginPathsServiceImpl implements PluginPathsService { } } - private async getLogsDirPath(): Promise { - const theiaDirPath = FileUri.fsPath(await this.envServer.getUserDataFolder()); - return path.join(theiaDirPath, PluginPaths.PLUGINS_LOGS_DIR); - } - - private async getWorkspaceStorageDirPath(): Promise { - const theiaDirPath = FileUri.fsPath(await this.envServer.getUserDataFolder()); - return path.join(theiaDirPath, PluginPaths.PLUGINS_WORKSPACE_STORAGE_DIR); - } - /** * Generate time folder name in format: YYYYMMDDTHHMMSS, for example: 20181205T093828 */ @@ -127,6 +117,16 @@ export class PluginPathsServiceImpl implements PluginPathsService { return timeStamp; } + private async getLogsDirPath(): Promise { + const configDirUri = await this.envServer.getConfigDirUri(); + return path.join(FileUri.fsPath(configDirUri), PluginPaths.PLUGINS_LOGS_DIR); + } + + private async getWorkspaceStorageDirPath(): Promise { + const configDirUri = await this.envServer.getConfigDirUri(); + return path.join(FileUri.fsPath(configDirUri), PluginPaths.PLUGINS_WORKSPACE_STORAGE_DIR); + } + private async cleanupOldLogs(parentLogsDir: string): Promise { // @ts-ignore - fs-extra types (Even latest version) is not updated with the `withFileTypes` option. const dirEntries = await readdir(parentLogsDir, { withFileTypes: true }); diff --git a/packages/plugin-ext/src/main/node/plugins-key-value-storage.ts b/packages/plugin-ext/src/main/node/plugins-key-value-storage.ts index b2acde3cb5996..e52bde12afb93 100644 --- a/packages/plugin-ext/src/main/node/plugins-key-value-storage.ts +++ b/packages/plugin-ext/src/main/node/plugins-key-value-storage.ts @@ -19,7 +19,6 @@ import * as fs from 'fs-extra'; import * as path from 'path'; import { FileUri } from '@theia/core/lib/node/file-uri'; import { Deferred } from '@theia/core/lib/common/promise-util'; -import { FileSystem } from '@theia/filesystem/lib/common'; import { EnvVariablesServer } from '@theia/core/lib/common/env-variables'; import { PluginPaths } from './paths/const'; import { PluginPathsService } from '../common/plugin-paths-protocol'; @@ -37,19 +36,19 @@ export class PluginsKeyValueStorage { @inject(EnvVariablesServer) protected readonly envServer: EnvVariablesServer; - @inject(FileSystem) - protected readonly fileSystem: FileSystem; - @postConstruct() protected async init(): Promise { try { - const theiaDataFolderPath = FileUri.fsPath(await this.envServer.getUserDataFolder()); - await this.fileSystem.createFolder(theiaDataFolderPath); - const globalDataPath = path.join(theiaDataFolderPath, PluginPaths.PLUGINS_GLOBAL_STORAGE_DIR, 'global-state.json'); - await this.fileSystem.createFolder(path.dirname(globalDataPath)); - this.deferredGlobalDataPath.resolve(globalDataPath); + const configDirUri = await this.envServer.getConfigDirUri(); + const globalStorageFsPath = path.join(FileUri.fsPath(configDirUri), PluginPaths.PLUGINS_GLOBAL_STORAGE_DIR); + const exists = await fs.pathExists(globalStorageFsPath); + if (!exists) { + await fs.mkdirs(globalStorageFsPath); + } + const globalDataFsPath = path.join(globalStorageFsPath, 'global-state.json'); + this.deferredGlobalDataPath.resolve(globalDataFsPath); } catch (e) { - console.error('Faild to initialize global state path: ', e); + console.error('Failed to initialize global state path: ', e); this.deferredGlobalDataPath.resolve(undefined); } } diff --git a/packages/plugin-ext/src/plugin/env-variables-server-ext.ts b/packages/plugin-ext/src/plugin/env-variables-server-ext.ts deleted file mode 100644 index 19872df120c32..0000000000000 --- a/packages/plugin-ext/src/plugin/env-variables-server-ext.ts +++ /dev/null @@ -1,58 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2019 Red Hat, Inc. and others. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * This Source Code may also be made available under the following Secondary - * Licenses when the conditions for such availability set forth in the Eclipse - * Public License v. 2.0 are satisfied: GNU General Public License, version 2 - * with the GNU Classpath Exception which is available at - * https://www.gnu.org/software/classpath/license.html. - * - * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 - ********************************************************************************/ - -import { RPCProtocol } from '../common/rpc-protocol'; -import { PLUGIN_RPC_CONTEXT, EnvMain } from '../common'; -import { EnvVariablesServer, EnvVariable } from '@theia/core/lib/common/env-variables'; - -export class EnvVariablesServerExt implements EnvVariablesServer { - - protected readonly proxy: EnvMain; - - constructor(rpc: RPCProtocol) { - this.proxy = rpc.getProxy(PLUGIN_RPC_CONTEXT.ENV_MAIN); - } - - getExecPath(): Promise { - return this.proxy.$getExecPath(); - } - - getVariables(): Promise { - return this.proxy.$getAllEnvVariables(); - } - - async getValue(name: string): Promise { - const value = await this.proxy.$getEnvVariable(name); - return { name, value }; - } - - getUserHomeFolder(): Promise { - return this.proxy.$getUserHomeFolderPath(); - } - - getDataFolderName(): Promise { - return this.proxy.$getDataFolderName(); - } - - getUserDataFolder(): Promise { - return this.proxy.$getUserDataFolderPath(); - } - - getAppDataFolder(): Promise { - return this.proxy.$getAppDataPath(); - } - -} diff --git a/packages/preferences/src/browser/folders-preferences-provider.ts b/packages/preferences/src/browser/folders-preferences-provider.ts index d3187249fe86e..48a3014decbd2 100644 --- a/packages/preferences/src/browser/folders-preferences-provider.ts +++ b/packages/preferences/src/browser/folders-preferences-provider.ts @@ -41,7 +41,7 @@ export class FoldersPreferencesProvider extends PreferenceProvider { protected async init(): Promise { await this.workspaceService.roots; - await this.updateProviders(); + this.updateProviders(); this.workspaceService.onWorkspaceChanged(() => this.updateProviders()); const readyPromises: Promise[] = []; @@ -51,13 +51,13 @@ export class FoldersPreferencesProvider extends PreferenceProvider { Promise.all(readyPromises).then(() => this._ready.resolve()); } - protected async updateProviders(): Promise { + protected updateProviders(): void { const roots = this.workspaceService.tryGetRoots(); const toDelete = new Set(this.providers.keys()); for (const folder of roots) { - for (const configPath of await this.configurations.getPaths()) { + for (const configPath of this.configurations.getPaths()) { for (const configName of [...this.configurations.getSectionNames(), this.configurations.getConfigName()]) { - const configUri = await this.configurations.createUri(new URI(folder.uri), configPath, configName); + const configUri = this.configurations.createUri(new URI(folder.uri), configPath, configName); const key = configUri.toString(); toDelete.delete(key); if (!this.providers.has(key)) { diff --git a/packages/preferences/src/browser/preferences-tree-widget.ts b/packages/preferences/src/browser/preferences-tree-widget.ts index 2135de3c9e33e..ba525717713d4 100644 --- a/packages/preferences/src/browser/preferences-tree-widget.ts +++ b/packages/preferences/src/browser/preferences-tree-widget.ts @@ -454,7 +454,8 @@ export class PreferencesEditorsContainer extends DockPanel { let uri = preferenceUri; if (preferenceUri.scheme === UserStorageUri.SCHEME && homeUri) { - uri = homeUri.resolve(await this.envServer.getDataFolderName()).resolve(preferenceUri.path); + const configDirUri = await this.envServer.getConfigDirUri(); + uri = new URI(configDirUri).resolve(preferenceUri.path); } return homeUri ? FileSystemUtils.tildifyPath(uri.path.toString(), homeUri.path.toString()) @@ -473,8 +474,6 @@ export class PreferencesTreeWidget extends TreeWidget { private readonly onPreferenceSelectedEmitter: Emitter<{ [key: string]: string }>; readonly onPreferenceSelected: Event<{ [key: string]: string }>; - protected readonly toDispose: DisposableCollection; - @inject(PreferencesMenuFactory) protected readonly preferencesMenuFactory: PreferencesMenuFactory; @inject(PreferenceService) protected readonly preferenceService: PreferenceService; @inject(PreferencesDecorator) protected readonly decorator: PreferencesDecorator; @@ -490,14 +489,12 @@ export class PreferencesTreeWidget extends TreeWidget { this.onPreferenceSelectedEmitter = new Emitter<{ [key: string]: string }>(); this.onPreferenceSelected = this.onPreferenceSelectedEmitter.event; - this.toDispose = new DisposableCollection(); this.toDispose.push(this.onPreferenceSelectedEmitter); this.id = PreferencesTreeWidget.ID; } dispose(): void { - this.toDispose.dispose(); super.dispose(); } diff --git a/packages/task/src/browser/task-configuration-manager.ts b/packages/task/src/browser/task-configuration-manager.ts index d0a41a8fae06d..b4fed15ea5a43 100644 --- a/packages/task/src/browser/task-configuration-manager.ts +++ b/packages/task/src/browser/task-configuration-manager.ts @@ -171,7 +171,7 @@ export class TaskConfigurationManager { if (configUri && configUri.path.base === 'tasks.json') { uri = configUri; } else { // fallback - uri = new URI(model.workspaceFolderUri).resolve((await this.preferenceConfigurations.getPaths())[0] + '/tasks.json'); + uri = new URI(model.workspaceFolderUri).resolve(`${this.preferenceConfigurations.getPaths()[0]}/tasks.json`); } const fileStat = await this.filesystem.getFileStat(uri.toString()); diff --git a/packages/task/src/browser/task-configurations.ts b/packages/task/src/browser/task-configurations.ts index d926c01522adf..e33d5ffb2724f 100644 --- a/packages/task/src/browser/task-configurations.ts +++ b/packages/task/src/browser/task-configurations.ts @@ -33,7 +33,6 @@ import URI from '@theia/core/lib/common/uri'; import { FileChange, FileChangeType } from '@theia/filesystem/lib/common/filesystem-watcher-protocol'; import { WorkspaceService } from '@theia/workspace/lib/browser'; import { OpenerService } from '@theia/core/lib/browser'; -import { EnvVariablesServer } from '@theia/core/lib/common/env-variables'; export interface TaskConfigurationClient { /** @@ -60,11 +59,6 @@ export class TaskConfigurations implements Disposable { */ protected taskCustomizationMap = new Map(); - /** last directory element under which we look for task config */ - protected taskFilePath: string; - /** task configuration file name */ - protected readonly TASKFILE = 'tasks.json'; - protected client: TaskConfigurationClient | undefined = undefined; /** @@ -97,9 +91,6 @@ export class TaskConfigurations implements Disposable { @inject(TaskSourceResolver) protected readonly taskSourceResolver: TaskSourceResolver; - @inject(EnvVariablesServer) - protected readonly envServer: EnvVariablesServer; - constructor() { this.toDispose.push(Disposable.create(() => { this.tasksMap.clear(); @@ -110,7 +101,7 @@ export class TaskConfigurations implements Disposable { } @postConstruct() - protected async init(): Promise { + protected init(): void { this.toDispose.push( this.taskConfigurationManager.onDidChangeTaskConfig(async change => { try { @@ -123,7 +114,6 @@ export class TaskConfigurations implements Disposable { } }) ); - this.taskFilePath = await this.envServer.getDataFolderName(); this.reorganizeTasks(); this.toDispose.push(this.taskSchemaUpdater.onDidChangeTaskSchema(() => this.reorganizeTasks())); } @@ -244,11 +234,6 @@ export class TaskConfigurations implements Disposable { return undefined; } - /** returns the string uri of where the config file would be, if it existed under a given root directory */ - protected getConfigFileUri(rootDir: string): string { - return new URI(rootDir).resolve(this.taskFilePath).resolve(this.TASKFILE).toString(); - } - /** * Called when a change, to a config file we watch, is detected. */ @@ -306,7 +291,7 @@ export class TaskConfigurations implements Disposable { try { await this.taskConfigurationManager.openConfiguration(sourceFolderUri); } catch (e) { - console.error(`Error occurred while opening: ${this.TASKFILE}.`, e); + console.error(`Error occurred while opening 'tasks.json' in ${sourceFolderUri}.`, e); } } diff --git a/packages/userstorage/src/browser/user-storage-service-filesystem.spec.ts b/packages/userstorage/src/browser/user-storage-service-filesystem.spec.ts index f7e9a91aa5151..812614d082b7f 100644 --- a/packages/userstorage/src/browser/user-storage-service-filesystem.spec.ts +++ b/packages/userstorage/src/browser/user-storage-service-filesystem.spec.ts @@ -16,6 +16,7 @@ import { Container } from 'inversify'; import * as chai from 'chai'; +import * as temp from 'temp'; import { UserStorageServiceFilesystemImpl } from './user-storage-service-filesystem'; import { UserStorageService } from './user-storage-service'; import { UserStorageResource } from './user-storage-resource'; @@ -33,6 +34,7 @@ import { EnvVariablesServer } from '@theia/core/lib/common/env-variables'; import { MockEnvVariablesServerImpl } from '@theia/core/lib/browser/test/mock-env-variables-server'; import { UserStorageUri } from './user-storage-uri'; import URI from '@theia/core/lib/common/uri'; +import { FileUri } from '@theia/core/lib/node'; import * as sinon from 'sinon'; @@ -41,9 +43,10 @@ let testContainer: Container; let userStorageService: UserStorageServiceFilesystemImpl; -const homeDir = '/home/test'; -const THEIA_USER_STORAGE_FOLDER = '.theia'; -const userStorageFolder = new URI('file://' + homeDir).resolve(THEIA_USER_STORAGE_FOLDER); +const track = temp.track(); +const userStorageFolder = FileUri.create(track.mkdirSync()); +const envVariableServer = new MockEnvVariablesServerImpl(userStorageFolder); + const mockOnFileChangedEmitter = new Emitter(); let files: { [key: string]: string; } = {}; @@ -82,13 +85,6 @@ before(async () => { testContainer.bind(FileSystem).toDynamicValue(ctx => { const fs = new MockFilesystem(); - sinon.stub(fs, 'getCurrentUserHome').callsFake(() => Promise.resolve( - { - uri: 'file://' + homeDir, - lastModification: 0, - isDirectory: true - })); - sinon.stub(fs, 'resolveContent').callsFake((uri): Promise<{ stat: FileStat, content: string }> => { const content = files[uri]; return Promise.resolve( @@ -107,10 +103,14 @@ before(async () => { return fs; }).inSingletonScope(); - testContainer.bind(EnvVariablesServer).to(MockEnvVariablesServerImpl).inSingletonScope(); + testContainer.bind(EnvVariablesServer).toConstantValue(envVariableServer); testContainer.bind(UserStorageService).to(UserStorageServiceFilesystemImpl); }); +after(() => { + track.cleanupSync(); +}); + describe('User Storage Service (Filesystem implementation)', () => { let testFile: string; before(() => { @@ -128,21 +128,20 @@ describe('User Storage Service (Filesystem implementation)', () => { it('Should return a user storage uri from a filesystem uri', () => { - const test = UserStorageServiceFilesystemImpl.toUserStorageUri(userStorageFolder, new URI('file://' + homeDir + '/' + THEIA_USER_STORAGE_FOLDER + '/' + testFile)); + const test = UserStorageServiceFilesystemImpl.toUserStorageUri(userStorageFolder, userStorageFolder.resolve(testFile)); expect(test.scheme).eq(UserStorageUri.SCHEME); expect(test.toString()).eq(UserStorageUri.SCHEME + ':' + testFile); const testFragment = UserStorageServiceFilesystemImpl. - toUserStorageUri(userStorageFolder, new URI('file://' + homeDir + '/' + THEIA_USER_STORAGE_FOLDER + '/' + testFile + '#test')); + toUserStorageUri(userStorageFolder, userStorageFolder.resolve(testFile).withFragment('test')); expect(testFragment.fragment).eq('test'); const testQuery = UserStorageServiceFilesystemImpl. - toUserStorageUri(userStorageFolder, new URI('file://' + homeDir + '/' + THEIA_USER_STORAGE_FOLDER + '/' + testFile + '?test=1')); + toUserStorageUri(userStorageFolder, userStorageFolder.resolve(testFile).withQuery('test=1')); expect(testQuery.query).eq('test=1'); const testQueryAndFragment = UserStorageServiceFilesystemImpl. - toUserStorageUri(userStorageFolder, new URI('file://' + homeDir + '/' + THEIA_USER_STORAGE_FOLDER + '/' + testFile - + '?test=1' + '#test')); + toUserStorageUri(userStorageFolder, userStorageFolder.resolve(testFile).withQuery('test=1').withFragment('test')); expect(testQueryAndFragment.fragment).eq('test'); expect(testQueryAndFragment.query).eq('test=1'); }); @@ -151,10 +150,10 @@ describe('User Storage Service (Filesystem implementation)', () => { const test = UserStorageServiceFilesystemImpl.toFilesystemURI(userStorageFolder, new URI(UserStorageUri.SCHEME + ':' + testFile)); expect(test.scheme).eq('file'); - expect(test.path.toString()).eq(homeDir + '/' + THEIA_USER_STORAGE_FOLDER + '/' + testFile); + expect(test.toString()).eq(userStorageFolder.resolve(testFile).toString()); }); - it('Should register a client and notifies it of the fs changesby converting them to user storage changes', done => { + it('Should register a client and notifies it of the fs changes by converting them to user storage changes', done => { userStorageService.onUserStorageChanged(event => { const userStorageUri = event.uris[0]; expect(userStorageUri.scheme).eq(UserStorageUri.SCHEME); @@ -174,7 +173,7 @@ describe('User Storage Service (Filesystem implementation)', () => { it('Should save the contents correctly using a user storage uri to a filesystem uri', async () => { const userStorageUri = UserStorageServiceFilesystemImpl. - toUserStorageUri(userStorageFolder, new URI('file://' + homeDir + '/' + THEIA_USER_STORAGE_FOLDER + '/' + testFile)); + toUserStorageUri(userStorageFolder, userStorageFolder.resolve(testFile)); await userStorageService.saveContents(userStorageUri, 'test content'); @@ -197,7 +196,7 @@ describe('User Storage Resource (Filesystem implementation)', () => { testFile = 'test.json'; userStorageService = testContainer.get(UserStorageService); const userStorageUriTest = UserStorageServiceFilesystemImpl. - toUserStorageUri(userStorageFolder, new URI('file://' + homeDir + '/' + THEIA_USER_STORAGE_FOLDER + '/' + testFile)); + toUserStorageUri(userStorageFolder, userStorageFolder.resolve(testFile)); userStorageResource = new UserStorageResource(userStorageUriTest, userStorageService); }); diff --git a/packages/userstorage/src/browser/user-storage-service-filesystem.ts b/packages/userstorage/src/browser/user-storage-service-filesystem.ts index f95993c03f417..ec0917e9370a7 100644 --- a/packages/userstorage/src/browser/user-storage-service-filesystem.ts +++ b/packages/userstorage/src/browser/user-storage-service-filesystem.ts @@ -28,16 +28,17 @@ export class UserStorageServiceFilesystemImpl implements UserStorageService { protected readonly toDispose = new DisposableCollection(); protected readonly onUserStorageChangedEmitter = new Emitter(); - protected userStorageFolder: Promise; + protected readonly userStorageFolder: Promise; constructor( @inject(FileSystem) protected readonly fileSystem: FileSystem, @inject(FileSystemWatcher) protected readonly watcher: FileSystemWatcher, @inject(ILogger) protected readonly logger: ILogger, @inject(EnvVariablesServer) protected readonly envServer: EnvVariablesServer + ) { - this.userStorageFolder = this.envServer.getUserDataFolder().then(userDataFolder => { - const userDataFolderUri = new URI(userDataFolder); + this.userStorageFolder = this.envServer.getConfigDirUri().then(configDirUri => { + const userDataFolderUri = new URI(configDirUri); watcher.watchFileChanges(userDataFolderUri).then(disposable => this.toDispose.push(disposable) ); @@ -46,6 +47,7 @@ export class UserStorageServiceFilesystemImpl implements UserStorageService { }); this.toDispose.push(this.onUserStorageChangedEmitter); + } dispose(): void { diff --git a/packages/workspace/src/browser/quick-open-workspace.ts b/packages/workspace/src/browser/quick-open-workspace.ts index bc09983f6c822..dbcfef1c0faa9 100644 --- a/packages/workspace/src/browser/quick-open-workspace.ts +++ b/packages/workspace/src/browser/quick-open-workspace.ts @@ -39,8 +39,11 @@ export class QuickOpenWorkspace implements QuickOpenModel { async open(workspaces: string[]): Promise { this.items = []; - const homeDirPath: string = (await this.fileSystem.getFsPath(await this.envServer.getUserHomeFolder()))!; - const tempWorkspaceFile = getTemporaryWorkspaceFileUri(await this.envServer.getUserDataFolder()); + const [homeDirUri, tempWorkspaceFile] = await Promise.all([ + this.fileSystem.getCurrentUserHome(), + getTemporaryWorkspaceFileUri(this.envServer) + ]); + const homeDirPath = homeDirUri ? await this.fileSystem.getFsPath(homeDirUri.uri) : undefined; await this.preferences.ready; if (!workspaces.length) { this.items.push(new QuickOpenGroupItem({ @@ -55,7 +58,7 @@ export class QuickOpenWorkspace implements QuickOpenModel { !this.preferences['workspace.supportMultiRootWorkspace'] && !stat.isDirectory) { continue; // skip the workspace files if multi root is not supported } - if (tempWorkspaceFile && uri.toString() === tempWorkspaceFile.toString()) { + if (uri.toString() === tempWorkspaceFile.toString()) { continue; // skip the temporary workspace files } const icon = this.labelProvider.getIcon(stat); diff --git a/packages/workspace/src/browser/workspace-service.spec.ts b/packages/workspace/src/browser/workspace-service.spec.ts index 6b2f25e9d9a3d..7a887f6ed278d 100644 --- a/packages/workspace/src/browser/workspace-service.spec.ts +++ b/packages/workspace/src/browser/workspace-service.spec.ts @@ -37,10 +37,13 @@ import * as jsoncparser from 'jsonc-parser'; import * as sinon from 'sinon'; import * as chai from 'chai'; import * as assert from 'assert'; +import * as temp from 'temp'; +import { FileUri } from '@theia/core/lib/node'; import URI from '@theia/core/lib/common/uri'; const expect = chai.expect; disableJSDOM(); +const track = temp.track(); const folderA = Object.freeze({ uri: 'file:///home/folderA', @@ -88,6 +91,7 @@ describe('WorkspaceService', () => { after(() => { disableJSDOM(); + track.cleanupSync(); }); beforeEach(() => { @@ -109,7 +113,7 @@ describe('WorkspaceService', () => { testContainer.bind(WindowService).toConstantValue(mockWindowService); testContainer.bind(ILogger).toConstantValue(mockILogger); testContainer.bind(WorkspacePreferences).toConstantValue(mockPref); - testContainer.bind(EnvVariablesServer).to(MockEnvVariablesServerImpl); + testContainer.bind(EnvVariablesServer).toConstantValue(new MockEnvVariablesServerImpl(FileUri.create(track.mkdirSync()))); testContainer.bind(PreferenceServiceImpl).toConstantValue(mockPreferenceServiceImpl); testContainer.bind(PreferenceSchemaProvider).toConstantValue(mockPreferenceSchemaProvider); diff --git a/packages/workspace/src/browser/workspace-service.ts b/packages/workspace/src/browser/workspace-service.ts index 43c8d62aeaae1..64a2b86da72da 100644 --- a/packages/workspace/src/browser/workspace-service.ts +++ b/packages/workspace/src/browser/workspace-service.ts @@ -67,7 +67,7 @@ export class WorkspaceService implements FrontendApplicationContribution { protected readonly schemaProvider: PreferenceSchemaProvider; @inject(EnvVariablesServer) - protected readonly envServer: EnvVariablesServer; + protected readonly envVariableServer: EnvVariablesServer; protected applicationName: string; @@ -380,7 +380,7 @@ export class WorkspaceService implements FrontendApplicationContribution { } protected async getUntitledWorkspace(): Promise { - return getTemporaryWorkspaceFileUri(await this.envServer.getUserDataFolder()); + return getTemporaryWorkspaceFileUri(this.envVariableServer); } private async writeWorkspaceFile(workspaceFile: FileStat | undefined, workspaceData: WorkspaceData): Promise { diff --git a/packages/workspace/src/common/utils.ts b/packages/workspace/src/common/utils.ts index 375574021a569..6288e0e80542c 100644 --- a/packages/workspace/src/common/utils.ts +++ b/packages/workspace/src/common/utils.ts @@ -15,17 +15,12 @@ ********************************************************************************/ import URI from '@theia/core/lib/common/uri'; +import { EnvVariablesServer } from '@theia/core/lib/common/env-variables'; export const THEIA_EXT = 'theia-workspace'; export const VSCODE_EXT = 'code-workspace'; -export function getTemporaryWorkspaceFileUri(userDataFolder: string | URI): URI { - let userDataFolderUri: URI; - if (typeof userDataFolder === 'string') { - userDataFolderUri = new URI(userDataFolder); - } else { - userDataFolderUri = userDataFolder; - } - - return userDataFolderUri.resolve(`Untitled.${THEIA_EXT}`).withScheme('file'); +export async function getTemporaryWorkspaceFileUri(envVariableServer: EnvVariablesServer): Promise { + const configDirUri = await envVariableServer.getConfigDirUri(); + return new URI(configDirUri).resolve(`Untitled.${THEIA_EXT}`); } diff --git a/packages/workspace/src/node/default-workspace-server.ts b/packages/workspace/src/node/default-workspace-server.ts index e6821b52e9d07..7ccdfa73efac7 100644 --- a/packages/workspace/src/node/default-workspace-server.ts +++ b/packages/workspace/src/node/default-workspace-server.ts @@ -20,6 +20,7 @@ import * as fs from 'fs-extra'; import * as jsoncparser from 'jsonc-parser'; import { injectable, inject, postConstruct } from 'inversify'; +import URI from '@theia/core/lib/common/uri'; import { FileUri } from '@theia/core/lib/node'; import { CliContribution } from '@theia/core/lib/node/cli'; import { Deferred } from '@theia/core/lib/common/promise-util'; @@ -123,8 +124,8 @@ export class DefaultWorkspaceServer implements WorkspaceServer { return listUri; } - protected workspaceStillExist(wspath: string): boolean { - return fs.pathExistsSync(FileUri.fsPath(wspath)); + protected workspaceStillExist(workspaceRootUri: string): boolean { + return fs.pathExistsSync(FileUri.fsPath(workspaceRootUri)); } protected async getWorkspaceURIFromCli(): Promise { @@ -141,32 +142,33 @@ export class DefaultWorkspaceServer implements WorkspaceServer { await this.writeToFile(file, data); } - protected async writeToFile(filePath: string, data: object): Promise { - if (!await fs.pathExists(filePath)) { - await fs.mkdirs(path.resolve(filePath, '..')); + protected async writeToFile(fsPath: string, data: object): Promise { + if (!await fs.pathExists(fsPath)) { + await fs.mkdirs(path.resolve(fsPath, '..')); } - await fs.writeJson(filePath, data); + await fs.writeJson(fsPath, data); } /** * Reads the most recently used workspace root from the user's home directory. */ protected async readRecentWorkspacePathsFromUserHome(): Promise { - const filePath = await this.getUserStoragePath(); - const data = await this.readJsonFromFile(filePath); + const fsPath = await this.getUserStoragePath(); + const data = await this.readJsonFromFile(fsPath); return RecentWorkspacePathsData.is(data) ? data : undefined; } - protected async readJsonFromFile(filePath: string): Promise { - if (await fs.pathExists(filePath)) { - const rawContent = await fs.readFile(filePath, 'utf-8'); + protected async readJsonFromFile(fsPath: string): Promise { + if (await fs.pathExists(fsPath)) { + const rawContent = await fs.readFile(fsPath, 'utf-8'); const strippedContent = jsoncparser.stripComments(rawContent); return jsoncparser.parse(strippedContent); } } protected async getUserStoragePath(): Promise { - return path.resolve(FileUri.fsPath(await this.envServer.getUserDataFolder()), 'recentworkspace.json'); + const configDirUri = await this.envServer.getConfigDirUri(); + return path.resolve(FileUri.fsPath(configDirUri), 'recentworkspace.json'); } }