diff options
author | Martin Aeschlimann <martinae@microsoft.com> | 2022-01-21 19:19:52 +0300 |
---|---|---|
committer | Martin Aeschlimann <martinae@microsoft.com> | 2022-01-21 19:19:52 +0300 |
commit | ef4549b141911a3dbad90c6ed67c6e03e5a8e931 (patch) | |
tree | 5b117eef9ba89fffd9651a1d906c4f19dc9b3082 | |
parent | d46ed369512cc513866870e0f15ecdce473583f4 (diff) | |
parent | 79981da8e24357bad94366eedea6da1f30e4e4fe (diff) |
Merge branch 'main' into aeschli/noremoteindicatorweb
80 files changed, 600 insertions, 318 deletions
diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index ed7096ad474..a2e8fd1a800 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -5,7 +5,7 @@ import * as fs from 'fs'; import * as path from 'path'; -import { CancellationToken, Command, Disposable, Event, EventEmitter, Memento, OutputChannel, ProgressLocation, ProgressOptions, scm, SourceControl, SourceControlInputBox, SourceControlInputBoxValidation, SourceControlInputBoxValidationType, SourceControlResourceDecorations, SourceControlResourceGroup, SourceControlResourceState, ThemeColor, Uri, window, workspace, WorkspaceEdit, FileDecoration, commands } from 'vscode'; +import { CancellationToken, Command, Disposable, Event, EventEmitter, Memento, OutputChannel, ProgressLocation, ProgressOptions, scm, SourceControl, SourceControlInputBox, SourceControlInputBoxValidation, SourceControlInputBoxValidationType, SourceControlResourceDecorations, SourceControlResourceGroup, SourceControlResourceState, ThemeColor, Uri, window, workspace, WorkspaceEdit, FileDecoration, commands, SourceControlActionButton } from 'vscode'; import TelemetryReporter from '@vscode/extension-telemetry'; import * as nls from 'vscode-nls'; import { Branch, Change, ForcePushMode, GitErrorCodes, LogOptions, Ref, RefType, Remote, Status, CommitOptions, BranchQuery, FetchOptions } from './api/git'; @@ -1935,7 +1935,7 @@ export class Repository implements Disposable { return undefined; }); - let actionButton: SourceControl['actionButton']; + let actionButton: SourceControlActionButton | undefined; if (HEAD !== undefined) { const config = workspace.getConfiguration('git', Uri.file(this.repository.root)); const showActionButton = config.get<string>('showUnpublishedCommitsButton', 'whenEmpty'); @@ -1948,18 +1948,23 @@ export class Repository implements Disposable { const rebaseWhenSync = config.get<string>('rebaseWhenSync'); actionButton = { - command: rebaseWhenSync ? 'git.syncRebase' : 'git.sync', - title: localize('scm button sync title', '$(sync) Sync Changes {0}{1}', HEAD.behind ? `${HEAD.behind}$(arrow-down) ` : '', `${HEAD.ahead}$(arrow-up)`), - tooltip: this.syncTooltip, - arguments: [this._sourceControl], + command: { + command: rebaseWhenSync ? 'git.syncRebase' : 'git.sync', + title: localize('scm button sync title', "$(sync) {0}{1}", HEAD.behind ? `${HEAD.behind}$(arrow-down) ` : '', `${HEAD.ahead}$(arrow-up)`), + tooltip: this.syncTooltip, + arguments: [this._sourceControl], + }, + description: localize('scm button sync description', "$(sync) Sync Changes {0}{1}", HEAD.behind ? `${HEAD.behind}$(arrow-down) ` : '', `${HEAD.ahead}$(arrow-up)`) }; } } else { actionButton = { - command: 'git.publish', - title: localize('scm button publish title', "$(cloud-upload) Publish Branch"), - tooltip: localize('scm button publish tooltip', "Publish Branch"), - arguments: [this._sourceControl], + command: { + command: 'git.publish', + title: localize('scm button publish title', "$(cloud-upload) Publish Branch"), + tooltip: localize('scm button publish tooltip', "Publish Branch"), + arguments: [this._sourceControl], + } }; } } diff --git a/scripts/code-server.js b/scripts/code-server.js index 4ee2e88abc8..287b229bdda 100644 --- a/scripts/code-server.js +++ b/scripts/code-server.js @@ -40,7 +40,7 @@ const HOST = args['host'] ?? 'localhost'; const PORT = args['port'] ?? '9888'; const TOKEN = args['connection-token'] ?? String(crypto.randomInt(0xffffffff)); -if (args['launch'] && args['connection-token'] === undefined && args['connection-token-file'] === undefined && !args['no-connection-token']) { +if (args['launch'] && args['connection-token'] === undefined && args['connection-token-file'] === undefined && !args['without-connection-token']) { serverArgs.push('--connection-token', TOKEN); } if (args['host'] === undefined) { diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index a4147b57382..2b6bc81dca2 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -85,10 +85,10 @@ import { UserDataAutoSyncService } from 'vs/platform/userDataSync/electron-sandb import { ActiveWindowManager } from 'vs/platform/windows/node/windowTracker'; import { ISignService } from 'vs/platform/sign/common/sign'; import { SignService } from 'vs/platform/sign/node/signService'; -import { ISharedTunnelsService } from 'vs/platform/remote/common/tunnel'; -import { SharedTunnelsService } from 'vs/platform/remote/node/tunnelService'; +import { ISharedTunnelsService } from 'vs/platform/tunnel/common/tunnel'; +import { SharedTunnelsService } from 'vs/platform/tunnel/node/tunnelService'; import { ipcSharedProcessTunnelChannelName, ISharedProcessTunnelService } from 'vs/platform/remote/common/sharedProcessTunnelService'; -import { SharedProcessTunnelService } from 'vs/platform/remote/node/sharedProcessTunnelService'; +import { SharedProcessTunnelService } from 'vs/platform/tunnel/node/sharedProcessTunnelService'; import { ipcSharedProcessWorkerChannelName, ISharedProcessWorkerConfiguration, ISharedProcessWorkerService } from 'vs/platform/sharedProcess/common/sharedProcessWorkerService'; import { SharedProcessWorkerService } from 'vs/platform/sharedProcess/electron-browser/sharedProcessWorkerService'; import { AssignmentService } from 'vs/platform/assignment/common/assignmentService'; diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index b607b95dc61..4ba46359de9 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -49,8 +49,8 @@ import product from 'vs/platform/product/common/product'; import { IProductService } from 'vs/platform/product/common/productService'; import { IProtocolMainService } from 'vs/platform/protocol/electron-main/protocol'; import { ProtocolMainService } from 'vs/platform/protocol/electron-main/protocolMainService'; -import { ITunnelService } from 'vs/platform/remote/common/tunnel'; -import { TunnelService } from 'vs/platform/remote/node/tunnelService'; +import { ITunnelService } from 'vs/platform/tunnel/common/tunnel'; +import { TunnelService } from 'vs/platform/tunnel/node/tunnelService'; import { IRequestService } from 'vs/platform/request/common/request'; import { RequestMainService } from 'vs/platform/request/electron-main/requestMainService'; import { ISignService } from 'vs/platform/sign/common/sign'; diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index d8f40bfb81d..22d030e30f4 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -5,7 +5,6 @@ import { URI } from 'vs/base/common/uri'; import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; -import { ExtensionKind } from 'vs/platform/extensions/common/extensions'; import { createDecorator, refineServiceDecorator } from 'vs/platform/instantiation/common/instantiation'; export const IEnvironmentService = createDecorator<IEnvironmentService>('environmentService'); @@ -22,6 +21,13 @@ export interface IExtensionHostDebugParams extends IDebugParams { } /** + * Type of extension. + * + * **NOTE**: This is defined in `platform/environment` because it can appear as a CLI argument. + */ +export type ExtensionKind = 'ui' | 'workspace' | 'web'; + +/** * A basic environment service that can be used in various processes, * such as main, renderer and shared process. Use subclasses of this * service for specific environment. diff --git a/src/vs/platform/environment/common/environmentService.ts b/src/vs/platform/environment/common/environmentService.ts index 1ee4818dbf9..f2a3ea1b718 100644 --- a/src/vs/platform/environment/common/environmentService.ts +++ b/src/vs/platform/environment/common/environmentService.ts @@ -11,8 +11,7 @@ import { env } from 'vs/base/common/process'; import { joinPath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; -import { IDebugParams, IExtensionHostDebugParams, INativeEnvironmentService } from 'vs/platform/environment/common/environment'; -import { ExtensionKind } from 'vs/platform/extensions/common/extensions'; +import { ExtensionKind, IDebugParams, IExtensionHostDebugParams, INativeEnvironmentService } from 'vs/platform/environment/common/environment'; import { IProductService } from 'vs/platform/product/common/productService'; export interface INativeEnvironmentPaths { diff --git a/src/vs/platform/extensions/common/extensions.ts b/src/vs/platform/extensions/common/extensions.ts index fa86a6e9aed..72d69909b9e 100644 --- a/src/vs/platform/extensions/common/extensions.ts +++ b/src/vs/platform/extensions/common/extensions.ts @@ -5,8 +5,8 @@ import * as strings from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; +import { ExtensionKind } from 'vs/platform/environment/common/environment'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { ILocalization } from 'vs/platform/localizations/common/localizations'; import { getRemoteName } from 'vs/platform/remote/common/remoteHosts'; export const MANIFEST_CACHE_FOLDER = 'CachedExtensions'; @@ -159,6 +159,19 @@ export interface INotebookRendererContribution { readonly mimeTypes: string[]; } +export interface ITranslation { + id: string; + path: string; +} + +export interface ILocalizationContribution { + languageId: string; + languageName?: string; + localizedLanguageName?: string; + translations: ITranslation[]; + minimalTranslations?: { [key: string]: string }; +} + export interface IExtensionContributions { commands?: ICommand[]; configuration?: IConfiguration | IConfiguration[]; @@ -175,7 +188,7 @@ export interface IExtensionContributions { viewsContainers?: { [location: string]: IViewContainer[] }; views?: { [location: string]: IView[] }; colors?: IColor[]; - localizations?: ILocalization[]; + localizations?: ILocalizationContribution[]; readonly customEditors?: readonly IWebviewEditor[]; readonly codeActions?: readonly ICodeActionContribution[]; authentication?: IAuthenticationContribution[]; @@ -192,7 +205,6 @@ export interface IExtensionCapabilities { export const ALL_EXTENSION_KINDS: readonly ExtensionKind[] = ['ui', 'workspace', 'web']; -export type ExtensionKind = 'ui' | 'workspace' | 'web'; export type LimitedWorkspaceSupportType = 'limited'; export type ExtensionUntrustedWorkspaceSupportType = boolean | LimitedWorkspaceSupportType; diff --git a/src/vs/platform/localizations/common/localizations.ts b/src/vs/platform/localizations/common/localizations.ts index 65e2dd9d4c4..6792440125c 100644 --- a/src/vs/platform/localizations/common/localizations.ts +++ b/src/vs/platform/localizations/common/localizations.ts @@ -3,28 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { ILocalizationContribution } from 'vs/platform/extensions/common/extensions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -export interface ILocalization { - languageId: string; - languageName?: string; - localizedLanguageName?: string; - translations: ITranslation[]; - minimalTranslations?: { [key: string]: string }; -} - -export interface ITranslation { - id: string; - path: string; -} - export const ILocalizationsService = createDecorator<ILocalizationsService>('localizationsService'); export interface ILocalizationsService { readonly _serviceBrand: undefined; getLanguageIds(): Promise<string[]>; } -export function isValidLocalization(localization: ILocalization): boolean { +export function isValidLocalization(localization: ILocalizationContribution): boolean { if (typeof localization.languageId !== 'string') { return false; } diff --git a/src/vs/platform/remote/common/remoteAuthorityResolver.ts b/src/vs/platform/remote/common/remoteAuthorityResolver.ts index 570c9072e3b..fb899410b07 100644 --- a/src/vs/platform/remote/common/remoteAuthorityResolver.ts +++ b/src/vs/platform/remote/common/remoteAuthorityResolver.ts @@ -6,7 +6,6 @@ import { Event } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { TunnelPrivacy } from 'vs/platform/remote/common/tunnel'; export const IRemoteAuthorityResolverService = createDecorator<IRemoteAuthorityResolverService>('remoteAuthorityResolverService'); @@ -29,6 +28,11 @@ export interface TunnelDescription { privacy?: string; protocol?: string; } +export interface TunnelPrivacy { + themeIcon: string; + id: string; + label: string; +} export interface TunnelInformation { environmentTunnels?: TunnelDescription[]; features?: { diff --git a/src/vs/platform/remote/common/remoteHosts.ts b/src/vs/platform/remote/common/remoteHosts.ts index 36675b64b39..c24b5295b2c 100644 --- a/src/vs/platform/remote/common/remoteHosts.ts +++ b/src/vs/platform/remote/common/remoteHosts.ts @@ -5,7 +5,6 @@ import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; -import { IWorkspace } from 'vs/platform/workspace/common/workspace'; export function getRemoteAuthority(uri: URI): string | undefined { return uri.scheme === Schemas.vscodeRemote ? uri.authority : undefined; @@ -25,24 +24,3 @@ export function getRemoteName(authority: string | undefined): string | undefined } return authority.substr(0, pos); } - -export function isVirtualResource(resource: URI) { - return resource.scheme !== Schemas.file && resource.scheme !== Schemas.vscodeRemote; -} - -export function getVirtualWorkspaceLocation(workspace: IWorkspace): { scheme: string, authority: string } | undefined { - if (workspace.folders.length) { - return workspace.folders.every(f => isVirtualResource(f.uri)) ? workspace.folders[0].uri : undefined; - } else if (workspace.configuration && isVirtualResource(workspace.configuration)) { - return workspace.configuration; - } - return undefined; -} - -export function getVirtualWorkspaceScheme(workspace: IWorkspace): string | undefined { - return getVirtualWorkspaceLocation(workspace)?.scheme; -} - -export function isVirtualWorkspace(workspace: IWorkspace): boolean { - return getVirtualWorkspaceLocation(workspace) !== undefined; -} diff --git a/src/vs/platform/remote/common/tunnel.ts b/src/vs/platform/tunnel/common/tunnel.ts index adb97ae8cf2..f4a174e37cf 100644 --- a/src/vs/platform/remote/common/tunnel.ts +++ b/src/vs/platform/tunnel/common/tunnel.ts @@ -11,6 +11,7 @@ import { URI } from 'vs/base/common/uri'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; import { IAddressProvider } from 'vs/platform/remote/common/remoteAgentConnection'; +import { TunnelPrivacy } from 'vs/platform/remote/common/remoteAuthorityResolver'; export const ITunnelService = createDecorator<ITunnelService>('tunnelService'); export const ISharedTunnelsService = createDecorator<ISharedTunnelsService>('sharedTunnelsService'); @@ -49,12 +50,6 @@ export interface TunnelCreationOptions { elevationRequired?: boolean; } -export interface TunnelPrivacy { - themeIcon: string; - id: string; - label: string; -} - export interface TunnelProviderFeatures { elevation: boolean; /** diff --git a/src/vs/platform/remote/node/sharedProcessTunnelService.ts b/src/vs/platform/tunnel/node/sharedProcessTunnelService.ts index fa7975c1891..ddea6ea6cdf 100644 --- a/src/vs/platform/remote/node/sharedProcessTunnelService.ts +++ b/src/vs/platform/tunnel/node/sharedProcessTunnelService.ts @@ -5,7 +5,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { ISharedProcessTunnel, ISharedProcessTunnelService } from 'vs/platform/remote/common/sharedProcessTunnelService'; -import { ISharedTunnelsService, RemoteTunnel } from 'vs/platform/remote/common/tunnel'; +import { ISharedTunnelsService, RemoteTunnel } from 'vs/platform/tunnel/common/tunnel'; import { IAddress, IAddressProvider } from 'vs/platform/remote/common/remoteAgentConnection'; import { Disposable } from 'vs/base/common/lifecycle'; import { canceled } from 'vs/base/common/errors'; diff --git a/src/vs/platform/remote/node/tunnelService.ts b/src/vs/platform/tunnel/node/tunnelService.ts index 322b743d2c9..6aa019bf1e4 100644 --- a/src/vs/platform/remote/node/tunnelService.ts +++ b/src/vs/platform/tunnel/node/tunnelService.ts @@ -14,7 +14,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { ILogService } from 'vs/platform/log/common/log'; import { IProductService } from 'vs/platform/product/common/productService'; import { connectRemoteAgentTunnel, IAddressProvider, IConnectionOptions, ISocketFactory } from 'vs/platform/remote/common/remoteAgentConnection'; -import { AbstractTunnelService, isAllInterfaces, ISharedTunnelsService as ISharedTunnelsService, isLocalhost, ITunnelService, RemoteTunnel, TunnelPrivacyId } from 'vs/platform/remote/common/tunnel'; +import { AbstractTunnelService, isAllInterfaces, ISharedTunnelsService as ISharedTunnelsService, isLocalhost, ITunnelService, RemoteTunnel, TunnelPrivacyId } from 'vs/platform/tunnel/common/tunnel'; import { ISignService } from 'vs/platform/sign/common/sign'; async function createRemoteTunnel(options: IConnectionOptions, defaultTunnelHost: string, tunnelRemoteHost: string, tunnelRemotePort: number, tunnelLocalPort?: number): Promise<RemoteTunnel> { diff --git a/src/vs/platform/webview/common/webviewPortMapping.ts b/src/vs/platform/webview/common/webviewPortMapping.ts index 5f7fafd3371..ee1014f7cce 100644 --- a/src/vs/platform/webview/common/webviewPortMapping.ts +++ b/src/vs/platform/webview/common/webviewPortMapping.ts @@ -7,7 +7,7 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; import { IAddress } from 'vs/platform/remote/common/remoteAgentConnection'; -import { extractLocalHostUriMetaDataForPortMapping, ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel'; +import { extractLocalHostUriMetaDataForPortMapping, ITunnelService, RemoteTunnel } from 'vs/platform/tunnel/common/tunnel'; export interface IWebviewPortMapping { readonly webviewPort: number; diff --git a/src/vs/platform/workspace/common/virtualWorkspace.ts b/src/vs/platform/workspace/common/virtualWorkspace.ts new file mode 100644 index 00000000000..0dc19dde88e --- /dev/null +++ b/src/vs/platform/workspace/common/virtualWorkspace.ts @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Schemas } from 'vs/base/common/network'; +import { URI } from 'vs/base/common/uri'; +import { IWorkspace } from 'vs/platform/workspace/common/workspace'; + +export function isVirtualResource(resource: URI) { + return resource.scheme !== Schemas.file && resource.scheme !== Schemas.vscodeRemote; +} + +export function getVirtualWorkspaceLocation(workspace: IWorkspace): { scheme: string, authority: string } | undefined { + if (workspace.folders.length) { + return workspace.folders.every(f => isVirtualResource(f.uri)) ? workspace.folders[0].uri : undefined; + } else if (workspace.configuration && isVirtualResource(workspace.configuration)) { + return workspace.configuration; + } + return undefined; +} + +export function getVirtualWorkspaceScheme(workspace: IWorkspace): string | undefined { + return getVirtualWorkspaceLocation(workspace)?.scheme; +} + +export function isVirtualWorkspace(workspace: IWorkspace): boolean { + return getVirtualWorkspaceLocation(workspace) !== undefined; +} diff --git a/src/vs/server/node/remoteAgentEnvironmentImpl.ts b/src/vs/server/node/remoteAgentEnvironmentImpl.ts index 9f842bfd971..e59722bb1dc 100644 --- a/src/vs/server/node/remoteAgentEnvironmentImpl.ts +++ b/src/vs/server/node/remoteAgentEnvironmentImpl.ts @@ -30,6 +30,7 @@ import { IExtensionManagementCLIService, InstallOptions } from 'vs/platform/exte import { cwd } from 'vs/base/common/process'; import { Promises } from 'vs/base/node/pfs'; import { IProductService } from 'vs/platform/product/common/productService'; +import { ServerConnectionToken, ServerConnectionTokenType } from 'vs/server/node/serverConnectionToken'; let _SystemExtensionsRoot: string | null = null; function getSystemExtensionsRoot(): string { @@ -54,7 +55,7 @@ export class RemoteAgentEnvironmentChannel implements IServerChannel { private readonly whenExtensionsReady: Promise<void>; constructor( - private readonly _connectionToken: string, + private readonly _connectionToken: ServerConnectionToken, private readonly environmentService: IServerEnvironmentService, extensionManagementCLIService: IExtensionManagementCLIService, private readonly logService: ILogService, @@ -310,7 +311,7 @@ export class RemoteAgentEnvironmentChannel implements IServerChannel { private async _getEnvironmentData(): Promise<IRemoteAgentEnvironmentDTO> { return { pid: process.pid, - connectionToken: this._connectionToken, + connectionToken: (this._connectionToken.type !== ServerConnectionTokenType.None ? this._connectionToken.value : ''), appRoot: URI.file(this.environmentService.appRoot), settingsPath: this.environmentService.machineSettingsResource, logsPath: URI.file(this.environmentService.logsPath), diff --git a/src/vs/server/node/remoteExtensionHostAgentServer.ts b/src/vs/server/node/remoteExtensionHostAgentServer.ts index 9c44444a7c6..5498815eb21 100644 --- a/src/vs/server/node/remoteExtensionHostAgentServer.ts +++ b/src/vs/server/node/remoteExtensionHostAgentServer.ts @@ -34,7 +34,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IRequestService } from 'vs/platform/request/common/request'; import { RequestService } from 'vs/platform/request/node/requestService'; import { ITelemetryAppender, NullAppender, supportsTelemetry } from 'vs/platform/telemetry/common/telemetryUtils'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { ITelemetryService, TelemetryLevel } from 'vs/platform/telemetry/common/telemetry'; import { IExtensionGalleryService, IExtensionManagementCLIService, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionGalleryServiceWithNoStorageService } from 'vs/platform/extensionManagement/common/extensionGalleryService'; import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; @@ -83,7 +83,7 @@ import { ICredentialsMainService } from 'vs/platform/credentials/common/credenti import { CredentialsMainService } from 'vs/platform/credentials/node/credentialsMainService'; import { EncryptionMainService } from 'vs/platform/encryption/node/encryptionMainService'; import { RemoteTelemetryChannel } from 'vs/server/node/remoteTelemetryChannel'; -import { parseConnectionToken, ServerConnectionTokenParseError } from 'vs/server/node/connectionToken'; +import { parseServerConnectionToken, ServerConnectionToken, ServerConnectionTokenParseError, ServerConnectionTokenType } from 'vs/server/node/serverConnectionToken'; import { IEncryptionMainService } from 'vs/platform/encryption/common/encryptionService'; const SHUTDOWN_TIMEOUT = 5 * 60 * 1000; @@ -227,8 +227,7 @@ export class RemoteExtensionHostAgentServer extends Disposable { constructor( private readonly _environmentService: IServerEnvironmentService, private readonly _productService: IProductService, - private readonly _connectionToken: string, - private readonly _connectionTokenIsMandatory: boolean, + private readonly _connectionToken: ServerConnectionToken, hasWebClient: boolean, REMOTE_DATA_FOLDER: string ) { @@ -306,8 +305,19 @@ export class RemoteExtensionHostAgentServer extends Disposable { commonProperties: resolveCommonProperties(fileService, release(), hostname(), process.arch, this._productService.commit, this._productService.version + '-remote', machineId, this._productService.msftInternalDomains, this._environmentService.installSourcePath, 'remoteAgent'), piiPaths: [this._environmentService.appRoot] }; - - services.set(IRemoteTelemetryService, new SyncDescriptor(RemoteTelemetryService, [config, undefined])); + const initialTelemetryLevelArg = this._environmentService.args['telemetry-level']; + let injectedTelemetryLevel: TelemetryLevel | undefined = undefined; + // Convert the passed in CLI argument into a telemetry level for the telemetry service + if (initialTelemetryLevelArg === 'all') { + injectedTelemetryLevel = TelemetryLevel.USAGE; + } else if (initialTelemetryLevelArg === 'error') { + injectedTelemetryLevel = TelemetryLevel.ERROR; + } else if (initialTelemetryLevelArg === 'crash') { + injectedTelemetryLevel = TelemetryLevel.CRASH; + } else if (initialTelemetryLevelArg !== undefined) { + injectedTelemetryLevel = TelemetryLevel.NONE; + } + services.set(IRemoteTelemetryService, new SyncDescriptor(RemoteTelemetryService, [config, injectedTelemetryLevel])); } else { services.set(IRemoteTelemetryService, RemoteNullTelemetryService); } @@ -414,7 +424,7 @@ export class RemoteExtensionHostAgentServer extends Disposable { if (pathname === '/vscode-remote-resource') { // Handle HTTP requests for resources rendered in the rich client (images, fonts, etc.) // These resources could be files shipped with extensions or even workspace files. - if (parsedUrl.query['tkn'] !== this._connectionToken) { + if (!this._connectionToken.validate(parsedUrl.query['tkn'])) { return serveError(req, res, 403, `Forbidden.`); } @@ -612,7 +622,7 @@ export class RemoteExtensionHostAgentServer extends Disposable { return rejectWebSocketConnection(`Invalid first message`); } - if (this._connectionTokenIsMandatory && msg1.auth !== this._connectionToken) { + if (this._connectionToken.type === ServerConnectionTokenType.Mandatory && !this._connectionToken.validate(msg1.auth)) { return rejectWebSocketConnection(`Unauthorized client refused: auth mismatch`); } @@ -667,7 +677,7 @@ export class RemoteExtensionHostAgentServer extends Disposable { let valid = false; if (!validator) { valid = true; - } else if (msg2.signedData === this._connectionToken) { + } else if (this._connectionToken.validate(msg2.signedData)) { // web client valid = true; } else { @@ -987,21 +997,20 @@ export async function createServer(address: string | net.AddressInfo | null, arg } } - const connectionTokenParseResult = parseConnectionToken(args); - if (connectionTokenParseResult instanceof ServerConnectionTokenParseError) { - console.warn(connectionTokenParseResult.message); + const connectionToken = parseServerConnectionToken(args); + if (connectionToken instanceof ServerConnectionTokenParseError) { + console.warn(connectionToken.message); process.exit(1); } - const connectionToken = connectionTokenParseResult.value; - const connectionTokenIsMandatory = connectionTokenParseResult.isMandatory; const hasWebClient = fs.existsSync(FileAccess.asFileUri('vs/code/browser/workbench/workbench.html', require).fsPath); if (hasWebClient && address && typeof address !== 'string') { // ships the web ui! - console.log(`Web UI available at http://${args.host || 'localhost'}${address.port === 80 ? '' : `:${address.port}`}/?tkn=${connectionToken}`); + const queryPart = (connectionToken.type !== ServerConnectionTokenType.None ? `?tkn=${connectionToken.value}` : ''); + console.log(`Web UI available at http://localhost${address.port === 80 ? '' : `:${address.port}`}/${queryPart}`); } - const remoteExtensionHostAgentServer = new RemoteExtensionHostAgentServer(environmentService, productService, connectionToken, connectionTokenIsMandatory, hasWebClient, REMOTE_DATA_FOLDER); + const remoteExtensionHostAgentServer = new RemoteExtensionHostAgentServer(environmentService, productService, connectionToken, hasWebClient, REMOTE_DATA_FOLDER); const services = await remoteExtensionHostAgentServer.initialize(); const { telemetryService } = services; diff --git a/src/vs/server/node/connectionToken.ts b/src/vs/server/node/serverConnectionToken.ts index 7dda233d25c..6d169289a17 100644 --- a/src/vs/server/node/connectionToken.ts +++ b/src/vs/server/node/serverConnectionToken.ts @@ -9,21 +9,51 @@ import { ServerParsedArgs } from 'vs/server/node/serverEnvironmentService'; const connectionTokenRegex = /^[0-9A-Za-z-]+$/; -export class ServerConnectionToken { - constructor( - public readonly value: string, - public readonly isMandatory: boolean, - ) { +export const enum ServerConnectionTokenType { + None, + Optional,// TODO: Remove this soon + Mandatory +} + +export class NoneServerConnectionToken { + public readonly type = ServerConnectionTokenType.None; + + public validate(connectionToken: any): boolean { + return true; } } +export class OptionalServerConnectionToken { + public readonly type = ServerConnectionTokenType.Optional; + + constructor(public readonly value: string) { + } + + public validate(connectionToken: any): boolean { + return (connectionToken === this.value); + } +} + +export class MandatoryServerConnectionToken { + public readonly type = ServerConnectionTokenType.Mandatory; + + constructor(public readonly value: string) { + } + + public validate(connectionToken: any): boolean { + return (connectionToken === this.value); + } +} + +export type ServerConnectionToken = NoneServerConnectionToken | OptionalServerConnectionToken | MandatoryServerConnectionToken; + export class ServerConnectionTokenParseError { constructor( public readonly message: string - ) {} + ) { } } -export function parseConnectionToken(args: ServerParsedArgs): ServerConnectionToken | ServerConnectionTokenParseError { +export function parseServerConnectionToken(args: ServerParsedArgs): ServerConnectionToken | ServerConnectionTokenParseError { const withoutConnectionToken = args['without-connection-token']; const connectionToken = args['connection-token']; const connectionTokenFile = args['connection-token-file']; @@ -33,7 +63,7 @@ export function parseConnectionToken(args: ServerParsedArgs): ServerConnectionTo if (typeof connectionToken !== 'undefined' || typeof connectionTokenFile !== 'undefined') { return new ServerConnectionTokenParseError(`Please do not use the argument '--connection-token' or '--connection-token-file' at the same time as '--without-connection-token'.`); } - return new ServerConnectionToken('without-connection-token' /* to be implemented @alexd */, false); + return new NoneServerConnectionToken(); } if (typeof connectionTokenFile !== 'undefined') { @@ -52,7 +82,7 @@ export function parseConnectionToken(args: ServerParsedArgs): ServerConnectionTo return new ServerConnectionTokenParseError(`The connection token defined in '${connectionTokenFile} does not adhere to the characters 0-9, a-z, A-Z or -.`); } - return new ServerConnectionToken(rawConnectionToken, true); + return new MandatoryServerConnectionToken(rawConnectionToken); } if (typeof connectionToken !== 'undefined') { @@ -62,19 +92,17 @@ export function parseConnectionToken(args: ServerParsedArgs): ServerConnectionTo if (compatibility) { // TODO: Remove this case soon - return new ServerConnectionToken(connectionToken, false); + return new OptionalServerConnectionToken(connectionToken); } - return new ServerConnectionToken(connectionToken, true); + return new MandatoryServerConnectionToken(connectionToken); } if (compatibility) { // TODO: Remove this case soon console.log(`Breaking change in the next release: Please use one of the following arguments: '--connection-token', '--connection-token-file' or '--without-connection-token'.`); - return new ServerConnectionToken(generateUuid(), false); + return new OptionalServerConnectionToken(generateUuid()); } - // TODO: fixme - return new ServerConnectionToken(generateUuid(), false); - // return new ServerConnectionTokenParseError(`Please use one of the following arguments: '--connection-token', '--connection-token-file' or '--without-connection-token'.`); + return new ServerConnectionTokenParseError(`Please use one of the following arguments: '--connection-token', '--connection-token-file' or '--without-connection-token'.`); } diff --git a/src/vs/server/node/serverEnvironmentService.ts b/src/vs/server/node/serverEnvironmentService.ts index 8250c06d7b2..1f93531ca3c 100644 --- a/src/vs/server/node/serverEnvironmentService.ts +++ b/src/vs/server/node/serverEnvironmentService.ts @@ -26,6 +26,7 @@ export const serverOptions: OptionDescriptions<ServerParsedArgs> = { 'print-ip-address': { type: 'boolean' }, 'accept-server-license-terms': { type: 'boolean', cat: 'o', description: nls.localize('acceptLicenseTerms', 'If set, the user accepts the server license terms and the server will be started without a user prompt.') }, 'server-data-dir': { type: 'string', cat: 'o', description: nls.localize('serverDataDir', 'Specifies the directory that server data is kept in.') }, + 'telemetry-level': { type: 'string', cat: 'o', args: 'off | crash | error | all', description: nls.localize('telemetry-level', 'Sets the initial telemetry level. If not specified, the server will await a connection before sending any telemetry. Setting this to off is equivalent to --disable-telemetry') }, /* ----- vs code options ----- */ @@ -127,6 +128,8 @@ export interface ServerParsedArgs { 'server-data-dir'?: string; + 'telemetry-level'?: string; + /* ----- vs code options ----- */ 'user-data-dir'?: string; diff --git a/src/vs/server/node/webClientServer.ts b/src/vs/server/node/webClientServer.ts index 259d0b0373c..64e2ed2489d 100644 --- a/src/vs/server/node/webClientServer.ts +++ b/src/vs/server/node/webClientServer.ts @@ -18,6 +18,7 @@ import { extname, dirname, join, normalize } from 'vs/base/common/path'; import { FileAccess } from 'vs/base/common/network'; import { generateUuid } from 'vs/base/common/uuid'; import { IProductService } from 'vs/platform/product/common/productService'; +import { ServerConnectionToken, ServerConnectionTokenType } from 'vs/server/node/serverConnectionToken'; const textMimeType = { '.html': 'text/html', @@ -73,7 +74,7 @@ const APP_ROOT = dirname(FileAccess.asFileUri('', require).fsPath); export class WebClientServer { constructor( - private readonly _connectionToken: string, + private readonly _connectionToken: ServerConnectionToken, private readonly _environmentService: IServerEnvironmentService, private readonly _logService: ILogService, private readonly _productService: IProductService @@ -111,7 +112,7 @@ export class WebClientServer { private _hasCorrectTokenCookie(req: http.IncomingMessage): boolean { const cookies = cookie.parse(req.headers.cookie || ''); - return (cookies['vscode-tkn'] === this._connectionToken); + return this._connectionToken.validate(cookies['vscode-tkn']); } /** @@ -207,14 +208,25 @@ export class WebClientServer { 'manifest-src \'self\';' ].join(' '); - res.writeHead(200, { + const headers: http.OutgoingHttpHeaders = { 'Content-Type': 'text/html', + 'Content-Security-Policy': cspDirectives + }; + if (this._connectionToken.type !== ServerConnectionTokenType.None) { // At this point we know the client has a valid cookie // and we want to set it prolong it to ensure that this // client is valid for another 1 week at least - 'Set-Cookie': cookie.serialize('vscode-tkn', this._connectionToken, { sameSite: 'strict', maxAge: 60 * 60 * 24 * 7 /* 1 week */ }), - 'Content-Security-Policy': cspDirectives - }); + headers['Set-Cookie'] = cookie.serialize( + 'vscode-tkn', + this._connectionToken.value, + { + sameSite: 'strict', + maxAge: 60 * 60 * 24 * 7 /* 1 week */ + } + ); + } + + res.writeHead(200, headers); return res.end(data); } diff --git a/src/vs/server/test/node/serverConnectionToken.test.ts b/src/vs/server/test/node/serverConnectionToken.test.ts index bbfa17f0c45..cea6d7ac4a5 100644 --- a/src/vs/server/test/node/serverConnectionToken.test.ts +++ b/src/vs/server/test/node/serverConnectionToken.test.ts @@ -7,7 +7,7 @@ import * as assert from 'assert'; import * as os from 'os'; import * as path from 'path'; import { getRandomTestPath } from 'vs/base/test/node/testUtils'; -import { parseConnectionToken, ServerConnectionToken, ServerConnectionTokenParseError } from 'vs/server/node/connectionToken'; +import { parseServerConnectionToken, ServerConnectionToken, ServerConnectionTokenParseError, ServerConnectionTokenType } from 'vs/server/node/serverConnectionToken'; import { ServerParsedArgs } from 'vs/server/node/serverEnvironmentService'; import { Promises } from 'vs/base/node/pfs'; @@ -21,33 +21,32 @@ suite('parseServerConnectionToken', () => { assert.strictEqual(isError(r), true); } - // test('no arguments results in error', () => { - // assertIsError(parseConnectionToken({} as ServerParsedArgs)); - // }); + test('no arguments results in error', () => { + assertIsError(parseServerConnectionToken({} as ServerParsedArgs)); + }); test('no arguments with --compatibility generates a token that is not mandatory', () => { - const result = parseConnectionToken({ 'compatibility': '1.63' } as ServerParsedArgs); - assert.ok(result instanceof ServerConnectionToken); - assert.strictEqual(result.isMandatory, false); + const result = parseServerConnectionToken({ 'compatibility': '1.63' } as ServerParsedArgs); + assert.ok(!(result instanceof ServerConnectionTokenParseError)); + assert.ok(result.type === ServerConnectionTokenType.Optional); }); test('--without-connection-token', () => { - const result = parseConnectionToken({ 'without-connection-token': true } as ServerParsedArgs); - assert.ok(result instanceof ServerConnectionToken); - assert.strictEqual(result.value, 'without-connection-token'); - assert.strictEqual(result.isMandatory, false); + const result = parseServerConnectionToken({ 'without-connection-token': true } as ServerParsedArgs); + assert.ok(!(result instanceof ServerConnectionTokenParseError)); + assert.ok(result.type === ServerConnectionTokenType.None); }); test('--without-connection-token --connection-token results in error', () => { - assertIsError(parseConnectionToken({ 'without-connection-token': true, 'connection-token': '0' } as ServerParsedArgs)); + assertIsError(parseServerConnectionToken({ 'without-connection-token': true, 'connection-token': '0' } as ServerParsedArgs)); }); test('--without-connection-token --connection-token-file results in error', () => { - assertIsError(parseConnectionToken({ 'without-connection-token': true, 'connection-token-file': '0' } as ServerParsedArgs)); + assertIsError(parseServerConnectionToken({ 'without-connection-token': true, 'connection-token-file': '0' } as ServerParsedArgs)); }); test('--connection-token-file --connection-token results in error', async () => { - assertIsError(parseConnectionToken({ 'connection-token-file': '0', 'connection-token': '0' } as ServerParsedArgs)); + assertIsError(parseServerConnectionToken({ 'connection-token-file': '0', 'connection-token': '0' } as ServerParsedArgs)); }); test('--connection-token-file', async () => { @@ -56,26 +55,26 @@ suite('parseServerConnectionToken', () => { const filename = path.join(testDir, 'connection-token-file'); const connectionToken = `12345-123-abc`; await Promises.writeFile(filename, connectionToken); - const result = parseConnectionToken({ 'connection-token-file': filename } as ServerParsedArgs); - assert.ok(result instanceof ServerConnectionToken); + const result = parseServerConnectionToken({ 'connection-token-file': filename } as ServerParsedArgs); + assert.ok(!(result instanceof ServerConnectionTokenParseError)); + assert.ok(result.type === ServerConnectionTokenType.Mandatory); assert.strictEqual(result.value, connectionToken); - assert.strictEqual(result.isMandatory, true); await Promises.rm(testDir); }); test('--connection-token', async () => { const connectionToken = `12345-123-abc`; - const result = parseConnectionToken({ 'connection-token': connectionToken } as ServerParsedArgs); - assert.ok(result instanceof ServerConnectionToken); + const result = parseServerConnectionToken({ 'connection-token': connectionToken } as ServerParsedArgs); + assert.ok(!(result instanceof ServerConnectionTokenParseError)); + assert.ok(result.type === ServerConnectionTokenType.Mandatory); assert.strictEqual(result.value, connectionToken); - assert.strictEqual(result.isMandatory, true); }); test('--connection-token --compatibility marks a as not mandatory', async () => { const connectionToken = `12345-123-abc`; - const result = parseConnectionToken({ 'connection-token': connectionToken, 'compatibility': '1.63' } as ServerParsedArgs); - assert.ok(result instanceof ServerConnectionToken); + const result = parseServerConnectionToken({ 'connection-token': connectionToken, 'compatibility': '1.63' } as ServerParsedArgs); + assert.ok(!(result instanceof ServerConnectionTokenParseError)); + assert.ok(result.type === ServerConnectionTokenType.Optional); assert.strictEqual(result.value, connectionToken); - assert.strictEqual(result.isMandatory, false); }); }); diff --git a/src/vs/workbench/api/browser/mainThreadSCM.ts b/src/vs/workbench/api/browser/mainThreadSCM.ts index 60b414cf488..6a7c8bad373 100644 --- a/src/vs/workbench/api/browser/mainThreadSCM.ts +++ b/src/vs/workbench/api/browser/mainThreadSCM.ts @@ -6,7 +6,7 @@ import { URI, UriComponents } from 'vs/base/common/uri'; import { Event, Emitter } from 'vs/base/common/event'; import { IDisposable, DisposableStore, combinedDisposable } from 'vs/base/common/lifecycle'; -import { ISCMService, ISCMRepository, ISCMProvider, ISCMResource, ISCMResourceGroup, ISCMResourceDecorations, IInputValidation, ISCMViewService, InputValidationType } from 'vs/workbench/contrib/scm/common/scm'; +import { ISCMService, ISCMRepository, ISCMProvider, ISCMResource, ISCMResourceGroup, ISCMResourceDecorations, IInputValidation, ISCMViewService, InputValidationType, ISCMActionButtonDescriptor } from 'vs/workbench/contrib/scm/common/scm'; import { ExtHostContext, MainThreadSCMShape, ExtHostSCMShape, SCMProviderFeatures, SCMRawResourceSplices, SCMGroupFeatures, MainContext, IExtHostContext } from '../common/extHost.protocol'; import { Command } from 'vs/editor/common/languages'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; @@ -120,7 +120,7 @@ class MainThreadSCMProvider implements ISCMProvider { get commitTemplate(): string { return this.features.commitTemplate || ''; } get acceptInputCommand(): Command | undefined { return this.features.acceptInputCommand; } - get actionButton(): Command | undefined { return this.features.actionButton ?? undefined; } + get actionButton(): ISCMActionButtonDescriptor | undefined { return this.features.actionButton ?? undefined; } get statusBarCommands(): Command[] | undefined { return this.features.statusBarCommands; } get count(): number | undefined { return this.features.count; } diff --git a/src/vs/workbench/api/browser/mainThreadTunnelService.ts b/src/vs/workbench/api/browser/mainThreadTunnelService.ts index e0127baa03e..924fa83ab9d 100644 --- a/src/vs/workbench/api/browser/mainThreadTunnelService.ts +++ b/src/vs/workbench/api/browser/mainThreadTunnelService.ts @@ -8,7 +8,7 @@ import { MainThreadTunnelServiceShape, IExtHostContext, MainContext, ExtHostCont import { TunnelDto } from 'vs/workbench/api/common/extHostTunnelService'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { CandidatePort, IRemoteExplorerService, makeAddress, PORT_AUTO_FORWARD_SETTING, PORT_AUTO_SOURCE_SETTING, PORT_AUTO_SOURCE_SETTING_OUTPUT, PORT_AUTO_SOURCE_SETTING_PROCESS, TunnelSource } from 'vs/workbench/services/remote/common/remoteExplorerService'; -import { ITunnelProvider, ITunnelService, TunnelCreationOptions, TunnelProviderFeatures, TunnelOptions, RemoteTunnel, isPortPrivileged, ProvidedPortAttributes, PortAttributesProvider, TunnelProtocol } from 'vs/platform/remote/common/tunnel'; +import { ITunnelProvider, ITunnelService, TunnelCreationOptions, TunnelProviderFeatures, TunnelOptions, RemoteTunnel, isPortPrivileged, ProvidedPortAttributes, PortAttributesProvider, TunnelProtocol } from 'vs/platform/tunnel/common/tunnel'; import { Disposable } from 'vs/base/common/lifecycle'; import type { TunnelDescription } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index f5c4d47c56b..b5bc807f568 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -34,7 +34,7 @@ import { IMarkerData } from 'vs/platform/markers/common/markers'; import { IProgressOptions, IProgressStep } from 'vs/platform/progress/common/progress'; import * as quickInput from 'vs/platform/quickinput/common/quickInput'; import { IRemoteConnectionData, RemoteAuthorityResolverErrorCode, ResolverResult, TunnelDescription } from 'vs/platform/remote/common/remoteAuthorityResolver'; -import { ProvidedPortAttributes, TunnelCreationOptions, TunnelOptions, TunnelProviderFeatures } from 'vs/platform/remote/common/tunnel'; +import { ProvidedPortAttributes, TunnelCreationOptions, TunnelOptions, TunnelProviderFeatures } from 'vs/platform/tunnel/common/tunnel'; import { ClassifiedEvent, GDPRClassification, StrictPropertyCheck } from 'vs/platform/telemetry/common/gdprTypings'; import { ITelemetryInfo, TelemetryLevel } from 'vs/platform/telemetry/common/telemetry'; import { ICreateContributedTerminalProfileOptions, IProcessProperty, IShellLaunchConfigDto, ITerminalEnvironment, ITerminalLaunchError, ITerminalProfile, TerminalLocation } from 'vs/platform/terminal/common/terminal'; @@ -1085,10 +1085,15 @@ export interface SCMProviderFeatures { count?: number; commitTemplate?: string; acceptInputCommand?: modes.Command; - actionButton?: ICommandDto | null; + actionButton?: SCMActionButtonDto | null; statusBarCommands?: ICommandDto[]; } +export interface SCMActionButtonDto { + command: ICommandDto; + description?: string; +} + export interface SCMGroupFeatures { hideWhenEmpty?: boolean; } diff --git a/src/vs/workbench/api/common/extHostSCM.ts b/src/vs/workbench/api/common/extHostSCM.ts index 8a9effffd6b..34c5bbf5176 100644 --- a/src/vs/workbench/api/common/extHostSCM.ts +++ b/src/vs/workbench/api/common/extHostSCM.ts @@ -506,18 +506,22 @@ class ExtHostSourceControl implements vscode.SourceControl { } private _actionButtonDisposables = new MutableDisposable<DisposableStore>(); - private _actionButton: vscode.Command | undefined; - get actionButton(): vscode.Command | undefined { + private _actionButton: vscode.SourceControlActionButton | undefined; + get actionButton(): vscode.SourceControlActionButton | undefined { checkProposedApiEnabled(this._extension, 'scmActionButton'); return this._actionButton; } - set actionButton(actionButton: vscode.Command | undefined) { + set actionButton(actionButton: vscode.SourceControlActionButton | undefined) { checkProposedApiEnabled(this._extension, 'scmActionButton'); this._actionButtonDisposables.value = new DisposableStore(); this._actionButton = actionButton; - const internal = actionButton !== undefined ? this._commands.converter.toInternal(this._actionButton, this._actionButtonDisposables.value) : undefined; + const internal = actionButton !== undefined ? + { + command: this._commands.converter.toInternal(actionButton.command, this._actionButtonDisposables.value), + description: actionButton.description + } : undefined; this.#proxy.$updateSourceControl(this.handle, { actionButton: internal ?? null }); } diff --git a/src/vs/workbench/api/common/extHostTunnelService.ts b/src/vs/workbench/api/common/extHostTunnelService.ts index e861d188f72..461db2f4499 100644 --- a/src/vs/workbench/api/common/extHostTunnelService.ts +++ b/src/vs/workbench/api/common/extHostTunnelService.ts @@ -6,7 +6,7 @@ import { ExtHostTunnelServiceShape } from 'vs/workbench/api/common/extHost.protocol'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import * as vscode from 'vscode'; -import { ProvidedPortAttributes, RemoteTunnel, TunnelCreationOptions, TunnelOptions, TunnelPrivacyId } from 'vs/platform/remote/common/tunnel'; +import { ProvidedPortAttributes, RemoteTunnel, TunnelCreationOptions, TunnelOptions, TunnelPrivacyId } from 'vs/platform/tunnel/common/tunnel'; import { IDisposable } from 'vs/base/common/lifecycle'; import { Emitter } from 'vs/base/common/event'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; diff --git a/src/vs/workbench/api/node/extHostTunnelService.ts b/src/vs/workbench/api/node/extHostTunnelService.ts index 03d0f5229f8..f480c0fa3e7 100644 --- a/src/vs/workbench/api/node/extHostTunnelService.ts +++ b/src/vs/workbench/api/node/extHostTunnelService.ts @@ -17,7 +17,7 @@ import * as types from 'vs/workbench/api/common/extHostTypes'; import { isLinux } from 'vs/base/common/platform'; import { IExtHostTunnelService, TunnelDto } from 'vs/workbench/api/common/extHostTunnelService'; import { Event, Emitter } from 'vs/base/common/event'; -import { TunnelOptions, TunnelCreationOptions, ProvidedPortAttributes, ProvidedOnAutoForward, isLocalhost, isAllInterfaces } from 'vs/platform/remote/common/tunnel'; +import { TunnelOptions, TunnelCreationOptions, ProvidedPortAttributes, ProvidedOnAutoForward, isLocalhost, isAllInterfaces } from 'vs/platform/tunnel/common/tunnel'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { MovingAverage } from 'vs/base/common/numbers'; import { CandidatePort } from 'vs/workbench/services/remote/common/remoteExplorerService'; diff --git a/src/vs/workbench/browser/contextkeys.ts b/src/vs/workbench/browser/contextkeys.ts index 781200500af..125f92237c4 100644 --- a/src/vs/workbench/browser/contextkeys.ts +++ b/src/vs/workbench/browser/contextkeys.ts @@ -18,7 +18,8 @@ import { WorkbenchState, IWorkspaceContextService } from 'vs/platform/workspace/ import { SideBarVisibleContext } from 'vs/workbench/common/viewlet'; import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService'; import { PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext } from 'vs/workbench/common/panel'; -import { getRemoteName, getVirtualWorkspaceScheme } from 'vs/platform/remote/common/remoteHosts'; +import { getRemoteName } from 'vs/platform/remote/common/remoteHosts'; +import { getVirtualWorkspaceScheme } from 'vs/platform/workspace/common/virtualWorkspace'; import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { isNative } from 'vs/base/common/platform'; import { IEditorResolverService } from 'vs/workbench/services/editor/common/editorResolverService'; diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 023d21d4968..b8afad55fcd 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -45,7 +45,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { ILogService } from 'vs/platform/log/common/log'; import { DeferredPromise, Promises } from 'vs/base/common/async'; import { IBannerService } from 'vs/workbench/services/banner/browser/bannerService'; -import { getVirtualWorkspaceScheme } from 'vs/platform/remote/common/remoteHosts'; +import { getVirtualWorkspaceScheme } from 'vs/platform/workspace/common/virtualWorkspace'; import { Schemas } from 'vs/base/common/network'; import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; import { ActivitybarPart } from 'vs/workbench/browser/parts/activitybar/activitybarPart'; diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 829ea03e13d..a08aee86cf0 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -42,7 +42,7 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { Schemas } from 'vs/base/common/network'; import { withNullAsUndefined } from 'vs/base/common/types'; import { Codicon } from 'vs/base/common/codicons'; -import { getVirtualWorkspaceLocation } from 'vs/platform/remote/common/remoteHosts'; +import { getVirtualWorkspaceLocation } from 'vs/platform/workspace/common/virtualWorkspace'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; diff --git a/src/vs/workbench/contrib/audioCues/browser/audioCueContribution.ts b/src/vs/workbench/contrib/audioCues/browser/audioCueContribution.ts index 649cfd1e0dc..8ed9f6087ca 100644 --- a/src/vs/workbench/contrib/audioCues/browser/audioCueContribution.ts +++ b/src/vs/workbench/contrib/audioCues/browser/audioCueContribution.ts @@ -133,7 +133,7 @@ export class AudioCueContribution extends DisposableStore implements IWorkbenchC hadMarker = hasMarker; const regionAtLine = foldingModel?.getRegionAtLine(lineNumber); - const hasFolding = regionAtLine?.startLineNumber === lineNumber; + const hasFolding = !regionAtLine ? false : regionAtLine.isCollapsed && regionAtLine.startLineNumber === lineNumber; if (hasFolding && !hadFoldedArea) { this.handleFoldedAreasOnLine(); } diff --git a/src/vs/workbench/contrib/audioCues/browser/media/break.opus b/src/vs/workbench/contrib/audioCues/browser/media/break.opus Binary files differindex e025dd7e784..a500eec2fb8 100644 --- a/src/vs/workbench/contrib/audioCues/browser/media/break.opus +++ b/src/vs/workbench/contrib/audioCues/browser/media/break.opus diff --git a/src/vs/workbench/contrib/audioCues/browser/media/error.opus b/src/vs/workbench/contrib/audioCues/browser/media/error.opus Binary files differindex eade1900ece..9f0170086f9 100644 --- a/src/vs/workbench/contrib/audioCues/browser/media/error.opus +++ b/src/vs/workbench/contrib/audioCues/browser/media/error.opus diff --git a/src/vs/workbench/contrib/audioCues/browser/media/foldedAreas.opus b/src/vs/workbench/contrib/audioCues/browser/media/foldedAreas.opus Binary files differindex f840aaa2953..c5be7c1b84c 100644 --- a/src/vs/workbench/contrib/audioCues/browser/media/foldedAreas.opus +++ b/src/vs/workbench/contrib/audioCues/browser/media/foldedAreas.opus diff --git a/src/vs/workbench/contrib/debug/browser/linkDetector.ts b/src/vs/workbench/contrib/debug/browser/linkDetector.ts index 7f30bb7ec46..cab19a5d584 100644 --- a/src/vs/workbench/contrib/debug/browser/linkDetector.ts +++ b/src/vs/workbench/contrib/debug/browser/linkDetector.ts @@ -16,7 +16,7 @@ import { IPathService } from 'vs/workbench/services/path/common/pathService'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; import { localize } from 'vs/nls'; -import { ITunnelService } from 'vs/platform/remote/common/tunnel'; +import { ITunnelService } from 'vs/platform/tunnel/common/tunnel'; const CONTROL_CODES = '\\u0000-\\u0020\\u007f-\\u009f'; const WEB_LINK_REGEX = new RegExp('(?:[a-zA-Z][a-zA-Z0-9+.-]{2,}:\\/\\/|data:|www\\.)[^\\s' + CONTROL_CODES + '"]{2,}[^\\s' + CONTROL_CODES + '"\')}\\],:;.!?]', 'ug'); diff --git a/src/vs/workbench/contrib/debug/test/browser/linkDetector.test.ts b/src/vs/workbench/contrib/debug/test/browser/linkDetector.test.ts index f95af99c8c8..3e7ed14e6f8 100644 --- a/src/vs/workbench/contrib/debug/test/browser/linkDetector.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/linkDetector.test.ts @@ -10,7 +10,7 @@ import { LinkDetector } from 'vs/workbench/contrib/debug/browser/linkDetector'; import { isWindows } from 'vs/base/common/platform'; import { WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { URI } from 'vs/base/common/uri'; -import { ITunnelService } from 'vs/platform/remote/common/tunnel'; +import { ITunnelService } from 'vs/platform/tunnel/common/tunnel'; import { DisposableStore } from 'vs/base/common/lifecycle'; suite('Debug - Link Detector', () => { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index a639ecb32c4..f83f656ca0a 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -59,7 +59,7 @@ import { errorIcon, infoIcon, manageExtensionIcon, syncEnabledIcon, syncIgnoredI import { isIOS, isWeb } from 'vs/base/common/platform'; import { IExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService'; import { IWorkspaceTrustEnablementService, IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust'; -import { isVirtualWorkspace } from 'vs/platform/remote/common/remoteHosts'; +import { isVirtualWorkspace } from 'vs/platform/workspace/common/virtualWorkspace'; import { escapeMarkdownSyntaxTokens, IMarkdownString, MarkdownString } from 'vs/base/common/htmlContent'; import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; import { ViewContainerLocation } from 'vs/workbench/common/views'; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index 5c5ab4a29ae..edd8b3cda81 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -49,7 +49,7 @@ import { IPreferencesService } from 'vs/workbench/services/preferences/common/pr import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService'; -import { isVirtualWorkspace } from 'vs/platform/remote/common/remoteHosts'; +import { isVirtualWorkspace } from 'vs/platform/workspace/common/virtualWorkspace'; import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust'; import { IWorkbenchLayoutService, Position } from 'vs/workbench/services/layout/browser/layoutService'; import { HoverPosition } from 'vs/base/browser/ui/hover/hoverWidget'; diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts index a5a52385a93..b7d6e54158c 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts @@ -34,7 +34,8 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { NativeURLService } from 'vs/platform/url/common/urlService'; import { URI } from 'vs/base/common/uri'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { ExtensionType, IExtension, ExtensionKind } from 'vs/platform/extensions/common/extensions'; +import { ExtensionType, IExtension } from 'vs/platform/extensions/common/extensions'; +import { ExtensionKind } from 'vs/platform/environment/common/environment'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { RemoteAgentService } from 'vs/workbench/services/remote/electron-sandbox/remoteAgentService'; import { ISharedProcessService } from 'vs/platform/ipc/electron-sandbox/services'; diff --git a/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts b/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts index 1021166c131..e4748579291 100644 --- a/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts +++ b/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts @@ -21,7 +21,7 @@ import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal import { IDebugService } from 'vs/workbench/contrib/debug/common/debug'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { isWeb, OperatingSystem } from 'vs/base/common/platform'; -import { isPortPrivileged, ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel'; +import { isPortPrivileged, ITunnelService, RemoteTunnel } from 'vs/platform/tunnel/common/tunnel'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity'; diff --git a/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts b/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts index 711097dba4a..8c7b07b6354 100644 --- a/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts +++ b/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts @@ -25,7 +25,8 @@ import { isWeb } from 'vs/base/common/platform'; import { once } from 'vs/base/common/functional'; import { truncate } from 'vs/base/common/strings'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { getRemoteName, getVirtualWorkspaceLocation } from 'vs/platform/remote/common/remoteHosts'; +import { getRemoteName } from 'vs/platform/remote/common/remoteHosts'; +import { getVirtualWorkspaceLocation } from 'vs/platform/workspace/common/virtualWorkspace'; import { getCodiconAriaLabel } from 'vs/base/common/codicons'; import { ILogService } from 'vs/platform/log/common/log'; import { ReloadWindowAction } from 'vs/workbench/browser/actions/windowActions'; diff --git a/src/vs/workbench/contrib/remote/browser/tunnelView.ts b/src/vs/workbench/contrib/remote/browser/tunnelView.ts index 3d9236aa451..298de73ffbd 100644 --- a/src/vs/workbench/contrib/remote/browser/tunnelView.ts +++ b/src/vs/workbench/contrib/remote/browser/tunnelView.ts @@ -34,7 +34,8 @@ import { IThemeService, registerThemingParticipant, ThemeIcon } from 'vs/platfor import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPane'; import { URI } from 'vs/base/common/uri'; -import { isAllInterfaces, isLocalhost, isPortPrivileged, ITunnelService, RemoteTunnel, TunnelPrivacy, TunnelPrivacyId, TunnelProtocol } from 'vs/platform/remote/common/tunnel'; +import { isAllInterfaces, isLocalhost, isPortPrivileged, ITunnelService, RemoteTunnel, TunnelPrivacyId, TunnelProtocol } from 'vs/platform/tunnel/common/tunnel'; +import { TunnelPrivacy } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; diff --git a/src/vs/workbench/contrib/remote/common/tunnelFactory.ts b/src/vs/workbench/contrib/remote/common/tunnelFactory.ts index d07daa8f4af..135231c51f6 100644 --- a/src/vs/workbench/contrib/remote/common/tunnelFactory.ts +++ b/src/vs/workbench/contrib/remote/common/tunnelFactory.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { ITunnelService, TunnelOptions, RemoteTunnel, TunnelCreationOptions, ITunnel, TunnelProtocol, TunnelPrivacyId } from 'vs/platform/remote/common/tunnel'; +import { ITunnelService, TunnelOptions, RemoteTunnel, TunnelCreationOptions, ITunnel, TunnelProtocol, TunnelPrivacyId } from 'vs/platform/tunnel/common/tunnel'; import { Disposable } from 'vs/base/common/lifecycle'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; diff --git a/src/vs/workbench/contrib/scm/browser/media/scm.css b/src/vs/workbench/contrib/scm/browser/media/scm.css index be9ec9e5944..88664cdd502 100644 --- a/src/vs/workbench/contrib/scm/browser/media/scm.css +++ b/src/vs/workbench/contrib/scm/browser/media/scm.css @@ -198,19 +198,43 @@ } .scm-view .button-container { - padding-left: 11px; - height: 100%; display: flex; + height: 100%; + padding-left: 11px; align-items: center; } +.scm-view .button-container > .monaco-description-button { + flex-direction: row; + flex-wrap: wrap; + height: 30px; + overflow: hidden; +} + +.scm-view .button-container > .monaco-description-button > .monaco-button-label { + flex-grow: 1; + width: 0; + overflow: hidden; +} + +.scm-view .button-container > .monaco-description-button > .monaco-button-description { + flex-basis: 100%; +} + +.scm-view .button-container > .monaco-description-button > .monaco-button-label, +.scm-view .button-container > .monaco-description-button > .monaco-button-description { + font-style: inherit; +} + .scm-view .button-container .codicon.codicon-cloud-upload, .scm-view .button-container .codicon.codicon-sync { + line-height: 22px; margin: 0 0.4em 0 0; } .scm-view .button-container .codicon.codicon-arrow-up, .scm-view .button-container .codicon.codicon-arrow-down { + line-height: 22px; font-size: small !important; margin: 0 0.2em 0 0; } diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index d50fe7dd7cc..bb666ecdda5 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -10,7 +10,7 @@ import { IDisposable, Disposable, DisposableStore, combinedDisposable, dispose, import { ViewPane, IViewPaneOptions, ViewAction } from 'vs/workbench/browser/parts/views/viewPane'; import { append, $, Dimension, asCSSUrl, trackFocus, clearNode } from 'vs/base/browser/dom'; import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list'; -import { ISCMResourceGroup, ISCMResource, InputValidationType, ISCMRepository, ISCMInput, IInputValidation, ISCMViewService, ISCMViewVisibleRepositoryChangeEvent, ISCMService, SCMInputChangeReason, VIEW_PANE_ID, ISCMActionButton } from 'vs/workbench/contrib/scm/common/scm'; +import { ISCMResourceGroup, ISCMResource, InputValidationType, ISCMRepository, ISCMInput, IInputValidation, ISCMViewService, ISCMViewVisibleRepositoryChangeEvent, ISCMService, SCMInputChangeReason, VIEW_PANE_ID, ISCMActionButton, ISCMActionButtonDescriptor } from 'vs/workbench/contrib/scm/common/scm'; import { ResourceLabels, IResourceLabel, IFileLabelOptions } from 'vs/workbench/browser/labels'; import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -82,8 +82,7 @@ import { API_OPEN_DIFF_EDITOR_COMMAND_ID, API_OPEN_EDITOR_COMMAND_ID } from 'vs/ import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { MarkdownRenderer } from 'vs/editor/browser/core/markdownRenderer'; -import { Button } from 'vs/base/browser/ui/button/button'; -import { Command } from 'vs/editor/common/languages'; +import { Button, ButtonWithDescription } from 'vs/base/browser/ui/button/button'; import { INotificationService } from 'vs/platform/notification/common/notification'; type TreeElement = ISCMRepository | ISCMInput | ISCMActionButton | ISCMResourceGroup | IResourceNode<ISCMResource, ISCMResourceGroup> | ISCMResource; @@ -95,7 +94,7 @@ interface ISCMLayout { } interface ActionButtonTemplate { - readonly actionButton: ScmActionButton; + readonly actionButton: SCMActionButton; disposable: IDisposable; readonly templateDisposable: IDisposable; } @@ -120,7 +119,7 @@ class ActionButtonRenderer implements ICompressibleTreeRenderer<ISCMActionButton container.parentElement!.parentElement!.classList.add('force-no-hover'); const buttonContainer = append(container, $('.button-container')); - const actionButton = new ScmActionButton(buttonContainer, this.commandService, this.themeService, this.notificationService); + const actionButton = new SCMActionButton(buttonContainer, this.commandService, this.themeService, this.notificationService); return { actionButton, disposable: Disposable.None, templateDisposable: actionButton }; } @@ -832,7 +831,7 @@ export class SCMAccessibilityProvider implements IListAccessibilityProvider<Tree } else if (isSCMInput(element)) { return localize('input', "Source Control Input"); } else if (isSCMActionButton(element)) { - return element.button?.title ?? ''; + return element.button?.command.title ?? ''; } else if (isSCMResourceGroup(element)) { return element.label; } else { @@ -2479,8 +2478,8 @@ registerThemingParticipant((theme, collector) => { } }); -export class ScmActionButton implements IDisposable { - private button: Button | undefined; +export class SCMActionButton implements IDisposable { + private button: Button | ButtonWithDescription | undefined; private readonly disposables = new MutableDisposable<DisposableStore>(); constructor( @@ -2495,19 +2494,26 @@ export class ScmActionButton implements IDisposable { this.disposables?.dispose(); } - - setButton(button: Command | undefined): void { + setButton(button: ISCMActionButtonDescriptor | undefined): void { // Clear old button this.clear(); if (!button) { return; } - this.button = new Button(this.container, { title: button.tooltip, supportIcons: true }); - this.button.label = button.title; + if (button.description) { + // ButtonWithDescription + this.button = new ButtonWithDescription(this.container, { supportIcons: true }); + (this.button as ButtonWithDescription).description = button.description; + } else { + // Button + this.button = new Button(this.container, { supportIcons: true }); + } + + this.button.label = button.command.title; this.button.onDidClick(async () => { try { - await this.commandService.executeCommand(button!.id, ...(button!.arguments || [])); + await this.commandService.executeCommand(button.command.id, ...(button.command.arguments || [])); } catch (ex) { this.notificationService.error(ex); } diff --git a/src/vs/workbench/contrib/scm/common/scm.ts b/src/vs/workbench/contrib/scm/common/scm.ts index 9ecf3c32001..f69a3186779 100644 --- a/src/vs/workbench/contrib/scm/common/scm.ts +++ b/src/vs/workbench/contrib/scm/common/scm.ts @@ -65,7 +65,7 @@ export interface ISCMProvider extends IDisposable { readonly onDidChangeCommitTemplate: Event<string>; readonly onDidChangeStatusBarCommands?: Event<Command[]>; readonly acceptInputCommand?: Command; - readonly actionButton?: Command; + readonly actionButton?: ISCMActionButtonDescriptor; readonly statusBarCommands?: Command[]; readonly onDidChange: Event<void>; @@ -97,10 +97,15 @@ export interface ISCMInputChangeEvent { readonly reason?: SCMInputChangeReason; } +export interface ISCMActionButtonDescriptor { + command: Command; + description?: string +} + export interface ISCMActionButton { readonly type: 'actionButton'; readonly repository: ISCMRepository; - readonly button?: Command; + readonly button?: ISCMActionButtonDescriptor; } export interface ISCMInput { diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 581d17f807f..f43bad7b5d3 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -3013,7 +3013,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer switch (taskSource) { case TaskSourceKind.User: tasksExistInFile = this.configHasTasks(configValue.userValue); target = ConfigurationTarget.USER; break; case TaskSourceKind.WorkspaceFile: tasksExistInFile = this.configHasTasks(configValue.workspaceValue); target = ConfigurationTarget.WORKSPACE; break; - default: tasksExistInFile = this.configHasTasks(configValue.value); target = ConfigurationTarget.WORKSPACE_FOLDER; + default: tasksExistInFile = this.configHasTasks(configValue.workspaceFolderValue); target = ConfigurationTarget.WORKSPACE_FOLDER; } let content; if (!tasksExistInFile) { @@ -3156,7 +3156,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } } } - if (needsCreateOrOpen) { + if (needsCreateOrOpen || (taskMap.get(USER_TASKS_GROUP_KEY).length === tasks.length)) { let label = stats[0] !== undefined ? openLabel : createLabel; if (entries.length) { entries.push({ type: 'separator' }); diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalBaseLinkProvider.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalBaseLinkProvider.ts index 3adccc7f5c2..28d61c962d0 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalBaseLinkProvider.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalBaseLinkProvider.ts @@ -3,13 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import type { ILinkProvider, ILink } from 'xterm'; +import type { ILinkProvider } from 'xterm'; import { TerminalLink } from 'vs/workbench/contrib/terminal/browser/links/terminalLink'; export abstract class TerminalBaseLinkProvider implements ILinkProvider { private _activeLinks: TerminalLink[] | undefined; - async provideLinks(bufferLineNumber: number, callback: (links: ILink[] | undefined) => void): Promise<void> { + async provideLinks(bufferLineNumber: number, callback: (links: TerminalLink[] | undefined) => void): Promise<void> { this._activeLinks?.forEach(l => l.dispose); this._activeLinks = await this._provideLinks(bufferLineNumber); callback(this._activeLinks); diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalLink.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalLink.ts index e1421365a86..3815b4d2498 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalLink.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalLink.ts @@ -19,6 +19,7 @@ export const FOLDER_NOT_IN_WORKSPACE_LABEL = localize('openFolder', 'Open folder export class TerminalLink extends DisposableStore implements ILink { decorations: ILinkDecorations; + asyncActivate: Promise<void> | undefined; private _tooltipScheduler: RunOnceScheduler | undefined; private _hoverListeners: DisposableStore | undefined; @@ -26,12 +27,14 @@ export class TerminalLink extends DisposableStore implements ILink { private readonly _onInvalidated = new Emitter<void>(); get onInvalidated(): Event<void> { return this._onInvalidated.event; } + + constructor( private readonly _xterm: Terminal, readonly range: IBufferRange, readonly text: string, private readonly _viewportY: number, - private readonly _activateCallback: (event: MouseEvent | undefined, uri: string) => void, + private readonly _activateCallback: (event: MouseEvent | undefined, uri: string) => Promise<void>, private readonly _tooltipCallback: (link: TerminalLink, viewportRange: IViewportRange, modifierDownCallback?: () => void, modifierUpCallback?: () => void) => void, private readonly _isHighConfidenceLink: boolean, readonly label: string | undefined, @@ -53,7 +56,9 @@ export class TerminalLink extends DisposableStore implements ILink { } activate(event: MouseEvent | undefined, text: string): void { - this._activateCallback(event, text); + // Trigger the xterm.js callback synchronously but track the promise resolution so we can + // use it in tests + this.asyncActivate = this._activateCallback(event, text); } hover(event: MouseEvent, text: string): void { diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalLinkManager.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalLinkManager.ts index 811243cb7da..88906a8e3db 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalLinkManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalLinkManager.ts @@ -27,7 +27,7 @@ import { IXtermCore } from 'vs/workbench/contrib/terminal/browser/xterm-private' import { TerminalHover, ILinkHoverTargetOptions } from 'vs/workbench/contrib/terminal/browser/widgets/terminalHoverWidget'; import { TerminalLink } from 'vs/workbench/contrib/terminal/browser/links/terminalLink'; import { TerminalExternalLinkProviderAdapter } from 'vs/workbench/contrib/terminal/browser/links/terminalExternalLinkProviderAdapter'; -import { ITunnelService } from 'vs/platform/remote/common/tunnel'; +import { ITunnelService } from 'vs/platform/tunnel/common/tunnel'; import { XtermTerminal } from 'vs/workbench/contrib/terminal/browser/xterm/xtermTerminal'; import { TerminalCapabilityStoreMultiplexer } from 'vs/workbench/contrib/terminal/common/capabilities/terminalCapabilityStore'; diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalProtocolLinkProvider.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalProtocolLinkProvider.ts index 137cfbf6fda..226b0a48aa9 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalProtocolLinkProvider.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalProtocolLinkProvider.ts @@ -22,7 +22,7 @@ export class TerminalProtocolLinkProvider extends TerminalBaseLinkProvider { static id: string = 'TerminalProtocolLinkProvider'; constructor( private readonly _xterm: Terminal, - private readonly _activateCallback: (event: MouseEvent | undefined, uri: string) => void, + private readonly _activateCallback: (event: MouseEvent | undefined, uri: string) => Promise<void>, private readonly _wrapLinkHandler: (handler: (event: MouseEvent | undefined, link: string) => void) => XtermLinkMatcherHandler, private readonly _tooltipCallback: (link: TerminalLink, viewportRange: IViewportRange, modifierDownCallback?: () => void, modifierUpCallback?: () => void) => void, private readonly _validationCallback: (link: string, callback: (result: { uri: URI, isDirectory: boolean } | undefined) => void) => void, diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index 95cfb3064a6..2968201665a 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -17,8 +17,9 @@ import { DeserializedTerminalEditorInput } from 'vs/workbench/contrib/terminal/b import { TerminalEditorInput } from 'vs/workbench/contrib/terminal/browser/terminalEditorInput'; import { EditorGroupColumn } from 'vs/workbench/services/editor/common/editorGroupColumn'; import { IKeyMods } from 'vs/platform/quickinput/common/quickInput'; -import { IMarker } from 'xterm'; +import { ILink, IMarker } from 'xterm'; import { ITerminalCapabilityStore } from 'vs/workbench/contrib/terminal/common/capabilities/capabilities'; +import { IDetectedLinks, TerminalLinkManager } from 'vs/workbench/contrib/terminal/browser/links/terminalLinkManager'; export const ITerminalService = createDecorator<ITerminalService>('terminalService'); export const ITerminalEditorService = createDecorator<ITerminalEditorService>('terminalEditorService'); @@ -884,6 +885,16 @@ export interface IXtermTerminal { * When process capabilites are updated, update the command tracker */ upgradeCommandTracker(): void; + + /* + * Activates the most recent link for the type + */ + openRecentLink(linkManager: TerminalLinkManager, type: 'file' | 'web'): Promise<ILink | undefined>; + + /* + * Gets all of the links + */ + getLinks(linkManager: TerminalLinkManager): Promise<IDetectedLinks>; } export interface IRequestAddInstanceToGroupEvent { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 7d354a8ba8e..de242332542 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -27,9 +27,9 @@ import { ITerminalProcessManager, ProcessState, TERMINAL_VIEW_ID, INavigationMod import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { IDetectedLinks, TerminalLinkManager } from 'vs/workbench/contrib/terminal/browser/links/terminalLinkManager'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; -import { ITerminalInstance, ITerminalExternalLinkProvider, IRequestAddInstanceToGroupEvent, TerminalCommand, TerminalLinkQuickPickEvent } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ITerminalInstance, ITerminalExternalLinkProvider, IRequestAddInstanceToGroupEvent, TerminalCommand } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalProcessManager } from 'vs/workbench/contrib/terminal/browser/terminalProcessManager'; -import type { Terminal as XTermTerminal, ITerminalAddon, ILink } from 'xterm'; +import type { Terminal as XTermTerminal, ITerminalAddon } from 'xterm'; import { NavigationModeAddon } from 'vs/workbench/contrib/terminal/browser/xterm/navigationModeAddon'; import { IViewsService, IViewDescriptorService, ViewContainerLocation } from 'vs/workbench/common/views'; import { EnvironmentVariableInfoWidget } from 'vs/workbench/contrib/terminal/browser/widgets/environmentVariableInfoWidget'; @@ -678,26 +678,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { if (!this.xterm) { throw new Error('no xterm'); } - const wordResults: ILink[] = []; - const webResults: ILink[] = []; - const fileResults: ILink[] = []; - - for (let i = this.xterm.raw.buffer.active.length - 1; i >= this.xterm.raw.buffer.active.viewportY; i--) { - const links = await this._linkManager.getLinks(i); - if (links) { - const { wordLinks, webLinks, fileLinks } = links; - if (wordLinks && wordLinks.length) { - wordResults!.push(...wordLinks.reverse()); - } - if (webLinks && webLinks.length) { - webResults!.push(...webLinks.reverse()); - } - if (fileLinks && fileLinks.length) { - fileResults!.push(...fileLinks.reverse()); - } - } - } - return { wordLinks: wordResults, webLinks: webResults, fileLinks: fileResults }; + return this.xterm.getLinks(this._linkManager); } async openRecentLink(type: 'file' | 'web'): Promise<void> { @@ -707,19 +688,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { if (!this.xterm) { throw new Error('no xterm'); } - - let links; - let i = this.xterm.raw.buffer.active.length; - while ((!links || links.length === 0) && i >= this.xterm.raw.buffer.active.viewportY) { - links = await this._linkManager.getLinksForType(i, type); - i--; - } - - if (!links || links.length < 1) { - return; - } - const event = new TerminalLinkQuickPickEvent(dom.EventType.CLICK); - links[0].activate(event, links[0].text); + this.xterm.openRecentLink(this._linkManager, type); } async runRecent(type: 'command' | 'cwd'): Promise<void> { diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts index 09527bac68f..7e25eb2c1df 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import type { IBuffer, ITheme, RendererType, Terminal as RawXtermTerminal } from 'xterm'; +import type { IBuffer, ILink, ITheme, RendererType, Terminal as RawXtermTerminal } from 'xterm'; import type { ISearchOptions, SearchAddon as SearchAddonType } from 'xterm-addon-search'; import type { Unicode11Addon as Unicode11AddonType } from 'xterm-addon-unicode11'; import type { WebglAddon as WebglAddonType } from 'xterm-addon-webgl'; @@ -15,7 +15,7 @@ import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { TerminalCapability, TerminalLocation, TerminalSettingId } from 'vs/platform/terminal/common/terminal'; import { IShellIntegration, ITerminalFont, TERMINAL_VIEW_ID } from 'vs/workbench/contrib/terminal/common/terminal'; import { isSafari } from 'vs/base/browser/browser'; -import { ICommandTracker, IXtermTerminal } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ICommandTracker, IXtermTerminal, TerminalLinkQuickPickEvent } from 'vs/workbench/contrib/terminal/browser/terminal'; import { ILogService } from 'vs/platform/log/common/log'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { TerminalStorageKeys } from 'vs/workbench/contrib/terminal/common/terminalStorageKeys'; @@ -31,6 +31,8 @@ import { Color } from 'vs/base/common/color'; import { ShellIntegrationAddon, ShellIntegrationInteraction } from 'vs/workbench/contrib/terminal/browser/xterm/shellIntegrationAddon'; import { CognisantCommandTrackerAddon } from 'vs/workbench/contrib/terminal/browser/xterm/cognisantCommandTrackerAddon'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IDetectedLinks, TerminalLinkManager } from 'vs/workbench/contrib/terminal/browser/links/terminalLinkManager'; +import { EventType } from 'vs/base/browser/dom'; // How long in milliseconds should an average frame take to render for a notification to appear // which suggests the fallback DOM-based renderer @@ -117,8 +119,8 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal { macOptionClickForcesSelection: config.macOptionClickForcesSelection, rightClickSelectsWord: config.rightClickBehavior === 'selectWord', fastScrollModifier: 'alt', - fastScrollSensitivity: editorOptions.fastScrollSensitivity, - scrollSensitivity: editorOptions.mouseWheelScrollSensitivity, + fastScrollSensitivity: config.fastScrollSensitivity, + scrollSensitivity: config.mouseWheelScrollSensitivity, rendererType: this._getBuiltInXtermRenderer(config.gpuAcceleration, XtermTerminal._suggestedRendererType), wordSeparator: config.wordSeparators })); @@ -169,6 +171,45 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal { } + async getLinks(linkManager: TerminalLinkManager): Promise<IDetectedLinks> { + const wordResults: ILink[] = []; + const webResults: ILink[] = []; + const fileResults: ILink[] = []; + + for (let i = this.raw.buffer.active.length - 1; i >= this.raw.buffer.active.viewportY; i--) { + const links = await linkManager.getLinks(i); + if (links) { + const { wordLinks, webLinks, fileLinks } = links; + if (wordLinks && wordLinks.length) { + wordResults.push(...wordLinks.reverse()); + } + if (webLinks && webLinks.length) { + webResults.push(...webLinks.reverse()); + } + if (fileLinks && fileLinks.length) { + fileResults.push(...fileLinks.reverse()); + } + } + } + return { webLinks: webResults, fileLinks: fileResults, wordLinks: wordResults }; + } + + async openRecentLink(linkManager: TerminalLinkManager, type: 'file' | 'web'): Promise<ILink | undefined> { + let links; + let i = this.raw.buffer.active.length; + while ((!links || links.length === 0) && i >= this.raw.buffer.active.viewportY) { + links = await linkManager.getLinksForType(i, type); + i--; + } + + if (!links || links.length < 1) { + return undefined; + } + const event = new TerminalLinkQuickPickEvent(EventType.CLICK); + links[0].activate(event, links[0].text); + return links[0]; + } + upgradeCommandTracker(): void { if (this._commandTrackerAddon instanceof CognisantCommandTrackerAddon) { return; diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 3951db9b09a..f0b99a2a5af 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -40,6 +40,13 @@ export const DEFAULT_FONT_WEIGHT = 'normal'; export const DEFAULT_BOLD_FONT_WEIGHT = 'bold'; export const SUGGESTIONS_FONT_WEIGHT = ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900']; + +export interface ITerminalLinkActivationResult { + source: 'editor' | 'quickpick', + link: string +} + + export const ITerminalProfileResolverService = createDecorator<ITerminalProfileResolverService>('terminalProfileResolverService'); export interface ITerminalProfileResolverService { readonly _serviceBrand: undefined; diff --git a/src/vs/workbench/contrib/terminal/test/browser/links/terminalWordLinkProvider.test.ts b/src/vs/workbench/contrib/terminal/test/browser/links/terminalWordLinkProvider.test.ts index c77c1366a7b..09b5eb33e01 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/links/terminalWordLinkProvider.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/links/terminalWordLinkProvider.test.ts @@ -4,19 +4,19 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { Terminal, ILink } from 'xterm'; +import { Terminal } from 'xterm'; import { TerminalWordLinkProvider } from 'vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IEditorOptions, ITextResourceEditorInput } from 'vs/platform/editor/common/editor'; -import { AbstractLogger, DEFAULT_LOG_LEVEL, ILogger, ILogService, LogLevel, LogService, NullLogService } from 'vs/platform/log/common/log'; +import { ILogService, NullLogService } from 'vs/platform/log/common/log'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; import { IViewDescriptorService } from 'vs/workbench/common/views'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; -import { ITerminalConfigHelper, ITerminalConfiguration } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalConfigHelper, ITerminalConfiguration, ITerminalLinkActivationResult } from 'vs/workbench/contrib/terminal/common/terminal'; import { TestContextService, TestStorageService } from 'vs/workbench/test/common/workbenchTestServices'; import { TerminalCapabilityStore } from 'vs/workbench/contrib/terminal/common/capabilities/terminalCapabilityStore'; import { XtermTerminal } from 'vs/workbench/contrib/terminal/browser/xterm/xtermTerminal'; @@ -28,6 +28,16 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { XtermLinkMatcherHandler } from 'vs/workbench/contrib/terminal/browser/links/terminalLinkManager'; +import { FileService } from 'vs/platform/files/common/fileService'; +import { IResolveMetadataFileOptions, IFileStatWithMetadata, IResolveFileOptions, IFileStat, IFileService } from 'vs/platform/files/common/files'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { TerminalLinkQuickPickEvent } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { EventType } from 'vs/base/browser/dom'; +import { URI } from 'vs/base/common/uri'; +import { TerminalLink } from 'vs/workbench/contrib/terminal/browser/links/terminalLink'; +import { isWindows } from 'vs/base/common/platform'; +const pathSeparator = isWindows ? '\\' : '/'; +const filePrefix = 'file:' + pathSeparator.repeat(2); const defaultTerminalConfig: Partial<ITerminalConfiguration> = { fontFamily: 'monospace', @@ -41,81 +51,57 @@ const defaultTerminalConfig: Partial<ITerminalConfiguration> = { wordSeparators: ' ()[]{}\',"`─‘’' }; +class TestFileService extends FileService { + private _files: string[] = []; + override async resolve(resource: URI, options: IResolveMetadataFileOptions): Promise<IFileStatWithMetadata>; + override async resolve(resource: URI, options?: IResolveFileOptions): Promise<IFileStat>; + override async resolve(resource: URI, options?: IResolveFileOptions): Promise<IFileStat> { + if (this._files.includes(resource.path)) { + return { isFile: true, isDirectory: false, isSymbolicLink: false } as IFileStat; + } else { + return { isFile: false, isDirectory: false, isSymbolicLink: false } as IFileStat; + } + } + setFiles(files: string[]): void { + this._files = files; + } +} + class TestCommandTracker extends CognisantCommandTrackerAddon { private _currentCwd: string | undefined; override getCwdForLine(y: number): string { - return this._currentCwd || ''; + if (!this._currentCwd) { + throw new Error('no cwd'); + } + return this._currentCwd; } setCwd(cwd: string): void { this._currentCwd = cwd; } } -class TestLogger extends AbstractLogger implements ILogger { - - public logs: string[] = []; - - constructor(logLevel: LogLevel = DEFAULT_LOG_LEVEL) { - super(); - this.setLevel(logLevel); - } - - trace(message: string, ...args: any[]): void { - if (this.getLevel() <= LogLevel.Trace) { - this.logs.push(message + JSON.stringify(args)); - } - } - - debug(message: string, ...args: any[]): void { - if (this.getLevel() <= LogLevel.Debug) { - this.logs.push(message); - } - } - - info(message: string, ...args: any[]): void { - if (this.getLevel() <= LogLevel.Info) { - this.logs.push(message); - } - } - - warn(message: string | Error, ...args: any[]): void { - if (this.getLevel() <= LogLevel.Warning) { - this.logs.push(message.toString()); - } - } - - error(message: string, ...args: any[]): void { - if (this.getLevel() <= LogLevel.Error) { - this.logs.push(message); - } - } - - critical(message: string, ...args: any[]): void { - if (this.getLevel() <= LogLevel.Critical) { - this.logs.push(message); - } - } - - override dispose(): void { } - flush(): void { } -} class TestXtermTerminal extends XtermTerminal { - override get commandTracker(): TestCommandTracker { return new TestCommandTracker(new LogService(new TestLogger())); } + private _commandTracker = new TestCommandTracker(new NullLogService()); + override get commandTracker(): TestCommandTracker { + return this._commandTracker; + } } suite('Workbench - TerminalWordLinkProvider', () => { let instantiationService: TestInstantiationService; let configurationService: TestConfigurationService; let themeService: TestThemeService; + let fileService: TestFileService; let viewDescriptorService: TestViewDescriptorService; let xterm: TestXtermTerminal; let configHelper: ITerminalConfigHelper; let capabilities: TerminalCapabilityStore; - let activateResult: ITerminalLinkActivationResult | undefined; + let activationResult: ITerminalLinkActivationResult | undefined; setup(() => { instantiationService = new TestInstantiationService(); + fileService = new TestFileService(new NullLogService()); configurationService = new TestConfigurationService(); instantiationService.stub(IConfigurationService, configurationService); configurationService = new TestConfigurationService({ @@ -127,29 +113,34 @@ suite('Workbench - TerminalWordLinkProvider', () => { integrated: defaultTerminalConfig } }); + themeService = new TestThemeService(); viewDescriptorService = new TestViewDescriptorService(); capabilities = new TerminalCapabilityStore(); instantiationService = new TestInstantiationService(); + instantiationService.stub(IWorkbenchEnvironmentService, { + remoteAuthority: undefined + } as Partial<IWorkbenchEnvironmentService>); instantiationService.stub(IConfigurationService, configurationService); instantiationService.stub(ILogService, new NullLogService()); instantiationService.stub(IStorageService, new TestStorageService()); instantiationService.stub(IThemeService, themeService); + instantiationService.stub(IFileService, fileService); instantiationService.stub(IViewDescriptorService, viewDescriptorService); instantiationService.stub(IWorkspaceContextService, new TestContextService()); // Allow intercepting link activations - activateResult = undefined; + activationResult = undefined; instantiationService.stub(IQuickInputService, { quickAccess: { show(link: string) { - activateResult = { link, source: 'quickpick' }; + activationResult = { link, source: 'quickpick' }; } } } as Partial<IQuickInputService>); instantiationService.stub(IEditorService, { async openEditor(editor: ITextResourceEditorInput): Promise<any> { - activateResult = { + activationResult = { source: 'editor', link: editor.resource?.toString() }; @@ -161,19 +152,25 @@ suite('Workbench - TerminalWordLinkProvider', () => { xterm = instantiationService.createInstance(TestXtermTerminal, Terminal, configHelper, 80, 30, TerminalLocation.Panel); }); - async function assertLink(text: string, expected: { text: string, range: [number, number][], linkActivationResult?: ITerminalLinkActivationResult }[], registerCwdDetectionCapability?: boolean) { + async function assertLink(text: string, expected: { text: string, range: [number, number][], linkActivationResult?: ITerminalLinkActivationResult }[], registerCwdDetectionCapability?: boolean, cwd?: string, files?: string[]) { xterm?.dispose(); xterm = instantiationService.createInstance(TestXtermTerminal, Terminal, configHelper, 80, 30, TerminalLocation.Panel); + if (cwd) { + xterm.commandTracker.setCwd(cwd); + } if (registerCwdDetectionCapability) { capabilities = new TerminalCapabilityStore(); capabilities.add(TerminalCapability.CwdDetection, new CwdDetectionCapability()); } + if (files) { + fileService.setFiles(files); + } // We don't want to cancel the event or anything from the tests so just pass in a wrapped // link handler that does nothing. const testWrappedLinkHandler = (handler: (event: MouseEvent | undefined, link: string) => void): XtermLinkMatcherHandler => { return async (event: MouseEvent | undefined, link: string) => { - handler(event, link); + await handler(event, link); }; }; const provider: TerminalWordLinkProvider = instantiationService.createInstance(TerminalWordLinkProvider, @@ -187,16 +184,16 @@ suite('Workbench - TerminalWordLinkProvider', () => { await new Promise<void>(r => xterm.raw.write(text, r)); // Ensure all links are provided - const links = (await new Promise<ILink[] | undefined>(r => provider.provideLinks(1, r)))!; + const links = (await new Promise<TerminalLink[] | undefined>(r => provider.provideLinks(1, r)))!; const actualLinks = await Promise.all(links.map(async e => { - if (registerCwdDetectionCapability) { - // HACK: Xterm.js works on sync links only but we use async links - await e.activate(new MouseEvent('click'), e.text); + if (capabilities.has(TerminalCapability.CwdDetection)) { + e.activate(new TerminalLinkQuickPickEvent(EventType.CLICK), e.text); + await e.asyncActivate; } return { text: e.text, range: e.range, - activateText: registerCwdDetectionCapability ? activateResult : undefined + activationResult }; })); @@ -206,7 +203,7 @@ suite('Workbench - TerminalWordLinkProvider', () => { start: { x: e.range[0][0], y: e.range[0][1] }, end: { x: e.range[1][0], y: e.range[1][1] }, }, - activateText: e.linkActivationResult + activationResult: e.linkActivationResult })); assert.deepStrictEqual(actualLinks, expectedVerbose); assert.strictEqual(links.length, expected.length); @@ -282,13 +279,25 @@ suite('Workbench - TerminalWordLinkProvider', () => { await assertLink('file:///C:/users/test/file.txt ', [{ range: [[1, 1], [30, 1]], text: 'file:///C:/users/test/file.txt' }]); await assertLink('file:///C:/users/test/file.txt:1:10 ', [{ range: [[1, 1], [35, 1]], text: 'file:///C:/users/test/file.txt:1:10' }]); }); - test.skip('should add cwd to link', async () => { - xterm.commandTracker.setCwd('/Users/home/folder'); - await assertLink('file.txt ', [{ range: [[1, 1], [8, 1]], text: 'file.txt', linkActivationResult: { link: '/Users/home/folder/file.txt', source: 'editor' } }], true); + test('should apply the cwd to the link only when the file exists and cwdDetection is enabled', async () => { + if (!isWindows) { + const { text, cwd, filePath } = generateLinkArgs(['Users', 'home', 'folder']); + await assertLink(text, [{ range: [[1, 1], [8, 1]], text, linkActivationResult: { link: filePrefix + filePath, source: 'editor' } }], true, cwd, [filePath]); + await assertLink(text, [{ range: [[1, 1], [8, 1]], text, linkActivationResult: { link: text, source: 'quickpick' } }], true, cwd, []); + } + }); + test('should not add the cwd to the link when cwdDetection is not enabled', async () => { + if (!isWindows) { + const { text, cwd, filePath } = generateLinkArgs(['Users', 'home', 'folder']); + await assertLink(text, [{ range: [[1, 1], [8, 1]], text, linkActivationResult: undefined }], false, cwd, [filePath]); + await assertLink(text, [{ range: [[1, 1], [8, 1]], text, linkActivationResult: undefined }], false, cwd, []); + } }); }); -interface ITerminalLinkActivationResult { - source: 'editor' | 'quickpick', - link: string +function generateLinkArgs(folders: string[]): { text: string, cwd: string, filePath: string } { + const cwd = ['', ...folders].join(pathSeparator); + const text = 'file.txt'; + const filePath = [cwd, text].join(pathSeparator); + return { text, cwd, filePath }; } diff --git a/src/vs/workbench/contrib/terminal/test/browser/xterm/xtermTerminal.test.ts b/src/vs/workbench/contrib/terminal/test/browser/xterm/xtermTerminal.test.ts index d23fb008882..cec41378d3f 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/xterm/xtermTerminal.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/xterm/xtermTerminal.test.ts @@ -3,11 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IEvent, Terminal } from 'xterm'; +import { IEvent, ILink, Terminal } from 'xterm'; import { XtermTerminal } from 'vs/workbench/contrib/terminal/browser/xterm/xtermTerminal'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; -import { ITerminalConfigHelper, ITerminalConfiguration, TERMINAL_VIEW_ID } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalConfigHelper, ITerminalConfiguration, ITerminalProcessManager, TERMINAL_VIEW_ID } from 'vs/workbench/contrib/terminal/common/terminal'; import { deepStrictEqual, strictEqual } from 'assert'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; @@ -24,6 +24,9 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServices'; import { isSafari } from 'vs/base/browser/browser'; import { TerminalLocation } from 'vs/platform/terminal/common/terminal'; +import { IDetectedLinks, TerminalLinkManager } from 'vs/workbench/contrib/terminal/browser/links/terminalLinkManager'; +import { ITerminalCapabilityStore } from 'vs/workbench/contrib/terminal/common/capabilities/capabilities'; +import { equals } from 'vs/base/common/objects'; class TestWebglAddon { static shouldThrow = false; @@ -41,6 +44,31 @@ class TestWebglAddon { clearTextureAtlas() { } } +class TestLinkManager extends TerminalLinkManager { + private _links: Map<number, IDetectedLinks | undefined> = new Map(); + override async getLinks(y: number): Promise<IDetectedLinks | undefined> { + return this._links.get(y); + } + override async getLinksForType(y: number, type: 'word' | 'web' | 'file'): Promise<ILink[] | undefined> { + const linkResult = await this.getLinks(y); + if (!linkResult) { + return undefined; + } + const { wordLinks, webLinks, fileLinks } = linkResult; + switch (type) { + case 'word': + return wordLinks; + case 'web': + return webLinks; + case 'file': + return fileLinks; + } + } + setLinks(links: Map<number, IDetectedLinks | undefined>): void { + this._links = links; + } +} + class TestXtermTerminal extends XtermTerminal { webglAddonPromise: Promise<typeof WebglAddon> = Promise.resolve(TestWebglAddon); protected override _getWebglAddonConstructor() { @@ -85,7 +113,7 @@ suite('XtermTerminal', () => { let configurationService: TestConfigurationService; let themeService: TestThemeService; let viewDescriptorService: TestViewDescriptorService; - + let linkManager: TestLinkManager; let xterm: TestXtermTerminal; let configHelper: ITerminalConfigHelper; @@ -111,6 +139,7 @@ suite('XtermTerminal', () => { configHelper = instantiationService.createInstance(TerminalConfigHelper); xterm = instantiationService.createInstance(TestXtermTerminal, Terminal, configHelper, 80, 30, TerminalLocation.Panel); + linkManager = instantiationService.createInstance(TestLinkManager, xterm, upcastPartial<ITerminalProcessManager>({}), upcastPartial<ITerminalCapabilityStore>({})); TestWebglAddon.shouldThrow = false; TestWebglAddon.isEnabled = false; @@ -269,4 +298,88 @@ suite('XtermTerminal', () => { strictEqual(TestWebglAddon.isEnabled, false); }); }); + suite('getLinks and open recent link', async () => { + test('should return no links', async () => { + const map: Map<number, IDetectedLinks> = new Map(); + map.set(0, {}); + linkManager.setLinks(map); + const links = await xterm.getLinks(linkManager); + equals(links, { webLinks: [], wordLinks: [], fileLinks: [] }); + const webLink = await xterm.openRecentLink(linkManager, 'web'); + strictEqual(webLink, undefined); + const fileLink = await xterm.openRecentLink(linkManager, 'file'); + strictEqual(fileLink, undefined); + }); + test('should return word links in order', async () => { + const map: Map<number, IDetectedLinks> = new Map(); + const link1 = { + range: { + start: { x: 1, y: 1 }, end: { x: 14, y: 1 } + }, + text: '1_我是学生.txt', + activate: () => Promise.resolve('') + }; + const link2 = { + range: { + start: { x: 1, y: 1 }, end: { x: 14, y: 1 } + }, + text: '2_我是学生.txt', + activate: () => Promise.resolve('') + }; + map.set(0, { wordLinks: [link1, link2] }); + linkManager.setLinks(map); + const links = await xterm.getLinks(linkManager); + deepStrictEqual(links, { webLinks: [], wordLinks: [link2, link1], fileLinks: [] }); + const webLink = await xterm.openRecentLink(linkManager, 'web'); + strictEqual(webLink, undefined); + const fileLink = await xterm.openRecentLink(linkManager, 'file'); + strictEqual(fileLink, undefined); + }); + test('should return web links in order', async () => { + const map: Map<number, IDetectedLinks> = new Map(); + const link1 = { + range: { start: { x: 5, y: 1 }, end: { x: 40, y: 1 } }, + text: 'https://foo.bar/[this is foo site 1]', + activate: () => Promise.resolve('') + }; + const link2 = { + range: { start: { x: 5, y: 2 }, end: { x: 40, y: 2 } }, + text: 'https://foo.bar/[this is foo site 2]', + activate: () => Promise.resolve('') + }; + map.set(0, { webLinks: [link1, link2] }); + linkManager.setLinks(map); + const links = await xterm.getLinks(linkManager); + deepStrictEqual(links, { webLinks: [link2, link1], wordLinks: [], fileLinks: [] }); + const webLink = await xterm.openRecentLink(linkManager, 'web'); + strictEqual(webLink, link2); + const fileLink = await xterm.openRecentLink(linkManager, 'file'); + strictEqual(fileLink, undefined); + }); + test('should return file links in order', async () => { + const map: Map<number, IDetectedLinks> = new Map(); + const link1 = { + range: { start: { x: 1, y: 1 }, end: { x: 32, y: 1 } }, + text: 'file:///C:/users/test/file_1.txt', + activate: () => Promise.resolve('') + }; + const link2 = { + range: { start: { x: 1, y: 2 }, end: { x: 32, y: 2 } }, + text: 'file:///C:/users/test/file_2.txt', + activate: () => Promise.resolve('') + }; + map.set(0, { fileLinks: [link1, link2] }); + linkManager.setLinks(map); + const links = await xterm.getLinks(linkManager); + deepStrictEqual(links, { webLinks: [], wordLinks: [], fileLinks: [link2, link1] }); + const webLink = await xterm.openRecentLink(linkManager, 'web'); + strictEqual(webLink, undefined); + const fileLink = await xterm.openRecentLink(linkManager, 'file'); + strictEqual(fileLink, link2); + }); + }); }); + +function upcastPartial<T>(v: Partial<T>): T { + return v as T; +} diff --git a/src/vs/workbench/contrib/webview/browser/webviewElement.ts b/src/vs/workbench/contrib/webview/browser/webviewElement.ts index f406a906518..23ee61899a2 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewElement.ts @@ -27,7 +27,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ILogService } from 'vs/platform/log/common/log'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; -import { ITunnelService } from 'vs/platform/remote/common/tunnel'; +import { ITunnelService } from 'vs/platform/tunnel/common/tunnel'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { WebviewPortMappingManager } from 'vs/platform/webview/common/webviewPortMapping'; import { asWebviewUri, decodeAuthority, webviewGenericCspSource, webviewRootResourceAuthority } from 'vs/workbench/api/common/shared/webview'; diff --git a/src/vs/workbench/contrib/webview/electron-sandbox/webviewElement.ts b/src/vs/workbench/contrib/webview/electron-sandbox/webviewElement.ts index 56f60d54d3a..86f4e5f5109 100644 --- a/src/vs/workbench/contrib/webview/electron-sandbox/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/electron-sandbox/webviewElement.ts @@ -16,7 +16,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; -import { ITunnelService } from 'vs/platform/remote/common/tunnel'; +import { ITunnelService } from 'vs/platform/tunnel/common/tunnel'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { FindInFrameOptions, IWebviewManagerService } from 'vs/platform/webview/common/webviewManagerService'; import { WebviewThemeDataProvider } from 'vs/workbench/contrib/webview/browser/themeing'; diff --git a/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts b/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts index 2a61fa5e5c7..3db1ea96f72 100644 --- a/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts +++ b/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts @@ -42,7 +42,7 @@ import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storag import { splitName } from 'vs/base/common/labels'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IBannerItem, IBannerService } from 'vs/workbench/services/banner/browser/bannerService'; -import { isVirtualWorkspace } from 'vs/platform/remote/common/remoteHosts'; +import { isVirtualWorkspace } from 'vs/platform/workspace/common/virtualWorkspace'; import { LIST_WORKSPACE_UNSUPPORTED_EXTENSIONS_COMMAND_ID } from 'vs/workbench/contrib/extensions/common/extensions'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { WORKSPACE_TRUST_SETTING_TAG } from 'vs/workbench/contrib/preferences/common/preferences'; diff --git a/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.ts b/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.ts index 405141da521..6997cbeb844 100644 --- a/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.ts +++ b/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.ts @@ -30,7 +30,7 @@ import { ILabelService } from 'vs/platform/label/common/label'; import { WorkbenchTable } from 'vs/platform/list/browser/listService'; import { Link } from 'vs/platform/opener/browser/link'; import { Registry } from 'vs/platform/registry/common/platform'; -import { isVirtualResource, isVirtualWorkspace } from 'vs/platform/remote/common/remoteHosts'; +import { isVirtualResource, isVirtualWorkspace } from 'vs/platform/workspace/common/virtualWorkspace'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { buttonBackground, buttonSecondaryBackground, editorErrorForeground } from 'vs/platform/theme/common/colorRegistry'; diff --git a/src/vs/workbench/contrib/workspaces/browser/workspaces.contribution.ts b/src/vs/workbench/contrib/workspaces/browser/workspaces.contribution.ts index 8bb7ed829e5..e20024f6c00 100644 --- a/src/vs/workbench/contrib/workspaces/browser/workspaces.contribution.ts +++ b/src/vs/workbench/contrib/workspaces/browser/workspaces.contribution.ts @@ -17,7 +17,7 @@ import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { isVirtualWorkspace } from 'vs/platform/remote/common/remoteHosts'; +import { isVirtualWorkspace } from 'vs/platform/workspace/common/virtualWorkspace'; /** * A workbench contribution that will look for `.code-workspace` files in the root of the diff --git a/src/vs/workbench/electron-sandbox/window.ts b/src/vs/workbench/electron-sandbox/window.ts index 8f8135b26fb..763c8d02384 100644 --- a/src/vs/workbench/electron-sandbox/window.ts +++ b/src/vs/workbench/electron-sandbox/window.ts @@ -46,7 +46,7 @@ import { Schemas } from 'vs/base/common/network'; import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import { posix, dirname } from 'vs/base/common/path'; import { getBaseLabel } from 'vs/base/common/labels'; -import { ITunnelService, extractLocalHostUriMetaDataForPortMapping } from 'vs/platform/remote/common/tunnel'; +import { ITunnelService, extractLocalHostUriMetaDataForPortMapping } from 'vs/platform/tunnel/common/tunnel'; import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService'; import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopy'; diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts index c69f4256db6..d34da178155 100644 --- a/src/vs/workbench/services/environment/browser/environmentService.ts +++ b/src/vs/workbench/services/environment/browser/environmentService.ts @@ -7,7 +7,7 @@ import { Schemas } from 'vs/base/common/network'; import { joinPath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; -import { IExtensionHostDebugParams } from 'vs/platform/environment/common/environment'; +import { ExtensionKind, IExtensionHostDebugParams } from 'vs/platform/environment/common/environment'; import { IPath, IWindowConfiguration } from 'vs/platform/windows/common/windows'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import type { IWorkbenchConstructionOptions as IWorkbenchOptions } from 'vs/workbench/workbench.web.api'; @@ -16,7 +16,6 @@ import { memoize } from 'vs/base/common/decorators'; import { onUnexpectedError } from 'vs/base/common/errors'; import { parseLineAndColumnAware } from 'vs/base/common/extpath'; import { LogLevelToString } from 'vs/platform/log/common/log'; -import { ExtensionKind } from 'vs/platform/extensions/common/extensions'; import { isUndefined } from 'vs/base/common/types'; class BrowserWorkbenchConfiguration implements IWindowConfiguration { diff --git a/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts b/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts index 12bb00c76e7..8e99812215f 100644 --- a/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts +++ b/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts @@ -25,7 +25,7 @@ import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IExtensionBisectService } from 'vs/workbench/services/extensionManagement/browser/extensionBisect'; import { IWorkspaceTrustManagementService, IWorkspaceTrustRequestService } from 'vs/platform/workspace/common/workspaceTrust'; import { IExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService'; -import { isVirtualWorkspace } from 'vs/platform/remote/common/remoteHosts'; +import { isVirtualWorkspace } from 'vs/platform/workspace/common/virtualWorkspace'; import { ILogService } from 'vs/platform/log/common/log'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; diff --git a/src/vs/workbench/services/extensions/browser/extensionService.ts b/src/vs/workbench/services/extensions/browser/extensionService.ts index bc46eaf51eb..33c451aad97 100644 --- a/src/vs/workbench/services/extensions/browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/browser/extensionService.ts @@ -18,7 +18,8 @@ import { RemoteExtensionHost, IRemoteExtensionHostDataProvider, IRemoteExtension import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { WebWorkerExtensionHost } from 'vs/workbench/services/extensions/browser/webWorkerExtensionHost'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ExtensionIdentifier, IExtensionDescription, ExtensionKind, IExtension, ExtensionType } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtensionDescription, IExtension, ExtensionType } from 'vs/platform/extensions/common/extensions'; +import { ExtensionKind } from 'vs/platform/environment/common/environment'; import { FetchFileSystemProvider } from 'vs/workbench/services/extensions/browser/webWorkerFileSystemProvider'; import { Schemas } from 'vs/base/common/network'; import { DisposableStore } from 'vs/base/common/lifecycle'; diff --git a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts index 0dcb821c830..6bf5f9d9b0c 100644 --- a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts +++ b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts @@ -20,7 +20,8 @@ import { ExtensionMessageCollector, ExtensionPoint, ExtensionsRegistry, IExtensi import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; import { ResponsiveState } from 'vs/workbench/services/extensions/common/rpcProtocol'; import { createExtensionHostManager, IExtensionHostManager } from 'vs/workbench/services/extensions/common/extensionHostManager'; -import { ExtensionIdentifier, IExtensionDescription, IExtension, ExtensionKind, IExtensionContributions } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtensionDescription, IExtension, IExtensionContributions } from 'vs/platform/extensions/common/extensions'; +import { ExtensionKind } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; import { parseExtensionDevOptions } from 'vs/workbench/services/extensions/common/extensionDevOptions'; import { IProductService } from 'vs/platform/product/common/productService'; diff --git a/src/vs/workbench/services/extensions/common/extensionManifestPropertiesService.ts b/src/vs/workbench/services/extensions/common/extensionManifestPropertiesService.ts index c3f8fc53dbf..334182c5f17 100644 --- a/src/vs/workbench/services/extensions/common/extensionManifestPropertiesService.ts +++ b/src/vs/workbench/services/extensions/common/extensionManifestPropertiesService.ts @@ -4,7 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IExtensionManifest, ExtensionKind, ExtensionIdentifier, ExtensionUntrustedWorkspaceSupportType, ExtensionVirtualWorkspaceSupportType, IExtensionIdentifier, ALL_EXTENSION_KINDS } from 'vs/platform/extensions/common/extensions'; +import { IExtensionManifest, ExtensionIdentifier, ExtensionUntrustedWorkspaceSupportType, ExtensionVirtualWorkspaceSupportType, IExtensionIdentifier, ALL_EXTENSION_KINDS } from 'vs/platform/extensions/common/extensions'; +import { ExtensionKind } from 'vs/platform/environment/common/environment'; import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { isNonEmptyArray } from 'vs/base/common/arrays'; diff --git a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts index 7ade0916dd0..008efa90af4 100644 --- a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts +++ b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts @@ -11,7 +11,8 @@ import { EXTENSION_IDENTIFIER_PATTERN } from 'vs/platform/extensionManagement/co import { Extensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; import { IMessage } from 'vs/workbench/services/extensions/common/extensions'; -import { ExtensionIdentifier, IExtensionDescription, EXTENSION_CATEGORIES, ExtensionKind } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtensionDescription, EXTENSION_CATEGORIES } from 'vs/platform/extensions/common/extensions'; +import { ExtensionKind } from 'vs/platform/environment/common/environment'; import { allApiProposals } from 'vs/workbench/services/extensions/common/extensionsApiProposals'; import { values } from 'vs/base/common/collections'; import { productSchemaId } from 'vs/platform/product/common/productService'; diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 3da31508314..582609ea7ad 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -24,7 +24,8 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IExtensionService, toExtension, ExtensionHostKind, IExtensionHost, webWorkerExtHostConfig, ExtensionRunningLocation, WebWorkerExtHostConfigValue, extensionRunningLocationToString, extensionHostKindToString } from 'vs/workbench/services/extensions/common/extensions'; import { IExtensionHostManager } from 'vs/workbench/services/extensions/common/extensionHostManager'; -import { ExtensionIdentifier, IExtension, ExtensionType, IExtensionDescription, ExtensionKind } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtension, ExtensionType, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { ExtensionKind } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; import { PersistentConnectionEventType } from 'vs/platform/remote/common/remoteAgentConnection'; import { IProductService } from 'vs/platform/product/common/productService'; diff --git a/src/vs/workbench/services/path/common/pathService.ts b/src/vs/workbench/services/path/common/pathService.ts index 8261fca7925..5f407090cbe 100644 --- a/src/vs/workbench/services/path/common/pathService.ts +++ b/src/vs/workbench/services/path/common/pathService.ts @@ -10,7 +10,7 @@ import { OperatingSystem, OS } from 'vs/base/common/platform'; import { basename } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { getVirtualWorkspaceScheme } from 'vs/platform/remote/common/remoteHosts'; +import { getVirtualWorkspaceScheme } from 'vs/platform/workspace/common/virtualWorkspace'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; diff --git a/src/vs/workbench/services/remote/common/remoteExplorerService.ts b/src/vs/workbench/services/remote/common/remoteExplorerService.ts index da201ae4735..db4f9c6cf83 100644 --- a/src/vs/workbench/services/remote/common/remoteExplorerService.ts +++ b/src/vs/workbench/services/remote/common/remoteExplorerService.ts @@ -8,11 +8,11 @@ import { Event, Emitter } from 'vs/base/common/event'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; -import { ALL_INTERFACES_ADDRESSES, isAllInterfaces, isLocalhost, ITunnelService, LOCALHOST_ADDRESSES, PortAttributesProvider, ProvidedOnAutoForward, ProvidedPortAttributes, RemoteTunnel, TunnelPrivacy, TunnelPrivacyId, TunnelProtocol } from 'vs/platform/remote/common/tunnel'; +import { ALL_INTERFACES_ADDRESSES, isAllInterfaces, isLocalhost, ITunnelService, LOCALHOST_ADDRESSES, PortAttributesProvider, ProvidedOnAutoForward, ProvidedPortAttributes, RemoteTunnel, TunnelPrivacyId, TunnelProtocol } from 'vs/platform/tunnel/common/tunnel'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { IEditableData } from 'vs/workbench/common/views'; import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { TunnelInformation, TunnelDescription, IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { TunnelInformation, TunnelDescription, IRemoteAuthorityResolverService, TunnelPrivacy } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IAddressProvider } from 'vs/platform/remote/common/remoteAgentConnection'; import { isNumber, isObject, isString } from 'vs/base/common/types'; diff --git a/src/vs/workbench/services/remote/browser/tunnelService.ts b/src/vs/workbench/services/tunnel/browser/tunnelService.ts index 4dcabd046bc..d433bb69371 100644 --- a/src/vs/workbench/services/remote/browser/tunnelService.ts +++ b/src/vs/workbench/services/tunnel/browser/tunnelService.ts @@ -7,7 +7,7 @@ import { URI } from 'vs/base/common/uri'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; import { IAddressProvider } from 'vs/platform/remote/common/remoteAgentConnection'; -import { AbstractTunnelService, ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel'; +import { AbstractTunnelService, ITunnelService, RemoteTunnel } from 'vs/platform/tunnel/common/tunnel'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; export class TunnelService extends AbstractTunnelService { diff --git a/src/vs/workbench/services/remote/electron-sandbox/tunnelService.ts b/src/vs/workbench/services/tunnel/electron-sandbox/tunnelService.ts index 12791bcd23a..505f814d12c 100644 --- a/src/vs/workbench/services/remote/electron-sandbox/tunnelService.ts +++ b/src/vs/workbench/services/tunnel/electron-sandbox/tunnelService.ts @@ -7,7 +7,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { URI } from 'vs/base/common/uri'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { ITunnelService, AbstractTunnelService, RemoteTunnel, TunnelPrivacyId } from 'vs/platform/remote/common/tunnel'; +import { ITunnelService, AbstractTunnelService, RemoteTunnel, TunnelPrivacyId } from 'vs/platform/tunnel/common/tunnel'; import { Disposable } from 'vs/base/common/lifecycle'; import { IAddressProvider } from 'vs/platform/remote/common/remoteAgentConnection'; import { ISharedProcessTunnelService } from 'vs/platform/remote/common/sharedProcessTunnelService'; diff --git a/src/vs/workbench/services/workspaces/common/workspaceTrust.ts b/src/vs/workbench/services/workspaces/common/workspaceTrust.ts index 6f315a7cf90..d326da2b0c5 100644 --- a/src/vs/workbench/services/workspaces/common/workspaceTrust.ts +++ b/src/vs/workbench/services/workspaces/common/workspaceTrust.ts @@ -12,7 +12,8 @@ import { IPath } from 'vs/platform/windows/common/windows'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IRemoteAuthorityResolverService, ResolverResult } from 'vs/platform/remote/common/remoteAuthorityResolver'; -import { getRemoteAuthority, isVirtualResource } from 'vs/platform/remote/common/remoteHosts'; +import { getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts'; +import { isVirtualResource } from 'vs/platform/workspace/common/virtualWorkspace'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IWorkspace, IWorkspaceContextService, IWorkspaceFolder, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { WorkspaceTrustRequestOptions, IWorkspaceTrustManagementService, IWorkspaceTrustInfo, IWorkspaceTrustUriInfo, IWorkspaceTrustRequestService, IWorkspaceTrustTransitionParticipant, WorkspaceTrustUriResponse, IWorkspaceTrustEnablementService } from 'vs/platform/workspace/common/workspaceTrust'; diff --git a/src/vs/workbench/workbench.sandbox.main.ts b/src/vs/workbench/workbench.sandbox.main.ts index 7c3a50d21da..4b363e7b336 100644 --- a/src/vs/workbench/workbench.sandbox.main.ts +++ b/src/vs/workbench/workbench.sandbox.main.ts @@ -74,7 +74,7 @@ import 'vs/workbench/services/integrity/electron-sandbox/integrityService'; import 'vs/workbench/services/workingCopy/electron-sandbox/workingCopyBackupService'; import 'vs/workbench/services/checksum/electron-sandbox/checksumService'; import 'vs/platform/remote/electron-sandbox/sharedProcessTunnelService'; -import 'vs/workbench/services/remote/electron-sandbox/tunnelService'; +import 'vs/workbench/services/tunnel/electron-sandbox/tunnelService'; import 'vs/platform/diagnostics/electron-sandbox/diagnosticsService'; import 'vs/platform/profiling/electron-sandbox/profilingService'; import 'vs/platform/telemetry/electron-sandbox/customEndpointTelemetryService'; diff --git a/src/vs/workbench/workbench.web.api.ts b/src/vs/workbench/workbench.web.api.ts index ab8bbf4f72e..bcbafa4283f 100644 --- a/src/vs/workbench/workbench.web.api.ts +++ b/src/vs/workbench/workbench.web.api.ts @@ -17,7 +17,7 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IProductConfiguration } from 'vs/base/common/product'; import { mark } from 'vs/base/common/performance'; import { ICredentialsProvider } from 'vs/platform/credentials/common/credentials'; -import { TunnelProviderFeatures } from 'vs/platform/remote/common/tunnel'; +import { TunnelProviderFeatures } from 'vs/platform/tunnel/common/tunnel'; import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; import { DeferredPromise } from 'vs/base/common/async'; import { asArray } from 'vs/base/common/arrays'; diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts index 961f2d087ba..83d5ccd3287 100644 --- a/src/vs/workbench/workbench.web.main.ts +++ b/src/vs/workbench/workbench.web.main.ts @@ -57,7 +57,7 @@ import 'vs/workbench/services/path/browser/pathService'; import 'vs/workbench/services/themes/browser/browserHostColorSchemeService'; import 'vs/workbench/services/encryption/browser/encryptionService'; import 'vs/workbench/services/workingCopy/browser/workingCopyBackupService'; -import 'vs/workbench/services/remote/browser/tunnelService'; +import 'vs/workbench/services/tunnel/browser/tunnelService'; import 'vs/workbench/services/files/browser/elevatedFileService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; diff --git a/src/vscode-dts/vscode.proposed.scmActionButton.d.ts b/src/vscode-dts/vscode.proposed.scmActionButton.d.ts index f60dc3047c7..82b4b936abc 100644 --- a/src/vscode-dts/vscode.proposed.scmActionButton.d.ts +++ b/src/vscode-dts/vscode.proposed.scmActionButton.d.ts @@ -6,7 +6,12 @@ declare module 'vscode' { // https://github.com/microsoft/vscode/issues/133935 + export interface SourceControlActionButton { + command: Command; + description?: string; + } + export interface SourceControl { - actionButton?: Command; + actionButton?: SourceControlActionButton; } } diff --git a/test/automation/src/playwrightDriver.ts b/test/automation/src/playwrightDriver.ts index 00b6c3ef3d4..fe7fd72050f 100644 --- a/test/automation/src/playwrightDriver.ts +++ b/test/automation/src/playwrightDriver.ts @@ -14,6 +14,7 @@ import * as kill from 'tree-kill'; import { PageFunction } from 'playwright-core/types/structs'; import { Logger, measureAndLog } from './logger'; import type { LaunchOptions } from './code'; +import * as crypto from 'crypto'; const width = 1200; const height = 800; @@ -224,7 +225,7 @@ async function launchServer(options: LaunchOptions) { ...process.env }; - const args = ['--disable-telemetry', '--port', `${port++}`, '--driver', 'web', '--extensions-dir', extensionsPath, '--server-data-dir', agentFolder]; + const args = ['--connection-token', String(crypto.randomInt(0xffffffff)), '--disable-telemetry', '--port', `${port++}`, '--driver', 'web', '--extensions-dir', extensionsPath, '--server-data-dir', agentFolder]; let serverLocation: string | undefined; if (codeServerPath) { |