Revert "fix: update VS source to more recent version"

This commit is contained in:
Mathew Pareles 2024-09-23 02:38:31 -07:00 committed by GitHub
parent 4b0c604d97
commit edaf355cdb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
85 changed files with 575 additions and 1032 deletions

View file

@ -136,6 +136,6 @@ export function isWCOEnabled(): boolean {
// Returns the bounding rect of the titlebar area if it is supported and defined
// See docs at https://developer.mozilla.org/en-US/docs/Web/API/WindowControlsOverlay/getTitlebarAreaRect
export function getWCOTitlebarAreaRect(targetWindow: Window): DOMRect | undefined {
return (targetWindow.navigator as any)?.windowControlsOverlay?.getTitlebarAreaRect();
export function getWCOBoundingRect(): DOMRect | undefined {
return (navigator as any)?.windowControlsOverlay?.getTitlebarAreaRect();
}

View file

@ -11,7 +11,7 @@ import { $ } from 'vs/base/browser/dom';
import { IHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate';
import { Button } from 'vs/base/browser/ui/button/button';
import { DisposableMap, DisposableStore } from 'vs/base/common/lifecycle';
import { createInstantHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory';
import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory';
export interface IRadioStyles {
readonly activeForeground?: string;
@ -53,7 +53,7 @@ export class Radio extends Widget {
constructor(opts: IRadioOptions) {
super();
this.hoverDelegate = opts.hoverDelegate ?? this._register(createInstantHoverDelegate());
this.hoverDelegate = opts.hoverDelegate ?? getDefaultHoverDelegate('element');
this.domNode = $('.monaco-custom-radio');
this.domNode.setAttribute('role', 'radio');

View file

@ -217,6 +217,24 @@ export interface FileFilter {
name: string;
}
export interface OpenDevToolsOptions {
/**
* Opens the devtools with specified dock state, can be `left`, `right`, `bottom`,
* `undocked`, `detach`. Defaults to last used dock state. In `undocked` mode it's
* possible to dock back. In `detach` mode it's not.
*/
mode: ('left' | 'right' | 'bottom' | 'undocked' | 'detach');
/**
* Whether to bring the opened devtools window to the foreground. The default is
* `true`.
*/
activate?: boolean;
/**
* A title for the DevTools window (only in `undocked` or `detach` mode).
*/
title?: string;
}
interface InputEvent {
// Docs: https://electronjs.org/docs/api/structures/input-event

View file

@ -222,7 +222,7 @@ export class HoverWidget extends Widget implements IHoverWidget {
}
// Show the hover hint if needed
if (options.appearance?.showHoverHint) {
if (hideOnHover && options.appearance?.showHoverHint) {
const statusBarElement = $('div.hover-row.status-bar');
const infoElement = $('div.info');
infoElement.textContent = localize('hoverhint', 'Hold {0} key to mouse over', isMacintosh ? 'Option' : 'Alt');

View file

@ -2019,9 +2019,9 @@ export interface CommentThreadChangedEvent<T> {
}
export interface CodeLens {
range: IRange;
id?: string;
command?: Command;
range: IRange; // the range of code
id?: string; // no idea what this is for
command?: Command; // command to run when they click the codeLens
}
export interface CodeLensList {

View file

@ -180,12 +180,6 @@ export class CodeActionController extends Disposable implements IEditorContribut
return;
}
const selection = this._editor.getSelection();
if (selection?.startLineNumber !== newState.position.lineNumber) {
return;
}
this._lightBulbWidget.value?.update(actions, newState.trigger, newState.position);
if (newState.trigger.type === CodeActionTriggerType.Invoke) {

View file

@ -75,14 +75,6 @@ export class LightBulbWidget extends Disposable implements IContentWidget {
private _gutterState: LightBulbState.State = LightBulbState.Hidden;
private _iconClasses: string[] = [];
private readonly lightbulbClasses = [
'codicon-' + GUTTER_LIGHTBULB_ICON.id,
'codicon-' + GUTTER_LIGHTBULB_AIFIX_AUTO_FIX_ICON.id,
'codicon-' + GUTTER_LIGHTBULB_AUTO_FIX_ICON.id,
'codicon-' + GUTTER_LIGHTBULB_AIFIX_ICON.id,
'codicon-' + GUTTER_SPARKLE_FILLED_ICON.id
];
private _preferredKbLabel?: string;
private _quickFixKbLabel?: string;
@ -156,8 +148,15 @@ export class LightBulbWidget extends Disposable implements IContentWidget {
}));
this._register(this._editor.onMouseDown(async (e: IEditorMouseEvent) => {
const lightbulbClasses = [
'codicon-' + GUTTER_LIGHTBULB_ICON.id,
'codicon-' + GUTTER_LIGHTBULB_AIFIX_AUTO_FIX_ICON.id,
'codicon-' + GUTTER_LIGHTBULB_AUTO_FIX_ICON.id,
'codicon-' + GUTTER_LIGHTBULB_AIFIX_ICON.id,
'codicon-' + GUTTER_SPARKLE_FILLED_ICON.id
];
if (!e.target.element || !this.lightbulbClasses.some(cls => e.target.element && e.target.element.classList.contains(cls))) {
if (!e.target.element || !lightbulbClasses.some(cls => e.target.element && e.target.element.classList.contains(cls))) {
return;
}
@ -248,9 +247,7 @@ export class LightBulbWidget extends Disposable implements IContentWidget {
let hasDecoration = false;
if (currLineDecorations) {
for (const decoration of currLineDecorations) {
const glyphClass = decoration.options.glyphMarginClassName;
if (glyphClass && !this.lightbulbClasses.some(className => glyphClass.includes(className))) {
if (decoration.options.glyphMarginClassName) {
hasDecoration = true;
break;
}
@ -274,6 +271,7 @@ export class LightBulbWidget extends Disposable implements IContentWidget {
const currLineEmptyOrIndented = isLineEmptyOrIndented(lineNumber);
const notEmpty = !nextLineEmptyOrIndented && !prevLineEmptyOrIndented;
// check above and below. if both are blocked, display lightbulb in the gutter.
if (!nextLineEmptyOrIndented && !prevLineEmptyOrIndented && !hasDecoration) {
this.gutterState = new LightBulbState.Showing(actions, trigger, atPosition, {
@ -282,7 +280,7 @@ export class LightBulbWidget extends Disposable implements IContentWidget {
});
this.renderGutterLightbub();
return this.hide();
} else if (prevLineEmptyOrIndented || endLine || (prevLineEmptyOrIndented && !currLineEmptyOrIndented)) {
} else if (prevLineEmptyOrIndented || endLine || (notEmpty && !currLineEmptyOrIndented)) {
effectiveLineNumber -= 1;
} else if (nextLineEmptyOrIndented || (notEmpty && currLineEmptyOrIndented)) {
effectiveLineNumber += 1;

View file

@ -3,17 +3,17 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Event } from 'vs/base/common/event';
import { LRUCache } from 'vs/base/common/map';
import { Range } from 'vs/editor/common/core/range';
import { ITextModel } from 'vs/editor/common/model';
import { CodeLens, CodeLensList, CodeLensProvider } from 'vs/editor/common/languages';
import { CodeLensModel } from 'vs/editor/contrib/codelens/browser/codelens';
import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IStorageService, StorageScope, StorageTarget, WillSaveStateReason } from 'vs/platform/storage/common/storage';
import { mainWindow } from 'vs/base/browser/window';
import { runWhenWindowIdle } from 'vs/base/browser/dom';
import { Event } from '../../../../base/common/event.js';
import { LRUCache } from '../../../../base/common/map.js';
import { Range } from '../../../common/core/range.js';
import { ITextModel } from '../../../common/model.js';
import { CodeLens, CodeLensList, CodeLensProvider } from '../../../common/languages.js';
import { CodeLensModel } from './codelens.js';
import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';
import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
import { IStorageService, StorageScope, StorageTarget, WillSaveStateReason } from '../../../../platform/storage/common/storage.js';
import { mainWindow } from '../../../../base/browser/window.js';
import { runWhenWindowIdle } from '../../../../base/browser/dom.js';
export const ICodeLensCache = createDecorator<ICodeLensCache>('ICodeLensCache');

View file

@ -3,17 +3,17 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CancellationToken } from 'vs/base/common/cancellation';
import { illegalArgument, onUnexpectedExternalError } from 'vs/base/common/errors';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { assertType } from 'vs/base/common/types';
import { URI } from 'vs/base/common/uri';
import { ITextModel } from 'vs/editor/common/model';
import { CodeLens, CodeLensList, CodeLensProvider } from 'vs/editor/common/languages';
import { IModelService } from 'vs/editor/common/services/model';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { LanguageFeatureRegistry } from 'vs/editor/common/languageFeatureRegistry';
import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures';
import { CancellationToken } from '../../../../base/common/cancellation.js';
import { illegalArgument, onUnexpectedExternalError } from '../../../../base/common/errors.js';
import { DisposableStore } from '../../../../base/common/lifecycle.js';
import { assertType } from '../../../../base/common/types.js';
import { URI } from '../../../../base/common/uri.js';
import { ITextModel } from '../../../common/model.js';
import { CodeLens, CodeLensList, CodeLensProvider } from '../../../common/languages.js';
import { IModelService } from '../../../common/services/model.js';
import { CommandsRegistry } from '../../../../platform/commands/common/commands.js';
import { LanguageFeatureRegistry } from '../../../common/languageFeatureRegistry.js';
import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js';
export interface CodeLensItem {
symbol: CodeLens;

View file

@ -4,26 +4,26 @@
*--------------------------------------------------------------------------------------------*/
import { CancelablePromise, createCancelablePromise, disposableTimeout, RunOnceScheduler } from 'vs/base/common/async';
import { onUnexpectedError, onUnexpectedExternalError } from 'vs/base/common/errors';
import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
import { StableEditorScrollState } from 'vs/editor/browser/stableEditorScroll';
import { IActiveCodeEditor, ICodeEditor, IViewZoneChangeAccessor, MouseTargetType } from 'vs/editor/browser/editorBrowser';
import { EditorAction, EditorContributionInstantiation, registerEditorAction, registerEditorContribution, ServicesAccessor } from 'vs/editor/browser/editorExtensions';
import { EditorOption, EDITOR_FONT_DEFAULTS } from 'vs/editor/common/config/editorOptions';
import { IEditorContribution } from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { IModelDecorationsChangeAccessor } from 'vs/editor/common/model';
import { CodeLens, Command } from 'vs/editor/common/languages';
import { CodeLensItem, CodeLensModel, getCodeLensModel } from 'vs/editor/contrib/codelens/browser/codelens';
import { ICodeLensCache } from 'vs/editor/contrib/codelens/browser/codeLensCache';
import { CodeLensHelper, CodeLensWidget } from 'vs/editor/contrib/codelens/browser/codelensWidget';
import { localize } from 'vs/nls';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { IFeatureDebounceInformation, ILanguageFeatureDebounceService } from 'vs/editor/common/services/languageFeatureDebounce';
import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures';
import { CancelablePromise, createCancelablePromise, disposableTimeout, RunOnceScheduler } from '../../../../base/common/async.js';
import { onUnexpectedError, onUnexpectedExternalError } from '../../../../base/common/errors.js';
import { DisposableStore, toDisposable } from '../../../../base/common/lifecycle.js';
import { StableEditorScrollState } from '../../../browser/stableEditorScroll.js';
import { IActiveCodeEditor, ICodeEditor, IViewZoneChangeAccessor, MouseTargetType } from '../../../browser/editorBrowser.js';
import { EditorAction, EditorContributionInstantiation, registerEditorAction, registerEditorContribution, ServicesAccessor } from '../../../browser/editorExtensions.js';
import { EditorOption, EDITOR_FONT_DEFAULTS } from '../../../common/config/editorOptions.js';
import { IEditorContribution } from '../../../common/editorCommon.js';
import { EditorContextKeys } from '../../../common/editorContextKeys.js';
import { IModelDecorationsChangeAccessor } from '../../../common/model.js';
import { CodeLens, Command } from '../../../common/languages.js';
import { CodeLensItem, CodeLensModel, getCodeLensModel } from './codelens.js';
import { ICodeLensCache } from './codeLensCache.js';
import { CodeLensHelper, CodeLensWidget } from './codelensWidget.js';
import { localize } from '../../../../nls.js';
import { ICommandService } from '../../../../platform/commands/common/commands.js';
import { INotificationService } from '../../../../platform/notification/common/notification.js';
import { IQuickInputService } from '../../../../platform/quickinput/common/quickInput.js';
import { IFeatureDebounceInformation, ILanguageFeatureDebounceService } from '../../../common/services/languageFeatureDebounce.js';
import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js';
export class CodeLensContribution implements IEditorContribution {

View file

@ -3,16 +3,16 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as dom from 'vs/base/browser/dom';
import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels';
import { Constants } from 'vs/base/common/uint';
import 'vs/css!./codelensWidget';
import { ContentWidgetPositionPreference, IActiveCodeEditor, IContentWidget, IContentWidgetPosition, IViewZone, IViewZoneChangeAccessor } from 'vs/editor/browser/editorBrowser';
import { Range } from 'vs/editor/common/core/range';
import { IModelDecorationsChangeAccessor, IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
import { CodeLens, Command } from 'vs/editor/common/languages';
import { CodeLensItem } from 'vs/editor/contrib/codelens/browser/codelens';
import * as dom from '../../../../base/browser/dom.js';
import { renderLabelWithIcons } from '../../../../base/browser/ui/iconLabel/iconLabels.js';
import { Constants } from '../../../../base/common/uint.js';
import './codelensWidget.css';
import { ContentWidgetPositionPreference, IActiveCodeEditor, IContentWidget, IContentWidgetPosition, IViewZone, IViewZoneChangeAccessor } from '../../../browser/editorBrowser.js';
import { Range } from '../../../common/core/range.js';
import { IModelDecorationsChangeAccessor, IModelDeltaDecoration, ITextModel } from '../../../common/model.js';
import { ModelDecorationOptions } from '../../../common/model/textModel.js';
import { CodeLens, Command } from '../../../common/languages.js';
import { CodeLensItem } from './codelens.js';
class CodeLensViewZone implements IViewZone {

View file

@ -318,8 +318,7 @@ export class RenameWidget implements IRenameWidget, IContentWidget, IDisposable
}
afterRender(position: ContentWidgetPositionPreference | null): void {
// FIXME@ulugbekna: commenting trace log out until we start unmounting the widget from editor properly - https://github.com/microsoft/vscode/issues/226975
// this._trace('invoking afterRender, position: ', position ? 'not null' : 'null');
this._trace('invoking afterRender, position: ', position ? 'not null' : 'null');
if (position === null) {
// cancel rename when input widget isn't rendered anymore
this.cancelInput(true, 'afterRender (because position is null)');
@ -364,7 +363,7 @@ export class RenameWidget implements IRenameWidget, IContentWidget, IDisposable
}
cancelInput(focusEditor: boolean, caller: string): void {
// this._trace(`invoking cancelInput, caller: ${caller}, _currentCancelInput: ${this._currentAcceptInput ? 'not undefined' : 'undefined'}`);
this._trace(`invoking cancelInput, caller: ${caller}, _currentCancelInput: ${this._currentAcceptInput ? 'not undefined' : 'undefined'}`);
this._currentCancelInput?.(focusEditor);
}

View file

@ -166,8 +166,8 @@ export class ActionList<T> extends Disposable {
private readonly _list: List<IActionListItem<T>>;
private readonly _actionLineHeight = 24;
private readonly _headerLineHeight = 26;
private readonly _actionLineHeight = 28;
private readonly _headerLineHeight = 28;
private readonly _allMenuItems: readonly IActionListItem<T>[];

View file

@ -132,9 +132,8 @@
/* Action bar */
.action-widget .action-widget-action-bar {
background-color: var(--vscode-editorActionList-background);
background-color: var(--vscode-editorHoverWidget-statusBarBackground);
border-top: 1px solid var(--vscode-editorHoverWidget-border);
margin-top: 2px;
}
.action-widget .action-widget-action-bar::before {
@ -144,7 +143,7 @@
}
.action-widget .action-widget-action-bar .actions-container {
padding: 3px 8px 0;
padding: 0 8px;
}
.action-widget-action-bar .action-label {

View file

@ -16,15 +16,15 @@ export class GlobalExtensionEnablementService extends Disposable implements IGlo
private _onDidChangeEnablement = new Emitter<{ readonly extensions: IExtensionIdentifier[]; readonly source?: string }>();
readonly onDidChangeEnablement: Event<{ readonly extensions: IExtensionIdentifier[]; readonly source?: string }> = this._onDidChangeEnablement.event;
private readonly storageManager: StorageManager;
private readonly storageManger: StorageManager;
constructor(
@IStorageService storageService: IStorageService,
@IExtensionManagementService extensionManagementService: IExtensionManagementService,
) {
super();
this.storageManager = this._register(new StorageManager(storageService));
this._register(this.storageManager.onDidChange(extensions => this._onDidChangeEnablement.fire({ extensions, source: 'storage' })));
this.storageManger = this._register(new StorageManager(storageService));
this._register(this.storageManger.onDidChange(extensions => this._onDidChangeEnablement.fire({ extensions, source: 'storage' })));
this._register(extensionManagementService.onDidInstallExtensions(e => e.forEach(({ local, operation }) => {
if (local && operation === InstallOperation.Migrate) {
this._removeFromDisabledExtensions(local.identifier); /* Reset migrated extensions */
@ -84,11 +84,11 @@ export class GlobalExtensionEnablementService extends Disposable implements IGlo
}
private _getExtensions(storageId: string): IExtensionIdentifier[] {
return this.storageManager.get(storageId, StorageScope.PROFILE);
return this.storageManger.get(storageId, StorageScope.PROFILE);
}
private _setExtensions(storageId: string, extensions: IExtensionIdentifier[]): void {
this.storageManager.set(storageId, extensions, StorageScope.PROFILE);
this.storageManger.set(storageId, extensions, StorageScope.PROFILE);
}
}

View file

@ -6,7 +6,7 @@
import { VSBuffer } from 'vs/base/common/buffer';
import { Event } from 'vs/base/common/event';
import { URI } from 'vs/base/common/uri';
import { MessageBoxOptions, MessageBoxReturnValue, OpenDialogOptions, OpenDialogReturnValue, SaveDialogOptions, SaveDialogReturnValue } from 'vs/base/parts/sandbox/common/electronTypes';
import { MessageBoxOptions, MessageBoxReturnValue, OpenDevToolsOptions, OpenDialogOptions, OpenDialogReturnValue, SaveDialogOptions, SaveDialogReturnValue } from 'vs/base/parts/sandbox/common/electronTypes';
import { ISerializableCommandAction } from 'vs/platform/action/common/action';
import { INativeOpenDialogOptions } from 'vs/platform/dialogs/common/dialogs';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
@ -178,7 +178,7 @@ export interface ICommonNativeHostService {
exit(code: number): Promise<void>;
// Development
openDevTools(options?: INativeHostOptions): Promise<void>;
openDevTools(options?: Partial<OpenDevToolsOptions> & INativeHostOptions): Promise<void>;
toggleDevTools(options?: INativeHostOptions): Promise<void>;
// Perf Introspection

View file

@ -5,7 +5,7 @@
import * as fs from 'fs';
import { exec } from 'child_process';
import { app, BrowserWindow, clipboard, Display, Menu, MessageBoxOptions, MessageBoxReturnValue, OpenDialogOptions, OpenDialogReturnValue, powerMonitor, SaveDialogOptions, SaveDialogReturnValue, screen, shell, webContents } from 'electron';
import { app, BrowserWindow, clipboard, Display, Menu, MessageBoxOptions, MessageBoxReturnValue, OpenDevToolsOptions, OpenDialogOptions, OpenDialogReturnValue, powerMonitor, SaveDialogOptions, SaveDialogReturnValue, screen, shell, webContents } from 'electron';
import { arch, cpus, freemem, loadavg, platform, release, totalmem, type } from 'os';
import { promisify } from 'util';
import { memoize } from 'vs/base/common/decorators';
@ -33,7 +33,7 @@ import { IProductService } from 'vs/platform/product/common/productService';
import { IPartsSplash } from 'vs/platform/theme/common/themeService';
import { IThemeMainService } from 'vs/platform/theme/electron-main/themeMainService';
import { ICodeWindow } from 'vs/platform/window/electron-main/window';
import { IColorScheme, IOpenedAuxiliaryWindow, IOpenedMainWindow, IOpenEmptyWindowOptions, IOpenWindowOptions, IPoint, IRectangle, IWindowOpenable, useWindowControlsOverlay } from 'vs/platform/window/common/window';
import { IColorScheme, IOpenedAuxiliaryWindow, IOpenedMainWindow, IOpenEmptyWindowOptions, IOpenWindowOptions, IPoint, IRectangle, IWindowOpenable } from 'vs/platform/window/common/window';
import { IWindowsMainService, OpenContext } from 'vs/platform/windows/electron-main/windows';
import { isWorkspaceIdentifier, toWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace';
import { IWorkspacesManagementMainService } from 'vs/platform/workspaces/electron-main/workspacesManagementMainService';
@ -855,28 +855,14 @@ export class NativeHostMainService extends Disposable implements INativeHostMain
//#region Development
async openDevTools(windowId: number | undefined, options?: INativeHostOptions): Promise<void> {
async openDevTools(windowId: number | undefined, options?: Partial<OpenDevToolsOptions> & INativeHostOptions): Promise<void> {
const window = this.windowById(options?.targetWindowId, windowId);
let mode: 'bottom' | undefined = undefined;
if (isLinux && useWindowControlsOverlay(this.configurationService)) {
mode = 'bottom'; // TODO@bpasero WCO and devtools collide with default option 'right'
}
window?.win?.webContents.openDevTools(mode ? { mode } : undefined);
window?.win?.webContents.openDevTools(options?.mode ? { mode: options.mode, activate: options.activate } : undefined);
}
async toggleDevTools(windowId: number | undefined, options?: INativeHostOptions): Promise<void> {
const window = this.windowById(options?.targetWindowId, windowId);
const webContents = window?.win?.webContents;
if (!webContents) {
return;
}
if (isLinux && useWindowControlsOverlay(this.configurationService) && !webContents.isDevToolsOpened()) {
webContents.openDevTools({ mode: 'bottom' }); // TODO@bpasero WCO and devtools collide with default option 'right'
} else {
webContents.toggleDevTools();
}
window?.win?.webContents.toggleDevTools();
}
//#endregion

View file

@ -159,7 +159,7 @@ class ColorRegistry implements IColorRegistry {
public registerColor(id: string, defaults: ColorDefaults | ColorValue | null, description: string, needsTransparency = false, deprecationMessage?: string): ColorIdentifier {
const colorContribution: ColorContribution = { id, description, defaults, needsTransparency, deprecationMessage };
this.colorsById[id] = colorContribution;
const propertySchema: IJSONSchemaWithSnippets = { type: 'string', format: 'color-hex', defaultSnippets: [{ body: '${1:#ff0000}' }] };
const propertySchema: IJSONSchemaWithSnippets = { type: 'string', description, format: 'color-hex', defaultSnippets: [{ body: '${1:#ff0000}' }] };
if (deprecationMessage) {
propertySchema.deprecationMessage = deprecationMessage;
}
@ -168,7 +168,6 @@ class ColorRegistry implements IColorRegistry {
propertySchema.patternErrorMessage = nls.localize('transparecyRequired', 'This color must be transparent or it will obscure content');
}
this.colorSchema.properties[id] = {
description,
oneOf: [
propertySchema,
{ type: 'string', const: DEFAULT_COLOR_CONFIG_VALUE, description: nls.localize('useDefault', 'Use the default color.') }

View file

@ -14,6 +14,7 @@ import { NativeParsedArgs } from 'vs/platform/environment/common/argv';
import { FileType } from 'vs/platform/files/common/files';
import { ILoggerResource, LogLevel } from 'vs/platform/log/common/log';
import { PolicyDefinition, PolicyValue } from 'vs/platform/policy/common/policy';
import product from 'vs/platform/product/common/product';
import { IPartsSplash } from 'vs/platform/theme/common/themeService';
import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile';
import { IAnyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace';
@ -239,6 +240,8 @@ export function useWindowControlsOverlay(configurationService: IConfigurationSer
if (typeof setting === 'boolean') {
return setting;
}
return product.quality !== 'stable'; // disable by default in stable for now (TODO@bpasero TODO@benibenj flip when custom title is default)
}
// Default to true.

View file

@ -28,7 +28,6 @@ import { IEditSessionIdentityService } from 'vs/platform/workspace/common/editSe
import { EditorResourceAccessor, SaveReason, SideBySideEditor } from 'vs/workbench/common/editor';
import { coalesce, firstOrDefault } from 'vs/base/common/arrays';
import { ICanonicalUriService } from 'vs/platform/workspace/common/canonicalUri';
import { revive } from 'vs/base/common/marshalling';
@extHostNamedCustomer(MainContext.MainThreadWorkspace)
export class MainThreadWorkspace implements MainThreadWorkspaceShape {
@ -147,7 +146,7 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
const query = this._queryBuilder.file(
includeFolder ? [includeFolder] : workspace.folders,
revive(options)
options
);
return this._searchService.fileSearch(query, token).then(result => {
@ -165,7 +164,7 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
const workspace = this._contextService.getWorkspace();
const folders = folder ? [folder] : workspace.folders.map(folder => folder.uri);
const query = this._queryBuilder.text(pattern, folders, revive(options));
const query = this._queryBuilder.text(pattern, folders, options);
query._reason = 'startTextSearch';
const onProgress = (p: ISearchProgressItem) => {

View file

@ -550,6 +550,14 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
registerCodeLensProvider(selector: vscode.DocumentSelector, provider: vscode.CodeLensProvider): vscode.Disposable {
return extHostLanguageFeatures.registerCodeLensProvider(extension, checkSelector(selector), provider);
},
// VOID added this (I think will need to add this back when add ctrl+K)
// registerVoidCtrlKProvider(selector: vscode.DocumentSelector, provider: vscode.CodeLensProvider): vscode.Disposable {
// return extHostLanguageFeatures.registerCodeLensProvider(extension, checkSelector(selector), provider);
// },
registerDefinitionProvider(selector: vscode.DocumentSelector, provider: vscode.DefinitionProvider): vscode.Disposable {
return extHostLanguageFeatures.registerDefinitionProvider(extension, checkSelector(selector), provider);
},

View file

@ -304,6 +304,17 @@ const newCommands: ApiCommand[] = [
})(value);
})
),
// // --- Void code lens
// new ApiCommand(
// 'vscode.executeVoidCodeLensProvider', '_executeVoidCodeLensProvider', 'Execute Void code lens provider.',
// [ApiCommandArgument.Uri, ApiCommandArgument.Number.with('itemResolveCount', 'Number of lenses that should be resolved and returned. Will only return resolved lenses, will impact performance)').optional()],
// new ApiCommandResult<languages.CodeLens[], vscode.CodeLens[] | undefined>('A promise that resolves to an array of VoidCodeLens-instances.', (value, _args, converter) => {
// return tryMapWith<languages.CodeLens, vscode.CodeLens>(item => {
// return new types.CodeLens(typeConverters.Range.to(item.range), item.command && converter.fromInternal(item.command));
// })(value);
// })
// ),
// --- code actions
new ApiCommand(
'vscode.executeCodeActionProvider', '_executeCodeActionProvider', 'Execute code action provider.',

View file

@ -2312,15 +2312,15 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
}
$provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Promise<extHostProtocol.ICodeLensListDto | undefined> {
return this._withAdapter(handle, CodeLensAdapter, adapter => adapter.provideCodeLenses(URI.revive(resource), token), undefined, token, resource.scheme === 'output');
return this._withAdapter(handle, CodeLensAdapter, adapter => adapter.provideCodeLenses(URI.revive(resource), token), undefined, token);
}
$resolveCodeLens(handle: number, symbol: extHostProtocol.ICodeLensDto, token: CancellationToken): Promise<extHostProtocol.ICodeLensDto | undefined> {
return this._withAdapter(handle, CodeLensAdapter, adapter => adapter.resolveCodeLens(symbol, token), undefined, undefined, true);
return this._withAdapter(handle, CodeLensAdapter, adapter => adapter.resolveCodeLens(symbol, token), undefined, undefined);
}
$releaseCodeLenses(handle: number, cacheId: number): void {
this._withAdapter(handle, CodeLensAdapter, adapter => Promise.resolve(adapter.releaseCodeLenses(cacheId)), undefined, undefined, true);
this._withAdapter(handle, CodeLensAdapter, adapter => Promise.resolve(adapter.releaseCodeLenses(cacheId)), undefined, undefined);
}
// --- declaration

View file

@ -584,7 +584,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
}
const parsedInclude = include ? parseSearchExcludeInclude(GlobPattern.from(include)) : undefined;
const excludePatterns = globsToISearchPatternBuilder(options.exclude);
const excludePatterns = include ? globsToISearchPatternBuilder(options.exclude) : undefined;
return {
options: {
@ -664,10 +664,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
async findTextInFilesBase(query: vscode.TextSearchQuery, queryOptions: QueryOptions<ITextQueryBuilderOptions>[] | undefined, callback: (result: ITextSearchResult<URI>, uri: URI) => void, token: vscode.CancellationToken = CancellationToken.None): Promise<vscode.TextSearchComplete> {
const requestId = this._requestIdProvider.getNext();
let isCanceled = false;
token.onCancellationRequested(_ => {
isCanceled = true;
});
const isCanceled = false;
this._activeSearchCallbacks[requestId] = p => {
if (isCanceled) {

View file

@ -138,11 +138,11 @@
color: var(--vscode-titleBar-activeForeground);
}
.monaco-workbench .part.titlebar.inactive > .titlebar-container > .titlebar-center > .window-title > .command-center > .monaco-toolbar > .monaco-action-bar > .actions-container > .action-item > .action-label {
.monaco-workbench .part.titlebar.inactive > .titlebar-container > .titlebar-center > .window-title > .command-center > .monaco-toolbar > .monaco-action-bar > .actions-container > .action-item > .action-label {
color: var(--vscode-titleBar-inactiveForeground);
}
.monaco-workbench .part.titlebar > .titlebar-container > .titlebar-center > .window-title > .command-center > .monaco-toolbar > .monaco-action-bar > .actions-container > .action-item > .action-label {
.monaco-workbench .part.titlebar > .titlebar-container > .titlebar-center > .window-title > .command-center > .monaco-toolbar > .monaco-action-bar > .actions-container > .action-item > .action-label {
color: inherit;
}
@ -182,7 +182,7 @@
text-overflow: ellipsis;
}
.monaco-workbench .part.titlebar > .titlebar-container > .titlebar-center > .window-title > .command-center .action-item.command-center-center.multiple {
.monaco-workbench .part.titlebar > .titlebar-container > .titlebar-center > .window-title > .command-center .action-item.command-center-center.multiple {
justify-content: flex-start;
padding: 0 12px;
}
@ -280,7 +280,7 @@
border-left: 1px solid transparent;
}
/* Window Controls Container */
/* Window Controls (Minimize, Max/Restore, Close) */
.monaco-workbench .part.titlebar .window-controls-container {
display: flex;
flex-grow: 0;
@ -292,12 +292,7 @@
height: 100%;
}
.monaco-workbench.fullscreen .part.titlebar .window-controls-container {
display: none;
background-color: transparent;
}
/* Window Controls Container Web: Apply WCO environment variables (https://developer.mozilla.org/en-US/docs/Web/CSS/env#titlebar-area-x) */
/* Web WCO Sizing/Ordering */
.monaco-workbench.web .part.titlebar .titlebar-right .window-controls-container {
width: calc(100vw - env(titlebar-area-width, 100vw) - env(titlebar-area-x, 0px));
height: env(titlebar-area-height, 35px);
@ -316,31 +311,29 @@
order: 1;
}
/* Window Controls Container Desktop: apply zoom friendly size */
.monaco-workbench:not(.web):not(.mac) .part.titlebar .window-controls-container {
/* Desktop Windows/Linux Window Controls*/
.monaco-workbench:not(.web):not(.mac) .part.titlebar .window-controls-container.primary {
width: calc(138px / var(--zoom-factor, 1));
}
.monaco-workbench:not(.web):not(.mac) .part.titlebar .titlebar-container.counter-zoom .window-controls-container {
.monaco-workbench:not(.web):not(.mac) .part.titlebar .titlebar-container.counter-zoom .window-controls-container.primary {
width: 138px;
}
.monaco-workbench.linux:not(.web) .part.titlebar .window-controls-container.wco-enabled {
width: calc(var(--title-wco-width, 138px));
}
.monaco-workbench.linux:not(.web) .part.titlebar .titlebar-container.counter-zoom .window-controls-container.wco-enabled {
width: var(--title-wco-width, 138px);
}
.monaco-workbench:not(.web):not(.mac) .part.titlebar .titlebar-container:not(.counter-zoom) .window-controls-container * {
zoom: calc(1 / var(--zoom-factor, 1));
}
.monaco-workbench:not(.web).mac .part.titlebar .window-controls-container {
/* Desktop macOS Window Controls */
.monaco-workbench:not(.web).mac .part.titlebar .window-controls-container.primary {
width: 70px;
}
.monaco-workbench.fullscreen .part.titlebar .window-controls-container {
display: none;
background-color: transparent;
}
/* Window Control Icons */
.monaco-workbench .part.titlebar .window-controls-container > .window-icon {
display: flex;
@ -349,11 +342,6 @@
height: 100%;
width: 46px;
font-size: 16px;
color: var(--vscode-titleBar-activeForeground);
}
.monaco-workbench .part.titlebar.inactive .window-controls-container > .window-icon {
color: var(--vscode-titleBar-inactiveForeground);
}
.monaco-workbench .part.titlebar .window-controls-container > .window-icon::before {
@ -388,6 +376,7 @@
z-index: 2500;
-webkit-app-region: no-drag;
height: 100%;
min-width: 28px;
}
.monaco-workbench .part.titlebar > .titlebar-container > .titlebar-right > .action-toolbar-container {
@ -466,3 +455,11 @@
border-radius: 16px;
text-align: center;
}
.monaco-workbench .part.titlebar .window-controls-container .window-icon {
color: var(--vscode-titleBar-activeForeground);
}
.monaco-workbench .part.titlebar.inactive .window-controls-container .window-icon {
color: var(--vscode-titleBar-inactiveForeground);
}

View file

@ -7,7 +7,7 @@ import 'vs/css!./media/titlebarpart';
import { localize, localize2 } from 'vs/nls';
import { MultiWindowParts, Part } from 'vs/workbench/browser/part';
import { ITitleService } from 'vs/workbench/services/title/browser/titleService';
import { getWCOTitlebarAreaRect, getZoomFactor, isWCOEnabled, onDidChangeZoomLevel } from 'vs/base/browser/browser';
import { getWCOBoundingRect, getZoomFactor, isWCOEnabled } from 'vs/base/browser/browser';
import { MenuBarVisibility, getTitleBarStyle, getMenuBarVisibility, TitlebarStyle, hasCustomTitlebar, hasNativeTitlebar, DEFAULT_CUSTOM_TITLEBAR_HEIGHT } from 'vs/platform/window/common/window';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
@ -228,7 +228,7 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart {
const wcoEnabled = isWeb && isWCOEnabled();
let value = this.isCommandCenterVisible || wcoEnabled ? DEFAULT_CUSTOM_TITLEBAR_HEIGHT : 30;
if (wcoEnabled) {
value = Math.max(value, getWCOTitlebarAreaRect(getWindow(this.element))?.height ?? 0);
value = Math.max(value, getWCOBoundingRect()?.height ?? 0);
}
return value / (this.preventZoom ? getZoomFactor(getWindow(this.element)) : 1);
@ -249,7 +249,7 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart {
//#endregion
protected rootContainer!: HTMLElement;
protected windowControlsContainer: HTMLElement | undefined;
protected primaryWindowControls: HTMLElement | undefined;
protected dragRegion: HTMLElement | undefined;
private title!: HTMLElement;
@ -476,49 +476,21 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart {
this.createActionToolBarMenus();
}
// Window Controls Container
let primaryControlLocation = isMacintosh ? 'left' : 'right';
if (isMacintosh && isNative) {
// Check if the locale is RTL, macOS will move traffic lights in RTL locales
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/textInfo
const localeInfo = new Intl.Locale(platformLocale) as any;
if (localeInfo?.textInfo?.direction === 'rtl') {
primaryControlLocation = 'right';
}
}
if (!hasNativeTitlebar(this.configurationService, this.titleBarStyle)) {
let primaryWindowControlsLocation = isMacintosh ? 'left' : 'right';
if (isMacintosh && isNative) {
// Check if the locale is RTL, macOS will move traffic lights in RTL locales
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/textInfo
const localeInfo = new Intl.Locale(platformLocale) as any;
if (localeInfo?.textInfo?.direction === 'rtl') {
primaryWindowControlsLocation = 'right';
}
}
if (isMacintosh && isNative && primaryWindowControlsLocation === 'left') {
// macOS native: controls are on the left and the container is not needed to make room
// for something, except for web where a custom menu being supported). not putting the
// container helps with allowing to move the window when clicking very close to the
// window control buttons.
} else {
this.windowControlsContainer = append(primaryWindowControlsLocation === 'left' ? this.leftContent : this.rightContent, $('div.window-controls-container'));
if (isWeb) {
// Web: its possible to have control overlays on both sides, for example on macOS
// with window controls on the left and PWA controls on the right.
append(primaryWindowControlsLocation === 'left' ? this.rightContent : this.leftContent, $('div.window-controls-container'));
}
if (isWCOEnabled()) {
this.windowControlsContainer.classList.add('wco-enabled');
const updateWCOWidthVariable = () => {
const targetWindow = getWindow(this.element);
const wcoTitlebarAreaRect = getWCOTitlebarAreaRect(targetWindow);
if (wcoTitlebarAreaRect) {
const wcoWidth = targetWindow.innerWidth - wcoTitlebarAreaRect.width - wcoTitlebarAreaRect.x;
this.windowControlsContainer?.style.setProperty('--title-wco-width', `${wcoWidth}px`);
}
};
updateWCOWidthVariable();
this._register(onDidChangeZoomLevel(() => setTimeout(() => updateWCOWidthVariable(), 5))); // Somehow it does not get the right size without this timeout :-/
}
}
this.primaryWindowControls = append(primaryControlLocation === 'left' ? this.leftContent : this.rightContent, $('div.window-controls-container.primary'));
append(primaryControlLocation === 'left' ? this.rightContent : this.leftContent, $('div.window-controls-container.secondary'));
}
// Context menu over title bar: depending on the OS and the location of the click this will either be

View file

@ -152,7 +152,7 @@ class ChatHistoryAction extends Action2 {
let lastDate: string | undefined = undefined;
const picks = items.flatMap((i): [IQuickPickSeparator | undefined, IChatPickerItem] => {
const timeAgoStr = fromNowByDay(i.lastMessageDate, true, true);
const timeAgoStr = fromNowByDay(i.lastMessageDate, true);
const separator: IQuickPickSeparator | undefined = timeAgoStr !== lastDate ? {
type: 'separator', label: timeAgoStr,
} : undefined;

View file

@ -36,7 +36,7 @@ import { ChatAccessibilityService } from 'vs/workbench/contrib/chat/browser/chat
import { ChatEditor, IChatEditorOptions } from 'vs/workbench/contrib/chat/browser/chatEditor';
import { ChatEditorInput, ChatEditorInputSerializer } from 'vs/workbench/contrib/chat/browser/chatEditorInput';
import { agentSlashCommandToMarkdown, agentToMarkdown } from 'vs/workbench/contrib/chat/browser/chatMarkdownDecorationsRenderer';
import { ChatCompatibilityNotifier, ChatExtensionPointHandler } from 'vs/workbench/contrib/chat/browser/chatParticipantContributions';
import { ChatExtensionPointHandler } from 'vs/workbench/contrib/chat/browser/chatParticipantContributions';
import { QuickChatService } from 'vs/workbench/contrib/chat/browser/chatQuick';
import { ChatResponseAccessibleView } from 'vs/workbench/contrib/chat/browser/chatResponseAccessibleView';
import { ChatVariablesService } from 'vs/workbench/contrib/chat/browser/chatVariables';
@ -261,7 +261,9 @@ workbenchContributionsRegistry.registerWorkbenchContribution(ChatSlashStaticSlas
Registry.as<IEditorFactoryRegistry>(EditorExtensions.EditorFactory).registerEditorSerializer(ChatEditorInput.TypeID, ChatEditorInputSerializer);
registerWorkbenchContribution2(ChatExtensionPointHandler.ID, ChatExtensionPointHandler, WorkbenchPhase.BlockStartup);
registerWorkbenchContribution2(LanguageModelToolsExtensionPointHandler.ID, LanguageModelToolsExtensionPointHandler, WorkbenchPhase.BlockRestore);
registerWorkbenchContribution2(ChatCompatibilityNotifier.ID, ChatCompatibilityNotifier, WorkbenchPhase.Eventually);
// Disabled until https://github.com/microsoft/vscode/issues/218646 is fixed
// registerWorkbenchContribution2(ChatCompatibilityNotifier.ID, ChatCompatibilityNotifier, WorkbenchPhase.Eventually);
registerChatActions();
registerChatCopyActions();

View file

@ -516,7 +516,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge
onDidChangeCursorPosition();
}
private initAttachedContext(container: HTMLElement, isLayout = false) {
private initAttachedContext(container: HTMLElement) {
const oldHeight = container.offsetHeight;
dom.clearNode(container);
this.attachedContextDisposables.clear();
@ -578,7 +578,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge
this.attachedContextDisposables.add(disp);
});
if (oldHeight !== container.offsetHeight && !isLayout) {
if (oldHeight !== container.offsetHeight) {
this._onDidChangeHeight.fire();
}
}
@ -609,7 +609,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge
private previousInputEditorDimension: IDimension | undefined;
private _layout(height: number, width: number, allowRecurse = true): void {
this.initAttachedContext(this.attachedContextContainer, true);
this.initAttachedContext(this.attachedContextContainer);
const data = this.getLayoutData();

View file

@ -3,16 +3,17 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Action } from 'vs/base/common/actions';
import { coalesce, isNonEmptyArray } from 'vs/base/common/arrays';
import { Codicon } from 'vs/base/common/codicons';
import { DisposableMap, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { Disposable, DisposableMap, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import * as strings from 'vs/base/common/strings';
import { localize, localize2 } from 'vs/nls';
import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { ILogService } from 'vs/platform/log/common/log';
import { Severity } from 'vs/platform/notification/common/notification';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { Registry } from 'vs/platform/registry/common/platform';
import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
@ -20,9 +21,7 @@ import { IViewContainersRegistry, IViewDescriptor, IViewsRegistry, ViewContainer
import { CHAT_VIEW_ID } from 'vs/workbench/contrib/chat/browser/chat';
import { CHAT_SIDEBAR_PANEL_ID, ChatViewPane } from 'vs/workbench/contrib/chat/browser/chatViewPane';
import { ChatAgentLocation, IChatAgentData, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents';
import { CONTEXT_CHAT_EXTENSION_INVALID, CONTEXT_CHAT_PANEL_PARTICIPANT_REGISTERED } from 'vs/workbench/contrib/chat/common/chatContextKeys';
import { IRawChatParticipantContribution } from 'vs/workbench/contrib/chat/common/chatParticipantContribTypes';
import { showExtensionsWithIdsCommandId } from 'vs/workbench/contrib/extensions/browser/extensionsActions';
import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions';
import { isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
import * as extensionsRegistry from 'vs/workbench/services/extensions/common/extensionsRegistry';
@ -161,6 +160,35 @@ const chatParticipantExtensionPoint = extensionsRegistry.ExtensionsRegistry.regi
},
});
export class ChatCompatibilityNotifier implements IWorkbenchContribution {
static readonly ID = 'workbench.contrib.chatCompatNotifier';
constructor(
@IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService,
@INotificationService notificationService: INotificationService,
@ICommandService commandService: ICommandService
) {
// It may be better to have some generic UI for this, for any extension that is incompatible,
// but this is only enabled for Copilot Chat now and it needs to be obvious.
extensionsWorkbenchService.queryLocal().then(exts => {
const chat = exts.find(ext => ext.identifier.id === 'github.copilot-chat');
if (chat?.local?.validations.some(v => v[0] === Severity.Error)) {
notificationService.notify({
severity: Severity.Error,
message: localize('chatFailErrorMessage', "Chat failed to load. Please ensure that the GitHub Copilot Chat extension is up to date."),
actions: {
primary: [
new Action('showExtension', localize('action.showExtension', "Show Extension"), undefined, true, () => {
return commandService.executeCommand('workbench.extensions.action.showExtensionsWithIds', ['GitHub.copilot-chat']);
})
]
}
});
}
});
}
}
export class ChatExtensionPointHandler implements IWorkbenchContribution {
static readonly ID = 'workbench.contrib.chatExtensionPointHandler';
@ -170,10 +198,9 @@ export class ChatExtensionPointHandler implements IWorkbenchContribution {
constructor(
@IChatAgentService private readonly _chatAgentService: IChatAgentService,
@ILogService private readonly logService: ILogService
@ILogService private readonly logService: ILogService,
) {
this._viewContainer = this.registerViewContainer();
this.registerDefaultParticipantView();
this.handleAndRegisterChatExtensions();
}
@ -212,6 +239,11 @@ export class ChatExtensionPointHandler implements IWorkbenchContribution {
continue;
}
const store = new DisposableStore();
if (providerDescriptor.isDefault && (!providerDescriptor.locations || providerDescriptor.locations?.includes(ChatAgentLocation.Panel))) {
store.add(this.registerDefaultParticipantView(providerDescriptor));
}
const participantsAndCommandsDisambiguation: {
categoryName: string;
description: string;
@ -228,7 +260,6 @@ export class ChatExtensionPointHandler implements IWorkbenchContribution {
}
}
const store = new DisposableStore();
store.add(this._chatAgentService.registerAgent(
providerDescriptor.id,
{
@ -287,9 +318,15 @@ export class ChatExtensionPointHandler implements IWorkbenchContribution {
return viewContainer;
}
private registerDefaultParticipantView(): IDisposable {
// Register View. Name must be hardcoded because we want to show it even when the extension fails to load due to an API version incompatibility.
const name = 'GitHub Copilot';
private hasRegisteredDefaultParticipantView = false;
private registerDefaultParticipantView(defaultParticipantDescriptor: IRawChatParticipantContribution): IDisposable {
if (this.hasRegisteredDefaultParticipantView) {
this.logService.warn(`Tried to register a second default chat participant view for "${defaultParticipantDescriptor.id}"`);
return Disposable.None;
}
// Register View
const name = defaultParticipantDescriptor.fullName ?? defaultParticipantDescriptor.name;
const viewDescriptor: IViewDescriptor[] = [{
id: CHAT_VIEW_ID,
containerIcon: this._viewContainer.icon,
@ -299,11 +336,12 @@ export class ChatExtensionPointHandler implements IWorkbenchContribution {
canToggleVisibility: false,
canMoveView: true,
ctorDescriptor: new SyncDescriptor(ChatViewPane),
when: ContextKeyExpr.or(CONTEXT_CHAT_PANEL_PARTICIPANT_REGISTERED, CONTEXT_CHAT_EXTENSION_INVALID)
}];
this.hasRegisteredDefaultParticipantView = true;
Registry.as<IViewsRegistry>(ViewExtensions.ViewsRegistry).registerViews(viewDescriptor, this._viewContainer);
return toDisposable(() => {
this.hasRegisteredDefaultParticipantView = false;
Registry.as<IViewsRegistry>(ViewExtensions.ViewsRegistry).deregisterViews(viewDescriptor, this._viewContainer);
});
}
@ -312,39 +350,3 @@ export class ChatExtensionPointHandler implements IWorkbenchContribution {
function getParticipantKey(extensionId: ExtensionIdentifier, participantName: string): string {
return `${extensionId.value}_${participantName}`;
}
export class ChatCompatibilityNotifier implements IWorkbenchContribution {
static readonly ID = 'workbench.contrib.chatCompatNotifier';
constructor(
@IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService,
@IContextKeyService contextKeyService: IContextKeyService,
@IChatAgentService chatAgentService: IChatAgentService,
) {
// It may be better to have some generic UI for this, for any extension that is incompatible,
// but this is only enabled for Copilot Chat now and it needs to be obvious.
const showExtensionLabel = localize('showExtension', "Show Extension");
const viewsRegistry = Registry.as<IViewsRegistry>(ViewExtensions.ViewsRegistry);
viewsRegistry.registerViewWelcomeContent(CHAT_VIEW_ID, {
content: localize('chatFailErrorMessage', "Chat failed to load. Please ensure that the GitHub Copilot Chat extension is up to date.") + `\n\n[${showExtensionLabel}](command:${showExtensionsWithIdsCommandId}?${encodeURIComponent(JSON.stringify([['GitHub.copilot-chat']]))})`,
when: CONTEXT_CHAT_EXTENSION_INVALID,
});
const isInvalid = CONTEXT_CHAT_EXTENSION_INVALID.bindTo(contextKeyService);
extensionsWorkbenchService.queryLocal().then(exts => {
const chat = exts.find(ext => ext.identifier.id === 'github.copilot-chat');
if (chat?.local?.validations.some(v => v[0] === Severity.Error)) {
// This catches vscode starting up with the invalid extension, but the extension may still get updated by vscode after this.
isInvalid.set(true);
}
});
const listener = chatAgentService.onDidChangeAgents(() => {
if (chatAgentService.getDefaultAgent(ChatAgentLocation.Panel)) {
isInvalid.set(false);
listener.dispose();
}
});
}
}

View file

@ -88,9 +88,8 @@ export class ChatViewPane extends ViewPane {
} else if (this._widget?.viewModel?.initState === ChatModelInitState.Initialized) {
// Model is initialized, and the default agent disappeared, so show welcome view
this.didUnregisterProvider = true;
this._onDidChangeViewWelcomeState.fire();
}
this._onDidChangeViewWelcomeState.fire();
}));
}
@ -115,10 +114,6 @@ export class ChatViewPane extends ViewPane {
}
override shouldShowWelcome(): boolean {
if (!this.chatAgentService.getContributedDefaultAgent(ChatAgentLocation.Panel)) {
return true;
}
const noPersistedSessions = !this.chatService.hasSessions();
return this.didUnregisterProvider || !this._widget?.viewModel && (noPersistedSessions || this.didProviderRegistrationFail);
}

View file

@ -770,11 +770,15 @@ export class CodeCompareBlockPart extends Disposable {
});
dom.reset(this.messageElement, message);
}
const diffData = await data.diffData;
if (!diffData) {
return;
}
if (!isEditApplied && diffData) {
if (!isEditApplied) {
const viewModel = this.diffEditor.createViewModel({
original: diffData.original,
modified: diffData.modified
@ -797,7 +801,6 @@ export class CodeCompareBlockPart extends Disposable {
} else {
this.diffEditor.setModel(null);
this._lastDiffEditorViewModel.value = undefined;
this._onDidChangeContentHeight.fire();
}
this.toolbar.context = {

View file

@ -24,7 +24,7 @@ import { ILogService } from 'vs/platform/log/common/log';
import { IProductService } from 'vs/platform/product/common/productService';
import { asJson, IRequestService } from 'vs/platform/request/common/request';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { CONTEXT_CHAT_ENABLED, CONTEXT_CHAT_PANEL_PARTICIPANT_REGISTERED } from 'vs/workbench/contrib/chat/common/chatContextKeys';
import { CONTEXT_CHAT_ENABLED } from 'vs/workbench/contrib/chat/common/chatContextKeys';
import { IChatProgressResponseContent, IChatRequestVariableData, ISerializableChatAgentData } from 'vs/workbench/contrib/chat/common/chatModel';
import { IRawChatCommandContribution, RawChatParticipantLocation } from 'vs/workbench/contrib/chat/common/chatParticipantContribTypes';
import { IChatFollowup, IChatLocationData, IChatProgress, IChatResponseErrorDetails, IChatTaskDto } from 'vs/workbench/contrib/chat/common/chatService';
@ -233,13 +233,11 @@ export class ChatAgentService implements IChatAgentService {
readonly onDidChangeAgents: Event<IChatAgent | undefined> = this._onDidChangeAgents.event;
private readonly _hasDefaultAgent: IContextKey<boolean>;
private readonly _defaultAgentRegistered: IContextKey<boolean>;
constructor(
@IContextKeyService private readonly contextKeyService: IContextKeyService,
) {
this._hasDefaultAgent = CONTEXT_CHAT_ENABLED.bindTo(this.contextKeyService);
this._defaultAgentRegistered = CONTEXT_CHAT_PANEL_PARTICIPANT_REGISTERED.bindTo(this.contextKeyService);
}
registerAgent(id: string, data: IChatAgentData): IDisposable {
@ -248,10 +246,6 @@ export class ChatAgentService implements IChatAgentService {
throw new Error(`Agent already registered: ${JSON.stringify(id)}`);
}
if (data.isDefault) {
this._defaultAgentRegistered.set(true);
}
const that = this;
const commands = data.slashCommands;
data = {
@ -262,13 +256,8 @@ export class ChatAgentService implements IChatAgentService {
};
const entry = { data };
this._agents.set(id, entry);
this._onDidChangeAgents.fire(undefined);
return toDisposable(() => {
this._agents.delete(id);
if (data.isDefault) {
this._defaultAgentRegistered.set(false);
}
this._onDidChangeAgents.fire(undefined);
});
}
@ -456,7 +445,7 @@ export class ChatAgentService implements IChatAgentService {
const participants = this.getAgents().reduce<IChatParticipantMetadata[]>((acc, a) => {
acc.push({ participant: a.id, disambiguation: a.disambiguation ?? [] });
for (const command of a.slashCommands) {
acc.push({ participant: a.id, command: command.name, disambiguation: command.disambiguation ?? [] });
acc.push({ participant: a.id, command: command.name, disambiguation: [] });
}
return acc;
}, []);

View file

@ -25,9 +25,7 @@ export const CONTEXT_CHAT_INPUT_HAS_FOCUS = new RawContextKey<boolean>('chatInpu
export const CONTEXT_IN_CHAT_INPUT = new RawContextKey<boolean>('inChatInput', false, { type: 'boolean', description: localize('inInteractiveInput', "True when focus is in the chat input, false otherwise.") });
export const CONTEXT_IN_CHAT_SESSION = new RawContextKey<boolean>('inChat', false, { type: 'boolean', description: localize('inChat', "True when focus is in the chat widget, false otherwise.") });
export const CONTEXT_CHAT_ENABLED = new RawContextKey<boolean>('chatIsEnabled', false, { type: 'boolean', description: localize('chatIsEnabled', "True when chat is enabled because a default chat participant is activated with an implementation.") });
export const CONTEXT_CHAT_PANEL_PARTICIPANT_REGISTERED = new RawContextKey<boolean>('chatPanelParticipantRegistered', false, { type: 'boolean', description: localize('chatParticipantRegistered', "True when a default chat participant is registered for the panel.") });
export const CONTEXT_CHAT_EXTENSION_INVALID = new RawContextKey<boolean>('chatExtensionInvalid', false, { type: 'boolean', description: localize('chatExtensionInvalid', "True when the installed chat extension is invalid and needs to be updated.") });
export const CONTEXT_CHAT_ENABLED = new RawContextKey<boolean>('chatIsEnabled', false, { type: 'boolean', description: localize('chatIsEnabled', "True when chat is enabled because a default chat participant is registered.") });
export const CONTEXT_CHAT_INPUT_CURSOR_AT_TOP = new RawContextKey<boolean>('chatCursorAtTop', false);
export const CONTEXT_CHAT_INPUT_HAS_AGENT = new RawContextKey<boolean>('chatInputHasAgent', false);
export const CONTEXT_CHAT_LOCATION = new RawContextKey<ChatAgentLocation>('chatLocation', undefined);

View file

@ -616,8 +616,6 @@ export type ISerializableChatDataIn = ISerializableChatData1 | ISerializableChat
* TODO- ChatModel#_deserialize and reviveSerializedAgent also still do some normalization and maybe that should be done in here too.
*/
export function normalizeSerializableChatData(raw: ISerializableChatDataIn): ISerializableChatData {
normalizeOldFields(raw);
if (!('version' in raw)) {
return {
version: 3,
@ -638,30 +636,6 @@ export function normalizeSerializableChatData(raw: ISerializableChatDataIn): ISe
return raw;
}
function normalizeOldFields(raw: ISerializableChatDataIn): void {
// Fill in fields that very old chat data may be missing
if (!raw.sessionId) {
raw.sessionId = generateUuid();
}
if (!raw.creationDate) {
raw.creationDate = getLastYearDate();
}
if ('version' in raw && (raw.version === 2 || raw.version === 3)) {
if (!raw.lastMessageDate) {
// A bug led to not porting creationDate properly, and that was copied to lastMessageDate, so fix that up if missing.
raw.lastMessageDate = getLastYearDate();
}
}
}
function getLastYearDate(): number {
const lastYearDate = new Date();
lastYearDate.setFullYear(lastYearDate.getFullYear() - 1);
return lastYearDate.getTime();
}
export function isExportableSessionData(obj: unknown): obj is IExportableChatData {
const data = obj as IExportableChatData;
return typeof data === 'object' &&

View file

@ -481,8 +481,9 @@ export class ChatService extends Disposable implements IChatService {
}
async sendRequest(sessionId: string, request: string, options?: IChatSendRequestOptions): Promise<IChatSendRequestData | undefined> {
this.trace('sendRequest', `sessionId: ${sessionId}, message: ${request.substring(0, 20)}${request.length > 20 ? '[...]' : ''}}`);
if (!request.trim() && !options?.slashCommand && !options?.agentId) {
if (!request.trim()) {
this.trace('sendRequest', 'Rejected empty message');
return;
}
@ -545,7 +546,6 @@ export class ChatService extends Disposable implements IChatService {
const agentPart = 'kind' in parsedRequest ? undefined : parsedRequest.parts.find((r): r is ChatRequestAgentPart => r instanceof ChatRequestAgentPart);
const agentSlashCommandPart = 'kind' in parsedRequest ? undefined : parsedRequest.parts.find((r): r is ChatRequestAgentSubcommandPart => r instanceof ChatRequestAgentSubcommandPart);
const commandPart = 'kind' in parsedRequest ? undefined : parsedRequest.parts.find((r): r is ChatRequestSlashCommandPart => r instanceof ChatRequestSlashCommandPart);
const requests = [...model.getRequests()];
let gotProgress = false;
const requestType = commandPart ? 'slashCommand' : 'string';
@ -639,7 +639,7 @@ export class ChatService extends Disposable implements IChatService {
if (this.configurationService.getValue('chat.experimental.detectParticipant.enabled') !== false && this.chatAgentService.hasChatParticipantDetectionProviders() && !agentPart && !commandPart && enableCommandDetection) {
// We have no agent or command to scope history with, pass the full history to the participant detection provider
const defaultAgentHistory = this.getHistoryEntriesFromModel(requests, model.sessionId, location, defaultAgent.id);
const defaultAgentHistory = this.getHistoryEntriesFromModel(model, location, defaultAgent.id);
// Prepare the request object that we will send to the participant detection provider
const chatAgentRequest = await prepareChatAgentRequest(defaultAgent, agentSlashCommandPart?.command, enableCommandDetection, undefined, false);
@ -658,7 +658,7 @@ export class ChatService extends Disposable implements IChatService {
await this.extensionService.activateByEvent(`onChatParticipant:${agent.id}`);
// Recompute history in case the agent or command changed
const history = this.getHistoryEntriesFromModel(requests, model.sessionId, location, agent.id);
const history = this.getHistoryEntriesFromModel(model, location, agent.id);
const requestProps = await prepareChatAgentRequest(agent, command, enableCommandDetection, request /* Reuse the request object if we already created it for participant detection */, !!detectedAgent);
const pendingRequest = this._pendingRequests.get(sessionId);
if (pendingRequest && !pendingRequest.requestId) {
@ -668,7 +668,7 @@ export class ChatService extends Disposable implements IChatService {
const agentResult = await this.chatAgentService.invokeAgent(agent.id, requestProps, progressCallback, history, token);
rawResult = agentResult;
agentOrCommandFollowups = this.chatAgentService.getFollowups(agent.id, requestProps, agentResult, history, followupsCancelToken);
chatTitlePromise = model.getRequests().length === 1 && !model.customTitle ? this.chatAgentService.getChatTitle(defaultAgent.id, this.getHistoryEntriesFromModel(model.getRequests(), model.sessionId, location, agent.id), CancellationToken.None) : undefined;
chatTitlePromise = model.getRequests().length === 1 && !model.customTitle ? this.chatAgentService.getChatTitle(defaultAgent.id, this.getHistoryEntriesFromModel(model, location, agent.id), CancellationToken.None) : undefined;
} else if (commandPart && this.chatSlashCommandService.hasCommand(commandPart.slashCommand.command)) {
request = model.addRequest(parsedRequest, { variables: [] }, attempt);
completeResponseCreated();
@ -775,9 +775,9 @@ export class ChatService extends Disposable implements IChatService {
};
}
private getHistoryEntriesFromModel(requests: IChatRequestModel[], sessionId: string, location: ChatAgentLocation, forAgentId: string): IChatAgentHistoryEntry[] {
private getHistoryEntriesFromModel(model: IChatModel, location: ChatAgentLocation, forAgentId: string): IChatAgentHistoryEntry[] {
const history: IChatAgentHistoryEntry[] = [];
for (const request of requests) {
for (const request of model.getRequests()) {
if (!request.response) {
continue;
}
@ -791,7 +791,7 @@ export class ChatService extends Disposable implements IChatService {
const promptTextResult = getPromptText(request.message);
const historyRequest: IChatAgentRequest = {
sessionId: sessionId,
sessionId: model.sessionId,
requestId: request.id,
agentId: request.response.agent?.id ?? '',
message: promptTextResult.message,

View file

@ -941,6 +941,12 @@ export class StopReadAloud extends Action2 {
when: ScopedChatSynthesisInProgress,
group: 'navigation',
order: -1
},
{
id: MENU_INLINE_CHAT_WIDGET_SECONDARY,
when: ScopedChatSynthesisInProgress,
group: 'navigation',
order: -1
}
]
});
@ -974,15 +980,6 @@ export class StopReadChatItemAloud extends Action2 {
CONTEXT_RESPONSE_FILTERED.negate() // but not when response is filtered
),
group: 'navigation'
},
{
id: MENU_INLINE_CHAT_WIDGET_SECONDARY,
when: ContextKeyExpr.and(
ScopedChatSynthesisInProgress, // only when in progress
CONTEXT_RESPONSE, // only for responses
CONTEXT_RESPONSE_FILTERED.negate() // but not when response is filtered
),
group: 'navigation'
}
]
});

View file

@ -17,7 +17,7 @@ import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKe
import { ILogService, NullLogService } from 'vs/platform/log/common/log';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { ChatAgentLocation, ChatAgentService, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents';
import { ChatModel, ISerializableChatData1, ISerializableChatData2, ISerializableChatData3, normalizeSerializableChatData, Response } from 'vs/workbench/contrib/chat/common/chatModel';
import { ChatModel, ISerializableChatData1, ISerializableChatData2, normalizeSerializableChatData, Response } from 'vs/workbench/contrib/chat/common/chatModel';
import { ChatRequestTextPart } from 'vs/workbench/contrib/chat/common/chatParserTypes';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { TestExtensionService, TestStorageService } from 'vs/workbench/test/common/workbenchTestServices';
@ -230,53 +230,4 @@ suite('normalizeSerializableChatData', () => {
assert.strictEqual(newData.lastMessageDate, v2Data.lastMessageDate);
assert.strictEqual(newData.customTitle, v2Data.computedTitle);
});
test('old bad data', () => {
const v1Data: ISerializableChatData1 = {
// Testing the scenario where these are missing
sessionId: undefined!,
creationDate: undefined!,
initialLocation: undefined,
isImported: false,
requesterAvatarIconUri: undefined,
requesterUsername: 'me',
requests: [],
responderAvatarIconUri: undefined,
responderUsername: 'bot',
welcomeMessage: []
};
const newData = normalizeSerializableChatData(v1Data);
assert.strictEqual(newData.version, 3);
assert.ok(newData.creationDate > 0);
assert.ok(newData.lastMessageDate > 0);
assert.ok(newData.sessionId);
});
test('v3 with bug', () => {
const v3Data: ISerializableChatData3 = {
// Test case where old data was wrongly normalized and these fields were missing
creationDate: undefined!,
lastMessageDate: undefined!,
version: 3,
initialLocation: undefined,
isImported: false,
requesterAvatarIconUri: undefined,
requesterUsername: 'me',
requests: [],
responderAvatarIconUri: undefined,
responderUsername: 'bot',
sessionId: 'session1',
welcomeMessage: [],
customTitle: 'computed title'
};
const newData = normalizeSerializableChatData(v3Data);
assert.strictEqual(newData.version, 3);
assert.ok(newData.creationDate > 0);
assert.ok(newData.lastMessageDate > 0);
assert.ok(newData.sessionId);
});
});

View file

@ -172,7 +172,7 @@ export class CommentsPanel extends FilterViewPane implements ICommentsView {
this.filters = this._register(new CommentsFilters({
showResolved: this.viewState['showResolved'] !== false,
showUnresolved: this.viewState['showUnresolved'] !== false,
sortBy: this.viewState['sortBy'] ?? CommentsSortOrder.ResourceAscending,
sortBy: this.viewState['sortBy'],
}, this.contextKeyService));
this.filter = new Filter(new FilterOptions(this.filterWidget.getFilterText(), this.filters.showResolved, this.filters.showUnresolved));

View file

@ -7,7 +7,7 @@ import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { Disposable } from 'vs/base/common/lifecycle';
import { localize } from 'vs/nls';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { ContextKeyExpr, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { Event, Emitter } from 'vs/base/common/event';
import { CommentsViewFilterFocusContextKey, ICommentsView } from 'vs/workbench/contrib/comments/browser/comments';
import { MenuId, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions';
@ -74,9 +74,9 @@ export class CommentsFilters extends Disposable {
}
}
private _sortBy: IContextKey<CommentsSortOrder> = CONTEXT_KEY_SORT_BY.bindTo(this.contextKeyService);
private _sortBy = CONTEXT_KEY_SORT_BY.bindTo(this.contextKeyService);
get sortBy(): CommentsSortOrder {
return this._sortBy.get() ?? CommentsSortOrder.ResourceAscending;
return this._sortBy.get()!;
}
set sortBy(sortBy: CommentsSortOrder) {
if (this._sortBy.get() !== sortBy) {
@ -208,7 +208,7 @@ registerAction2(class extends ViewAction<ICommentsView> {
icon: Codicon.history,
viewId: COMMENTS_VIEW_ID,
toggled: {
condition: ContextKeyExpr.equals(CONTEXT_KEY_SORT_BY.key, CommentsSortOrder.UpdatedAtDescending),
condition: ContextKeyExpr.equals('commentsView.sortBy', CommentsSortOrder.UpdatedAtDescending),
title: localize('sorting by updated at', "Updated Time"),
},
menu: {
@ -229,13 +229,13 @@ registerAction2(class extends ViewAction<ICommentsView> {
constructor() {
super({
id: `workbench.actions.${COMMENTS_VIEW_ID}.toggleSortByResource`,
title: localize('toggle sorting by resource', "Position in File"),
title: localize('toggle sorting by resource', "File"),
category: localize('comments', "Comments"),
icon: Codicon.history,
viewId: COMMENTS_VIEW_ID,
toggled: {
condition: ContextKeyExpr.equals(CONTEXT_KEY_SORT_BY.key, CommentsSortOrder.ResourceAscending),
title: localize('sorting by position in file', "Position in File"),
condition: ContextKeyExpr.equals('commentsView.sortBy', CommentsSortOrder.ResourceAscending),
title: localize('sorting by file', "File"),
},
menu: {
id: commentSortSubmenu,

View file

@ -12,24 +12,20 @@ import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cance
import { Codicon } from 'vs/base/common/codicons';
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { autorun, autorunWithStore, derived, IObservable, ISettableObservable, observableValue, transaction } from 'vs/base/common/observable';
import { autorun, autorunWithStore, derived, IObservable, ISettableObservable, observableValue } from 'vs/base/common/observable';
import { ThemeIcon } from 'vs/base/common/themables';
import { Constants } from 'vs/base/common/uint';
import { URI } from 'vs/base/common/uri';
import { generateUuid } from 'vs/base/common/uuid';
import 'vs/css!./media/callStackWidget';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EditorContributionCtor, EditorContributionInstantiation, IEditorContributionDescription } from 'vs/editor/browser/editorExtensions';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget';
import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget';
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { IWordAtPosition } from 'vs/editor/common/core/wordHelper';
import { IEditorContribution, IEditorDecorationsCollection } from 'vs/editor/common/editorCommon';
import { Location } from 'vs/editor/common/languages';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { ClickLinkGesture, ClickLinkMouseEvent } from 'vs/editor/contrib/gotoSymbol/browser/link/clickLinkGesture';
import { localize, localize2 } from 'vs/nls';
import { createActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem';
import { MenuWorkbenchToolBar } from 'vs/platform/actions/browser/toolbar';
@ -42,7 +38,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
import { defaultButtonStyles } from 'vs/platform/theme/browser/defaultStyles';
import { ResourceLabel } from 'vs/workbench/browser/labels';
import { makeStackFrameColumnDecoration, TOP_STACK_FRAME_DECORATION } from 'vs/workbench/contrib/debug/browser/callStackEditorContribution';
import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
export class CallStackFrame {
@ -101,9 +97,6 @@ class WrappedCustomStackFrame implements IFrameLikeItem {
constructor(public readonly original: CustomStackFrame) { }
}
const isFrameLike = (item: unknown): item is IFrameLikeItem =>
item instanceof WrappedCallStackFrame || item instanceof WrappedCustomStackFrame;
type ListItem = WrappedCallStackFrame | SkippedCallFrames | WrappedCustomStackFrame;
const WIDGET_CLASS_NAME = 'multiCallStackWidget';
@ -144,7 +137,6 @@ export class CallStackWidget extends Disposable {
multipleSelectionSupport: false,
mouseSupport: false,
keyboardSupport: false,
setRowLineHeight: false,
accessibilityProvider: instantiationService.createInstance(StackAccessibilityProvider),
}
) as WorkbenchList<ListItem>);
@ -165,17 +157,6 @@ export class CallStackWidget extends Disposable {
this.layoutEmitter.fire();
}
public collapseAll() {
transaction(tx => {
for (let i = 0; i < this.list.length; i++) {
const frame = this.list.element(i);
if (isFrameLike(frame)) {
frame.collapsed.set(true, tx);
}
}
});
}
private async loadFrame(replacing: SkippedCallFrames): Promise<void> {
if (!this.cts) {
return;
@ -375,9 +356,9 @@ abstract class AbstractFrameRenderer<T extends IAbstractFrameRendererTemplateDat
collapse.element.ariaExpanded = String(!collapsed);
elements.root.classList.toggle('collapsed', collapsed);
}));
const toggleCollapse = () => item.collapsed.set(!item.collapsed.get(), undefined);
elementStore.add(collapse.onDidClick(toggleCollapse));
elementStore.add(dom.addDisposableListener(elements.title, 'click', toggleCollapse));
elementStore.add(collapse.onDidClick(() => {
item.collapsed.set(!item.collapsed.get(), undefined);
}));
}
disposeElement(element: ListItem, index: number, templateData: T, height: number | undefined): void {
@ -401,33 +382,26 @@ class FrameCodeRenderer extends AbstractFrameRenderer<IStackTemplateData> {
private readonly containingEditor: ICodeEditor | undefined,
private readonly onLayout: Event<void>,
@ITextModelService private readonly modelService: ITextModelService,
@ICodeEditorService private readonly editorService: ICodeEditorService,
@IInstantiationService instantiationService: IInstantiationService,
) {
super(instantiationService);
}
protected override finishRenderTemplate(data: IAbstractFrameRendererTemplateData): IStackTemplateData {
// override default e.g. language contributions, only allow users to click
// on code in the call stack to go to its source location
const contributions: IEditorContributionDescription[] = [{
id: ClickToLocationContribution.ID,
instantiation: EditorContributionInstantiation.BeforeFirstInteraction,
ctor: ClickToLocationContribution as EditorContributionCtor,
}];
const editor = this.containingEditor
? this.instantiationService.createInstance(
EmbeddedCodeEditorWidget,
data.elements.editor,
editorOptions,
{ isSimpleWidget: true, contributions },
{ isSimpleWidget: true },
this.containingEditor,
)
: this.instantiationService.createInstance(
CodeEditorWidget,
data.elements.editor,
editorOptions,
{ isSimpleWidget: true, contributions },
{ isSimpleWidget: true },
);
data.templateStore.add(editor);
@ -449,6 +423,20 @@ class FrameCodeRenderer extends AbstractFrameRenderer<IStackTemplateData> {
const uri = item.source!;
template.label.element.setFile(uri);
template.elements.title.role = 'link';
elementStore.add(dom.addDisposableListener(template.elements.title, 'click', e => {
this.editorService.openCodeEditor({
resource: uri,
options: {
selection: Range.fromPositions({
column: item.column ?? 1,
lineNumber: item.line ?? 1,
}),
selectionRevealType: TextEditorSelectionRevealType.CenterIfOutsideViewport,
},
}, this.containingEditor || null, e.ctrlKey || e.metaKey);
}));
const cts = new CancellationTokenSource();
elementStore.add(toDisposable(() => cts.dispose(true)));
this.modelService.createModelReference(uri).then(reference => {
@ -644,73 +632,6 @@ class SkippedRenderer implements IListRenderer<ListItem, ISkippedTemplateData> {
}
}
/** A simple contribution that makes all data in the editor clickable to go to the location */
class ClickToLocationContribution extends Disposable implements IEditorContribution {
public static readonly ID = 'clickToLocation';
private readonly linkDecorations: IEditorDecorationsCollection;
private current: { line: number; word: IWordAtPosition } | undefined;
constructor(
private readonly editor: ICodeEditor,
@IEditorService editorService: IEditorService,
) {
super();
this.linkDecorations = editor.createDecorationsCollection();
this._register(toDisposable(() => this.linkDecorations.clear()));
const clickLinkGesture = this._register(new ClickLinkGesture(editor));
this._register(clickLinkGesture.onMouseMoveOrRelevantKeyDown(([mouseEvent, keyboardEvent]) => {
this.onMove(mouseEvent);
}));
this._register(clickLinkGesture.onExecute((e) => {
const model = this.editor.getModel();
if (!this.current || !model) {
return;
}
editorService.openEditor({
resource: model.uri,
options: {
selection: Range.fromPositions(new Position(this.current.line, this.current.word.startColumn)),
selectionRevealType: TextEditorSelectionRevealType.CenterIfOutsideViewport,
},
}, e.hasSideBySideModifier ? SIDE_GROUP : undefined);
}));
}
private onMove(mouseEvent: ClickLinkMouseEvent) {
if (!mouseEvent.hasTriggerModifier) {
return this.clear();
}
const position = mouseEvent.target.position;
const word = position && this.editor.getModel()?.getWordAtPosition(position);
if (!word) {
return this.clear();
}
const prev = this.current?.word;
if (prev && prev.startColumn === word.startColumn && prev.endColumn === word.endColumn && prev.word === word.word) {
return;
}
this.current = { word, line: position.lineNumber };
this.linkDecorations.set([{
range: new Range(position.lineNumber, word.startColumn, position.lineNumber, word.endColumn),
options: {
description: 'call-stack-go-to-file-link',
inlineClassName: 'call-stack-go-to-file-link',
},
}]);
}
private clear() {
this.linkDecorations.clear();
this.current = undefined;
}
}
registerAction2(class extends Action2 {
constructor() {
super({

View file

@ -24,10 +24,6 @@
&[role="link"] {
cursor: pointer;
}
.monaco-icon-label::before {
height: auto;
}
}
&.collapsed {
@ -43,7 +39,6 @@
.collapse-button {
width: 16px;
min-height: 1px; /* show even if empty */
line-height: 0;
a {
cursor: pointer;
@ -61,11 +56,6 @@
.multiCallStackWidget {
.multiCallStackFrameContainer {
background: none !important;
line-height: inherit !important;
}
}
.monaco-editor .call-stack-go-to-file-link {
text-decoration: underline;
cursor: pointer;
color: var(--vscode-editorLink-activeForeground) !important;
}

View file

@ -79,6 +79,7 @@ class ExtensionsViewState extends Disposable implements IExtensionsViewState {
}
}
export interface ExtensionsListViewOptions {
server?: IExtensionManagementServer;
flexibleHeight?: boolean;

View file

@ -11,7 +11,7 @@ import { EmbeddedDiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/em
import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { InlineChatController, InlineChatRunOptions } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController';
import { ACTION_ACCEPT_CHANGES, CTX_INLINE_CHAT_HAS_AGENT, CTX_INLINE_CHAT_HAS_STASHED_SESSION, CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_INNER_CURSOR_FIRST, CTX_INLINE_CHAT_INNER_CURSOR_LAST, CTX_INLINE_CHAT_VISIBLE, CTX_INLINE_CHAT_OUTER_CURSOR_POSITION, CTX_INLINE_CHAT_USER_DID_EDIT, CTX_INLINE_CHAT_DOCUMENT_CHANGED, CTX_INLINE_CHAT_EDIT_MODE, EditMode, MENU_INLINE_CHAT_WIDGET_STATUS, CTX_INLINE_CHAT_REQUEST_IN_PROGRESS, CTX_INLINE_CHAT_RESPONSE_TYPE, InlineChatResponseType, ACTION_REGENERATE_RESPONSE, MENU_INLINE_CHAT_CONTENT_STATUS, ACTION_VIEW_IN_CHAT, ACTION_TOGGLE_DIFF, CTX_INLINE_CHAT_CHANGE_HAS_DIFF, CTX_INLINE_CHAT_CHANGE_SHOWS_DIFF, MENU_INLINE_CHAT_ZONE, ACTION_DISCARD_CHANGES } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
import { ACTION_ACCEPT_CHANGES, CTX_INLINE_CHAT_HAS_AGENT, CTX_INLINE_CHAT_HAS_STASHED_SESSION, CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_INNER_CURSOR_FIRST, CTX_INLINE_CHAT_INNER_CURSOR_LAST, CTX_INLINE_CHAT_VISIBLE, CTX_INLINE_CHAT_OUTER_CURSOR_POSITION, CTX_INLINE_CHAT_USER_DID_EDIT, CTX_INLINE_CHAT_DOCUMENT_CHANGED, CTX_INLINE_CHAT_EDIT_MODE, EditMode, MENU_INLINE_CHAT_WIDGET_STATUS, CTX_INLINE_CHAT_REQUEST_IN_PROGRESS, CTX_INLINE_CHAT_RESPONSE_TYPE, InlineChatResponseType, ACTION_REGENERATE_RESPONSE, MENU_INLINE_CHAT_CONTENT_STATUS, ACTION_VIEW_IN_CHAT, ACTION_TOGGLE_DIFF, CTX_INLINE_CHAT_CHANGE_HAS_DIFF, CTX_INLINE_CHAT_CHANGE_SHOWS_DIFF, MENU_INLINE_CHAT_ZONE } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
import { localize, localize2 } from 'vs/nls';
import { Action2, IAction2Options } from 'vs/platform/actions/common/actions';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
@ -287,7 +287,7 @@ export class DiscardHunkAction extends AbstractInlineChatAction {
constructor() {
super({
id: ACTION_DISCARD_CHANGES,
id: 'inlineChat.discardHunkChange',
title: localize('discard', 'Discard'),
icon: Codicon.chromeClose,
precondition: CTX_INLINE_CHAT_VISIBLE,

View file

@ -39,7 +39,7 @@ import { ChatAgentLocation } from 'vs/workbench/contrib/chat/common/chatAgents';
import { ChatModel, ChatRequestRemovalReason, IChatRequestModel, IChatTextEditGroup, IChatTextEditGroupState, IResponse } from 'vs/workbench/contrib/chat/common/chatModel';
import { IChatService } from 'vs/workbench/contrib/chat/common/chatService';
import { InlineChatContentWidget } from 'vs/workbench/contrib/inlineChat/browser/inlineChatContentWidget';
import { HunkInformation, HunkState, Session, StashedSession } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession';
import { HunkInformation, Session, StashedSession } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession';
import { InlineChatError } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl';
import { EditModeStrategy, HunkAction, IEditObserver, LiveStrategy, PreviewStrategy, ProgressingEditsOptions } from 'vs/workbench/contrib/inlineChat/browser/inlineChatStrategies';
import { CTX_INLINE_CHAT_EDITING, CTX_INLINE_CHAT_REQUEST_IN_PROGRESS, CTX_INLINE_CHAT_RESPONSE_TYPE, CTX_INLINE_CHAT_USER_DID_EDIT, CTX_INLINE_CHAT_VISIBLE, EditMode, INLINE_CHAT_ID, InlineChatConfigKeys, InlineChatResponseType } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
@ -622,7 +622,6 @@ export class InlineChatController implements IEditorContribution {
return;
}
if (e.kind === 'move') {
assertType(this._session);
const log: typeof this._log = (msg: string, ...args: any[]) => this._log('state=_showRequest) moving inline chat', msg, ...args);
log('move was requested', e.target, e.range);
@ -637,13 +636,13 @@ export class InlineChatController implements IEditorContribution {
}
const newEditor = editorPane.getControl();
if (!isCodeEditor(newEditor) || !newEditor.hasModel()) {
if (!newEditor || !isCodeEditor(newEditor) || !newEditor.hasModel()) {
log('new editor is either missing or not a code editor or does not have a model');
return;
}
if (this._inlineChatSessionService.getSession(newEditor, e.target)) {
log('new editor ALREADY has a session');
if (!this._session) {
log('controller does not have a session');
return;
}
@ -742,7 +741,7 @@ export class InlineChatController implements IEditorContribution {
await responsePromise.p;
await progressiveEditsQueue.whenIdle();
if (response.result?.errorDetails) {
if (response.isCanceled) {
await this._session.undoChangesUntil(response.requestId);
}
@ -759,6 +758,7 @@ export class InlineChatController implements IEditorContribution {
if (response.result?.errorDetails) {
//
await this._session.undoChangesUntil(response.requestId);
} else if (response.response.value.length === 0) {
// empty -> show message
@ -990,21 +990,8 @@ export class InlineChatController implements IEditorContribution {
// ---- controller API
showSaveHint(): void {
if (!this._session) {
return;
}
const status = localize('savehint', "Accept or discard changes to continue saving.");
const status = localize('savehint', "Accept or discard changes to continue saving");
this._ui.value.zone.widget.updateStatus(status, { classes: ['warn'] });
if (this._ui.value.zone.position) {
this._editor.revealLineInCenterIfOutsideViewport(this._ui.value.zone.position.lineNumber);
} else {
const hunk = this._session.hunkData.getInfo().find(info => info.getState() === HunkState.Pending);
if (hunk) {
this._editor.revealLineInCenterIfOutsideViewport(hunk.getRangesN()[0].startLineNumber);
}
}
}
acceptInput() {

View file

@ -263,7 +263,7 @@ export class StashedSession {
// keep session for a little bit, only release when user continues to work (type, move cursor, etc.)
this._session = session;
this._ctxHasStashedSession.set(true);
this._listener = Event.once(Event.any(editor.onDidChangeCursorSelection, editor.onDidChangeModelContent, editor.onDidChangeModel, editor.onDidBlurEditorWidget))(() => {
this._listener = Event.once(Event.any(editor.onDidChangeCursorSelection, editor.onDidChangeModelContent, editor.onDidChangeModel))(() => {
this._session = undefined;
this._sessionService.releaseSession(session);
this._ctxHasStashedSession.reset();
@ -360,30 +360,27 @@ export class HunkData {
// mirror textModelN changes to textModel0 execept for those that
// overlap with a hunk
type HunkRangePair = { rangeN: Range; range0: Range; markAccepted: () => void };
type HunkRangePair = { rangeN: Range; range0: Range };
const hunkRanges: HunkRangePair[] = [];
const ranges0: Range[] = [];
for (const entry of this._data.values()) {
for (const { textModelNDecorations, textModel0Decorations, state } of this._data.values()) {
if (entry.state === HunkState.Pending) {
if (state === HunkState.Pending) {
// pending means the hunk's changes aren't "sync'd" yet
for (let i = 1; i < entry.textModelNDecorations.length; i++) {
const rangeN = this._textModelN.getDecorationRange(entry.textModelNDecorations[i]);
const range0 = this._textModel0.getDecorationRange(entry.textModel0Decorations[i]);
for (let i = 1; i < textModelNDecorations.length; i++) {
const rangeN = this._textModelN.getDecorationRange(textModelNDecorations[i]);
const range0 = this._textModel0.getDecorationRange(textModel0Decorations[i]);
if (rangeN && range0) {
hunkRanges.push({
rangeN, range0,
markAccepted: () => entry.state = HunkState.Accepted
});
hunkRanges.push({ rangeN, range0 });
}
}
} else if (entry.state === HunkState.Accepted) {
} else if (state === HunkState.Accepted) {
// accepted means the hunk's changes are also in textModel0
for (let i = 1; i < entry.textModel0Decorations.length; i++) {
const range = this._textModel0.getDecorationRange(entry.textModel0Decorations[i]);
for (let i = 1; i < textModel0Decorations.length; i++) {
const range = this._textModel0.getDecorationRange(textModel0Decorations[i]);
if (range) {
ranges0.push(range);
}
@ -402,20 +399,16 @@ export class HunkData {
let pendingChangesLen = 0;
for (const entry of hunkRanges) {
if (entry.rangeN.getEndPosition().isBefore(Range.getStartPosition(change.range))) {
for (const { rangeN, range0 } of hunkRanges) {
if (rangeN.getEndPosition().isBefore(Range.getStartPosition(change.range))) {
// pending hunk _before_ this change. When projecting into textModel0 we need to
// subtract that. Because diffing is relaxed it might include changes that are not
// actual insertions/deletions. Therefore we need to take the length of the original
// range into account.
pendingChangesLen += this._textModelN.getValueLengthInRange(entry.rangeN);
pendingChangesLen -= this._textModel0.getValueLengthInRange(entry.range0);
pendingChangesLen += this._textModelN.getValueLengthInRange(rangeN);
pendingChangesLen -= this._textModel0.getValueLengthInRange(range0);
} else if (Range.areIntersectingOrTouching(entry.rangeN, change.range)) {
// an edit overlaps with a (pending) hunk. We take this as a signal
// to mark the hunk as accepted and to ignore the edit. The range of the hunk
// will be up-to-date because of decorations created for them
entry.markAccepted();
} else if (Range.areIntersectingOrTouching(rangeN, change.range)) {
isOverlapping = true;
break;
@ -454,23 +447,24 @@ export class HunkData {
diff ??= await this._editorWorkerService.computeDiff(this._textModel0.uri, this._textModelN.uri, { ignoreTrimWhitespace: false, maxComputationTimeMs: Number.MAX_SAFE_INTEGER, computeMoves: false }, 'advanced');
let mergedChanges: DetailedLineRangeMapping[] = [];
if (!diff || diff.changes.length === 0) {
// return new HunkData([], session);
return;
}
if (diff && diff.changes.length > 0) {
// merge changes neighboring changes
mergedChanges = [diff.changes[0]];
for (let i = 1; i < diff.changes.length; i++) {
const lastChange = mergedChanges[mergedChanges.length - 1];
const thisChange = diff.changes[i];
if (thisChange.modified.startLineNumber - lastChange.modified.endLineNumberExclusive <= HunkData._HUNK_THRESHOLD) {
mergedChanges[mergedChanges.length - 1] = new DetailedLineRangeMapping(
lastChange.original.join(thisChange.original),
lastChange.modified.join(thisChange.modified),
(lastChange.innerChanges ?? []).concat(thisChange.innerChanges ?? [])
);
} else {
mergedChanges.push(thisChange);
}
// merge changes neighboring changes
const mergedChanges = [diff.changes[0]];
for (let i = 1; i < diff.changes.length; i++) {
const lastChange = mergedChanges[mergedChanges.length - 1];
const thisChange = diff.changes[i];
if (thisChange.modified.startLineNumber - lastChange.modified.endLineNumberExclusive <= HunkData._HUNK_THRESHOLD) {
mergedChanges[mergedChanges.length - 1] = new DetailedLineRangeMapping(
lastChange.original.join(thisChange.original),
lastChange.modified.join(thisChange.modified),
(lastChange.innerChanges ?? []).concat(thisChange.innerChanges ?? [])
);
} else {
mergedChanges.push(thisChange);
}
}

View file

@ -8,7 +8,7 @@ import { coalesceInPlace } from 'vs/base/common/arrays';
import { CancellationToken } from 'vs/base/common/cancellation';
import { Emitter, Event } from 'vs/base/common/event';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { themeColorFromId, ThemeIcon } from 'vs/base/common/themables';
import { themeColorFromId } from 'vs/base/common/themables';
import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, IViewZone, IViewZoneChangeAccessor } from 'vs/editor/browser/editorBrowser';
import { StableEditorScrollState } from 'vs/editor/browser/stableEditorScroll';
import { LineSource, RenderOptions, renderLines } from 'vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/renderLines';
@ -27,7 +27,7 @@ import { SaveReason } from 'vs/workbench/common/editor';
import { countWords } from 'vs/workbench/contrib/chat/common/chatWordCounter';
import { HunkInformation, Session, HunkState } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession';
import { InlineChatZoneWidget } from './inlineChatZoneWidget';
import { ACTION_TOGGLE_DIFF, CTX_INLINE_CHAT_CHANGE_HAS_DIFF, CTX_INLINE_CHAT_CHANGE_SHOWS_DIFF, CTX_INLINE_CHAT_DOCUMENT_CHANGED, InlineChatConfigKeys, MENU_INLINE_CHAT_ZONE, minimapInlineChatDiffInserted, overviewRulerInlineChatDiffInserted } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
import { CTX_INLINE_CHAT_CHANGE_HAS_DIFF, CTX_INLINE_CHAT_CHANGE_SHOWS_DIFF, CTX_INLINE_CHAT_DOCUMENT_CHANGED, InlineChatConfigKeys, MENU_INLINE_CHAT_ZONE, minimapInlineChatDiffInserted, overviewRulerInlineChatDiffInserted } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
import { assertType } from 'vs/base/common/types';
import { IModelService } from 'vs/editor/common/services/model';
import { performAsyncTextEdit, asProgressiveEdit } from './utils';
@ -43,9 +43,6 @@ import { generateUuid } from 'vs/base/common/uuid';
import { MenuWorkbenchButtonBar } from 'vs/platform/actions/browser/buttonbar';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { Iterable } from 'vs/base/common/iterator';
import { ConflictActionsFactory, IContentWidgetAction } from 'vs/workbench/contrib/mergeEditor/browser/view/conflictActions';
import { observableValue } from 'vs/base/common/observable';
import { IMenuService, MenuItemAction } from 'vs/platform/actions/common/actions';
export interface IEditObserver {
start(): void;
@ -219,10 +216,8 @@ type HunkDisplayData = {
decorationIds: string[];
diffViewZoneId: string | undefined;
diffViewZone: IViewZone;
lensActionsViewZoneIds?: string[];
viewZoneId: string | undefined;
viewZone: IViewZone;
distance: number;
position: Position;
@ -262,7 +257,6 @@ export class LiveStrategy extends EditModeStrategy {
private readonly _ctxCurrentChangeShowsDiff: IContextKey<boolean>;
private readonly _progressiveEditingDecorations: IEditorDecorationsCollection;
private readonly _lensActionsFactory: ConflictActionsFactory;
private _editCount: number = 0;
constructor(
@ -274,8 +268,6 @@ export class LiveStrategy extends EditModeStrategy {
@IEditorWorkerService protected readonly _editorWorkerService: IEditorWorkerService,
@IAccessibilityService private readonly _accessibilityService: IAccessibilityService,
@IConfigurationService private readonly _configService: IConfigurationService,
@IMenuService private readonly _menuService: IMenuService,
@IContextKeyService private readonly _contextService: IContextKeyService,
@ITextFileService textFileService: ITextFileService,
@IInstantiationService instaService: IInstantiationService
) {
@ -284,7 +276,6 @@ export class LiveStrategy extends EditModeStrategy {
this._ctxCurrentChangeShowsDiff = CTX_INLINE_CHAT_CHANGE_SHOWS_DIFF.bindTo(contextKeyService);
this._progressiveEditingDecorations = this._editor.createDecorationsCollection();
this._lensActionsFactory = this._store.add(new ConflictActionsFactory(this._editor));
}
@ -496,95 +487,45 @@ export class LiveStrategy extends EditModeStrategy {
afterLineNumber: -1,
heightInLines: result.heightInLines,
domNode,
ordinal: 50000 + 2 // more than https://github.com/microsoft/vscode/blob/bf52a5cfb2c75a7327c9adeaefbddc06d529dcad/src/vs/workbench/contrib/inlineChat/browser/inlineChatZoneWidget.ts#L42
ordinal: 50000 + 1 // more than https://github.com/microsoft/vscode/blob/bf52a5cfb2c75a7327c9adeaefbddc06d529dcad/src/vs/workbench/contrib/inlineChat/browser/inlineChatZoneWidget.ts#L42
};
const toggleDiff = () => {
const scrollState = StableEditorScrollState.capture(this._editor);
changeDecorationsAndViewZones(this._editor, (_decorationsAccessor, viewZoneAccessor) => {
assertType(data);
if (!data.diffViewZoneId) {
if (!data.viewZoneId) {
const [hunkRange] = hunkData.getRangesN();
viewZoneData.afterLineNumber = hunkRange.startLineNumber - 1;
data.diffViewZoneId = viewZoneAccessor.addZone(viewZoneData);
data.viewZoneId = viewZoneAccessor.addZone(viewZoneData);
overlay?.updateExtraTop(result.heightInLines);
} else {
viewZoneAccessor.removeZone(data.diffViewZoneId!);
viewZoneAccessor.removeZone(data.viewZoneId!);
overlay?.updateExtraTop(0);
data.diffViewZoneId = undefined;
data.viewZoneId = undefined;
}
});
this._ctxCurrentChangeShowsDiff.set(typeof data?.diffViewZoneId === 'string');
this._ctxCurrentChangeShowsDiff.set(typeof data?.viewZoneId === 'string');
scrollState.restore(this._editor);
};
const overlay = this._showOverlayToolbar && false
const overlay = this._showOverlayToolbar
? this._instaService.createInstance(InlineChangeOverlay, this._editor, hunkData)
: undefined;
let lensActions: DisposableStore | undefined;
const lensActionsViewZoneIds: string[] = [];
if (this._showOverlayToolbar && hunkData.getState() === HunkState.Pending) {
lensActions = new DisposableStore();
const menu = this._menuService.createMenu(MENU_INLINE_CHAT_ZONE, this._contextService);
const makeActions = () => {
const actions: IContentWidgetAction[] = [];
const tuples = menu.getActions();
for (const [, group] of tuples) {
for (const item of group) {
if (item instanceof MenuItemAction) {
let text = item.label;
if (item.id === ACTION_TOGGLE_DIFF) {
text = item.checked ? 'Hide Changes' : 'Show Changes';
} else if (ThemeIcon.isThemeIcon(item.item.icon)) {
text = `$(${item.item.icon.id}) ${text}`;
}
actions.push({
text,
tooltip: item.tooltip,
action: async () => item.run(),
});
}
}
}
return actions;
};
const obs = observableValue(this, makeActions());
lensActions.add(menu.onDidChange(() => obs.set(makeActions(), undefined)));
lensActions.add(menu);
lensActions.add(this._lensActionsFactory.createWidget(viewZoneAccessor,
hunkRanges[0].startLineNumber - 1,
obs,
lensActionsViewZoneIds
));
}
const remove = () => {
changeDecorationsAndViewZones(this._editor, (decorationsAccessor, viewZoneAccessor) => {
assertType(data);
for (const decorationId of data.decorationIds) {
decorationsAccessor.removeDecoration(decorationId);
}
if (data.diffViewZoneId) {
viewZoneAccessor.removeZone(data.diffViewZoneId);
if (data.viewZoneId) {
viewZoneAccessor.removeZone(data.viewZoneId);
}
data.decorationIds = [];
data.diffViewZoneId = undefined;
data.lensActionsViewZoneIds?.forEach(viewZoneAccessor.removeZone);
data.lensActionsViewZoneIds = undefined;
data.viewZoneId = undefined;
});
lensActions?.dispose();
overlay?.dispose();
};
@ -607,9 +548,8 @@ export class LiveStrategy extends EditModeStrategy {
data = {
hunk: hunkData,
decorationIds,
diffViewZoneId: '',
diffViewZone: viewZoneData,
lensActionsViewZoneIds,
viewZoneId: '',
viewZone: viewZoneData,
distance: myDistance,
position: hunkRanges[0].getStartPosition().delta(-1),
acceptHunk,

View file

@ -96,9 +96,9 @@ export class InlineChatWidget {
h('div.accessibleViewer@accessibleViewer'),
h('div.status@status', [
h('div.label.info.hidden@infoLabel'),
h('div.actions.hidden@toolbar1'),
h('div.actions.button-style.hidden@toolbar1'),
h('div.label.status.hidden@statusLabel'),
h('div.actions.secondary.hidden@toolbar2'),
h('div.actions.button-style.hidden@toolbar2'),
]),
]
);
@ -385,7 +385,7 @@ export class InlineChatWidget {
}
protected _getExtraHeight(): number {
return 2 /*border*/ + 4 /*shadow*/;
return 4 /* padding */ + 2 /*border*/ + 4 /*shadow*/;
}
get value(): string {

View file

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { addDisposableListener, Dimension } from 'vs/base/browser/dom';
import * as aria from 'vs/base/browser/ui/aria/aria';
import { MutableDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { toDisposable } from 'vs/base/common/lifecycle';
import { assertType } from 'vs/base/common/types';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EditorLayoutInfo, EditorOption } from 'vs/editor/common/config/editorOptions';
@ -29,7 +29,6 @@ export class InlineChatZoneWidget extends ZoneWidget {
readonly widget: EditorBasedInlineChatWidget;
private readonly _scrollUp = this._disposables.add(new ScrollUpState(this.editor));
private readonly _ctxCursorPosition: IContextKey<'above' | 'below' | ''>;
private _dimension?: Dimension;
@ -166,7 +165,6 @@ export class InlineChatZoneWidget extends ZoneWidget {
this.widget.focus();
revealZone();
this._scrollUp.enable();
}
override updatePositionAndHeight(position: Position): void {
@ -188,15 +186,14 @@ export class InlineChatZoneWidget extends ZoneWidget {
return isResponseVM(candidate) && candidate.response.value.length > 0;
});
if (hasResponse && zoneTop < scrollTop || this._scrollUp.didScrollUp) {
if (hasResponse && zoneTop < scrollTop) {
// don't reveal the zone if it is already out of view (unless we are still getting ready)
// or if an outside scroll-up happened (e.g the user scrolled up to see the new content)
return this._scrollUp.runIgnored(() => {
return () => {
scrollState.restore(this.editor);
});
};
}
return this._scrollUp.runIgnored(() => {
return () => {
scrollState.restore(this.editor);
const scrollTop = this.editor.getScrollTop();
@ -219,7 +216,7 @@ export class InlineChatZoneWidget extends ZoneWidget {
this._logService.trace('[IE] REVEAL zone', { zoneTop, lineTop, lineBottom, scrollTop, newScrollTop, forceScrollTop });
this.editor.setScrollTop(newScrollTop, ScrollType.Immediate);
}
});
};
}
protected override revealRange(range: Range, isLastLine: boolean): void {
@ -232,7 +229,6 @@ export class InlineChatZoneWidget extends ZoneWidget {
override hide(): void {
const scrollState = StableEditorBottomScrollState.capture(this.editor);
this._scrollUp.disable();
this._ctxCursorPosition.reset();
this.widget.reset();
this.widget.chatWidget.setVisible(false);
@ -241,54 +237,3 @@ export class InlineChatZoneWidget extends ZoneWidget {
scrollState.restore(this.editor);
}
}
class ScrollUpState {
private _lastScrollTop: number = this._editor.getScrollTop();
private _didScrollUp?: boolean;
private _ignoreEvents = false;
private readonly _listener = new MutableDisposable();
constructor(private readonly _editor: ICodeEditor) { }
dispose(): void {
this._listener.dispose();
}
enable(): void {
this._didScrollUp = undefined;
this._listener.value = this._editor.onDidScrollChange(e => {
if (!e.scrollTopChanged || this._ignoreEvents) {
return;
}
const currentScrollTop = e.scrollTop;
if (currentScrollTop > this._lastScrollTop) {
this._listener.clear();
this._didScrollUp = true;
}
this._lastScrollTop = currentScrollTop;
});
}
disable(): void {
this._listener.clear();
this._didScrollUp = undefined;
}
runIgnored(callback: () => void): () => void {
return () => {
this._ignoreEvents = true;
try {
return callback();
} finally {
this._ignoreEvents = false;
}
};
}
get didScrollUp(): boolean | undefined {
return this._didScrollUp;
}
}

View file

@ -20,7 +20,7 @@
}
.monaco-workbench .inline-chat .chat-widget .interactive-session .interactive-input-part {
padding: 2px 6px 0 6px;
padding: 4px 6px 0 6px;
}
.monaco-workbench .inline-chat .chat-widget .interactive-session .interactive-input-part .interactive-execute-toolbar {
@ -32,12 +32,6 @@
border-radius: 2px;
}
.monaco-workbench .inline-chat .chat-widget .interactive-session .interactive-input-part .interactive-input-followups .interactive-session-followups {
margin: 2px 0 0 4px;
}
.monaco-workbench .inline-chat .chat-widget .interactive-session .interactive-list {
padding: 4px 0 0 0;
}
@ -76,15 +70,7 @@
display: flex;
justify-content: space-between;
align-items: center;
padding-left: 6px;
padding-right: 6px;
}
.monaco-workbench .inline-chat > .status {
.label,
.actions {
padding-top: 6px;
}
padding: 6px 6px 0 6px
}
.monaco-workbench .inline-chat .status .actions.hidden {
@ -106,8 +92,7 @@
.monaco-workbench .inline-chat .status .label.status {
margin-left: auto;
padding-right: 6px;
padding-left: 6px;
padding: 0 6px;
}
.monaco-workbench .inline-chat .status .label.hidden,
@ -171,16 +156,6 @@
gap: 4px;
}
.monaco-workbench .inline-chat .status .actions.secondary {
display: none;
}
.monaco-workbench .inline-chat .status:hover .actions.secondary,
.monaco-workbench .inline-chat:focus .status .actions.secondary,
.monaco-workbench .inline-chat .status:focus-within .actions.secondary {
display: inherit;
}
.monaco-workbench .inline-chat-diff-overlay {
.monaco-button {

View file

@ -116,7 +116,6 @@ export const CTX_INLINE_CHAT_RESPONSE_TYPE = new RawContextKey<InlineChatRespons
// --- (selected) action identifier
export const ACTION_ACCEPT_CHANGES = 'inlineChat.acceptChanges';
export const ACTION_DISCARD_CHANGES = 'inlineChat.discardHunkChange';
export const ACTION_REGENERATE_RESPONSE = 'inlineChat.regenerate';
export const ACTION_VIEW_IN_CHAT = 'inlineChat.viewInChat';
export const ACTION_TOGGLE_DIFF = 'inlineChat.toggleDiff';

View file

@ -779,7 +779,7 @@ suite('InteractiveChatController', function () {
});
test('Stopping/cancelling a request should NOT undo its changes', async function () {
test('Stopping/cancelling a request should undo its changes', async function () {
model.setValue('World');
@ -819,7 +819,7 @@ suite('InteractiveChatController', function () {
chatService.cancelCurrentRequestForSession(ctrl.chatWidget.viewModel!.model.sessionId);
assert.strictEqual(await p2, undefined);
assert.strictEqual(model.getValue(), 'HelloWorld'); // CANCEL just stops the request and progressive typing but doesn't undo
assert.strictEqual(model.getValue(), 'World');
});

View file

@ -60,8 +60,6 @@ import { IWorkbenchAssignmentService } from 'vs/workbench/services/assignment/co
import { NullWorkbenchAssignmentService } from 'vs/workbench/services/assignment/test/common/nullAssignmentService';
import { ILanguageModelToolsService } from 'vs/workbench/contrib/chat/common/languageModelToolsService';
import { MockLanguageModelToolsService } from 'vs/workbench/contrib/chat/test/common/mockLanguageModelToolsService';
import { IChatRequestModel } from 'vs/workbench/contrib/chat/common/chatModel';
import { assertSnapshot } from 'vs/base/test/common/snapshot';
suite('InlineChatSession', function () {
@ -489,90 +487,4 @@ suite('InlineChatSession', function () {
inlineChatSessionService.releaseSession(session);
});
test('Pressing Escape after inline chat errored with "response filtered" leaves document dirty #7764', async function () {
const origValue = `class Foo {
private onError(error: string): void {
if (/The request timed out|The network connection was lost/i.test(error)) {
return;
}
error = error.replace(/See https:\/\/github\.com\/Squirrel\/Squirrel\.Mac\/issues\/182 for more information/, 'This might mean the application was put on quarantine by macOS. See [this link](https://github.com/microsoft/vscode/issues/7426#issuecomment-425093469) for more information');
this.notificationService.notify({
severity: Severity.Error,
message: error,
source: nls.localize('update service', "Update Service"),
});
}
}`;
model.setValue(origValue);
const session = await inlineChatSessionService.createSession(editor, { editMode: EditMode.Live }, CancellationToken.None);
assertType(session);
const fakeRequest = new class extends mock<IChatRequestModel>() {
override get id() { return 'one'; }
};
session.markModelVersion(fakeRequest);
assert.strictEqual(editor.getModel().getLineCount(), 15);
await makeEditAsAi([EditOperation.replace(new Range(7, 1, 7, Number.MAX_SAFE_INTEGER), `error = error.replace(
/See https:\/\/github\.com\/Squirrel\/Squirrel\.Mac\/issues\/182 for more information/,
'This might mean the application was put on quarantine by macOS. See [this link](https://github.com/microsoft/vscode/issues/7426#issuecomment-425093469) for more information'
);`)]);
assert.strictEqual(editor.getModel().getLineCount(), 18);
// called when a response errors out
await session.undoChangesUntil(fakeRequest.id);
await session.hunkData.recompute({ applied: 0, sha1: 'fakeSha1' }, undefined);
assert.strictEqual(editor.getModel().getValue(), origValue);
session.hunkData.discardAll(); // called when dimissing the session
assert.strictEqual(editor.getModel().getValue(), origValue);
});
test('Apply Code\'s preview should be easier to undo/esc #7537', async function () {
model.setValue(`export function fib(n) {
if (n <= 0) return 0;
if (n === 1) return 0;
if (n === 2) return 1;
return fib(n - 1) + fib(n - 2);
}`);
const session = await inlineChatSessionService.createSession(editor, { editMode: EditMode.Live }, CancellationToken.None);
assertType(session);
await makeEditAsAi([EditOperation.replace(new Range(5, 1, 6, Number.MAX_SAFE_INTEGER), `
let a = 0, b = 1, c;
for (let i = 3; i <= n; i++) {
c = a + b;
a = b;
b = c;
}
return b;
}`)]);
assert.strictEqual(session.hunkData.size, 1);
assert.strictEqual(session.hunkData.pending, 1);
assert.ok(session.hunkData.getInfo().every(d => d.getState() === HunkState.Pending));
await assertSnapshot(editor.getModel().getValue(), { name: '1' });
await model.undo();
await assertSnapshot(editor.getModel().getValue(), { name: '2' });
// overlapping edits (even UNDO) mark edits as accepted
assert.strictEqual(session.hunkData.size, 1);
assert.strictEqual(session.hunkData.pending, 0);
assert.ok(session.hunkData.getInfo().every(d => d.getState() === HunkState.Accepted));
// no further change when discarding
session.hunkData.discardAll(); // CANCEL
await assertSnapshot(editor.getModel().getValue(), { name: '2' });
});
});

View file

@ -54,8 +54,8 @@ export class ConflictActionsFactory extends Disposable {
newStyle += `${this._styleClassName} { font-family: var(${fontFamilyVar}), ${EDITOR_FONT_DEFAULTS.fontFamily}}`;
}
this._styleElement.textContent = newStyle;
this._editor.getContainerDomNode().style?.setProperty(fontFamilyVar, fontFamily ?? 'inherit');
this._editor.getContainerDomNode().style?.setProperty(fontFeaturesVar, editorFontInfo.fontFeatureSettings);
this._editor.getContainerDomNode().style.setProperty(fontFamilyVar, fontFamily ?? 'inherit');
this._editor.getContainerDomNode().style.setProperty(fontFeaturesVar, editorFontInfo.fontFeatureSettings);
}
private _getLayoutInfo() {

View file

@ -33,7 +33,6 @@ export abstract class FixedZoneWidget extends Disposable {
domNode: document.createElement('div'),
afterLineNumber: afterLineNumber,
heightInPx: height,
ordinal: 50000 + 1,
onComputedHeight: (height) => {
this.widgetDomNode.style.height = `${height}px`;
},

View file

@ -1074,7 +1074,7 @@ export class DeletedElement extends SingleSideDiffElement {
layout(state: IDiffElementLayoutState) {
DOM.scheduleAtNextAnimationFrame(DOM.getWindow(this._diffEditorContainer), () => {
if ((state.editorHeight || state.outerWidth) && this._editor) {
if (state.editorHeight || state.outerWidth) {
this._editorContainer.style.height = `${this.cell.layoutInfo.editorHeight}px`;
this._editor.layout({
width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, false),
@ -1254,7 +1254,7 @@ export class InsertElement extends SingleSideDiffElement {
layout(state: IDiffElementLayoutState) {
DOM.scheduleAtNextAnimationFrame(DOM.getWindow(this._diffEditorContainer), () => {
if ((state.editorHeight || state.outerWidth) && this._editor) {
if (state.editorHeight || state.outerWidth) {
this._editorContainer.style.height = `${this.cell.layoutInfo.editorHeight}px`;
this._editor.layout({
width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, false),
@ -1644,7 +1644,7 @@ export class ModifiedElement extends AbstractElementRenderer {
{
updateInfoRendering: () => renderSourceEditor(),
checkIfModified: (cell) => {
return cell.modified?.textModel.getTextBufferHash() !== cell.original?.textModel.getTextBufferHash() ? { reason: undefined } : false;
return cell.modified?.textModel.getValue() !== cell.original?.textModel.getValue() ? { reason: undefined } : false;
},
getFoldingState: (cell) => cell.cellFoldingState,
updateFoldingState: (cell, state) => cell.cellFoldingState = state,
@ -1660,7 +1660,7 @@ export class ModifiedElement extends AbstractElementRenderer {
const scopedContextKeyService = this.contextKeyService.createScoped(this.templateData.inputToolbarContainer);
this._register(scopedContextKeyService);
const inputChanged = NOTEBOOK_DIFF_CELL_INPUT.bindTo(scopedContextKeyService);
inputChanged.set(this.cell.modified.textModel.getTextBufferHash() !== this.cell.original.textModel.getTextBufferHash());
inputChanged.set(this.cell.modified.textModel.getValue() !== this.cell.original.textModel.getValue());
const ignoreWhitespace = NOTEBOOK_DIFF_CELL_IGNORE_WHITESPACE.bindTo(scopedContextKeyService);
const ignore = this.textConfigurationService.getValue<boolean>(this.cell.modified.uri, 'diffEditor.ignoreTrimWhitespace');
@ -1675,7 +1675,7 @@ export class ModifiedElement extends AbstractElementRenderer {
const refreshToolbar = () => {
const ignore = this.textConfigurationService.getValue<boolean>(this.cell.modified.uri, 'diffEditor.ignoreTrimWhitespace');
ignoreWhitespace.set(ignore);
const hasChanges = this.cell.modified.textModel.getTextBufferHash() !== this.cell.original.textModel.getTextBufferHash();
const hasChanges = this.cell.modified.textModel.getValue() !== this.cell.original.textModel.getValue();
inputChanged.set(hasChanges);
if (hasChanges) {

View file

@ -222,7 +222,7 @@ export abstract class DiffElementCellViewModelBase extends DiffElementViewModelB
layoutState: CellLayoutState.Uninitialized
};
this.cellFoldingState = modified?.getTextBufferHash() !== original?.getTextBufferHash() ? PropertyFoldingState.Expanded : PropertyFoldingState.Collapsed;
this.cellFoldingState = modified?.textModel?.getValue() !== original?.textModel?.getValue() ? PropertyFoldingState.Expanded : PropertyFoldingState.Collapsed;
this.metadataFoldingState = PropertyFoldingState.Collapsed;
this.outputFoldingState = PropertyFoldingState.Collapsed;

View file

@ -441,12 +441,16 @@ function createDiffViewModels(instantiationService: IInstantiationService, confi
);
}
case 'unchanged': {
const originalCell = originalModel.cells[diff.originalCellIndex];
const modifiedCell = modifiedModel.cells[diff.modifiedCellIndex];
const type = (originalCell.textModel?.getValue() !== modifiedCell.textModel?.getValue()) ? 'modified' : 'unchanged';
return new SideBySideDiffElementViewModel(
model.modified.notebook,
model.original.notebook,
originalModel.cells[diff.originalCellIndex],
modifiedModel.cells[diff.modifiedCellIndex],
'unchanged', eventDispatcher,
originalCell,
modifiedCell,
type,
eventDispatcher,
initData,
notebookService
);

View file

@ -419,10 +419,6 @@ export class NotebookCellTextModel extends Disposable implements ICell {
return false;
}
if (this.outputs.length !== b.outputs.length) {
return false;
}
if (this.getTextLength() !== b.getTextLength()) {
return false;
}

View file

@ -314,7 +314,7 @@ class HistoryItemRenderer implements ITreeRenderer<SCMHistoryItemViewModelTreeEl
const historyItemLabels = (historyItem.labels ?? [])
.filter(l => labels.includes(l.title));
if (historyItemLabels.length > 0) {
if (historyItemLabels) {
const historyItemGroupLocalColor = colorTheme.getColor(historyItemGroupLocal);
const historyItemGroupRemoteColor = colorTheme.getColor(historyItemGroupRemote);
const historyItemGroupBaseColor = colorTheme.getColor(historyItemGroupBase);

View file

@ -117,21 +117,22 @@ export class NotebookSearchService implements INotebookSearchService {
}
private async doesFileExist(includes: string[], folderQueries: IFolderQuery<URI>[], token: CancellationToken): Promise<boolean> {
const promises: Promise<boolean>[] = includes.map(async includePattern => {
const promises: Promise<void>[] = includes.map(async includePattern => {
const query = this.queryBuilder.file(folderQueries.map(e => e.folder), {
includePattern: includePattern.startsWith('/') ? includePattern : '**/' + includePattern, // todo: find cleaner way to ensure that globs match all appropriate filetypes
exists: true,
onlyFileScheme: true,
exists: true
});
return this.searchService.fileSearch(
query,
token
).then((ret) => {
return !!ret.limitHit;
if (!ret.limitHit) {
throw Error('File not found');
}
});
});
return Promise.any(promises);
return Promise.any(promises).then(() => true).catch(() => false);
}
private async getClosedNotebookResults(textQuery: ITextQuery, scannedFiles: ResourceSet, token: CancellationToken): Promise<IClosedNotebookSearchResults> {

View file

@ -328,10 +328,10 @@ __vsc_restore_exit_code() {
__vsc_prompt_cmd_original() {
__vsc_status="$?"
builtin local cmd
__vsc_restore_exit_code "${__vsc_status}"
# Evaluate the original PROMPT_COMMAND similarly to how bash would normally
# See https://unix.stackexchange.com/a/672843 for technique
builtin local cmd
for cmd in "${__vsc_original_prompt_command[@]}"; do
eval "${cmd:-}"
done

View file

@ -172,9 +172,9 @@ function Set-MappedKeyHandler {
function Get-KeywordCompletionResult(
$Keyword,
$Description = $Keyword
$Description = $null
) {
[System.Management.Automation.CompletionResult]::new($Keyword, $Keyword, [System.Management.Automation.CompletionResultType]::Keyword, $Description)
[System.Management.Automation.CompletionResult]::new($Keyword, $Keyword, [System.Management.Automation.CompletionResultType]::Keyword, $null -ne $Description ? $Description : $Keyword)
}
function Set-MappedKeyHandlers {
@ -360,7 +360,7 @@ function Send-Completions {
# completions are consistent regardless of where it was requested
elseif ($lastWord -match '[/\\]') {
$lastSlashIndex = $completionPrefix.LastIndexOfAny(@('/', '\'))
if ($lastSlashIndex -ne -1 -and $lastSlashIndex -lt $cursorIndex) {
if ($lastSlashIndex -ne -1 && $lastSlashIndex -lt $cursorIndex) {
$newCursorIndex = $lastSlashIndex + 1
$completionPrefix = $completionPrefix.Substring(0, $newCursorIndex)
$prefixCursorDelta = $cursorIndex - $newCursorIndex
@ -388,9 +388,9 @@ function Send-Completions {
if ($completions.CompletionMatches.Count -gt 0 -and $completions.CompletionMatches.Where({ $_.ResultType -eq 3 -or $_.ResultType -eq 4 })) {
# Add `../ relative to the top completion
$firstCompletion = $completions.CompletionMatches[0]
if ($firstCompletion.CompletionText.StartsWith("..$([System.IO.Path]::DirectorySeparatorChar)")) {
if ($completionPrefix -match "(\.\.\$([System.IO.Path]::DirectorySeparatorChar))+") {
$parentDir = "$($matches[0])..$([System.IO.Path]::DirectorySeparatorChar)"
if ($firstCompletion.CompletionText.StartsWith('../')) {
if ($completionPrefix -match '(\.\.\/)+') {
$parentDir = "$($matches[0])../"
$currentPath = Split-Path -Parent $firstCompletion.ToolTip
try {
$parentDirPath = Split-Path -Parent $currentPath
@ -430,7 +430,7 @@ function Send-Completions {
# completions are consistent regardless of where it was requested
if ($completionPrefix -match '[/\\]') {
$lastSlashIndex = $completionPrefix.LastIndexOfAny(@('/', '\'))
if ($lastSlashIndex -ne -1 -and $lastSlashIndex -lt $cursorIndex) {
if ($lastSlashIndex -ne -1 && $lastSlashIndex -lt $cursorIndex) {
$newCursorIndex = $lastSlashIndex + 1
$completionPrefix = $completionPrefix.Substring(0, $newCursorIndex)
$prefixCursorDelta = $cursorIndex - $newCursorIndex

View file

@ -2305,7 +2305,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
async handleMouseEvent(event: MouseEvent, contextMenu: IMenu): Promise<{ cancelContextMenu: boolean } | void> {
// Don't handle mouse event if it was on the scroll bar
if (dom.isHTMLElement(event.target) && (event.target.classList.contains('scrollbar') || event.target.classList.contains('slider'))) {
if (dom.isHTMLElement(event.target) && event.target.classList.contains('scrollbar')) {
return { cancelContextMenu: true };
}

View file

@ -9,7 +9,6 @@ import { AutoOpenBarrier } from 'vs/base/common/async';
import { Event } from 'vs/base/common/event';
import { KeyCode } from 'vs/base/common/keyCodes';
import { DisposableStore, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { isWindows } from 'vs/base/common/platform';
import { localize2 } from 'vs/nls';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ContextKeyExpr, IContextKey, IContextKeyService, IReadableSet } from 'vs/platform/contextkey/common/contextkey';
@ -171,20 +170,16 @@ class TerminalSuggestContribution extends DisposableStore implements ITerminalCo
// If completions are requested, pause and queue input events until completions are
// received. This fixing some problems in PowerShell, particularly enter not executing
// when typing quickly and some characters being printed twice. On Windows this isn't
// needed because inputs are _not_ echoed when not handled immediately.
// TODO: This should be based on the OS of the pty host, not the client
if (!isWindows) {
let barrier: AutoOpenBarrier | undefined;
this.add(addon.onDidRequestCompletions(() => {
barrier = new AutoOpenBarrier(2000);
this._instance.pauseInputEvents(barrier);
}));
this.add(addon.onDidReceiveCompletions(() => {
barrier?.open();
barrier = undefined;
}));
}
// when typing quickly and some characters being printed twice.
let barrier: AutoOpenBarrier | undefined;
this.add(addon.onDidRequestCompletions(() => {
barrier = new AutoOpenBarrier(2000);
this._instance.pauseInputEvents(barrier);
}));
this.add(addon.onDidReceiveCompletions(() => {
barrier?.open();
barrier = undefined;
}));
}
}
}

View file

@ -30,10 +30,6 @@ export class TestResultStackWidget extends Disposable {
));
}
public collapseAll() {
this.widget.collapseAll();
}
public update(messageFrame: AnyStackFrame, stack: ITestMessageStackFrame[]) {
this.widget.setFrames([messageFrame, ...stack.map(frame => new CallStackFrame(
frame.label,

View file

@ -22,9 +22,6 @@ interface ISubjectCommon {
controllerId: string;
}
export const inspectSubjectHasStack = (subject: InspectSubject | undefined) =>
subject instanceof MessageSubject && !!subject.stack?.length;
export class MessageSubject implements ISubjectCommon {
public readonly test: ITestItem;
public readonly message: ITestMessage;

View file

@ -838,21 +838,22 @@ class TreeActionsProvider {
}
if (element instanceof TestMessageElement) {
id = MenuId.TestMessageContext;
contextKeys.push([TestingContextKeys.testMessageContext.key, element.contextValue]);
primary.push(new Action(
'testing.outputPeek.goToTest',
localize('testing.goToTest', "Go to Test"),
'testing.outputPeek.goToFile',
localize('testing.goToFile', "Go to Source"),
ThemeIcon.asClassName(Codicon.goToFile),
undefined,
() => this.commandService.executeCommand('vscode.revealTest', element.test.item.extId),
));
}
if (element instanceof TestMessageElement) {
id = MenuId.TestMessageContext;
contextKeys.push([TestingContextKeys.testMessageContext.key, element.contextValue]);
if (this.showRevealLocationOnMessages && element.location) {
primary.push(new Action(
'testing.outputPeek.goToError',
localize('testing.goToError', "Go to Error"),
localize('testing.goToError', "Go to Source"),
ThemeIcon.asClassName(Codicon.goToFile),
undefined,
() => this.editorService.openEditor({

View file

@ -322,13 +322,6 @@ export class TestResultsViewContent extends Disposable {
});
}
/**
* Collapses all displayed stack frames.
*/
public collapseStack() {
this.callStackWidget.collapseAll();
}
private getCallFrames(subject: InspectSubject) {
if (!(subject instanceof MessageSubject)) {
return undefined;

View file

@ -25,7 +25,7 @@ import { testingResultsIcon, testingViewIcon } from 'vs/workbench/contrib/testin
import { TestCoverageView } from 'vs/workbench/contrib/testing/browser/testCoverageView';
import { TestingDecorationService, TestingDecorations } from 'vs/workbench/contrib/testing/browser/testingDecorations';
import { TestingExplorerView } from 'vs/workbench/contrib/testing/browser/testingExplorerView';
import { CloseTestPeek, CollapsePeekStack, GoToNextMessageAction, GoToPreviousMessageAction, OpenMessageInEditorAction, TestResultsView, TestingOutputPeekController, TestingPeekOpener, ToggleTestingPeekHistory } from 'vs/workbench/contrib/testing/browser/testingOutputPeek';
import { CloseTestPeek, GoToNextMessageAction, GoToPreviousMessageAction, OpenMessageInEditorAction, TestResultsView, TestingOutputPeekController, TestingPeekOpener, ToggleTestingPeekHistory } from 'vs/workbench/contrib/testing/browser/testingOutputPeek';
import { TestingProgressTrigger } from 'vs/workbench/contrib/testing/browser/testingProgressUiService';
import { TestingViewPaneContainer } from 'vs/workbench/contrib/testing/browser/testingViewPaneContainer';
import { testingConfiguration } from 'vs/workbench/contrib/testing/common/configuration';
@ -136,7 +136,6 @@ registerAction2(GoToPreviousMessageAction);
registerAction2(GoToNextMessageAction);
registerAction2(CloseTestPeek);
registerAction2(ToggleTestingPeekHistory);
registerAction2(CollapsePeekStack);
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(TestingContentProvider, LifecyclePhase.Restored);
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(TestingPeekOpener, LifecyclePhase.Eventually);

View file

@ -4,7 +4,6 @@
*--------------------------------------------------------------------------------------------*/
import * as dom from 'vs/base/browser/dom';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { Action, IAction, Separator, SubmenuAction } from 'vs/base/common/actions';
import { equals } from 'vs/base/common/arrays';
import { RunOnceScheduler } from 'vs/base/common/async';
@ -457,24 +456,11 @@ export class TestingDecorations extends Disposable implements IEditorContributio
decorations.syncDecorations(this._currentUri);
}
}));
const win = dom.getWindow(editor.getDomNode());
this._register(dom.addDisposableListener(win, 'keydown', e => {
if (new StandardKeyboardEvent(e).keyCode === KeyCode.Alt && this._currentUri) {
decorations.updateDecorationsAlternateAction(this._currentUri, true);
this._register(this.editor.onKeyDown(e => {
if (e.keyCode === KeyCode.Alt && this._currentUri) {
decorations.updateDecorationsAlternateAction(this._currentUri!, true);
}
}));
this._register(dom.addDisposableListener(win, 'keyup', e => {
if (new StandardKeyboardEvent(e).keyCode === KeyCode.Alt && this._currentUri) {
decorations.updateDecorationsAlternateAction(this._currentUri, false);
}
}));
this._register(dom.addDisposableListener(win, 'blur', () => {
if (this._currentUri) {
decorations.updateDecorationsAlternateAction(this._currentUri, false);
}
}));
this._register(this.editor.onKeyUp(e => {
if (e.keyCode === KeyCode.Alt && this._currentUri) {
decorations.updateDecorationsAlternateAction(this._currentUri!, false);

View file

@ -14,7 +14,6 @@ import { Iterable } from 'vs/base/common/iterator';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { Lazy } from 'vs/base/common/lazy';
import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle';
import { observableValue } from 'vs/base/common/observable';
import { count } from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri';
import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser';
@ -44,7 +43,6 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { bindContextKey } from 'vs/platform/observable/common/platformObservableUtils';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
@ -54,7 +52,7 @@ import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'
import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPane';
import { IViewDescriptorService } from 'vs/workbench/common/views';
import { renderTestMessageAsText } from 'vs/workbench/contrib/testing/browser/testMessageColorizer';
import { InspectSubject, MessageSubject, TaskSubject, TestOutputSubject, inspectSubjectHasStack, mapFindTestMessage } from 'vs/workbench/contrib/testing/browser/testResultsView/testResultsSubject';
import { InspectSubject, MessageSubject, TaskSubject, TestOutputSubject, mapFindTestMessage } from 'vs/workbench/contrib/testing/browser/testResultsView/testResultsSubject';
import { TestResultsViewContent } from 'vs/workbench/contrib/testing/browser/testResultsView/testResultsViewContent';
import { testingMessagePeekBorder, testingPeekBorder, testingPeekHeaderBackground, testingPeekMessageHeaderBackground } from 'vs/workbench/contrib/testing/browser/theme';
import { AutoOpenPeekViewWhen, TestingConfigKeys, getTestingConfiguration } from 'vs/workbench/contrib/testing/common/configuration';
@ -500,13 +498,6 @@ export class TestingOutputPeekController extends Disposable implements IEditorCo
this.peek.clear();
}
/**
* Collapses all displayed stack frames.
*/
public collapseStack() {
this.peek.value?.collapseStack();
}
/**
* Shows the next message in the peek, if possible.
*/
@ -654,14 +645,10 @@ class TestResultsPeek extends PeekViewWidget {
private static lastHeightInLines?: number;
private readonly visibilityChange = this._disposables.add(new Emitter<boolean>());
private readonly _current = observableValue<InspectSubject | undefined>('testPeekCurrent', undefined);
private content!: TestResultsViewContent;
private scopedContextKeyService!: IContextKeyService;
private dimension?: dom.Dimension;
public get current() {
return this._current.get();
}
public current?: InspectSubject;
constructor(
editor: ICodeEditor,
@ -715,14 +702,7 @@ class TestResultsPeek extends PeekViewWidget {
protected override _fillHead(container: HTMLElement): void {
super._fillHead(container);
const menuContextKeyService = this._disposables.add(this.contextKeyService.createScoped(container));
this._disposables.add(bindContextKey(
TestingContextKeys.peekHasStack,
menuContextKeyService,
reader => inspectSubjectHasStack(this._current.read(reader)),
));
const menu = this.menuService.createMenu(MenuId.TestPeekTitle, menuContextKeyService);
const menu = this.menuService.createMenu(MenuId.TestPeekTitle, this.contextKeyService);
const actionBar = this._actionbarWidget!;
this._disposables.add(menu.onDidChange(() => {
actions.length = 0;
@ -752,7 +732,7 @@ class TestResultsPeek extends PeekViewWidget {
*/
public setModel(subject: InspectSubject): Promise<void> {
if (subject instanceof TaskSubject || subject instanceof TestOutputSubject) {
this._current.set(subject, undefined);
this.current = subject;
return this.showInPlace(subject);
}
@ -763,14 +743,14 @@ class TestResultsPeek extends PeekViewWidget {
return Promise.resolve();
}
this._current.set(subject, undefined);
this.current = subject;
if (!revealLocation) {
return this.showInPlace(subject);
}
// If there is a stack we want to display, ensure the default size is large-ish
const peekLines = TestResultsPeek.lastHeightInLines || Math.max(
inspectSubjectHasStack(subject) ? Math.ceil(this.getVisibleEditorLines() / 2) : 0,
subject instanceof MessageSubject && subject.stack?.length ? Math.ceil(this.getVisibleEditorLines() / 2) : 0,
hintMessagePeekHeight(message)
);
@ -780,13 +760,6 @@ class TestResultsPeek extends PeekViewWidget {
return this.showInPlace(subject);
}
/**
* Collapses all displayed stack frames.
*/
public collapseStack() {
this.content.collapseStack();
}
private getVisibleEditorLines() {
// note that we don't use the view ranges because we don't want to get
// thrown off by large wrapping lines. Being approximate here is okay.
@ -1052,31 +1025,6 @@ export class GoToPreviousMessageAction extends Action2 {
}
}
export class CollapsePeekStack extends Action2 {
public static readonly ID = 'testing.collapsePeekStack';
constructor() {
super({
id: CollapsePeekStack.ID,
title: localize2('testing.collapsePeekStack', 'Collapse Stack Frames'),
icon: Codicon.collapseAll,
category: Categories.Test,
menu: [{
id: MenuId.TestPeekTitle,
when: TestingContextKeys.peekHasStack,
group: 'navigation',
order: 4,
}],
});
}
public override run(accessor: ServicesAccessor) {
const editor = getPeekedEditorFromFocus(accessor.get(ICodeEditorService));
if (editor) {
TestingOutputPeekController.get(editor)?.collapseStack();
}
}
}
export class OpenMessageInEditorAction extends Action2 {
public static readonly ID = 'testing.openMessageInEditor';
constructor() {

View file

@ -29,7 +29,6 @@ export namespace TestingContextKeys {
export const inlineCoverageEnabled = new RawContextKey('testing.inlineCoverageEnabled', false, { type: 'boolean', description: localize('testing.inlineCoverageEnabled', 'Indicates whether inline coverage is shown') });
export const canGoToRelatedCode = new RawContextKey('testing.canGoToRelatedCode', false, { type: 'boolean', description: localize('testing.canGoToRelatedCode', 'Whether a controller implements a capability to find code related to a test') });
export const canGoToRelatedTest = new RawContextKey('testing.canGoToRelatedTest', false, { type: 'boolean', description: localize('testing.canGoToRelatedTest', 'Whether a controller implements a capability to find tests related to code') });
export const peekHasStack = new RawContextKey('testing.peekHasStack', false, { type: 'boolean', description: localize('testing.peekHasStack', 'Whether the message shown in a peek view has a stack trace') });
export const capabilityToContextKey: { [K in TestRunProfileBitset]: RawContextKey<boolean> } = {
[TestRunProfileBitset.Run]: hasRunnableTests,

View file

@ -92,7 +92,6 @@ export class UserDataProfilesEditor extends EditorPane implements IUserDataProfi
private profileWidget: ProfileWidget | undefined;
private model: UserDataProfilesEditorModel | undefined;
private templates: readonly IProfileTemplateInfo[] = [];
constructor(
group: IEditorGroup,
@ -208,7 +207,7 @@ export class UserDataProfilesEditor extends EditorPane implements IUserDataProfi
actions: {
getActions: () => {
const actions: IAction[] = [];
if (this.templates.length) {
if (this.model?.templates.length) {
actions.push(new SubmenuAction('from.template', localize('from template', "From Template"), this.getCreateFromTemplateActions()));
actions.push(new Separator());
}
@ -226,13 +225,15 @@ export class UserDataProfilesEditor extends EditorPane implements IUserDataProfi
}
private getCreateFromTemplateActions(): IAction[] {
return this.templates.map(template =>
new Action(
`template:${template.url}`,
template.name,
undefined,
true,
() => this.createNewProfile(URI.parse(template.url))));
return this.model
? this.model.templates.map(template =>
new Action(
`template:${template.url}`,
template.name,
undefined,
true,
() => this.createNewProfile(URI.parse(template.url))))
: [];
}
private registerListeners(): void {
@ -342,12 +343,9 @@ export class UserDataProfilesEditor extends EditorPane implements IUserDataProfi
override async setInput(input: UserDataProfilesEditorInput, options: IEditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise<void> {
await super.setInput(input, options, context, token);
this.model = await input.resolve();
this.model.getTemplates().then(templates => {
this.templates = templates;
if (this.profileWidget) {
this.profileWidget.templates = templates;
}
});
if (this.profileWidget) {
this.profileWidget.templates = this.model.templates;
}
this.updateProfilesList();
this._register(this.model.onDidChange(element =>
this.updateProfilesList(element)));
@ -712,6 +710,7 @@ class ProfileTreeDataSource implements IAsyncDataSource<AbstractUserDataProfileE
children.push({ element: 'name', root: element });
children.push({ element: 'icon', root: element });
}
children.push({ element: 'useForCurrent', root: element });
children.push({ element: 'useAsDefault', root: element });
children.push({ element: 'contents', root: element });
}

View file

@ -713,7 +713,8 @@ export class UserDataProfilesEditorModel extends EditorModel {
private _onDidChange = this._register(new Emitter<AbstractUserDataProfileElement | undefined>());
readonly onDidChange = this._onDidChange.event;
private templates: Promise<readonly IProfileTemplateInfo[]> | undefined;
private _templates: IProfileTemplateInfo[] | undefined;
get templates(): readonly IProfileTemplateInfo[] { return this._templates ?? []; }
constructor(
@IUserDataProfileService private readonly userDataProfileService: IUserDataProfileService,
@ -760,11 +761,9 @@ export class UserDataProfilesEditorModel extends EditorModel {
}
}
getTemplates(): Promise<readonly IProfileTemplateInfo[]> {
if (!this.templates) {
this.templates = this.userDataProfileManagementService.getBuiltinProfileTemplates();
}
return this.templates;
override async resolve(): Promise<void> {
await super.resolve();
this._templates = await this.userDataProfileManagementService.getBuiltinProfileTemplates();
}
private createProfileElement(profile: IUserDataProfile): [UserDataProfileElement, DisposableStore] {
@ -772,7 +771,7 @@ export class UserDataProfilesEditorModel extends EditorModel {
const activateAction = disposables.add(new Action(
'userDataProfile.activate',
localize('active', "Use this Profile for Current Window"),
localize('active', "Use for Current Window"),
ThemeIcon.asClassName(Codicon.check),
true,
() => this.userDataProfileManagementService.switchProfile(profileElement.profile)
@ -809,16 +808,25 @@ export class UserDataProfilesEditorModel extends EditorModel {
() => this.openWindow(profileElement.profile)
));
const useAsNewWindowProfileAction = disposables.add(new Action(
'userDataProfile.useAsNewWindowProfile',
localize('use as new window', "Use for New Windows"),
undefined,
true,
() => profileElement.toggleNewWindowProfile()
));
const primaryActions: IAction[] = [];
primaryActions.push(activateAction);
primaryActions.push(newWindowAction);
if (!profile.isDefault) {
primaryActions.push(deleteAction);
}
const secondaryActions: IAction[] = [];
secondaryActions.push(activateAction);
secondaryActions.push(useAsNewWindowProfileAction);
secondaryActions.push(new Separator());
secondaryActions.push(copyFromProfileAction);
secondaryActions.push(exportAction);
if (!profile.isDefault) {
secondaryActions.push(new Separator());
secondaryActions.push(deleteAction);
}
const profileElement = disposables.add(this.instantiationService.createInstance(UserDataProfileElement,
profile,
@ -826,9 +834,16 @@ export class UserDataProfilesEditorModel extends EditorModel {
[primaryActions, secondaryActions]
));
activateAction.enabled = this.userDataProfileService.currentProfile.id !== profileElement.profile.id;
activateAction.checked = this.userDataProfileService.currentProfile.id === profileElement.profile.id;
disposables.add(this.userDataProfileService.onDidChangeCurrentProfile(() =>
activateAction.enabled = this.userDataProfileService.currentProfile.id !== profileElement.profile.id));
activateAction.checked = this.userDataProfileService.currentProfile.id === profileElement.profile.id));
useAsNewWindowProfileAction.checked = profileElement.isNewWindowProfile;
disposables.add(profileElement.onDidChange(e => {
if (e.newWindowProfile) {
useAsNewWindowProfileAction.checked = profileElement.isNewWindowProfile;
}
}));
return [profileElement, disposables];
}

View file

@ -226,7 +226,6 @@ export const walkthroughs: GettingStartedWalkthroughContent = [
'onSettingChanged:workbench.colorTheme',
'onCommand:workbench.action.selectTheme'
],
when: '!accessibilityModeEnabled',
media: { type: 'markdown', path: 'theme_picker', }
},
{
@ -400,7 +399,7 @@ export const walkthroughs: GettingStartedWalkthroughContent = [
isFeatured: true,
icon: setupIcon,
when: CONTEXT_ACCESSIBILITY_MODE_ENABLED.key,
next: 'Setup',
next: 'SetupScreenReaderExtended',
content: {
type: 'steps',
steps: [
@ -471,6 +470,90 @@ export const walkthroughs: GettingStartedWalkthroughContent = [
]
}
},
{
id: 'SetupScreenReaderExtended',
title: localize('gettingStarted.setupScreenReaderExtended.title', "Learn more about using VS Code with a Screen Reader"),
description: localize('gettingStarted.setupScreenReaderExtended.description', "Customize your editor, learn the basics, and start coding"),
isFeatured: true,
icon: setupIcon,
when: `!isWeb && ${CONTEXT_ACCESSIBILITY_MODE_ENABLED.key}`,
content: {
type: 'steps',
steps: [
{
id: 'extensionsWeb',
title: localize('gettingStarted.extensions.title', "Code with extensions"),
description: localize('gettingStarted.extensionsWeb.description.interpolated', "Extensions are VS Code's power-ups. A growing number are becoming available in the web.\n{0}", Button(localize('browsePopularWeb', "Browse Popular Web Extensions"), 'command:workbench.extensions.action.showPopularExtensions')),
when: 'workspacePlatform == \'webworker\'',
media: {
type: 'markdown', path: 'empty'
}
},
{
id: 'findLanguageExtensions',
title: localize('gettingStarted.findLanguageExts.title', "Rich support for all your languages"),
description: localize('gettingStarted.findLanguageExts.description.interpolated', "Code smarter with syntax highlighting, code completion, linting and debugging. While many languages are built-in, many more can be added as extensions.\n{0}", Button(localize('browseLangExts', "Browse Language Extensions"), 'command:workbench.extensions.action.showLanguageExtensions')),
when: 'workspacePlatform != \'webworker\'',
media: {
type: 'markdown', path: 'empty'
}
},
{
id: 'settings',
title: localize('gettingStarted.settings.title', "Tune your settings"),
description: localize('gettingStarted.settings.description.interpolated', "Customize every aspect of VS Code and your extensions to your liking. Commonly used settings are listed first to get you started.\n{0}", Button(localize('tweakSettings', "Open Settings"), 'command:toSide:workbench.action.openSettings')),
media: {
type: 'markdown', path: 'empty'
}
},
{
id: 'settingsSync',
title: localize('gettingStarted.settingsSync.title', "Sync settings across devices"),
description: localize('gettingStarted.settingsSync.description.interpolated', "Keep your essential customizations backed up and updated across all your devices.\n{0}", Button(localize('enableSync', "Backup and Sync Settings"), 'command:workbench.userDataSync.actions.turnOn')),
when: 'syncStatus != uninitialized',
completionEvents: ['onEvent:sync-enabled'],
media: {
type: 'markdown', path: 'empty'
}
},
{
id: 'commandPaletteTask',
title: localize('gettingStarted.commandPalette.title', "Unlock productivity with the Command Palette "),
description: localize('gettingStarted.commandPalette.description.interpolated', "Run commands without reaching for your mouse to accomplish any task in VS Code.\n{0}", Button(localize('commandPalette', "Open Command Palette"), 'command:workbench.action.showCommands')),
media: {
type: 'markdown', path: 'empty'
}
},
{
id: 'pickAFolderTask-Mac',
title: localize('gettingStarted.setup.OpenFolder.title', "Open up your code"),
description: localize('gettingStarted.setup.OpenFolder.description.interpolated', "You're all set to start coding. Open a project folder to get your files into VS Code.\n{0}", Button(localize('pickFolder', "Pick a Folder"), 'command:workbench.action.files.openFileFolder')),
when: 'isMac && workspaceFolderCount == 0',
media: {
type: 'markdown', path: 'empty'
}
},
{
id: 'pickAFolderTask-Other',
title: localize('gettingStarted.setup.OpenFolder.title', "Open up your code"),
description: localize('gettingStarted.setup.OpenFolder.description.interpolated', "You're all set to start coding. Open a project folder to get your files into VS Code.\n{0}", Button(localize('pickFolder', "Pick a Folder"), 'command:workbench.action.files.openFolder')),
when: '!isMac && workspaceFolderCount == 0',
media: {
type: 'markdown', path: 'empty'
}
},
{
id: 'quickOpen',
title: localize('gettingStarted.quickOpen.title', "Quickly navigate between your files"),
description: localize('gettingStarted.quickOpen.description.interpolated', "Navigate between files in an instant with one keystroke. Tip: Open multiple files by pressing the right arrow key.\n{0}", Button(localize('quickOpen', "Quick Open a File"), 'command:toSide:workbench.action.quickOpen')),
when: 'workspaceFolderCount != 0',
media: {
type: 'markdown', path: 'empty'
}
},
]
}
},
{
id: 'Beginner',
isFeatured: false,

View file

@ -28,6 +28,7 @@ import { NativeWindow } from 'vs/workbench/electron-sandbox/window';
import { ModifierKeyEmitter } from 'vs/base/browser/dom';
import { applicationConfigurationNodeBase, securityConfigurationNodeBase } from 'vs/workbench/common/configuration';
import { MAX_ZOOM_LEVEL, MIN_ZOOM_LEVEL } from 'vs/platform/window/electron-sandbox/window';
import product from 'vs/platform/product/common/product';
// Actions
(function registerActions(): void {
@ -239,7 +240,7 @@ import { MAX_ZOOM_LEVEL, MIN_ZOOM_LEVEL } from 'vs/platform/window/electron-sand
'type': 'boolean',
'included': isLinux,
'markdownDescription': localize('window.experimentalControlOverlay', "Show the native window controls when {0} is set to `custom` (Linux only).", '`#window.titleBarStyle#`'),
'default': true
'default': product.quality !== 'stable', // TODO@bpasero disable by default in stable for now (TODO@bpasero TODO@benibenj flip when custom title is default)
},
'window.customTitleBarVisibility': {
'type': 'string',

View file

@ -156,17 +156,17 @@ export class NativeTitlebarPart extends BrowserTitlebarPart {
})));
}
// Window Controls (Native Linux when WCO is disabled)
if (isLinux && !hasNativeTitlebar(this.configurationService) && !isWCOEnabled() && this.windowControlsContainer) {
// Window Controls (Native Windows/Linux)
if (!isMacintosh && !hasNativeTitlebar(this.configurationService) && !isWCOEnabled() && this.primaryWindowControls) {
// Minimize
const minimizeIcon = append(this.windowControlsContainer, $('div.window-icon.window-minimize' + ThemeIcon.asCSSSelector(Codicon.chromeMinimize)));
const minimizeIcon = append(this.primaryWindowControls, $('div.window-icon.window-minimize' + ThemeIcon.asCSSSelector(Codicon.chromeMinimize)));
this._register(addDisposableListener(minimizeIcon, EventType.CLICK, () => {
this.nativeHostService.minimizeWindow({ targetWindowId });
}));
// Restore
this.maxRestoreControl = append(this.windowControlsContainer, $('div.window-icon.window-max-restore'));
this.maxRestoreControl = append(this.primaryWindowControls, $('div.window-icon.window-max-restore'));
this._register(addDisposableListener(this.maxRestoreControl, EventType.CLICK, async () => {
const maximized = await this.nativeHostService.isMaximized({ targetWindowId });
if (maximized) {
@ -177,7 +177,7 @@ export class NativeTitlebarPart extends BrowserTitlebarPart {
}));
// Close
const closeIcon = append(this.windowControlsContainer, $('div.window-icon.window-close' + ThemeIcon.asCSSSelector(Codicon.chromeClose)));
const closeIcon = append(this.primaryWindowControls, $('div.window-icon.window-close' + ThemeIcon.asCSSSelector(Codicon.chromeClose)));
this._register(addDisposableListener(closeIcon, EventType.CLICK, () => {
this.nativeHostService.closeWindow({ targetWindowId });
}));

View file

@ -41,7 +41,7 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench
public readonly onEnablementChanged: Event<readonly IExtension[]> = this._onEnablementChanged.event;
protected readonly extensionsManager: ExtensionsManager;
private readonly storageManager: StorageManager;
private readonly storageManger: StorageManager;
constructor(
@IStorageService storageService: IStorageService,
@ -63,7 +63,7 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench
@IInstantiationService instantiationService: IInstantiationService,
) {
super();
this.storageManager = this._register(new StorageManager(storageService));
this.storageManger = this._register(new StorageManager(storageService));
const uninstallDisposable = this._register(Event.filter(extensionManagementService.onDidUninstallExtension, e => !e.error)(({ identifier }) => this._reset(identifier)));
let isDisposed = false;
@ -610,11 +610,11 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench
if (!this.hasWorkspace) {
return [];
}
return this.storageManager.get(storageId, StorageScope.WORKSPACE);
return this.storageManger.get(storageId, StorageScope.WORKSPACE);
}
private _setExtensions(storageId: string, extensions: IExtensionIdentifier[]): void {
this.storageManager.set(storageId, extensions, StorageScope.WORKSPACE);
this.storageManger.set(storageId, extensions, StorageScope.WORKSPACE);
}
private async _onDidChangeGloballyDisabledExtensions(extensionIdentifiers: ReadonlyArray<IExtensionIdentifier>, source?: string): Promise<void> {

View file

@ -13,8 +13,6 @@ import { URI } from 'vs/base/common/uri';
import { IFileMatch, IFileSearchProviderStats, IFolderQuery, ISearchCompleteStats, IFileQuery, QueryGlobTester, resolvePatternsForProvider, hasSiblingFn, excludeToGlobPattern, DEFAULT_MAX_SEARCH_RESULTS } from 'vs/workbench/services/search/common/search';
import { FileSearchProviderFolderOptions, FileSearchProviderNew, FileSearchProviderOptions } from 'vs/workbench/services/search/common/searchExtTypes';
import { TernarySearchTree } from 'vs/base/common/ternarySearchTree';
import { Disposable } from 'vs/base/common/lifecycle';
import { OldFileSearchProviderConverter } from 'vs/workbench/services/search/common/searchExtConversionTypes';
interface IInternalFileMatch {
base: URI;
@ -55,7 +53,7 @@ class FileSearchEngine {
private globalExcludePattern?: glob.ParsedExpression;
constructor(private config: IFileQuery, private provider: FileSearchProviderNew, private sessionLifecycle?: SessionLifecycle) {
constructor(private config: IFileQuery, private provider: FileSearchProviderNew, private sessionToken?: unknown) {
this.filePattern = config.filePattern;
this.includePattern = config.includePattern && glob.parse(config.includePattern);
this.maxResults = config.maxResults || undefined;
@ -118,11 +116,10 @@ class FileSearchEngine {
private async doSearch(fqs: IFolderQuery<URI>[], onResult: (match: IInternalFileMatch) => void): Promise<IFileSearchProviderStats | null> {
const cancellation = new CancellationTokenSource();
const folderOptions = fqs.map(fq => this.getSearchOptionsForFolder(fq));
const session = this.provider instanceof OldFileSearchProviderConverter ? this.sessionLifecycle?.tokenSource.token : this.sessionLifecycle?.obj;
const options: FileSearchProviderOptions = {
folderOptions,
maxResults: this.config.maxResults ?? DEFAULT_MAX_SEARCH_RESULTS,
session
session: this.sessionToken
};
@ -304,30 +301,11 @@ interface IInternalSearchComplete {
stats?: IFileSearchProviderStats;
}
/**
* For backwards compatibility, store both a cancellation token and a session object. The session object is the new implementation, where
*/
class SessionLifecycle extends Disposable {
public readonly obj: object;
public readonly tokenSource: CancellationTokenSource;
constructor() {
super();
this.obj = new Object();
this.tokenSource = new CancellationTokenSource();
}
public override dispose(): void {
this.tokenSource.cancel();
super.dispose();
}
}
export class FileSearchManager {
private static readonly BATCH_SIZE = 512;
private readonly sessions = new Map<string, SessionLifecycle>();
private readonly sessions = new Map<string, unknown>();
fileSearch(config: IFileQuery, provider: FileSearchProviderNew, onBatch: (matches: IFileMatch[]) => void, token: CancellationToken): Promise<ISearchCompleteStats> {
const sessionTokenSource = this.getSessionTokenSource(config.cacheKey);
@ -355,19 +333,17 @@ export class FileSearchManager {
}
clearCache(cacheKey: string): void {
// cancel the token
this.sessions.get(cacheKey)?.dispose();
// with no reference to this, it will be removed from WeakMaps
this.sessions.delete(cacheKey);
}
private getSessionTokenSource(cacheKey: string | undefined): SessionLifecycle | undefined {
private getSessionTokenSource(cacheKey: string | undefined): unknown {
if (!cacheKey) {
return undefined;
}
if (!this.sessions.has(cacheKey)) {
this.sessions.set(cacheKey, new SessionLifecycle());
this.sessions.set(cacheKey, new Object());
}
return this.sessions.get(cacheKey);

View file

@ -92,7 +92,6 @@ interface ICommonQueryBuilderOptions {
disregardSearchExcludeSettings?: boolean;
ignoreSymlinks?: boolean;
onlyOpenEditors?: boolean;
onlyFileScheme?: boolean;
}
export interface IFileQueryBuilderOptions extends ICommonQueryBuilderOptions {
@ -261,8 +260,7 @@ export class QueryBuilder {
excludePattern: excludeSearchPathsInfo.pattern,
includePattern: includeSearchPathsInfo.pattern,
onlyOpenEditors: options.onlyOpenEditors,
maxResults: options.maxResults,
onlyFileScheme: options.onlyFileScheme
maxResults: options.maxResults
};
if (options.onlyOpenEditors) {

View file

@ -100,7 +100,6 @@ export interface ICommonQueryProps<U extends UriComponents> {
maxResults?: number;
usingSearchPaths?: boolean;
onlyFileScheme?: boolean;
}
export interface IFileQueryProps<U extends UriComponents> extends ICommonQueryProps<U> {

View file

@ -271,9 +271,6 @@ export class SearchService extends Disposable implements ISearchService {
return [];
}
await Promise.all([...fqs.keys()].map(async scheme => {
if (query.onlyFileScheme && scheme !== Schemas.file) {
return;
}
const schemeFQs = fqs.get(scheme)!;
let provider = this.getSearchProvider(query.type).get(scheme);

View file

@ -143,7 +143,7 @@ export class TestNativeHostService implements INativeHostService {
async closeWindow(): Promise<void> { }
async quit(): Promise<void> { }
async exit(code: number): Promise<void> { }
async openDevTools(): Promise<void> { }
async openDevTools(options?: Partial<Electron.OpenDevToolsOptions> & INativeHostOptions | undefined): Promise<void> { }
async toggleDevTools(): Promise<void> { }
async resolveProxy(url: string): Promise<string | undefined> { return undefined; }
async lookupAuthorization(authInfo: AuthInfo): Promise<Credentials | undefined> { return undefined; }

View file

@ -3,6 +3,11 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
//#region --- void
import 'vs/workbench/contrib/void/browser/void.contribution'
//#endregion
//#region --- editor/workbench core
import 'vs/editor/editor.all';
@ -10,6 +15,7 @@ import 'vs/editor/editor.all';
import 'vs/workbench/api/browser/extensionHost.contribution';
import 'vs/workbench/browser/workbench.contribution';
//#endregion