From e8e3e2281fb2c0afc1501ef2f19da50e06d7a9e4 Mon Sep 17 00:00:00 2001 From: Yining Wang Date: Wed, 22 Jun 2022 17:09:07 -0400 Subject: [PATCH 1/3] Add missing vscode api in env namespace Added appHost, isNewAppInstall, isTelemetryEnabled, onDidChangeTelemetryEnabled, and remoteName API to our missing vscode API implementation. For isNewAppInstall, we use the creation date of a random theia file as installation date. Moreover, since theia doesn't support telemetry, we implemented stubs for the API related to telemetry. --- .../hosted/browser/worker/worker-env-ext.ts | 4 ++ packages/plugin-ext/src/plugin/env.ts | 28 ++++++++++++++ .../src/plugin/node/env-node-ext.ts | 13 +++++++ .../plugin-ext/src/plugin/plugin-context.ts | 7 ++++ packages/plugin/src/theia.d.ts | 37 +++++++++++++++++++ 5 files changed, 89 insertions(+) diff --git a/packages/plugin-ext/src/hosted/browser/worker/worker-env-ext.ts b/packages/plugin-ext/src/hosted/browser/worker/worker-env-ext.ts index f931c63ae9fcb..91755ab58e69a 100644 --- a/packages/plugin-ext/src/hosted/browser/worker/worker-env-ext.ts +++ b/packages/plugin-ext/src/hosted/browser/worker/worker-env-ext.ts @@ -34,4 +34,8 @@ export class WorkerEnvExtImpl extends EnvExtImpl { throw new Error('There is no app root in worker context'); } + get isNewAppInstall(): boolean { + throw new Error('Cannot determine the installation date in worker context'); + } + } diff --git a/packages/plugin-ext/src/plugin/env.ts b/packages/plugin-ext/src/plugin/env.ts index 6441172d6a9bf..408bb527451b7 100644 --- a/packages/plugin-ext/src/plugin/env.ts +++ b/packages/plugin-ext/src/plugin/env.ts @@ -19,6 +19,7 @@ import { RPCProtocol } from '../common/rpc-protocol'; import { EnvMain, PLUGIN_RPC_CONTEXT } from '../common/plugin-api-rpc'; import { QueryParameters } from '../common/env'; import { v4 } from 'uuid'; +import { Emitter, Event } from '@theia/core/lib/common/event'; export abstract class EnvExtImpl { private proxy: EnvMain; @@ -29,11 +30,20 @@ export abstract class EnvExtImpl { private ui: theia.UIKind; private envMachineId: string; private envSessionId: string; + private host: string; + private _isTelemetryEnabled: boolean; + private _remoteName: string | undefined; + private onDidChangeTelemetryEnabledEmitter: Emitter; constructor(rpc: RPCProtocol) { this.proxy = rpc.getProxy(PLUGIN_RPC_CONTEXT.ENV_MAIN); this.envSessionId = v4(); this.envMachineId = v4(); + this.host = 'desktop'; + // we don't support telemetry at the moment + this._isTelemetryEnabled = false; + this._remoteName = undefined; + this.onDidChangeTelemetryEnabledEmitter = new Emitter(); } getEnvVariable(envVarName: string): Promise { @@ -83,6 +93,24 @@ export abstract class EnvExtImpl { abstract get appRoot(): string; + abstract get isNewAppInstall(): boolean; + + get appHost(): string { + return this.host; + } + + get isTelemetryEnabled(): boolean { + return this._isTelemetryEnabled; + } + + get onDidChangeTelemetryEnabled(): Event { + return this.onDidChangeTelemetryEnabledEmitter.event; + } + + get remoteName(): string | undefined { + return this._remoteName; + } + get language(): string { return this.lang; } diff --git a/packages/plugin-ext/src/plugin/node/env-node-ext.ts b/packages/plugin-ext/src/plugin/node/env-node-ext.ts index efdaaca491158..5ad974155542d 100644 --- a/packages/plugin-ext/src/plugin/node/env-node-ext.ts +++ b/packages/plugin-ext/src/plugin/node/env-node-ext.ts @@ -19,6 +19,7 @@ import { EnvExtImpl } from '../env'; import { RPCProtocol } from '../../common/rpc-protocol'; import { createHash } from 'crypto'; import { v4 } from 'uuid'; +import fs = require('fs'); /** * Provides machineId using mac address. It's only possible on node side @@ -27,6 +28,7 @@ import { v4 } from 'uuid'; export class EnvNodeExtImpl extends EnvExtImpl { private macMachineId: string; + private _isNewAppInstall: boolean; constructor(rpc: RPCProtocol) { super(rpc); @@ -37,7 +39,14 @@ export class EnvNodeExtImpl extends EnvExtImpl { this.macMachineId = createHash('sha256').update(macAddress, 'utf8').digest('hex'); } }); + this._isNewAppInstall = this.computeIsNewAppInstall(); + } + private computeIsNewAppInstall(): boolean { + const creation = fs.statSync(__filename).birthtimeMs; + const current = Date.now(); + const dayMs = 24 * 3600 * 1000; + return (current - creation) < dayMs; } /** @@ -54,4 +63,8 @@ export class EnvNodeExtImpl extends EnvExtImpl { return __dirname; } + get isNewAppInstall(): boolean { + return this._isNewAppInstall; + } + } diff --git a/packages/plugin-ext/src/plugin/plugin-context.ts b/packages/plugin-ext/src/plugin/plugin-context.ts index 91dd775f2360f..2765168513acd 100644 --- a/packages/plugin-ext/src/plugin/plugin-context.ts +++ b/packages/plugin-ext/src/plugin/plugin-context.ts @@ -639,7 +639,14 @@ export function createAPIFactory( const env: typeof theia.env = Object.freeze({ get appName(): string { return envExt.appName; }, get appRoot(): string { return envExt.appRoot; }, + get appHost(): string { return envExt.appHost; }, get language(): string { return envExt.language; }, + get isNewAppInstall(): boolean { return envExt.isNewAppInstall; }, + get isTelemetryEnabled(): boolean { return envExt.isTelemetryEnabled; }, + get onDidChangeTelemetryEnabled(): theia.Event { + return envExt.onDidChangeTelemetryEnabled; + }, + get remoteName(): string | undefined { return envExt.remoteName; }, get machineId(): string { return envExt.machineId; }, get sessionId(): string { return envExt.sessionId; }, get uriScheme(): string { return envExt.uriScheme; }, diff --git a/packages/plugin/src/theia.d.ts b/packages/plugin/src/theia.d.ts index 802364d6bfc79..07770e0bb4c5c 100644 --- a/packages/plugin/src/theia.d.ts +++ b/packages/plugin/src/theia.d.ts @@ -6473,6 +6473,14 @@ export module '@theia/plugin' { */ export const appRoot: string; + /** + * The hosted location of the application + * On desktop this is 'desktop' + * In the web this is the specified embedder i.e. 'github.dev', 'codespaces', or 'web' if the embedder + * does not provide that information + */ + export const appHost: string; + /** * The custom uri scheme the editor registers to in the operating system. */ @@ -6483,6 +6491,35 @@ export module '@theia/plugin' { */ export const language: string; + /** + * Indicates that this is a fresh install of the application. + * `true` if within the first day of installation otherwise `false`. + */ + export const isNewAppInstall: boolean; + + /** + * Indicates whether the users has telemetry enabled. + * Can be observed to determine if the extension should send telemetry. + */ + export const isTelemetryEnabled: boolean; + + /** + * An {@link Event} which fires when the user enabled or disables telemetry. + * `true` if the user has enabled telemetry or `false` if the user has disabled telemetry. + */ + export const onDidChangeTelemetryEnabled: Event; + + /** + * The name of a remote. Defined by extensions, popular samples are `wsl` for the Windows + * Subsystem for Linux or `ssh-remote` for remotes using a secure shell. + * + * *Note* that the value is `undefined` when there is no remote extension host but that the + * value is defined in all extension hosts (local and remote) in case a remote extension host + * exists. Use {@link Extension.extensionKind} to know if + * a specific extension runs remote or not. + */ + export const remoteName: string | undefined; + /** * The detected default shell for the extension host. */ From a041a9a516521ffa3b2c675637aa058ed4f1d82d Mon Sep 17 00:00:00 2001 From: Yining Wang Date: Mon, 25 Jul 2022 10:37:08 -0400 Subject: [PATCH 2/3] Modified isNewAppInstall to make it not throw an error. Returning false to keep compatibility with vscode. --- packages/plugin-ext/src/hosted/browser/worker/worker-env-ext.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/plugin-ext/src/hosted/browser/worker/worker-env-ext.ts b/packages/plugin-ext/src/hosted/browser/worker/worker-env-ext.ts index 91755ab58e69a..b2b931f64065a 100644 --- a/packages/plugin-ext/src/hosted/browser/worker/worker-env-ext.ts +++ b/packages/plugin-ext/src/hosted/browser/worker/worker-env-ext.ts @@ -35,7 +35,7 @@ export class WorkerEnvExtImpl extends EnvExtImpl { } get isNewAppInstall(): boolean { - throw new Error('Cannot determine the installation date in worker context'); + return false; } } From 6d4f101d388f40990e13fe157e65dfe322ef5b42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20Mar=C3=A9chal?= Date: Thu, 28 Jul 2022 09:46:25 -0400 Subject: [PATCH 3/3] fixups Follow feedback on `env.appHost` and take the current runtime into account: `desktop` when running Electron, `web` otherwise. --- packages/plugin-ext/src/common/plugin-api-rpc.ts | 1 + .../plugin-ext/src/hosted/browser/hosted-plugin.ts | 6 ++++-- packages/plugin-ext/src/plugin/env.ts | 10 ++++++---- packages/plugin-ext/src/plugin/node/env-node-ext.ts | 13 ++++++------- packages/plugin-ext/src/plugin/plugin-manager.ts | 1 + 5 files changed, 18 insertions(+), 13 deletions(-) diff --git a/packages/plugin-ext/src/common/plugin-api-rpc.ts b/packages/plugin-ext/src/common/plugin-api-rpc.ts index 40670441412c3..ab0d2b6a7813a 100644 --- a/packages/plugin-ext/src/common/plugin-api-rpc.ts +++ b/packages/plugin-ext/src/common/plugin-api-rpc.ts @@ -141,6 +141,7 @@ export interface EnvInit { shell: string; uiKind: UIKind, appName: string; + appHost: string; } export interface PluginAPI { diff --git a/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts b/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts index bd69ccf2ac528..013340b9adaaa 100644 --- a/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts +++ b/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts @@ -488,6 +488,7 @@ export class HostedPluginSupport { return undefined; } + const isElectron = environment.electron.is(); await manager.$init({ preferences: getPreferences(this.preferenceProviderProvider, this.workspaceService.tryGetRoots()), globalState, @@ -496,8 +497,9 @@ export class HostedPluginSupport { queryParams: getQueryParameters(), language: nls.locale || 'en', shell: defaultShell, - uiKind: environment.electron.is() ? UIKind.Desktop : UIKind.Web, - appName: FrontendApplicationConfigProvider.get().applicationName + uiKind: isElectron ? UIKind.Desktop : UIKind.Web, + appName: FrontendApplicationConfigProvider.get().applicationName, + appHost: isElectron ? 'desktop' : 'web' // TODO: 'web' could be the embedder's name, e.g. 'github.dev' }, extApi, webview: { diff --git a/packages/plugin-ext/src/plugin/env.ts b/packages/plugin-ext/src/plugin/env.ts index 408bb527451b7..8033ca7c12b3b 100644 --- a/packages/plugin-ext/src/plugin/env.ts +++ b/packages/plugin-ext/src/plugin/env.ts @@ -14,12 +14,12 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 // ***************************************************************************** +import { Emitter, Event } from '@theia/core/lib/common/event'; import * as theia from '@theia/plugin'; import { RPCProtocol } from '../common/rpc-protocol'; import { EnvMain, PLUGIN_RPC_CONTEXT } from '../common/plugin-api-rpc'; import { QueryParameters } from '../common/env'; import { v4 } from 'uuid'; -import { Emitter, Event } from '@theia/core/lib/common/event'; export abstract class EnvExtImpl { private proxy: EnvMain; @@ -33,17 +33,15 @@ export abstract class EnvExtImpl { private host: string; private _isTelemetryEnabled: boolean; private _remoteName: string | undefined; - private onDidChangeTelemetryEnabledEmitter: Emitter; + private onDidChangeTelemetryEnabledEmitter = new Emitter(); constructor(rpc: RPCProtocol) { this.proxy = rpc.getProxy(PLUGIN_RPC_CONTEXT.ENV_MAIN); this.envSessionId = v4(); this.envMachineId = v4(); - this.host = 'desktop'; // we don't support telemetry at the moment this._isTelemetryEnabled = false; this._remoteName = undefined; - this.onDidChangeTelemetryEnabledEmitter = new Emitter(); } getEnvVariable(envVarName: string): Promise { @@ -83,6 +81,10 @@ export abstract class EnvExtImpl { this.ui = uiKind; } + setAppHost(appHost: string): void { + this.host = appHost; + } + getClientOperatingSystem(): Promise { return this.proxy.$getClientOperatingSystem(); } diff --git a/packages/plugin-ext/src/plugin/node/env-node-ext.ts b/packages/plugin-ext/src/plugin/node/env-node-ext.ts index 5ad974155542d..c1e6f75a5fee8 100644 --- a/packages/plugin-ext/src/plugin/node/env-node-ext.ts +++ b/packages/plugin-ext/src/plugin/node/env-node-ext.ts @@ -42,13 +42,6 @@ export class EnvNodeExtImpl extends EnvExtImpl { this._isNewAppInstall = this.computeIsNewAppInstall(); } - private computeIsNewAppInstall(): boolean { - const creation = fs.statSync(__filename).birthtimeMs; - const current = Date.now(); - const dayMs = 24 * 3600 * 1000; - return (current - creation) < dayMs; - } - /** * override machineID */ @@ -67,4 +60,10 @@ export class EnvNodeExtImpl extends EnvExtImpl { return this._isNewAppInstall; } + private computeIsNewAppInstall(): boolean { + const creation = fs.statSync(__filename).birthtimeMs; + const current = Date.now(); + const dayMs = 24 * 3600 * 1000; + return (current - creation) < dayMs; + } } diff --git a/packages/plugin-ext/src/plugin/plugin-manager.ts b/packages/plugin-ext/src/plugin/plugin-manager.ts index 617f525dd5588..2f19285f0a598 100644 --- a/packages/plugin-ext/src/plugin/plugin-manager.ts +++ b/packages/plugin-ext/src/plugin/plugin-manager.ts @@ -201,6 +201,7 @@ export class PluginManagerExtImpl implements PluginManagerExt, PluginManager { this.envExt.setShell(params.env.shell); this.envExt.setUIKind(params.env.uiKind); this.envExt.setApplicationName(params.env.appName); + this.envExt.setAppHost(params.env.appHost); this.preferencesManager.init(params.preferences);