Skip to content

Commit

Permalink
Make theia data folder configurable
Browse files Browse the repository at this point in the history
Signed-off-by: Mykola Morhun <[email protected]>
  • Loading branch information
mmorhun authored and Akos Kitta committed Feb 26, 2020
1 parent 02d6964 commit 3a9ca3d
Show file tree
Hide file tree
Showing 28 changed files with 317 additions and 107 deletions.
13 changes: 10 additions & 3 deletions packages/core/src/browser/preferences/preference-configurations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
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 {
Expand All @@ -34,9 +35,12 @@ export class PreferenceConfigurations {
@inject(ContributionProvider) @named(PreferenceConfiguration)
protected readonly provider: ContributionProvider<PreferenceConfiguration>;

@inject(EnvVariablesServer)
protected readonly envServer: EnvVariablesServer;

/* prefer Theia over VS Code by default */
getPaths(): string[] {
return ['.theia', '.vscode'];
async getPaths(): Promise<string[]> {
return [await this.envServer.getDataFolderName(), '.vscode'];
}

getConfigName(): string {
Expand Down Expand Up @@ -71,7 +75,10 @@ export class PreferenceConfigurations {
return configUri.parent.path.base;
}

createUri(folder: URI, configPath: string = this.getPaths()[0], configName: string = this.getConfigName()): URI {
async createUri(folder: URI, configPath: string, configName: string = this.getConfigName()): Promise<URI> {
if (!configPath) {
configPath = (await this.getPaths())[0];
}
return folder.resolve(configPath).resolve(configName + '.json');
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ 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 } = {};
Expand Down Expand Up @@ -50,6 +52,9 @@ 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);
Expand Down
46 changes: 46 additions & 0 deletions packages/core/src/browser/test/mock-env-variables-server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/********************************************************************************
* Copyright (C) 2020 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 { injectable } from 'inversify';
import { EnvVariablesServer, EnvVariable } from '../../common/env-variables';

@injectable()
export class MockEnvVariablesServerImpl implements EnvVariablesServer {
async getDataFolderName(): Promise<string> {
return '.theia';
}

async getUserHomeFolder(): Promise<string> {
return 'file:///home/test';
}

async getUserDataFolder(): Promise<string> {
return 'file:///home/test/.theia';
}

getExecPath(): Promise<string> {
throw new Error('Method not implemented.');
}
getVariables(): Promise<EnvVariable[]> {
throw new Error('Method not implemented.');
}
getValue(key: string): Promise<EnvVariable | undefined> {
throw new Error('Method not implemented.');
}
getAppDataFolder(): Promise<string> {
throw new Error('Method not implemented.');
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ export interface EnvVariablesServer {
getExecPath(): Promise<string>
getVariables(): Promise<EnvVariable[]>
getValue(key: string): Promise<EnvVariable | undefined>
getUserHomeFolder(): Promise<string>
getDataFolderName(): Promise<string>
getUserDataFolder(): Promise<string>
/** Windows specific. Returns system data folder of Theia. On other than Windows systems is the same as getUserDataFolder */
getAppDataFolder(): Promise<string>
}

export interface EnvVariable {
Expand Down
32 changes: 31 additions & 1 deletion packages/core/src/node/env-variables/env-variables-server.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (C) 2018 Red Hat, Inc. and others.
* Copyright (C) 2018-2020 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
Expand All @@ -14,9 +14,16 @@
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import * as os 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 {
Expand Down Expand Up @@ -44,4 +51,27 @@ export class EnvVariablesServerImpl implements EnvVariablesServer {
}
return this.envs[key];
}

async getUserHomeFolder(): Promise<string> {
return FileUri.create(os.homedir()).toString();
}

async getDataFolderName(): Promise<string> {
return THEIA_DATA_FOLDER;
}

async getUserDataFolder(): Promise<string> {
return FileUri.create(await this.getUserHomeFolder()).resolve(await this.getDataFolderName()).toString();
}

async getAppDataFolder(): Promise<string> {
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();
}

}
3 changes: 3 additions & 0 deletions packages/core/src/node/file-uri.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ export namespace FileUri {
return fsPathFromVsCodeUri + '\\';
}
}
if (fsPathFromVsCodeUri.startsWith('/file:')) {
return fsPathFromVsCodeUri.substring('/file:'.length);
}
return fsPathFromVsCodeUri;
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/debug/src/browser/debug-configuration-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ export class DebugConfigurationManager {
if (configUri && configUri.path.base === 'launch.json') {
uri = configUri;
} else { // fallback
uri = new URI(model.workspaceFolderUri).resolve(`${this.preferenceConfigurations.getPaths()[0]}/launch.json`);
uri = new URI(model.workspaceFolderUri).resolve((await this.preferenceConfigurations.getPaths())[0] + '/launch.json');
}
const debugType = await this.selectDebugType();
const configurations = debugType ? await this.provideDebugConfigurations(debugType, model.workspaceFolderUri) : [];
Expand Down
7 changes: 4 additions & 3 deletions packages/java/src/node/java-contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import * as os from 'os';
import * as path from 'path';
import * as glob from 'glob';
import { Socket } from 'net';
import { injectable, inject, named } from 'inversify';
import { Message, isRequestMessage } from 'vscode-ws-jsonrpc';
import { InitializeParams, InitializeRequest } from 'vscode-languageserver-protocol';
import { createSocketConnection } from 'vscode-ws-jsonrpc/lib/server';
import { DEBUG_MODE } from '@theia/core/lib/node';
import { DEBUG_MODE, FileUri } from '@theia/core/lib/node';
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
import { IConnection, BaseLanguageServerContribution, LanguageServerStartOptions } from '@theia/languages/lib/node';
import { JAVA_LANGUAGE_ID, JAVA_LANGUAGE_NAME, JavaStartParams } from '../common';
import { JavaCliContribution } from './java-cli-contribution';
Expand Down Expand Up @@ -52,6 +52,7 @@ export class JavaContribution extends BaseLanguageServerContribution {
protected readonly ready: Promise<void>;

constructor(
@inject(EnvVariablesServer) protected readonly envServer: EnvVariablesServer,
@inject(JavaCliContribution) protected readonly cli: JavaCliContribution,
@inject(ContributionProvider) @named(JavaExtensionContribution)
protected readonly contributions: ContributionProvider<JavaExtensionContribution>
Expand Down Expand Up @@ -103,7 +104,7 @@ export class JavaContribution extends BaseLanguageServerContribution {
this.activeDataFolders.add(dataFolderSuffix);
clientConnection.onClose(() => this.activeDataFolders.delete(dataFolderSuffix));

const workspacePath = path.resolve(os.homedir(), '.theia', 'jdt.ls', '_ws_' + dataFolderSuffix);
const workspacePath = path.resolve(FileUri.fsPath(await this.envServer.getUserDataFolder()), 'jdt.ls', '_ws_' + dataFolderSuffix);
const configuration = configurations.get(process.platform);
if (!configuration) {
throw new Error('Cannot find Java server configuration for ' + process.platform);
Expand Down
9 changes: 8 additions & 1 deletion packages/plugin-ext/src/common/plugin-api-rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ import { ArgumentProcessor } from '../plugin/command-registry';
import { MaybePromise } from '@theia/core/lib/common/types';
import { QuickOpenItem, QuickOpenItemOptions } from '@theia/core/lib/common/quick-open-model';
import { QuickTitleButton } from '@theia/core/lib/common/quick-open-model';
import { EnvVariable } from '@theia/core/lib/common/env-variables';

export interface PreferenceData {
[scope: number]: any;
Expand All @@ -95,7 +96,7 @@ export interface Plugin {
export interface ConfigStorage {
hostLogPath: string;
hostStoragePath?: string;
hostGlobalStoragePath?: string;
hostGlobalStoragePath: string;
}

export interface EnvInit {
Expand Down Expand Up @@ -992,7 +993,13 @@ export interface DocumentsMain {

export interface EnvMain {
$getEnvVariable(envVarName: string): Promise<string | undefined>;
$getAllEnvVariables(): Promise<EnvVariable[]>
$getClientOperatingSystem(): Promise<theia.OperatingSystem>;
$getExecPath(): Promise<string>
$getUserHomeFolderPath(): Promise<string>
$getDataFolderName(): Promise<string>
$getUserDataFolderPath(): Promise<string>
$getAppDataPath(): Promise<string>
}

export interface PreferenceRegistryMain {
Expand Down
35 changes: 30 additions & 5 deletions packages/plugin-ext/src/hosted/browser/hosted-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@ import { injectable, inject, interfaces, named, postConstruct } from 'inversify'
import { PluginWorker } from '../../main/browser/plugin-worker';
import { PluginMetadata, getPluginId, HostedPluginServer, DeployedPlugin } from '../../common/plugin-protocol';
import { HostedPluginWatcher } from './hosted-plugin-watcher';
import { MAIN_RPC_CONTEXT, PluginManagerExt } from '../../common/plugin-api-rpc';
import { MAIN_RPC_CONTEXT, PluginManagerExt, ConfigStorage } from '../../common/plugin-api-rpc';
import { setUpPluginApi } from '../../main/browser/main-context';
import { RPCProtocol, RPCProtocolImpl } from '../../common/rpc-protocol';
import {
Disposable, DisposableCollection,
ILogger, ContributionProvider, CommandRegistry, WillExecuteCommandEvent,
CancellationTokenSource, JsonRpcProxy, ProgressService
CancellationTokenSource, JsonRpcProxy, ProgressService, Path
} from '@theia/core';
import { PreferenceServiceImpl, PreferenceProviderProvider } from '@theia/core/lib/browser/preferences';
import { WorkspaceService } from '@theia/workspace/lib/browser';
Expand All @@ -47,6 +47,7 @@ import { Deferred } from '@theia/core/lib/common/promise-util';
import { DebugSessionManager } from '@theia/debug/lib/browser/debug-session-manager';
import { DebugConfigurationManager } from '@theia/debug/lib/browser/debug-configuration-manager';
import { WaitUntilEvent } from '@theia/core/lib/common/event';
import { FileSystem } from '@theia/filesystem/lib/common';
import { FileSearchService } from '@theia/file-search/lib/common/file-search-service';
import { Emitter, isCancelled } from '@theia/core';
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
Expand All @@ -56,6 +57,7 @@ import { WebviewEnvironment } from '../../main/browser/webview/webview-environme
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';

export type PluginHost = 'frontend' | string;
export type DebugActivationEvent = 'onDebugResolve' | 'onDebugInitialConfigurations' | 'onDebugAdapterProtocolTracker';
Expand Down Expand Up @@ -109,6 +111,9 @@ export class HostedPluginSupport {
@inject(DebugConfigurationManager)
protected readonly debugConfigurationManager: DebugConfigurationManager;

@inject(FileSystem)
protected readonly fileSystem: FileSystem;

@inject(FileSearchService)
protected readonly fileSearchService: FileSearchService;

Expand Down Expand Up @@ -136,6 +141,9 @@ export class HostedPluginSupport {
@inject(TerminalService)
protected readonly terminalService: TerminalService;

@inject(EnvVariablesServer)
protected readonly envServer: EnvVariablesServer;

private theiaReadyPromise: Promise<any>;

protected readonly managers = new Map<string, PluginManagerExt>();
Expand Down Expand Up @@ -330,15 +338,20 @@ export class HostedPluginSupport {
let started = 0;
const startPluginsMeasurement = this.createMeasurement('startPlugins');

const [hostLogPath, hostStoragePath] = await Promise.all([
const [hostLogPath, hostStoragePath, hostGlobalStoragePath] = await Promise.all([
this.pluginPathsService.getHostLogPath(),
this.getStoragePath()
this.getStoragePath(),
this.getHostGlobalStoragePath()
]);
if (toDisconnect.disposed) {
return;
}
const thenable: Promise<void>[] = [];
const configStorage = { hostLogPath, hostStoragePath };
const configStorage: ConfigStorage = {
hostLogPath: hostLogPath!,
hostStoragePath: hostStoragePath,
hostGlobalStoragePath: hostGlobalStoragePath!
};
for (const [host, hostContributions] of contributionsByHost) {
const manager = await this.obtainManager(host, hostContributions, toDisconnect);
if (!manager) {
Expand Down Expand Up @@ -456,6 +469,18 @@ export class HostedPluginSupport {
return this.pluginPathsService.getHostStoragePath(this.workspaceService.workspace, roots);
}

protected async getHostGlobalStoragePath(): Promise<string> {
const userDataFolderPath: string = (await this.fileSystem.getFsPath(await this.envServer.getUserDataFolder()))!;
const globalStorageFolderPath = new Path(userDataFolderPath).join('globalStorage').toString();

// Make sure that folder by the path exists
if (! await this.fileSystem.exists(globalStorageFolderPath)) {
await this.fileSystem.createFolder(globalStorageFolderPath);
}

return globalStorageFolderPath;
}

async activateByEvent(activationEvent: string): Promise<void> {
if (this.activationEvents.has(activationEvent)) {
return;
Expand Down
27 changes: 26 additions & 1 deletion packages/plugin-ext/src/main/browser/env-main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,15 @@
********************************************************************************/

import { interfaces } from 'inversify';
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
import { EnvVariablesServer, EnvVariable } from '@theia/core/lib/common/env-variables';
import { RPCProtocol } from '../../common/rpc-protocol';
import { EnvMain } from '../../common/plugin-api-rpc';
import { QueryParameters } from '../../common/env';
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) {
Expand All @@ -33,6 +34,10 @@ export class EnvMainImpl implements EnvMain {
return this.envVariableServer.getValue(envVarName).then(result => result ? result.value : undefined);
}

$getAllEnvVariables(): Promise<EnvVariable[]> {
return this.envVariableServer.getVariables();
}

async $getClientOperatingSystem(): Promise<OperatingSystem> {
if (isWindows) {
return OperatingSystem.Windows;
Expand All @@ -42,6 +47,26 @@ export class EnvMainImpl implements EnvMain {
}
return OperatingSystem.Linux;
}

$getExecPath(): Promise<string> {
return this.envVariableServer.getExecPath();
}

$getUserHomeFolderPath(): Promise<string> {
return this.envVariableServer.getUserHomeFolder();
}

$getDataFolderName(): Promise<string> {
return this.envVariableServer.getDataFolderName();
}

$getUserDataFolderPath(): Promise<string> {
return this.envVariableServer.getUserDataFolder();
}

$getAppDataPath(): Promise<string> {
return this.envVariableServer.getAppDataFolder();
}
}

/**
Expand Down
Loading

0 comments on commit 3a9ca3d

Please sign in to comment.