Merge remote-tracking branch 'origin/main' into alexd/spiritual-bug

This commit is contained in:
Alex Dima 2024-03-26 17:06:07 +01:00
commit 27eb6dfa41
No known key found for this signature in database
GPG key ID: 4FA498B1FFF19E4D
25 changed files with 223 additions and 150 deletions

View file

@ -8,7 +8,7 @@ import * as path from 'path';
import { Repository, GitResourceGroup } from './repository';
import { Model } from './model';
import { debounce } from './decorators';
import { filterEvent, dispose, anyEvent, fireEvent, PromiseSource, combinedDisposable } from './util';
import { filterEvent, dispose, anyEvent, fireEvent, PromiseSource, combinedDisposable, runAndSubscribeEvent } from './util';
import { Change, GitErrorCodes, Status } from './api/git';
class GitIgnoreDecorationProvider implements FileDecorationProvider {
@ -101,7 +101,7 @@ class GitDecorationProvider implements FileDecorationProvider {
constructor(private repository: Repository) {
this.disposables.push(
window.registerFileDecorationProvider(this),
repository.onDidRunGitStatus(this.onDidRunGitStatus, this)
runAndSubscribeEvent(repository.onDidRunGitStatus, () => this.onDidRunGitStatus())
);
}
@ -162,8 +162,10 @@ class GitIncomingChangesFileDecorationProvider implements FileDecorationProvider
private readonly disposables: Disposable[] = [];
constructor(private readonly repository: Repository) {
this.disposables.push(window.registerFileDecorationProvider(this));
repository.historyProvider.onDidChangeCurrentHistoryItemGroup(this.onDidChangeCurrentHistoryItemGroup, this, this.disposables);
this.disposables.push(
window.registerFileDecorationProvider(this),
runAndSubscribeEvent(repository.historyProvider.onDidChangeCurrentHistoryItemGroup, () => this.onDidChangeCurrentHistoryItemGroup())
);
}
private async onDidChangeCurrentHistoryItemGroup(): Promise<void> {

View file

@ -47,6 +47,13 @@ export function filterEvent<T>(event: Event<T>, filter: (e: T) => boolean): Even
return (listener: (e: T) => any, thisArgs?: any, disposables?: Disposable[]) => event(e => filter(e) && listener.call(thisArgs, e), null, disposables);
}
export function runAndSubscribeEvent<T>(event: Event<T>, handler: (e: T) => any, initial: T): IDisposable;
export function runAndSubscribeEvent<T>(event: Event<T>, handler: (e: T | undefined) => any): IDisposable;
export function runAndSubscribeEvent<T>(event: Event<T>, handler: (e: T | undefined) => any, initial?: T): IDisposable {
handler(initial);
return event(e => handler(e));
}
export function anyEvent<T>(...events: Event<T>[]): Event<T> {
return (listener: (e: T) => any, thisArgs?: any, disposables?: Disposable[]) => {
const result = combinedDisposable(events.map(event => event(i => listener.call(thisArgs, i))));

View file

@ -31,11 +31,21 @@ export const MENU_ESCAPED_MNEMONIC_REGEX = /(&amp;)?(&amp;)([^\s&])/g;
export enum Direction {
export enum HorizontalDirection {
Right,
Left
}
export enum VerticalDirection {
Above,
Below
}
export interface IMenuDirection {
horizontal: HorizontalDirection;
vertical: VerticalDirection;
}
export interface IMenuOptions {
context?: unknown;
actionViewItemProvider?: IActionViewItemProvider;
@ -44,7 +54,7 @@ export interface IMenuOptions {
ariaLabel?: string;
enableMnemonics?: boolean;
anchorAlignment?: AnchorAlignment;
expandDirection?: Direction;
expandDirection?: IMenuDirection;
useEventAsContext?: boolean;
submenuIds?: Set<string>;
}
@ -725,7 +735,7 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem {
private mouseOver: boolean = false;
private showScheduler: RunOnceScheduler;
private hideScheduler: RunOnceScheduler;
private expandDirection: Direction;
private expandDirection: IMenuDirection;
constructor(
action: IAction,
@ -736,7 +746,7 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem {
) {
super(action, action, submenuOptions, menuStyles);
this.expandDirection = submenuOptions && submenuOptions.expandDirection !== undefined ? submenuOptions.expandDirection : Direction.Right;
this.expandDirection = submenuOptions && submenuOptions.expandDirection !== undefined ? submenuOptions.expandDirection : { horizontal: HorizontalDirection.Right, vertical: VerticalDirection.Below };
this.showScheduler = new RunOnceScheduler(() => {
if (this.mouseOver) {
@ -850,11 +860,11 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem {
}
}
private calculateSubmenuMenuLayout(windowDimensions: Dimension, submenu: Dimension, entry: IDomNodePagePosition, expandDirection: Direction): { top: number; left: number } {
private calculateSubmenuMenuLayout(windowDimensions: Dimension, submenu: Dimension, entry: IDomNodePagePosition, expandDirection: IMenuDirection): { top: number; left: number } {
const ret = { top: 0, left: 0 };
// Start with horizontal
ret.left = layout(windowDimensions.width, submenu.width, { position: expandDirection === Direction.Right ? LayoutAnchorPosition.Before : LayoutAnchorPosition.After, offset: entry.left, size: entry.width });
ret.left = layout(windowDimensions.width, submenu.width, { position: expandDirection.horizontal === HorizontalDirection.Right ? LayoutAnchorPosition.Before : LayoutAnchorPosition.After, offset: entry.left, size: entry.width });
// We don't have enough room to layout the menu fully, so we are overlapping the menu
if (ret.left >= entry.left && ret.left < entry.left + entry.width) {

View file

@ -8,7 +8,7 @@ import * as DOM from 'vs/base/browser/dom';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import { EventType, Gesture, GestureEvent } from 'vs/base/browser/touch';
import { cleanMnemonic, Direction, IMenuOptions, IMenuStyles, Menu, MENU_ESCAPED_MNEMONIC_REGEX, MENU_MNEMONIC_REGEX } from 'vs/base/browser/ui/menu/menu';
import { cleanMnemonic, HorizontalDirection, IMenuDirection, IMenuOptions, IMenuStyles, Menu, MENU_ESCAPED_MNEMONIC_REGEX, MENU_MNEMONIC_REGEX, VerticalDirection } from 'vs/base/browser/ui/menu/menu';
import { ActionRunner, IAction, IActionRunner, Separator, SubmenuAction } from 'vs/base/common/actions';
import { asArray } from 'vs/base/common/arrays';
import { RunOnceScheduler } from 'vs/base/common/async';
@ -32,7 +32,7 @@ export interface IMenuBarOptions {
visibility?: string;
getKeybinding?: (action: IAction) => ResolvedKeybinding | undefined;
alwaysOnMnemonics?: boolean;
compactMode?: Direction;
compactMode?: IMenuDirection;
actionRunner?: IActionRunner;
getCompactMenuActions?: () => IAction[];
}
@ -333,9 +333,9 @@ export class MenuBar extends Disposable {
} else {
triggerKeys.push(KeyCode.Space);
if (this.options.compactMode === Direction.Right) {
if (this.options.compactMode?.horizontal === HorizontalDirection.Right) {
triggerKeys.push(KeyCode.RightArrow);
} else if (this.options.compactMode === Direction.Left) {
} else if (this.options.compactMode?.horizontal === HorizontalDirection.Left) {
triggerKeys.push(KeyCode.LeftArrow);
}
}
@ -1007,18 +1007,25 @@ export class MenuBar extends Disposable {
const titleBoundingRect = customMenu.titleElement.getBoundingClientRect();
const titleBoundingRectZoom = DOM.getDomNodeZoomLevel(customMenu.titleElement);
if (this.options.compactMode === Direction.Right) {
menuHolder.style.top = `${titleBoundingRect.top}px`;
if (this.options.compactMode?.horizontal === HorizontalDirection.Right) {
menuHolder.style.left = `${titleBoundingRect.left + this.container.clientWidth}px`;
} else if (this.options.compactMode === Direction.Left) {
} else if (this.options.compactMode?.horizontal === HorizontalDirection.Left) {
menuHolder.style.top = `${titleBoundingRect.top}px`;
menuHolder.style.right = `${this.container.clientWidth}px`;
menuHolder.style.left = 'auto';
} else {
menuHolder.style.top = `${titleBoundingRect.bottom * titleBoundingRectZoom}px`;
menuHolder.style.left = `${titleBoundingRect.left * titleBoundingRectZoom}px`;
}
if (this.options.compactMode?.vertical === VerticalDirection.Above) {
// TODO@benibenj Do not hardcode the height of the menu holder
menuHolder.style.top = `${titleBoundingRect.top - this.menus.length * 30 + this.container.clientHeight}px`;
} else if (this.options.compactMode?.vertical === VerticalDirection.Below) {
menuHolder.style.top = `${titleBoundingRect.top}px`;
} else {
menuHolder.style.top = `${titleBoundingRect.bottom * titleBoundingRectZoom}px`;
}
customMenu.buttonElement.appendChild(menuHolder);
const menuOptions: IMenuOptions = {
@ -1026,7 +1033,7 @@ export class MenuBar extends Disposable {
actionRunner: this.actionRunner,
enableMnemonics: this.options.alwaysOnMnemonics || (this.mnemonicsInUse && this.options.enableMnemonics),
ariaLabel: customMenu.buttonElement.getAttribute('aria-label') ?? undefined,
expandDirection: this.isCompact ? this.options.compactMode : Direction.Right,
expandDirection: this.isCompact ? this.options.compactMode : { horizontal: HorizontalDirection.Right, vertical: VerticalDirection.Below },
useEventAsContext: true
};

View file

@ -84,7 +84,7 @@ export class DiffEditorGutter extends Disposable {
const currentDiff = this._currentDiff.read(reader);
return diffs.mappings.map(m => new DiffGutterItem(
m.lineRangeMapping,
m.lineRangeMapping.withInnerChangesFromLineRanges(),
m.lineRangeMapping === currentDiff?.lineRangeMapping,
MenuId.DiffEditorHunkToolbar,
undefined,

View file

@ -118,6 +118,12 @@ export class DetailedLineRangeMapping extends LineRangeMapping {
public override flip(): DetailedLineRangeMapping {
return new DetailedLineRangeMapping(this.modified, this.original, this.innerChanges?.map(c => c.flip()));
}
public withInnerChangesFromLineRanges(): DetailedLineRangeMapping {
return new DetailedLineRangeMapping(this.original, this.modified, [
new RangeMapping(this.original.toExclusiveRange(), this.modified.toExclusiveRange()),
]);
}
}
/**

View file

@ -14,7 +14,7 @@ import { IStorageService } from 'vs/platform/storage/common/storage';
import { contrastBorder } from 'vs/platform/theme/common/colorRegistry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { ActiveAuxiliaryContext, AuxiliaryBarFocusContext } from 'vs/workbench/common/contextkeys';
import { ACTIVITY_BAR_BADGE_BACKGROUND, ACTIVITY_BAR_BADGE_FOREGROUND, PANEL_ACTIVE_TITLE_BORDER, PANEL_ACTIVE_TITLE_FOREGROUND, PANEL_DRAG_AND_DROP_BORDER, PANEL_INACTIVE_TITLE_FOREGROUND, SIDE_BAR_BACKGROUND, SIDE_BAR_BORDER, SIDE_BAR_FOREGROUND } from 'vs/workbench/common/theme';
import { ACTIVITY_BAR_BADGE_BACKGROUND, ACTIVITY_BAR_BADGE_FOREGROUND, ACTIVITY_BAR_TOP_ACTIVE_BORDER, ACTIVITY_BAR_TOP_DRAG_AND_DROP_BORDER, ACTIVITY_BAR_TOP_FOREGROUND, ACTIVITY_BAR_TOP_INACTIVE_FOREGROUND, PANEL_ACTIVE_TITLE_BORDER, PANEL_ACTIVE_TITLE_FOREGROUND, PANEL_DRAG_AND_DROP_BORDER, PANEL_INACTIVE_TITLE_FOREGROUND, SIDE_BAR_BACKGROUND, SIDE_BAR_BORDER, SIDE_BAR_FOREGROUND } from 'vs/workbench/common/theme';
import { IViewDescriptorService } from 'vs/workbench/common/views';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { ActivityBarPosition, IWorkbenchLayoutService, LayoutSettings, Parts, Position } from 'vs/workbench/services/layout/browser/layoutService';
@ -169,12 +169,12 @@ export class AuxiliaryBarPart extends AbstractPaneCompositePart {
colors: theme => ({
activeBackgroundColor: theme.getColor(SIDE_BAR_BACKGROUND),
inactiveBackgroundColor: theme.getColor(SIDE_BAR_BACKGROUND),
activeBorderBottomColor: theme.getColor(PANEL_ACTIVE_TITLE_BORDER),
activeForegroundColor: theme.getColor(PANEL_ACTIVE_TITLE_FOREGROUND),
inactiveForegroundColor: theme.getColor(PANEL_INACTIVE_TITLE_FOREGROUND),
get activeBorderBottomColor() { return $this.getCompositeBarPosition() === CompositeBarPosition.TITLE ? theme.getColor(PANEL_ACTIVE_TITLE_BORDER) : theme.getColor(ACTIVITY_BAR_TOP_ACTIVE_BORDER); },
get activeForegroundColor() { return $this.getCompositeBarPosition() === CompositeBarPosition.TITLE ? theme.getColor(PANEL_ACTIVE_TITLE_FOREGROUND) : theme.getColor(ACTIVITY_BAR_TOP_FOREGROUND); },
get inactiveForegroundColor() { return $this.getCompositeBarPosition() === CompositeBarPosition.TITLE ? theme.getColor(PANEL_INACTIVE_TITLE_FOREGROUND) : theme.getColor(ACTIVITY_BAR_TOP_INACTIVE_FOREGROUND); },
badgeBackground: theme.getColor(ACTIVITY_BAR_BADGE_BACKGROUND),
badgeForeground: theme.getColor(ACTIVITY_BAR_BADGE_FOREGROUND),
dragAndDropBorder: theme.getColor(PANEL_DRAG_AND_DROP_BORDER)
get dragAndDropBorder() { return $this.getCompositeBarPosition() === CompositeBarPosition.TITLE ? theme.getColor(PANEL_DRAG_AND_DROP_BORDER) : theme.getColor(ACTIVITY_BAR_TOP_DRAG_AND_DROP_BORDER); }
}),
compact: true
};
@ -205,6 +205,7 @@ export class AuxiliaryBarPart extends AbstractPaneCompositePart {
return this.configurationService.getValue<ActivityBarPosition>(LayoutSettings.ACTIVITY_BAR_LOCATION) !== ActivityBarPosition.HIDDEN;
}
// TODO@benibenj chache this
protected getCompositeBarPosition(): CompositeBarPosition {
const activityBarPosition = this.configurationService.getValue<ActivityBarPosition>(LayoutSettings.ACTIVITY_BAR_LOCATION);
switch (activityBarPosition) {

View file

@ -51,20 +51,26 @@
left: 6px; /* place icon in center */
}
.monaco-workbench .part.auxiliarybar > .header-or-footer > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item.checked:not(:focus) .active-item-indicator:before,
.monaco-workbench .part.auxiliarybar > .header-or-footer > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item.checked.clicked:focus .active-item-indicator:before,
.monaco-workbench .part.auxiliarybar > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item.checked:not(:focus) .active-item-indicator:before,
.monaco-workbench .part.auxiliarybar > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item.checked.clicked:focus .active-item-indicator:before {
border-top-color: var(--vscode-panelTitle-activeBorder) !important;
}
.monaco-workbench .part.auxiliarybar > .header-or-footer > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item.checked:not(:focus) .active-item-indicator:before,
.monaco-workbench .part.auxiliarybar > .header-or-footer > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item.checked.clicked:focus .active-item-indicator:before {
border-top-color: var(--vscode-activityBarTop-activeBorder) !important;
}
.monaco-workbench .part.auxiliarybar > .header-or-footer > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item:hover .action-label,
.monaco-workbench .part.auxiliarybar > .header-or-footer > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item:focus .action-label,
.monaco-workbench .part.auxiliarybar > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item:hover .action-label,
.monaco-workbench .part.auxiliarybar > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item:focus .action-label {
color: var(--vscode-sideBarTitle-foreground) !important;
}
.monaco-workbench .part.auxiliarybar > .header-or-footer > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item:hover .action-label,
.monaco-workbench .part.auxiliarybar > .header-or-footer > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item:focus .action-label {
color: var(--vscode-activityBarTop-foreground) !important;
}
.monaco-workbench .part.auxiliarybar > .header-or-footer > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item.checked .action-label,
.monaco-workbench .part.auxiliarybar > .header-or-footer > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item:hover .action-label,
.monaco-workbench .part.auxiliarybar > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item.checked .action-label,

View file

@ -25,7 +25,7 @@ import { INotificationService, Severity } from 'vs/platform/notification/common/
import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { MenuBar, IMenuBarOptions } from 'vs/base/browser/ui/menu/menubar';
import { Direction } from 'vs/base/browser/ui/menu/menu';
import { HorizontalDirection, IMenuDirection, VerticalDirection } from 'vs/base/browser/ui/menu/menu';
import { mnemonicMenuLabel, unmnemonicLabel } from 'vs/base/common/labels';
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
import { isFullscreen, onDidChangeFullscreen } from 'vs/base/browser/browser';
@ -41,6 +41,7 @@ import { isICommandActionToggleInfo } from 'vs/platform/action/common/action';
import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
import { defaultMenuStyles } from 'vs/platform/theme/browser/defaultStyles';
import { mainWindow } from 'vs/base/browser/window';
import { ActivityBarPosition } from 'vs/workbench/services/layout/browser/layoutService';
export type IOpenRecentAction = IAction & { uri: URI; remoteAuthority?: string };
@ -536,14 +537,19 @@ export class CustomMenubarControl extends MenubarControl {
return enableMenuBarMnemonics && (!isWeb || isFullscreen(mainWindow));
}
private get currentCompactMenuMode(): Direction | undefined {
private get currentCompactMenuMode(): IMenuDirection | undefined {
if (this.currentMenubarVisibility !== 'compact') {
return undefined;
}
// Menu bar lives in activity bar and should flow based on its location
const currentSidebarLocation = this.configurationService.getValue<string>('workbench.sideBar.location');
return currentSidebarLocation === 'right' ? Direction.Left : Direction.Right;
const horizontalDirection = currentSidebarLocation === 'right' ? HorizontalDirection.Left : HorizontalDirection.Right;
const activityBarLocation = this.configurationService.getValue<string>('workbench.activityBar.location');
const verticalDirection = activityBarLocation === ActivityBarPosition.BOTTOM ? VerticalDirection.Above : VerticalDirection.Below;
return { horizontal: horizontalDirection, vertical: verticalDirection };
}
private onDidVisibilityChange(visible: boolean): void {

View file

@ -286,12 +286,12 @@ const registry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Con
},
'workbench.editor.enablePreviewFromQuickOpen': {
'type': 'boolean',
'markdownDescription': localize({ comment: ['{0}, {1} will be a setting name rendered as a link'], key: 'enablePreviewFromQuickOpen' }, "Controls whether editors opened from Quick Open show as preview editors. Preview editors do not stay open, and are reused until explicitly set to be kept open (via double-click or editing). When enabled, hold Ctrl before selection to open an editor as a non-preview. This value is ignored when {0} is not set to {1}.", '`#workbench.editor.enablePreview#`', '`multiple`'),
'markdownDescription': localize({ comment: ['{0}, {1} will be a setting name rendered as a link'], key: 'enablePreviewFromQuickOpen' }, "Controls whether editors opened from Quick Open show as preview editors. Preview editors do not stay open, and are reused until explicitly set to be kept open (via double-click or editing). When enabled, hold Ctrl before selection to open an editor as a non-preview. This value is ignored when {0} is not set to {1}.", '`#workbench.editor.showTabs#`', '`multiple`'),
'default': false
},
'workbench.editor.enablePreviewFromCodeNavigation': {
'type': 'boolean',
'markdownDescription': localize({ comment: ['{0}, {1} will be a setting name rendered as a link'], key: 'enablePreviewFromCodeNavigation' }, "Controls whether editors remain in preview when a code navigation is started from them. Preview editors do not stay open, and are reused until explicitly set to be kept open (via double-click or editing). This value is ignored when {0} is not set to {1}.", '`#workbench.editor.enablePreview#`', '`multiple`'),
'markdownDescription': localize({ comment: ['{0}, {1} will be a setting name rendered as a link'], key: 'enablePreviewFromCodeNavigation' }, "Controls whether editors remain in preview when a code navigation is started from them. Preview editors do not stay open, and are reused until explicitly set to be kept open (via double-click or editing). This value is ignored when {0} is not set to {1}.", '`#workbench.editor.showTabs#`', '`multiple`'),
'default': false
},
'workbench.editor.closeOnFileDelete': {

View file

@ -684,7 +684,7 @@ export class AccessibleView extends Disposable {
private _getAccessibleViewHelpDialogContent(providerHasSymbols?: boolean): string {
const navigationHint = this._getNavigationHint();
const goToSymbolHint = this._getGoToSymbolHint(providerHasSymbols);
const toolbarHint = localize('toolbar', "Navigate to the toolbar (Shift+Tab)).");
const toolbarHint = localize('toolbar', "Navigate to the toolbar (Shift+Tab).");
const chatHints = this._getChatHints();
let hint = localize('intro', "In the accessible view, you can:\n");
@ -720,7 +720,7 @@ export class AccessibleView extends Disposable {
if (insertIntoNewFileKb) {
hint += localize('insertIntoNewFile', " - Insert the code block into a new file ({0}).\n", insertIntoNewFileKb);
} else {
hint += localize('insertIntoNewFileNoKb', " - Insert the code block into a new file by configuring a keybinding for the Chat: Insert at Cursor command.\n");
hint += localize('insertIntoNewFileNoKb', " - Insert the code block into a new file by configuring a keybinding for the Chat: Insert into New File command.\n");
}
if (runInTerminalKb) {
hint += localize('runInTerminal', " - Run the code block in the terminal ({0}).\n", runInTerminalKb);
@ -761,7 +761,7 @@ export class AccessibleView extends Disposable {
let goToSymbolHint = '';
if (providerHasSymbols) {
if (goToSymbolKb) {
goToSymbolHint = localize('goToSymbolHint', 'Go to a symbol ({0})', goToSymbolKb);
goToSymbolHint = localize('goToSymbolHint', 'Go to a symbol ({0}).', goToSymbolKb);
} else {
goToSymbolHint = localize('goToSymbolHintNoKb', 'To go to a symbol, configure a keybinding for the command Go To Symbol in Accessible View');
}

View file

@ -326,7 +326,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge
// Only allow history navigation when the input is empty.
// (If this model change happened as a result of a history navigation, this is canceled out by a call in this.navigateHistory)
const model = this._inputEditor.getModel();
const inputHasText = !!model && model.getValueLength() > 0;
const inputHasText = !!model && model.getValue().trim().length > 0;
this.inputEditorHasText.set(inputHasText);
// If the user is typing on a history entry, then reset the onHistoryEntry flag so that history navigation can be disabled
@ -372,9 +372,11 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge
},
hiddenItemStrategy: HiddenItemStrategy.Ignore, // keep it lean when hiding items and avoid a "..." overflow menu
actionViewItemProvider: (action, options) => {
if ((action.id === SubmitAction.ID || action.id === CancelAction.ID) && action instanceof MenuItemAction) {
const dropdownAction = this.instantiationService.createInstance(MenuItemAction, { id: 'chat.moreExecuteActions', title: localize('notebook.moreExecuteActionsLabel', "More..."), icon: Codicon.chevronDown }, undefined, undefined, undefined);
return this.instantiationService.createInstance(ChatSubmitDropdownActionItem, action, dropdownAction);
if (this.location === ChatAgentLocation.Panel) {
if ((action.id === SubmitAction.ID || action.id === CancelAction.ID) && action instanceof MenuItemAction) {
const dropdownAction = this.instantiationService.createInstance(MenuItemAction, { id: 'chat.moreExecuteActions', title: localize('notebook.moreExecuteActionsLabel', "More..."), icon: Codicon.chevronDown }, undefined, undefined, undefined);
return this.instantiationService.createInstance(ChatSubmitDropdownActionItem, action, dropdownAction);
}
}
return undefined;

View file

@ -6,9 +6,7 @@
/*
* Replace with "microphone" icon.
*/
.monaco-workbench .interactive-input-part .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled)::before,
.monaco-workbench .inline-chat .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled)::before,
.monaco-workbench .terminal-inline-chat .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled)::before {
.monaco-workbench .interactive-input-part .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled)::before {
content: "\ec1c";
font-family: 'codicon';
}
@ -16,18 +14,14 @@
/*
* Clear animation styles when reduced motion is enabled.
*/
.monaco-workbench.reduce-motion .interactive-input-part .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled),
.monaco-workbench.reduce-motion .inline-chat .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled),
.monaco-workbench.reduce-motion .terminal-inline-chat .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled) {
.monaco-workbench.reduce-motion .interactive-input-part .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled) {
animation: none;
}
/*
* Replace with "stop" icon when reduced motion is enabled.
*/
.monaco-workbench.reduce-motion .interactive-input-part .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled)::before,
.monaco-workbench.reduce-motion .inline-chat .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled)::before,
.monaco-workbench.reduce-motion .terminal-inline-chat .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled)::before {
.monaco-workbench.reduce-motion .interactive-input-part .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled)::before {
content: "\ead7";
font-family: 'codicon';
}

View file

@ -104,6 +104,15 @@ class VoiceChatSessionControllerFactory {
// Currently Focused Context
if (context === 'focused') {
// Try with the terminal chat
const activeInstance = terminalService.activeInstance;
if (activeInstance) {
const terminalChat = TerminalChatController.activeChatWidget || TerminalChatController.get(activeInstance);
if (terminalChat?.hasFocus()) {
return VoiceChatSessionControllerFactory.doCreateForTerminalChat(terminalChat);
}
}
// Try with the chat widget service, which currently
// only supports the chat view and quick chat
// https://github.com/microsoft/vscode/issues/191191
@ -134,15 +143,6 @@ class VoiceChatSessionControllerFactory {
return VoiceChatSessionControllerFactory.doCreateForInlineChat(inlineChat);
}
}
// Try with the terminal chat
const activeInstance = terminalService.activeInstance;
if (activeInstance) {
const terminalChat = TerminalChatController.activeChatWidget || TerminalChatController.get(activeInstance);
if (terminalChat?.hasFocus()) {
return VoiceChatSessionControllerFactory.doCreateForTerminalChat(terminalChat);
}
}
}
// View Chat
@ -699,7 +699,6 @@ class BaseStopListeningAction extends Action2 {
private readonly target: 'inline' | 'terminal' | 'quick' | 'view' | 'editor' | undefined,
context: RawContextKey<boolean>,
menu: MenuId | undefined,
group: 'navigation' | 'main' = 'navigation'
) {
super({
...desc,
@ -713,7 +712,7 @@ class BaseStopListeningAction extends Action2 {
menu: menu ? [{
id: menu,
when: ContextKeyExpr.and(CanVoiceChat, context),
group,
group: 'navigation',
order: -1
}] : undefined
});
@ -765,7 +764,7 @@ export class StopListeningInTerminalChatAction extends BaseStopListeningAction {
static readonly ID = 'workbench.action.chat.stopListeningInTerminalChat';
constructor() {
super({ id: StopListeningInTerminalChatAction.ID, icon: spinningLoading }, 'terminal', CONTEXT_TERMINAL_VOICE_CHAT_IN_PROGRESS, MenuId.for('terminalChatInput'), 'main');
super({ id: StopListeningInTerminalChatAction.ID, icon: spinningLoading }, 'terminal', CONTEXT_TERMINAL_VOICE_CHAT_IN_PROGRESS, MenuId.for('terminalChatInput'));
}
}
@ -806,9 +805,7 @@ registerThemingParticipant((theme, collector) => {
// Show a "microphone" icon when recording is in progress that glows via outline.
collector.addRule(`
.monaco-workbench:not(.reduce-motion) .interactive-input-part .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled),
.monaco-workbench:not(.reduce-motion) .inline-chat .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled),
.monaco-workbench:not(.reduce-motion) .terminal-inline-chat .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled) {
.monaco-workbench:not(.reduce-motion) .interactive-input-part .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled) {
color: ${activeRecordingColor};
outline: 1px solid ${activeRecordingColor};
outline-offset: -1px;
@ -816,9 +813,7 @@ registerThemingParticipant((theme, collector) => {
border-radius: 50%;
}
.monaco-workbench:not(.reduce-motion) .interactive-input-part .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled)::before,
.monaco-workbench:not(.reduce-motion) .inline-chat .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled)::before,
.monaco-workbench:not(.reduce-motion) .terminal-inline-chat .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled)::before {
.monaco-workbench:not(.reduce-motion) .interactive-input-part .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled)::before {
position: absolute;
outline: 1px solid ${activeRecordingColor};
outline-offset: 2px;
@ -827,16 +822,13 @@ registerThemingParticipant((theme, collector) => {
height: 16px;
}
.monaco-workbench:not(.reduce-motion) .interactive-input-part .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled)::after,
.monaco-workbench:not(.reduce-motion) .inline-chat .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled),
.monaco-workbench:not(.reduce-motion) .terminal-inline-chat .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled)::after {
.monaco-workbench:not(.reduce-motion) .interactive-input-part .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled)::after {
outline: 2px solid ${activeRecordingColor};
outline-offset: -1px;
animation: pulseAnimation 1500ms cubic-bezier(0.75, 0, 0.25, 1) infinite;
}
.monaco-workbench:not(.reduce-motion) .interactive-input-part .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled)::before,
.monaco-workbench:not(.reduce-motion) .inline-chat .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled)::before {
.monaco-workbench:not(.reduce-motion) .interactive-input-part .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled)::before {
position: absolute;
outline: 1px solid ${activeRecordingColor};
outline-offset: 2px;

View file

@ -20,6 +20,8 @@ import { ChatAgentLocation } from 'vs/workbench/contrib/chat/common/chatAgents';
import { editorBackground, editorForeground, inputBackground } from 'vs/platform/theme/common/colorRegistry';
import { ChatModel } from 'vs/workbench/contrib/chat/common/chatModel';
import { Range } from 'vs/editor/common/core/range';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
export class InlineChatContentWidget implements IContentWidget {
@ -45,11 +47,19 @@ export class InlineChatContentWidget implements IContentWidget {
constructor(
private readonly _editor: ICodeEditor,
@IInstantiationService instaService: IInstantiationService,
@IContextKeyService contextKeyService: IContextKeyService,
) {
this._defaultChatModel = this._store.add(instaService.createInstance(ChatModel, `inlineChatDefaultModel/editorContentWidgetPlaceholder`, undefined));
this._widget = instaService.createInstance(
const scopedInstaService = instaService.createChild(
new ServiceCollection([
IContextKeyService,
this._store.add(contextKeyService.createScoped(this._domNode))
])
);
this._widget = scopedInstaService.createInstance(
ChatWidget,
ChatAgentLocation.Editor,
{ resource: true },

View file

@ -606,7 +606,7 @@ export class InlineChatController implements IEditorContribution {
}
return false;
});
if (refer && slashCommandLike) {
if (refer && slashCommandLike && !this._session.lastExchange) {
this._log('[IE] seeing refer command, continuing outside editor', this._session.provider.extensionId);
// cancel this request
@ -627,11 +627,7 @@ export class InlineChatController implements IEditorContribution {
// if agent has a refer command, massage the input to include the agent name
await this._instaService.invokeFunction(sendRequest, massagedInput);
if (!this._session.lastExchange) {
// DONE when there wasn't any exchange yet. We used the inline chat only as trampoline
return State.ACCEPT;
}
return State.WAIT_FOR_INPUT;
return State.ACCEPT;
}
this._session.addInput(new SessionPrompt(input, this._nextAttempt, this._nextWithIntentDetection));

View file

@ -54,6 +54,7 @@ import { SuggestController } from 'vs/editor/contrib/suggest/browser/suggestCont
import { IChatService } from 'vs/workbench/contrib/chat/common/chatService';
import { setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget';
import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
export interface InlineChatWidgetViewState {
@ -174,7 +175,16 @@ export class InlineChatWidget {
this._store.add(this._progressBar);
let allowRequests = false;
this._chatWidget = _instantiationService.createInstance(
const scopedInstaService = _instantiationService.createChild(
new ServiceCollection([
IContextKeyService,
this._store.add(_contextKeyService.createScoped(this._elements.chatWidget))
])
);
this._chatWidget = scopedInstaService.createInstance(
ChatWidget,
location,
{ resource: true },
@ -365,16 +375,24 @@ export class InlineChatWidget {
get contentHeight(): number {
const data = {
followUpsHeight: getTotalHeight(this._elements.followUps),
chatWidgetHeight: this._chatWidget.contentHeight,
chatWidgetContentHeight: this._chatWidget.contentHeight,
progressHeight: getTotalHeight(this._elements.progress),
statusHeight: getTotalHeight(this._elements.status),
extraHeight: this._getExtraHeight()
};
const result = data.progressHeight + data.chatWidgetHeight + data.followUpsHeight + data.statusHeight + data.extraHeight;
// console.log(`InlineChat#contentHeight ${result}`, data);
const result = data.progressHeight + data.chatWidgetContentHeight + data.followUpsHeight + data.statusHeight + data.extraHeight;
return result;
}
get minHeight(): number {
// The chat widget is variable height and supports scrolling. It
// should be at least 100px high and at most the content height.
let value = this.contentHeight;
value -= this._chatWidget.contentHeight;
value += Math.min(100, this._chatWidget.contentHeight);
return value;
}
protected _getExtraHeight(): number {
return 12 /* padding */ + 2 /*border*/ + 12 /*shadow*/;
}
@ -535,9 +553,10 @@ export class InlineChatWidget {
const isTempMessage = typeof ops.resetAfter === 'number';
if (isTempMessage && !this._elements.statusLabel.dataset['state']) {
const statusLabel = this._elements.statusLabel.innerText;
const title = this._elements.statusLabel.dataset['title'];
const classes = Array.from(this._elements.statusLabel.classList.values());
setTimeout(() => {
this.updateStatus(statusLabel, { classes, keepMessage: true });
this.updateStatus(statusLabel, { classes, keepMessage: true, title });
}, ops.resetAfter);
}
const renderedMessage = renderLabelWithIcons(message);
@ -550,7 +569,11 @@ export class InlineChatWidget {
delete this._elements.statusLabel.dataset['state'];
}
this._elements.statusLabel.dataset['title'] = ops.title;
if (ops.title) {
this._elements.statusLabel.dataset['title'] = ops.title;
} else {
delete this._elements.statusLabel.dataset['title'];
}
this._onDidChangeHeight.fire();
}

View file

@ -114,9 +114,8 @@ export class InlineChatZoneWidget extends ZoneWidget {
const chatContentHeight = this.widget.contentHeight;
const editorHeight = this.editor.getLayoutInfo().height;
const contentHeight = Math.min(chatContentHeight, editorHeight * 0.42);
const contentHeight = Math.min(chatContentHeight, Math.max(this.widget.minHeight, editorHeight * 0.42));
const heightInLines = contentHeight / this.editor.getOption(EditorOption.lineHeight);
// console.log('ZONE#_computeHeightInLines', { chatContentHeight, editorHeight, contentHeight, heightInLines });
return heightInLines;
}

View file

@ -7,6 +7,10 @@
z-index: 3;
}
.monaco-workbench .zone-widget.inline-chat-widget .interactive-session {
max-width: unset;
}
.monaco-workbench .zone-widget-container.inside-selection {
background-color: var(--vscode-inlineChat-regionHighlight);
}

View file

@ -38,14 +38,16 @@ export class CellDiagnostics extends Disposable {
) {
super();
this.updateEnabled();
if (cell.viewType !== 'interactive') {
this.updateEnabled();
this._register(inlineChatService.onDidChangeProviders(() => this.updateEnabled()));
this._register(configurationService.onDidChangeConfiguration((e) => {
if (e.affectsConfiguration(NotebookSetting.cellFailureDiagnostics)) {
this.updateEnabled();
}
}));
this._register(inlineChatService.onDidChangeProviders(() => this.updateEnabled()));
this._register(configurationService.onDidChangeConfiguration((e) => {
if (e.affectsConfiguration(NotebookSetting.cellFailureDiagnostics)) {
this.updateEnabled();
}
}));
}
}
private updateEnabled() {
@ -103,4 +105,9 @@ export class CellDiagnostics extends Disposable {
};
}
override dispose() {
super.dispose();
this.clear();
}
}

View file

@ -91,7 +91,7 @@ registerActiveXtermAction({
return;
}
const contr = TerminalChatController.activeChatWidget || TerminalChatController.get(activeInstance);
contr?.chatWidget?.focusResponse();
contr?.chatWidget?.inlineChatWidget.chatWidget.focusLastMessage();
}
});
@ -100,7 +100,8 @@ registerActiveXtermAction({
title: localize2('focusTerminalInput', 'Focus Terminal Input'),
keybinding: {
primary: KeyMod.CtrlCmd | KeyCode.UpArrow,
when: TerminalChatContextKeys.focused,
secondary: [KeyMod.CtrlCmd | KeyCode.KeyI],
when: ContextKeyExpr.and(TerminalChatContextKeys.focused, CTX_INLINE_CHAT_FOCUSED.toNegated()),
weight: KeybindingWeight.WorkbenchContrib,
},
f1: true,

View file

@ -261,6 +261,7 @@ export class TerminalChatController extends Disposable implements ITerminalContr
throw new Error('Could not start chat session');
}
}
this._messages.fire(Message.ACCEPT_INPUT);
const model = this._model.value;
this._lastInput = this._chatWidget?.value?.input();
@ -326,7 +327,6 @@ export class TerminalChatController extends Disposable implements ITerminalContr
this._chatWidget?.value.inlineChatWidget.updateChatMessage({ message: new MarkdownString(responseContent), requestId: this._currentRequest.id, providerId: 'terminal' }, false, containsCode);
this._responseContainsCodeBlockContextKey.set(containsCode);
this._chatWidget?.value.inlineChatWidget.updateToolbar(true);
this._messages.fire(Message.ACCEPT_INPUT);
}
const supportIssueReporting = this._currentRequest?.response?.agent?.metadata?.supportIssueReporting;
if (supportIssueReporting !== undefined) {

View file

@ -158,12 +158,6 @@ export class TerminalChatWidget extends Disposable {
focus(): void {
this._inlineChatWidget.focus();
}
focusResponse(): void {
const responseElement = this._inlineChatWidget.domNode.querySelector(ChatElementSelectors.ResponseEditor) || this._inlineChatWidget.domNode.querySelector(ChatElementSelectors.ResponseMessage);
if (responseElement instanceof HTMLElement) {
responseElement.focus();
}
}
hasFocus(): boolean {
return this._inlineChatWidget.hasFocus();
}
@ -190,7 +184,3 @@ export class TerminalChatWidget extends Disposable {
}
}
const enum ChatElementSelectors {
ResponseEditor = '.chatMessageContent textarea',
ResponseMessage = '.chatMessageContent',
}

View file

@ -581,40 +581,46 @@ export class SimpleFileDialog implements ISimpleFileDialog {
valueUri = this.root(this.currentFolder);
value = this.pathFromUri(valueUri);
return await this.updateItems(valueUri, true) ? UpdateResult.UpdatedWithTrailing : UpdateResult.Updated;
} else if (!resources.extUriIgnorePathCase.isEqual(this.currentFolder, valueUri) && (this.endsWithSlash(value) || (!resources.extUriIgnorePathCase.isEqual(this.currentFolder, resources.dirname(valueUri)) && resources.extUriIgnorePathCase.isEqualOrParent(this.currentFolder, resources.dirname(valueUri))))) {
let stat: IFileStatWithPartialMetadata | undefined;
try {
stat = await this.fileService.stat(valueUri);
} catch (e) {
// do nothing
}
if (stat && stat.isDirectory && (resources.basename(valueUri) !== '.') && this.endsWithSlash(value)) {
valueUri = this.tryAddTrailingSeparatorToDirectory(valueUri, stat);
return await this.updateItems(valueUri) ? UpdateResult.UpdatedWithTrailing : UpdateResult.Updated;
} else if (this.endsWithSlash(value)) {
// The input box contains a path that doesn't exist on the system.
this.filePickBox.validationMessage = nls.localize('remoteFileDialog.badPath', 'The path does not exist.');
// Save this bad path. It can take too long to a stat on every user entered character, but once a user enters a bad path they are likely
// to keep typing more bad path. We can compare against this bad path and see if the user entered path starts with it.
this.badPath = value;
return UpdateResult.InvalidPath;
} else {
let inputUriDirname = resources.dirname(valueUri);
const currentFolderWithoutSep = resources.removeTrailingPathSeparator(resources.addTrailingPathSeparator(this.currentFolder));
const inputUriDirnameWithoutSep = resources.removeTrailingPathSeparator(resources.addTrailingPathSeparator(inputUriDirname));
if (!resources.extUriIgnorePathCase.isEqual(currentFolderWithoutSep, inputUriDirnameWithoutSep)
&& (!/^[a-zA-Z]:$/.test(this.filePickBox.value)
|| !equalsIgnoreCase(this.pathFromUri(this.currentFolder).substring(0, this.filePickBox.value.length), this.filePickBox.value))) {
let statWithoutTrailing: IFileStatWithPartialMetadata | undefined;
try {
statWithoutTrailing = await this.fileService.stat(inputUriDirname);
} catch (e) {
// do nothing
}
if (statWithoutTrailing && statWithoutTrailing.isDirectory) {
this.badPath = undefined;
inputUriDirname = this.tryAddTrailingSeparatorToDirectory(inputUriDirname, statWithoutTrailing);
return await this.updateItems(inputUriDirname, false, resources.basename(valueUri)) ? UpdateResult.UpdatedWithTrailing : UpdateResult.Updated;
} else {
const newFolderIsOldFolder = resources.extUriIgnorePathCase.isEqual(this.currentFolder, valueUri);
const newFolderIsSubFolder = resources.extUriIgnorePathCase.isEqual(this.currentFolder, resources.dirname(valueUri));
const newFolderIsParent = !newFolderIsOldFolder && resources.extUriIgnorePathCase.isEqualOrParent(this.currentFolder, resources.dirname(valueUri));
const newFolderIsUnrelated = !newFolderIsOldFolder && !newFolderIsParent && !newFolderIsSubFolder;
if (this.endsWithSlash(value) || newFolderIsParent || newFolderIsUnrelated) {
let stat: IFileStatWithPartialMetadata | undefined;
try {
stat = await this.fileService.stat(valueUri);
} catch (e) {
// do nothing
}
if (stat && stat.isDirectory && (resources.basename(valueUri) !== '.') && this.endsWithSlash(value)) {
valueUri = this.tryAddTrailingSeparatorToDirectory(valueUri, stat);
return await this.updateItems(valueUri) ? UpdateResult.UpdatedWithTrailing : UpdateResult.Updated;
} else if (this.endsWithSlash(value)) {
// The input box contains a path that doesn't exist on the system.
this.filePickBox.validationMessage = nls.localize('remoteFileDialog.badPath', 'The path does not exist.');
// Save this bad path. It can take too long to a stat on every user entered character, but once a user enters a bad path they are likely
// to keep typing more bad path. We can compare against this bad path and see if the user entered path starts with it.
this.badPath = value;
return UpdateResult.InvalidPath;
} else {
let inputUriDirname = resources.dirname(valueUri);
const currentFolderWithoutSep = resources.removeTrailingPathSeparator(resources.addTrailingPathSeparator(this.currentFolder));
const inputUriDirnameWithoutSep = resources.removeTrailingPathSeparator(resources.addTrailingPathSeparator(inputUriDirname));
if (!resources.extUriIgnorePathCase.isEqual(currentFolderWithoutSep, inputUriDirnameWithoutSep)
&& (!/^[a-zA-Z]:$/.test(this.filePickBox.value)
|| !equalsIgnoreCase(this.pathFromUri(this.currentFolder).substring(0, this.filePickBox.value.length), this.filePickBox.value))) {
let statWithoutTrailing: IFileStatWithPartialMetadata | undefined;
try {
statWithoutTrailing = await this.fileService.stat(inputUriDirname);
} catch (e) {
// do nothing
}
if (statWithoutTrailing && statWithoutTrailing.isDirectory) {
this.badPath = undefined;
inputUriDirname = this.tryAddTrailingSeparatorToDirectory(inputUriDirname, statWithoutTrailing);
return await this.updateItems(inputUriDirname, false, resources.basename(valueUri)) ? UpdateResult.UpdatedWithTrailing : UpdateResult.Updated;
}
}
}
}

View file

@ -12,7 +12,7 @@ declare module 'vscode' {
/**
* The {@link Terminal} the command was executed in.
*/
terminal: Terminal;
readonly terminal: Terminal;
/**
* The full command line that was executed, including both the command and arguments.
@ -26,7 +26,7 @@ declare module 'vscode' {
* - It may be inaccurate if the shell integration does not support command line reporting
* via the [`OSC 633 ; E` sequence](https://code.visualstudio.com/docs/terminal/shell-integration#_vs-code-custom-sequences-osc-633-st).
*/
commandLine: string | undefined;
readonly commandLine: string | undefined;
/**
* The working directory that was reported by the shell when this command executed. This
@ -35,12 +35,12 @@ declare module 'vscode' {
* reporting via the [`OSC 633 ; P`](https://code.visualstudio.com/docs/terminal/shell-integration#_vs-code-custom-sequences-osc-633-st)
* or `OSC 1337 ; CurrentDir=<Cwd> ST` sequences.
*/
cwd: Uri | string | undefined;
readonly cwd: Uri | string | undefined;
/**
* The exit code reported by the shell.
*/
exitCode: Thenable<number | undefined>;
readonly exitCode: Thenable<number | undefined>;
/**
* Creates a stream of raw data (including escape sequences) that is written to the
@ -65,8 +65,12 @@ declare module 'vscode' {
* features for the terminal. This will always be undefined immediately after the terminal
* is created. Listen to {@link window.onDidActivateTerminalShellIntegration} to be notified
* when shell integration is activated for a terminal.
*
* Note that this object may remain undefined if shell integation never activates. For
* example Command Prompt does not support shell integration and a user's shell setup could
* conflict with the automatic shell integration activation.
*/
shellIntegration: TerminalShellIntegration | undefined;
readonly shellIntegration: TerminalShellIntegration | undefined;
}
export interface TerminalShellIntegration {
@ -75,7 +79,7 @@ declare module 'vscode' {
* The current working directory of the terminal. This will be a {@link Uri} if the string
* reported by the shell can reliably be mapped to the connected machine.
*/
cwd: Uri | string | undefined;
readonly cwd: Uri | string | undefined;
/**
* Execute a command, sending ^C as necessary to interrupt any running command if needed.
@ -177,11 +181,11 @@ declare module 'vscode' {
/**
* The terminal that shell integration has been activated in.
*/
terminal: Terminal;
readonly terminal: Terminal;
/**
* The shell integration object.
*/
shellIntegration: TerminalShellIntegration;
readonly shellIntegration: TerminalShellIntegration;
}
export namespace window {