mirror of
https://github.com/Microsoft/vscode
synced 2024-08-28 05:19:39 +00:00
Merge remote-tracking branch 'origin/main' into alexd/spiritual-bug
This commit is contained in:
commit
27eb6dfa41
|
@ -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> {
|
||||
|
|
|
@ -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))));
|
||||
|
|
|
@ -31,11 +31,21 @@ export const MENU_ESCAPED_MNEMONIC_REGEX = /(&)?(&)([^\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) {
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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()),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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': {
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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';
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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',
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue