From 227096140942ce2b08a2358d55b6f28fa1dce573 Mon Sep 17 00:00:00 2001 From: NriotHrreion Date: Mon, 19 Feb 2024 09:27:00 +0800 Subject: [PATCH 01/18] fix #205353 --- src/vs/editor/browser/widget/hoverWidget/hoverWidget.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/vs/editor/browser/widget/hoverWidget/hoverWidget.ts b/src/vs/editor/browser/widget/hoverWidget/hoverWidget.ts index d6f6249675c..8b4814a92c9 100644 --- a/src/vs/editor/browser/widget/hoverWidget/hoverWidget.ts +++ b/src/vs/editor/browser/widget/hoverWidget/hoverWidget.ts @@ -331,10 +331,7 @@ export class HoverWidget extends Widget implements IHoverWidget { }; const targetBounds = this._target.targetElements.map(e => getZoomAccountedBoundingClientRect(e)); - const top = Math.min(...targetBounds.map(e => e.top)); - const right = Math.max(...targetBounds.map(e => e.right)); - const bottom = Math.max(...targetBounds.map(e => e.bottom)); - const left = Math.min(...targetBounds.map(e => e.left)); + const { top, right, bottom, left } = targetBounds[0]; const width = right - left; const height = bottom - top; From ef1836b20c47096ef173f82783cfb01568e82f77 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 23 Feb 2024 11:37:30 -0800 Subject: [PATCH 02/18] Exit early when object is disposed Fixes #206116 --- .../stickyScroll/browser/terminalStickyScrollOverlay.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollOverlay.ts b/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollOverlay.ts index 8868f63beef..4dc0908e54e 100644 --- a/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollOverlay.ts +++ b/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollOverlay.ts @@ -95,6 +95,9 @@ export class TerminalStickyScrollOverlay extends Disposable { // Eagerly create the overlay xtermCtor.then(ctor => { + if (this._store.isDisposed) { + return; + } this._stickyScrollOverlay = this._register(new ctor({ rows: 1, cols: this._xterm.raw.cols, From 45e763b40ca1e5d45e92b66ad04d02f320f0a64b Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 23 Feb 2024 21:52:10 +0100 Subject: [PATCH 03/18] Configure web server base path (#202491) * add support for custom root URL path through command line option `--route-base-url-path` * fix CI/CD errors * refine path joining in `RemoteAuthoritiesImpl` * revert changes to `.gitignore` * avoid RemotePaths global * polish. Option is now called `server-base-path` * revert uri changes * remove unnecessary file * remove unnecessary new line * revert address port change --------- Co-authored-by: sysadmin <> Co-authored-by: Jared C <28533997+oakaigh@users.noreply.github.com> Co-authored-by: sysadmin --- src/vs/base/common/network.ts | 19 ++++++++++++++++--- .../common/extensionResourceLoader.ts | 9 ++++----- .../browser/remoteAuthorityResolverService.ts | 5 +++-- .../remote/common/remoteAgentConnection.ts | 4 ++-- src/vs/platform/remote/common/remoteHosts.ts | 9 --------- .../remoteAuthorityResolverService.ts | 3 +-- .../node/remoteExtensionHostAgentServer.ts | 18 ++++++++++++------ .../server/node/serverEnvironmentService.ts | 7 +++++++ src/vs/server/node/webClientServer.ts | 10 ++++++---- src/vs/workbench/browser/web.api.ts | 7 +++++++ src/vs/workbench/browser/web.main.ts | 4 ++-- .../test/browser/configurationService.test.ts | 6 +++--- .../test/browser/extensionService.test.ts | 2 +- 13 files changed, 64 insertions(+), 39 deletions(-) diff --git a/src/vs/base/common/network.ts b/src/vs/base/common/network.ts index 974d0c21743..5cbdad174b7 100644 --- a/src/vs/base/common/network.ts +++ b/src/vs/base/common/network.ts @@ -7,6 +7,7 @@ import * as errors from 'vs/base/common/errors'; import * as platform from 'vs/base/common/platform'; import { equalsIgnoreCase, startsWithIgnoreCase } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; +import * as paths from 'vs/base/common/path'; export namespace Schemas { @@ -144,7 +145,7 @@ class RemoteAuthoritiesImpl { private readonly _connectionTokens: { [authority: string]: string | undefined } = Object.create(null); private _preferredWebSchema: 'http' | 'https' = 'http'; private _delegate: ((uri: URI) => URI) | null = null; - private _remoteResourcesPath: string = `/${Schemas.vscodeRemoteResource}`; + private _serverRootPath: string = '/'; setPreferredWebSchema(schema: 'http' | 'https') { this._preferredWebSchema = schema; @@ -154,8 +155,16 @@ class RemoteAuthoritiesImpl { this._delegate = delegate; } - setServerRootPath(serverRootPath: string): void { - this._remoteResourcesPath = `${serverRootPath}/${Schemas.vscodeRemoteResource}`; + setServerRootPath(product: { quality?: string; commit?: string }, serverBasePath: string | undefined): void { + this._serverRootPath = getServerRootPath(product, serverBasePath); + } + + getServerRootPath(): string { + return this._serverRootPath; + } + + private get _remoteResourcesPath(): string { + return paths.posix.join(this._serverRootPath, Schemas.vscodeRemoteResource); } set(authority: string, host: string, port: number): void { @@ -202,6 +211,10 @@ class RemoteAuthoritiesImpl { export const RemoteAuthorities = new RemoteAuthoritiesImpl(); +export function getServerRootPath(product: { quality?: string; commit?: string }, basePath: string | undefined): string { + return paths.posix.join(basePath ?? '/', `${product.quality ?? 'oss'}-${product.commit ?? 'dev'}`); +} + /** * A string pointing to a path inside the app. It should not begin with ./ or ../ */ diff --git a/src/vs/platform/extensionResourceLoader/common/extensionResourceLoader.ts b/src/vs/platform/extensionResourceLoader/common/extensionResourceLoader.ts index e63c48d0c2f..f1660961c58 100644 --- a/src/vs/platform/extensionResourceLoader/common/extensionResourceLoader.ts +++ b/src/vs/platform/extensionResourceLoader/common/extensionResourceLoader.ts @@ -17,10 +17,9 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; import { TelemetryLevel } from 'vs/platform/telemetry/common/telemetry'; import { getTelemetryLevel, supportsTelemetry } from 'vs/platform/telemetry/common/telemetryUtils'; import { RemoteAuthorities } from 'vs/base/common/network'; -import { getRemoteServerRootPath } from 'vs/platform/remote/common/remoteHosts'; import { TargetPlatform } from 'vs/platform/extensions/common/extensions'; -const WEB_EXTENSION_RESOURCE_END_POINT = 'web-extension-resource'; +const WEB_EXTENSION_RESOURCE_END_POINT_SEGMENT = '/web-extension-resource/'; export const IExtensionResourceLoaderService = createDecorator('extensionResourceLoaderService'); @@ -67,7 +66,6 @@ export abstract class AbstractExtensionResourceLoaderService implements IExtensi readonly _serviceBrand: undefined; - private readonly _webExtensionResourceEndPoint: string; private readonly _extensionGalleryResourceUrlTemplate: string | undefined; private readonly _extensionGalleryAuthority: string | undefined; @@ -78,7 +76,6 @@ export abstract class AbstractExtensionResourceLoaderService implements IExtensi private readonly _environmentService: IEnvironmentService, private readonly _configurationService: IConfigurationService, ) { - this._webExtensionResourceEndPoint = `${getRemoteServerRootPath(_productService)}/${WEB_EXTENSION_RESOURCE_END_POINT}/`; if (_productService.extensionsGallery) { this._extensionGalleryResourceUrlTemplate = _productService.extensionsGallery.resourceUrlTemplate; this._extensionGalleryAuthority = this._extensionGalleryResourceUrlTemplate ? this._getExtensionGalleryAuthority(URI.parse(this._extensionGalleryResourceUrlTemplate)) : undefined; @@ -144,7 +141,9 @@ export abstract class AbstractExtensionResourceLoaderService implements IExtensi } protected _isWebExtensionResourceEndPoint(uri: URI): boolean { - return uri.path.startsWith(this._webExtensionResourceEndPoint); + const uriPath = uri.path, serverRootPath = RemoteAuthorities.getServerRootPath(); + // test if the path starts with the server root path followed by the web extension resource end point segment + return uriPath.startsWith(serverRootPath) && uriPath.startsWith(WEB_EXTENSION_RESOURCE_END_POINT_SEGMENT, serverRootPath.length); } } diff --git a/src/vs/platform/remote/browser/remoteAuthorityResolverService.ts b/src/vs/platform/remote/browser/remoteAuthorityResolverService.ts index 529d9d74999..8b85c237151 100644 --- a/src/vs/platform/remote/browser/remoteAuthorityResolverService.ts +++ b/src/vs/platform/remote/browser/remoteAuthorityResolverService.ts @@ -15,7 +15,7 @@ import { URI } from 'vs/base/common/uri'; import { ILogService } from 'vs/platform/log/common/log'; import { IProductService } from 'vs/platform/product/common/productService'; import { IRemoteAuthorityResolverService, IRemoteConnectionData, RemoteConnectionType, ResolvedAuthority, ResolvedOptions, ResolverResult, WebSocketRemoteConnection, getRemoteAuthorityPrefix } from 'vs/platform/remote/common/remoteAuthorityResolver'; -import { getRemoteServerRootPath, parseAuthorityWithOptionalPort } from 'vs/platform/remote/common/remoteHosts'; +import { parseAuthorityWithOptionalPort } from 'vs/platform/remote/common/remoteHosts'; export class RemoteAuthorityResolverService extends Disposable implements IRemoteAuthorityResolverService { @@ -34,6 +34,7 @@ export class RemoteAuthorityResolverService extends Disposable implements IRemot isWorkbenchOptionsBasedResolution: boolean, connectionToken: Promise | string | undefined, resourceUriProvider: ((uri: URI) => URI) | undefined, + serverBasePath: string | undefined, @IProductService productService: IProductService, @ILogService private readonly _logService: ILogService, ) { @@ -44,7 +45,7 @@ export class RemoteAuthorityResolverService extends Disposable implements IRemot if (resourceUriProvider) { RemoteAuthorities.setDelegate(resourceUriProvider); } - RemoteAuthorities.setServerRootPath(getRemoteServerRootPath(productService)); + RemoteAuthorities.setServerRootPath(productService, serverBasePath); } async resolveAuthority(authority: string): Promise { diff --git a/src/vs/platform/remote/common/remoteAgentConnection.ts b/src/vs/platform/remote/common/remoteAgentConnection.ts index 9539650dec0..45ebbe8df04 100644 --- a/src/vs/platform/remote/common/remoteAgentConnection.ts +++ b/src/vs/platform/remote/common/remoteAgentConnection.ts @@ -9,6 +9,7 @@ import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cance import { isCancellationError, onUnexpectedError } from 'vs/base/common/errors'; import { Emitter } from 'vs/base/common/event'; import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { RemoteAuthorities } from 'vs/base/common/network'; import * as performance from 'vs/base/common/performance'; import { StopWatch } from 'vs/base/common/stopwatch'; import { generateUuid } from 'vs/base/common/uuid'; @@ -17,7 +18,6 @@ import { Client, ISocket, PersistentProtocol, SocketCloseEventType } from 'vs/ba import { ILogService } from 'vs/platform/log/common/log'; import { RemoteAgentConnectionContext } from 'vs/platform/remote/common/remoteAgentEnvironment'; import { RemoteAuthorityResolverError, RemoteConnection } from 'vs/platform/remote/common/remoteAuthorityResolver'; -import { getRemoteServerRootPath } from 'vs/platform/remote/common/remoteHosts'; import { IRemoteSocketFactoryService } from 'vs/platform/remote/common/remoteSocketFactoryService'; import { ISignService } from 'vs/platform/sign/common/sign'; @@ -232,7 +232,7 @@ async function connectToRemoteExtensionHostAgent(opt let socket: ISocket; try { - socket = await createSocket(options.logService, options.remoteSocketFactoryService, options.connectTo, getRemoteServerRootPath(options), `reconnectionToken=${options.reconnectionToken}&reconnection=${options.reconnectionProtocol ? 'true' : 'false'}`, connectionTypeToString(connectionType), `renderer-${connectionTypeToString(connectionType)}-${options.reconnectionToken}`, timeoutCancellationToken); + socket = await createSocket(options.logService, options.remoteSocketFactoryService, options.connectTo, RemoteAuthorities.getServerRootPath(), `reconnectionToken=${options.reconnectionToken}&reconnection=${options.reconnectionProtocol ? 'true' : 'false'}`, connectionTypeToString(connectionType), `renderer-${connectionTypeToString(connectionType)}-${options.reconnectionToken}`, timeoutCancellationToken); } catch (error) { options.logService.error(`${logPrefix} socketFactory.connect() failed or timed out. Error:`); options.logService.error(error); diff --git a/src/vs/platform/remote/common/remoteHosts.ts b/src/vs/platform/remote/common/remoteHosts.ts index ccc99953c8d..ccf58f9accb 100644 --- a/src/vs/platform/remote/common/remoteHosts.ts +++ b/src/vs/platform/remote/common/remoteHosts.ts @@ -25,15 +25,6 @@ export function getRemoteName(authority: string | undefined): string | undefined return authority.substr(0, pos); } -/** - * The root path to use when accessing the remote server. The path contains the quality and commit of the current build. - * @param product - * @returns - */ -export function getRemoteServerRootPath(product: { quality?: string; commit?: string }): string { - return `/${product.quality ?? 'oss'}-${product.commit ?? 'dev'}`; -} - export function parseAuthorityWithPort(authority: string): { host: string; port: number } { const { host, port } = parseAuthority(authority); if (typeof port === 'undefined') { diff --git a/src/vs/platform/remote/electron-sandbox/remoteAuthorityResolverService.ts b/src/vs/platform/remote/electron-sandbox/remoteAuthorityResolverService.ts index debbe333ae8..9948495f898 100644 --- a/src/vs/platform/remote/electron-sandbox/remoteAuthorityResolverService.ts +++ b/src/vs/platform/remote/electron-sandbox/remoteAuthorityResolverService.ts @@ -11,7 +11,6 @@ import { RemoteAuthorities } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; import { IProductService } from 'vs/platform/product/common/productService'; import { IRemoteAuthorityResolverService, IRemoteConnectionData, RemoteConnectionType, ResolvedAuthority, ResolvedOptions, ResolverResult } from 'vs/platform/remote/common/remoteAuthorityResolver'; -import { getRemoteServerRootPath } from 'vs/platform/remote/common/remoteHosts'; import { ElectronRemoteResourceLoader } from 'vs/platform/remote/electron-sandbox/electronRemoteResourceLoader'; export class RemoteAuthorityResolverService extends Disposable implements IRemoteAuthorityResolverService { @@ -33,7 +32,7 @@ export class RemoteAuthorityResolverService extends Disposable implements IRemot this._canonicalURIRequests = new Map(); this._canonicalURIProvider = null; - RemoteAuthorities.setServerRootPath(getRemoteServerRootPath(productService)); + RemoteAuthorities.setServerRootPath(productService, undefined); // on the desktop we don't support custom server base paths } resolveAuthority(authority: string): Promise { diff --git a/src/vs/server/node/remoteExtensionHostAgentServer.ts b/src/vs/server/node/remoteExtensionHostAgentServer.ts index a90d28e82cf..84664bbb39a 100644 --- a/src/vs/server/node/remoteExtensionHostAgentServer.ts +++ b/src/vs/server/node/remoteExtensionHostAgentServer.ts @@ -15,7 +15,7 @@ import { CharCode } from 'vs/base/common/charCode'; import { isSigPipeError, onUnexpectedError, setUnexpectedErrorHandler } from 'vs/base/common/errors'; import { isEqualOrParent } from 'vs/base/common/extpath'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { connectionTokenQueryName, FileAccess, Schemas } from 'vs/base/common/network'; +import { connectionTokenQueryName, FileAccess, getServerRootPath, Schemas } from 'vs/base/common/network'; import { dirname, join } from 'vs/base/common/path'; import * as perf from 'vs/base/common/performance'; import * as platform from 'vs/base/common/platform'; @@ -33,7 +33,6 @@ import { ILogService } from 'vs/platform/log/common/log'; import { IProductService } from 'vs/platform/product/common/productService'; import { ConnectionType, ConnectionTypeRequest, ErrorMessage, HandshakeMessage, IRemoteExtensionHostStartParams, ITunnelConnectionStartParams, SignRequest } from 'vs/platform/remote/common/remoteAgentConnection'; import { RemoteAgentConnectionContext } from 'vs/platform/remote/common/remoteAgentEnvironment'; -import { getRemoteServerRootPath } from 'vs/platform/remote/common/remoteHosts'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ExtensionHostConnection } from 'vs/server/node/extensionHostConnection'; import { ManagementConnection } from 'vs/server/node/remoteExtensionManagement'; @@ -75,6 +74,7 @@ class RemoteExtensionHostAgentServer extends Disposable implements IServerAPI { private readonly _connectionToken: ServerConnectionToken, private readonly _vsdaMod: typeof vsda | null, hasWebClient: boolean, + serverBasePath: string | undefined, @IServerEnvironmentService private readonly _environmentService: IServerEnvironmentService, @IProductService private readonly _productService: IProductService, @ILogService private readonly _logService: ILogService, @@ -82,13 +82,13 @@ class RemoteExtensionHostAgentServer extends Disposable implements IServerAPI { ) { super(); - this._serverRootPath = getRemoteServerRootPath(_productService); + this._serverRootPath = getServerRootPath(_productService, serverBasePath); this._extHostConnections = Object.create(null); this._managementConnections = Object.create(null); this._allReconnectionTokens = new Set(); this._webClientServer = ( hasWebClient - ? this._instantiationService.createInstance(WebClientServer, this._connectionToken) + ? this._instantiationService.createInstance(WebClientServer, this._connectionToken, serverBasePath ?? '/', this._serverRootPath) : null ); this._logService.info(`Extension host agent started.`); @@ -665,6 +665,7 @@ export interface IServerAPI { } export async function createServer(address: string | net.AddressInfo | null, args: ServerParsedArgs, REMOTE_DATA_FOLDER: string): Promise { + const connectionToken = await determineServerConnectionToken(args); if (connectionToken instanceof ServerConnectionTokenParseError) { console.warn(connectionToken.message); @@ -774,15 +775,20 @@ export async function createServer(address: string | net.AddressInfo | null, arg return null; }); + let serverBasePath = args['server-base-path']; + if (serverBasePath && !serverBasePath.startsWith('/')) { + serverBasePath = `/${serverBasePath}`; + } + const hasWebClient = fs.existsSync(FileAccess.asFileUri('vs/code/browser/workbench/workbench.html').fsPath); if (hasWebClient && address && typeof address !== 'string') { // ships the web ui! const queryPart = (connectionToken.type !== ServerConnectionTokenType.None ? `?${connectionTokenQueryName}=${connectionToken.value}` : ''); - console.log(`Web UI available at http://localhost${address.port === 80 ? '' : `:${address.port}`}/${queryPart}`); + console.log(`Web UI available at http://localhost${address.port === 80 ? '' : `:${address.port}`}${serverBasePath ?? ''}${queryPart}`); } - const remoteExtensionHostAgentServer = instantiationService.createInstance(RemoteExtensionHostAgentServer, socketServer, connectionToken, vsdaMod, hasWebClient); + const remoteExtensionHostAgentServer = instantiationService.createInstance(RemoteExtensionHostAgentServer, socketServer, connectionToken, vsdaMod, hasWebClient, serverBasePath); perf.mark('code/server/ready'); const currentTime = performance.now(); diff --git a/src/vs/server/node/serverEnvironmentService.ts b/src/vs/server/node/serverEnvironmentService.ts index 900815a06d2..fce1842f1bd 100644 --- a/src/vs/server/node/serverEnvironmentService.ts +++ b/src/vs/server/node/serverEnvironmentService.ts @@ -19,6 +19,7 @@ export const serverOptions: OptionDescriptions> = { 'host': { type: 'string', cat: 'o', args: 'ip-address', description: nls.localize('host', "The host name or IP address the server should listen to. If not set, defaults to 'localhost'.") }, 'port': { type: 'string', cat: 'o', args: 'port | port range', description: nls.localize('port', "The port the server should listen to. If 0 is passed a random free port is picked. If a range in the format num-num is passed, a free port from the range (end inclusive) is selected.") }, 'socket-path': { type: 'string', cat: 'o', args: 'path', description: nls.localize('socket-path', "The path to a socket file for the server to listen to.") }, + 'server-base-path': { type: 'string', cat: 'o', args: 'path', description: nls.localize('server-base-path', "The path under which the web UI and the code server is provided. Defaults to '/'.`") }, 'connection-token': { type: 'string', cat: 'o', args: 'token', deprecates: ['connectionToken'], description: nls.localize('connection-token', "A secret that must be included with all requests.") }, 'connection-token-file': { type: 'string', cat: 'o', args: 'path', deprecates: ['connection-secret', 'connectionTokenFile'], description: nls.localize('connection-token-file', "Path to a file that contains the connection token.") }, 'without-connection-token': { type: 'boolean', cat: 'o', description: nls.localize('without-connection-token', "Run without a connection token. Only use this if the connection is secured by other means.") }, @@ -102,6 +103,12 @@ export interface ServerParsedArgs { port?: string; 'socket-path'?: string; + /** + * The path under which the web UI and the code server is provided. + * By defaults it is '/'.` + */ + 'server-base-path'?: string; + /** * A secret token that must be provided by the web client with all requests. * Use only `[0-9A-Za-z\-]`. diff --git a/src/vs/server/node/webClientServer.ts b/src/vs/server/node/webClientServer.ts index a46d6748d0b..feed129fc94 100644 --- a/src/vs/server/node/webClientServer.ts +++ b/src/vs/server/node/webClientServer.ts @@ -28,7 +28,6 @@ import { streamToBuffer } from 'vs/base/common/buffer'; import { IProductConfiguration } from 'vs/base/common/product'; import { isString } from 'vs/base/common/types'; import { CharCode } from 'vs/base/common/charCode'; -import { getRemoteServerRootPath } from 'vs/platform/remote/common/remoteHosts'; import { IExtensionManifest } from 'vs/platform/extensions/common/extensions'; const textMimeType = { @@ -104,13 +103,15 @@ export class WebClientServer { constructor( private readonly _connectionToken: ServerConnectionToken, + private readonly _basePath: string, + readonly serverRootPath: string, @IServerEnvironmentService private readonly _environmentService: IServerEnvironmentService, @ILogService private readonly _logService: ILogService, @IRequestService private readonly _requestService: IRequestService, @IProductService private readonly _productService: IProductService, ) { this._webExtensionResourceUrlTemplate = this._productService.extensionsGallery?.resourceUrlTemplate ? URI.parse(this._productService.extensionsGallery.resourceUrlTemplate) : undefined; - const serverRootPath = getRemoteServerRootPath(_productService); + this._staticRoute = `${serverRootPath}/static`; this._callbackRoute = `${serverRootPath}/callback`; this._webExtensionRoute = `${serverRootPath}/web-extension-resource`; @@ -128,7 +129,7 @@ export class WebClientServer { if (pathname.startsWith(this._staticRoute) && pathname.charCodeAt(this._staticRoute.length) === CharCode.Slash) { return this._handleStatic(req, res, parsedUrl); } - if (pathname === '/') { + if (pathname === this._basePath) { return this._handleRoot(req, res, parsedUrl); } if (pathname === this._callbackRoute) { @@ -262,7 +263,7 @@ export class WebClientServer { newQuery[key] = parsedUrl.query[key]; } } - const newLocation = url.format({ pathname: '/', query: newQuery }); + const newLocation = url.format({ pathname: parsedUrl.pathname, query: newQuery }); responseHeaders['Location'] = newLocation; res.writeHead(302, responseHeaders); @@ -326,6 +327,7 @@ export class WebClientServer { const workbenchWebConfiguration = { remoteAuthority, + remoteBaseUrl: this._basePath, _wrapWebWorkerExtHostInIframe, developmentOptions: { enableSmokeTestDriver: this._environmentService.args['enable-smoke-test-driver'] ? true : undefined, logLevel: this._logService.getLevel() }, settingsSyncOptions: !this._environmentService.isBuilt && this._environmentService.args['enable-sync'] ? { enabled: true } : undefined, diff --git a/src/vs/workbench/browser/web.api.ts b/src/vs/workbench/browser/web.api.ts index 50d76a2213c..196edf88796 100644 --- a/src/vs/workbench/browser/web.api.ts +++ b/src/vs/workbench/browser/web.api.ts @@ -142,6 +142,13 @@ export interface IWorkbenchConstructionOptions { */ readonly remoteAuthority?: string; + /** + * The server base path is the path where the workbench is served from. + * The path must be absolute (start with a slash). + * Corresponds to option `server-base-path` on the server side. + */ + readonly serverBasePath?: string; + /** * The connection token to send to the server. */ diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index 9061af27859..b36400ec9f1 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -284,11 +284,11 @@ export class BrowserMain extends Disposable { // Register them early because they are needed for the profiles initialization await this.registerIndexedDBFileSystemProviders(environmentService, fileService, logService, loggerService, logsPath); - // Remote + const connectionToken = environmentService.options.connectionToken || getCookieValue(connectionTokenCookieName); const remoteResourceLoader = this.configuration.remoteResourceProvider ? new BrowserRemoteResourceLoader(fileService, this.configuration.remoteResourceProvider) : undefined; const resourceUriProvider = this.configuration.resourceUriProvider ?? remoteResourceLoader?.getResourceUriProvider(); - const remoteAuthorityResolverService = new RemoteAuthorityResolverService(!environmentService.expectsResolverExtension, connectionToken, resourceUriProvider, productService, logService); + const remoteAuthorityResolverService = new RemoteAuthorityResolverService(!environmentService.expectsResolverExtension, connectionToken, resourceUriProvider, this.configuration.serverBasePath, productService, logService); serviceCollection.set(IRemoteAuthorityResolverService, remoteAuthorityResolverService); // Signing diff --git a/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts b/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts index 0ca8625187e..a7ef7154baa 100644 --- a/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts +++ b/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts @@ -101,7 +101,7 @@ suite('WorkspaceContextService - Folder', () => { userDataProfileService, environmentService, TestProductService, - disposables.add(new RemoteAuthorityResolverService(false, undefined, undefined, TestProductService, logService)), + disposables.add(new RemoteAuthorityResolverService(false, undefined, undefined, undefined, TestProductService, logService)), new SignService(TestProductService), new NullLogService())), uriIdentityService, new NullLogService(), @@ -152,7 +152,7 @@ suite('WorkspaceContextService - Folder', () => { userDataProfileService, userDataProfilesService, fileService, - disposables.add(new RemoteAgentService(new RemoteSocketFactoryService(), userDataProfileService, environmentService, TestProductService, disposables.add(new RemoteAuthorityResolverService(false, undefined, undefined, TestProductService, logService)), new SignService(TestProductService), new NullLogService())), + disposables.add(new RemoteAgentService(new RemoteSocketFactoryService(), userDataProfileService, environmentService, TestProductService, disposables.add(new RemoteAuthorityResolverService(false, undefined, undefined, undefined, TestProductService, logService)), new SignService(TestProductService), new NullLogService())), uriIdentityService, new NullLogService(), new NullPolicyService())); @@ -184,7 +184,7 @@ suite('WorkspaceContextService - Folder', () => { userDataProfileService, userDataProfilesService, fileService, - disposables.add(new RemoteAgentService(new RemoteSocketFactoryService(), userDataProfileService, environmentService, TestProductService, disposables.add(new RemoteAuthorityResolverService(false, undefined, undefined, TestProductService, logService)), new SignService(TestProductService), new NullLogService())), + disposables.add(new RemoteAgentService(new RemoteSocketFactoryService(), userDataProfileService, environmentService, TestProductService, disposables.add(new RemoteAuthorityResolverService(false, undefined, undefined, undefined, TestProductService, logService)), new SignService(TestProductService), new NullLogService())), uriIdentityService, new NullLogService(), new NullPolicyService())); diff --git a/src/vs/workbench/services/extensions/test/browser/extensionService.test.ts b/src/vs/workbench/services/extensions/test/browser/extensionService.test.ts index 84682289313..3ced43a5f42 100644 --- a/src/vs/workbench/services/extensions/test/browser/extensionService.test.ts +++ b/src/vs/workbench/services/extensions/test/browser/extensionService.test.ts @@ -250,7 +250,7 @@ suite('ExtensionService', () => { [IUserDataProfileService, TestUserDataProfileService], [IUriIdentityService, UriIdentityService], [IRemoteExtensionsScannerService, TestRemoteExtensionsScannerService], - [IRemoteAuthorityResolverService, new RemoteAuthorityResolverService(false, undefined, undefined, testProductService, new NullLogService())] + [IRemoteAuthorityResolverService, new RemoteAuthorityResolverService(false, undefined, undefined, undefined, testProductService, new NullLogService())] ])); extService = instantiationService.get(IExtensionService); }); From 9854e101dd46a652155d0af52224f377f5fa5c0a Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Fri, 23 Feb 2024 13:29:43 -0800 Subject: [PATCH 04/18] fix #204395 --- .../contrib/accessibility/browser/accessibleView.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index cfbfa70a242..3e90ba4b1a8 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -13,7 +13,7 @@ import { Event } from 'vs/base/common/event'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { marked } from 'vs/base/common/marked/marked'; -import { isMacintosh } from 'vs/base/common/platform'; +import { isMacintosh, isWindows } from 'vs/base/common/platform'; import { ThemeIcon } from 'vs/base/common/themables'; import { URI } from 'vs/base/common/uri'; import { IEditorConstructionOptions } from 'vs/editor/browser/config/editorConfiguration'; @@ -458,7 +458,7 @@ export class AccessibleView extends Disposable { const exitThisDialogHint = verbose && !provider.options.position ? localize('exit', '\n\nExit this dialog (Escape).') : ''; this._currentContent = message + provider.provideContent() + readMoreLink + disableHelpHint + exitThisDialogHint; this._updateContextKeys(provider, true); - + const widgetIsFocused = this._editorWidget.hasTextFocus() || this._editorWidget.hasWidgetFocus(); this._getTextModel(URI.from({ path: `accessible-view-${provider.verbositySettingKey}`, scheme: 'accessible-view', fragment: this._currentContent })).then((model) => { if (!model) { return; @@ -483,6 +483,11 @@ export class AccessibleView extends Disposable { } else if (actionsHint) { ariaLabel = localize('accessibility-help-hint', "Accessibility Help, {0}", actionsHint); } + if (isWindows && widgetIsFocused) { + // prevent the screen reader on windows from reading + // the aria label again when it's refocused + ariaLabel = ''; + } this._editorWidget.updateOptions({ ariaLabel }); this._editorWidget.focus(); if (this._currentProvider?.options.position) { From e6132eaeff5fbf19b01f82cda7601c337e8d17ed Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 23 Feb 2024 13:48:39 -0800 Subject: [PATCH 05/18] Preview selected detected link Fixes #206126 --- .../links/browser/terminalLink.ts | 4 ++ .../browser/terminalLinkDetectorAdapter.ts | 6 +- .../links/browser/terminalLinkQuickpick.ts | 69 ++++++++++++++++++- 3 files changed, 73 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLink.ts b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLink.ts index 4654d703f1d..84d9ca4fdd2 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLink.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLink.ts @@ -13,6 +13,8 @@ import { Emitter, Event } from 'vs/base/common/event'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { TerminalLinkType } from 'vs/workbench/contrib/terminalContrib/links/browser/links'; import { IHoverAction } from 'vs/platform/hover/browser/hover'; +import type { URI } from 'vs/base/common/uri'; +import type { IParsedLink } from 'vs/workbench/contrib/terminalContrib/links/browser/terminalLinkParsing'; export class TerminalLink extends DisposableStore implements ILink { decorations: ILinkDecorations; @@ -30,6 +32,8 @@ export class TerminalLink extends DisposableStore implements ILink { private readonly _xterm: Terminal, readonly range: IBufferRange, readonly text: string, + readonly uri: URI | undefined, + readonly parsedLink: IParsedLink | undefined, readonly actions: IHoverAction[] | undefined, private readonly _viewportY: number, private readonly _activateCallback: (event: MouseEvent | undefined, uri: string) => Promise, diff --git a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkDetectorAdapter.ts b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkDetectorAdapter.ts index 8226841a9a0..26c047ea42d 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkDetectorAdapter.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkDetectorAdapter.ts @@ -92,9 +92,7 @@ export class TerminalLinkDetectorAdapter extends Disposable implements ILinkProv const detectedLinks = await this._detector.detect(lines, startLine, endLine); for (const link of detectedLinks) { - links.push(this._createTerminalLink(link, async (event) => { - this._onDidActivateLink.fire({ link, event }); - })); + links.push(this._createTerminalLink(link, async (event) => this._onDidActivateLink.fire({ link, event }))); } return links; @@ -110,6 +108,8 @@ export class TerminalLinkDetectorAdapter extends Disposable implements ILinkProv this._detector.xterm, l.bufferRange, l.text, + l.uri, + l.parsedLink, l.actions, this._detector.xterm.buffer.active.viewportY, activateCallback, diff --git a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts index e954be537df..aafd2e45711 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts @@ -6,24 +6,37 @@ import { EventType } from 'vs/base/browser/dom'; import { Emitter, Event } from 'vs/base/common/event'; import { localize } from 'vs/nls'; -import { QuickPickItem, IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; +import { QuickPickItem, IQuickInputService, IQuickPickItem, QuickInputHideReason } from 'vs/platform/quickinput/common/quickInput'; import { IDetectedLinks } from 'vs/workbench/contrib/terminalContrib/links/browser/terminalLinkManager'; import { TerminalLinkQuickPickEvent } from 'vs/workbench/contrib/terminal/browser/terminal'; import type { ILink } from '@xterm/xterm'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; import { AccessibleViewProviderId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; +import type { TerminalLink } from 'vs/workbench/contrib/terminalContrib/links/browser/terminalLink'; +import { Sequencer } from 'vs/base/common/async'; +import { EditorViewState } from 'vs/workbench/browser/quickaccess'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IHistoryService } from 'vs/workbench/services/history/common/history'; +import { getLinkSuffix } from 'vs/workbench/contrib/terminalContrib/links/browser/terminalLinkParsing'; +import type { ITextEditorSelection } from 'vs/platform/editor/common/editor'; export class TerminalLinkQuickpick extends DisposableStore { + private readonly _editorSequencer = new Sequencer(); + private readonly _editorViewState: EditorViewState; + private readonly _onDidRequestMoreLinks = this.add(new Emitter()); readonly onDidRequestMoreLinks = this._onDidRequestMoreLinks.event; constructor( + @IEditorService private readonly _editorService: IEditorService, + @IHistoryService private readonly _historyService: IHistoryService, @IQuickInputService private readonly _quickInputService: IQuickInputService, @IAccessibleViewService private readonly _accessibleViewService: IAccessibleViewService ) { super(); + this._editorViewState = new EditorViewState(_editorService); } async show(links: { viewport: IDetectedLinks; all: Promise }): Promise { @@ -57,6 +70,9 @@ export class TerminalLinkQuickpick extends DisposableStore { pick.placeholder = localize('terminal.integrated.openDetectedLink', "Select the link to open, type to filter all links"); pick.sortByLabel = false; pick.show(); + if (pick.activeItems.length > 0) { + this._previewItem(pick.activeItems[0]); + } // Show all results only when filtering begins, this is done so the quick pick will show up // ASAP with only the viewport entries. @@ -93,8 +109,20 @@ export class TerminalLinkQuickpick extends DisposableStore { pick.items = picks; })); + disposables.add(pick.onDidChangeActive(async () => { + const [item] = pick.activeItems; + this._previewItem(item); + })); + return new Promise(r => { - disposables.add(pick.onDidHide(() => { + disposables.add(pick.onDidHide(({ reason }) => { + // Restore view state upon cancellation if we changed it + // but only when the picker was closed via explicit user + // gesture and not e.g. when focus was lost because that + // could mean the user clicked into the editor directly. + if (reason === QuickInputHideReason.Gesture) { + this._editorViewState.restore(true); + } disposables.dispose(); if (pick.selectedItems.length === 0) { this._accessibleViewService.showLastProvider(AccessibleViewProviderId.Terminal); @@ -132,10 +160,45 @@ export class TerminalLinkQuickpick extends DisposableStore { } return picks.length > 0 ? picks : undefined; } + + private _previewItem(item: ITerminalLinkQuickPickItem | IQuickPickItem) { + if (item && 'link' in item && item.link && 'uri' in item.link && item.link.uri) { + this._editorViewState.set(); + const link = item.link; + const uri = link.uri; + + + + const linkSuffix = link.parsedLink ? link.parsedLink.suffix : getLinkSuffix(link.text); + let selection: ITextEditorSelection | undefined;// = link.selection; + if (!selection) { + selection = linkSuffix?.row === undefined ? undefined : { + startLineNumber: linkSuffix.row ?? 1, + startColumn: linkSuffix.col ?? 1, + endLineNumber: linkSuffix.rowEnd, + endColumn: linkSuffix.colEnd + }; + } + + + this._editorSequencer.queue(async () => { + // disable and re-enable history service so that we can ignore this history entry + const disposable = this._historyService.suspendTracking(); + try { + await this._editorService.openEditor({ + resource: uri, + options: { preserveFocus: true, revealIfOpened: true, ignoreError: true, selection } + }); + } finally { + disposable.dispose(); + } + }); + } + } } export interface ITerminalLinkQuickPickItem extends IQuickPickItem { - link: ILink; + link: ILink | TerminalLink; } type LinkQuickPickItem = ITerminalLinkQuickPickItem | QuickPickItem; From 228a35f333e8ca1f3fe78b8d5a5ca558eab870f7 Mon Sep 17 00:00:00 2001 From: Vinicius Stock Date: Fri, 23 Feb 2024 17:07:53 -0500 Subject: [PATCH 06/18] Fix accidental dedent for `in` and `when` dedent in Ruby comments --- extensions/ruby/language-configuration.json | 2 +- .../contrib/indentation/test/browser/indentation.test.ts | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/extensions/ruby/language-configuration.json b/extensions/ruby/language-configuration.json index e61f3ac410f..e1125e0bf2b 100644 --- a/extensions/ruby/language-configuration.json +++ b/extensions/ruby/language-configuration.json @@ -26,6 +26,6 @@ ], "indentationRules": { "increaseIndentPattern": "^\\s*((begin|class|(private|protected)\\s+def|def|else|elsif|ensure|for|if|module|rescue|unless|until|when|in|while|case)|([^#]*\\sdo\\b)|([^#]*=\\s*(case|if|unless)))\\b([^#\\{;]|(\"|'|\/).*\\4)*(#.*)?$", - "decreaseIndentPattern": "^\\s*([}\\]]([,)]?\\s*(#|$)|\\.[a-zA-Z_]\\w*\\b)|(end|rescue|ensure|else|elsif)\\b)|((in|when)\\s)" + "decreaseIndentPattern": "^\\s*([}\\]]([,)]?\\s*(#|$)|\\.[a-zA-Z_]\\w*\\b)|(end|rescue|ensure|else|elsif)\\b|(in|when)\\s)" } } diff --git a/src/vs/editor/contrib/indentation/test/browser/indentation.test.ts b/src/vs/editor/contrib/indentation/test/browser/indentation.test.ts index 91666a05d19..516966c9477 100644 --- a/src/vs/editor/contrib/indentation/test/browser/indentation.test.ts +++ b/src/vs/editor/contrib/indentation/test/browser/indentation.test.ts @@ -373,16 +373,23 @@ suite('Editor Contrib - Auto Dedent On Type', () => { ['(', ')'] ], indentationRules: { - decreaseIndentPattern: /\s*([}\]]([,)]?\s*(#|$)|\.[a-zA-Z_]\w*\b)|(end|rescue|ensure|else|elsif)\b)|((in|when)\s)/, + decreaseIndentPattern: /^\s*([}\]]([,)]?\s*(#|$)|\.[a-zA-Z_]\w*\b)|(end|rescue|ensure|else|elsif)\b|(in|when)\s)/, increaseIndentPattern: /^\s*((begin|class|(private|protected)\s+def|def|else|elsif|ensure|for|if|module|rescue|unless|until|when|in|while|case)|([^#]*\sdo\b)|([^#]*=\s*(case|if|unless)))\b([^#\{;]|(\"|'|\/).*\4)*(#.*)?$/, }, }); + viewModel.model.setValue(""); viewModel.type("def foo\n i"); viewModel.type("n", 'keyboard'); assert.strictEqual(model.getValue(), "def foo\n in"); viewModel.type(" ", 'keyboard'); assert.strictEqual(model.getValue(), "def foo\nin "); + + viewModel.model.setValue(""); + viewModel.type(" # in"); + assert.strictEqual(model.getValue(), " # in"); + viewModel.type(" ", 'keyboard'); + assert.strictEqual(model.getValue(), " # in "); improvedLanguageModel.dispose(); }); }); From c43cd02a59395b830e90ccaac720c0433c74371b Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 23 Feb 2024 14:27:53 -0800 Subject: [PATCH 07/18] fix #203722 --- .../contrib/accessibility/browser/accessibleView.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index cfbfa70a242..1e7899de2d4 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -29,6 +29,7 @@ import { IAccessibilityService } from 'vs/platform/accessibility/common/accessib import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { WorkbenchToolBar } from 'vs/platform/actions/browser/toolbar'; import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; +import { ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextViewDelegate, IContextViewService } from 'vs/platform/contextview/browser/contextView'; @@ -157,7 +158,8 @@ export class AccessibleView extends Disposable { @IAccessibilityService private readonly _accessibilityService: IAccessibilityService, @IKeybindingService private readonly _keybindingService: IKeybindingService, @ILayoutService private readonly _layoutService: ILayoutService, - @IMenuService private readonly _menuService: IMenuService + @IMenuService private readonly _menuService: IMenuService, + @ICommandService private readonly _commandService: ICommandService ) { super(); @@ -252,6 +254,10 @@ export class AccessibleView extends Disposable { } } + activateLink(): void { + this._commandService.executeCommand('editor.action.openLink'); + } + showLastProvider(id: AccessibleViewProviderId): void { if (!this._lastProvider || this._lastProvider.options.id !== id) { return; @@ -509,7 +515,9 @@ export class AccessibleView extends Disposable { }; const disposableStore = new DisposableStore(); disposableStore.add(this._editorWidget.onKeyDown((e) => { - if (e.keyCode === KeyCode.Escape || shouldHide(e.browserEvent, this._keybindingService, this._configurationService)) { + if (e.keyCode === KeyCode.Enter) { + this.activateLink(); + } else if (e.keyCode === KeyCode.Escape || shouldHide(e.browserEvent, this._keybindingService, this._configurationService)) { hide(e); } else if (e.keyCode === KeyCode.KeyH && provider.options.readMoreUrl) { const url: string = provider.options.readMoreUrl; From db5fbe6fd930a7fe0a7fe03f432eb1aa71ca14bd Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 23 Feb 2024 14:31:18 -0800 Subject: [PATCH 08/18] simplify --- .../contrib/accessibility/browser/accessibleView.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index 1e7899de2d4..130efbcc20c 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -254,9 +254,6 @@ export class AccessibleView extends Disposable { } } - activateLink(): void { - this._commandService.executeCommand('editor.action.openLink'); - } showLastProvider(id: AccessibleViewProviderId): void { if (!this._lastProvider || this._lastProvider.options.id !== id) { @@ -516,7 +513,7 @@ export class AccessibleView extends Disposable { const disposableStore = new DisposableStore(); disposableStore.add(this._editorWidget.onKeyDown((e) => { if (e.keyCode === KeyCode.Enter) { - this.activateLink(); + this._commandService.executeCommand('editor.action.openLink'); } else if (e.keyCode === KeyCode.Escape || shouldHide(e.browserEvent, this._keybindingService, this._configurationService)) { hide(e); } else if (e.keyCode === KeyCode.KeyH && provider.options.readMoreUrl) { From 9a07ceb9f7f1dc1803913105c816009cb4bfdec1 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 23 Feb 2024 15:02:22 -0800 Subject: [PATCH 09/18] cli: ensure the canonical snap exe is used for the CLI (#206133) Fixes #204907 --- cli/src/commands/tunnels.rs | 7 +--- cli/src/util/machine.rs | 81 ++++++++++++++++++++++++++++++++++++- 2 files changed, 82 insertions(+), 6 deletions(-) diff --git a/cli/src/commands/tunnels.rs b/cli/src/commands/tunnels.rs index f9ae6883075..02a697c1793 100644 --- a/cli/src/commands/tunnels.rs +++ b/cli/src/commands/tunnels.rs @@ -50,10 +50,7 @@ use crate::{ AuthRequired, Next, ServeStreamParams, ServiceContainer, ServiceManager, }, util::{ - app_lock::AppMutex, - command::new_std_command, - errors::{wrap, AnyError, CodeError}, - prereqs::PreReqChecker, + app_lock::AppMutex, command::new_std_command, errors::{wrap, AnyError, CodeError}, machine::canonical_exe, prereqs::PreReqChecker }, }; use crate::{ @@ -231,7 +228,7 @@ pub async fn service( legal::require_consent(&ctx.paths, args.accept_server_license_terms)?; let current_exe = - std::env::current_exe().map_err(|e| wrap(e, "could not get current exe"))?; + canonical_exe().map_err(|e| wrap(e, "could not get current exe"))?; manager .register( diff --git a/cli/src/util/machine.rs b/cli/src/util/machine.rs index 4c7b6729e43..a573231c2b9 100644 --- a/cli/src/util/machine.rs +++ b/cli/src/util/machine.rs @@ -3,7 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -use std::{path::Path, time::Duration}; + use std::{ + ffi::OsString, + path::{Path, PathBuf}, + time::Duration, +}; use sysinfo::{Pid, PidExt, ProcessExt, System, SystemExt}; pub fn process_at_path_exists(pid: u32, name: &Path) -> bool { @@ -71,3 +75,78 @@ pub async fn wait_until_exe_deleted(current_exe: &Path, poll_ms: u64) { tokio::time::sleep(duration).await; } } + +/// Gets the canonical current exe location, referring to the "current" symlink +/// if running inside snap. +pub fn canonical_exe() -> std::io::Result { + canonical_exe_inner( + std::env::current_exe(), + std::env::var_os("SNAP"), + std::env::var_os("SNAP_REVISION"), + ) +} + +#[inline(always)] +#[allow(unused_variables)] +fn canonical_exe_inner( + exe: std::io::Result, + snap: Option, + rev: Option, +) -> std::io::Result { + let exe = exe?; + + #[cfg(target_os = "linux")] + if let (Some(snap), Some(rev)) = (snap, rev) { + if !exe.starts_with(snap) { + return Ok(exe); + } + + let mut out = PathBuf::new(); + for part in exe.iter() { + if part == rev { + out.push("current") + } else { + out.push(part) + } + } + + return Ok(out); + } + + Ok(exe) +} + +#[cfg(test)] +mod tests { + use super::*; + use std::path::PathBuf; + + #[test] + #[cfg(target_os = "linux")] + fn test_canonical_exe_in_snap() { + let exe = canonical_exe_inner( + Ok(PathBuf::from("/snap/my-snap/1234/some/exe")), + Some("/snap/my-snap/1234".into()), + Some("1234".into()), + ) + .unwrap(); + assert_eq!(exe, PathBuf::from("/snap/my-snap/current/some/exe")); + } + + #[test] + fn test_canonical_exe_not_in_snap() { + let exe = canonical_exe_inner( + Ok(PathBuf::from("/not-in-snap")), + Some("/snap/my-snap/1234".into()), + Some("1234".into()), + ) + .unwrap(); + assert_eq!(exe, PathBuf::from("/not-in-snap")); + } + + #[test] + fn test_canonical_exe_not_in_snap2() { + let exe = canonical_exe_inner(Ok(PathBuf::from("/not-in-snap")), None, None).unwrap(); + assert_eq!(exe, PathBuf::from("/not-in-snap")); + } +} From 47002157e96daf36dfebb911ae5724a6e7710e60 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 23 Feb 2024 15:18:47 -0800 Subject: [PATCH 10/18] debug: fix loading state in triggered bp widget (#206140) Fixes #204699 --- .../contrib/debug/browser/breakpointWidget.ts | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts b/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts index 7289d25ba29..59a3a4dd4bf 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts @@ -265,30 +265,28 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi private createTriggerBreakpointInput(container: HTMLElement) { const breakpoints = this.debugService.getModel().getBreakpoints().filter(bp => bp !== this.breakpoint); + const breakpointOptions: ISelectOptionItem[] = [ + { text: nls.localize('noTriggerByBreakpoint', 'None'), isDisabled: true }, + ...breakpoints.map(bp => ({ + text: `${this.labelService.getUriLabel(bp.uri, { relative: true })}: ${bp.lineNumber}`, + description: nls.localize('triggerByLoading', 'Loading...') + })), + ]; const index = breakpoints.findIndex((bp) => this.breakpoint?.triggeredBy === bp.getId()); - let select = 0; - if (index > -1) { - select = index + 1; - } - - Promise.all(breakpoints.map(async (bp): Promise => ({ - text: `${this.labelService.getUriLabel(bp.uri, { relative: true })}: ${bp.lineNumber}`, - description: await this.textModelService.createModelReference(bp.uri).then(ref => { + for (const [i, bp] of breakpoints.entries()) { + this.textModelService.createModelReference(bp.uri).then(ref => { try { - return ref.object.textEditorModel.getLineContent(bp.lineNumber).trim(); + breakpointOptions[i + 1].description = ref.object.textEditorModel.getLineContent(bp.lineNumber).trim(); } finally { ref.dispose(); } - }, () => undefined), - }))).then(breakpoints => { - selectBreakpointBox.setOptions([ - { text: nls.localize('noTriggerByBreakpoint', 'None') }, - ...breakpoints - ], select); - }); + }).catch(() => { + breakpointOptions[i + 1].description = nls.localize('noBpSource', 'Could not load source.'); + }); + } - const selectBreakpointBox = this.selectBreakpointBox = new SelectBox([{ text: nls.localize('triggerByLoading', 'Loading...'), isDisabled: true }], 0, this.contextViewService, defaultSelectBoxStyles, { ariaLabel: nls.localize('selectBreakpoint', 'Select breakpoint') }); + const selectBreakpointBox = this.selectBreakpointBox = new SelectBox(breakpointOptions, index + 1, this.contextViewService, defaultSelectBoxStyles, { ariaLabel: nls.localize('selectBreakpoint', 'Select breakpoint') }); selectBreakpointBox.onDidSelect(e => { if (e.index === 0) { this.triggeredByBreakpointInput = undefined; From d63202a5382aa104f5515ea09053a2a21a2587c6 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 23 Feb 2024 16:05:26 -0800 Subject: [PATCH 11/18] Fix sharing copy paste data across editor groups (#206142) Fix sharing copy paste data across editor groups --- .../browser/copyPasteController.ts | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/vs/editor/contrib/dropOrPasteInto/browser/copyPasteController.ts b/src/vs/editor/contrib/dropOrPasteInto/browser/copyPasteController.ts index 10654d61a52..adc0684cfca 100644 --- a/src/vs/editor/contrib/dropOrPasteInto/browser/copyPasteController.ts +++ b/src/vs/editor/contrib/dropOrPasteInto/browser/copyPasteController.ts @@ -56,13 +56,20 @@ export class CopyPasteController extends Disposable implements IEditorContributi return editor.getContribution(CopyPasteController.ID); } - private readonly _editor: ICodeEditor; - - private _currentCopyOperation?: { + /** + * Global tracking the last copy operation. + * + * This is shared across all editors so that you can copy and paste between groups. + * + * TODO: figure out how to make this work with multiple windows + */ + private static _currentCopyOperation?: { readonly handle: string; readonly dataTransferPromise: CancelablePromise; }; + private readonly _editor: ICodeEditor; + private _currentPasteOperation?: CancelablePromise; private _pasteAsActionContext?: { readonly preferredId: string | undefined }; @@ -204,8 +211,8 @@ export class CopyPasteController extends Disposable implements IEditorContributi return dataTransfer; }); - this._currentCopyOperation?.dataTransferPromise.cancel(); - this._currentCopyOperation = { handle: handle, dataTransferPromise: promise }; + CopyPasteController._currentCopyOperation?.dataTransferPromise.cancel(); + CopyPasteController._currentCopyOperation = { handle: handle, dataTransferPromise: promise }; } private async handlePaste(e: ClipboardEvent) { @@ -436,8 +443,8 @@ export class CopyPasteController extends Disposable implements IEditorContributi } private async mergeInDataFromCopy(dataTransfer: VSDataTransfer, metadata: CopyMetadata | undefined, token: CancellationToken): Promise { - if (metadata?.id && this._currentCopyOperation?.handle === metadata.id) { - const toMergeDataTransfer = await this._currentCopyOperation.dataTransferPromise; + if (metadata?.id && CopyPasteController._currentCopyOperation?.handle === metadata.id) { + const toMergeDataTransfer = await CopyPasteController._currentCopyOperation.dataTransferPromise; if (token.isCancellationRequested) { return; } From 660264a263fcfeb5e15604ecd8eaeb71ca98cd5f Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Sat, 24 Feb 2024 16:21:20 +0100 Subject: [PATCH 12/18] Adopt custom hover for extension and runtime view (#206154) adopt custom hover for extension and runtime view --- .../abstractRuntimeExtensionsEditor.ts | 12 ++++++--- .../extensions/browser/extensionEditor.ts | 24 ++++++++++++----- .../extensions/browser/extensionsWidgets.ts | 26 ++++++++++++++----- 3 files changed, 45 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor.ts b/src/vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor.ts index 991a3df035c..cb2c01bdf75 100644 --- a/src/vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor.ts @@ -5,6 +5,8 @@ import { $, Dimension, addDisposableListener, append, clearNode } from 'vs/base/browser/dom'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels'; import { IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; @@ -364,13 +366,15 @@ export abstract class AbstractRuntimeExtensionsEditor extends EditorPane { } else { title = nls.localize('extensionActivating', "Extension is activating..."); } - data.activationTime.title = title; + data.elementDisposables.push(setupCustomHover(getDefaultHoverDelegate('mouse'), data.activationTime, title)); clearNode(data.msgContainer); if (this._getUnresponsiveProfile(element.description.identifier)) { const el = $('span', undefined, ...renderLabelWithIcons(` $(alert) Unresponsive`)); - el.title = nls.localize('unresponsive.title', "Extension has caused the extension host to freeze."); + const extensionHostFreezTitle = nls.localize('unresponsive.title', "Extension has caused the extension host to freeze."); + data.elementDisposables.push(setupCustomHover(getDefaultHoverDelegate('mouse'), el, extensionHostFreezTitle)); + data.msgContainer.appendChild(el); } @@ -416,7 +420,9 @@ export abstract class AbstractRuntimeExtensionsEditor extends EditorPane { } if (accessData?.current) { const element = $('span', undefined, nls.localize('requests count', "{0} Requests: {1} (Session)", feature.label, accessData.current.count)); - element.title = nls.localize('requests count title', "Last request was {0}. Overall Requests: {1}", fromNow(accessData.current.lastAccessed, true, true), accessData.totalCount); + const title = nls.localize('requests count title', "Last request was {0}. Overall Requests: {1}", fromNow(accessData.current.lastAccessed, true, true), accessData.totalCount); + data.elementDisposables.push(setupCustomHover(getDefaultHoverDelegate('mouse'), element, title)); + data.msgContainer.appendChild(element); } } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index 21986a4b709..097f4a3c634 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -5,6 +5,8 @@ import { $, Dimension, addDisposableListener, append, setParentFlowTo } from 'vs/base/browser/dom'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { CheckboxActionViewItem } from 'vs/base/browser/ui/toggle/toggle'; import { Action, IAction } from 'vs/base/common/actions'; @@ -188,7 +190,8 @@ class VersionWidget extends ExtensionWithDifferentGalleryVersionWidget { private readonly element: HTMLElement; constructor(container: HTMLElement) { super(); - this.element = append(container, $('code.version', { title: localize('extension version', "Extension Version") })); + this.element = append(container, $('code.version')); + this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), this.element, localize('extension version', "Extension Version"))); this.render(); } render(): void { @@ -268,25 +271,30 @@ export class ExtensionEditor extends EditorPane { const details = append(header, $('.details')); const title = append(details, $('.title')); - const name = append(title, $('span.name.clickable', { title: localize('name', "Extension name"), role: 'heading', tabIndex: 0 })); + const name = append(title, $('span.name.clickable', { role: 'heading', tabIndex: 0 })); + this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), name, localize('name', "Extension name"))); const versionWidget = new VersionWidget(title); - const preview = append(title, $('span.preview', { title: localize('preview', "Preview") })); + const preview = append(title, $('span.preview')); + this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), preview, localize('preview', "Preview"))); preview.textContent = localize('preview', "Preview"); const builtin = append(title, $('span.builtin')); builtin.textContent = localize('builtin', "Built-in"); const subtitle = append(details, $('.subtitle')); - const publisher = append(append(subtitle, $('.subtitle-entry')), $('.publisher.clickable', { title: localize('publisher', "Publisher"), tabIndex: 0 })); + const publisher = append(append(subtitle, $('.subtitle-entry')), $('.publisher.clickable', { tabIndex: 0 })); + this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), publisher, localize('publisher', "Publisher"))); publisher.setAttribute('role', 'button'); const publisherDisplayName = append(publisher, $('.publisher-name')); const verifiedPublisherWidget = this.instantiationService.createInstance(VerifiedPublisherWidget, append(publisher, $('.verified-publisher')), false); - const installCount = append(append(subtitle, $('.subtitle-entry')), $('span.install', { title: localize('install count', "Install count"), tabIndex: 0 })); + const installCount = append(append(subtitle, $('.subtitle-entry')), $('span.install', { tabIndex: 0 })); + this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), installCount, localize('install count', "Install count"))); const installCountWidget = this.instantiationService.createInstance(InstallCountWidget, installCount, false); - const rating = append(append(subtitle, $('.subtitle-entry')), $('span.rating.clickable', { title: localize('rating', "Rating"), tabIndex: 0 })); + const rating = append(append(subtitle, $('.subtitle-entry')), $('span.rating.clickable', { tabIndex: 0 })); + this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), rating, localize('rating', "Rating"))); rating.setAttribute('role', 'link'); // #132645 const ratingsWidget = this.instantiationService.createInstance(RatingsWidget, rating, false); @@ -914,7 +922,9 @@ export class ExtensionEditor extends EditorPane { append(extensionResourcesContainer, $('.additional-details-title', undefined, localize('resources', "Resources"))); const resourcesElement = append(extensionResourcesContainer, $('.resources')); for (const [label, uri] of resources) { - this.transientDisposables.add(onClick(append(resourcesElement, $('a.resource', { title: uri.toString(), tabindex: '0' }, label)), () => this.openerService.open(uri))); + const resource = append(resourcesElement, $('a.resource', { tabindex: '0' }, label)); + this.transientDisposables.add(onClick(resource, () => this.openerService.open(uri))); + this.transientDisposables.add(setupCustomHover(getDefaultHoverDelegate('mouse'), resource, uri.toString())); } } } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts index a4a887e596a..4ad3680b7d7 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts @@ -31,7 +31,7 @@ import { URI } from 'vs/base/common/uri'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import Severity from 'vs/base/common/severity'; -import { setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; +import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; import { Color } from 'vs/base/common/color'; import { renderMarkdown } from 'vs/base/browser/markdownRenderer'; import { IOpenerService } from 'vs/platform/opener/common/opener'; @@ -41,6 +41,7 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { defaultCountBadgeStyles } from 'vs/platform/theme/browser/defaultStyles'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; export abstract class ExtensionWidget extends Disposable implements IExtensionContainer { private _extension: IExtension | null = null; @@ -124,6 +125,8 @@ export class InstallCountWidget extends ExtensionWidget { export class RatingsWidget extends ExtensionWidget { + private readonly containerHover: ICustomHover; + constructor( private container: HTMLElement, private small: boolean @@ -135,12 +138,13 @@ export class RatingsWidget extends ExtensionWidget { container.classList.add('small'); } + this.containerHover = this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), container, '')); + this.render(); } render(): void { this.container.innerText = ''; - this.container.title = ''; if (!this.extension) { return; @@ -159,7 +163,7 @@ export class RatingsWidget extends ExtensionWidget { } const rating = Math.round(this.extension.rating * 2) / 2; - this.container.title = localize('ratedLabel', "Average rating: {0} out of 5", rating); + this.containerHover.update(localize('ratedLabel', "Average rating: {0} out of 5", rating)); if (this.small) { append(this.container, $('span' + ThemeIcon.asCSSSelector(starFullIcon))); @@ -186,6 +190,7 @@ export class RatingsWidget extends ExtensionWidget { export class VerifiedPublisherWidget extends ExtensionWidget { private disposables = this._register(new DisposableStore()); + private readonly containerHover: ICustomHover; constructor( private container: HTMLElement, @@ -193,6 +198,7 @@ export class VerifiedPublisherWidget extends ExtensionWidget { @IOpenerService private readonly openerService: IOpenerService, ) { super(); + this.containerHover = this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), container, '')); this.render(); } @@ -209,7 +215,7 @@ export class VerifiedPublisherWidget extends ExtensionWidget { if (!this.small) { verifiedPublisher.tabIndex = 0; - verifiedPublisher.title = `Verified Domain: ${this.extension.publisherDomain.link}`; + this.containerHover.update(`Verified Domain: ${this.extension.publisherDomain.link}`); verifiedPublisher.setAttribute('role', 'link'); append(verifiedPublisher, $('span.extension-verified-publisher-domain', undefined, publisherDomainLink.authority.startsWith('www.') ? publisherDomainLink.authority.substring(4) : publisherDomainLink.authority)); @@ -239,7 +245,8 @@ export class SponsorWidget extends ExtensionWidget { return; } - const sponsor = append(this.container, $('span.sponsor.clickable', { tabIndex: 0, title: this.extension?.publisherSponsorLink })); + const sponsor = append(this.container, $('span.sponsor.clickable', { tabIndex: 0 })); + this.disposables.add(setupCustomHover(getDefaultHoverDelegate('mouse'), sponsor, this.extension?.publisherSponsorLink.toString() ?? '')); sponsor.setAttribute('role', 'link'); // #132645 const sponsorIconElement = renderIcon(sponsorIcon); const label = $('span', undefined, localize('sponsor', "Sponsor")); @@ -367,6 +374,7 @@ export class RemoteBadgeWidget extends ExtensionWidget { class RemoteBadge extends Disposable { readonly element: HTMLElement; + readonly elementHover: ICustomHover; constructor( private readonly tooltip: boolean, @@ -376,6 +384,7 @@ class RemoteBadge extends Disposable { ) { super(); this.element = $('div.extension-badge.extension-remote-badge'); + this.elementHover = this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), this.element, '')); this.render(); } @@ -397,7 +406,7 @@ class RemoteBadge extends Disposable { if (this.tooltip) { const updateTitle = () => { if (this.element && this.extensionManagementServerService.remoteExtensionManagementServer) { - this.element.title = localize('remote extension title', "Extension in {0}", this.extensionManagementServerService.remoteExtensionManagementServer.label); + this.elementHover.update(localize('remote extension title', "Extension in {0}", this.extensionManagementServerService.remoteExtensionManagementServer.label)); } }; this._register(this.labelService.onDidChangeFormatters(() => updateTitle())); @@ -435,6 +444,8 @@ export class ExtensionPackCountWidget extends ExtensionWidget { export class SyncIgnoredWidget extends ExtensionWidget { + private readonly disposables = this._register(new DisposableStore()); + constructor( private readonly container: HTMLElement, @IConfigurationService private readonly configurationService: IConfigurationService, @@ -448,11 +459,12 @@ export class SyncIgnoredWidget extends ExtensionWidget { } render(): void { + this.disposables.clear(); this.container.innerText = ''; if (this.extension && this.extension.state === ExtensionState.Installed && this.userDataSyncEnablementService.isEnabled() && this.extensionsWorkbenchService.isExtensionIgnoredToSync(this.extension)) { const element = append(this.container, $('span.extension-sync-ignored' + ThemeIcon.asCSSSelector(syncIgnoredIcon))); - element.title = localize('syncingore.label', "This extension is ignored during sync."); + this.disposables.add(setupCustomHover(getDefaultHoverDelegate('mouse'), element, localize('syncingore.label', "This extension is ignored during sync."))); element.classList.add(...ThemeIcon.asClassNameArray(syncIgnoredIcon)); } } From f10dd6906c031e1bfacfe6b1976883947688a636 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sat, 24 Feb 2024 07:35:39 -0800 Subject: [PATCH 13/18] Only preview detected links for files and when preview is on Part of #206126 --- .../links/browser/terminalLinkQuickpick.ts | 69 +++++++++++-------- 1 file changed, 40 insertions(+), 29 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts index aafd2e45711..6ceb6c3db32 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts @@ -20,6 +20,9 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { getLinkSuffix } from 'vs/workbench/contrib/terminalContrib/links/browser/terminalLinkParsing'; import type { ITextEditorSelection } from 'vs/platform/editor/common/editor'; +import { TerminalBuiltinLinkType } from 'vs/workbench/contrib/terminalContrib/links/browser/links'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import type { IFilesConfiguration } from 'vs/workbench/contrib/files/common/files'; export class TerminalLinkQuickpick extends DisposableStore { @@ -30,6 +33,7 @@ export class TerminalLinkQuickpick extends DisposableStore { readonly onDidRequestMoreLinks = this._onDidRequestMoreLinks.event; constructor( + @IConfigurationService private readonly _configurationService: IConfigurationService, @IEditorService private readonly _editorService: IEditorService, @IHistoryService private readonly _historyService: IHistoryService, @IQuickInputService private readonly _quickInputService: IQuickInputService, @@ -162,38 +166,45 @@ export class TerminalLinkQuickpick extends DisposableStore { } private _previewItem(item: ITerminalLinkQuickPickItem | IQuickPickItem) { - if (item && 'link' in item && item.link && 'uri' in item.link && item.link.uri) { - this._editorViewState.set(); - const link = item.link; - const uri = link.uri; - - - - const linkSuffix = link.parsedLink ? link.parsedLink.suffix : getLinkSuffix(link.text); - let selection: ITextEditorSelection | undefined;// = link.selection; - if (!selection) { - selection = linkSuffix?.row === undefined ? undefined : { - startLineNumber: linkSuffix.row ?? 1, - startColumn: linkSuffix.col ?? 1, - endLineNumber: linkSuffix.rowEnd, - endColumn: linkSuffix.colEnd - }; - } + if (!item || !('link' in item) || !item.link || !('uri' in item.link) || !item.link.uri) { + return; + } + const link = item.link; + if (link.type !== TerminalBuiltinLinkType.LocalFile) { + return; + } - this._editorSequencer.queue(async () => { - // disable and re-enable history service so that we can ignore this history entry - const disposable = this._historyService.suspendTracking(); - try { - await this._editorService.openEditor({ - resource: uri, - options: { preserveFocus: true, revealIfOpened: true, ignoreError: true, selection } - }); - } finally { - disposable.dispose(); - } - }); + // Don't open if preview editors are disabled as it may open many editor + const config = this._configurationService.getValue(); + if (!config.workbench?.editor?.enablePreview) { + return; + } + + const linkSuffix = link.parsedLink ? link.parsedLink.suffix : getLinkSuffix(link.text); + let selection: ITextEditorSelection | undefined;// = link.selection; + if (!selection) { + selection = linkSuffix?.row === undefined ? undefined : { + startLineNumber: linkSuffix.row ?? 1, + startColumn: linkSuffix.col ?? 1, + endLineNumber: linkSuffix.rowEnd, + endColumn: linkSuffix.colEnd + }; } + + this._editorViewState.set(); + this._editorSequencer.queue(async () => { + // disable and re-enable history service so that we can ignore this history entry + const disposable = this._historyService.suspendTracking(); + try { + await this._editorService.openEditor({ + resource: link.uri, + options: { preserveFocus: true, revealIfOpened: true, ignoreError: true, selection, } + }); + } finally { + disposable.dispose(); + } + }); } } From 523dd898669fd40d2587197655c33ee066f9f62a Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sat, 24 Feb 2024 07:38:26 -0800 Subject: [PATCH 14/18] Simplify setting selection --- .../links/browser/terminalLinkQuickpick.ts | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts index 6ceb6c3db32..0cea7b3f514 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts @@ -19,7 +19,6 @@ import { EditorViewState } from 'vs/workbench/browser/quickaccess'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { getLinkSuffix } from 'vs/workbench/contrib/terminalContrib/links/browser/terminalLinkParsing'; -import type { ITextEditorSelection } from 'vs/platform/editor/common/editor'; import { TerminalBuiltinLinkType } from 'vs/workbench/contrib/terminalContrib/links/browser/links'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import type { IFilesConfiguration } from 'vs/workbench/contrib/files/common/files'; @@ -182,15 +181,12 @@ export class TerminalLinkQuickpick extends DisposableStore { } const linkSuffix = link.parsedLink ? link.parsedLink.suffix : getLinkSuffix(link.text); - let selection: ITextEditorSelection | undefined;// = link.selection; - if (!selection) { - selection = linkSuffix?.row === undefined ? undefined : { - startLineNumber: linkSuffix.row ?? 1, - startColumn: linkSuffix.col ?? 1, - endLineNumber: linkSuffix.rowEnd, - endColumn: linkSuffix.colEnd - }; - } + const selection = linkSuffix?.row === undefined ? undefined : { + startLineNumber: linkSuffix.row ?? 1, + startColumn: linkSuffix.col ?? 1, + endLineNumber: linkSuffix.rowEnd, + endColumn: linkSuffix.colEnd + }; this._editorViewState.set(); this._editorSequencer.queue(async () => { From d2f340649b56500692ae84c82b8fac41f4bf8832 Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Sun, 25 Feb 2024 18:38:20 +0100 Subject: [PATCH 15/18] Adopt custom hover for inline chat widget (#206170) inline chat custom hover adoption --- src/vs/platform/actions/browser/buttonbar.ts | 19 ++++++++++++++----- .../inlineChat/browser/inlineChatWidget.ts | 10 ++++++++-- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/vs/platform/actions/browser/buttonbar.ts b/src/vs/platform/actions/browser/buttonbar.ts index 21d4c4c5fc6..94720d5b557 100644 --- a/src/vs/platform/actions/browser/buttonbar.ts +++ b/src/vs/platform/actions/browser/buttonbar.ts @@ -4,6 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { ButtonBar, IButton } from 'vs/base/browser/ui/button/button'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; import { ActionRunner, IAction, IActionRunner, SubmenuAction, WorkbenchActionExecutedClassification, WorkbenchActionExecutedEvent } from 'vs/base/common/actions'; import { Emitter, Event } from 'vs/base/common/event'; import { DisposableStore } from 'vs/base/common/lifecycle'; @@ -29,6 +31,7 @@ export interface IWorkbenchButtonBarOptions { export class WorkbenchButtonBar extends ButtonBar { protected readonly _store = new DisposableStore(); + protected readonly _updateStore = new DisposableStore(); private readonly _actionRunner: IActionRunner; private readonly _onDidChange = new Emitter(); @@ -57,6 +60,7 @@ export class WorkbenchButtonBar extends ButtonBar { override dispose() { this._onDidChange.dispose(); + this._updateStore.dispose(); this._store.dispose(); super.dispose(); } @@ -65,8 +69,12 @@ export class WorkbenchButtonBar extends ButtonBar { const conifgProvider: IButtonConfigProvider = this._options?.buttonConfigProvider ?? (() => ({ showLabel: true })); + this._updateStore.clear(); this.clear(); + // Support instamt hover between buttons + const hoverDelegate = this._updateStore.add(getDefaultHoverDelegate('element', true)); + for (let i = 0; i < actions.length; i++) { const secondary = i > 0; @@ -107,15 +115,16 @@ export class WorkbenchButtonBar extends ButtonBar { } } const kb = this._keybindingService.lookupKeybinding(action.id); + let tooltip: string; if (kb) { - btn.element.title = localize('labelWithKeybinding', "{0} ({1})", action.label, kb.getLabel()); + tooltip = localize('labelWithKeybinding', "{0} ({1})", action.label, kb.getLabel()); } else { - btn.element.title = action.label; - + tooltip = action.label; } - btn.onDidClick(async () => { + this._updateStore.add(setupCustomHover(hoverDelegate, btn.element, tooltip)); + this._updateStore.add(btn.onDidClick(async () => { this._actionRunner.run(action); - }); + })); } this._onDidChange.fire(this); } diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts index 50bba01479e..084e20c5898 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts @@ -66,6 +66,7 @@ import { ExpansionState, HunkData, HunkInformation, Session } from 'vs/workbench import { asRange, invertLineRange } from 'vs/workbench/contrib/inlineChat/browser/utils'; import { ACTION_ACCEPT_CHANGES, ACTION_REGENERATE_RESPONSE, ACTION_VIEW_IN_CHAT, CTX_INLINE_CHAT_EMPTY, CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_INNER_CURSOR_END, CTX_INLINE_CHAT_INNER_CURSOR_FIRST, CTX_INLINE_CHAT_INNER_CURSOR_LAST, CTX_INLINE_CHAT_INNER_CURSOR_START, CTX_INLINE_CHAT_MESSAGE_CROP_STATE, CTX_INLINE_CHAT_OUTER_CURSOR_POSITION, CTX_INLINE_CHAT_RESPONSE_FOCUSED, CTX_INLINE_CHAT_VISIBLE, IInlineChatFollowup, IInlineChatSlashCommand, MENU_INLINE_CHAT_INPUT, MENU_INLINE_CHAT_WIDGET, MENU_INLINE_CHAT_WIDGET_FEEDBACK, MENU_INLINE_CHAT_WIDGET_MARKDOWN_MESSAGE, MENU_INLINE_CHAT_WIDGET_STATUS } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { IUntitledTextEditorModel } from 'vs/workbench/services/untitled/common/untitledTextEditorModel'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; const defaultAriaLabel = localize('aria-label', "Inline Chat Input"); @@ -373,12 +374,16 @@ export class InlineChatWidget { this._slashCommandContentWidget = new SlashCommandContentWidget(this._inputEditor); this._store.add(this._slashCommandContentWidget); + // Share hover delegates between toolbars to support instant hover between both + const hoverDelegate = this._store.add(getDefaultHoverDelegate('element', true)); + // toolbars this._store.add(this._instantiationService.createInstance(MenuWorkbenchToolBar, this._elements.editorToolbar, _options.menuId, { telemetrySource: 'interactiveEditorWidget-toolbar', toolbarOptions: { primaryGroup: 'main' }, - hiddenItemStrategy: HiddenItemStrategy.Ignore // keep it lean when hiding items and avoid a "..." overflow menu + hiddenItemStrategy: HiddenItemStrategy.Ignore, // keep it lean when hiding items and avoid a "..." overflow menu + hoverDelegate })); this._progressBar = new ProgressBar(this._elements.progress); @@ -387,7 +392,8 @@ export class InlineChatWidget { this._store.add(this._instantiationService.createInstance(MenuWorkbenchToolBar, this._elements.widgetToolbar, _options.widgetMenuId, { telemetrySource: 'interactiveEditorWidget-toolbar', - toolbarOptions: { primaryGroup: 'main' } + toolbarOptions: { primaryGroup: 'main' }, + hoverDelegate })); const workbenchMenubarOptions: IWorkbenchButtonBarOptions = { From 03f6a7894e4f3b21a2f4936eeb1dc762606a5bd4 Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Mon, 26 Feb 2024 02:20:16 +0100 Subject: [PATCH 16/18] Updated actionViewItemProvider to include options parameter (#206188) Updated actionViewItemProvider to include options parameter in various components --- .../workbench/browser/parts/compositeBar.ts | 4 +-- .../browser/parts/globalCompositeBar.ts | 5 ++-- .../notifications/notificationsCenter.ts | 3 ++- .../parts/titlebar/commandCenterControl.ts | 11 +++++--- .../browser/parts/titlebar/titlebarPart.ts | 7 ++--- .../browser/parts/views/viewFilter.ts | 5 ++-- .../contrib/comments/browser/commentNode.ts | 26 +++++++++---------- .../extensions/browser/extensionEditor.ts | 8 +++--- .../extensions/browser/extensionsActions.ts | 10 ++++--- .../extensions/browser/extensionsList.ts | 7 ++--- .../contrib/markers/browser/markersTable.ts | 2 +- .../markers/browser/markersTreeViewer.ts | 6 ++--- .../markers/browser/markersViewActions.ts | 8 +++--- .../contrib/find/notebookFindReplaceWidget.ts | 8 +++--- .../notebook/browser/diff/diffComponents.ts | 4 +-- .../notebook/browser/diff/notebookDiffList.ts | 4 +-- .../browser/view/cellParts/cellToolbars.ts | 6 ++--- .../view/cellParts/codeCellRunToolbar.ts | 3 ++- .../viewParts/notebookTopCellToolbar.ts | 4 +-- .../preferences/browser/keybindingsEditor.ts | 5 ++-- .../preferences/browser/preferencesWidgets.ts | 4 +-- .../preferences/browser/settingsEditor2.ts | 4 +-- .../preferences/browser/settingsSearchMenu.ts | 3 +++ .../terminal/browser/terminalTabsList.ts | 4 +-- .../testing/browser/testingExplorerFilter.ts | 7 ++--- .../testing/browser/testingOutputPeek.ts | 4 +-- 26 files changed, 91 insertions(+), 71 deletions(-) diff --git a/src/vs/workbench/browser/parts/compositeBar.ts b/src/vs/workbench/browser/parts/compositeBar.ts index 76d6c0c1ee4..27d0b3738ea 100644 --- a/src/vs/workbench/browser/parts/compositeBar.ts +++ b/src/vs/workbench/browser/parts/compositeBar.ts @@ -201,14 +201,14 @@ export class CompositeBar extends Widget implements ICompositeBar { create(parent: HTMLElement): HTMLElement { const actionBarDiv = parent.appendChild($('.composite-bar')); this.compositeSwitcherBar = this._register(new ActionBar(actionBarDiv, { - actionViewItemProvider: action => { + actionViewItemProvider: (action, options) => { if (action instanceof CompositeOverflowActivityAction) { return this.compositeOverflowActionViewItem; } const item = this.model.findItem(action.id); return item && this.instantiationService.createInstance( CompositeActionViewItem, - { draggable: true, colors: this.options.colors, icon: this.options.icon, hoverOptions: this.options.activityHoverOptions, compact: this.options.compact }, + { ...options, draggable: true, colors: this.options.colors, icon: this.options.icon, hoverOptions: this.options.activityHoverOptions, compact: this.options.compact }, action as CompositeBarAction, item.pinnedAction, item.toggleBadgeAction, diff --git a/src/vs/workbench/browser/parts/globalCompositeBar.ts b/src/vs/workbench/browser/parts/globalCompositeBar.ts index db3471fcadc..3490432e312 100644 --- a/src/vs/workbench/browser/parts/globalCompositeBar.ts +++ b/src/vs/workbench/browser/parts/globalCompositeBar.ts @@ -72,15 +72,16 @@ export class GlobalCompositeBar extends Disposable { anchorAxisAlignment: AnchorAxisAlignment.HORIZONTAL }); this.globalActivityActionBar = this._register(new ActionBar(this.element, { - actionViewItemProvider: action => { + actionViewItemProvider: (action, options) => { if (action.id === GLOBAL_ACTIVITY_ID) { - return this.instantiationService.createInstance(GlobalActivityActionViewItem, this.contextMenuActionsProvider, { colors: this.colors, hoverOptions: this.activityHoverOptions }, contextMenuAlignmentOptions); + return this.instantiationService.createInstance(GlobalActivityActionViewItem, this.contextMenuActionsProvider, { ...options, colors: this.colors, hoverOptions: this.activityHoverOptions }, contextMenuAlignmentOptions); } if (action.id === ACCOUNTS_ACTIVITY_ID) { return this.instantiationService.createInstance(AccountsActivityActionViewItem, this.contextMenuActionsProvider, { + ...options, colors: this.colors, hoverOptions: this.activityHoverOptions }, diff --git a/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts b/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts index e4cbaff2272..40bcde5fdb4 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts @@ -173,7 +173,7 @@ export class NotificationsCenter extends Themable implements INotificationsCente const notificationsToolBar = this._register(new ActionBar(toolbarContainer, { ariaLabel: localize('notificationsToolbar', "Notification Center Actions"), actionRunner, - actionViewItemProvider: action => { + actionViewItemProvider: (action, options) => { if (action.id === ConfigureDoNotDisturbAction.ID) { return this._register(this.instantiationService.createInstance(DropdownMenuActionViewItem, action, { getActions() { @@ -208,6 +208,7 @@ export class NotificationsCenter extends Themable implements INotificationsCente return actions; }, }, this.contextMenuService, { + ...options, actionRunner, classNames: action.class, keybindingProvider: action => this.keybindingService.lookupKeybinding(action.id) diff --git a/src/vs/workbench/browser/parts/titlebar/commandCenterControl.ts b/src/vs/workbench/browser/parts/titlebar/commandCenterControl.ts index 3843068e941..72eeea0b590 100644 --- a/src/vs/workbench/browser/parts/titlebar/commandCenterControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/commandCenterControl.ts @@ -5,6 +5,7 @@ import { isActiveDocument, reset } from 'vs/base/browser/dom'; import { BaseActionViewItem, IBaseActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; import { setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; import { renderIcon } from 'vs/base/browser/ui/iconLabel/iconLabels'; @@ -46,11 +47,11 @@ export class CommandCenterControl { primaryGroup: () => true, }, telemetrySource: 'commandCenter', - actionViewItemProvider: (action) => { + actionViewItemProvider: (action, options) => { if (action instanceof SubmenuItemAction && action.item.submenu === MenuId.CommandCenterCenter) { - return instantiationService.createInstance(CommandCenterCenterViewItem, action, windowTitle, hoverDelegate, {}); + return instantiationService.createInstance(CommandCenterCenterViewItem, action, windowTitle, { ...options, hoverDelegate }); } else { - return createActionViewItem(instantiationService, action, { hoverDelegate }); + return createActionViewItem(instantiationService, action, { ...options, hoverDelegate }); } } }); @@ -75,16 +76,18 @@ class CommandCenterCenterViewItem extends BaseActionViewItem { private static readonly _quickOpenCommandId = 'workbench.action.quickOpenWithModes'; + private readonly _hoverDelegate: IHoverDelegate; + constructor( private readonly _submenu: SubmenuItemAction, private readonly _windowTitle: WindowTitle, - private readonly _hoverDelegate: IHoverDelegate, options: IBaseActionViewItemOptions, @IKeybindingService private _keybindingService: IKeybindingService, @IInstantiationService private _instaService: IInstantiationService, @IEditorGroupsService private _editorGroupService: IEditorGroupsService, ) { super(undefined, _submenu.actions.find(action => action.id === 'workbench.action.quickOpenWithModes') ?? _submenu.actions[0], options); + this._hoverDelegate = options.hoverDelegate ?? getDefaultHoverDelegate('mouse'); } override render(container: HTMLElement): void { diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 05ad43933f8..176ac265172 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -532,7 +532,7 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { // --- Editor Actions const activeEditorPane = this.editorGroupsContainer.activeGroup?.activeEditorPane; if (activeEditorPane && activeEditorPane instanceof EditorPane) { - const result = activeEditorPane.getActionViewItem(action, { hoverDelegate: this.hoverDelegate }); + const result = activeEditorPane.getActionViewItem(action, options); if (result) { return result; @@ -540,7 +540,7 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { } // Check extensions - return createActionViewItem(this.instantiationService, action, { ...options, hoverDelegate: this.hoverDelegate, menuAsChild: false }); + return createActionViewItem(this.instantiationService, action, { ...options, menuAsChild: false }); } private getKeybinding(action: IAction): ResolvedKeybinding | undefined { @@ -565,7 +565,8 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { anchorAlignmentProvider: () => AnchorAlignment.RIGHT, telemetrySource: 'titlePart', highlightToggledItems: this.editorActionsEnabled, // Only show toggled state for editor actions (Layout actions are not shown as toggled) - actionViewItemProvider: (action, options) => this.actionViewItemProvider(action, options) + actionViewItemProvider: (action, options) => this.actionViewItemProvider(action, options), + hoverDelegate: this.hoverDelegate })); if (this.editorActionsEnabled) { diff --git a/src/vs/workbench/browser/parts/views/viewFilter.ts b/src/vs/workbench/browser/parts/views/viewFilter.ts index 3e5763a2203..b6285e45c71 100644 --- a/src/vs/workbench/browser/parts/views/viewFilter.ts +++ b/src/vs/workbench/browser/parts/views/viewFilter.ts @@ -25,6 +25,7 @@ import { SubmenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntr import { Widget } from 'vs/base/browser/ui/widget'; import { Emitter } from 'vs/base/common/event'; import { defaultInputBoxStyles } from 'vs/platform/theme/browser/defaultStyles'; +import { IActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; const viewFilterMenu = new MenuId('menu.view.filter'); export const viewFilterSubmenu = new MenuId('submenu.view.filter'); @@ -196,9 +197,9 @@ export class FilterWidget extends Widget { return this.instantiationService.createInstance(MenuWorkbenchToolBar, container, viewFilterMenu, { hiddenItemStrategy: HiddenItemStrategy.NoHide, - actionViewItemProvider: (action: IAction) => { + actionViewItemProvider: (action: IAction, options: IActionViewItemOptions) => { if (action instanceof SubmenuItemAction && action.item.submenu.id === viewFilterSubmenu.id) { - this.moreFiltersActionViewItem = this.instantiationService.createInstance(MoreFiltersActionViewItem, action, undefined); + this.moreFiltersActionViewItem = this.instantiationService.createInstance(MoreFiltersActionViewItem, action, options); this.moreFiltersActionViewItem.checked = this.isMoreFiltersChecked; return this.moreFiltersActionViewItem; } diff --git a/src/vs/workbench/contrib/comments/browser/commentNode.ts b/src/vs/workbench/contrib/comments/browser/commentNode.ts index 7085518f94a..58e376f9561 100644 --- a/src/vs/workbench/contrib/comments/browser/commentNode.ts +++ b/src/vs/workbench/contrib/comments/browser/commentNode.ts @@ -30,7 +30,7 @@ import { MenuEntryActionViewItem, SubmenuEntryActionViewItem } from 'vs/platform import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { CommentFormActions } from 'vs/workbench/contrib/comments/browser/commentFormActions'; import { MOUSE_CURSOR_TEXT_CSS_CLASS_NAME } from 'vs/base/browser/ui/mouseCursor/mouseCursor'; -import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { ActionViewItem, IActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; import { Codicon } from 'vs/base/common/codicons'; import { ThemeIcon } from 'vs/base/common/themables'; @@ -301,21 +301,22 @@ export class CommentNode extends Disposable { private createToolbar() { this.toolbar = new ToolBar(this._actionsToolbarContainer, this.contextMenuService, { - actionViewItemProvider: action => { + actionViewItemProvider: (action, options) => { if (action.id === ToggleReactionsAction.ID) { return new DropdownMenuActionViewItem( action, (action).menuActions, this.contextMenuService, { - actionViewItemProvider: action => this.actionViewItemProvider(action as Action), + ...options, + actionViewItemProvider: (action, options) => this.actionViewItemProvider(action as Action, options), actionRunner: this.actionRunner, classNames: ['toolbar-toggle-pickReactions', ...ThemeIcon.asClassNameArray(Codicon.reactions)], anchorAlignmentProvider: () => AnchorAlignment.RIGHT } ); } - return this.actionViewItemProvider(action as Action); + return this.actionViewItemProvider(action as Action, options); }, orientation: ActionsOrientation.HORIZONTAL }); @@ -357,8 +358,7 @@ export class CommentNode extends Disposable { } } - actionViewItemProvider(action: Action) { - let options = {}; + actionViewItemProvider(action: Action, options: IActionViewItemOptions) { if (action.id === ToggleReactionsAction.ID) { options = { label: false, icon: true }; } else { @@ -369,9 +369,9 @@ export class CommentNode extends Disposable { const item = new ReactionActionViewItem(action); return item; } else if (action instanceof MenuItemAction) { - return this.instantiationService.createInstance(MenuEntryActionViewItem, action, undefined); + return this.instantiationService.createInstance(MenuEntryActionViewItem, action, { hoverDelegate: options.hoverDelegate }); } else if (action instanceof SubmenuItemAction) { - return this.instantiationService.createInstance(SubmenuEntryActionViewItem, action, undefined); + return this.instantiationService.createInstance(SubmenuEntryActionViewItem, action, options); } else { const item = new ActionViewItem({}, action, options); return item; @@ -413,11 +413,11 @@ export class CommentNode extends Disposable { (toggleReactionAction).menuActions, this.contextMenuService, { - actionViewItemProvider: action => { + actionViewItemProvider: (action, options) => { if (action.id === ToggleReactionsAction.ID) { return toggleReactionActionViewItem; } - return this.actionViewItemProvider(action as Action); + return this.actionViewItemProvider(action as Action, options); }, actionRunner: this.actionRunner, classNames: 'toolbar-toggle-pickReactions', @@ -431,21 +431,21 @@ export class CommentNode extends Disposable { private createReactionsContainer(commentDetailsContainer: HTMLElement): void { this._reactionActionsContainer = dom.append(commentDetailsContainer, dom.$('div.comment-reactions')); this._reactionsActionBar = new ActionBar(this._reactionActionsContainer, { - actionViewItemProvider: action => { + actionViewItemProvider: (action, options) => { if (action.id === ToggleReactionsAction.ID) { return new DropdownMenuActionViewItem( action, (action).menuActions, this.contextMenuService, { - actionViewItemProvider: action => this.actionViewItemProvider(action as Action), + actionViewItemProvider: (action, options) => this.actionViewItemProvider(action as Action, options), actionRunner: this.actionRunner, classNames: ['toolbar-toggle-pickReactions', ...ThemeIcon.asClassNameArray(Codicon.reactions)], anchorAlignmentProvider: () => AnchorAlignment.RIGHT } ); } - return this.actionViewItemProvider(action as Action); + return this.actionViewItemProvider(action as Action, options); } }); this._register(this._reactionsActionBar); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index 097f4a3c634..11ee0d9a2b9 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -344,15 +344,15 @@ export class ExtensionEditor extends EditorPane { const actionsAndStatusContainer = append(details, $('.actions-status-container')); const extensionActionBar = this._register(new ActionBar(actionsAndStatusContainer, { - actionViewItemProvider: (action: IAction) => { + actionViewItemProvider: (action: IAction, options) => { if (action instanceof ExtensionDropDownAction) { - return action.createActionViewItem(); + return action.createActionViewItem(options); } if (action instanceof ActionWithDropDownAction) { - return new ExtensionActionWithDropdownActionViewItem(action, { icon: true, label: true, menuActionsOrProvider: { getActions: () => action.menuActions }, menuActionClassNames: (action.class || '').split(' ') }, this.contextMenuService); + return new ExtensionActionWithDropdownActionViewItem(action, { ...options, icon: true, label: true, menuActionsOrProvider: { getActions: () => action.menuActions }, menuActionClassNames: (action.class || '').split(' ') }, this.contextMenuService); } if (action instanceof ToggleAutoUpdateForExtensionAction) { - return new CheckboxActionViewItem(undefined, action, { icon: true, label: true, checkboxStyles: defaultCheckboxStyles }); + return new CheckboxActionViewItem(undefined, action, { ...options, icon: true, label: true, checkboxStyles: defaultCheckboxStyles }); } return undefined; }, diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index 1fc58a9e0f5..6e0155f3249 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -1021,8 +1021,8 @@ export abstract class ExtensionDropDownAction extends ExtensionAction { } private _actionViewItem: DropDownMenuActionViewItem | null = null; - createActionViewItem(): DropDownMenuActionViewItem { - this._actionViewItem = this.instantiationService.createInstance(DropDownMenuActionViewItem, this); + createActionViewItem(options: IActionViewItemOptions): DropDownMenuActionViewItem { + this._actionViewItem = this.instantiationService.createInstance(DropDownMenuActionViewItem, this, options); return this._actionViewItem; } @@ -1034,10 +1034,12 @@ export abstract class ExtensionDropDownAction extends ExtensionAction { export class DropDownMenuActionViewItem extends ActionViewItem { - constructor(action: ExtensionDropDownAction, + constructor( + action: ExtensionDropDownAction, + options: IActionViewItemOptions, @IContextMenuService private readonly contextMenuService: IContextMenuService ) { - super(null, action, { icon: true, label: true }); + super(null, action, { ...options, icon: true, label: true }); } public showMenu(menuActionGroups: IAction[][], disposeActionsOnHide: boolean): void { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsList.ts b/src/vs/workbench/contrib/extensions/browser/extensionsList.ts index 1bf769f4c66..d5058c51b43 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsList.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsList.ts @@ -26,6 +26,7 @@ import { WORKBENCH_BACKGROUND } from 'vs/workbench/common/theme'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { HoverPosition } from 'vs/base/browser/ui/hover/hoverWidget'; import { verifiedPublisherIcon as verifiedPublisherThemeIcon } from 'vs/workbench/contrib/extensions/browser/extensionsIcons'; +import { IActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; const EXTENSION_LIST_ELEMENT_HEIGHT = 72; @@ -98,12 +99,12 @@ export class Renderer implements IPagedRenderer { const verifiedPublisherWidget = this.instantiationService.createInstance(VerifiedPublisherWidget, append(publisher, $(`.verified-publisher`)), true); const publisherDisplayName = append(publisher, $('.publisher-name.ellipsis')); const actionbar = new ActionBar(footer, { - actionViewItemProvider: (action: IAction) => { + actionViewItemProvider: (action: IAction, options: IActionViewItemOptions) => { if (action instanceof ActionWithDropDownAction) { - return new ExtensionActionWithDropdownActionViewItem(action, { icon: true, label: true, menuActionsOrProvider: { getActions: () => action.menuActions }, menuActionClassNames: (action.class || '').split(' ') }, this.contextMenuService); + return new ExtensionActionWithDropdownActionViewItem(action, { ...options, icon: true, label: true, menuActionsOrProvider: { getActions: () => action.menuActions }, menuActionClassNames: (action.class || '').split(' ') }, this.contextMenuService); } if (action instanceof ExtensionDropDownAction) { - return action.createActionViewItem(); + return action.createActionViewItem(options); } return undefined; }, diff --git a/src/vs/workbench/contrib/markers/browser/markersTable.ts b/src/vs/workbench/contrib/markers/browser/markersTable.ts index dc454d054ed..bb63d92e9d2 100644 --- a/src/vs/workbench/contrib/markers/browser/markersTable.ts +++ b/src/vs/workbench/contrib/markers/browser/markersTable.ts @@ -74,7 +74,7 @@ class MarkerSeverityColumnRenderer implements ITableRenderer action.id === QuickFixAction.ID ? this.instantiationService.createInstance(QuickFixActionViewItem, action) : undefined + actionViewItemProvider: (action: IAction, options) => action.id === QuickFixAction.ID ? this.instantiationService.createInstance(QuickFixActionViewItem, action, options) : undefined }); return { actionBar, icon }; diff --git a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts index 4b7a07b3b17..180a378ac81 100644 --- a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts +++ b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts @@ -295,7 +295,7 @@ class MarkerWidget extends Disposable { ) { super(); this.actionBar = this._register(new ActionBar(dom.append(parent, dom.$('.actions')), { - actionViewItemProvider: (action: IAction) => action.id === QuickFixAction.ID ? _instantiationService.createInstance(QuickFixActionViewItem, action) : undefined + actionViewItemProvider: (action: IAction, options) => action.id === QuickFixAction.ID ? _instantiationService.createInstance(QuickFixActionViewItem, action, options) : undefined })); // wrap the icon in a container that get the icon color as foreground color. That way, if the @@ -342,9 +342,9 @@ class MarkerWidget extends Disposable { private renderMultilineActionbar(marker: Marker, parent: HTMLElement): void { const multilineActionbar = this.disposables.add(new ActionBar(dom.append(parent, dom.$('.multiline-actions')), { - actionViewItemProvider: (action) => { + actionViewItemProvider: (action, options) => { if (action.id === toggleMultilineAction) { - return new ToggleMultilineActionViewItem(undefined, action, { icon: true }); + return new ToggleMultilineActionViewItem(undefined, action, { ...options, icon: true }); } return undefined; } diff --git a/src/vs/workbench/contrib/markers/browser/markersViewActions.ts b/src/vs/workbench/contrib/markers/browser/markersViewActions.ts index 818f2f56fe6..74fa00a81ae 100644 --- a/src/vs/workbench/contrib/markers/browser/markersViewActions.ts +++ b/src/vs/workbench/contrib/markers/browser/markersViewActions.ts @@ -13,7 +13,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { Event, Emitter } from 'vs/base/common/event'; import { Codicon } from 'vs/base/common/codicons'; import { ThemeIcon } from 'vs/base/common/themables'; -import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { ActionViewItem, IActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { MarkersContextKeys } from 'vs/workbench/contrib/markers/common/markers'; import 'vs/css!./markersViewActions'; @@ -145,10 +145,12 @@ export class QuickFixAction extends Action { export class QuickFixActionViewItem extends ActionViewItem { - constructor(action: QuickFixAction, + constructor( + action: QuickFixAction, + options: IActionViewItemOptions, @IContextMenuService private readonly contextMenuService: IContextMenuService, ) { - super(null, action, { icon: true, label: false }); + super(null, action, { ...options, icon: true, label: false }); } public override onClick(event: DOM.EventLike): void { diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts index 480033afb33..fd9a432d6c3 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts @@ -40,6 +40,7 @@ import { defaultInputBoxStyles, defaultProgressBarStyles, defaultToggleStyles } import { IToggleStyles } from 'vs/base/browser/ui/toggle/toggle'; import { Disposable } from 'vs/base/common/lifecycle'; import { NotebookSetting } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { IActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; const NLS_FIND_INPUT_LABEL = nls.localize('label.find', "Find"); const NLS_FIND_INPUT_PLACEHOLDER = nls.localize('placeholder.find', "Find"); @@ -63,11 +64,12 @@ const NOTEBOOK_FIND_IN_CODE_OUTPUT = nls.localize('notebook.find.filter.findInCo const NOTEBOOK_FIND_WIDGET_INITIAL_WIDTH = 318; const NOTEBOOK_FIND_WIDGET_INITIAL_HORIZONTAL_PADDING = 4; class NotebookFindFilterActionViewItem extends DropdownMenuActionViewItem { - constructor(readonly filters: NotebookFindFilters, action: IAction, actionRunner: IActionRunner, @IContextMenuService contextMenuService: IContextMenuService) { + constructor(readonly filters: NotebookFindFilters, action: IAction, options: IActionViewItemOptions, actionRunner: IActionRunner, @IContextMenuService contextMenuService: IContextMenuService) { super(action, { getActions: () => this.getActions() }, contextMenuService, { + ...options, actionRunner, classNames: action.class, anchorAlignmentProvider: () => AnchorAlignment.RIGHT @@ -196,9 +198,9 @@ export class NotebookFindInputFilterButton extends Disposable { private createFilters(container: HTMLElement): void { this._actionbar = this._register(new ActionBar(container, { - actionViewItemProvider: action => { + actionViewItemProvider: (action, options) => { if (action.id === this._filtersAction.id) { - return this.instantiationService.createInstance(NotebookFindFilterActionViewItem, this.filters, action, new ActionRunner()); + return this.instantiationService.createInstance(NotebookFindFilterActionViewItem, this.filters, action, options, new ActionRunner()); } return undefined; } diff --git a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts index c1218b15ce8..43b387b9689 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts @@ -117,9 +117,9 @@ class PropertyHeader extends Disposable { const cellToolbarContainer = DOM.append(this.propertyHeaderContainer, DOM.$('div.property-toolbar')); this._toolbar = new WorkbenchToolBar(cellToolbarContainer, { - actionViewItemProvider: action => { + actionViewItemProvider: (action, options) => { if (action instanceof MenuItemAction) { - const item = new CodiconActionViewItem(action, undefined, this.keybindingService, this.notificationService, this.contextKeyService, this.themeService, this.contextMenuService, this.accessibilityService); + const item = new CodiconActionViewItem(action, { hoverDelegate: options.hoverDelegate }, this.keybindingService, this.notificationService, this.contextKeyService, this.themeService, this.contextMenuService, this.accessibilityService); return item; } diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts index 9248d1cee6e..4e92fa8a42f 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts @@ -189,9 +189,9 @@ export class CellDiffSideBySideRenderer implements IListRenderer { + actionViewItemProvider: (action, options) => { if (action instanceof MenuItemAction) { - const item = new CodiconActionViewItem(action, undefined, this.keybindingService, this.notificationService, this.contextKeyService, this.themeService, this.contextMenuService, this.accessibilityService); + const item = new CodiconActionViewItem(action, { hoverDelegate: options.hoverDelegate }, this.keybindingService, this.notificationService, this.contextKeyService, this.themeService, this.contextMenuService, this.accessibilityService); return item; } diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts index 12b86db201e..f10530616f1 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts @@ -46,12 +46,12 @@ export class BetweenCellToolbar extends CellOverlayPart { } const betweenCellToolbar = this._register(new ToolBar(this._bottomCellToolbarContainer, this.contextMenuService, { - actionViewItemProvider: action => { + actionViewItemProvider: (action, options) => { if (action instanceof MenuItemAction) { if (this._notebookEditor.notebookOptions.getDisplayOptions().insertToolbarAlignment === 'center') { - return this.instantiationService.createInstance(CodiconActionViewItem, action, undefined); + return this.instantiationService.createInstance(CodiconActionViewItem, action, { hoverDelegate: options.hoverDelegate }); } else { - return this.instantiationService.createInstance(MenuEntryActionViewItem, action, undefined); + return this.instantiationService.createInstance(MenuEntryActionViewItem, action, { hoverDelegate: options.hoverDelegate }); } } diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCellRunToolbar.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCellRunToolbar.ts index 48974ad10a5..de2c0e912bf 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCellRunToolbar.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCellRunToolbar.ts @@ -84,7 +84,7 @@ export class RunToolbar extends CellContentPart { const executionContextKeyService = this._register(getCodeCellExecutionContextKeyService(contextKeyService)); this.toolbar = this._register(new ToolBar(container, this.contextMenuService, { getKeyBinding: keybindingProvider, - actionViewItemProvider: _action => { + actionViewItemProvider: (_action, _options) => { actionViewItemDisposables.clear(); const primary = this.getCellToolbarActions(this.primaryMenu).primary[0]; @@ -104,6 +104,7 @@ export class RunToolbar extends CellContentPart { 'notebook-cell-run-toolbar', this.contextMenuService, { + ..._options, getKeyBinding: keybindingProvider }); actionViewItemDisposables.add(item.onDidChangeDropdownVisibility(visible => { diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookTopCellToolbar.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookTopCellToolbar.ts index 9a558d0dd28..f606649ca03 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookTopCellToolbar.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookTopCellToolbar.ts @@ -96,9 +96,9 @@ export class ListTopCellToolbar extends Disposable { DOM.clearNode(this.topCellToolbar); const toolbar = this.instantiationService.createInstance(MenuWorkbenchToolBar, this.topCellToolbar, this.notebookEditor.creationOptions.menuIds.cellTopInsertToolbar, { - actionViewItemProvider: action => { + actionViewItemProvider: (action, options) => { if (action instanceof MenuItemAction) { - const item = this.instantiationService.createInstance(CodiconActionViewItem, action, undefined); + const item = this.instantiationService.createInstance(CodiconActionViewItem, action, { hoverDelegate: options.hoverDelegate }); return item; } diff --git a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts index b0026da674c..eec7a8ce974 100644 --- a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts +++ b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts @@ -57,6 +57,7 @@ import { settingsTextInputBorder } from 'vs/workbench/contrib/preferences/common import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { AccessibilityVerbositySettingId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; import { registerNavigableContainer } from 'vs/workbench/browser/actions/widgetNavigationCommands'; +import { IActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; const $ = DOM.$; @@ -397,9 +398,9 @@ export class KeybindingsEditor extends EditorPane implements IKeybindingsEditorP const actions = [this.recordKeysAction, this.sortByPrecedenceAction, clearInputAction]; const toolBar = this._register(new ToolBar(this.actionsContainer, this.contextMenuService, { - actionViewItemProvider: (action: IAction) => { + actionViewItemProvider: (action: IAction, options: IActionViewItemOptions) => { if (action.id === this.sortByPrecedenceAction.id || action.id === this.recordKeysAction.id) { - return new ToggleActionViewItem(null, action, { keybinding: this.keybindingsService.lookupKeybinding(action.id)?.getLabel(), toggleStyles: defaultToggleStyles }); + return new ToggleActionViewItem(null, action, { ...options, keybinding: this.keybindingsService.lookupKeybinding(action.id)?.getLabel(), toggleStyles: defaultToggleStyles }); } return undefined; }, diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts b/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts index 1be32db1a39..30542844cc9 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts @@ -6,7 +6,7 @@ import * as DOM from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { ActionBar, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; -import { BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { BaseActionViewItem, IActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { HistoryInputBox, IHistoryInputOptions } from 'vs/base/browser/ui/inputbox/inputBox'; import { Widget } from 'vs/base/browser/ui/widget'; import { Action, IAction } from 'vs/base/common/actions'; @@ -252,7 +252,7 @@ export class SettingsTargetsWidget extends Widget { orientation: ActionsOrientation.HORIZONTAL, focusOnlyEnabledItems: true, ariaLabel: localize('settingsSwitcherBarAriaLabel', "Settings Switcher"), - actionViewItemProvider: (action: IAction) => action.id === 'folderSettings' ? this.folderSettings : undefined + actionViewItemProvider: (action: IAction, options: IActionViewItemOptions) => action.id === 'folderSettings' ? this.folderSettings : undefined })); this.userLocalSettings = new Action('userSettings', '', '.settings-tab', true, () => this.updateTarget(ConfigurationTarget.USER_LOCAL)); diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index c0632e494bc..33c9e99303b 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -655,9 +655,9 @@ export class SettingsEditor2 extends EditorPane { this.controlsElement = DOM.append(searchContainer, DOM.$('.settings-clear-widget')); const actionBar = this._register(new ActionBar(this.controlsElement, { - actionViewItemProvider: (action) => { + actionViewItemProvider: (action, options) => { if (action.id === filterAction.id) { - return this.instantiationService.createInstance(SettingsSearchFilterDropdownMenuActionViewItem, action, this.actionRunner, this.searchWidget); + return this.instantiationService.createInstance(SettingsSearchFilterDropdownMenuActionViewItem, action, options, this.actionRunner, this.searchWidget); } return undefined; } diff --git a/src/vs/workbench/contrib/preferences/browser/settingsSearchMenu.ts b/src/vs/workbench/contrib/preferences/browser/settingsSearchMenu.ts index 93e13c0234d..d119cd97f69 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsSearchMenu.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsSearchMenu.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { IActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; import { IAction, IActionRunner } from 'vs/base/common/actions'; @@ -17,6 +18,7 @@ export class SettingsSearchFilterDropdownMenuActionViewItem extends DropdownMenu constructor( action: IAction, + options: IActionViewItemOptions, actionRunner: IActionRunner | undefined, private readonly searchWidget: SuggestEnabledInput, @IContextMenuService contextMenuService: IContextMenuService @@ -25,6 +27,7 @@ export class SettingsSearchFilterDropdownMenuActionViewItem extends DropdownMenu { getActions: () => this.getActions() }, contextMenuService, { + ...options, actionRunner, classNames: action.class, anchorAlignmentProvider: () => AnchorAlignment.RIGHT, diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts index e9bf671e021..e2fd9619a69 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts @@ -279,9 +279,9 @@ class TerminalTabsRenderer implements IListRenderer + actionViewItemProvider: (action, options) => action instanceof MenuItemAction - ? this._instantiationService.createInstance(MenuEntryActionViewItem, action, undefined) + ? this._instantiationService.createInstance(MenuEntryActionViewItem, action, { hoverDelegate: options.hoverDelegate }) : undefined }); diff --git a/src/vs/workbench/contrib/testing/browser/testingExplorerFilter.ts b/src/vs/workbench/contrib/testing/browser/testingExplorerFilter.ts index a7490c873cf..16b2f5f1e92 100644 --- a/src/vs/workbench/contrib/testing/browser/testingExplorerFilter.ts +++ b/src/vs/workbench/contrib/testing/browser/testingExplorerFilter.ts @@ -5,7 +5,7 @@ import * as dom from 'vs/base/browser/dom'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; -import { BaseActionViewItem, IBaseActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { BaseActionViewItem, IActionViewItemOptions, IBaseActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; import { Action, IAction, IActionRunner, Separator } from 'vs/base/common/actions'; @@ -121,9 +121,9 @@ export class TestingExplorerFilter extends BaseActionViewItem { }))); const actionbar = this._register(new ActionBar(container, { - actionViewItemProvider: action => { + actionViewItemProvider: (action, options) => { if (action.id === this.filtersAction.id) { - return this.instantiationService.createInstance(FiltersDropdownMenuActionViewItem, action, this.state, this.actionRunner); + return this.instantiationService.createInstance(FiltersDropdownMenuActionViewItem, action, options, this.state, this.actionRunner); } return undefined; }, @@ -176,6 +176,7 @@ class FiltersDropdownMenuActionViewItem extends DropdownMenuActionViewItem { constructor( action: IAction, + options: IActionViewItemOptions, private readonly filters: ITestExplorerFilterState, actionRunner: IActionRunner, @IContextMenuService contextMenuService: IContextMenuService, diff --git a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts index 7f5dcfb7920..901d0a5d5da 100644 --- a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts +++ b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts @@ -2227,9 +2227,9 @@ class TestRunElementRenderer implements ICompressibleTreeRenderer + actionViewItemProvider: (action, options) => action instanceof MenuItemAction - ? this.instantiationService.createInstance(MenuEntryActionViewItem, action, undefined) + ? this.instantiationService.createInstance(MenuEntryActionViewItem, action, { hoverDelegate: options.hoverDelegate }) : undefined }); From 13a2ba89a6159246bdf9b9d220c30a57eecfbd79 Mon Sep 17 00:00:00 2001 From: Ulugbek Abdullaev Date: Sun, 25 Feb 2024 20:49:24 +0100 Subject: [PATCH 17/18] rename suggestions: fix width overflow --- src/vs/editor/contrib/rename/browser/renameInputField.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/editor/contrib/rename/browser/renameInputField.ts b/src/vs/editor/contrib/rename/browser/renameInputField.ts index cf76d531a2a..1b3728c30c6 100644 --- a/src/vs/editor/contrib/rename/browser/renameInputField.ts +++ b/src/vs/editor/contrib/rename/browser/renameInputField.ts @@ -515,7 +515,6 @@ class CandidatesView { public layout({ height, width }: { height: number; width: number }): void { this._availableHeight = height; this._minimumWidth = width; - this._listContainer.style.width = `${this._minimumWidth}px`; } public setCandidates(candidates: NewSymbolName[]): void { From 458194ed77b3b341f498d3058ca83f4c286bc7d3 Mon Sep 17 00:00:00 2001 From: Roman Nikitenko Date: Mon, 18 Mar 2024 16:29:41 +0200 Subject: [PATCH 18/18] Update ./rebase/changelog file Signed-off-by: Roman Nikitenko rh-pre-commit.version: 2.2.0 rh-pre-commit.check-secrets: ENABLED --- .rebase/CHANGELOG.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.rebase/CHANGELOG.md b/.rebase/CHANGELOG.md index 284738bec2b..285f3e253ff 100644 --- a/.rebase/CHANGELOG.md +++ b/.rebase/CHANGELOG.md @@ -20,3 +20,17 @@ https://github.com/che-incubator/che-code/pull/326 - code/product.json --- + +#### @benoitf @RomanNikitenko +https://github.com/che-incubator/che-code/pull/43/commits/d04ac87e51e09d3f2328efb2bdcdcd23a259b969 \ +https://github.com/che-incubator/che-code/pull/337/commits/875893566c2acd0bb7031ac3db9d7002ca36feb1#diff-bf304bf7a29dbc8f1d0cbb3219344cd3aeb6d8262394731c63bc40326a8760ce + +- code/src/vs/base/common/network.ts +--- + + +#### @benoitf +https://github.com/che-incubator/che-code/commit/eed0a5213ba1b29b810d53f6365aaa2294165845#diff-2735bf66f14ee64b9ce6fdc30355a5e3085ae96a791cd01d65843a8dcef7c166 + +- code/src/vs/server/webClientServer.ts +---