Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduced config dir on the env variable server #7214

Merged
merged 2 commits into from
Feb 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,33 @@
- `test:references`: fails if typescript references are out of sync.
- `prepare:references`: updates typescript references, if required.
- [repo] the `prepare` script now updates typescript references.
- [core] From now on, downstream projects can refine where the configuration files (such as `settings.json`, `keymaps.json`, `recentworkspace.json`, etc.) will be stored by Theia. [#4488](https://github.com/eclipse-theia/theia/pull/4488)
The default location remains the same: `~/.theia`, however it can be customized by overriding the `#getConfigDirUri` method of the `EnvVariablesServer` API. The easiest way is to subclass the `EnvVariablesServerImpl` and rebind it in your backend module:
```ts
// your-env-variables-server.ts:

import { injectable } from 'inversify';
import { EnvVariablesServerImpl } from '@theia/core/lib/node/env-variables';

@injectable()
export class YourEnvVariableServer extends EnvVariablesServerImpl {

async getConfigDirUri(): Promise<string> {
return 'file:///path/to/your/desired/config/dir';
}

}

// your-backend-application-module.ts:

import { ContainerModule } from 'inversify';
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
import { YourEnvVariableServer } from './your-env-variables-server';

export default new ContainerModule((bind, unbind, isBound, rebind) => {
rebind(EnvVariablesServer).to(YourEnvVariableServer).inSingletonScope();
});
```

Breaking changes:

Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/browser/label-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
40 changes: 40 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,40 @@
/********************************************************************************
* 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 URI from '../../common/uri';
import { EnvVariablesServer, EnvVariable } from '../../common/env-variables';

export class MockEnvVariablesServerImpl implements EnvVariablesServer {

constructor(protected readonly configDirUri: URI) { }

async getConfigDirUri(): Promise<string> {
return this.configDirUri.toString();
}

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.');
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export interface EnvVariablesServer {
getExecPath(): Promise<string>
getVariables(): Promise<EnvVariable[]>
getValue(key: string): Promise<EnvVariable | undefined>
getConfigDirUri(): Promise<string>;
}

export interface EnvVariable {
Expand Down
11 changes: 10 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,14 +14,18 @@
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

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';

@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;
Expand All @@ -44,4 +48,9 @@ export class EnvVariablesServerImpl implements EnvVariablesServer {
}
return this.envs[key];
}

async getConfigDirUri(): Promise<string> {
return this.configDirUri;
}

}
9 changes: 6 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,9 @@ 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 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);
Expand Down
2 changes: 1 addition & 1 deletion packages/plugin-ext/src/common/plugin-api-rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ export interface Plugin {
export interface ConfigStorage {
hostLogPath: string;
hostStoragePath?: string;
hostGlobalStoragePath?: string;
hostGlobalStoragePath: string;
}

export interface EnvInit {
Expand Down
41 changes: 35 additions & 6 deletions packages/plugin-ext/src/hosted/browser/hosted-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ 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 {
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,8 @@ 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';
import URI from '@theia/core/lib/common/uri';

export type PluginHost = 'frontend' | string;
export type DebugActivationEvent = 'onDebugResolve' | 'onDebugInitialConfigurations' | 'onDebugAdapterProtocolTracker';
Expand Down Expand Up @@ -109,6 +112,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 +142,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 +339,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,
hostStoragePath,
hostGlobalStoragePath
};
for (const [host, hostContributions] of contributionsByHost) {
const manager = await this.obtainManager(host, hostContributions, toDisconnect);
if (!manager) {
Expand Down Expand Up @@ -456,6 +470,21 @@ export class HostedPluginSupport {
return this.pluginPathsService.getHostStoragePath(this.workspaceService.workspace, roots);
}

protected async getHostGlobalStoragePath(): Promise<string> {
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(globalStorageFolderUri)) {
await this.fileSystem.createFolder(globalStorageFolderUri);
}
const globalStorageFolderFsPath = await this.fileSystem.getFsPath(globalStorageFolderUri);
if (!globalStorageFolderFsPath) {
throw new Error(`Could not resolve the FS path for URI: ${globalStorageFolderUri}`);
}
return globalStorageFolderFsPath;
}

async activateByEvent(activationEvent: string): Promise<void> {
if (this.activationEvents.has(activationEvent)) {
return;
Expand Down Expand Up @@ -599,8 +628,8 @@ export class HostedPluginSupport {
protected logMeasurement(prefix: string, count: number, measurement: () => number): void {
const duration = measurement();
if (duration === Number.NaN) {
// Measurement was prevented by native API, do not log NaN duration
return;
// Measurement was prevented by native API, do not log NaN duration
return;
}

const pluginCount = `${count} plugin${count === 1 ? '' : 's'}`;
Expand Down
2 changes: 0 additions & 2 deletions packages/plugin-ext/src/main/common/plugin-paths-protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,4 @@ export interface PluginPathsService {
getHostLogPath(): Promise<string>;
/** Returns storage path for given workspace */
getHostStoragePath(workspace: FileStat | undefined, roots: FileStat[]): Promise<string | undefined>;
/** Returns Theia data directory (one for all Theia workspaces, so doesn't change) */
getTheiaDirPath(): Promise<string>;
}
4 changes: 0 additions & 4 deletions packages/plugin-ext/src/main/node/paths/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,6 @@
********************************************************************************/

export namespace PluginPaths {
export const WINDOWS_APP_DATA_DIR = 'AppData';
export const WINDOWS_ROAMING_DIR = 'Roaming';

export const THEIA_DIR = '.theia';
export const PLUGINS_LOGS_DIR = 'logs';
export const PLUGINS_GLOBAL_STORAGE_DIR = 'plugin-storage';
export const PLUGINS_WORKSPACE_STORAGE_DIR = 'workspace-storage';
Expand Down
40 changes: 12 additions & 28 deletions packages/plugin-ext/src/main/node/paths/plugin-paths-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ 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 { ILogger, isWindows } from '@theia/core';
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';
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
import { PluginCliContribution } from '../plugin-cli-contribution';

const SESSION_TIMESTAMP_PATTERN = /^\d{8}T\d{6}$/;
Expand All @@ -32,14 +34,15 @@ const SESSION_TIMESTAMP_PATTERN = /^\d{8}T\d{6}$/;
@injectable()
export class PluginPathsServiceImpl implements PluginPathsService {

private readonly windowsDataFolders = [PluginPaths.WINDOWS_APP_DATA_DIR, PluginPaths.WINDOWS_ROAMING_DIR];

@inject(ILogger)
protected readonly logger: ILogger;

@inject(FileSystem)
protected readonly fileSystem: FileSystem;

@inject(EnvVariablesServer)
protected readonly envServer: EnvVariablesServer;

@inject(PluginCliContribution)
protected readonly cliContribution: PluginCliContribution;

Expand Down Expand Up @@ -82,8 +85,7 @@ export class PluginPathsServiceImpl implements PluginPathsService {
}

protected async buildWorkspaceId(workspace: FileStat, roots: FileStat[]): Promise<string> {
const homeDir = await this.getUserHomeDir();
const untitledWorkspace = getTemporaryWorkspaceFileUri(new URI(homeDir));
const untitledWorkspace = await getTemporaryWorkspaceFileUri(this.envServer);

if (untitledWorkspace.toString() === workspace.uri) {
// if workspace is temporary
Expand Down Expand Up @@ -116,31 +118,13 @@ export class PluginPathsServiceImpl implements PluginPathsService {
}

private async getLogsDirPath(): Promise<string> {
const theiaDir = await this.getTheiaDirPath();
return path.join(theiaDir, PluginPaths.PLUGINS_LOGS_DIR);
const configDirUri = await this.envServer.getConfigDirUri();
return path.join(FileUri.fsPath(configDirUri), PluginPaths.PLUGINS_LOGS_DIR);
}

private async getWorkspaceStorageDirPath(): Promise<string> {
const theiaDir = await this.getTheiaDirPath();
return path.join(theiaDir, PluginPaths.PLUGINS_WORKSPACE_STORAGE_DIR);
}

async getTheiaDirPath(): Promise<string> {
const homeDir = await this.getUserHomeDir();
return path.join(
homeDir,
...(isWindows ? this.windowsDataFolders : ['']),
PluginPaths.THEIA_DIR
);
}

private async getUserHomeDir(): Promise<string> {
const homeDirStat = await this.fileSystem.getCurrentUserHome();
if (!homeDirStat) {
throw new Error('Unable to get user home directory');
}
const homeDirPath = await this.fileSystem.getFsPath(homeDirStat.uri);
return homeDirPath!;
const configDirUri = await this.envServer.getConfigDirUri();
return path.join(FileUri.fsPath(configDirUri), PluginPaths.PLUGINS_WORKSPACE_STORAGE_DIR);
}

private async cleanupOldLogs(parentLogsDir: string): Promise<void> {
Expand All @@ -150,7 +134,7 @@ export class PluginPathsServiceImpl implements PluginPathsService {
// However, upgrading the @types/node in theia to 10.11 (as defined in engine field)
// Causes other packages to break in compilation, so we are using the infamous `any` type...
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const subDirEntries = dirEntries.filter((dirent: any) => dirent.isDirectory() );
const subDirEntries = dirEntries.filter((dirent: any) => dirent.isDirectory());
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const subDirNames = subDirEntries.map((dirent: any) => dirent.name);
// We never clean a folder that is not a Theia logs session folder.
Expand Down
Loading