mirror of
https://github.com/voideditor/void
synced 2026-05-24 09:58:23 +00:00
Merge pull request #29 from quant-eagle/fix/codelens_imports
fix: update VS source to more recent version
This commit is contained in:
commit
d3fd53974e
85 changed files with 1032 additions and 575 deletions
|
|
@ -136,6 +136,6 @@ export function isWCOEnabled(): boolean {
|
||||||
|
|
||||||
// Returns the bounding rect of the titlebar area if it is supported and defined
|
// Returns the bounding rect of the titlebar area if it is supported and defined
|
||||||
// See docs at https://developer.mozilla.org/en-US/docs/Web/API/WindowControlsOverlay/getTitlebarAreaRect
|
// See docs at https://developer.mozilla.org/en-US/docs/Web/API/WindowControlsOverlay/getTitlebarAreaRect
|
||||||
export function getWCOBoundingRect(): DOMRect | undefined {
|
export function getWCOTitlebarAreaRect(targetWindow: Window): DOMRect | undefined {
|
||||||
return (navigator as any)?.windowControlsOverlay?.getTitlebarAreaRect();
|
return (targetWindow.navigator as any)?.windowControlsOverlay?.getTitlebarAreaRect();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import { $ } from 'vs/base/browser/dom';
|
||||||
import { IHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate';
|
import { IHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate';
|
||||||
import { Button } from 'vs/base/browser/ui/button/button';
|
import { Button } from 'vs/base/browser/ui/button/button';
|
||||||
import { DisposableMap, DisposableStore } from 'vs/base/common/lifecycle';
|
import { DisposableMap, DisposableStore } from 'vs/base/common/lifecycle';
|
||||||
import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory';
|
import { createInstantHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory';
|
||||||
|
|
||||||
export interface IRadioStyles {
|
export interface IRadioStyles {
|
||||||
readonly activeForeground?: string;
|
readonly activeForeground?: string;
|
||||||
|
|
@ -53,7 +53,7 @@ export class Radio extends Widget {
|
||||||
constructor(opts: IRadioOptions) {
|
constructor(opts: IRadioOptions) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.hoverDelegate = opts.hoverDelegate ?? getDefaultHoverDelegate('element');
|
this.hoverDelegate = opts.hoverDelegate ?? this._register(createInstantHoverDelegate());
|
||||||
|
|
||||||
this.domNode = $('.monaco-custom-radio');
|
this.domNode = $('.monaco-custom-radio');
|
||||||
this.domNode.setAttribute('role', 'radio');
|
this.domNode.setAttribute('role', 'radio');
|
||||||
|
|
|
||||||
|
|
@ -217,24 +217,6 @@ export interface FileFilter {
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OpenDevToolsOptions {
|
|
||||||
/**
|
|
||||||
* Opens the devtools with specified dock state, can be `left`, `right`, `bottom`,
|
|
||||||
* `undocked`, `detach`. Defaults to last used dock state. In `undocked` mode it's
|
|
||||||
* possible to dock back. In `detach` mode it's not.
|
|
||||||
*/
|
|
||||||
mode: ('left' | 'right' | 'bottom' | 'undocked' | 'detach');
|
|
||||||
/**
|
|
||||||
* Whether to bring the opened devtools window to the foreground. The default is
|
|
||||||
* `true`.
|
|
||||||
*/
|
|
||||||
activate?: boolean;
|
|
||||||
/**
|
|
||||||
* A title for the DevTools window (only in `undocked` or `detach` mode).
|
|
||||||
*/
|
|
||||||
title?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface InputEvent {
|
interface InputEvent {
|
||||||
|
|
||||||
// Docs: https://electronjs.org/docs/api/structures/input-event
|
// Docs: https://electronjs.org/docs/api/structures/input-event
|
||||||
|
|
|
||||||
|
|
@ -222,7 +222,7 @@ export class HoverWidget extends Widget implements IHoverWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show the hover hint if needed
|
// Show the hover hint if needed
|
||||||
if (hideOnHover && options.appearance?.showHoverHint) {
|
if (options.appearance?.showHoverHint) {
|
||||||
const statusBarElement = $('div.hover-row.status-bar');
|
const statusBarElement = $('div.hover-row.status-bar');
|
||||||
const infoElement = $('div.info');
|
const infoElement = $('div.info');
|
||||||
infoElement.textContent = localize('hoverhint', 'Hold {0} key to mouse over', isMacintosh ? 'Option' : 'Alt');
|
infoElement.textContent = localize('hoverhint', 'Hold {0} key to mouse over', isMacintosh ? 'Option' : 'Alt');
|
||||||
|
|
|
||||||
|
|
@ -2019,9 +2019,9 @@ export interface CommentThreadChangedEvent<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CodeLens {
|
export interface CodeLens {
|
||||||
range: IRange; // the range of code
|
range: IRange;
|
||||||
id?: string; // no idea what this is for
|
id?: string;
|
||||||
command?: Command; // command to run when they click the codeLens
|
command?: Command;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CodeLensList {
|
export interface CodeLensList {
|
||||||
|
|
|
||||||
|
|
@ -180,6 +180,12 @@ export class CodeActionController extends Disposable implements IEditorContribut
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const selection = this._editor.getSelection();
|
||||||
|
if (selection?.startLineNumber !== newState.position.lineNumber) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this._lightBulbWidget.value?.update(actions, newState.trigger, newState.position);
|
this._lightBulbWidget.value?.update(actions, newState.trigger, newState.position);
|
||||||
|
|
||||||
if (newState.trigger.type === CodeActionTriggerType.Invoke) {
|
if (newState.trigger.type === CodeActionTriggerType.Invoke) {
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,14 @@ export class LightBulbWidget extends Disposable implements IContentWidget {
|
||||||
private _gutterState: LightBulbState.State = LightBulbState.Hidden;
|
private _gutterState: LightBulbState.State = LightBulbState.Hidden;
|
||||||
private _iconClasses: string[] = [];
|
private _iconClasses: string[] = [];
|
||||||
|
|
||||||
|
private readonly lightbulbClasses = [
|
||||||
|
'codicon-' + GUTTER_LIGHTBULB_ICON.id,
|
||||||
|
'codicon-' + GUTTER_LIGHTBULB_AIFIX_AUTO_FIX_ICON.id,
|
||||||
|
'codicon-' + GUTTER_LIGHTBULB_AUTO_FIX_ICON.id,
|
||||||
|
'codicon-' + GUTTER_LIGHTBULB_AIFIX_ICON.id,
|
||||||
|
'codicon-' + GUTTER_SPARKLE_FILLED_ICON.id
|
||||||
|
];
|
||||||
|
|
||||||
private _preferredKbLabel?: string;
|
private _preferredKbLabel?: string;
|
||||||
private _quickFixKbLabel?: string;
|
private _quickFixKbLabel?: string;
|
||||||
|
|
||||||
|
|
@ -148,15 +156,8 @@ export class LightBulbWidget extends Disposable implements IContentWidget {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this._register(this._editor.onMouseDown(async (e: IEditorMouseEvent) => {
|
this._register(this._editor.onMouseDown(async (e: IEditorMouseEvent) => {
|
||||||
const lightbulbClasses = [
|
|
||||||
'codicon-' + GUTTER_LIGHTBULB_ICON.id,
|
|
||||||
'codicon-' + GUTTER_LIGHTBULB_AIFIX_AUTO_FIX_ICON.id,
|
|
||||||
'codicon-' + GUTTER_LIGHTBULB_AUTO_FIX_ICON.id,
|
|
||||||
'codicon-' + GUTTER_LIGHTBULB_AIFIX_ICON.id,
|
|
||||||
'codicon-' + GUTTER_SPARKLE_FILLED_ICON.id
|
|
||||||
];
|
|
||||||
|
|
||||||
if (!e.target.element || !lightbulbClasses.some(cls => e.target.element && e.target.element.classList.contains(cls))) {
|
if (!e.target.element || !this.lightbulbClasses.some(cls => e.target.element && e.target.element.classList.contains(cls))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -247,7 +248,9 @@ export class LightBulbWidget extends Disposable implements IContentWidget {
|
||||||
let hasDecoration = false;
|
let hasDecoration = false;
|
||||||
if (currLineDecorations) {
|
if (currLineDecorations) {
|
||||||
for (const decoration of currLineDecorations) {
|
for (const decoration of currLineDecorations) {
|
||||||
if (decoration.options.glyphMarginClassName) {
|
const glyphClass = decoration.options.glyphMarginClassName;
|
||||||
|
|
||||||
|
if (glyphClass && !this.lightbulbClasses.some(className => glyphClass.includes(className))) {
|
||||||
hasDecoration = true;
|
hasDecoration = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -271,7 +274,6 @@ export class LightBulbWidget extends Disposable implements IContentWidget {
|
||||||
const currLineEmptyOrIndented = isLineEmptyOrIndented(lineNumber);
|
const currLineEmptyOrIndented = isLineEmptyOrIndented(lineNumber);
|
||||||
const notEmpty = !nextLineEmptyOrIndented && !prevLineEmptyOrIndented;
|
const notEmpty = !nextLineEmptyOrIndented && !prevLineEmptyOrIndented;
|
||||||
|
|
||||||
|
|
||||||
// check above and below. if both are blocked, display lightbulb in the gutter.
|
// check above and below. if both are blocked, display lightbulb in the gutter.
|
||||||
if (!nextLineEmptyOrIndented && !prevLineEmptyOrIndented && !hasDecoration) {
|
if (!nextLineEmptyOrIndented && !prevLineEmptyOrIndented && !hasDecoration) {
|
||||||
this.gutterState = new LightBulbState.Showing(actions, trigger, atPosition, {
|
this.gutterState = new LightBulbState.Showing(actions, trigger, atPosition, {
|
||||||
|
|
@ -280,7 +282,7 @@ export class LightBulbWidget extends Disposable implements IContentWidget {
|
||||||
});
|
});
|
||||||
this.renderGutterLightbub();
|
this.renderGutterLightbub();
|
||||||
return this.hide();
|
return this.hide();
|
||||||
} else if (prevLineEmptyOrIndented || endLine || (notEmpty && !currLineEmptyOrIndented)) {
|
} else if (prevLineEmptyOrIndented || endLine || (prevLineEmptyOrIndented && !currLineEmptyOrIndented)) {
|
||||||
effectiveLineNumber -= 1;
|
effectiveLineNumber -= 1;
|
||||||
} else if (nextLineEmptyOrIndented || (notEmpty && currLineEmptyOrIndented)) {
|
} else if (nextLineEmptyOrIndented || (notEmpty && currLineEmptyOrIndented)) {
|
||||||
effectiveLineNumber += 1;
|
effectiveLineNumber += 1;
|
||||||
|
|
|
||||||
|
|
@ -3,17 +3,17 @@
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { Event } from '../../../../base/common/event.js';
|
import { Event } from 'vs/base/common/event';
|
||||||
import { LRUCache } from '../../../../base/common/map.js';
|
import { LRUCache } from 'vs/base/common/map';
|
||||||
import { Range } from '../../../common/core/range.js';
|
import { Range } from 'vs/editor/common/core/range';
|
||||||
import { ITextModel } from '../../../common/model.js';
|
import { ITextModel } from 'vs/editor/common/model';
|
||||||
import { CodeLens, CodeLensList, CodeLensProvider } from '../../../common/languages.js';
|
import { CodeLens, CodeLensList, CodeLensProvider } from 'vs/editor/common/languages';
|
||||||
import { CodeLensModel } from './codelens.js';
|
import { CodeLensModel } from 'vs/editor/contrib/codelens/browser/codelens';
|
||||||
import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';
|
import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||||
import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
|
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||||
import { IStorageService, StorageScope, StorageTarget, WillSaveStateReason } from '../../../../platform/storage/common/storage.js';
|
import { IStorageService, StorageScope, StorageTarget, WillSaveStateReason } from 'vs/platform/storage/common/storage';
|
||||||
import { mainWindow } from '../../../../base/browser/window.js';
|
import { mainWindow } from 'vs/base/browser/window';
|
||||||
import { runWhenWindowIdle } from '../../../../base/browser/dom.js';
|
import { runWhenWindowIdle } from 'vs/base/browser/dom';
|
||||||
|
|
||||||
export const ICodeLensCache = createDecorator<ICodeLensCache>('ICodeLensCache');
|
export const ICodeLensCache = createDecorator<ICodeLensCache>('ICodeLensCache');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,17 +3,17 @@
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { CancellationToken } from '../../../../base/common/cancellation.js';
|
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||||
import { illegalArgument, onUnexpectedExternalError } from '../../../../base/common/errors.js';
|
import { illegalArgument, onUnexpectedExternalError } from 'vs/base/common/errors';
|
||||||
import { DisposableStore } from '../../../../base/common/lifecycle.js';
|
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||||
import { assertType } from '../../../../base/common/types.js';
|
import { assertType } from 'vs/base/common/types';
|
||||||
import { URI } from '../../../../base/common/uri.js';
|
import { URI } from 'vs/base/common/uri';
|
||||||
import { ITextModel } from '../../../common/model.js';
|
import { ITextModel } from 'vs/editor/common/model';
|
||||||
import { CodeLens, CodeLensList, CodeLensProvider } from '../../../common/languages.js';
|
import { CodeLens, CodeLensList, CodeLensProvider } from 'vs/editor/common/languages';
|
||||||
import { IModelService } from '../../../common/services/model.js';
|
import { IModelService } from 'vs/editor/common/services/model';
|
||||||
import { CommandsRegistry } from '../../../../platform/commands/common/commands.js';
|
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||||
import { LanguageFeatureRegistry } from '../../../common/languageFeatureRegistry.js';
|
import { LanguageFeatureRegistry } from 'vs/editor/common/languageFeatureRegistry';
|
||||||
import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js';
|
import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures';
|
||||||
|
|
||||||
export interface CodeLensItem {
|
export interface CodeLensItem {
|
||||||
symbol: CodeLens;
|
symbol: CodeLens;
|
||||||
|
|
|
||||||
|
|
@ -4,26 +4,26 @@
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
|
||||||
import { CancelablePromise, createCancelablePromise, disposableTimeout, RunOnceScheduler } from '../../../../base/common/async.js';
|
import { CancelablePromise, createCancelablePromise, disposableTimeout, RunOnceScheduler } from 'vs/base/common/async';
|
||||||
import { onUnexpectedError, onUnexpectedExternalError } from '../../../../base/common/errors.js';
|
import { onUnexpectedError, onUnexpectedExternalError } from 'vs/base/common/errors';
|
||||||
import { DisposableStore, toDisposable } from '../../../../base/common/lifecycle.js';
|
import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
|
||||||
import { StableEditorScrollState } from '../../../browser/stableEditorScroll.js';
|
import { StableEditorScrollState } from 'vs/editor/browser/stableEditorScroll';
|
||||||
import { IActiveCodeEditor, ICodeEditor, IViewZoneChangeAccessor, MouseTargetType } from '../../../browser/editorBrowser.js';
|
import { IActiveCodeEditor, ICodeEditor, IViewZoneChangeAccessor, MouseTargetType } from 'vs/editor/browser/editorBrowser';
|
||||||
import { EditorAction, EditorContributionInstantiation, registerEditorAction, registerEditorContribution, ServicesAccessor } from '../../../browser/editorExtensions.js';
|
import { EditorAction, EditorContributionInstantiation, registerEditorAction, registerEditorContribution, ServicesAccessor } from 'vs/editor/browser/editorExtensions';
|
||||||
import { EditorOption, EDITOR_FONT_DEFAULTS } from '../../../common/config/editorOptions.js';
|
import { EditorOption, EDITOR_FONT_DEFAULTS } from 'vs/editor/common/config/editorOptions';
|
||||||
import { IEditorContribution } from '../../../common/editorCommon.js';
|
import { IEditorContribution } from 'vs/editor/common/editorCommon';
|
||||||
import { EditorContextKeys } from '../../../common/editorContextKeys.js';
|
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||||
import { IModelDecorationsChangeAccessor } from '../../../common/model.js';
|
import { IModelDecorationsChangeAccessor } from 'vs/editor/common/model';
|
||||||
import { CodeLens, Command } from '../../../common/languages.js';
|
import { CodeLens, Command } from 'vs/editor/common/languages';
|
||||||
import { CodeLensItem, CodeLensModel, getCodeLensModel } from './codelens.js';
|
import { CodeLensItem, CodeLensModel, getCodeLensModel } from 'vs/editor/contrib/codelens/browser/codelens';
|
||||||
import { ICodeLensCache } from './codeLensCache.js';
|
import { ICodeLensCache } from 'vs/editor/contrib/codelens/browser/codeLensCache';
|
||||||
import { CodeLensHelper, CodeLensWidget } from './codelensWidget.js';
|
import { CodeLensHelper, CodeLensWidget } from 'vs/editor/contrib/codelens/browser/codelensWidget';
|
||||||
import { localize } from '../../../../nls.js';
|
import { localize } from 'vs/nls';
|
||||||
import { ICommandService } from '../../../../platform/commands/common/commands.js';
|
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||||
import { INotificationService } from '../../../../platform/notification/common/notification.js';
|
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||||
import { IQuickInputService } from '../../../../platform/quickinput/common/quickInput.js';
|
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
||||||
import { IFeatureDebounceInformation, ILanguageFeatureDebounceService } from '../../../common/services/languageFeatureDebounce.js';
|
import { IFeatureDebounceInformation, ILanguageFeatureDebounceService } from 'vs/editor/common/services/languageFeatureDebounce';
|
||||||
import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js';
|
import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures';
|
||||||
|
|
||||||
export class CodeLensContribution implements IEditorContribution {
|
export class CodeLensContribution implements IEditorContribution {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,16 @@
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as dom from '../../../../base/browser/dom.js';
|
import * as dom from 'vs/base/browser/dom';
|
||||||
import { renderLabelWithIcons } from '../../../../base/browser/ui/iconLabel/iconLabels.js';
|
import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels';
|
||||||
import { Constants } from '../../../../base/common/uint.js';
|
import { Constants } from 'vs/base/common/uint';
|
||||||
import './codelensWidget.css';
|
import 'vs/css!./codelensWidget';
|
||||||
import { ContentWidgetPositionPreference, IActiveCodeEditor, IContentWidget, IContentWidgetPosition, IViewZone, IViewZoneChangeAccessor } from '../../../browser/editorBrowser.js';
|
import { ContentWidgetPositionPreference, IActiveCodeEditor, IContentWidget, IContentWidgetPosition, IViewZone, IViewZoneChangeAccessor } from 'vs/editor/browser/editorBrowser';
|
||||||
import { Range } from '../../../common/core/range.js';
|
import { Range } from 'vs/editor/common/core/range';
|
||||||
import { IModelDecorationsChangeAccessor, IModelDeltaDecoration, ITextModel } from '../../../common/model.js';
|
import { IModelDecorationsChangeAccessor, IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model';
|
||||||
import { ModelDecorationOptions } from '../../../common/model/textModel.js';
|
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
|
||||||
import { CodeLens, Command } from '../../../common/languages.js';
|
import { CodeLens, Command } from 'vs/editor/common/languages';
|
||||||
import { CodeLensItem } from './codelens.js';
|
import { CodeLensItem } from 'vs/editor/contrib/codelens/browser/codelens';
|
||||||
|
|
||||||
class CodeLensViewZone implements IViewZone {
|
class CodeLensViewZone implements IViewZone {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -318,7 +318,8 @@ export class RenameWidget implements IRenameWidget, IContentWidget, IDisposable
|
||||||
}
|
}
|
||||||
|
|
||||||
afterRender(position: ContentWidgetPositionPreference | null): void {
|
afterRender(position: ContentWidgetPositionPreference | null): void {
|
||||||
this._trace('invoking afterRender, position: ', position ? 'not null' : 'null');
|
// FIXME@ulugbekna: commenting trace log out until we start unmounting the widget from editor properly - https://github.com/microsoft/vscode/issues/226975
|
||||||
|
// this._trace('invoking afterRender, position: ', position ? 'not null' : 'null');
|
||||||
if (position === null) {
|
if (position === null) {
|
||||||
// cancel rename when input widget isn't rendered anymore
|
// cancel rename when input widget isn't rendered anymore
|
||||||
this.cancelInput(true, 'afterRender (because position is null)');
|
this.cancelInput(true, 'afterRender (because position is null)');
|
||||||
|
|
@ -363,7 +364,7 @@ export class RenameWidget implements IRenameWidget, IContentWidget, IDisposable
|
||||||
}
|
}
|
||||||
|
|
||||||
cancelInput(focusEditor: boolean, caller: string): void {
|
cancelInput(focusEditor: boolean, caller: string): void {
|
||||||
this._trace(`invoking cancelInput, caller: ${caller}, _currentCancelInput: ${this._currentAcceptInput ? 'not undefined' : 'undefined'}`);
|
// this._trace(`invoking cancelInput, caller: ${caller}, _currentCancelInput: ${this._currentAcceptInput ? 'not undefined' : 'undefined'}`);
|
||||||
this._currentCancelInput?.(focusEditor);
|
this._currentCancelInput?.(focusEditor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -166,8 +166,8 @@ export class ActionList<T> extends Disposable {
|
||||||
|
|
||||||
private readonly _list: List<IActionListItem<T>>;
|
private readonly _list: List<IActionListItem<T>>;
|
||||||
|
|
||||||
private readonly _actionLineHeight = 28;
|
private readonly _actionLineHeight = 24;
|
||||||
private readonly _headerLineHeight = 28;
|
private readonly _headerLineHeight = 26;
|
||||||
|
|
||||||
private readonly _allMenuItems: readonly IActionListItem<T>[];
|
private readonly _allMenuItems: readonly IActionListItem<T>[];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -132,8 +132,9 @@
|
||||||
/* Action bar */
|
/* Action bar */
|
||||||
|
|
||||||
.action-widget .action-widget-action-bar {
|
.action-widget .action-widget-action-bar {
|
||||||
background-color: var(--vscode-editorHoverWidget-statusBarBackground);
|
background-color: var(--vscode-editorActionList-background);
|
||||||
border-top: 1px solid var(--vscode-editorHoverWidget-border);
|
border-top: 1px solid var(--vscode-editorHoverWidget-border);
|
||||||
|
margin-top: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-widget .action-widget-action-bar::before {
|
.action-widget .action-widget-action-bar::before {
|
||||||
|
|
@ -143,7 +144,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-widget .action-widget-action-bar .actions-container {
|
.action-widget .action-widget-action-bar .actions-container {
|
||||||
padding: 0 8px;
|
padding: 3px 8px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-widget-action-bar .action-label {
|
.action-widget-action-bar .action-label {
|
||||||
|
|
|
||||||
|
|
@ -16,15 +16,15 @@ export class GlobalExtensionEnablementService extends Disposable implements IGlo
|
||||||
|
|
||||||
private _onDidChangeEnablement = new Emitter<{ readonly extensions: IExtensionIdentifier[]; readonly source?: string }>();
|
private _onDidChangeEnablement = new Emitter<{ readonly extensions: IExtensionIdentifier[]; readonly source?: string }>();
|
||||||
readonly onDidChangeEnablement: Event<{ readonly extensions: IExtensionIdentifier[]; readonly source?: string }> = this._onDidChangeEnablement.event;
|
readonly onDidChangeEnablement: Event<{ readonly extensions: IExtensionIdentifier[]; readonly source?: string }> = this._onDidChangeEnablement.event;
|
||||||
private readonly storageManger: StorageManager;
|
private readonly storageManager: StorageManager;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@IStorageService storageService: IStorageService,
|
@IStorageService storageService: IStorageService,
|
||||||
@IExtensionManagementService extensionManagementService: IExtensionManagementService,
|
@IExtensionManagementService extensionManagementService: IExtensionManagementService,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this.storageManger = this._register(new StorageManager(storageService));
|
this.storageManager = this._register(new StorageManager(storageService));
|
||||||
this._register(this.storageManger.onDidChange(extensions => this._onDidChangeEnablement.fire({ extensions, source: 'storage' })));
|
this._register(this.storageManager.onDidChange(extensions => this._onDidChangeEnablement.fire({ extensions, source: 'storage' })));
|
||||||
this._register(extensionManagementService.onDidInstallExtensions(e => e.forEach(({ local, operation }) => {
|
this._register(extensionManagementService.onDidInstallExtensions(e => e.forEach(({ local, operation }) => {
|
||||||
if (local && operation === InstallOperation.Migrate) {
|
if (local && operation === InstallOperation.Migrate) {
|
||||||
this._removeFromDisabledExtensions(local.identifier); /* Reset migrated extensions */
|
this._removeFromDisabledExtensions(local.identifier); /* Reset migrated extensions */
|
||||||
|
|
@ -84,11 +84,11 @@ export class GlobalExtensionEnablementService extends Disposable implements IGlo
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getExtensions(storageId: string): IExtensionIdentifier[] {
|
private _getExtensions(storageId: string): IExtensionIdentifier[] {
|
||||||
return this.storageManger.get(storageId, StorageScope.PROFILE);
|
return this.storageManager.get(storageId, StorageScope.PROFILE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _setExtensions(storageId: string, extensions: IExtensionIdentifier[]): void {
|
private _setExtensions(storageId: string, extensions: IExtensionIdentifier[]): void {
|
||||||
this.storageManger.set(storageId, extensions, StorageScope.PROFILE);
|
this.storageManager.set(storageId, extensions, StorageScope.PROFILE);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
import { VSBuffer } from 'vs/base/common/buffer';
|
import { VSBuffer } from 'vs/base/common/buffer';
|
||||||
import { Event } from 'vs/base/common/event';
|
import { Event } from 'vs/base/common/event';
|
||||||
import { URI } from 'vs/base/common/uri';
|
import { URI } from 'vs/base/common/uri';
|
||||||
import { MessageBoxOptions, MessageBoxReturnValue, OpenDevToolsOptions, OpenDialogOptions, OpenDialogReturnValue, SaveDialogOptions, SaveDialogReturnValue } from 'vs/base/parts/sandbox/common/electronTypes';
|
import { MessageBoxOptions, MessageBoxReturnValue, OpenDialogOptions, OpenDialogReturnValue, SaveDialogOptions, SaveDialogReturnValue } from 'vs/base/parts/sandbox/common/electronTypes';
|
||||||
import { ISerializableCommandAction } from 'vs/platform/action/common/action';
|
import { ISerializableCommandAction } from 'vs/platform/action/common/action';
|
||||||
import { INativeOpenDialogOptions } from 'vs/platform/dialogs/common/dialogs';
|
import { INativeOpenDialogOptions } from 'vs/platform/dialogs/common/dialogs';
|
||||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||||
|
|
@ -178,7 +178,7 @@ export interface ICommonNativeHostService {
|
||||||
exit(code: number): Promise<void>;
|
exit(code: number): Promise<void>;
|
||||||
|
|
||||||
// Development
|
// Development
|
||||||
openDevTools(options?: Partial<OpenDevToolsOptions> & INativeHostOptions): Promise<void>;
|
openDevTools(options?: INativeHostOptions): Promise<void>;
|
||||||
toggleDevTools(options?: INativeHostOptions): Promise<void>;
|
toggleDevTools(options?: INativeHostOptions): Promise<void>;
|
||||||
|
|
||||||
// Perf Introspection
|
// Perf Introspection
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import { exec } from 'child_process';
|
import { exec } from 'child_process';
|
||||||
import { app, BrowserWindow, clipboard, Display, Menu, MessageBoxOptions, MessageBoxReturnValue, OpenDevToolsOptions, OpenDialogOptions, OpenDialogReturnValue, powerMonitor, SaveDialogOptions, SaveDialogReturnValue, screen, shell, webContents } from 'electron';
|
import { app, BrowserWindow, clipboard, Display, Menu, MessageBoxOptions, MessageBoxReturnValue, OpenDialogOptions, OpenDialogReturnValue, powerMonitor, SaveDialogOptions, SaveDialogReturnValue, screen, shell, webContents } from 'electron';
|
||||||
import { arch, cpus, freemem, loadavg, platform, release, totalmem, type } from 'os';
|
import { arch, cpus, freemem, loadavg, platform, release, totalmem, type } from 'os';
|
||||||
import { promisify } from 'util';
|
import { promisify } from 'util';
|
||||||
import { memoize } from 'vs/base/common/decorators';
|
import { memoize } from 'vs/base/common/decorators';
|
||||||
|
|
@ -33,7 +33,7 @@ import { IProductService } from 'vs/platform/product/common/productService';
|
||||||
import { IPartsSplash } from 'vs/platform/theme/common/themeService';
|
import { IPartsSplash } from 'vs/platform/theme/common/themeService';
|
||||||
import { IThemeMainService } from 'vs/platform/theme/electron-main/themeMainService';
|
import { IThemeMainService } from 'vs/platform/theme/electron-main/themeMainService';
|
||||||
import { ICodeWindow } from 'vs/platform/window/electron-main/window';
|
import { ICodeWindow } from 'vs/platform/window/electron-main/window';
|
||||||
import { IColorScheme, IOpenedAuxiliaryWindow, IOpenedMainWindow, IOpenEmptyWindowOptions, IOpenWindowOptions, IPoint, IRectangle, IWindowOpenable } from 'vs/platform/window/common/window';
|
import { IColorScheme, IOpenedAuxiliaryWindow, IOpenedMainWindow, IOpenEmptyWindowOptions, IOpenWindowOptions, IPoint, IRectangle, IWindowOpenable, useWindowControlsOverlay } from 'vs/platform/window/common/window';
|
||||||
import { IWindowsMainService, OpenContext } from 'vs/platform/windows/electron-main/windows';
|
import { IWindowsMainService, OpenContext } from 'vs/platform/windows/electron-main/windows';
|
||||||
import { isWorkspaceIdentifier, toWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace';
|
import { isWorkspaceIdentifier, toWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace';
|
||||||
import { IWorkspacesManagementMainService } from 'vs/platform/workspaces/electron-main/workspacesManagementMainService';
|
import { IWorkspacesManagementMainService } from 'vs/platform/workspaces/electron-main/workspacesManagementMainService';
|
||||||
|
|
@ -855,14 +855,28 @@ export class NativeHostMainService extends Disposable implements INativeHostMain
|
||||||
|
|
||||||
//#region Development
|
//#region Development
|
||||||
|
|
||||||
async openDevTools(windowId: number | undefined, options?: Partial<OpenDevToolsOptions> & INativeHostOptions): Promise<void> {
|
async openDevTools(windowId: number | undefined, options?: INativeHostOptions): Promise<void> {
|
||||||
const window = this.windowById(options?.targetWindowId, windowId);
|
const window = this.windowById(options?.targetWindowId, windowId);
|
||||||
window?.win?.webContents.openDevTools(options?.mode ? { mode: options.mode, activate: options.activate } : undefined);
|
|
||||||
|
let mode: 'bottom' | undefined = undefined;
|
||||||
|
if (isLinux && useWindowControlsOverlay(this.configurationService)) {
|
||||||
|
mode = 'bottom'; // TODO@bpasero WCO and devtools collide with default option 'right'
|
||||||
|
}
|
||||||
|
window?.win?.webContents.openDevTools(mode ? { mode } : undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
async toggleDevTools(windowId: number | undefined, options?: INativeHostOptions): Promise<void> {
|
async toggleDevTools(windowId: number | undefined, options?: INativeHostOptions): Promise<void> {
|
||||||
const window = this.windowById(options?.targetWindowId, windowId);
|
const window = this.windowById(options?.targetWindowId, windowId);
|
||||||
window?.win?.webContents.toggleDevTools();
|
const webContents = window?.win?.webContents;
|
||||||
|
if (!webContents) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLinux && useWindowControlsOverlay(this.configurationService) && !webContents.isDevToolsOpened()) {
|
||||||
|
webContents.openDevTools({ mode: 'bottom' }); // TODO@bpasero WCO and devtools collide with default option 'right'
|
||||||
|
} else {
|
||||||
|
webContents.toggleDevTools();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
|
||||||
|
|
@ -159,7 +159,7 @@ class ColorRegistry implements IColorRegistry {
|
||||||
public registerColor(id: string, defaults: ColorDefaults | ColorValue | null, description: string, needsTransparency = false, deprecationMessage?: string): ColorIdentifier {
|
public registerColor(id: string, defaults: ColorDefaults | ColorValue | null, description: string, needsTransparency = false, deprecationMessage?: string): ColorIdentifier {
|
||||||
const colorContribution: ColorContribution = { id, description, defaults, needsTransparency, deprecationMessage };
|
const colorContribution: ColorContribution = { id, description, defaults, needsTransparency, deprecationMessage };
|
||||||
this.colorsById[id] = colorContribution;
|
this.colorsById[id] = colorContribution;
|
||||||
const propertySchema: IJSONSchemaWithSnippets = { type: 'string', description, format: 'color-hex', defaultSnippets: [{ body: '${1:#ff0000}' }] };
|
const propertySchema: IJSONSchemaWithSnippets = { type: 'string', format: 'color-hex', defaultSnippets: [{ body: '${1:#ff0000}' }] };
|
||||||
if (deprecationMessage) {
|
if (deprecationMessage) {
|
||||||
propertySchema.deprecationMessage = deprecationMessage;
|
propertySchema.deprecationMessage = deprecationMessage;
|
||||||
}
|
}
|
||||||
|
|
@ -168,6 +168,7 @@ class ColorRegistry implements IColorRegistry {
|
||||||
propertySchema.patternErrorMessage = nls.localize('transparecyRequired', 'This color must be transparent or it will obscure content');
|
propertySchema.patternErrorMessage = nls.localize('transparecyRequired', 'This color must be transparent or it will obscure content');
|
||||||
}
|
}
|
||||||
this.colorSchema.properties[id] = {
|
this.colorSchema.properties[id] = {
|
||||||
|
description,
|
||||||
oneOf: [
|
oneOf: [
|
||||||
propertySchema,
|
propertySchema,
|
||||||
{ type: 'string', const: DEFAULT_COLOR_CONFIG_VALUE, description: nls.localize('useDefault', 'Use the default color.') }
|
{ type: 'string', const: DEFAULT_COLOR_CONFIG_VALUE, description: nls.localize('useDefault', 'Use the default color.') }
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ import { NativeParsedArgs } from 'vs/platform/environment/common/argv';
|
||||||
import { FileType } from 'vs/platform/files/common/files';
|
import { FileType } from 'vs/platform/files/common/files';
|
||||||
import { ILoggerResource, LogLevel } from 'vs/platform/log/common/log';
|
import { ILoggerResource, LogLevel } from 'vs/platform/log/common/log';
|
||||||
import { PolicyDefinition, PolicyValue } from 'vs/platform/policy/common/policy';
|
import { PolicyDefinition, PolicyValue } from 'vs/platform/policy/common/policy';
|
||||||
import product from 'vs/platform/product/common/product';
|
|
||||||
import { IPartsSplash } from 'vs/platform/theme/common/themeService';
|
import { IPartsSplash } from 'vs/platform/theme/common/themeService';
|
||||||
import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile';
|
import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile';
|
||||||
import { IAnyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace';
|
import { IAnyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace';
|
||||||
|
|
@ -240,8 +239,6 @@ export function useWindowControlsOverlay(configurationService: IConfigurationSer
|
||||||
if (typeof setting === 'boolean') {
|
if (typeof setting === 'boolean') {
|
||||||
return setting;
|
return setting;
|
||||||
}
|
}
|
||||||
|
|
||||||
return product.quality !== 'stable'; // disable by default in stable for now (TODO@bpasero TODO@benibenj flip when custom title is default)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default to true.
|
// Default to true.
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ import { IEditSessionIdentityService } from 'vs/platform/workspace/common/editSe
|
||||||
import { EditorResourceAccessor, SaveReason, SideBySideEditor } from 'vs/workbench/common/editor';
|
import { EditorResourceAccessor, SaveReason, SideBySideEditor } from 'vs/workbench/common/editor';
|
||||||
import { coalesce, firstOrDefault } from 'vs/base/common/arrays';
|
import { coalesce, firstOrDefault } from 'vs/base/common/arrays';
|
||||||
import { ICanonicalUriService } from 'vs/platform/workspace/common/canonicalUri';
|
import { ICanonicalUriService } from 'vs/platform/workspace/common/canonicalUri';
|
||||||
|
import { revive } from 'vs/base/common/marshalling';
|
||||||
|
|
||||||
@extHostNamedCustomer(MainContext.MainThreadWorkspace)
|
@extHostNamedCustomer(MainContext.MainThreadWorkspace)
|
||||||
export class MainThreadWorkspace implements MainThreadWorkspaceShape {
|
export class MainThreadWorkspace implements MainThreadWorkspaceShape {
|
||||||
|
|
@ -146,7 +147,7 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
|
||||||
|
|
||||||
const query = this._queryBuilder.file(
|
const query = this._queryBuilder.file(
|
||||||
includeFolder ? [includeFolder] : workspace.folders,
|
includeFolder ? [includeFolder] : workspace.folders,
|
||||||
options
|
revive(options)
|
||||||
);
|
);
|
||||||
|
|
||||||
return this._searchService.fileSearch(query, token).then(result => {
|
return this._searchService.fileSearch(query, token).then(result => {
|
||||||
|
|
@ -164,7 +165,7 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
|
||||||
const workspace = this._contextService.getWorkspace();
|
const workspace = this._contextService.getWorkspace();
|
||||||
const folders = folder ? [folder] : workspace.folders.map(folder => folder.uri);
|
const folders = folder ? [folder] : workspace.folders.map(folder => folder.uri);
|
||||||
|
|
||||||
const query = this._queryBuilder.text(pattern, folders, options);
|
const query = this._queryBuilder.text(pattern, folders, revive(options));
|
||||||
query._reason = 'startTextSearch';
|
query._reason = 'startTextSearch';
|
||||||
|
|
||||||
const onProgress = (p: ISearchProgressItem) => {
|
const onProgress = (p: ISearchProgressItem) => {
|
||||||
|
|
|
||||||
|
|
@ -550,14 +550,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||||
registerCodeLensProvider(selector: vscode.DocumentSelector, provider: vscode.CodeLensProvider): vscode.Disposable {
|
registerCodeLensProvider(selector: vscode.DocumentSelector, provider: vscode.CodeLensProvider): vscode.Disposable {
|
||||||
return extHostLanguageFeatures.registerCodeLensProvider(extension, checkSelector(selector), provider);
|
return extHostLanguageFeatures.registerCodeLensProvider(extension, checkSelector(selector), provider);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
// VOID added this (I think will need to add this back when add ctrl+K)
|
|
||||||
// registerVoidCtrlKProvider(selector: vscode.DocumentSelector, provider: vscode.CodeLensProvider): vscode.Disposable {
|
|
||||||
// return extHostLanguageFeatures.registerCodeLensProvider(extension, checkSelector(selector), provider);
|
|
||||||
// },
|
|
||||||
|
|
||||||
|
|
||||||
registerDefinitionProvider(selector: vscode.DocumentSelector, provider: vscode.DefinitionProvider): vscode.Disposable {
|
registerDefinitionProvider(selector: vscode.DocumentSelector, provider: vscode.DefinitionProvider): vscode.Disposable {
|
||||||
return extHostLanguageFeatures.registerDefinitionProvider(extension, checkSelector(selector), provider);
|
return extHostLanguageFeatures.registerDefinitionProvider(extension, checkSelector(selector), provider);
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -304,17 +304,6 @@ const newCommands: ApiCommand[] = [
|
||||||
})(value);
|
})(value);
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
// // --- Void code lens
|
|
||||||
// new ApiCommand(
|
|
||||||
// 'vscode.executeVoidCodeLensProvider', '_executeVoidCodeLensProvider', 'Execute Void code lens provider.',
|
|
||||||
// [ApiCommandArgument.Uri, ApiCommandArgument.Number.with('itemResolveCount', 'Number of lenses that should be resolved and returned. Will only return resolved lenses, will impact performance)').optional()],
|
|
||||||
// new ApiCommandResult<languages.CodeLens[], vscode.CodeLens[] | undefined>('A promise that resolves to an array of VoidCodeLens-instances.', (value, _args, converter) => {
|
|
||||||
// return tryMapWith<languages.CodeLens, vscode.CodeLens>(item => {
|
|
||||||
// return new types.CodeLens(typeConverters.Range.to(item.range), item.command && converter.fromInternal(item.command));
|
|
||||||
// })(value);
|
|
||||||
// })
|
|
||||||
// ),
|
|
||||||
|
|
||||||
// --- code actions
|
// --- code actions
|
||||||
new ApiCommand(
|
new ApiCommand(
|
||||||
'vscode.executeCodeActionProvider', '_executeCodeActionProvider', 'Execute code action provider.',
|
'vscode.executeCodeActionProvider', '_executeCodeActionProvider', 'Execute code action provider.',
|
||||||
|
|
|
||||||
|
|
@ -2312,15 +2312,15 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
|
||||||
}
|
}
|
||||||
|
|
||||||
$provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Promise<extHostProtocol.ICodeLensListDto | undefined> {
|
$provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Promise<extHostProtocol.ICodeLensListDto | undefined> {
|
||||||
return this._withAdapter(handle, CodeLensAdapter, adapter => adapter.provideCodeLenses(URI.revive(resource), token), undefined, token);
|
return this._withAdapter(handle, CodeLensAdapter, adapter => adapter.provideCodeLenses(URI.revive(resource), token), undefined, token, resource.scheme === 'output');
|
||||||
}
|
}
|
||||||
|
|
||||||
$resolveCodeLens(handle: number, symbol: extHostProtocol.ICodeLensDto, token: CancellationToken): Promise<extHostProtocol.ICodeLensDto | undefined> {
|
$resolveCodeLens(handle: number, symbol: extHostProtocol.ICodeLensDto, token: CancellationToken): Promise<extHostProtocol.ICodeLensDto | undefined> {
|
||||||
return this._withAdapter(handle, CodeLensAdapter, adapter => adapter.resolveCodeLens(symbol, token), undefined, undefined);
|
return this._withAdapter(handle, CodeLensAdapter, adapter => adapter.resolveCodeLens(symbol, token), undefined, undefined, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
$releaseCodeLenses(handle: number, cacheId: number): void {
|
$releaseCodeLenses(handle: number, cacheId: number): void {
|
||||||
this._withAdapter(handle, CodeLensAdapter, adapter => Promise.resolve(adapter.releaseCodeLenses(cacheId)), undefined, undefined);
|
this._withAdapter(handle, CodeLensAdapter, adapter => Promise.resolve(adapter.releaseCodeLenses(cacheId)), undefined, undefined, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- declaration
|
// --- declaration
|
||||||
|
|
|
||||||
|
|
@ -584,7 +584,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
|
||||||
}
|
}
|
||||||
const parsedInclude = include ? parseSearchExcludeInclude(GlobPattern.from(include)) : undefined;
|
const parsedInclude = include ? parseSearchExcludeInclude(GlobPattern.from(include)) : undefined;
|
||||||
|
|
||||||
const excludePatterns = include ? globsToISearchPatternBuilder(options.exclude) : undefined;
|
const excludePatterns = globsToISearchPatternBuilder(options.exclude);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
options: {
|
options: {
|
||||||
|
|
@ -664,7 +664,10 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
|
||||||
async findTextInFilesBase(query: vscode.TextSearchQuery, queryOptions: QueryOptions<ITextQueryBuilderOptions>[] | undefined, callback: (result: ITextSearchResult<URI>, uri: URI) => void, token: vscode.CancellationToken = CancellationToken.None): Promise<vscode.TextSearchComplete> {
|
async findTextInFilesBase(query: vscode.TextSearchQuery, queryOptions: QueryOptions<ITextQueryBuilderOptions>[] | undefined, callback: (result: ITextSearchResult<URI>, uri: URI) => void, token: vscode.CancellationToken = CancellationToken.None): Promise<vscode.TextSearchComplete> {
|
||||||
const requestId = this._requestIdProvider.getNext();
|
const requestId = this._requestIdProvider.getNext();
|
||||||
|
|
||||||
const isCanceled = false;
|
let isCanceled = false;
|
||||||
|
token.onCancellationRequested(_ => {
|
||||||
|
isCanceled = true;
|
||||||
|
});
|
||||||
|
|
||||||
this._activeSearchCallbacks[requestId] = p => {
|
this._activeSearchCallbacks[requestId] = p => {
|
||||||
if (isCanceled) {
|
if (isCanceled) {
|
||||||
|
|
|
||||||
|
|
@ -138,11 +138,11 @@
|
||||||
color: var(--vscode-titleBar-activeForeground);
|
color: var(--vscode-titleBar-activeForeground);
|
||||||
}
|
}
|
||||||
|
|
||||||
.monaco-workbench .part.titlebar.inactive > .titlebar-container > .titlebar-center > .window-title > .command-center > .monaco-toolbar > .monaco-action-bar > .actions-container > .action-item > .action-label {
|
.monaco-workbench .part.titlebar.inactive > .titlebar-container > .titlebar-center > .window-title > .command-center > .monaco-toolbar > .monaco-action-bar > .actions-container > .action-item > .action-label {
|
||||||
color: var(--vscode-titleBar-inactiveForeground);
|
color: var(--vscode-titleBar-inactiveForeground);
|
||||||
}
|
}
|
||||||
|
|
||||||
.monaco-workbench .part.titlebar > .titlebar-container > .titlebar-center > .window-title > .command-center > .monaco-toolbar > .monaco-action-bar > .actions-container > .action-item > .action-label {
|
.monaco-workbench .part.titlebar > .titlebar-container > .titlebar-center > .window-title > .command-center > .monaco-toolbar > .monaco-action-bar > .actions-container > .action-item > .action-label {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -182,7 +182,7 @@
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
.monaco-workbench .part.titlebar > .titlebar-container > .titlebar-center > .window-title > .command-center .action-item.command-center-center.multiple {
|
.monaco-workbench .part.titlebar > .titlebar-container > .titlebar-center > .window-title > .command-center .action-item.command-center-center.multiple {
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
padding: 0 12px;
|
padding: 0 12px;
|
||||||
}
|
}
|
||||||
|
|
@ -280,7 +280,7 @@
|
||||||
border-left: 1px solid transparent;
|
border-left: 1px solid transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Window Controls (Minimize, Max/Restore, Close) */
|
/* Window Controls Container */
|
||||||
.monaco-workbench .part.titlebar .window-controls-container {
|
.monaco-workbench .part.titlebar .window-controls-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-grow: 0;
|
flex-grow: 0;
|
||||||
|
|
@ -292,7 +292,12 @@
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Web WCO Sizing/Ordering */
|
.monaco-workbench.fullscreen .part.titlebar .window-controls-container {
|
||||||
|
display: none;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Window Controls Container Web: Apply WCO environment variables (https://developer.mozilla.org/en-US/docs/Web/CSS/env#titlebar-area-x) */
|
||||||
.monaco-workbench.web .part.titlebar .titlebar-right .window-controls-container {
|
.monaco-workbench.web .part.titlebar .titlebar-right .window-controls-container {
|
||||||
width: calc(100vw - env(titlebar-area-width, 100vw) - env(titlebar-area-x, 0px));
|
width: calc(100vw - env(titlebar-area-width, 100vw) - env(titlebar-area-x, 0px));
|
||||||
height: env(titlebar-area-height, 35px);
|
height: env(titlebar-area-height, 35px);
|
||||||
|
|
@ -311,29 +316,31 @@
|
||||||
order: 1;
|
order: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Desktop Windows/Linux Window Controls*/
|
/* Window Controls Container Desktop: apply zoom friendly size */
|
||||||
.monaco-workbench:not(.web):not(.mac) .part.titlebar .window-controls-container.primary {
|
.monaco-workbench:not(.web):not(.mac) .part.titlebar .window-controls-container {
|
||||||
width: calc(138px / var(--zoom-factor, 1));
|
width: calc(138px / var(--zoom-factor, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
.monaco-workbench:not(.web):not(.mac) .part.titlebar .titlebar-container.counter-zoom .window-controls-container.primary {
|
.monaco-workbench:not(.web):not(.mac) .part.titlebar .titlebar-container.counter-zoom .window-controls-container {
|
||||||
width: 138px;
|
width: 138px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.monaco-workbench.linux:not(.web) .part.titlebar .window-controls-container.wco-enabled {
|
||||||
|
width: calc(var(--title-wco-width, 138px));
|
||||||
|
}
|
||||||
|
|
||||||
|
.monaco-workbench.linux:not(.web) .part.titlebar .titlebar-container.counter-zoom .window-controls-container.wco-enabled {
|
||||||
|
width: var(--title-wco-width, 138px);
|
||||||
|
}
|
||||||
|
|
||||||
.monaco-workbench:not(.web):not(.mac) .part.titlebar .titlebar-container:not(.counter-zoom) .window-controls-container * {
|
.monaco-workbench:not(.web):not(.mac) .part.titlebar .titlebar-container:not(.counter-zoom) .window-controls-container * {
|
||||||
zoom: calc(1 / var(--zoom-factor, 1));
|
zoom: calc(1 / var(--zoom-factor, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Desktop macOS Window Controls */
|
.monaco-workbench:not(.web).mac .part.titlebar .window-controls-container {
|
||||||
.monaco-workbench:not(.web).mac .part.titlebar .window-controls-container.primary {
|
|
||||||
width: 70px;
|
width: 70px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.monaco-workbench.fullscreen .part.titlebar .window-controls-container {
|
|
||||||
display: none;
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Window Control Icons */
|
/* Window Control Icons */
|
||||||
.monaco-workbench .part.titlebar .window-controls-container > .window-icon {
|
.monaco-workbench .part.titlebar .window-controls-container > .window-icon {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -342,6 +349,11 @@
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 46px;
|
width: 46px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
|
color: var(--vscode-titleBar-activeForeground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.monaco-workbench .part.titlebar.inactive .window-controls-container > .window-icon {
|
||||||
|
color: var(--vscode-titleBar-inactiveForeground);
|
||||||
}
|
}
|
||||||
|
|
||||||
.monaco-workbench .part.titlebar .window-controls-container > .window-icon::before {
|
.monaco-workbench .part.titlebar .window-controls-container > .window-icon::before {
|
||||||
|
|
@ -376,7 +388,6 @@
|
||||||
z-index: 2500;
|
z-index: 2500;
|
||||||
-webkit-app-region: no-drag;
|
-webkit-app-region: no-drag;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
min-width: 28px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.monaco-workbench .part.titlebar > .titlebar-container > .titlebar-right > .action-toolbar-container {
|
.monaco-workbench .part.titlebar > .titlebar-container > .titlebar-right > .action-toolbar-container {
|
||||||
|
|
@ -455,11 +466,3 @@
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.monaco-workbench .part.titlebar .window-controls-container .window-icon {
|
|
||||||
color: var(--vscode-titleBar-activeForeground);
|
|
||||||
}
|
|
||||||
|
|
||||||
.monaco-workbench .part.titlebar.inactive .window-controls-container .window-icon {
|
|
||||||
color: var(--vscode-titleBar-inactiveForeground);
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import 'vs/css!./media/titlebarpart';
|
||||||
import { localize, localize2 } from 'vs/nls';
|
import { localize, localize2 } from 'vs/nls';
|
||||||
import { MultiWindowParts, Part } from 'vs/workbench/browser/part';
|
import { MultiWindowParts, Part } from 'vs/workbench/browser/part';
|
||||||
import { ITitleService } from 'vs/workbench/services/title/browser/titleService';
|
import { ITitleService } from 'vs/workbench/services/title/browser/titleService';
|
||||||
import { getWCOBoundingRect, getZoomFactor, isWCOEnabled } from 'vs/base/browser/browser';
|
import { getWCOTitlebarAreaRect, getZoomFactor, isWCOEnabled, onDidChangeZoomLevel } from 'vs/base/browser/browser';
|
||||||
import { MenuBarVisibility, getTitleBarStyle, getMenuBarVisibility, TitlebarStyle, hasCustomTitlebar, hasNativeTitlebar, DEFAULT_CUSTOM_TITLEBAR_HEIGHT } from 'vs/platform/window/common/window';
|
import { MenuBarVisibility, getTitleBarStyle, getMenuBarVisibility, TitlebarStyle, hasCustomTitlebar, hasNativeTitlebar, DEFAULT_CUSTOM_TITLEBAR_HEIGHT } from 'vs/platform/window/common/window';
|
||||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||||
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
|
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||||
|
|
@ -228,7 +228,7 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart {
|
||||||
const wcoEnabled = isWeb && isWCOEnabled();
|
const wcoEnabled = isWeb && isWCOEnabled();
|
||||||
let value = this.isCommandCenterVisible || wcoEnabled ? DEFAULT_CUSTOM_TITLEBAR_HEIGHT : 30;
|
let value = this.isCommandCenterVisible || wcoEnabled ? DEFAULT_CUSTOM_TITLEBAR_HEIGHT : 30;
|
||||||
if (wcoEnabled) {
|
if (wcoEnabled) {
|
||||||
value = Math.max(value, getWCOBoundingRect()?.height ?? 0);
|
value = Math.max(value, getWCOTitlebarAreaRect(getWindow(this.element))?.height ?? 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return value / (this.preventZoom ? getZoomFactor(getWindow(this.element)) : 1);
|
return value / (this.preventZoom ? getZoomFactor(getWindow(this.element)) : 1);
|
||||||
|
|
@ -249,7 +249,7 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart {
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
protected rootContainer!: HTMLElement;
|
protected rootContainer!: HTMLElement;
|
||||||
protected primaryWindowControls: HTMLElement | undefined;
|
protected windowControlsContainer: HTMLElement | undefined;
|
||||||
protected dragRegion: HTMLElement | undefined;
|
protected dragRegion: HTMLElement | undefined;
|
||||||
private title!: HTMLElement;
|
private title!: HTMLElement;
|
||||||
|
|
||||||
|
|
@ -476,21 +476,49 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart {
|
||||||
this.createActionToolBarMenus();
|
this.createActionToolBarMenus();
|
||||||
}
|
}
|
||||||
|
|
||||||
let primaryControlLocation = isMacintosh ? 'left' : 'right';
|
// Window Controls Container
|
||||||
if (isMacintosh && isNative) {
|
|
||||||
|
|
||||||
// Check if the locale is RTL, macOS will move traffic lights in RTL locales
|
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/textInfo
|
|
||||||
|
|
||||||
const localeInfo = new Intl.Locale(platformLocale) as any;
|
|
||||||
if (localeInfo?.textInfo?.direction === 'rtl') {
|
|
||||||
primaryControlLocation = 'right';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hasNativeTitlebar(this.configurationService, this.titleBarStyle)) {
|
if (!hasNativeTitlebar(this.configurationService, this.titleBarStyle)) {
|
||||||
this.primaryWindowControls = append(primaryControlLocation === 'left' ? this.leftContent : this.rightContent, $('div.window-controls-container.primary'));
|
let primaryWindowControlsLocation = isMacintosh ? 'left' : 'right';
|
||||||
append(primaryControlLocation === 'left' ? this.rightContent : this.leftContent, $('div.window-controls-container.secondary'));
|
if (isMacintosh && isNative) {
|
||||||
|
|
||||||
|
// Check if the locale is RTL, macOS will move traffic lights in RTL locales
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/textInfo
|
||||||
|
|
||||||
|
const localeInfo = new Intl.Locale(platformLocale) as any;
|
||||||
|
if (localeInfo?.textInfo?.direction === 'rtl') {
|
||||||
|
primaryWindowControlsLocation = 'right';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isMacintosh && isNative && primaryWindowControlsLocation === 'left') {
|
||||||
|
// macOS native: controls are on the left and the container is not needed to make room
|
||||||
|
// for something, except for web where a custom menu being supported). not putting the
|
||||||
|
// container helps with allowing to move the window when clicking very close to the
|
||||||
|
// window control buttons.
|
||||||
|
} else {
|
||||||
|
this.windowControlsContainer = append(primaryWindowControlsLocation === 'left' ? this.leftContent : this.rightContent, $('div.window-controls-container'));
|
||||||
|
if (isWeb) {
|
||||||
|
// Web: its possible to have control overlays on both sides, for example on macOS
|
||||||
|
// with window controls on the left and PWA controls on the right.
|
||||||
|
append(primaryWindowControlsLocation === 'left' ? this.rightContent : this.leftContent, $('div.window-controls-container'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isWCOEnabled()) {
|
||||||
|
this.windowControlsContainer.classList.add('wco-enabled');
|
||||||
|
|
||||||
|
const updateWCOWidthVariable = () => {
|
||||||
|
const targetWindow = getWindow(this.element);
|
||||||
|
const wcoTitlebarAreaRect = getWCOTitlebarAreaRect(targetWindow);
|
||||||
|
if (wcoTitlebarAreaRect) {
|
||||||
|
const wcoWidth = targetWindow.innerWidth - wcoTitlebarAreaRect.width - wcoTitlebarAreaRect.x;
|
||||||
|
this.windowControlsContainer?.style.setProperty('--title-wco-width', `${wcoWidth}px`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
updateWCOWidthVariable();
|
||||||
|
|
||||||
|
this._register(onDidChangeZoomLevel(() => setTimeout(() => updateWCOWidthVariable(), 5))); // Somehow it does not get the right size without this timeout :-/
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Context menu over title bar: depending on the OS and the location of the click this will either be
|
// Context menu over title bar: depending on the OS and the location of the click this will either be
|
||||||
|
|
|
||||||
|
|
@ -152,7 +152,7 @@ class ChatHistoryAction extends Action2 {
|
||||||
|
|
||||||
let lastDate: string | undefined = undefined;
|
let lastDate: string | undefined = undefined;
|
||||||
const picks = items.flatMap((i): [IQuickPickSeparator | undefined, IChatPickerItem] => {
|
const picks = items.flatMap((i): [IQuickPickSeparator | undefined, IChatPickerItem] => {
|
||||||
const timeAgoStr = fromNowByDay(i.lastMessageDate, true);
|
const timeAgoStr = fromNowByDay(i.lastMessageDate, true, true);
|
||||||
const separator: IQuickPickSeparator | undefined = timeAgoStr !== lastDate ? {
|
const separator: IQuickPickSeparator | undefined = timeAgoStr !== lastDate ? {
|
||||||
type: 'separator', label: timeAgoStr,
|
type: 'separator', label: timeAgoStr,
|
||||||
} : undefined;
|
} : undefined;
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ import { ChatAccessibilityService } from 'vs/workbench/contrib/chat/browser/chat
|
||||||
import { ChatEditor, IChatEditorOptions } from 'vs/workbench/contrib/chat/browser/chatEditor';
|
import { ChatEditor, IChatEditorOptions } from 'vs/workbench/contrib/chat/browser/chatEditor';
|
||||||
import { ChatEditorInput, ChatEditorInputSerializer } from 'vs/workbench/contrib/chat/browser/chatEditorInput';
|
import { ChatEditorInput, ChatEditorInputSerializer } from 'vs/workbench/contrib/chat/browser/chatEditorInput';
|
||||||
import { agentSlashCommandToMarkdown, agentToMarkdown } from 'vs/workbench/contrib/chat/browser/chatMarkdownDecorationsRenderer';
|
import { agentSlashCommandToMarkdown, agentToMarkdown } from 'vs/workbench/contrib/chat/browser/chatMarkdownDecorationsRenderer';
|
||||||
import { ChatExtensionPointHandler } from 'vs/workbench/contrib/chat/browser/chatParticipantContributions';
|
import { ChatCompatibilityNotifier, ChatExtensionPointHandler } from 'vs/workbench/contrib/chat/browser/chatParticipantContributions';
|
||||||
import { QuickChatService } from 'vs/workbench/contrib/chat/browser/chatQuick';
|
import { QuickChatService } from 'vs/workbench/contrib/chat/browser/chatQuick';
|
||||||
import { ChatResponseAccessibleView } from 'vs/workbench/contrib/chat/browser/chatResponseAccessibleView';
|
import { ChatResponseAccessibleView } from 'vs/workbench/contrib/chat/browser/chatResponseAccessibleView';
|
||||||
import { ChatVariablesService } from 'vs/workbench/contrib/chat/browser/chatVariables';
|
import { ChatVariablesService } from 'vs/workbench/contrib/chat/browser/chatVariables';
|
||||||
|
|
@ -261,9 +261,7 @@ workbenchContributionsRegistry.registerWorkbenchContribution(ChatSlashStaticSlas
|
||||||
Registry.as<IEditorFactoryRegistry>(EditorExtensions.EditorFactory).registerEditorSerializer(ChatEditorInput.TypeID, ChatEditorInputSerializer);
|
Registry.as<IEditorFactoryRegistry>(EditorExtensions.EditorFactory).registerEditorSerializer(ChatEditorInput.TypeID, ChatEditorInputSerializer);
|
||||||
registerWorkbenchContribution2(ChatExtensionPointHandler.ID, ChatExtensionPointHandler, WorkbenchPhase.BlockStartup);
|
registerWorkbenchContribution2(ChatExtensionPointHandler.ID, ChatExtensionPointHandler, WorkbenchPhase.BlockStartup);
|
||||||
registerWorkbenchContribution2(LanguageModelToolsExtensionPointHandler.ID, LanguageModelToolsExtensionPointHandler, WorkbenchPhase.BlockRestore);
|
registerWorkbenchContribution2(LanguageModelToolsExtensionPointHandler.ID, LanguageModelToolsExtensionPointHandler, WorkbenchPhase.BlockRestore);
|
||||||
|
registerWorkbenchContribution2(ChatCompatibilityNotifier.ID, ChatCompatibilityNotifier, WorkbenchPhase.Eventually);
|
||||||
// Disabled until https://github.com/microsoft/vscode/issues/218646 is fixed
|
|
||||||
// registerWorkbenchContribution2(ChatCompatibilityNotifier.ID, ChatCompatibilityNotifier, WorkbenchPhase.Eventually);
|
|
||||||
|
|
||||||
registerChatActions();
|
registerChatActions();
|
||||||
registerChatCopyActions();
|
registerChatCopyActions();
|
||||||
|
|
|
||||||
|
|
@ -516,7 +516,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge
|
||||||
onDidChangeCursorPosition();
|
onDidChangeCursorPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
private initAttachedContext(container: HTMLElement) {
|
private initAttachedContext(container: HTMLElement, isLayout = false) {
|
||||||
const oldHeight = container.offsetHeight;
|
const oldHeight = container.offsetHeight;
|
||||||
dom.clearNode(container);
|
dom.clearNode(container);
|
||||||
this.attachedContextDisposables.clear();
|
this.attachedContextDisposables.clear();
|
||||||
|
|
@ -578,7 +578,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge
|
||||||
this.attachedContextDisposables.add(disp);
|
this.attachedContextDisposables.add(disp);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (oldHeight !== container.offsetHeight) {
|
if (oldHeight !== container.offsetHeight && !isLayout) {
|
||||||
this._onDidChangeHeight.fire();
|
this._onDidChangeHeight.fire();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -609,7 +609,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge
|
||||||
|
|
||||||
private previousInputEditorDimension: IDimension | undefined;
|
private previousInputEditorDimension: IDimension | undefined;
|
||||||
private _layout(height: number, width: number, allowRecurse = true): void {
|
private _layout(height: number, width: number, allowRecurse = true): void {
|
||||||
this.initAttachedContext(this.attachedContextContainer);
|
this.initAttachedContext(this.attachedContextContainer, true);
|
||||||
|
|
||||||
const data = this.getLayoutData();
|
const data = this.getLayoutData();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,17 +3,16 @@
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { Action } from 'vs/base/common/actions';
|
|
||||||
import { coalesce, isNonEmptyArray } from 'vs/base/common/arrays';
|
import { coalesce, isNonEmptyArray } from 'vs/base/common/arrays';
|
||||||
import { Codicon } from 'vs/base/common/codicons';
|
import { Codicon } from 'vs/base/common/codicons';
|
||||||
import { Disposable, DisposableMap, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
import { DisposableMap, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||||
import * as strings from 'vs/base/common/strings';
|
import * as strings from 'vs/base/common/strings';
|
||||||
import { localize, localize2 } from 'vs/nls';
|
import { localize, localize2 } from 'vs/nls';
|
||||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||||
import { ILogService } from 'vs/platform/log/common/log';
|
import { ILogService } from 'vs/platform/log/common/log';
|
||||||
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
|
import { Severity } from 'vs/platform/notification/common/notification';
|
||||||
import { Registry } from 'vs/platform/registry/common/platform';
|
import { Registry } from 'vs/platform/registry/common/platform';
|
||||||
import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer';
|
import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer';
|
||||||
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||||
|
|
@ -21,7 +20,9 @@ import { IViewContainersRegistry, IViewDescriptor, IViewsRegistry, ViewContainer
|
||||||
import { CHAT_VIEW_ID } from 'vs/workbench/contrib/chat/browser/chat';
|
import { CHAT_VIEW_ID } from 'vs/workbench/contrib/chat/browser/chat';
|
||||||
import { CHAT_SIDEBAR_PANEL_ID, ChatViewPane } from 'vs/workbench/contrib/chat/browser/chatViewPane';
|
import { CHAT_SIDEBAR_PANEL_ID, ChatViewPane } from 'vs/workbench/contrib/chat/browser/chatViewPane';
|
||||||
import { ChatAgentLocation, IChatAgentData, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents';
|
import { ChatAgentLocation, IChatAgentData, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents';
|
||||||
|
import { CONTEXT_CHAT_EXTENSION_INVALID, CONTEXT_CHAT_PANEL_PARTICIPANT_REGISTERED } from 'vs/workbench/contrib/chat/common/chatContextKeys';
|
||||||
import { IRawChatParticipantContribution } from 'vs/workbench/contrib/chat/common/chatParticipantContribTypes';
|
import { IRawChatParticipantContribution } from 'vs/workbench/contrib/chat/common/chatParticipantContribTypes';
|
||||||
|
import { showExtensionsWithIdsCommandId } from 'vs/workbench/contrib/extensions/browser/extensionsActions';
|
||||||
import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions';
|
import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions';
|
||||||
import { isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
|
import { isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
|
||||||
import * as extensionsRegistry from 'vs/workbench/services/extensions/common/extensionsRegistry';
|
import * as extensionsRegistry from 'vs/workbench/services/extensions/common/extensionsRegistry';
|
||||||
|
|
@ -160,35 +161,6 @@ const chatParticipantExtensionPoint = extensionsRegistry.ExtensionsRegistry.regi
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export class ChatCompatibilityNotifier implements IWorkbenchContribution {
|
|
||||||
static readonly ID = 'workbench.contrib.chatCompatNotifier';
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
@IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService,
|
|
||||||
@INotificationService notificationService: INotificationService,
|
|
||||||
@ICommandService commandService: ICommandService
|
|
||||||
) {
|
|
||||||
// It may be better to have some generic UI for this, for any extension that is incompatible,
|
|
||||||
// but this is only enabled for Copilot Chat now and it needs to be obvious.
|
|
||||||
extensionsWorkbenchService.queryLocal().then(exts => {
|
|
||||||
const chat = exts.find(ext => ext.identifier.id === 'github.copilot-chat');
|
|
||||||
if (chat?.local?.validations.some(v => v[0] === Severity.Error)) {
|
|
||||||
notificationService.notify({
|
|
||||||
severity: Severity.Error,
|
|
||||||
message: localize('chatFailErrorMessage', "Chat failed to load. Please ensure that the GitHub Copilot Chat extension is up to date."),
|
|
||||||
actions: {
|
|
||||||
primary: [
|
|
||||||
new Action('showExtension', localize('action.showExtension', "Show Extension"), undefined, true, () => {
|
|
||||||
return commandService.executeCommand('workbench.extensions.action.showExtensionsWithIds', ['GitHub.copilot-chat']);
|
|
||||||
})
|
|
||||||
]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ChatExtensionPointHandler implements IWorkbenchContribution {
|
export class ChatExtensionPointHandler implements IWorkbenchContribution {
|
||||||
|
|
||||||
static readonly ID = 'workbench.contrib.chatExtensionPointHandler';
|
static readonly ID = 'workbench.contrib.chatExtensionPointHandler';
|
||||||
|
|
@ -198,9 +170,10 @@ export class ChatExtensionPointHandler implements IWorkbenchContribution {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@IChatAgentService private readonly _chatAgentService: IChatAgentService,
|
@IChatAgentService private readonly _chatAgentService: IChatAgentService,
|
||||||
@ILogService private readonly logService: ILogService,
|
@ILogService private readonly logService: ILogService
|
||||||
) {
|
) {
|
||||||
this._viewContainer = this.registerViewContainer();
|
this._viewContainer = this.registerViewContainer();
|
||||||
|
this.registerDefaultParticipantView();
|
||||||
this.handleAndRegisterChatExtensions();
|
this.handleAndRegisterChatExtensions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -239,11 +212,6 @@ export class ChatExtensionPointHandler implements IWorkbenchContribution {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const store = new DisposableStore();
|
|
||||||
if (providerDescriptor.isDefault && (!providerDescriptor.locations || providerDescriptor.locations?.includes(ChatAgentLocation.Panel))) {
|
|
||||||
store.add(this.registerDefaultParticipantView(providerDescriptor));
|
|
||||||
}
|
|
||||||
|
|
||||||
const participantsAndCommandsDisambiguation: {
|
const participantsAndCommandsDisambiguation: {
|
||||||
categoryName: string;
|
categoryName: string;
|
||||||
description: string;
|
description: string;
|
||||||
|
|
@ -260,6 +228,7 @@ export class ChatExtensionPointHandler implements IWorkbenchContribution {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const store = new DisposableStore();
|
||||||
store.add(this._chatAgentService.registerAgent(
|
store.add(this._chatAgentService.registerAgent(
|
||||||
providerDescriptor.id,
|
providerDescriptor.id,
|
||||||
{
|
{
|
||||||
|
|
@ -318,15 +287,9 @@ export class ChatExtensionPointHandler implements IWorkbenchContribution {
|
||||||
return viewContainer;
|
return viewContainer;
|
||||||
}
|
}
|
||||||
|
|
||||||
private hasRegisteredDefaultParticipantView = false;
|
private registerDefaultParticipantView(): IDisposable {
|
||||||
private registerDefaultParticipantView(defaultParticipantDescriptor: IRawChatParticipantContribution): IDisposable {
|
// Register View. Name must be hardcoded because we want to show it even when the extension fails to load due to an API version incompatibility.
|
||||||
if (this.hasRegisteredDefaultParticipantView) {
|
const name = 'GitHub Copilot';
|
||||||
this.logService.warn(`Tried to register a second default chat participant view for "${defaultParticipantDescriptor.id}"`);
|
|
||||||
return Disposable.None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register View
|
|
||||||
const name = defaultParticipantDescriptor.fullName ?? defaultParticipantDescriptor.name;
|
|
||||||
const viewDescriptor: IViewDescriptor[] = [{
|
const viewDescriptor: IViewDescriptor[] = [{
|
||||||
id: CHAT_VIEW_ID,
|
id: CHAT_VIEW_ID,
|
||||||
containerIcon: this._viewContainer.icon,
|
containerIcon: this._viewContainer.icon,
|
||||||
|
|
@ -336,12 +299,11 @@ export class ChatExtensionPointHandler implements IWorkbenchContribution {
|
||||||
canToggleVisibility: false,
|
canToggleVisibility: false,
|
||||||
canMoveView: true,
|
canMoveView: true,
|
||||||
ctorDescriptor: new SyncDescriptor(ChatViewPane),
|
ctorDescriptor: new SyncDescriptor(ChatViewPane),
|
||||||
|
when: ContextKeyExpr.or(CONTEXT_CHAT_PANEL_PARTICIPANT_REGISTERED, CONTEXT_CHAT_EXTENSION_INVALID)
|
||||||
}];
|
}];
|
||||||
this.hasRegisteredDefaultParticipantView = true;
|
|
||||||
Registry.as<IViewsRegistry>(ViewExtensions.ViewsRegistry).registerViews(viewDescriptor, this._viewContainer);
|
Registry.as<IViewsRegistry>(ViewExtensions.ViewsRegistry).registerViews(viewDescriptor, this._viewContainer);
|
||||||
|
|
||||||
return toDisposable(() => {
|
return toDisposable(() => {
|
||||||
this.hasRegisteredDefaultParticipantView = false;
|
|
||||||
Registry.as<IViewsRegistry>(ViewExtensions.ViewsRegistry).deregisterViews(viewDescriptor, this._viewContainer);
|
Registry.as<IViewsRegistry>(ViewExtensions.ViewsRegistry).deregisterViews(viewDescriptor, this._viewContainer);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -350,3 +312,39 @@ export class ChatExtensionPointHandler implements IWorkbenchContribution {
|
||||||
function getParticipantKey(extensionId: ExtensionIdentifier, participantName: string): string {
|
function getParticipantKey(extensionId: ExtensionIdentifier, participantName: string): string {
|
||||||
return `${extensionId.value}_${participantName}`;
|
return `${extensionId.value}_${participantName}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class ChatCompatibilityNotifier implements IWorkbenchContribution {
|
||||||
|
static readonly ID = 'workbench.contrib.chatCompatNotifier';
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
@IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService,
|
||||||
|
@IContextKeyService contextKeyService: IContextKeyService,
|
||||||
|
@IChatAgentService chatAgentService: IChatAgentService,
|
||||||
|
) {
|
||||||
|
// It may be better to have some generic UI for this, for any extension that is incompatible,
|
||||||
|
// but this is only enabled for Copilot Chat now and it needs to be obvious.
|
||||||
|
|
||||||
|
const showExtensionLabel = localize('showExtension', "Show Extension");
|
||||||
|
const viewsRegistry = Registry.as<IViewsRegistry>(ViewExtensions.ViewsRegistry);
|
||||||
|
viewsRegistry.registerViewWelcomeContent(CHAT_VIEW_ID, {
|
||||||
|
content: localize('chatFailErrorMessage', "Chat failed to load. Please ensure that the GitHub Copilot Chat extension is up to date.") + `\n\n[${showExtensionLabel}](command:${showExtensionsWithIdsCommandId}?${encodeURIComponent(JSON.stringify([['GitHub.copilot-chat']]))})`,
|
||||||
|
when: CONTEXT_CHAT_EXTENSION_INVALID,
|
||||||
|
});
|
||||||
|
|
||||||
|
const isInvalid = CONTEXT_CHAT_EXTENSION_INVALID.bindTo(contextKeyService);
|
||||||
|
extensionsWorkbenchService.queryLocal().then(exts => {
|
||||||
|
const chat = exts.find(ext => ext.identifier.id === 'github.copilot-chat');
|
||||||
|
if (chat?.local?.validations.some(v => v[0] === Severity.Error)) {
|
||||||
|
// This catches vscode starting up with the invalid extension, but the extension may still get updated by vscode after this.
|
||||||
|
isInvalid.set(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const listener = chatAgentService.onDidChangeAgents(() => {
|
||||||
|
if (chatAgentService.getDefaultAgent(ChatAgentLocation.Panel)) {
|
||||||
|
isInvalid.set(false);
|
||||||
|
listener.dispose();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -88,8 +88,9 @@ export class ChatViewPane extends ViewPane {
|
||||||
} else if (this._widget?.viewModel?.initState === ChatModelInitState.Initialized) {
|
} else if (this._widget?.viewModel?.initState === ChatModelInitState.Initialized) {
|
||||||
// Model is initialized, and the default agent disappeared, so show welcome view
|
// Model is initialized, and the default agent disappeared, so show welcome view
|
||||||
this.didUnregisterProvider = true;
|
this.didUnregisterProvider = true;
|
||||||
this._onDidChangeViewWelcomeState.fire();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._onDidChangeViewWelcomeState.fire();
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -114,6 +115,10 @@ export class ChatViewPane extends ViewPane {
|
||||||
}
|
}
|
||||||
|
|
||||||
override shouldShowWelcome(): boolean {
|
override shouldShowWelcome(): boolean {
|
||||||
|
if (!this.chatAgentService.getContributedDefaultAgent(ChatAgentLocation.Panel)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
const noPersistedSessions = !this.chatService.hasSessions();
|
const noPersistedSessions = !this.chatService.hasSessions();
|
||||||
return this.didUnregisterProvider || !this._widget?.viewModel && (noPersistedSessions || this.didProviderRegistrationFail);
|
return this.didUnregisterProvider || !this._widget?.viewModel && (noPersistedSessions || this.didProviderRegistrationFail);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -770,15 +770,11 @@ export class CodeCompareBlockPart extends Disposable {
|
||||||
});
|
});
|
||||||
|
|
||||||
dom.reset(this.messageElement, message);
|
dom.reset(this.messageElement, message);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const diffData = await data.diffData;
|
const diffData = await data.diffData;
|
||||||
if (!diffData) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isEditApplied) {
|
if (!isEditApplied && diffData) {
|
||||||
const viewModel = this.diffEditor.createViewModel({
|
const viewModel = this.diffEditor.createViewModel({
|
||||||
original: diffData.original,
|
original: diffData.original,
|
||||||
modified: diffData.modified
|
modified: diffData.modified
|
||||||
|
|
@ -801,6 +797,7 @@ export class CodeCompareBlockPart extends Disposable {
|
||||||
} else {
|
} else {
|
||||||
this.diffEditor.setModel(null);
|
this.diffEditor.setModel(null);
|
||||||
this._lastDiffEditorViewModel.value = undefined;
|
this._lastDiffEditorViewModel.value = undefined;
|
||||||
|
this._onDidChangeContentHeight.fire();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.toolbar.context = {
|
this.toolbar.context = {
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ import { ILogService } from 'vs/platform/log/common/log';
|
||||||
import { IProductService } from 'vs/platform/product/common/productService';
|
import { IProductService } from 'vs/platform/product/common/productService';
|
||||||
import { asJson, IRequestService } from 'vs/platform/request/common/request';
|
import { asJson, IRequestService } from 'vs/platform/request/common/request';
|
||||||
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||||
import { CONTEXT_CHAT_ENABLED } from 'vs/workbench/contrib/chat/common/chatContextKeys';
|
import { CONTEXT_CHAT_ENABLED, CONTEXT_CHAT_PANEL_PARTICIPANT_REGISTERED } from 'vs/workbench/contrib/chat/common/chatContextKeys';
|
||||||
import { IChatProgressResponseContent, IChatRequestVariableData, ISerializableChatAgentData } from 'vs/workbench/contrib/chat/common/chatModel';
|
import { IChatProgressResponseContent, IChatRequestVariableData, ISerializableChatAgentData } from 'vs/workbench/contrib/chat/common/chatModel';
|
||||||
import { IRawChatCommandContribution, RawChatParticipantLocation } from 'vs/workbench/contrib/chat/common/chatParticipantContribTypes';
|
import { IRawChatCommandContribution, RawChatParticipantLocation } from 'vs/workbench/contrib/chat/common/chatParticipantContribTypes';
|
||||||
import { IChatFollowup, IChatLocationData, IChatProgress, IChatResponseErrorDetails, IChatTaskDto } from 'vs/workbench/contrib/chat/common/chatService';
|
import { IChatFollowup, IChatLocationData, IChatProgress, IChatResponseErrorDetails, IChatTaskDto } from 'vs/workbench/contrib/chat/common/chatService';
|
||||||
|
|
@ -233,11 +233,13 @@ export class ChatAgentService implements IChatAgentService {
|
||||||
readonly onDidChangeAgents: Event<IChatAgent | undefined> = this._onDidChangeAgents.event;
|
readonly onDidChangeAgents: Event<IChatAgent | undefined> = this._onDidChangeAgents.event;
|
||||||
|
|
||||||
private readonly _hasDefaultAgent: IContextKey<boolean>;
|
private readonly _hasDefaultAgent: IContextKey<boolean>;
|
||||||
|
private readonly _defaultAgentRegistered: IContextKey<boolean>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@IContextKeyService private readonly contextKeyService: IContextKeyService,
|
@IContextKeyService private readonly contextKeyService: IContextKeyService,
|
||||||
) {
|
) {
|
||||||
this._hasDefaultAgent = CONTEXT_CHAT_ENABLED.bindTo(this.contextKeyService);
|
this._hasDefaultAgent = CONTEXT_CHAT_ENABLED.bindTo(this.contextKeyService);
|
||||||
|
this._defaultAgentRegistered = CONTEXT_CHAT_PANEL_PARTICIPANT_REGISTERED.bindTo(this.contextKeyService);
|
||||||
}
|
}
|
||||||
|
|
||||||
registerAgent(id: string, data: IChatAgentData): IDisposable {
|
registerAgent(id: string, data: IChatAgentData): IDisposable {
|
||||||
|
|
@ -246,6 +248,10 @@ export class ChatAgentService implements IChatAgentService {
|
||||||
throw new Error(`Agent already registered: ${JSON.stringify(id)}`);
|
throw new Error(`Agent already registered: ${JSON.stringify(id)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data.isDefault) {
|
||||||
|
this._defaultAgentRegistered.set(true);
|
||||||
|
}
|
||||||
|
|
||||||
const that = this;
|
const that = this;
|
||||||
const commands = data.slashCommands;
|
const commands = data.slashCommands;
|
||||||
data = {
|
data = {
|
||||||
|
|
@ -256,8 +262,13 @@ export class ChatAgentService implements IChatAgentService {
|
||||||
};
|
};
|
||||||
const entry = { data };
|
const entry = { data };
|
||||||
this._agents.set(id, entry);
|
this._agents.set(id, entry);
|
||||||
|
this._onDidChangeAgents.fire(undefined);
|
||||||
return toDisposable(() => {
|
return toDisposable(() => {
|
||||||
this._agents.delete(id);
|
this._agents.delete(id);
|
||||||
|
if (data.isDefault) {
|
||||||
|
this._defaultAgentRegistered.set(false);
|
||||||
|
}
|
||||||
|
|
||||||
this._onDidChangeAgents.fire(undefined);
|
this._onDidChangeAgents.fire(undefined);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -445,7 +456,7 @@ export class ChatAgentService implements IChatAgentService {
|
||||||
const participants = this.getAgents().reduce<IChatParticipantMetadata[]>((acc, a) => {
|
const participants = this.getAgents().reduce<IChatParticipantMetadata[]>((acc, a) => {
|
||||||
acc.push({ participant: a.id, disambiguation: a.disambiguation ?? [] });
|
acc.push({ participant: a.id, disambiguation: a.disambiguation ?? [] });
|
||||||
for (const command of a.slashCommands) {
|
for (const command of a.slashCommands) {
|
||||||
acc.push({ participant: a.id, command: command.name, disambiguation: [] });
|
acc.push({ participant: a.id, command: command.name, disambiguation: command.disambiguation ?? [] });
|
||||||
}
|
}
|
||||||
return acc;
|
return acc;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,9 @@ export const CONTEXT_CHAT_INPUT_HAS_FOCUS = new RawContextKey<boolean>('chatInpu
|
||||||
export const CONTEXT_IN_CHAT_INPUT = new RawContextKey<boolean>('inChatInput', false, { type: 'boolean', description: localize('inInteractiveInput', "True when focus is in the chat input, false otherwise.") });
|
export const CONTEXT_IN_CHAT_INPUT = new RawContextKey<boolean>('inChatInput', false, { type: 'boolean', description: localize('inInteractiveInput', "True when focus is in the chat input, false otherwise.") });
|
||||||
export const CONTEXT_IN_CHAT_SESSION = new RawContextKey<boolean>('inChat', false, { type: 'boolean', description: localize('inChat', "True when focus is in the chat widget, false otherwise.") });
|
export const CONTEXT_IN_CHAT_SESSION = new RawContextKey<boolean>('inChat', false, { type: 'boolean', description: localize('inChat', "True when focus is in the chat widget, false otherwise.") });
|
||||||
|
|
||||||
export const CONTEXT_CHAT_ENABLED = new RawContextKey<boolean>('chatIsEnabled', false, { type: 'boolean', description: localize('chatIsEnabled', "True when chat is enabled because a default chat participant is registered.") });
|
export const CONTEXT_CHAT_ENABLED = new RawContextKey<boolean>('chatIsEnabled', false, { type: 'boolean', description: localize('chatIsEnabled', "True when chat is enabled because a default chat participant is activated with an implementation.") });
|
||||||
|
export const CONTEXT_CHAT_PANEL_PARTICIPANT_REGISTERED = new RawContextKey<boolean>('chatPanelParticipantRegistered', false, { type: 'boolean', description: localize('chatParticipantRegistered', "True when a default chat participant is registered for the panel.") });
|
||||||
|
export const CONTEXT_CHAT_EXTENSION_INVALID = new RawContextKey<boolean>('chatExtensionInvalid', false, { type: 'boolean', description: localize('chatExtensionInvalid', "True when the installed chat extension is invalid and needs to be updated.") });
|
||||||
export const CONTEXT_CHAT_INPUT_CURSOR_AT_TOP = new RawContextKey<boolean>('chatCursorAtTop', false);
|
export const CONTEXT_CHAT_INPUT_CURSOR_AT_TOP = new RawContextKey<boolean>('chatCursorAtTop', false);
|
||||||
export const CONTEXT_CHAT_INPUT_HAS_AGENT = new RawContextKey<boolean>('chatInputHasAgent', false);
|
export const CONTEXT_CHAT_INPUT_HAS_AGENT = new RawContextKey<boolean>('chatInputHasAgent', false);
|
||||||
export const CONTEXT_CHAT_LOCATION = new RawContextKey<ChatAgentLocation>('chatLocation', undefined);
|
export const CONTEXT_CHAT_LOCATION = new RawContextKey<ChatAgentLocation>('chatLocation', undefined);
|
||||||
|
|
|
||||||
|
|
@ -616,6 +616,8 @@ export type ISerializableChatDataIn = ISerializableChatData1 | ISerializableChat
|
||||||
* TODO- ChatModel#_deserialize and reviveSerializedAgent also still do some normalization and maybe that should be done in here too.
|
* TODO- ChatModel#_deserialize and reviveSerializedAgent also still do some normalization and maybe that should be done in here too.
|
||||||
*/
|
*/
|
||||||
export function normalizeSerializableChatData(raw: ISerializableChatDataIn): ISerializableChatData {
|
export function normalizeSerializableChatData(raw: ISerializableChatDataIn): ISerializableChatData {
|
||||||
|
normalizeOldFields(raw);
|
||||||
|
|
||||||
if (!('version' in raw)) {
|
if (!('version' in raw)) {
|
||||||
return {
|
return {
|
||||||
version: 3,
|
version: 3,
|
||||||
|
|
@ -636,6 +638,30 @@ export function normalizeSerializableChatData(raw: ISerializableChatDataIn): ISe
|
||||||
return raw;
|
return raw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeOldFields(raw: ISerializableChatDataIn): void {
|
||||||
|
// Fill in fields that very old chat data may be missing
|
||||||
|
if (!raw.sessionId) {
|
||||||
|
raw.sessionId = generateUuid();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!raw.creationDate) {
|
||||||
|
raw.creationDate = getLastYearDate();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('version' in raw && (raw.version === 2 || raw.version === 3)) {
|
||||||
|
if (!raw.lastMessageDate) {
|
||||||
|
// A bug led to not porting creationDate properly, and that was copied to lastMessageDate, so fix that up if missing.
|
||||||
|
raw.lastMessageDate = getLastYearDate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLastYearDate(): number {
|
||||||
|
const lastYearDate = new Date();
|
||||||
|
lastYearDate.setFullYear(lastYearDate.getFullYear() - 1);
|
||||||
|
return lastYearDate.getTime();
|
||||||
|
}
|
||||||
|
|
||||||
export function isExportableSessionData(obj: unknown): obj is IExportableChatData {
|
export function isExportableSessionData(obj: unknown): obj is IExportableChatData {
|
||||||
const data = obj as IExportableChatData;
|
const data = obj as IExportableChatData;
|
||||||
return typeof data === 'object' &&
|
return typeof data === 'object' &&
|
||||||
|
|
|
||||||
|
|
@ -481,9 +481,8 @@ export class ChatService extends Disposable implements IChatService {
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendRequest(sessionId: string, request: string, options?: IChatSendRequestOptions): Promise<IChatSendRequestData | undefined> {
|
async sendRequest(sessionId: string, request: string, options?: IChatSendRequestOptions): Promise<IChatSendRequestData | undefined> {
|
||||||
|
|
||||||
this.trace('sendRequest', `sessionId: ${sessionId}, message: ${request.substring(0, 20)}${request.length > 20 ? '[...]' : ''}}`);
|
this.trace('sendRequest', `sessionId: ${sessionId}, message: ${request.substring(0, 20)}${request.length > 20 ? '[...]' : ''}}`);
|
||||||
if (!request.trim()) {
|
if (!request.trim() && !options?.slashCommand && !options?.agentId) {
|
||||||
this.trace('sendRequest', 'Rejected empty message');
|
this.trace('sendRequest', 'Rejected empty message');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -546,6 +545,7 @@ export class ChatService extends Disposable implements IChatService {
|
||||||
const agentPart = 'kind' in parsedRequest ? undefined : parsedRequest.parts.find((r): r is ChatRequestAgentPart => r instanceof ChatRequestAgentPart);
|
const agentPart = 'kind' in parsedRequest ? undefined : parsedRequest.parts.find((r): r is ChatRequestAgentPart => r instanceof ChatRequestAgentPart);
|
||||||
const agentSlashCommandPart = 'kind' in parsedRequest ? undefined : parsedRequest.parts.find((r): r is ChatRequestAgentSubcommandPart => r instanceof ChatRequestAgentSubcommandPart);
|
const agentSlashCommandPart = 'kind' in parsedRequest ? undefined : parsedRequest.parts.find((r): r is ChatRequestAgentSubcommandPart => r instanceof ChatRequestAgentSubcommandPart);
|
||||||
const commandPart = 'kind' in parsedRequest ? undefined : parsedRequest.parts.find((r): r is ChatRequestSlashCommandPart => r instanceof ChatRequestSlashCommandPart);
|
const commandPart = 'kind' in parsedRequest ? undefined : parsedRequest.parts.find((r): r is ChatRequestSlashCommandPart => r instanceof ChatRequestSlashCommandPart);
|
||||||
|
const requests = [...model.getRequests()];
|
||||||
|
|
||||||
let gotProgress = false;
|
let gotProgress = false;
|
||||||
const requestType = commandPart ? 'slashCommand' : 'string';
|
const requestType = commandPart ? 'slashCommand' : 'string';
|
||||||
|
|
@ -639,7 +639,7 @@ export class ChatService extends Disposable implements IChatService {
|
||||||
|
|
||||||
if (this.configurationService.getValue('chat.experimental.detectParticipant.enabled') !== false && this.chatAgentService.hasChatParticipantDetectionProviders() && !agentPart && !commandPart && enableCommandDetection) {
|
if (this.configurationService.getValue('chat.experimental.detectParticipant.enabled') !== false && this.chatAgentService.hasChatParticipantDetectionProviders() && !agentPart && !commandPart && enableCommandDetection) {
|
||||||
// We have no agent or command to scope history with, pass the full history to the participant detection provider
|
// We have no agent or command to scope history with, pass the full history to the participant detection provider
|
||||||
const defaultAgentHistory = this.getHistoryEntriesFromModel(model, location, defaultAgent.id);
|
const defaultAgentHistory = this.getHistoryEntriesFromModel(requests, model.sessionId, location, defaultAgent.id);
|
||||||
|
|
||||||
// Prepare the request object that we will send to the participant detection provider
|
// Prepare the request object that we will send to the participant detection provider
|
||||||
const chatAgentRequest = await prepareChatAgentRequest(defaultAgent, agentSlashCommandPart?.command, enableCommandDetection, undefined, false);
|
const chatAgentRequest = await prepareChatAgentRequest(defaultAgent, agentSlashCommandPart?.command, enableCommandDetection, undefined, false);
|
||||||
|
|
@ -658,7 +658,7 @@ export class ChatService extends Disposable implements IChatService {
|
||||||
await this.extensionService.activateByEvent(`onChatParticipant:${agent.id}`);
|
await this.extensionService.activateByEvent(`onChatParticipant:${agent.id}`);
|
||||||
|
|
||||||
// Recompute history in case the agent or command changed
|
// Recompute history in case the agent or command changed
|
||||||
const history = this.getHistoryEntriesFromModel(model, location, agent.id);
|
const history = this.getHistoryEntriesFromModel(requests, model.sessionId, location, agent.id);
|
||||||
const requestProps = await prepareChatAgentRequest(agent, command, enableCommandDetection, request /* Reuse the request object if we already created it for participant detection */, !!detectedAgent);
|
const requestProps = await prepareChatAgentRequest(agent, command, enableCommandDetection, request /* Reuse the request object if we already created it for participant detection */, !!detectedAgent);
|
||||||
const pendingRequest = this._pendingRequests.get(sessionId);
|
const pendingRequest = this._pendingRequests.get(sessionId);
|
||||||
if (pendingRequest && !pendingRequest.requestId) {
|
if (pendingRequest && !pendingRequest.requestId) {
|
||||||
|
|
@ -668,7 +668,7 @@ export class ChatService extends Disposable implements IChatService {
|
||||||
const agentResult = await this.chatAgentService.invokeAgent(agent.id, requestProps, progressCallback, history, token);
|
const agentResult = await this.chatAgentService.invokeAgent(agent.id, requestProps, progressCallback, history, token);
|
||||||
rawResult = agentResult;
|
rawResult = agentResult;
|
||||||
agentOrCommandFollowups = this.chatAgentService.getFollowups(agent.id, requestProps, agentResult, history, followupsCancelToken);
|
agentOrCommandFollowups = this.chatAgentService.getFollowups(agent.id, requestProps, agentResult, history, followupsCancelToken);
|
||||||
chatTitlePromise = model.getRequests().length === 1 && !model.customTitle ? this.chatAgentService.getChatTitle(defaultAgent.id, this.getHistoryEntriesFromModel(model, location, agent.id), CancellationToken.None) : undefined;
|
chatTitlePromise = model.getRequests().length === 1 && !model.customTitle ? this.chatAgentService.getChatTitle(defaultAgent.id, this.getHistoryEntriesFromModel(model.getRequests(), model.sessionId, location, agent.id), CancellationToken.None) : undefined;
|
||||||
} else if (commandPart && this.chatSlashCommandService.hasCommand(commandPart.slashCommand.command)) {
|
} else if (commandPart && this.chatSlashCommandService.hasCommand(commandPart.slashCommand.command)) {
|
||||||
request = model.addRequest(parsedRequest, { variables: [] }, attempt);
|
request = model.addRequest(parsedRequest, { variables: [] }, attempt);
|
||||||
completeResponseCreated();
|
completeResponseCreated();
|
||||||
|
|
@ -775,9 +775,9 @@ export class ChatService extends Disposable implements IChatService {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private getHistoryEntriesFromModel(model: IChatModel, location: ChatAgentLocation, forAgentId: string): IChatAgentHistoryEntry[] {
|
private getHistoryEntriesFromModel(requests: IChatRequestModel[], sessionId: string, location: ChatAgentLocation, forAgentId: string): IChatAgentHistoryEntry[] {
|
||||||
const history: IChatAgentHistoryEntry[] = [];
|
const history: IChatAgentHistoryEntry[] = [];
|
||||||
for (const request of model.getRequests()) {
|
for (const request of requests) {
|
||||||
if (!request.response) {
|
if (!request.response) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -791,7 +791,7 @@ export class ChatService extends Disposable implements IChatService {
|
||||||
|
|
||||||
const promptTextResult = getPromptText(request.message);
|
const promptTextResult = getPromptText(request.message);
|
||||||
const historyRequest: IChatAgentRequest = {
|
const historyRequest: IChatAgentRequest = {
|
||||||
sessionId: model.sessionId,
|
sessionId: sessionId,
|
||||||
requestId: request.id,
|
requestId: request.id,
|
||||||
agentId: request.response.agent?.id ?? '',
|
agentId: request.response.agent?.id ?? '',
|
||||||
message: promptTextResult.message,
|
message: promptTextResult.message,
|
||||||
|
|
|
||||||
|
|
@ -941,12 +941,6 @@ export class StopReadAloud extends Action2 {
|
||||||
when: ScopedChatSynthesisInProgress,
|
when: ScopedChatSynthesisInProgress,
|
||||||
group: 'navigation',
|
group: 'navigation',
|
||||||
order: -1
|
order: -1
|
||||||
},
|
|
||||||
{
|
|
||||||
id: MENU_INLINE_CHAT_WIDGET_SECONDARY,
|
|
||||||
when: ScopedChatSynthesisInProgress,
|
|
||||||
group: 'navigation',
|
|
||||||
order: -1
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
@ -980,6 +974,15 @@ export class StopReadChatItemAloud extends Action2 {
|
||||||
CONTEXT_RESPONSE_FILTERED.negate() // but not when response is filtered
|
CONTEXT_RESPONSE_FILTERED.negate() // but not when response is filtered
|
||||||
),
|
),
|
||||||
group: 'navigation'
|
group: 'navigation'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: MENU_INLINE_CHAT_WIDGET_SECONDARY,
|
||||||
|
when: ContextKeyExpr.and(
|
||||||
|
ScopedChatSynthesisInProgress, // only when in progress
|
||||||
|
CONTEXT_RESPONSE, // only for responses
|
||||||
|
CONTEXT_RESPONSE_FILTERED.negate() // but not when response is filtered
|
||||||
|
),
|
||||||
|
group: 'navigation'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKe
|
||||||
import { ILogService, NullLogService } from 'vs/platform/log/common/log';
|
import { ILogService, NullLogService } from 'vs/platform/log/common/log';
|
||||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||||
import { ChatAgentLocation, ChatAgentService, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents';
|
import { ChatAgentLocation, ChatAgentService, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents';
|
||||||
import { ChatModel, ISerializableChatData1, ISerializableChatData2, normalizeSerializableChatData, Response } from 'vs/workbench/contrib/chat/common/chatModel';
|
import { ChatModel, ISerializableChatData1, ISerializableChatData2, ISerializableChatData3, normalizeSerializableChatData, Response } from 'vs/workbench/contrib/chat/common/chatModel';
|
||||||
import { ChatRequestTextPart } from 'vs/workbench/contrib/chat/common/chatParserTypes';
|
import { ChatRequestTextPart } from 'vs/workbench/contrib/chat/common/chatParserTypes';
|
||||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||||
import { TestExtensionService, TestStorageService } from 'vs/workbench/test/common/workbenchTestServices';
|
import { TestExtensionService, TestStorageService } from 'vs/workbench/test/common/workbenchTestServices';
|
||||||
|
|
@ -230,4 +230,53 @@ suite('normalizeSerializableChatData', () => {
|
||||||
assert.strictEqual(newData.lastMessageDate, v2Data.lastMessageDate);
|
assert.strictEqual(newData.lastMessageDate, v2Data.lastMessageDate);
|
||||||
assert.strictEqual(newData.customTitle, v2Data.computedTitle);
|
assert.strictEqual(newData.customTitle, v2Data.computedTitle);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('old bad data', () => {
|
||||||
|
const v1Data: ISerializableChatData1 = {
|
||||||
|
// Testing the scenario where these are missing
|
||||||
|
sessionId: undefined!,
|
||||||
|
creationDate: undefined!,
|
||||||
|
|
||||||
|
initialLocation: undefined,
|
||||||
|
isImported: false,
|
||||||
|
requesterAvatarIconUri: undefined,
|
||||||
|
requesterUsername: 'me',
|
||||||
|
requests: [],
|
||||||
|
responderAvatarIconUri: undefined,
|
||||||
|
responderUsername: 'bot',
|
||||||
|
welcomeMessage: []
|
||||||
|
};
|
||||||
|
|
||||||
|
const newData = normalizeSerializableChatData(v1Data);
|
||||||
|
assert.strictEqual(newData.version, 3);
|
||||||
|
assert.ok(newData.creationDate > 0);
|
||||||
|
assert.ok(newData.lastMessageDate > 0);
|
||||||
|
assert.ok(newData.sessionId);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('v3 with bug', () => {
|
||||||
|
const v3Data: ISerializableChatData3 = {
|
||||||
|
// Test case where old data was wrongly normalized and these fields were missing
|
||||||
|
creationDate: undefined!,
|
||||||
|
lastMessageDate: undefined!,
|
||||||
|
|
||||||
|
version: 3,
|
||||||
|
initialLocation: undefined,
|
||||||
|
isImported: false,
|
||||||
|
requesterAvatarIconUri: undefined,
|
||||||
|
requesterUsername: 'me',
|
||||||
|
requests: [],
|
||||||
|
responderAvatarIconUri: undefined,
|
||||||
|
responderUsername: 'bot',
|
||||||
|
sessionId: 'session1',
|
||||||
|
welcomeMessage: [],
|
||||||
|
customTitle: 'computed title'
|
||||||
|
};
|
||||||
|
|
||||||
|
const newData = normalizeSerializableChatData(v3Data);
|
||||||
|
assert.strictEqual(newData.version, 3);
|
||||||
|
assert.ok(newData.creationDate > 0);
|
||||||
|
assert.ok(newData.lastMessageDate > 0);
|
||||||
|
assert.ok(newData.sessionId);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -172,7 +172,7 @@ export class CommentsPanel extends FilterViewPane implements ICommentsView {
|
||||||
this.filters = this._register(new CommentsFilters({
|
this.filters = this._register(new CommentsFilters({
|
||||||
showResolved: this.viewState['showResolved'] !== false,
|
showResolved: this.viewState['showResolved'] !== false,
|
||||||
showUnresolved: this.viewState['showUnresolved'] !== false,
|
showUnresolved: this.viewState['showUnresolved'] !== false,
|
||||||
sortBy: this.viewState['sortBy'],
|
sortBy: this.viewState['sortBy'] ?? CommentsSortOrder.ResourceAscending,
|
||||||
}, this.contextKeyService));
|
}, this.contextKeyService));
|
||||||
this.filter = new Filter(new FilterOptions(this.filterWidget.getFilterText(), this.filters.showResolved, this.filters.showUnresolved));
|
this.filter = new Filter(new FilterOptions(this.filterWidget.getFilterText(), this.filters.showResolved, this.filters.showUnresolved));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||||
import { Disposable } from 'vs/base/common/lifecycle';
|
import { Disposable } from 'vs/base/common/lifecycle';
|
||||||
import { localize } from 'vs/nls';
|
import { localize } from 'vs/nls';
|
||||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||||
import { ContextKeyExpr, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||||
import { Event, Emitter } from 'vs/base/common/event';
|
import { Event, Emitter } from 'vs/base/common/event';
|
||||||
import { CommentsViewFilterFocusContextKey, ICommentsView } from 'vs/workbench/contrib/comments/browser/comments';
|
import { CommentsViewFilterFocusContextKey, ICommentsView } from 'vs/workbench/contrib/comments/browser/comments';
|
||||||
import { MenuId, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions';
|
import { MenuId, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions';
|
||||||
|
|
@ -74,9 +74,9 @@ export class CommentsFilters extends Disposable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _sortBy = CONTEXT_KEY_SORT_BY.bindTo(this.contextKeyService);
|
private _sortBy: IContextKey<CommentsSortOrder> = CONTEXT_KEY_SORT_BY.bindTo(this.contextKeyService);
|
||||||
get sortBy(): CommentsSortOrder {
|
get sortBy(): CommentsSortOrder {
|
||||||
return this._sortBy.get()!;
|
return this._sortBy.get() ?? CommentsSortOrder.ResourceAscending;
|
||||||
}
|
}
|
||||||
set sortBy(sortBy: CommentsSortOrder) {
|
set sortBy(sortBy: CommentsSortOrder) {
|
||||||
if (this._sortBy.get() !== sortBy) {
|
if (this._sortBy.get() !== sortBy) {
|
||||||
|
|
@ -208,7 +208,7 @@ registerAction2(class extends ViewAction<ICommentsView> {
|
||||||
icon: Codicon.history,
|
icon: Codicon.history,
|
||||||
viewId: COMMENTS_VIEW_ID,
|
viewId: COMMENTS_VIEW_ID,
|
||||||
toggled: {
|
toggled: {
|
||||||
condition: ContextKeyExpr.equals('commentsView.sortBy', CommentsSortOrder.UpdatedAtDescending),
|
condition: ContextKeyExpr.equals(CONTEXT_KEY_SORT_BY.key, CommentsSortOrder.UpdatedAtDescending),
|
||||||
title: localize('sorting by updated at', "Updated Time"),
|
title: localize('sorting by updated at', "Updated Time"),
|
||||||
},
|
},
|
||||||
menu: {
|
menu: {
|
||||||
|
|
@ -229,13 +229,13 @@ registerAction2(class extends ViewAction<ICommentsView> {
|
||||||
constructor() {
|
constructor() {
|
||||||
super({
|
super({
|
||||||
id: `workbench.actions.${COMMENTS_VIEW_ID}.toggleSortByResource`,
|
id: `workbench.actions.${COMMENTS_VIEW_ID}.toggleSortByResource`,
|
||||||
title: localize('toggle sorting by resource', "File"),
|
title: localize('toggle sorting by resource', "Position in File"),
|
||||||
category: localize('comments', "Comments"),
|
category: localize('comments', "Comments"),
|
||||||
icon: Codicon.history,
|
icon: Codicon.history,
|
||||||
viewId: COMMENTS_VIEW_ID,
|
viewId: COMMENTS_VIEW_ID,
|
||||||
toggled: {
|
toggled: {
|
||||||
condition: ContextKeyExpr.equals('commentsView.sortBy', CommentsSortOrder.ResourceAscending),
|
condition: ContextKeyExpr.equals(CONTEXT_KEY_SORT_BY.key, CommentsSortOrder.ResourceAscending),
|
||||||
title: localize('sorting by file', "File"),
|
title: localize('sorting by position in file', "Position in File"),
|
||||||
},
|
},
|
||||||
menu: {
|
menu: {
|
||||||
id: commentSortSubmenu,
|
id: commentSortSubmenu,
|
||||||
|
|
|
||||||
|
|
@ -12,20 +12,24 @@ import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cance
|
||||||
import { Codicon } from 'vs/base/common/codicons';
|
import { Codicon } from 'vs/base/common/codicons';
|
||||||
import { Emitter, Event } from 'vs/base/common/event';
|
import { Emitter, Event } from 'vs/base/common/event';
|
||||||
import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||||
import { autorun, autorunWithStore, derived, IObservable, ISettableObservable, observableValue } from 'vs/base/common/observable';
|
import { autorun, autorunWithStore, derived, IObservable, ISettableObservable, observableValue, transaction } from 'vs/base/common/observable';
|
||||||
import { ThemeIcon } from 'vs/base/common/themables';
|
import { ThemeIcon } from 'vs/base/common/themables';
|
||||||
import { Constants } from 'vs/base/common/uint';
|
import { Constants } from 'vs/base/common/uint';
|
||||||
import { URI } from 'vs/base/common/uri';
|
import { URI } from 'vs/base/common/uri';
|
||||||
import { generateUuid } from 'vs/base/common/uuid';
|
import { generateUuid } from 'vs/base/common/uuid';
|
||||||
import 'vs/css!./media/callStackWidget';
|
import 'vs/css!./media/callStackWidget';
|
||||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
import { EditorContributionCtor, EditorContributionInstantiation, IEditorContributionDescription } from 'vs/editor/browser/editorExtensions';
|
||||||
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget';
|
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget';
|
||||||
import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget';
|
import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget';
|
||||||
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
|
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||||
|
import { Position } from 'vs/editor/common/core/position';
|
||||||
import { Range } from 'vs/editor/common/core/range';
|
import { Range } from 'vs/editor/common/core/range';
|
||||||
|
import { IWordAtPosition } from 'vs/editor/common/core/wordHelper';
|
||||||
|
import { IEditorContribution, IEditorDecorationsCollection } from 'vs/editor/common/editorCommon';
|
||||||
import { Location } from 'vs/editor/common/languages';
|
import { Location } from 'vs/editor/common/languages';
|
||||||
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||||
|
import { ClickLinkGesture, ClickLinkMouseEvent } from 'vs/editor/contrib/gotoSymbol/browser/link/clickLinkGesture';
|
||||||
import { localize, localize2 } from 'vs/nls';
|
import { localize, localize2 } from 'vs/nls';
|
||||||
import { createActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem';
|
import { createActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem';
|
||||||
import { MenuWorkbenchToolBar } from 'vs/platform/actions/browser/toolbar';
|
import { MenuWorkbenchToolBar } from 'vs/platform/actions/browser/toolbar';
|
||||||
|
|
@ -38,7 +42,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
|
||||||
import { defaultButtonStyles } from 'vs/platform/theme/browser/defaultStyles';
|
import { defaultButtonStyles } from 'vs/platform/theme/browser/defaultStyles';
|
||||||
import { ResourceLabel } from 'vs/workbench/browser/labels';
|
import { ResourceLabel } from 'vs/workbench/browser/labels';
|
||||||
import { makeStackFrameColumnDecoration, TOP_STACK_FRAME_DECORATION } from 'vs/workbench/contrib/debug/browser/callStackEditorContribution';
|
import { makeStackFrameColumnDecoration, TOP_STACK_FRAME_DECORATION } from 'vs/workbench/contrib/debug/browser/callStackEditorContribution';
|
||||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
|
||||||
|
|
||||||
|
|
||||||
export class CallStackFrame {
|
export class CallStackFrame {
|
||||||
|
|
@ -97,6 +101,9 @@ class WrappedCustomStackFrame implements IFrameLikeItem {
|
||||||
constructor(public readonly original: CustomStackFrame) { }
|
constructor(public readonly original: CustomStackFrame) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isFrameLike = (item: unknown): item is IFrameLikeItem =>
|
||||||
|
item instanceof WrappedCallStackFrame || item instanceof WrappedCustomStackFrame;
|
||||||
|
|
||||||
type ListItem = WrappedCallStackFrame | SkippedCallFrames | WrappedCustomStackFrame;
|
type ListItem = WrappedCallStackFrame | SkippedCallFrames | WrappedCustomStackFrame;
|
||||||
|
|
||||||
const WIDGET_CLASS_NAME = 'multiCallStackWidget';
|
const WIDGET_CLASS_NAME = 'multiCallStackWidget';
|
||||||
|
|
@ -137,6 +144,7 @@ export class CallStackWidget extends Disposable {
|
||||||
multipleSelectionSupport: false,
|
multipleSelectionSupport: false,
|
||||||
mouseSupport: false,
|
mouseSupport: false,
|
||||||
keyboardSupport: false,
|
keyboardSupport: false,
|
||||||
|
setRowLineHeight: false,
|
||||||
accessibilityProvider: instantiationService.createInstance(StackAccessibilityProvider),
|
accessibilityProvider: instantiationService.createInstance(StackAccessibilityProvider),
|
||||||
}
|
}
|
||||||
) as WorkbenchList<ListItem>);
|
) as WorkbenchList<ListItem>);
|
||||||
|
|
@ -157,6 +165,17 @@ export class CallStackWidget extends Disposable {
|
||||||
this.layoutEmitter.fire();
|
this.layoutEmitter.fire();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public collapseAll() {
|
||||||
|
transaction(tx => {
|
||||||
|
for (let i = 0; i < this.list.length; i++) {
|
||||||
|
const frame = this.list.element(i);
|
||||||
|
if (isFrameLike(frame)) {
|
||||||
|
frame.collapsed.set(true, tx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private async loadFrame(replacing: SkippedCallFrames): Promise<void> {
|
private async loadFrame(replacing: SkippedCallFrames): Promise<void> {
|
||||||
if (!this.cts) {
|
if (!this.cts) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -356,9 +375,9 @@ abstract class AbstractFrameRenderer<T extends IAbstractFrameRendererTemplateDat
|
||||||
collapse.element.ariaExpanded = String(!collapsed);
|
collapse.element.ariaExpanded = String(!collapsed);
|
||||||
elements.root.classList.toggle('collapsed', collapsed);
|
elements.root.classList.toggle('collapsed', collapsed);
|
||||||
}));
|
}));
|
||||||
elementStore.add(collapse.onDidClick(() => {
|
const toggleCollapse = () => item.collapsed.set(!item.collapsed.get(), undefined);
|
||||||
item.collapsed.set(!item.collapsed.get(), undefined);
|
elementStore.add(collapse.onDidClick(toggleCollapse));
|
||||||
}));
|
elementStore.add(dom.addDisposableListener(elements.title, 'click', toggleCollapse));
|
||||||
}
|
}
|
||||||
|
|
||||||
disposeElement(element: ListItem, index: number, templateData: T, height: number | undefined): void {
|
disposeElement(element: ListItem, index: number, templateData: T, height: number | undefined): void {
|
||||||
|
|
@ -382,26 +401,33 @@ class FrameCodeRenderer extends AbstractFrameRenderer<IStackTemplateData> {
|
||||||
private readonly containingEditor: ICodeEditor | undefined,
|
private readonly containingEditor: ICodeEditor | undefined,
|
||||||
private readonly onLayout: Event<void>,
|
private readonly onLayout: Event<void>,
|
||||||
@ITextModelService private readonly modelService: ITextModelService,
|
@ITextModelService private readonly modelService: ITextModelService,
|
||||||
@ICodeEditorService private readonly editorService: ICodeEditorService,
|
|
||||||
@IInstantiationService instantiationService: IInstantiationService,
|
@IInstantiationService instantiationService: IInstantiationService,
|
||||||
) {
|
) {
|
||||||
super(instantiationService);
|
super(instantiationService);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override finishRenderTemplate(data: IAbstractFrameRendererTemplateData): IStackTemplateData {
|
protected override finishRenderTemplate(data: IAbstractFrameRendererTemplateData): IStackTemplateData {
|
||||||
|
// override default e.g. language contributions, only allow users to click
|
||||||
|
// on code in the call stack to go to its source location
|
||||||
|
const contributions: IEditorContributionDescription[] = [{
|
||||||
|
id: ClickToLocationContribution.ID,
|
||||||
|
instantiation: EditorContributionInstantiation.BeforeFirstInteraction,
|
||||||
|
ctor: ClickToLocationContribution as EditorContributionCtor,
|
||||||
|
}];
|
||||||
|
|
||||||
const editor = this.containingEditor
|
const editor = this.containingEditor
|
||||||
? this.instantiationService.createInstance(
|
? this.instantiationService.createInstance(
|
||||||
EmbeddedCodeEditorWidget,
|
EmbeddedCodeEditorWidget,
|
||||||
data.elements.editor,
|
data.elements.editor,
|
||||||
editorOptions,
|
editorOptions,
|
||||||
{ isSimpleWidget: true },
|
{ isSimpleWidget: true, contributions },
|
||||||
this.containingEditor,
|
this.containingEditor,
|
||||||
)
|
)
|
||||||
: this.instantiationService.createInstance(
|
: this.instantiationService.createInstance(
|
||||||
CodeEditorWidget,
|
CodeEditorWidget,
|
||||||
data.elements.editor,
|
data.elements.editor,
|
||||||
editorOptions,
|
editorOptions,
|
||||||
{ isSimpleWidget: true },
|
{ isSimpleWidget: true, contributions },
|
||||||
);
|
);
|
||||||
|
|
||||||
data.templateStore.add(editor);
|
data.templateStore.add(editor);
|
||||||
|
|
@ -423,20 +449,6 @@ class FrameCodeRenderer extends AbstractFrameRenderer<IStackTemplateData> {
|
||||||
const uri = item.source!;
|
const uri = item.source!;
|
||||||
|
|
||||||
template.label.element.setFile(uri);
|
template.label.element.setFile(uri);
|
||||||
template.elements.title.role = 'link';
|
|
||||||
elementStore.add(dom.addDisposableListener(template.elements.title, 'click', e => {
|
|
||||||
this.editorService.openCodeEditor({
|
|
||||||
resource: uri,
|
|
||||||
options: {
|
|
||||||
selection: Range.fromPositions({
|
|
||||||
column: item.column ?? 1,
|
|
||||||
lineNumber: item.line ?? 1,
|
|
||||||
}),
|
|
||||||
selectionRevealType: TextEditorSelectionRevealType.CenterIfOutsideViewport,
|
|
||||||
},
|
|
||||||
}, this.containingEditor || null, e.ctrlKey || e.metaKey);
|
|
||||||
}));
|
|
||||||
|
|
||||||
const cts = new CancellationTokenSource();
|
const cts = new CancellationTokenSource();
|
||||||
elementStore.add(toDisposable(() => cts.dispose(true)));
|
elementStore.add(toDisposable(() => cts.dispose(true)));
|
||||||
this.modelService.createModelReference(uri).then(reference => {
|
this.modelService.createModelReference(uri).then(reference => {
|
||||||
|
|
@ -632,6 +644,73 @@ class SkippedRenderer implements IListRenderer<ListItem, ISkippedTemplateData> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** A simple contribution that makes all data in the editor clickable to go to the location */
|
||||||
|
class ClickToLocationContribution extends Disposable implements IEditorContribution {
|
||||||
|
public static readonly ID = 'clickToLocation';
|
||||||
|
private readonly linkDecorations: IEditorDecorationsCollection;
|
||||||
|
private current: { line: number; word: IWordAtPosition } | undefined;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly editor: ICodeEditor,
|
||||||
|
@IEditorService editorService: IEditorService,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
this.linkDecorations = editor.createDecorationsCollection();
|
||||||
|
this._register(toDisposable(() => this.linkDecorations.clear()));
|
||||||
|
|
||||||
|
const clickLinkGesture = this._register(new ClickLinkGesture(editor));
|
||||||
|
|
||||||
|
this._register(clickLinkGesture.onMouseMoveOrRelevantKeyDown(([mouseEvent, keyboardEvent]) => {
|
||||||
|
this.onMove(mouseEvent);
|
||||||
|
}));
|
||||||
|
this._register(clickLinkGesture.onExecute((e) => {
|
||||||
|
const model = this.editor.getModel();
|
||||||
|
if (!this.current || !model) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
editorService.openEditor({
|
||||||
|
resource: model.uri,
|
||||||
|
options: {
|
||||||
|
selection: Range.fromPositions(new Position(this.current.line, this.current.word.startColumn)),
|
||||||
|
selectionRevealType: TextEditorSelectionRevealType.CenterIfOutsideViewport,
|
||||||
|
},
|
||||||
|
}, e.hasSideBySideModifier ? SIDE_GROUP : undefined);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private onMove(mouseEvent: ClickLinkMouseEvent) {
|
||||||
|
if (!mouseEvent.hasTriggerModifier) {
|
||||||
|
return this.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
const position = mouseEvent.target.position;
|
||||||
|
const word = position && this.editor.getModel()?.getWordAtPosition(position);
|
||||||
|
if (!word) {
|
||||||
|
return this.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
const prev = this.current?.word;
|
||||||
|
if (prev && prev.startColumn === word.startColumn && prev.endColumn === word.endColumn && prev.word === word.word) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.current = { word, line: position.lineNumber };
|
||||||
|
this.linkDecorations.set([{
|
||||||
|
range: new Range(position.lineNumber, word.startColumn, position.lineNumber, word.endColumn),
|
||||||
|
options: {
|
||||||
|
description: 'call-stack-go-to-file-link',
|
||||||
|
inlineClassName: 'call-stack-go-to-file-link',
|
||||||
|
},
|
||||||
|
}]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private clear() {
|
||||||
|
this.linkDecorations.clear();
|
||||||
|
this.current = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
registerAction2(class extends Action2 {
|
registerAction2(class extends Action2 {
|
||||||
constructor() {
|
constructor() {
|
||||||
super({
|
super({
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,10 @@
|
||||||
&[role="link"] {
|
&[role="link"] {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.monaco-icon-label::before {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.collapsed {
|
&.collapsed {
|
||||||
|
|
@ -39,6 +43,7 @@
|
||||||
.collapse-button {
|
.collapse-button {
|
||||||
width: 16px;
|
width: 16px;
|
||||||
min-height: 1px; /* show even if empty */
|
min-height: 1px; /* show even if empty */
|
||||||
|
line-height: 0;
|
||||||
|
|
||||||
a {
|
a {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
@ -56,6 +61,11 @@
|
||||||
.multiCallStackWidget {
|
.multiCallStackWidget {
|
||||||
.multiCallStackFrameContainer {
|
.multiCallStackFrameContainer {
|
||||||
background: none !important;
|
background: none !important;
|
||||||
line-height: inherit !important;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.monaco-editor .call-stack-go-to-file-link {
|
||||||
|
text-decoration: underline;
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--vscode-editorLink-activeForeground) !important;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,6 @@ class ExtensionsViewState extends Disposable implements IExtensionsViewState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export interface ExtensionsListViewOptions {
|
export interface ExtensionsListViewOptions {
|
||||||
server?: IExtensionManagementServer;
|
server?: IExtensionManagementServer;
|
||||||
flexibleHeight?: boolean;
|
flexibleHeight?: boolean;
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import { EmbeddedDiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/em
|
||||||
import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget';
|
import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget';
|
||||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||||
import { InlineChatController, InlineChatRunOptions } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController';
|
import { InlineChatController, InlineChatRunOptions } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController';
|
||||||
import { ACTION_ACCEPT_CHANGES, CTX_INLINE_CHAT_HAS_AGENT, CTX_INLINE_CHAT_HAS_STASHED_SESSION, CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_INNER_CURSOR_FIRST, CTX_INLINE_CHAT_INNER_CURSOR_LAST, CTX_INLINE_CHAT_VISIBLE, CTX_INLINE_CHAT_OUTER_CURSOR_POSITION, CTX_INLINE_CHAT_USER_DID_EDIT, CTX_INLINE_CHAT_DOCUMENT_CHANGED, CTX_INLINE_CHAT_EDIT_MODE, EditMode, MENU_INLINE_CHAT_WIDGET_STATUS, CTX_INLINE_CHAT_REQUEST_IN_PROGRESS, CTX_INLINE_CHAT_RESPONSE_TYPE, InlineChatResponseType, ACTION_REGENERATE_RESPONSE, MENU_INLINE_CHAT_CONTENT_STATUS, ACTION_VIEW_IN_CHAT, ACTION_TOGGLE_DIFF, CTX_INLINE_CHAT_CHANGE_HAS_DIFF, CTX_INLINE_CHAT_CHANGE_SHOWS_DIFF, MENU_INLINE_CHAT_ZONE } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
|
import { ACTION_ACCEPT_CHANGES, CTX_INLINE_CHAT_HAS_AGENT, CTX_INLINE_CHAT_HAS_STASHED_SESSION, CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_INNER_CURSOR_FIRST, CTX_INLINE_CHAT_INNER_CURSOR_LAST, CTX_INLINE_CHAT_VISIBLE, CTX_INLINE_CHAT_OUTER_CURSOR_POSITION, CTX_INLINE_CHAT_USER_DID_EDIT, CTX_INLINE_CHAT_DOCUMENT_CHANGED, CTX_INLINE_CHAT_EDIT_MODE, EditMode, MENU_INLINE_CHAT_WIDGET_STATUS, CTX_INLINE_CHAT_REQUEST_IN_PROGRESS, CTX_INLINE_CHAT_RESPONSE_TYPE, InlineChatResponseType, ACTION_REGENERATE_RESPONSE, MENU_INLINE_CHAT_CONTENT_STATUS, ACTION_VIEW_IN_CHAT, ACTION_TOGGLE_DIFF, CTX_INLINE_CHAT_CHANGE_HAS_DIFF, CTX_INLINE_CHAT_CHANGE_SHOWS_DIFF, MENU_INLINE_CHAT_ZONE, ACTION_DISCARD_CHANGES } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
|
||||||
import { localize, localize2 } from 'vs/nls';
|
import { localize, localize2 } from 'vs/nls';
|
||||||
import { Action2, IAction2Options } from 'vs/platform/actions/common/actions';
|
import { Action2, IAction2Options } from 'vs/platform/actions/common/actions';
|
||||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||||
|
|
@ -287,7 +287,7 @@ export class DiscardHunkAction extends AbstractInlineChatAction {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super({
|
super({
|
||||||
id: 'inlineChat.discardHunkChange',
|
id: ACTION_DISCARD_CHANGES,
|
||||||
title: localize('discard', 'Discard'),
|
title: localize('discard', 'Discard'),
|
||||||
icon: Codicon.chromeClose,
|
icon: Codicon.chromeClose,
|
||||||
precondition: CTX_INLINE_CHAT_VISIBLE,
|
precondition: CTX_INLINE_CHAT_VISIBLE,
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ import { ChatAgentLocation } from 'vs/workbench/contrib/chat/common/chatAgents';
|
||||||
import { ChatModel, ChatRequestRemovalReason, IChatRequestModel, IChatTextEditGroup, IChatTextEditGroupState, IResponse } from 'vs/workbench/contrib/chat/common/chatModel';
|
import { ChatModel, ChatRequestRemovalReason, IChatRequestModel, IChatTextEditGroup, IChatTextEditGroupState, IResponse } from 'vs/workbench/contrib/chat/common/chatModel';
|
||||||
import { IChatService } from 'vs/workbench/contrib/chat/common/chatService';
|
import { IChatService } from 'vs/workbench/contrib/chat/common/chatService';
|
||||||
import { InlineChatContentWidget } from 'vs/workbench/contrib/inlineChat/browser/inlineChatContentWidget';
|
import { InlineChatContentWidget } from 'vs/workbench/contrib/inlineChat/browser/inlineChatContentWidget';
|
||||||
import { HunkInformation, Session, StashedSession } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession';
|
import { HunkInformation, HunkState, Session, StashedSession } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession';
|
||||||
import { InlineChatError } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl';
|
import { InlineChatError } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl';
|
||||||
import { EditModeStrategy, HunkAction, IEditObserver, LiveStrategy, PreviewStrategy, ProgressingEditsOptions } from 'vs/workbench/contrib/inlineChat/browser/inlineChatStrategies';
|
import { EditModeStrategy, HunkAction, IEditObserver, LiveStrategy, PreviewStrategy, ProgressingEditsOptions } from 'vs/workbench/contrib/inlineChat/browser/inlineChatStrategies';
|
||||||
import { CTX_INLINE_CHAT_EDITING, CTX_INLINE_CHAT_REQUEST_IN_PROGRESS, CTX_INLINE_CHAT_RESPONSE_TYPE, CTX_INLINE_CHAT_USER_DID_EDIT, CTX_INLINE_CHAT_VISIBLE, EditMode, INLINE_CHAT_ID, InlineChatConfigKeys, InlineChatResponseType } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
|
import { CTX_INLINE_CHAT_EDITING, CTX_INLINE_CHAT_REQUEST_IN_PROGRESS, CTX_INLINE_CHAT_RESPONSE_TYPE, CTX_INLINE_CHAT_USER_DID_EDIT, CTX_INLINE_CHAT_VISIBLE, EditMode, INLINE_CHAT_ID, InlineChatConfigKeys, InlineChatResponseType } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
|
||||||
|
|
@ -622,6 +622,7 @@ export class InlineChatController implements IEditorContribution {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (e.kind === 'move') {
|
if (e.kind === 'move') {
|
||||||
|
assertType(this._session);
|
||||||
const log: typeof this._log = (msg: string, ...args: any[]) => this._log('state=_showRequest) moving inline chat', msg, ...args);
|
const log: typeof this._log = (msg: string, ...args: any[]) => this._log('state=_showRequest) moving inline chat', msg, ...args);
|
||||||
|
|
||||||
log('move was requested', e.target, e.range);
|
log('move was requested', e.target, e.range);
|
||||||
|
|
@ -636,13 +637,13 @@ export class InlineChatController implements IEditorContribution {
|
||||||
}
|
}
|
||||||
|
|
||||||
const newEditor = editorPane.getControl();
|
const newEditor = editorPane.getControl();
|
||||||
if (!newEditor || !isCodeEditor(newEditor) || !newEditor.hasModel()) {
|
if (!isCodeEditor(newEditor) || !newEditor.hasModel()) {
|
||||||
log('new editor is either missing or not a code editor or does not have a model');
|
log('new editor is either missing or not a code editor or does not have a model');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this._session) {
|
if (this._inlineChatSessionService.getSession(newEditor, e.target)) {
|
||||||
log('controller does not have a session');
|
log('new editor ALREADY has a session');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -741,7 +742,7 @@ export class InlineChatController implements IEditorContribution {
|
||||||
await responsePromise.p;
|
await responsePromise.p;
|
||||||
await progressiveEditsQueue.whenIdle();
|
await progressiveEditsQueue.whenIdle();
|
||||||
|
|
||||||
if (response.isCanceled) {
|
if (response.result?.errorDetails) {
|
||||||
await this._session.undoChangesUntil(response.requestId);
|
await this._session.undoChangesUntil(response.requestId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -758,7 +759,6 @@ export class InlineChatController implements IEditorContribution {
|
||||||
|
|
||||||
if (response.result?.errorDetails) {
|
if (response.result?.errorDetails) {
|
||||||
//
|
//
|
||||||
await this._session.undoChangesUntil(response.requestId);
|
|
||||||
|
|
||||||
} else if (response.response.value.length === 0) {
|
} else if (response.response.value.length === 0) {
|
||||||
// empty -> show message
|
// empty -> show message
|
||||||
|
|
@ -990,8 +990,21 @@ export class InlineChatController implements IEditorContribution {
|
||||||
// ---- controller API
|
// ---- controller API
|
||||||
|
|
||||||
showSaveHint(): void {
|
showSaveHint(): void {
|
||||||
const status = localize('savehint', "Accept or discard changes to continue saving");
|
if (!this._session) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const status = localize('savehint', "Accept or discard changes to continue saving.");
|
||||||
this._ui.value.zone.widget.updateStatus(status, { classes: ['warn'] });
|
this._ui.value.zone.widget.updateStatus(status, { classes: ['warn'] });
|
||||||
|
|
||||||
|
if (this._ui.value.zone.position) {
|
||||||
|
this._editor.revealLineInCenterIfOutsideViewport(this._ui.value.zone.position.lineNumber);
|
||||||
|
} else {
|
||||||
|
const hunk = this._session.hunkData.getInfo().find(info => info.getState() === HunkState.Pending);
|
||||||
|
if (hunk) {
|
||||||
|
this._editor.revealLineInCenterIfOutsideViewport(hunk.getRangesN()[0].startLineNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
acceptInput() {
|
acceptInput() {
|
||||||
|
|
|
||||||
|
|
@ -263,7 +263,7 @@ export class StashedSession {
|
||||||
// keep session for a little bit, only release when user continues to work (type, move cursor, etc.)
|
// keep session for a little bit, only release when user continues to work (type, move cursor, etc.)
|
||||||
this._session = session;
|
this._session = session;
|
||||||
this._ctxHasStashedSession.set(true);
|
this._ctxHasStashedSession.set(true);
|
||||||
this._listener = Event.once(Event.any(editor.onDidChangeCursorSelection, editor.onDidChangeModelContent, editor.onDidChangeModel))(() => {
|
this._listener = Event.once(Event.any(editor.onDidChangeCursorSelection, editor.onDidChangeModelContent, editor.onDidChangeModel, editor.onDidBlurEditorWidget))(() => {
|
||||||
this._session = undefined;
|
this._session = undefined;
|
||||||
this._sessionService.releaseSession(session);
|
this._sessionService.releaseSession(session);
|
||||||
this._ctxHasStashedSession.reset();
|
this._ctxHasStashedSession.reset();
|
||||||
|
|
@ -360,27 +360,30 @@ export class HunkData {
|
||||||
// mirror textModelN changes to textModel0 execept for those that
|
// mirror textModelN changes to textModel0 execept for those that
|
||||||
// overlap with a hunk
|
// overlap with a hunk
|
||||||
|
|
||||||
type HunkRangePair = { rangeN: Range; range0: Range };
|
type HunkRangePair = { rangeN: Range; range0: Range; markAccepted: () => void };
|
||||||
const hunkRanges: HunkRangePair[] = [];
|
const hunkRanges: HunkRangePair[] = [];
|
||||||
|
|
||||||
const ranges0: Range[] = [];
|
const ranges0: Range[] = [];
|
||||||
|
|
||||||
for (const { textModelNDecorations, textModel0Decorations, state } of this._data.values()) {
|
for (const entry of this._data.values()) {
|
||||||
|
|
||||||
if (state === HunkState.Pending) {
|
if (entry.state === HunkState.Pending) {
|
||||||
// pending means the hunk's changes aren't "sync'd" yet
|
// pending means the hunk's changes aren't "sync'd" yet
|
||||||
for (let i = 1; i < textModelNDecorations.length; i++) {
|
for (let i = 1; i < entry.textModelNDecorations.length; i++) {
|
||||||
const rangeN = this._textModelN.getDecorationRange(textModelNDecorations[i]);
|
const rangeN = this._textModelN.getDecorationRange(entry.textModelNDecorations[i]);
|
||||||
const range0 = this._textModel0.getDecorationRange(textModel0Decorations[i]);
|
const range0 = this._textModel0.getDecorationRange(entry.textModel0Decorations[i]);
|
||||||
if (rangeN && range0) {
|
if (rangeN && range0) {
|
||||||
hunkRanges.push({ rangeN, range0 });
|
hunkRanges.push({
|
||||||
|
rangeN, range0,
|
||||||
|
markAccepted: () => entry.state = HunkState.Accepted
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (state === HunkState.Accepted) {
|
} else if (entry.state === HunkState.Accepted) {
|
||||||
// accepted means the hunk's changes are also in textModel0
|
// accepted means the hunk's changes are also in textModel0
|
||||||
for (let i = 1; i < textModel0Decorations.length; i++) {
|
for (let i = 1; i < entry.textModel0Decorations.length; i++) {
|
||||||
const range = this._textModel0.getDecorationRange(textModel0Decorations[i]);
|
const range = this._textModel0.getDecorationRange(entry.textModel0Decorations[i]);
|
||||||
if (range) {
|
if (range) {
|
||||||
ranges0.push(range);
|
ranges0.push(range);
|
||||||
}
|
}
|
||||||
|
|
@ -399,16 +402,20 @@ export class HunkData {
|
||||||
|
|
||||||
let pendingChangesLen = 0;
|
let pendingChangesLen = 0;
|
||||||
|
|
||||||
for (const { rangeN, range0 } of hunkRanges) {
|
for (const entry of hunkRanges) {
|
||||||
if (rangeN.getEndPosition().isBefore(Range.getStartPosition(change.range))) {
|
if (entry.rangeN.getEndPosition().isBefore(Range.getStartPosition(change.range))) {
|
||||||
// pending hunk _before_ this change. When projecting into textModel0 we need to
|
// pending hunk _before_ this change. When projecting into textModel0 we need to
|
||||||
// subtract that. Because diffing is relaxed it might include changes that are not
|
// subtract that. Because diffing is relaxed it might include changes that are not
|
||||||
// actual insertions/deletions. Therefore we need to take the length of the original
|
// actual insertions/deletions. Therefore we need to take the length of the original
|
||||||
// range into account.
|
// range into account.
|
||||||
pendingChangesLen += this._textModelN.getValueLengthInRange(rangeN);
|
pendingChangesLen += this._textModelN.getValueLengthInRange(entry.rangeN);
|
||||||
pendingChangesLen -= this._textModel0.getValueLengthInRange(range0);
|
pendingChangesLen -= this._textModel0.getValueLengthInRange(entry.range0);
|
||||||
|
|
||||||
} else if (Range.areIntersectingOrTouching(rangeN, change.range)) {
|
} else if (Range.areIntersectingOrTouching(entry.rangeN, change.range)) {
|
||||||
|
// an edit overlaps with a (pending) hunk. We take this as a signal
|
||||||
|
// to mark the hunk as accepted and to ignore the edit. The range of the hunk
|
||||||
|
// will be up-to-date because of decorations created for them
|
||||||
|
entry.markAccepted();
|
||||||
isOverlapping = true;
|
isOverlapping = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
@ -447,24 +454,23 @@ export class HunkData {
|
||||||
|
|
||||||
diff ??= await this._editorWorkerService.computeDiff(this._textModel0.uri, this._textModelN.uri, { ignoreTrimWhitespace: false, maxComputationTimeMs: Number.MAX_SAFE_INTEGER, computeMoves: false }, 'advanced');
|
diff ??= await this._editorWorkerService.computeDiff(this._textModel0.uri, this._textModelN.uri, { ignoreTrimWhitespace: false, maxComputationTimeMs: Number.MAX_SAFE_INTEGER, computeMoves: false }, 'advanced');
|
||||||
|
|
||||||
if (!diff || diff.changes.length === 0) {
|
let mergedChanges: DetailedLineRangeMapping[] = [];
|
||||||
// return new HunkData([], session);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// merge changes neighboring changes
|
if (diff && diff.changes.length > 0) {
|
||||||
const mergedChanges = [diff.changes[0]];
|
// merge changes neighboring changes
|
||||||
for (let i = 1; i < diff.changes.length; i++) {
|
mergedChanges = [diff.changes[0]];
|
||||||
const lastChange = mergedChanges[mergedChanges.length - 1];
|
for (let i = 1; i < diff.changes.length; i++) {
|
||||||
const thisChange = diff.changes[i];
|
const lastChange = mergedChanges[mergedChanges.length - 1];
|
||||||
if (thisChange.modified.startLineNumber - lastChange.modified.endLineNumberExclusive <= HunkData._HUNK_THRESHOLD) {
|
const thisChange = diff.changes[i];
|
||||||
mergedChanges[mergedChanges.length - 1] = new DetailedLineRangeMapping(
|
if (thisChange.modified.startLineNumber - lastChange.modified.endLineNumberExclusive <= HunkData._HUNK_THRESHOLD) {
|
||||||
lastChange.original.join(thisChange.original),
|
mergedChanges[mergedChanges.length - 1] = new DetailedLineRangeMapping(
|
||||||
lastChange.modified.join(thisChange.modified),
|
lastChange.original.join(thisChange.original),
|
||||||
(lastChange.innerChanges ?? []).concat(thisChange.innerChanges ?? [])
|
lastChange.modified.join(thisChange.modified),
|
||||||
);
|
(lastChange.innerChanges ?? []).concat(thisChange.innerChanges ?? [])
|
||||||
} else {
|
);
|
||||||
mergedChanges.push(thisChange);
|
} else {
|
||||||
|
mergedChanges.push(thisChange);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import { coalesceInPlace } from 'vs/base/common/arrays';
|
||||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||||
import { Emitter, Event } from 'vs/base/common/event';
|
import { Emitter, Event } from 'vs/base/common/event';
|
||||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||||
import { themeColorFromId } from 'vs/base/common/themables';
|
import { themeColorFromId, ThemeIcon } from 'vs/base/common/themables';
|
||||||
import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, IViewZone, IViewZoneChangeAccessor } from 'vs/editor/browser/editorBrowser';
|
import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, IViewZone, IViewZoneChangeAccessor } from 'vs/editor/browser/editorBrowser';
|
||||||
import { StableEditorScrollState } from 'vs/editor/browser/stableEditorScroll';
|
import { StableEditorScrollState } from 'vs/editor/browser/stableEditorScroll';
|
||||||
import { LineSource, RenderOptions, renderLines } from 'vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/renderLines';
|
import { LineSource, RenderOptions, renderLines } from 'vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/renderLines';
|
||||||
|
|
@ -27,7 +27,7 @@ import { SaveReason } from 'vs/workbench/common/editor';
|
||||||
import { countWords } from 'vs/workbench/contrib/chat/common/chatWordCounter';
|
import { countWords } from 'vs/workbench/contrib/chat/common/chatWordCounter';
|
||||||
import { HunkInformation, Session, HunkState } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession';
|
import { HunkInformation, Session, HunkState } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession';
|
||||||
import { InlineChatZoneWidget } from './inlineChatZoneWidget';
|
import { InlineChatZoneWidget } from './inlineChatZoneWidget';
|
||||||
import { CTX_INLINE_CHAT_CHANGE_HAS_DIFF, CTX_INLINE_CHAT_CHANGE_SHOWS_DIFF, CTX_INLINE_CHAT_DOCUMENT_CHANGED, InlineChatConfigKeys, MENU_INLINE_CHAT_ZONE, minimapInlineChatDiffInserted, overviewRulerInlineChatDiffInserted } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
|
import { ACTION_TOGGLE_DIFF, CTX_INLINE_CHAT_CHANGE_HAS_DIFF, CTX_INLINE_CHAT_CHANGE_SHOWS_DIFF, CTX_INLINE_CHAT_DOCUMENT_CHANGED, InlineChatConfigKeys, MENU_INLINE_CHAT_ZONE, minimapInlineChatDiffInserted, overviewRulerInlineChatDiffInserted } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
|
||||||
import { assertType } from 'vs/base/common/types';
|
import { assertType } from 'vs/base/common/types';
|
||||||
import { IModelService } from 'vs/editor/common/services/model';
|
import { IModelService } from 'vs/editor/common/services/model';
|
||||||
import { performAsyncTextEdit, asProgressiveEdit } from './utils';
|
import { performAsyncTextEdit, asProgressiveEdit } from './utils';
|
||||||
|
|
@ -43,6 +43,9 @@ import { generateUuid } from 'vs/base/common/uuid';
|
||||||
import { MenuWorkbenchButtonBar } from 'vs/platform/actions/browser/buttonbar';
|
import { MenuWorkbenchButtonBar } from 'vs/platform/actions/browser/buttonbar';
|
||||||
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||||
import { Iterable } from 'vs/base/common/iterator';
|
import { Iterable } from 'vs/base/common/iterator';
|
||||||
|
import { ConflictActionsFactory, IContentWidgetAction } from 'vs/workbench/contrib/mergeEditor/browser/view/conflictActions';
|
||||||
|
import { observableValue } from 'vs/base/common/observable';
|
||||||
|
import { IMenuService, MenuItemAction } from 'vs/platform/actions/common/actions';
|
||||||
|
|
||||||
export interface IEditObserver {
|
export interface IEditObserver {
|
||||||
start(): void;
|
start(): void;
|
||||||
|
|
@ -216,8 +219,10 @@ type HunkDisplayData = {
|
||||||
|
|
||||||
decorationIds: string[];
|
decorationIds: string[];
|
||||||
|
|
||||||
viewZoneId: string | undefined;
|
diffViewZoneId: string | undefined;
|
||||||
viewZone: IViewZone;
|
diffViewZone: IViewZone;
|
||||||
|
|
||||||
|
lensActionsViewZoneIds?: string[];
|
||||||
|
|
||||||
distance: number;
|
distance: number;
|
||||||
position: Position;
|
position: Position;
|
||||||
|
|
@ -257,6 +262,7 @@ export class LiveStrategy extends EditModeStrategy {
|
||||||
private readonly _ctxCurrentChangeShowsDiff: IContextKey<boolean>;
|
private readonly _ctxCurrentChangeShowsDiff: IContextKey<boolean>;
|
||||||
|
|
||||||
private readonly _progressiveEditingDecorations: IEditorDecorationsCollection;
|
private readonly _progressiveEditingDecorations: IEditorDecorationsCollection;
|
||||||
|
private readonly _lensActionsFactory: ConflictActionsFactory;
|
||||||
private _editCount: number = 0;
|
private _editCount: number = 0;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
|
@ -268,6 +274,8 @@ export class LiveStrategy extends EditModeStrategy {
|
||||||
@IEditorWorkerService protected readonly _editorWorkerService: IEditorWorkerService,
|
@IEditorWorkerService protected readonly _editorWorkerService: IEditorWorkerService,
|
||||||
@IAccessibilityService private readonly _accessibilityService: IAccessibilityService,
|
@IAccessibilityService private readonly _accessibilityService: IAccessibilityService,
|
||||||
@IConfigurationService private readonly _configService: IConfigurationService,
|
@IConfigurationService private readonly _configService: IConfigurationService,
|
||||||
|
@IMenuService private readonly _menuService: IMenuService,
|
||||||
|
@IContextKeyService private readonly _contextService: IContextKeyService,
|
||||||
@ITextFileService textFileService: ITextFileService,
|
@ITextFileService textFileService: ITextFileService,
|
||||||
@IInstantiationService instaService: IInstantiationService
|
@IInstantiationService instaService: IInstantiationService
|
||||||
) {
|
) {
|
||||||
|
|
@ -276,6 +284,7 @@ export class LiveStrategy extends EditModeStrategy {
|
||||||
this._ctxCurrentChangeShowsDiff = CTX_INLINE_CHAT_CHANGE_SHOWS_DIFF.bindTo(contextKeyService);
|
this._ctxCurrentChangeShowsDiff = CTX_INLINE_CHAT_CHANGE_SHOWS_DIFF.bindTo(contextKeyService);
|
||||||
|
|
||||||
this._progressiveEditingDecorations = this._editor.createDecorationsCollection();
|
this._progressiveEditingDecorations = this._editor.createDecorationsCollection();
|
||||||
|
this._lensActionsFactory = this._store.add(new ConflictActionsFactory(this._editor));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -487,45 +496,95 @@ export class LiveStrategy extends EditModeStrategy {
|
||||||
afterLineNumber: -1,
|
afterLineNumber: -1,
|
||||||
heightInLines: result.heightInLines,
|
heightInLines: result.heightInLines,
|
||||||
domNode,
|
domNode,
|
||||||
ordinal: 50000 + 1 // more than https://github.com/microsoft/vscode/blob/bf52a5cfb2c75a7327c9adeaefbddc06d529dcad/src/vs/workbench/contrib/inlineChat/browser/inlineChatZoneWidget.ts#L42
|
ordinal: 50000 + 2 // more than https://github.com/microsoft/vscode/blob/bf52a5cfb2c75a7327c9adeaefbddc06d529dcad/src/vs/workbench/contrib/inlineChat/browser/inlineChatZoneWidget.ts#L42
|
||||||
};
|
};
|
||||||
|
|
||||||
const toggleDiff = () => {
|
const toggleDiff = () => {
|
||||||
const scrollState = StableEditorScrollState.capture(this._editor);
|
const scrollState = StableEditorScrollState.capture(this._editor);
|
||||||
changeDecorationsAndViewZones(this._editor, (_decorationsAccessor, viewZoneAccessor) => {
|
changeDecorationsAndViewZones(this._editor, (_decorationsAccessor, viewZoneAccessor) => {
|
||||||
assertType(data);
|
assertType(data);
|
||||||
if (!data.viewZoneId) {
|
if (!data.diffViewZoneId) {
|
||||||
const [hunkRange] = hunkData.getRangesN();
|
const [hunkRange] = hunkData.getRangesN();
|
||||||
viewZoneData.afterLineNumber = hunkRange.startLineNumber - 1;
|
viewZoneData.afterLineNumber = hunkRange.startLineNumber - 1;
|
||||||
data.viewZoneId = viewZoneAccessor.addZone(viewZoneData);
|
data.diffViewZoneId = viewZoneAccessor.addZone(viewZoneData);
|
||||||
overlay?.updateExtraTop(result.heightInLines);
|
overlay?.updateExtraTop(result.heightInLines);
|
||||||
} else {
|
} else {
|
||||||
viewZoneAccessor.removeZone(data.viewZoneId!);
|
viewZoneAccessor.removeZone(data.diffViewZoneId!);
|
||||||
overlay?.updateExtraTop(0);
|
overlay?.updateExtraTop(0);
|
||||||
data.viewZoneId = undefined;
|
data.diffViewZoneId = undefined;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this._ctxCurrentChangeShowsDiff.set(typeof data?.viewZoneId === 'string');
|
this._ctxCurrentChangeShowsDiff.set(typeof data?.diffViewZoneId === 'string');
|
||||||
scrollState.restore(this._editor);
|
scrollState.restore(this._editor);
|
||||||
};
|
};
|
||||||
|
|
||||||
const overlay = this._showOverlayToolbar
|
const overlay = this._showOverlayToolbar && false
|
||||||
? this._instaService.createInstance(InlineChangeOverlay, this._editor, hunkData)
|
? this._instaService.createInstance(InlineChangeOverlay, this._editor, hunkData)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
|
|
||||||
|
let lensActions: DisposableStore | undefined;
|
||||||
|
const lensActionsViewZoneIds: string[] = [];
|
||||||
|
|
||||||
|
if (this._showOverlayToolbar && hunkData.getState() === HunkState.Pending) {
|
||||||
|
|
||||||
|
lensActions = new DisposableStore();
|
||||||
|
|
||||||
|
const menu = this._menuService.createMenu(MENU_INLINE_CHAT_ZONE, this._contextService);
|
||||||
|
const makeActions = () => {
|
||||||
|
const actions: IContentWidgetAction[] = [];
|
||||||
|
const tuples = menu.getActions();
|
||||||
|
for (const [, group] of tuples) {
|
||||||
|
for (const item of group) {
|
||||||
|
if (item instanceof MenuItemAction) {
|
||||||
|
|
||||||
|
let text = item.label;
|
||||||
|
|
||||||
|
if (item.id === ACTION_TOGGLE_DIFF) {
|
||||||
|
text = item.checked ? 'Hide Changes' : 'Show Changes';
|
||||||
|
} else if (ThemeIcon.isThemeIcon(item.item.icon)) {
|
||||||
|
text = `$(${item.item.icon.id}) ${text}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
actions.push({
|
||||||
|
text,
|
||||||
|
tooltip: item.tooltip,
|
||||||
|
action: async () => item.run(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return actions;
|
||||||
|
};
|
||||||
|
|
||||||
|
const obs = observableValue(this, makeActions());
|
||||||
|
lensActions.add(menu.onDidChange(() => obs.set(makeActions(), undefined)));
|
||||||
|
lensActions.add(menu);
|
||||||
|
|
||||||
|
lensActions.add(this._lensActionsFactory.createWidget(viewZoneAccessor,
|
||||||
|
hunkRanges[0].startLineNumber - 1,
|
||||||
|
obs,
|
||||||
|
lensActionsViewZoneIds
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
const remove = () => {
|
const remove = () => {
|
||||||
changeDecorationsAndViewZones(this._editor, (decorationsAccessor, viewZoneAccessor) => {
|
changeDecorationsAndViewZones(this._editor, (decorationsAccessor, viewZoneAccessor) => {
|
||||||
assertType(data);
|
assertType(data);
|
||||||
for (const decorationId of data.decorationIds) {
|
for (const decorationId of data.decorationIds) {
|
||||||
decorationsAccessor.removeDecoration(decorationId);
|
decorationsAccessor.removeDecoration(decorationId);
|
||||||
}
|
}
|
||||||
if (data.viewZoneId) {
|
if (data.diffViewZoneId) {
|
||||||
viewZoneAccessor.removeZone(data.viewZoneId);
|
viewZoneAccessor.removeZone(data.diffViewZoneId);
|
||||||
}
|
}
|
||||||
data.decorationIds = [];
|
data.decorationIds = [];
|
||||||
data.viewZoneId = undefined;
|
data.diffViewZoneId = undefined;
|
||||||
|
|
||||||
|
data.lensActionsViewZoneIds?.forEach(viewZoneAccessor.removeZone);
|
||||||
|
data.lensActionsViewZoneIds = undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
lensActions?.dispose();
|
||||||
overlay?.dispose();
|
overlay?.dispose();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -548,8 +607,9 @@ export class LiveStrategy extends EditModeStrategy {
|
||||||
data = {
|
data = {
|
||||||
hunk: hunkData,
|
hunk: hunkData,
|
||||||
decorationIds,
|
decorationIds,
|
||||||
viewZoneId: '',
|
diffViewZoneId: '',
|
||||||
viewZone: viewZoneData,
|
diffViewZone: viewZoneData,
|
||||||
|
lensActionsViewZoneIds,
|
||||||
distance: myDistance,
|
distance: myDistance,
|
||||||
position: hunkRanges[0].getStartPosition().delta(-1),
|
position: hunkRanges[0].getStartPosition().delta(-1),
|
||||||
acceptHunk,
|
acceptHunk,
|
||||||
|
|
|
||||||
|
|
@ -96,9 +96,9 @@ export class InlineChatWidget {
|
||||||
h('div.accessibleViewer@accessibleViewer'),
|
h('div.accessibleViewer@accessibleViewer'),
|
||||||
h('div.status@status', [
|
h('div.status@status', [
|
||||||
h('div.label.info.hidden@infoLabel'),
|
h('div.label.info.hidden@infoLabel'),
|
||||||
h('div.actions.button-style.hidden@toolbar1'),
|
h('div.actions.hidden@toolbar1'),
|
||||||
h('div.label.status.hidden@statusLabel'),
|
h('div.label.status.hidden@statusLabel'),
|
||||||
h('div.actions.button-style.hidden@toolbar2'),
|
h('div.actions.secondary.hidden@toolbar2'),
|
||||||
]),
|
]),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
@ -385,7 +385,7 @@ export class InlineChatWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _getExtraHeight(): number {
|
protected _getExtraHeight(): number {
|
||||||
return 4 /* padding */ + 2 /*border*/ + 4 /*shadow*/;
|
return 2 /*border*/ + 4 /*shadow*/;
|
||||||
}
|
}
|
||||||
|
|
||||||
get value(): string {
|
get value(): string {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
import { addDisposableListener, Dimension } from 'vs/base/browser/dom';
|
import { addDisposableListener, Dimension } from 'vs/base/browser/dom';
|
||||||
import * as aria from 'vs/base/browser/ui/aria/aria';
|
import * as aria from 'vs/base/browser/ui/aria/aria';
|
||||||
import { toDisposable } from 'vs/base/common/lifecycle';
|
import { MutableDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||||
import { assertType } from 'vs/base/common/types';
|
import { assertType } from 'vs/base/common/types';
|
||||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||||
import { EditorLayoutInfo, EditorOption } from 'vs/editor/common/config/editorOptions';
|
import { EditorLayoutInfo, EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||||
|
|
@ -29,6 +29,7 @@ export class InlineChatZoneWidget extends ZoneWidget {
|
||||||
|
|
||||||
readonly widget: EditorBasedInlineChatWidget;
|
readonly widget: EditorBasedInlineChatWidget;
|
||||||
|
|
||||||
|
private readonly _scrollUp = this._disposables.add(new ScrollUpState(this.editor));
|
||||||
private readonly _ctxCursorPosition: IContextKey<'above' | 'below' | ''>;
|
private readonly _ctxCursorPosition: IContextKey<'above' | 'below' | ''>;
|
||||||
private _dimension?: Dimension;
|
private _dimension?: Dimension;
|
||||||
|
|
||||||
|
|
@ -165,6 +166,7 @@ export class InlineChatZoneWidget extends ZoneWidget {
|
||||||
this.widget.focus();
|
this.widget.focus();
|
||||||
|
|
||||||
revealZone();
|
revealZone();
|
||||||
|
this._scrollUp.enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
override updatePositionAndHeight(position: Position): void {
|
override updatePositionAndHeight(position: Position): void {
|
||||||
|
|
@ -186,14 +188,15 @@ export class InlineChatZoneWidget extends ZoneWidget {
|
||||||
return isResponseVM(candidate) && candidate.response.value.length > 0;
|
return isResponseVM(candidate) && candidate.response.value.length > 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (hasResponse && zoneTop < scrollTop) {
|
if (hasResponse && zoneTop < scrollTop || this._scrollUp.didScrollUp) {
|
||||||
// don't reveal the zone if it is already out of view (unless we are still getting ready)
|
// don't reveal the zone if it is already out of view (unless we are still getting ready)
|
||||||
return () => {
|
// or if an outside scroll-up happened (e.g the user scrolled up to see the new content)
|
||||||
|
return this._scrollUp.runIgnored(() => {
|
||||||
scrollState.restore(this.editor);
|
scrollState.restore(this.editor);
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
return this._scrollUp.runIgnored(() => {
|
||||||
scrollState.restore(this.editor);
|
scrollState.restore(this.editor);
|
||||||
|
|
||||||
const scrollTop = this.editor.getScrollTop();
|
const scrollTop = this.editor.getScrollTop();
|
||||||
|
|
@ -216,7 +219,7 @@ export class InlineChatZoneWidget extends ZoneWidget {
|
||||||
this._logService.trace('[IE] REVEAL zone', { zoneTop, lineTop, lineBottom, scrollTop, newScrollTop, forceScrollTop });
|
this._logService.trace('[IE] REVEAL zone', { zoneTop, lineTop, lineBottom, scrollTop, newScrollTop, forceScrollTop });
|
||||||
this.editor.setScrollTop(newScrollTop, ScrollType.Immediate);
|
this.editor.setScrollTop(newScrollTop, ScrollType.Immediate);
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override revealRange(range: Range, isLastLine: boolean): void {
|
protected override revealRange(range: Range, isLastLine: boolean): void {
|
||||||
|
|
@ -229,6 +232,7 @@ export class InlineChatZoneWidget extends ZoneWidget {
|
||||||
|
|
||||||
override hide(): void {
|
override hide(): void {
|
||||||
const scrollState = StableEditorBottomScrollState.capture(this.editor);
|
const scrollState = StableEditorBottomScrollState.capture(this.editor);
|
||||||
|
this._scrollUp.disable();
|
||||||
this._ctxCursorPosition.reset();
|
this._ctxCursorPosition.reset();
|
||||||
this.widget.reset();
|
this.widget.reset();
|
||||||
this.widget.chatWidget.setVisible(false);
|
this.widget.chatWidget.setVisible(false);
|
||||||
|
|
@ -237,3 +241,54 @@ export class InlineChatZoneWidget extends ZoneWidget {
|
||||||
scrollState.restore(this.editor);
|
scrollState.restore(this.editor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ScrollUpState {
|
||||||
|
|
||||||
|
private _lastScrollTop: number = this._editor.getScrollTop();
|
||||||
|
private _didScrollUp?: boolean;
|
||||||
|
private _ignoreEvents = false;
|
||||||
|
|
||||||
|
private readonly _listener = new MutableDisposable();
|
||||||
|
|
||||||
|
constructor(private readonly _editor: ICodeEditor) { }
|
||||||
|
|
||||||
|
dispose(): void {
|
||||||
|
this._listener.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
enable(): void {
|
||||||
|
this._didScrollUp = undefined;
|
||||||
|
this._listener.value = this._editor.onDidScrollChange(e => {
|
||||||
|
if (!e.scrollTopChanged || this._ignoreEvents) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const currentScrollTop = e.scrollTop;
|
||||||
|
if (currentScrollTop > this._lastScrollTop) {
|
||||||
|
this._listener.clear();
|
||||||
|
this._didScrollUp = true;
|
||||||
|
}
|
||||||
|
this._lastScrollTop = currentScrollTop;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
disable(): void {
|
||||||
|
this._listener.clear();
|
||||||
|
this._didScrollUp = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
runIgnored(callback: () => void): () => void {
|
||||||
|
return () => {
|
||||||
|
this._ignoreEvents = true;
|
||||||
|
try {
|
||||||
|
return callback();
|
||||||
|
} finally {
|
||||||
|
this._ignoreEvents = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
get didScrollUp(): boolean | undefined {
|
||||||
|
return this._didScrollUp;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.monaco-workbench .inline-chat .chat-widget .interactive-session .interactive-input-part {
|
.monaco-workbench .inline-chat .chat-widget .interactive-session .interactive-input-part {
|
||||||
padding: 4px 6px 0 6px;
|
padding: 2px 6px 0 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.monaco-workbench .inline-chat .chat-widget .interactive-session .interactive-input-part .interactive-execute-toolbar {
|
.monaco-workbench .inline-chat .chat-widget .interactive-session .interactive-input-part .interactive-execute-toolbar {
|
||||||
|
|
@ -32,6 +32,12 @@
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.monaco-workbench .inline-chat .chat-widget .interactive-session .interactive-input-part .interactive-input-followups .interactive-session-followups {
|
||||||
|
margin: 2px 0 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.monaco-workbench .inline-chat .chat-widget .interactive-session .interactive-list {
|
.monaco-workbench .inline-chat .chat-widget .interactive-session .interactive-list {
|
||||||
padding: 4px 0 0 0;
|
padding: 4px 0 0 0;
|
||||||
}
|
}
|
||||||
|
|
@ -70,7 +76,15 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 6px 6px 0 6px
|
padding-left: 6px;
|
||||||
|
padding-right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.monaco-workbench .inline-chat > .status {
|
||||||
|
.label,
|
||||||
|
.actions {
|
||||||
|
padding-top: 6px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.monaco-workbench .inline-chat .status .actions.hidden {
|
.monaco-workbench .inline-chat .status .actions.hidden {
|
||||||
|
|
@ -92,7 +106,8 @@
|
||||||
|
|
||||||
.monaco-workbench .inline-chat .status .label.status {
|
.monaco-workbench .inline-chat .status .label.status {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
padding: 0 6px;
|
padding-right: 6px;
|
||||||
|
padding-left: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.monaco-workbench .inline-chat .status .label.hidden,
|
.monaco-workbench .inline-chat .status .label.hidden,
|
||||||
|
|
@ -156,6 +171,16 @@
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.monaco-workbench .inline-chat .status .actions.secondary {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.monaco-workbench .inline-chat .status:hover .actions.secondary,
|
||||||
|
.monaco-workbench .inline-chat:focus .status .actions.secondary,
|
||||||
|
.monaco-workbench .inline-chat .status:focus-within .actions.secondary {
|
||||||
|
display: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
.monaco-workbench .inline-chat-diff-overlay {
|
.monaco-workbench .inline-chat-diff-overlay {
|
||||||
|
|
||||||
.monaco-button {
|
.monaco-button {
|
||||||
|
|
|
||||||
|
|
@ -116,6 +116,7 @@ export const CTX_INLINE_CHAT_RESPONSE_TYPE = new RawContextKey<InlineChatRespons
|
||||||
// --- (selected) action identifier
|
// --- (selected) action identifier
|
||||||
|
|
||||||
export const ACTION_ACCEPT_CHANGES = 'inlineChat.acceptChanges';
|
export const ACTION_ACCEPT_CHANGES = 'inlineChat.acceptChanges';
|
||||||
|
export const ACTION_DISCARD_CHANGES = 'inlineChat.discardHunkChange';
|
||||||
export const ACTION_REGENERATE_RESPONSE = 'inlineChat.regenerate';
|
export const ACTION_REGENERATE_RESPONSE = 'inlineChat.regenerate';
|
||||||
export const ACTION_VIEW_IN_CHAT = 'inlineChat.viewInChat';
|
export const ACTION_VIEW_IN_CHAT = 'inlineChat.viewInChat';
|
||||||
export const ACTION_TOGGLE_DIFF = 'inlineChat.toggleDiff';
|
export const ACTION_TOGGLE_DIFF = 'inlineChat.toggleDiff';
|
||||||
|
|
|
||||||
|
|
@ -779,7 +779,7 @@ suite('InteractiveChatController', function () {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Stopping/cancelling a request should undo its changes', async function () {
|
test('Stopping/cancelling a request should NOT undo its changes', async function () {
|
||||||
|
|
||||||
model.setValue('World');
|
model.setValue('World');
|
||||||
|
|
||||||
|
|
@ -819,7 +819,7 @@ suite('InteractiveChatController', function () {
|
||||||
chatService.cancelCurrentRequestForSession(ctrl.chatWidget.viewModel!.model.sessionId);
|
chatService.cancelCurrentRequestForSession(ctrl.chatWidget.viewModel!.model.sessionId);
|
||||||
assert.strictEqual(await p2, undefined);
|
assert.strictEqual(await p2, undefined);
|
||||||
|
|
||||||
assert.strictEqual(model.getValue(), 'World');
|
assert.strictEqual(model.getValue(), 'HelloWorld'); // CANCEL just stops the request and progressive typing but doesn't undo
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,8 @@ import { IWorkbenchAssignmentService } from 'vs/workbench/services/assignment/co
|
||||||
import { NullWorkbenchAssignmentService } from 'vs/workbench/services/assignment/test/common/nullAssignmentService';
|
import { NullWorkbenchAssignmentService } from 'vs/workbench/services/assignment/test/common/nullAssignmentService';
|
||||||
import { ILanguageModelToolsService } from 'vs/workbench/contrib/chat/common/languageModelToolsService';
|
import { ILanguageModelToolsService } from 'vs/workbench/contrib/chat/common/languageModelToolsService';
|
||||||
import { MockLanguageModelToolsService } from 'vs/workbench/contrib/chat/test/common/mockLanguageModelToolsService';
|
import { MockLanguageModelToolsService } from 'vs/workbench/contrib/chat/test/common/mockLanguageModelToolsService';
|
||||||
|
import { IChatRequestModel } from 'vs/workbench/contrib/chat/common/chatModel';
|
||||||
|
import { assertSnapshot } from 'vs/base/test/common/snapshot';
|
||||||
|
|
||||||
suite('InlineChatSession', function () {
|
suite('InlineChatSession', function () {
|
||||||
|
|
||||||
|
|
@ -487,4 +489,90 @@ suite('InlineChatSession', function () {
|
||||||
|
|
||||||
inlineChatSessionService.releaseSession(session);
|
inlineChatSessionService.releaseSession(session);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Pressing Escape after inline chat errored with "response filtered" leaves document dirty #7764', async function () {
|
||||||
|
|
||||||
|
const origValue = `class Foo {
|
||||||
|
private onError(error: string): void {
|
||||||
|
if (/The request timed out|The network connection was lost/i.test(error)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = error.replace(/See https:\/\/github\.com\/Squirrel\/Squirrel\.Mac\/issues\/182 for more information/, 'This might mean the application was put on quarantine by macOS. See [this link](https://github.com/microsoft/vscode/issues/7426#issuecomment-425093469) for more information');
|
||||||
|
|
||||||
|
this.notificationService.notify({
|
||||||
|
severity: Severity.Error,
|
||||||
|
message: error,
|
||||||
|
source: nls.localize('update service', "Update Service"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}`;
|
||||||
|
model.setValue(origValue);
|
||||||
|
|
||||||
|
const session = await inlineChatSessionService.createSession(editor, { editMode: EditMode.Live }, CancellationToken.None);
|
||||||
|
assertType(session);
|
||||||
|
|
||||||
|
const fakeRequest = new class extends mock<IChatRequestModel>() {
|
||||||
|
override get id() { return 'one'; }
|
||||||
|
};
|
||||||
|
session.markModelVersion(fakeRequest);
|
||||||
|
|
||||||
|
assert.strictEqual(editor.getModel().getLineCount(), 15);
|
||||||
|
|
||||||
|
await makeEditAsAi([EditOperation.replace(new Range(7, 1, 7, Number.MAX_SAFE_INTEGER), `error = error.replace(
|
||||||
|
/See https:\/\/github\.com\/Squirrel\/Squirrel\.Mac\/issues\/182 for more information/,
|
||||||
|
'This might mean the application was put on quarantine by macOS. See [this link](https://github.com/microsoft/vscode/issues/7426#issuecomment-425093469) for more information'
|
||||||
|
);`)]);
|
||||||
|
|
||||||
|
assert.strictEqual(editor.getModel().getLineCount(), 18);
|
||||||
|
|
||||||
|
// called when a response errors out
|
||||||
|
await session.undoChangesUntil(fakeRequest.id);
|
||||||
|
await session.hunkData.recompute({ applied: 0, sha1: 'fakeSha1' }, undefined);
|
||||||
|
|
||||||
|
assert.strictEqual(editor.getModel().getValue(), origValue);
|
||||||
|
|
||||||
|
session.hunkData.discardAll(); // called when dimissing the session
|
||||||
|
assert.strictEqual(editor.getModel().getValue(), origValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Apply Code\'s preview should be easier to undo/esc #7537', async function () {
|
||||||
|
model.setValue(`export function fib(n) {
|
||||||
|
if (n <= 0) return 0;
|
||||||
|
if (n === 1) return 0;
|
||||||
|
if (n === 2) return 1;
|
||||||
|
return fib(n - 1) + fib(n - 2);
|
||||||
|
}`);
|
||||||
|
const session = await inlineChatSessionService.createSession(editor, { editMode: EditMode.Live }, CancellationToken.None);
|
||||||
|
assertType(session);
|
||||||
|
|
||||||
|
await makeEditAsAi([EditOperation.replace(new Range(5, 1, 6, Number.MAX_SAFE_INTEGER), `
|
||||||
|
let a = 0, b = 1, c;
|
||||||
|
for (let i = 3; i <= n; i++) {
|
||||||
|
c = a + b;
|
||||||
|
a = b;
|
||||||
|
b = c;
|
||||||
|
}
|
||||||
|
return b;
|
||||||
|
}`)]);
|
||||||
|
|
||||||
|
assert.strictEqual(session.hunkData.size, 1);
|
||||||
|
assert.strictEqual(session.hunkData.pending, 1);
|
||||||
|
assert.ok(session.hunkData.getInfo().every(d => d.getState() === HunkState.Pending));
|
||||||
|
|
||||||
|
await assertSnapshot(editor.getModel().getValue(), { name: '1' });
|
||||||
|
|
||||||
|
await model.undo();
|
||||||
|
await assertSnapshot(editor.getModel().getValue(), { name: '2' });
|
||||||
|
|
||||||
|
// overlapping edits (even UNDO) mark edits as accepted
|
||||||
|
assert.strictEqual(session.hunkData.size, 1);
|
||||||
|
assert.strictEqual(session.hunkData.pending, 0);
|
||||||
|
assert.ok(session.hunkData.getInfo().every(d => d.getState() === HunkState.Accepted));
|
||||||
|
|
||||||
|
// no further change when discarding
|
||||||
|
session.hunkData.discardAll(); // CANCEL
|
||||||
|
await assertSnapshot(editor.getModel().getValue(), { name: '2' });
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -54,8 +54,8 @@ export class ConflictActionsFactory extends Disposable {
|
||||||
newStyle += `${this._styleClassName} { font-family: var(${fontFamilyVar}), ${EDITOR_FONT_DEFAULTS.fontFamily}}`;
|
newStyle += `${this._styleClassName} { font-family: var(${fontFamilyVar}), ${EDITOR_FONT_DEFAULTS.fontFamily}}`;
|
||||||
}
|
}
|
||||||
this._styleElement.textContent = newStyle;
|
this._styleElement.textContent = newStyle;
|
||||||
this._editor.getContainerDomNode().style.setProperty(fontFamilyVar, fontFamily ?? 'inherit');
|
this._editor.getContainerDomNode().style?.setProperty(fontFamilyVar, fontFamily ?? 'inherit');
|
||||||
this._editor.getContainerDomNode().style.setProperty(fontFeaturesVar, editorFontInfo.fontFeatureSettings);
|
this._editor.getContainerDomNode().style?.setProperty(fontFeaturesVar, editorFontInfo.fontFeatureSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getLayoutInfo() {
|
private _getLayoutInfo() {
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ export abstract class FixedZoneWidget extends Disposable {
|
||||||
domNode: document.createElement('div'),
|
domNode: document.createElement('div'),
|
||||||
afterLineNumber: afterLineNumber,
|
afterLineNumber: afterLineNumber,
|
||||||
heightInPx: height,
|
heightInPx: height,
|
||||||
|
ordinal: 50000 + 1,
|
||||||
onComputedHeight: (height) => {
|
onComputedHeight: (height) => {
|
||||||
this.widgetDomNode.style.height = `${height}px`;
|
this.widgetDomNode.style.height = `${height}px`;
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1074,7 +1074,7 @@ export class DeletedElement extends SingleSideDiffElement {
|
||||||
|
|
||||||
layout(state: IDiffElementLayoutState) {
|
layout(state: IDiffElementLayoutState) {
|
||||||
DOM.scheduleAtNextAnimationFrame(DOM.getWindow(this._diffEditorContainer), () => {
|
DOM.scheduleAtNextAnimationFrame(DOM.getWindow(this._diffEditorContainer), () => {
|
||||||
if (state.editorHeight || state.outerWidth) {
|
if ((state.editorHeight || state.outerWidth) && this._editor) {
|
||||||
this._editorContainer.style.height = `${this.cell.layoutInfo.editorHeight}px`;
|
this._editorContainer.style.height = `${this.cell.layoutInfo.editorHeight}px`;
|
||||||
this._editor.layout({
|
this._editor.layout({
|
||||||
width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, false),
|
width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, false),
|
||||||
|
|
@ -1254,7 +1254,7 @@ export class InsertElement extends SingleSideDiffElement {
|
||||||
|
|
||||||
layout(state: IDiffElementLayoutState) {
|
layout(state: IDiffElementLayoutState) {
|
||||||
DOM.scheduleAtNextAnimationFrame(DOM.getWindow(this._diffEditorContainer), () => {
|
DOM.scheduleAtNextAnimationFrame(DOM.getWindow(this._diffEditorContainer), () => {
|
||||||
if (state.editorHeight || state.outerWidth) {
|
if ((state.editorHeight || state.outerWidth) && this._editor) {
|
||||||
this._editorContainer.style.height = `${this.cell.layoutInfo.editorHeight}px`;
|
this._editorContainer.style.height = `${this.cell.layoutInfo.editorHeight}px`;
|
||||||
this._editor.layout({
|
this._editor.layout({
|
||||||
width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, false),
|
width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, false),
|
||||||
|
|
@ -1644,7 +1644,7 @@ export class ModifiedElement extends AbstractElementRenderer {
|
||||||
{
|
{
|
||||||
updateInfoRendering: () => renderSourceEditor(),
|
updateInfoRendering: () => renderSourceEditor(),
|
||||||
checkIfModified: (cell) => {
|
checkIfModified: (cell) => {
|
||||||
return cell.modified?.textModel.getValue() !== cell.original?.textModel.getValue() ? { reason: undefined } : false;
|
return cell.modified?.textModel.getTextBufferHash() !== cell.original?.textModel.getTextBufferHash() ? { reason: undefined } : false;
|
||||||
},
|
},
|
||||||
getFoldingState: (cell) => cell.cellFoldingState,
|
getFoldingState: (cell) => cell.cellFoldingState,
|
||||||
updateFoldingState: (cell, state) => cell.cellFoldingState = state,
|
updateFoldingState: (cell, state) => cell.cellFoldingState = state,
|
||||||
|
|
@ -1660,7 +1660,7 @@ export class ModifiedElement extends AbstractElementRenderer {
|
||||||
const scopedContextKeyService = this.contextKeyService.createScoped(this.templateData.inputToolbarContainer);
|
const scopedContextKeyService = this.contextKeyService.createScoped(this.templateData.inputToolbarContainer);
|
||||||
this._register(scopedContextKeyService);
|
this._register(scopedContextKeyService);
|
||||||
const inputChanged = NOTEBOOK_DIFF_CELL_INPUT.bindTo(scopedContextKeyService);
|
const inputChanged = NOTEBOOK_DIFF_CELL_INPUT.bindTo(scopedContextKeyService);
|
||||||
inputChanged.set(this.cell.modified.textModel.getValue() !== this.cell.original.textModel.getValue());
|
inputChanged.set(this.cell.modified.textModel.getTextBufferHash() !== this.cell.original.textModel.getTextBufferHash());
|
||||||
|
|
||||||
const ignoreWhitespace = NOTEBOOK_DIFF_CELL_IGNORE_WHITESPACE.bindTo(scopedContextKeyService);
|
const ignoreWhitespace = NOTEBOOK_DIFF_CELL_IGNORE_WHITESPACE.bindTo(scopedContextKeyService);
|
||||||
const ignore = this.textConfigurationService.getValue<boolean>(this.cell.modified.uri, 'diffEditor.ignoreTrimWhitespace');
|
const ignore = this.textConfigurationService.getValue<boolean>(this.cell.modified.uri, 'diffEditor.ignoreTrimWhitespace');
|
||||||
|
|
@ -1675,7 +1675,7 @@ export class ModifiedElement extends AbstractElementRenderer {
|
||||||
const refreshToolbar = () => {
|
const refreshToolbar = () => {
|
||||||
const ignore = this.textConfigurationService.getValue<boolean>(this.cell.modified.uri, 'diffEditor.ignoreTrimWhitespace');
|
const ignore = this.textConfigurationService.getValue<boolean>(this.cell.modified.uri, 'diffEditor.ignoreTrimWhitespace');
|
||||||
ignoreWhitespace.set(ignore);
|
ignoreWhitespace.set(ignore);
|
||||||
const hasChanges = this.cell.modified.textModel.getValue() !== this.cell.original.textModel.getValue();
|
const hasChanges = this.cell.modified.textModel.getTextBufferHash() !== this.cell.original.textModel.getTextBufferHash();
|
||||||
inputChanged.set(hasChanges);
|
inputChanged.set(hasChanges);
|
||||||
|
|
||||||
if (hasChanges) {
|
if (hasChanges) {
|
||||||
|
|
|
||||||
|
|
@ -222,7 +222,7 @@ export abstract class DiffElementCellViewModelBase extends DiffElementViewModelB
|
||||||
layoutState: CellLayoutState.Uninitialized
|
layoutState: CellLayoutState.Uninitialized
|
||||||
};
|
};
|
||||||
|
|
||||||
this.cellFoldingState = modified?.textModel?.getValue() !== original?.textModel?.getValue() ? PropertyFoldingState.Expanded : PropertyFoldingState.Collapsed;
|
this.cellFoldingState = modified?.getTextBufferHash() !== original?.getTextBufferHash() ? PropertyFoldingState.Expanded : PropertyFoldingState.Collapsed;
|
||||||
this.metadataFoldingState = PropertyFoldingState.Collapsed;
|
this.metadataFoldingState = PropertyFoldingState.Collapsed;
|
||||||
this.outputFoldingState = PropertyFoldingState.Collapsed;
|
this.outputFoldingState = PropertyFoldingState.Collapsed;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -441,16 +441,12 @@ function createDiffViewModels(instantiationService: IInstantiationService, confi
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
case 'unchanged': {
|
case 'unchanged': {
|
||||||
const originalCell = originalModel.cells[diff.originalCellIndex];
|
|
||||||
const modifiedCell = modifiedModel.cells[diff.modifiedCellIndex];
|
|
||||||
const type = (originalCell.textModel?.getValue() !== modifiedCell.textModel?.getValue()) ? 'modified' : 'unchanged';
|
|
||||||
return new SideBySideDiffElementViewModel(
|
return new SideBySideDiffElementViewModel(
|
||||||
model.modified.notebook,
|
model.modified.notebook,
|
||||||
model.original.notebook,
|
model.original.notebook,
|
||||||
originalCell,
|
originalModel.cells[diff.originalCellIndex],
|
||||||
modifiedCell,
|
modifiedModel.cells[diff.modifiedCellIndex],
|
||||||
type,
|
'unchanged', eventDispatcher,
|
||||||
eventDispatcher,
|
|
||||||
initData,
|
initData,
|
||||||
notebookService
|
notebookService
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -419,6 +419,10 @@ export class NotebookCellTextModel extends Disposable implements ICell {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.outputs.length !== b.outputs.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.getTextLength() !== b.getTextLength()) {
|
if (this.getTextLength() !== b.getTextLength()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -314,7 +314,7 @@ class HistoryItemRenderer implements ITreeRenderer<SCMHistoryItemViewModelTreeEl
|
||||||
const historyItemLabels = (historyItem.labels ?? [])
|
const historyItemLabels = (historyItem.labels ?? [])
|
||||||
.filter(l => labels.includes(l.title));
|
.filter(l => labels.includes(l.title));
|
||||||
|
|
||||||
if (historyItemLabels) {
|
if (historyItemLabels.length > 0) {
|
||||||
const historyItemGroupLocalColor = colorTheme.getColor(historyItemGroupLocal);
|
const historyItemGroupLocalColor = colorTheme.getColor(historyItemGroupLocal);
|
||||||
const historyItemGroupRemoteColor = colorTheme.getColor(historyItemGroupRemote);
|
const historyItemGroupRemoteColor = colorTheme.getColor(historyItemGroupRemote);
|
||||||
const historyItemGroupBaseColor = colorTheme.getColor(historyItemGroupBase);
|
const historyItemGroupBaseColor = colorTheme.getColor(historyItemGroupBase);
|
||||||
|
|
|
||||||
|
|
@ -117,22 +117,21 @@ export class NotebookSearchService implements INotebookSearchService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private async doesFileExist(includes: string[], folderQueries: IFolderQuery<URI>[], token: CancellationToken): Promise<boolean> {
|
private async doesFileExist(includes: string[], folderQueries: IFolderQuery<URI>[], token: CancellationToken): Promise<boolean> {
|
||||||
const promises: Promise<void>[] = includes.map(async includePattern => {
|
const promises: Promise<boolean>[] = includes.map(async includePattern => {
|
||||||
const query = this.queryBuilder.file(folderQueries.map(e => e.folder), {
|
const query = this.queryBuilder.file(folderQueries.map(e => e.folder), {
|
||||||
includePattern: includePattern.startsWith('/') ? includePattern : '**/' + includePattern, // todo: find cleaner way to ensure that globs match all appropriate filetypes
|
includePattern: includePattern.startsWith('/') ? includePattern : '**/' + includePattern, // todo: find cleaner way to ensure that globs match all appropriate filetypes
|
||||||
exists: true
|
exists: true,
|
||||||
|
onlyFileScheme: true,
|
||||||
});
|
});
|
||||||
return this.searchService.fileSearch(
|
return this.searchService.fileSearch(
|
||||||
query,
|
query,
|
||||||
token
|
token
|
||||||
).then((ret) => {
|
).then((ret) => {
|
||||||
if (!ret.limitHit) {
|
return !!ret.limitHit;
|
||||||
throw Error('File not found');
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return Promise.any(promises).then(() => true).catch(() => false);
|
return Promise.any(promises);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getClosedNotebookResults(textQuery: ITextQuery, scannedFiles: ResourceSet, token: CancellationToken): Promise<IClosedNotebookSearchResults> {
|
private async getClosedNotebookResults(textQuery: ITextQuery, scannedFiles: ResourceSet, token: CancellationToken): Promise<IClosedNotebookSearchResults> {
|
||||||
|
|
|
||||||
|
|
@ -328,10 +328,10 @@ __vsc_restore_exit_code() {
|
||||||
|
|
||||||
__vsc_prompt_cmd_original() {
|
__vsc_prompt_cmd_original() {
|
||||||
__vsc_status="$?"
|
__vsc_status="$?"
|
||||||
|
builtin local cmd
|
||||||
__vsc_restore_exit_code "${__vsc_status}"
|
__vsc_restore_exit_code "${__vsc_status}"
|
||||||
# Evaluate the original PROMPT_COMMAND similarly to how bash would normally
|
# Evaluate the original PROMPT_COMMAND similarly to how bash would normally
|
||||||
# See https://unix.stackexchange.com/a/672843 for technique
|
# See https://unix.stackexchange.com/a/672843 for technique
|
||||||
builtin local cmd
|
|
||||||
for cmd in "${__vsc_original_prompt_command[@]}"; do
|
for cmd in "${__vsc_original_prompt_command[@]}"; do
|
||||||
eval "${cmd:-}"
|
eval "${cmd:-}"
|
||||||
done
|
done
|
||||||
|
|
|
||||||
|
|
@ -172,9 +172,9 @@ function Set-MappedKeyHandler {
|
||||||
|
|
||||||
function Get-KeywordCompletionResult(
|
function Get-KeywordCompletionResult(
|
||||||
$Keyword,
|
$Keyword,
|
||||||
$Description = $null
|
$Description = $Keyword
|
||||||
) {
|
) {
|
||||||
[System.Management.Automation.CompletionResult]::new($Keyword, $Keyword, [System.Management.Automation.CompletionResultType]::Keyword, $null -ne $Description ? $Description : $Keyword)
|
[System.Management.Automation.CompletionResult]::new($Keyword, $Keyword, [System.Management.Automation.CompletionResultType]::Keyword, $Description)
|
||||||
}
|
}
|
||||||
|
|
||||||
function Set-MappedKeyHandlers {
|
function Set-MappedKeyHandlers {
|
||||||
|
|
@ -360,7 +360,7 @@ function Send-Completions {
|
||||||
# completions are consistent regardless of where it was requested
|
# completions are consistent regardless of where it was requested
|
||||||
elseif ($lastWord -match '[/\\]') {
|
elseif ($lastWord -match '[/\\]') {
|
||||||
$lastSlashIndex = $completionPrefix.LastIndexOfAny(@('/', '\'))
|
$lastSlashIndex = $completionPrefix.LastIndexOfAny(@('/', '\'))
|
||||||
if ($lastSlashIndex -ne -1 && $lastSlashIndex -lt $cursorIndex) {
|
if ($lastSlashIndex -ne -1 -and $lastSlashIndex -lt $cursorIndex) {
|
||||||
$newCursorIndex = $lastSlashIndex + 1
|
$newCursorIndex = $lastSlashIndex + 1
|
||||||
$completionPrefix = $completionPrefix.Substring(0, $newCursorIndex)
|
$completionPrefix = $completionPrefix.Substring(0, $newCursorIndex)
|
||||||
$prefixCursorDelta = $cursorIndex - $newCursorIndex
|
$prefixCursorDelta = $cursorIndex - $newCursorIndex
|
||||||
|
|
@ -388,9 +388,9 @@ function Send-Completions {
|
||||||
if ($completions.CompletionMatches.Count -gt 0 -and $completions.CompletionMatches.Where({ $_.ResultType -eq 3 -or $_.ResultType -eq 4 })) {
|
if ($completions.CompletionMatches.Count -gt 0 -and $completions.CompletionMatches.Where({ $_.ResultType -eq 3 -or $_.ResultType -eq 4 })) {
|
||||||
# Add `../ relative to the top completion
|
# Add `../ relative to the top completion
|
||||||
$firstCompletion = $completions.CompletionMatches[0]
|
$firstCompletion = $completions.CompletionMatches[0]
|
||||||
if ($firstCompletion.CompletionText.StartsWith('../')) {
|
if ($firstCompletion.CompletionText.StartsWith("..$([System.IO.Path]::DirectorySeparatorChar)")) {
|
||||||
if ($completionPrefix -match '(\.\.\/)+') {
|
if ($completionPrefix -match "(\.\.\$([System.IO.Path]::DirectorySeparatorChar))+") {
|
||||||
$parentDir = "$($matches[0])../"
|
$parentDir = "$($matches[0])..$([System.IO.Path]::DirectorySeparatorChar)"
|
||||||
$currentPath = Split-Path -Parent $firstCompletion.ToolTip
|
$currentPath = Split-Path -Parent $firstCompletion.ToolTip
|
||||||
try {
|
try {
|
||||||
$parentDirPath = Split-Path -Parent $currentPath
|
$parentDirPath = Split-Path -Parent $currentPath
|
||||||
|
|
@ -430,7 +430,7 @@ function Send-Completions {
|
||||||
# completions are consistent regardless of where it was requested
|
# completions are consistent regardless of where it was requested
|
||||||
if ($completionPrefix -match '[/\\]') {
|
if ($completionPrefix -match '[/\\]') {
|
||||||
$lastSlashIndex = $completionPrefix.LastIndexOfAny(@('/', '\'))
|
$lastSlashIndex = $completionPrefix.LastIndexOfAny(@('/', '\'))
|
||||||
if ($lastSlashIndex -ne -1 && $lastSlashIndex -lt $cursorIndex) {
|
if ($lastSlashIndex -ne -1 -and $lastSlashIndex -lt $cursorIndex) {
|
||||||
$newCursorIndex = $lastSlashIndex + 1
|
$newCursorIndex = $lastSlashIndex + 1
|
||||||
$completionPrefix = $completionPrefix.Substring(0, $newCursorIndex)
|
$completionPrefix = $completionPrefix.Substring(0, $newCursorIndex)
|
||||||
$prefixCursorDelta = $cursorIndex - $newCursorIndex
|
$prefixCursorDelta = $cursorIndex - $newCursorIndex
|
||||||
|
|
|
||||||
|
|
@ -2305,7 +2305,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
||||||
|
|
||||||
async handleMouseEvent(event: MouseEvent, contextMenu: IMenu): Promise<{ cancelContextMenu: boolean } | void> {
|
async handleMouseEvent(event: MouseEvent, contextMenu: IMenu): Promise<{ cancelContextMenu: boolean } | void> {
|
||||||
// Don't handle mouse event if it was on the scroll bar
|
// Don't handle mouse event if it was on the scroll bar
|
||||||
if (dom.isHTMLElement(event.target) && event.target.classList.contains('scrollbar')) {
|
if (dom.isHTMLElement(event.target) && (event.target.classList.contains('scrollbar') || event.target.classList.contains('slider'))) {
|
||||||
return { cancelContextMenu: true };
|
return { cancelContextMenu: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import { AutoOpenBarrier } from 'vs/base/common/async';
|
||||||
import { Event } from 'vs/base/common/event';
|
import { Event } from 'vs/base/common/event';
|
||||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||||
import { DisposableStore, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
import { DisposableStore, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||||
|
import { isWindows } from 'vs/base/common/platform';
|
||||||
import { localize2 } from 'vs/nls';
|
import { localize2 } from 'vs/nls';
|
||||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||||
import { ContextKeyExpr, IContextKey, IContextKeyService, IReadableSet } from 'vs/platform/contextkey/common/contextkey';
|
import { ContextKeyExpr, IContextKey, IContextKeyService, IReadableSet } from 'vs/platform/contextkey/common/contextkey';
|
||||||
|
|
@ -170,16 +171,20 @@ class TerminalSuggestContribution extends DisposableStore implements ITerminalCo
|
||||||
|
|
||||||
// If completions are requested, pause and queue input events until completions are
|
// If completions are requested, pause and queue input events until completions are
|
||||||
// received. This fixing some problems in PowerShell, particularly enter not executing
|
// received. This fixing some problems in PowerShell, particularly enter not executing
|
||||||
// when typing quickly and some characters being printed twice.
|
// when typing quickly and some characters being printed twice. On Windows this isn't
|
||||||
let barrier: AutoOpenBarrier | undefined;
|
// needed because inputs are _not_ echoed when not handled immediately.
|
||||||
this.add(addon.onDidRequestCompletions(() => {
|
// TODO: This should be based on the OS of the pty host, not the client
|
||||||
barrier = new AutoOpenBarrier(2000);
|
if (!isWindows) {
|
||||||
this._instance.pauseInputEvents(barrier);
|
let barrier: AutoOpenBarrier | undefined;
|
||||||
}));
|
this.add(addon.onDidRequestCompletions(() => {
|
||||||
this.add(addon.onDidReceiveCompletions(() => {
|
barrier = new AutoOpenBarrier(2000);
|
||||||
barrier?.open();
|
this._instance.pauseInputEvents(barrier);
|
||||||
barrier = undefined;
|
}));
|
||||||
}));
|
this.add(addon.onDidReceiveCompletions(() => {
|
||||||
|
barrier?.open();
|
||||||
|
barrier = undefined;
|
||||||
|
}));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,10 @@ export class TestResultStackWidget extends Disposable {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public collapseAll() {
|
||||||
|
this.widget.collapseAll();
|
||||||
|
}
|
||||||
|
|
||||||
public update(messageFrame: AnyStackFrame, stack: ITestMessageStackFrame[]) {
|
public update(messageFrame: AnyStackFrame, stack: ITestMessageStackFrame[]) {
|
||||||
this.widget.setFrames([messageFrame, ...stack.map(frame => new CallStackFrame(
|
this.widget.setFrames([messageFrame, ...stack.map(frame => new CallStackFrame(
|
||||||
frame.label,
|
frame.label,
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,9 @@ interface ISubjectCommon {
|
||||||
controllerId: string;
|
controllerId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const inspectSubjectHasStack = (subject: InspectSubject | undefined) =>
|
||||||
|
subject instanceof MessageSubject && !!subject.stack?.length;
|
||||||
|
|
||||||
export class MessageSubject implements ISubjectCommon {
|
export class MessageSubject implements ISubjectCommon {
|
||||||
public readonly test: ITestItem;
|
public readonly test: ITestItem;
|
||||||
public readonly message: ITestMessage;
|
public readonly message: ITestMessage;
|
||||||
|
|
|
||||||
|
|
@ -838,22 +838,21 @@ class TreeActionsProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (element instanceof TestMessageElement) {
|
if (element instanceof TestMessageElement) {
|
||||||
|
id = MenuId.TestMessageContext;
|
||||||
|
contextKeys.push([TestingContextKeys.testMessageContext.key, element.contextValue]);
|
||||||
|
|
||||||
primary.push(new Action(
|
primary.push(new Action(
|
||||||
'testing.outputPeek.goToFile',
|
'testing.outputPeek.goToTest',
|
||||||
localize('testing.goToFile', "Go to Source"),
|
localize('testing.goToTest', "Go to Test"),
|
||||||
ThemeIcon.asClassName(Codicon.goToFile),
|
ThemeIcon.asClassName(Codicon.goToFile),
|
||||||
undefined,
|
undefined,
|
||||||
() => this.commandService.executeCommand('vscode.revealTest', element.test.item.extId),
|
() => this.commandService.executeCommand('vscode.revealTest', element.test.item.extId),
|
||||||
));
|
));
|
||||||
}
|
|
||||||
|
|
||||||
if (element instanceof TestMessageElement) {
|
|
||||||
id = MenuId.TestMessageContext;
|
|
||||||
contextKeys.push([TestingContextKeys.testMessageContext.key, element.contextValue]);
|
|
||||||
if (this.showRevealLocationOnMessages && element.location) {
|
if (this.showRevealLocationOnMessages && element.location) {
|
||||||
primary.push(new Action(
|
primary.push(new Action(
|
||||||
'testing.outputPeek.goToError',
|
'testing.outputPeek.goToError',
|
||||||
localize('testing.goToError', "Go to Source"),
|
localize('testing.goToError', "Go to Error"),
|
||||||
ThemeIcon.asClassName(Codicon.goToFile),
|
ThemeIcon.asClassName(Codicon.goToFile),
|
||||||
undefined,
|
undefined,
|
||||||
() => this.editorService.openEditor({
|
() => this.editorService.openEditor({
|
||||||
|
|
|
||||||
|
|
@ -322,6 +322,13 @@ export class TestResultsViewContent extends Disposable {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collapses all displayed stack frames.
|
||||||
|
*/
|
||||||
|
public collapseStack() {
|
||||||
|
this.callStackWidget.collapseAll();
|
||||||
|
}
|
||||||
|
|
||||||
private getCallFrames(subject: InspectSubject) {
|
private getCallFrames(subject: InspectSubject) {
|
||||||
if (!(subject instanceof MessageSubject)) {
|
if (!(subject instanceof MessageSubject)) {
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import { testingResultsIcon, testingViewIcon } from 'vs/workbench/contrib/testin
|
||||||
import { TestCoverageView } from 'vs/workbench/contrib/testing/browser/testCoverageView';
|
import { TestCoverageView } from 'vs/workbench/contrib/testing/browser/testCoverageView';
|
||||||
import { TestingDecorationService, TestingDecorations } from 'vs/workbench/contrib/testing/browser/testingDecorations';
|
import { TestingDecorationService, TestingDecorations } from 'vs/workbench/contrib/testing/browser/testingDecorations';
|
||||||
import { TestingExplorerView } from 'vs/workbench/contrib/testing/browser/testingExplorerView';
|
import { TestingExplorerView } from 'vs/workbench/contrib/testing/browser/testingExplorerView';
|
||||||
import { CloseTestPeek, GoToNextMessageAction, GoToPreviousMessageAction, OpenMessageInEditorAction, TestResultsView, TestingOutputPeekController, TestingPeekOpener, ToggleTestingPeekHistory } from 'vs/workbench/contrib/testing/browser/testingOutputPeek';
|
import { CloseTestPeek, CollapsePeekStack, GoToNextMessageAction, GoToPreviousMessageAction, OpenMessageInEditorAction, TestResultsView, TestingOutputPeekController, TestingPeekOpener, ToggleTestingPeekHistory } from 'vs/workbench/contrib/testing/browser/testingOutputPeek';
|
||||||
import { TestingProgressTrigger } from 'vs/workbench/contrib/testing/browser/testingProgressUiService';
|
import { TestingProgressTrigger } from 'vs/workbench/contrib/testing/browser/testingProgressUiService';
|
||||||
import { TestingViewPaneContainer } from 'vs/workbench/contrib/testing/browser/testingViewPaneContainer';
|
import { TestingViewPaneContainer } from 'vs/workbench/contrib/testing/browser/testingViewPaneContainer';
|
||||||
import { testingConfiguration } from 'vs/workbench/contrib/testing/common/configuration';
|
import { testingConfiguration } from 'vs/workbench/contrib/testing/common/configuration';
|
||||||
|
|
@ -136,6 +136,7 @@ registerAction2(GoToPreviousMessageAction);
|
||||||
registerAction2(GoToNextMessageAction);
|
registerAction2(GoToNextMessageAction);
|
||||||
registerAction2(CloseTestPeek);
|
registerAction2(CloseTestPeek);
|
||||||
registerAction2(ToggleTestingPeekHistory);
|
registerAction2(ToggleTestingPeekHistory);
|
||||||
|
registerAction2(CollapsePeekStack);
|
||||||
|
|
||||||
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(TestingContentProvider, LifecyclePhase.Restored);
|
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(TestingContentProvider, LifecyclePhase.Restored);
|
||||||
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(TestingPeekOpener, LifecyclePhase.Eventually);
|
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(TestingPeekOpener, LifecyclePhase.Eventually);
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as dom from 'vs/base/browser/dom';
|
import * as dom from 'vs/base/browser/dom';
|
||||||
|
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||||
import { Action, IAction, Separator, SubmenuAction } from 'vs/base/common/actions';
|
import { Action, IAction, Separator, SubmenuAction } from 'vs/base/common/actions';
|
||||||
import { equals } from 'vs/base/common/arrays';
|
import { equals } from 'vs/base/common/arrays';
|
||||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||||
|
|
@ -456,11 +457,24 @@ export class TestingDecorations extends Disposable implements IEditorContributio
|
||||||
decorations.syncDecorations(this._currentUri);
|
decorations.syncDecorations(this._currentUri);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
this._register(this.editor.onKeyDown(e => {
|
|
||||||
if (e.keyCode === KeyCode.Alt && this._currentUri) {
|
const win = dom.getWindow(editor.getDomNode());
|
||||||
decorations.updateDecorationsAlternateAction(this._currentUri!, true);
|
this._register(dom.addDisposableListener(win, 'keydown', e => {
|
||||||
|
if (new StandardKeyboardEvent(e).keyCode === KeyCode.Alt && this._currentUri) {
|
||||||
|
decorations.updateDecorationsAlternateAction(this._currentUri, true);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
this._register(dom.addDisposableListener(win, 'keyup', e => {
|
||||||
|
if (new StandardKeyboardEvent(e).keyCode === KeyCode.Alt && this._currentUri) {
|
||||||
|
decorations.updateDecorationsAlternateAction(this._currentUri, false);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
this._register(dom.addDisposableListener(win, 'blur', () => {
|
||||||
|
if (this._currentUri) {
|
||||||
|
decorations.updateDecorationsAlternateAction(this._currentUri, false);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
this._register(this.editor.onKeyUp(e => {
|
this._register(this.editor.onKeyUp(e => {
|
||||||
if (e.keyCode === KeyCode.Alt && this._currentUri) {
|
if (e.keyCode === KeyCode.Alt && this._currentUri) {
|
||||||
decorations.updateDecorationsAlternateAction(this._currentUri!, false);
|
decorations.updateDecorationsAlternateAction(this._currentUri!, false);
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import { Iterable } from 'vs/base/common/iterator';
|
||||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||||
import { Lazy } from 'vs/base/common/lazy';
|
import { Lazy } from 'vs/base/common/lazy';
|
||||||
import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle';
|
import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle';
|
||||||
|
import { observableValue } from 'vs/base/common/observable';
|
||||||
import { count } from 'vs/base/common/strings';
|
import { count } from 'vs/base/common/strings';
|
||||||
import { URI } from 'vs/base/common/uri';
|
import { URI } from 'vs/base/common/uri';
|
||||||
import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser';
|
import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||||
|
|
@ -43,6 +44,7 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle
|
||||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||||
|
import { bindContextKey } from 'vs/platform/observable/common/platformObservableUtils';
|
||||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||||
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||||
|
|
@ -52,7 +54,7 @@ import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'
|
||||||
import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPane';
|
import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPane';
|
||||||
import { IViewDescriptorService } from 'vs/workbench/common/views';
|
import { IViewDescriptorService } from 'vs/workbench/common/views';
|
||||||
import { renderTestMessageAsText } from 'vs/workbench/contrib/testing/browser/testMessageColorizer';
|
import { renderTestMessageAsText } from 'vs/workbench/contrib/testing/browser/testMessageColorizer';
|
||||||
import { InspectSubject, MessageSubject, TaskSubject, TestOutputSubject, mapFindTestMessage } from 'vs/workbench/contrib/testing/browser/testResultsView/testResultsSubject';
|
import { InspectSubject, MessageSubject, TaskSubject, TestOutputSubject, inspectSubjectHasStack, mapFindTestMessage } from 'vs/workbench/contrib/testing/browser/testResultsView/testResultsSubject';
|
||||||
import { TestResultsViewContent } from 'vs/workbench/contrib/testing/browser/testResultsView/testResultsViewContent';
|
import { TestResultsViewContent } from 'vs/workbench/contrib/testing/browser/testResultsView/testResultsViewContent';
|
||||||
import { testingMessagePeekBorder, testingPeekBorder, testingPeekHeaderBackground, testingPeekMessageHeaderBackground } from 'vs/workbench/contrib/testing/browser/theme';
|
import { testingMessagePeekBorder, testingPeekBorder, testingPeekHeaderBackground, testingPeekMessageHeaderBackground } from 'vs/workbench/contrib/testing/browser/theme';
|
||||||
import { AutoOpenPeekViewWhen, TestingConfigKeys, getTestingConfiguration } from 'vs/workbench/contrib/testing/common/configuration';
|
import { AutoOpenPeekViewWhen, TestingConfigKeys, getTestingConfiguration } from 'vs/workbench/contrib/testing/common/configuration';
|
||||||
|
|
@ -498,6 +500,13 @@ export class TestingOutputPeekController extends Disposable implements IEditorCo
|
||||||
this.peek.clear();
|
this.peek.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collapses all displayed stack frames.
|
||||||
|
*/
|
||||||
|
public collapseStack() {
|
||||||
|
this.peek.value?.collapseStack();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows the next message in the peek, if possible.
|
* Shows the next message in the peek, if possible.
|
||||||
*/
|
*/
|
||||||
|
|
@ -645,10 +654,14 @@ class TestResultsPeek extends PeekViewWidget {
|
||||||
private static lastHeightInLines?: number;
|
private static lastHeightInLines?: number;
|
||||||
|
|
||||||
private readonly visibilityChange = this._disposables.add(new Emitter<boolean>());
|
private readonly visibilityChange = this._disposables.add(new Emitter<boolean>());
|
||||||
|
private readonly _current = observableValue<InspectSubject | undefined>('testPeekCurrent', undefined);
|
||||||
private content!: TestResultsViewContent;
|
private content!: TestResultsViewContent;
|
||||||
private scopedContextKeyService!: IContextKeyService;
|
private scopedContextKeyService!: IContextKeyService;
|
||||||
private dimension?: dom.Dimension;
|
private dimension?: dom.Dimension;
|
||||||
public current?: InspectSubject;
|
|
||||||
|
public get current() {
|
||||||
|
return this._current.get();
|
||||||
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
editor: ICodeEditor,
|
editor: ICodeEditor,
|
||||||
|
|
@ -702,7 +715,14 @@ class TestResultsPeek extends PeekViewWidget {
|
||||||
protected override _fillHead(container: HTMLElement): void {
|
protected override _fillHead(container: HTMLElement): void {
|
||||||
super._fillHead(container);
|
super._fillHead(container);
|
||||||
|
|
||||||
const menu = this.menuService.createMenu(MenuId.TestPeekTitle, this.contextKeyService);
|
const menuContextKeyService = this._disposables.add(this.contextKeyService.createScoped(container));
|
||||||
|
this._disposables.add(bindContextKey(
|
||||||
|
TestingContextKeys.peekHasStack,
|
||||||
|
menuContextKeyService,
|
||||||
|
reader => inspectSubjectHasStack(this._current.read(reader)),
|
||||||
|
));
|
||||||
|
|
||||||
|
const menu = this.menuService.createMenu(MenuId.TestPeekTitle, menuContextKeyService);
|
||||||
const actionBar = this._actionbarWidget!;
|
const actionBar = this._actionbarWidget!;
|
||||||
this._disposables.add(menu.onDidChange(() => {
|
this._disposables.add(menu.onDidChange(() => {
|
||||||
actions.length = 0;
|
actions.length = 0;
|
||||||
|
|
@ -732,7 +752,7 @@ class TestResultsPeek extends PeekViewWidget {
|
||||||
*/
|
*/
|
||||||
public setModel(subject: InspectSubject): Promise<void> {
|
public setModel(subject: InspectSubject): Promise<void> {
|
||||||
if (subject instanceof TaskSubject || subject instanceof TestOutputSubject) {
|
if (subject instanceof TaskSubject || subject instanceof TestOutputSubject) {
|
||||||
this.current = subject;
|
this._current.set(subject, undefined);
|
||||||
return this.showInPlace(subject);
|
return this.showInPlace(subject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -743,14 +763,14 @@ class TestResultsPeek extends PeekViewWidget {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.current = subject;
|
this._current.set(subject, undefined);
|
||||||
if (!revealLocation) {
|
if (!revealLocation) {
|
||||||
return this.showInPlace(subject);
|
return this.showInPlace(subject);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is a stack we want to display, ensure the default size is large-ish
|
// If there is a stack we want to display, ensure the default size is large-ish
|
||||||
const peekLines = TestResultsPeek.lastHeightInLines || Math.max(
|
const peekLines = TestResultsPeek.lastHeightInLines || Math.max(
|
||||||
subject instanceof MessageSubject && subject.stack?.length ? Math.ceil(this.getVisibleEditorLines() / 2) : 0,
|
inspectSubjectHasStack(subject) ? Math.ceil(this.getVisibleEditorLines() / 2) : 0,
|
||||||
hintMessagePeekHeight(message)
|
hintMessagePeekHeight(message)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -760,6 +780,13 @@ class TestResultsPeek extends PeekViewWidget {
|
||||||
return this.showInPlace(subject);
|
return this.showInPlace(subject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collapses all displayed stack frames.
|
||||||
|
*/
|
||||||
|
public collapseStack() {
|
||||||
|
this.content.collapseStack();
|
||||||
|
}
|
||||||
|
|
||||||
private getVisibleEditorLines() {
|
private getVisibleEditorLines() {
|
||||||
// note that we don't use the view ranges because we don't want to get
|
// note that we don't use the view ranges because we don't want to get
|
||||||
// thrown off by large wrapping lines. Being approximate here is okay.
|
// thrown off by large wrapping lines. Being approximate here is okay.
|
||||||
|
|
@ -1025,6 +1052,31 @@ export class GoToPreviousMessageAction extends Action2 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class CollapsePeekStack extends Action2 {
|
||||||
|
public static readonly ID = 'testing.collapsePeekStack';
|
||||||
|
constructor() {
|
||||||
|
super({
|
||||||
|
id: CollapsePeekStack.ID,
|
||||||
|
title: localize2('testing.collapsePeekStack', 'Collapse Stack Frames'),
|
||||||
|
icon: Codicon.collapseAll,
|
||||||
|
category: Categories.Test,
|
||||||
|
menu: [{
|
||||||
|
id: MenuId.TestPeekTitle,
|
||||||
|
when: TestingContextKeys.peekHasStack,
|
||||||
|
group: 'navigation',
|
||||||
|
order: 4,
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public override run(accessor: ServicesAccessor) {
|
||||||
|
const editor = getPeekedEditorFromFocus(accessor.get(ICodeEditorService));
|
||||||
|
if (editor) {
|
||||||
|
TestingOutputPeekController.get(editor)?.collapseStack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class OpenMessageInEditorAction extends Action2 {
|
export class OpenMessageInEditorAction extends Action2 {
|
||||||
public static readonly ID = 'testing.openMessageInEditor';
|
public static readonly ID = 'testing.openMessageInEditor';
|
||||||
constructor() {
|
constructor() {
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ export namespace TestingContextKeys {
|
||||||
export const inlineCoverageEnabled = new RawContextKey('testing.inlineCoverageEnabled', false, { type: 'boolean', description: localize('testing.inlineCoverageEnabled', 'Indicates whether inline coverage is shown') });
|
export const inlineCoverageEnabled = new RawContextKey('testing.inlineCoverageEnabled', false, { type: 'boolean', description: localize('testing.inlineCoverageEnabled', 'Indicates whether inline coverage is shown') });
|
||||||
export const canGoToRelatedCode = new RawContextKey('testing.canGoToRelatedCode', false, { type: 'boolean', description: localize('testing.canGoToRelatedCode', 'Whether a controller implements a capability to find code related to a test') });
|
export const canGoToRelatedCode = new RawContextKey('testing.canGoToRelatedCode', false, { type: 'boolean', description: localize('testing.canGoToRelatedCode', 'Whether a controller implements a capability to find code related to a test') });
|
||||||
export const canGoToRelatedTest = new RawContextKey('testing.canGoToRelatedTest', false, { type: 'boolean', description: localize('testing.canGoToRelatedTest', 'Whether a controller implements a capability to find tests related to code') });
|
export const canGoToRelatedTest = new RawContextKey('testing.canGoToRelatedTest', false, { type: 'boolean', description: localize('testing.canGoToRelatedTest', 'Whether a controller implements a capability to find tests related to code') });
|
||||||
|
export const peekHasStack = new RawContextKey('testing.peekHasStack', false, { type: 'boolean', description: localize('testing.peekHasStack', 'Whether the message shown in a peek view has a stack trace') });
|
||||||
|
|
||||||
export const capabilityToContextKey: { [K in TestRunProfileBitset]: RawContextKey<boolean> } = {
|
export const capabilityToContextKey: { [K in TestRunProfileBitset]: RawContextKey<boolean> } = {
|
||||||
[TestRunProfileBitset.Run]: hasRunnableTests,
|
[TestRunProfileBitset.Run]: hasRunnableTests,
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,7 @@ export class UserDataProfilesEditor extends EditorPane implements IUserDataProfi
|
||||||
private profileWidget: ProfileWidget | undefined;
|
private profileWidget: ProfileWidget | undefined;
|
||||||
|
|
||||||
private model: UserDataProfilesEditorModel | undefined;
|
private model: UserDataProfilesEditorModel | undefined;
|
||||||
|
private templates: readonly IProfileTemplateInfo[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
group: IEditorGroup,
|
group: IEditorGroup,
|
||||||
|
|
@ -207,7 +208,7 @@ export class UserDataProfilesEditor extends EditorPane implements IUserDataProfi
|
||||||
actions: {
|
actions: {
|
||||||
getActions: () => {
|
getActions: () => {
|
||||||
const actions: IAction[] = [];
|
const actions: IAction[] = [];
|
||||||
if (this.model?.templates.length) {
|
if (this.templates.length) {
|
||||||
actions.push(new SubmenuAction('from.template', localize('from template', "From Template"), this.getCreateFromTemplateActions()));
|
actions.push(new SubmenuAction('from.template', localize('from template', "From Template"), this.getCreateFromTemplateActions()));
|
||||||
actions.push(new Separator());
|
actions.push(new Separator());
|
||||||
}
|
}
|
||||||
|
|
@ -225,15 +226,13 @@ export class UserDataProfilesEditor extends EditorPane implements IUserDataProfi
|
||||||
}
|
}
|
||||||
|
|
||||||
private getCreateFromTemplateActions(): IAction[] {
|
private getCreateFromTemplateActions(): IAction[] {
|
||||||
return this.model
|
return this.templates.map(template =>
|
||||||
? this.model.templates.map(template =>
|
new Action(
|
||||||
new Action(
|
`template:${template.url}`,
|
||||||
`template:${template.url}`,
|
template.name,
|
||||||
template.name,
|
undefined,
|
||||||
undefined,
|
true,
|
||||||
true,
|
() => this.createNewProfile(URI.parse(template.url))));
|
||||||
() => this.createNewProfile(URI.parse(template.url))))
|
|
||||||
: [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private registerListeners(): void {
|
private registerListeners(): void {
|
||||||
|
|
@ -343,9 +342,12 @@ export class UserDataProfilesEditor extends EditorPane implements IUserDataProfi
|
||||||
override async setInput(input: UserDataProfilesEditorInput, options: IEditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise<void> {
|
override async setInput(input: UserDataProfilesEditorInput, options: IEditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise<void> {
|
||||||
await super.setInput(input, options, context, token);
|
await super.setInput(input, options, context, token);
|
||||||
this.model = await input.resolve();
|
this.model = await input.resolve();
|
||||||
if (this.profileWidget) {
|
this.model.getTemplates().then(templates => {
|
||||||
this.profileWidget.templates = this.model.templates;
|
this.templates = templates;
|
||||||
}
|
if (this.profileWidget) {
|
||||||
|
this.profileWidget.templates = templates;
|
||||||
|
}
|
||||||
|
});
|
||||||
this.updateProfilesList();
|
this.updateProfilesList();
|
||||||
this._register(this.model.onDidChange(element =>
|
this._register(this.model.onDidChange(element =>
|
||||||
this.updateProfilesList(element)));
|
this.updateProfilesList(element)));
|
||||||
|
|
@ -710,7 +712,6 @@ class ProfileTreeDataSource implements IAsyncDataSource<AbstractUserDataProfileE
|
||||||
children.push({ element: 'name', root: element });
|
children.push({ element: 'name', root: element });
|
||||||
children.push({ element: 'icon', root: element });
|
children.push({ element: 'icon', root: element });
|
||||||
}
|
}
|
||||||
children.push({ element: 'useForCurrent', root: element });
|
|
||||||
children.push({ element: 'useAsDefault', root: element });
|
children.push({ element: 'useAsDefault', root: element });
|
||||||
children.push({ element: 'contents', root: element });
|
children.push({ element: 'contents', root: element });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -713,8 +713,7 @@ export class UserDataProfilesEditorModel extends EditorModel {
|
||||||
private _onDidChange = this._register(new Emitter<AbstractUserDataProfileElement | undefined>());
|
private _onDidChange = this._register(new Emitter<AbstractUserDataProfileElement | undefined>());
|
||||||
readonly onDidChange = this._onDidChange.event;
|
readonly onDidChange = this._onDidChange.event;
|
||||||
|
|
||||||
private _templates: IProfileTemplateInfo[] | undefined;
|
private templates: Promise<readonly IProfileTemplateInfo[]> | undefined;
|
||||||
get templates(): readonly IProfileTemplateInfo[] { return this._templates ?? []; }
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@IUserDataProfileService private readonly userDataProfileService: IUserDataProfileService,
|
@IUserDataProfileService private readonly userDataProfileService: IUserDataProfileService,
|
||||||
|
|
@ -761,9 +760,11 @@ export class UserDataProfilesEditorModel extends EditorModel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override async resolve(): Promise<void> {
|
getTemplates(): Promise<readonly IProfileTemplateInfo[]> {
|
||||||
await super.resolve();
|
if (!this.templates) {
|
||||||
this._templates = await this.userDataProfileManagementService.getBuiltinProfileTemplates();
|
this.templates = this.userDataProfileManagementService.getBuiltinProfileTemplates();
|
||||||
|
}
|
||||||
|
return this.templates;
|
||||||
}
|
}
|
||||||
|
|
||||||
private createProfileElement(profile: IUserDataProfile): [UserDataProfileElement, DisposableStore] {
|
private createProfileElement(profile: IUserDataProfile): [UserDataProfileElement, DisposableStore] {
|
||||||
|
|
@ -771,7 +772,7 @@ export class UserDataProfilesEditorModel extends EditorModel {
|
||||||
|
|
||||||
const activateAction = disposables.add(new Action(
|
const activateAction = disposables.add(new Action(
|
||||||
'userDataProfile.activate',
|
'userDataProfile.activate',
|
||||||
localize('active', "Use for Current Window"),
|
localize('active', "Use this Profile for Current Window"),
|
||||||
ThemeIcon.asClassName(Codicon.check),
|
ThemeIcon.asClassName(Codicon.check),
|
||||||
true,
|
true,
|
||||||
() => this.userDataProfileManagementService.switchProfile(profileElement.profile)
|
() => this.userDataProfileManagementService.switchProfile(profileElement.profile)
|
||||||
|
|
@ -808,25 +809,16 @@ export class UserDataProfilesEditorModel extends EditorModel {
|
||||||
() => this.openWindow(profileElement.profile)
|
() => this.openWindow(profileElement.profile)
|
||||||
));
|
));
|
||||||
|
|
||||||
const useAsNewWindowProfileAction = disposables.add(new Action(
|
|
||||||
'userDataProfile.useAsNewWindowProfile',
|
|
||||||
localize('use as new window', "Use for New Windows"),
|
|
||||||
undefined,
|
|
||||||
true,
|
|
||||||
() => profileElement.toggleNewWindowProfile()
|
|
||||||
));
|
|
||||||
|
|
||||||
const primaryActions: IAction[] = [];
|
const primaryActions: IAction[] = [];
|
||||||
|
primaryActions.push(activateAction);
|
||||||
primaryActions.push(newWindowAction);
|
primaryActions.push(newWindowAction);
|
||||||
if (!profile.isDefault) {
|
|
||||||
primaryActions.push(deleteAction);
|
|
||||||
}
|
|
||||||
const secondaryActions: IAction[] = [];
|
const secondaryActions: IAction[] = [];
|
||||||
secondaryActions.push(activateAction);
|
|
||||||
secondaryActions.push(useAsNewWindowProfileAction);
|
|
||||||
secondaryActions.push(new Separator());
|
|
||||||
secondaryActions.push(copyFromProfileAction);
|
secondaryActions.push(copyFromProfileAction);
|
||||||
secondaryActions.push(exportAction);
|
secondaryActions.push(exportAction);
|
||||||
|
if (!profile.isDefault) {
|
||||||
|
secondaryActions.push(new Separator());
|
||||||
|
secondaryActions.push(deleteAction);
|
||||||
|
}
|
||||||
|
|
||||||
const profileElement = disposables.add(this.instantiationService.createInstance(UserDataProfileElement,
|
const profileElement = disposables.add(this.instantiationService.createInstance(UserDataProfileElement,
|
||||||
profile,
|
profile,
|
||||||
|
|
@ -834,16 +826,9 @@ export class UserDataProfilesEditorModel extends EditorModel {
|
||||||
[primaryActions, secondaryActions]
|
[primaryActions, secondaryActions]
|
||||||
));
|
));
|
||||||
|
|
||||||
activateAction.checked = this.userDataProfileService.currentProfile.id === profileElement.profile.id;
|
activateAction.enabled = this.userDataProfileService.currentProfile.id !== profileElement.profile.id;
|
||||||
disposables.add(this.userDataProfileService.onDidChangeCurrentProfile(() =>
|
disposables.add(this.userDataProfileService.onDidChangeCurrentProfile(() =>
|
||||||
activateAction.checked = this.userDataProfileService.currentProfile.id === profileElement.profile.id));
|
activateAction.enabled = this.userDataProfileService.currentProfile.id !== profileElement.profile.id));
|
||||||
|
|
||||||
useAsNewWindowProfileAction.checked = profileElement.isNewWindowProfile;
|
|
||||||
disposables.add(profileElement.onDidChange(e => {
|
|
||||||
if (e.newWindowProfile) {
|
|
||||||
useAsNewWindowProfileAction.checked = profileElement.isNewWindowProfile;
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
return [profileElement, disposables];
|
return [profileElement, disposables];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -226,6 +226,7 @@ export const walkthroughs: GettingStartedWalkthroughContent = [
|
||||||
'onSettingChanged:workbench.colorTheme',
|
'onSettingChanged:workbench.colorTheme',
|
||||||
'onCommand:workbench.action.selectTheme'
|
'onCommand:workbench.action.selectTheme'
|
||||||
],
|
],
|
||||||
|
when: '!accessibilityModeEnabled',
|
||||||
media: { type: 'markdown', path: 'theme_picker', }
|
media: { type: 'markdown', path: 'theme_picker', }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -399,7 +400,7 @@ export const walkthroughs: GettingStartedWalkthroughContent = [
|
||||||
isFeatured: true,
|
isFeatured: true,
|
||||||
icon: setupIcon,
|
icon: setupIcon,
|
||||||
when: CONTEXT_ACCESSIBILITY_MODE_ENABLED.key,
|
when: CONTEXT_ACCESSIBILITY_MODE_ENABLED.key,
|
||||||
next: 'SetupScreenReaderExtended',
|
next: 'Setup',
|
||||||
content: {
|
content: {
|
||||||
type: 'steps',
|
type: 'steps',
|
||||||
steps: [
|
steps: [
|
||||||
|
|
@ -470,90 +471,6 @@ export const walkthroughs: GettingStartedWalkthroughContent = [
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
id: 'SetupScreenReaderExtended',
|
|
||||||
title: localize('gettingStarted.setupScreenReaderExtended.title', "Learn more about using VS Code with a Screen Reader"),
|
|
||||||
description: localize('gettingStarted.setupScreenReaderExtended.description', "Customize your editor, learn the basics, and start coding"),
|
|
||||||
isFeatured: true,
|
|
||||||
icon: setupIcon,
|
|
||||||
when: `!isWeb && ${CONTEXT_ACCESSIBILITY_MODE_ENABLED.key}`,
|
|
||||||
content: {
|
|
||||||
type: 'steps',
|
|
||||||
steps: [
|
|
||||||
{
|
|
||||||
id: 'extensionsWeb',
|
|
||||||
title: localize('gettingStarted.extensions.title', "Code with extensions"),
|
|
||||||
description: localize('gettingStarted.extensionsWeb.description.interpolated', "Extensions are VS Code's power-ups. A growing number are becoming available in the web.\n{0}", Button(localize('browsePopularWeb', "Browse Popular Web Extensions"), 'command:workbench.extensions.action.showPopularExtensions')),
|
|
||||||
when: 'workspacePlatform == \'webworker\'',
|
|
||||||
media: {
|
|
||||||
type: 'markdown', path: 'empty'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'findLanguageExtensions',
|
|
||||||
title: localize('gettingStarted.findLanguageExts.title', "Rich support for all your languages"),
|
|
||||||
description: localize('gettingStarted.findLanguageExts.description.interpolated', "Code smarter with syntax highlighting, code completion, linting and debugging. While many languages are built-in, many more can be added as extensions.\n{0}", Button(localize('browseLangExts', "Browse Language Extensions"), 'command:workbench.extensions.action.showLanguageExtensions')),
|
|
||||||
when: 'workspacePlatform != \'webworker\'',
|
|
||||||
media: {
|
|
||||||
type: 'markdown', path: 'empty'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'settings',
|
|
||||||
title: localize('gettingStarted.settings.title', "Tune your settings"),
|
|
||||||
description: localize('gettingStarted.settings.description.interpolated', "Customize every aspect of VS Code and your extensions to your liking. Commonly used settings are listed first to get you started.\n{0}", Button(localize('tweakSettings', "Open Settings"), 'command:toSide:workbench.action.openSettings')),
|
|
||||||
media: {
|
|
||||||
type: 'markdown', path: 'empty'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'settingsSync',
|
|
||||||
title: localize('gettingStarted.settingsSync.title', "Sync settings across devices"),
|
|
||||||
description: localize('gettingStarted.settingsSync.description.interpolated', "Keep your essential customizations backed up and updated across all your devices.\n{0}", Button(localize('enableSync', "Backup and Sync Settings"), 'command:workbench.userDataSync.actions.turnOn')),
|
|
||||||
when: 'syncStatus != uninitialized',
|
|
||||||
completionEvents: ['onEvent:sync-enabled'],
|
|
||||||
media: {
|
|
||||||
type: 'markdown', path: 'empty'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'commandPaletteTask',
|
|
||||||
title: localize('gettingStarted.commandPalette.title', "Unlock productivity with the Command Palette "),
|
|
||||||
description: localize('gettingStarted.commandPalette.description.interpolated', "Run commands without reaching for your mouse to accomplish any task in VS Code.\n{0}", Button(localize('commandPalette', "Open Command Palette"), 'command:workbench.action.showCommands')),
|
|
||||||
media: {
|
|
||||||
type: 'markdown', path: 'empty'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'pickAFolderTask-Mac',
|
|
||||||
title: localize('gettingStarted.setup.OpenFolder.title', "Open up your code"),
|
|
||||||
description: localize('gettingStarted.setup.OpenFolder.description.interpolated', "You're all set to start coding. Open a project folder to get your files into VS Code.\n{0}", Button(localize('pickFolder', "Pick a Folder"), 'command:workbench.action.files.openFileFolder')),
|
|
||||||
when: 'isMac && workspaceFolderCount == 0',
|
|
||||||
media: {
|
|
||||||
type: 'markdown', path: 'empty'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'pickAFolderTask-Other',
|
|
||||||
title: localize('gettingStarted.setup.OpenFolder.title', "Open up your code"),
|
|
||||||
description: localize('gettingStarted.setup.OpenFolder.description.interpolated', "You're all set to start coding. Open a project folder to get your files into VS Code.\n{0}", Button(localize('pickFolder', "Pick a Folder"), 'command:workbench.action.files.openFolder')),
|
|
||||||
when: '!isMac && workspaceFolderCount == 0',
|
|
||||||
media: {
|
|
||||||
type: 'markdown', path: 'empty'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'quickOpen',
|
|
||||||
title: localize('gettingStarted.quickOpen.title', "Quickly navigate between your files"),
|
|
||||||
description: localize('gettingStarted.quickOpen.description.interpolated', "Navigate between files in an instant with one keystroke. Tip: Open multiple files by pressing the right arrow key.\n{0}", Button(localize('quickOpen', "Quick Open a File"), 'command:toSide:workbench.action.quickOpen')),
|
|
||||||
when: 'workspaceFolderCount != 0',
|
|
||||||
media: {
|
|
||||||
type: 'markdown', path: 'empty'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: 'Beginner',
|
id: 'Beginner',
|
||||||
isFeatured: false,
|
isFeatured: false,
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,6 @@ import { NativeWindow } from 'vs/workbench/electron-sandbox/window';
|
||||||
import { ModifierKeyEmitter } from 'vs/base/browser/dom';
|
import { ModifierKeyEmitter } from 'vs/base/browser/dom';
|
||||||
import { applicationConfigurationNodeBase, securityConfigurationNodeBase } from 'vs/workbench/common/configuration';
|
import { applicationConfigurationNodeBase, securityConfigurationNodeBase } from 'vs/workbench/common/configuration';
|
||||||
import { MAX_ZOOM_LEVEL, MIN_ZOOM_LEVEL } from 'vs/platform/window/electron-sandbox/window';
|
import { MAX_ZOOM_LEVEL, MIN_ZOOM_LEVEL } from 'vs/platform/window/electron-sandbox/window';
|
||||||
import product from 'vs/platform/product/common/product';
|
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
(function registerActions(): void {
|
(function registerActions(): void {
|
||||||
|
|
@ -240,7 +239,7 @@ import product from 'vs/platform/product/common/product';
|
||||||
'type': 'boolean',
|
'type': 'boolean',
|
||||||
'included': isLinux,
|
'included': isLinux,
|
||||||
'markdownDescription': localize('window.experimentalControlOverlay', "Show the native window controls when {0} is set to `custom` (Linux only).", '`#window.titleBarStyle#`'),
|
'markdownDescription': localize('window.experimentalControlOverlay', "Show the native window controls when {0} is set to `custom` (Linux only).", '`#window.titleBarStyle#`'),
|
||||||
'default': product.quality !== 'stable', // TODO@bpasero disable by default in stable for now (TODO@bpasero TODO@benibenj flip when custom title is default)
|
'default': true
|
||||||
},
|
},
|
||||||
'window.customTitleBarVisibility': {
|
'window.customTitleBarVisibility': {
|
||||||
'type': 'string',
|
'type': 'string',
|
||||||
|
|
|
||||||
|
|
@ -156,17 +156,17 @@ export class NativeTitlebarPart extends BrowserTitlebarPart {
|
||||||
})));
|
})));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Window Controls (Native Windows/Linux)
|
// Window Controls (Native Linux when WCO is disabled)
|
||||||
if (!isMacintosh && !hasNativeTitlebar(this.configurationService) && !isWCOEnabled() && this.primaryWindowControls) {
|
if (isLinux && !hasNativeTitlebar(this.configurationService) && !isWCOEnabled() && this.windowControlsContainer) {
|
||||||
|
|
||||||
// Minimize
|
// Minimize
|
||||||
const minimizeIcon = append(this.primaryWindowControls, $('div.window-icon.window-minimize' + ThemeIcon.asCSSSelector(Codicon.chromeMinimize)));
|
const minimizeIcon = append(this.windowControlsContainer, $('div.window-icon.window-minimize' + ThemeIcon.asCSSSelector(Codicon.chromeMinimize)));
|
||||||
this._register(addDisposableListener(minimizeIcon, EventType.CLICK, () => {
|
this._register(addDisposableListener(minimizeIcon, EventType.CLICK, () => {
|
||||||
this.nativeHostService.minimizeWindow({ targetWindowId });
|
this.nativeHostService.minimizeWindow({ targetWindowId });
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Restore
|
// Restore
|
||||||
this.maxRestoreControl = append(this.primaryWindowControls, $('div.window-icon.window-max-restore'));
|
this.maxRestoreControl = append(this.windowControlsContainer, $('div.window-icon.window-max-restore'));
|
||||||
this._register(addDisposableListener(this.maxRestoreControl, EventType.CLICK, async () => {
|
this._register(addDisposableListener(this.maxRestoreControl, EventType.CLICK, async () => {
|
||||||
const maximized = await this.nativeHostService.isMaximized({ targetWindowId });
|
const maximized = await this.nativeHostService.isMaximized({ targetWindowId });
|
||||||
if (maximized) {
|
if (maximized) {
|
||||||
|
|
@ -177,7 +177,7 @@ export class NativeTitlebarPart extends BrowserTitlebarPart {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Close
|
// Close
|
||||||
const closeIcon = append(this.primaryWindowControls, $('div.window-icon.window-close' + ThemeIcon.asCSSSelector(Codicon.chromeClose)));
|
const closeIcon = append(this.windowControlsContainer, $('div.window-icon.window-close' + ThemeIcon.asCSSSelector(Codicon.chromeClose)));
|
||||||
this._register(addDisposableListener(closeIcon, EventType.CLICK, () => {
|
this._register(addDisposableListener(closeIcon, EventType.CLICK, () => {
|
||||||
this.nativeHostService.closeWindow({ targetWindowId });
|
this.nativeHostService.closeWindow({ targetWindowId });
|
||||||
}));
|
}));
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench
|
||||||
public readonly onEnablementChanged: Event<readonly IExtension[]> = this._onEnablementChanged.event;
|
public readonly onEnablementChanged: Event<readonly IExtension[]> = this._onEnablementChanged.event;
|
||||||
|
|
||||||
protected readonly extensionsManager: ExtensionsManager;
|
protected readonly extensionsManager: ExtensionsManager;
|
||||||
private readonly storageManger: StorageManager;
|
private readonly storageManager: StorageManager;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@IStorageService storageService: IStorageService,
|
@IStorageService storageService: IStorageService,
|
||||||
|
|
@ -63,7 +63,7 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench
|
||||||
@IInstantiationService instantiationService: IInstantiationService,
|
@IInstantiationService instantiationService: IInstantiationService,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this.storageManger = this._register(new StorageManager(storageService));
|
this.storageManager = this._register(new StorageManager(storageService));
|
||||||
|
|
||||||
const uninstallDisposable = this._register(Event.filter(extensionManagementService.onDidUninstallExtension, e => !e.error)(({ identifier }) => this._reset(identifier)));
|
const uninstallDisposable = this._register(Event.filter(extensionManagementService.onDidUninstallExtension, e => !e.error)(({ identifier }) => this._reset(identifier)));
|
||||||
let isDisposed = false;
|
let isDisposed = false;
|
||||||
|
|
@ -610,11 +610,11 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench
|
||||||
if (!this.hasWorkspace) {
|
if (!this.hasWorkspace) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
return this.storageManger.get(storageId, StorageScope.WORKSPACE);
|
return this.storageManager.get(storageId, StorageScope.WORKSPACE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _setExtensions(storageId: string, extensions: IExtensionIdentifier[]): void {
|
private _setExtensions(storageId: string, extensions: IExtensionIdentifier[]): void {
|
||||||
this.storageManger.set(storageId, extensions, StorageScope.WORKSPACE);
|
this.storageManager.set(storageId, extensions, StorageScope.WORKSPACE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _onDidChangeGloballyDisabledExtensions(extensionIdentifiers: ReadonlyArray<IExtensionIdentifier>, source?: string): Promise<void> {
|
private async _onDidChangeGloballyDisabledExtensions(extensionIdentifiers: ReadonlyArray<IExtensionIdentifier>, source?: string): Promise<void> {
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,8 @@ import { URI } from 'vs/base/common/uri';
|
||||||
import { IFileMatch, IFileSearchProviderStats, IFolderQuery, ISearchCompleteStats, IFileQuery, QueryGlobTester, resolvePatternsForProvider, hasSiblingFn, excludeToGlobPattern, DEFAULT_MAX_SEARCH_RESULTS } from 'vs/workbench/services/search/common/search';
|
import { IFileMatch, IFileSearchProviderStats, IFolderQuery, ISearchCompleteStats, IFileQuery, QueryGlobTester, resolvePatternsForProvider, hasSiblingFn, excludeToGlobPattern, DEFAULT_MAX_SEARCH_RESULTS } from 'vs/workbench/services/search/common/search';
|
||||||
import { FileSearchProviderFolderOptions, FileSearchProviderNew, FileSearchProviderOptions } from 'vs/workbench/services/search/common/searchExtTypes';
|
import { FileSearchProviderFolderOptions, FileSearchProviderNew, FileSearchProviderOptions } from 'vs/workbench/services/search/common/searchExtTypes';
|
||||||
import { TernarySearchTree } from 'vs/base/common/ternarySearchTree';
|
import { TernarySearchTree } from 'vs/base/common/ternarySearchTree';
|
||||||
|
import { Disposable } from 'vs/base/common/lifecycle';
|
||||||
|
import { OldFileSearchProviderConverter } from 'vs/workbench/services/search/common/searchExtConversionTypes';
|
||||||
|
|
||||||
interface IInternalFileMatch {
|
interface IInternalFileMatch {
|
||||||
base: URI;
|
base: URI;
|
||||||
|
|
@ -53,7 +55,7 @@ class FileSearchEngine {
|
||||||
|
|
||||||
private globalExcludePattern?: glob.ParsedExpression;
|
private globalExcludePattern?: glob.ParsedExpression;
|
||||||
|
|
||||||
constructor(private config: IFileQuery, private provider: FileSearchProviderNew, private sessionToken?: unknown) {
|
constructor(private config: IFileQuery, private provider: FileSearchProviderNew, private sessionLifecycle?: SessionLifecycle) {
|
||||||
this.filePattern = config.filePattern;
|
this.filePattern = config.filePattern;
|
||||||
this.includePattern = config.includePattern && glob.parse(config.includePattern);
|
this.includePattern = config.includePattern && glob.parse(config.includePattern);
|
||||||
this.maxResults = config.maxResults || undefined;
|
this.maxResults = config.maxResults || undefined;
|
||||||
|
|
@ -116,10 +118,11 @@ class FileSearchEngine {
|
||||||
private async doSearch(fqs: IFolderQuery<URI>[], onResult: (match: IInternalFileMatch) => void): Promise<IFileSearchProviderStats | null> {
|
private async doSearch(fqs: IFolderQuery<URI>[], onResult: (match: IInternalFileMatch) => void): Promise<IFileSearchProviderStats | null> {
|
||||||
const cancellation = new CancellationTokenSource();
|
const cancellation = new CancellationTokenSource();
|
||||||
const folderOptions = fqs.map(fq => this.getSearchOptionsForFolder(fq));
|
const folderOptions = fqs.map(fq => this.getSearchOptionsForFolder(fq));
|
||||||
|
const session = this.provider instanceof OldFileSearchProviderConverter ? this.sessionLifecycle?.tokenSource.token : this.sessionLifecycle?.obj;
|
||||||
const options: FileSearchProviderOptions = {
|
const options: FileSearchProviderOptions = {
|
||||||
folderOptions,
|
folderOptions,
|
||||||
maxResults: this.config.maxResults ?? DEFAULT_MAX_SEARCH_RESULTS,
|
maxResults: this.config.maxResults ?? DEFAULT_MAX_SEARCH_RESULTS,
|
||||||
session: this.sessionToken
|
session
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -301,11 +304,30 @@ interface IInternalSearchComplete {
|
||||||
stats?: IFileSearchProviderStats;
|
stats?: IFileSearchProviderStats;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For backwards compatibility, store both a cancellation token and a session object. The session object is the new implementation, where
|
||||||
|
*/
|
||||||
|
class SessionLifecycle extends Disposable {
|
||||||
|
public readonly obj: object;
|
||||||
|
public readonly tokenSource: CancellationTokenSource;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.obj = new Object();
|
||||||
|
this.tokenSource = new CancellationTokenSource();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override dispose(): void {
|
||||||
|
this.tokenSource.cancel();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class FileSearchManager {
|
export class FileSearchManager {
|
||||||
|
|
||||||
private static readonly BATCH_SIZE = 512;
|
private static readonly BATCH_SIZE = 512;
|
||||||
|
|
||||||
private readonly sessions = new Map<string, unknown>();
|
private readonly sessions = new Map<string, SessionLifecycle>();
|
||||||
|
|
||||||
fileSearch(config: IFileQuery, provider: FileSearchProviderNew, onBatch: (matches: IFileMatch[]) => void, token: CancellationToken): Promise<ISearchCompleteStats> {
|
fileSearch(config: IFileQuery, provider: FileSearchProviderNew, onBatch: (matches: IFileMatch[]) => void, token: CancellationToken): Promise<ISearchCompleteStats> {
|
||||||
const sessionTokenSource = this.getSessionTokenSource(config.cacheKey);
|
const sessionTokenSource = this.getSessionTokenSource(config.cacheKey);
|
||||||
|
|
@ -333,17 +355,19 @@ export class FileSearchManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
clearCache(cacheKey: string): void {
|
clearCache(cacheKey: string): void {
|
||||||
|
// cancel the token
|
||||||
|
this.sessions.get(cacheKey)?.dispose();
|
||||||
// with no reference to this, it will be removed from WeakMaps
|
// with no reference to this, it will be removed from WeakMaps
|
||||||
this.sessions.delete(cacheKey);
|
this.sessions.delete(cacheKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
private getSessionTokenSource(cacheKey: string | undefined): unknown {
|
private getSessionTokenSource(cacheKey: string | undefined): SessionLifecycle | undefined {
|
||||||
if (!cacheKey) {
|
if (!cacheKey) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.sessions.has(cacheKey)) {
|
if (!this.sessions.has(cacheKey)) {
|
||||||
this.sessions.set(cacheKey, new Object());
|
this.sessions.set(cacheKey, new SessionLifecycle());
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.sessions.get(cacheKey);
|
return this.sessions.get(cacheKey);
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,7 @@ interface ICommonQueryBuilderOptions {
|
||||||
disregardSearchExcludeSettings?: boolean;
|
disregardSearchExcludeSettings?: boolean;
|
||||||
ignoreSymlinks?: boolean;
|
ignoreSymlinks?: boolean;
|
||||||
onlyOpenEditors?: boolean;
|
onlyOpenEditors?: boolean;
|
||||||
|
onlyFileScheme?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IFileQueryBuilderOptions extends ICommonQueryBuilderOptions {
|
export interface IFileQueryBuilderOptions extends ICommonQueryBuilderOptions {
|
||||||
|
|
@ -260,7 +261,8 @@ export class QueryBuilder {
|
||||||
excludePattern: excludeSearchPathsInfo.pattern,
|
excludePattern: excludeSearchPathsInfo.pattern,
|
||||||
includePattern: includeSearchPathsInfo.pattern,
|
includePattern: includeSearchPathsInfo.pattern,
|
||||||
onlyOpenEditors: options.onlyOpenEditors,
|
onlyOpenEditors: options.onlyOpenEditors,
|
||||||
maxResults: options.maxResults
|
maxResults: options.maxResults,
|
||||||
|
onlyFileScheme: options.onlyFileScheme
|
||||||
};
|
};
|
||||||
|
|
||||||
if (options.onlyOpenEditors) {
|
if (options.onlyOpenEditors) {
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,7 @@ export interface ICommonQueryProps<U extends UriComponents> {
|
||||||
|
|
||||||
maxResults?: number;
|
maxResults?: number;
|
||||||
usingSearchPaths?: boolean;
|
usingSearchPaths?: boolean;
|
||||||
|
onlyFileScheme?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IFileQueryProps<U extends UriComponents> extends ICommonQueryProps<U> {
|
export interface IFileQueryProps<U extends UriComponents> extends ICommonQueryProps<U> {
|
||||||
|
|
|
||||||
|
|
@ -271,6 +271,9 @@ export class SearchService extends Disposable implements ISearchService {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
await Promise.all([...fqs.keys()].map(async scheme => {
|
await Promise.all([...fqs.keys()].map(async scheme => {
|
||||||
|
if (query.onlyFileScheme && scheme !== Schemas.file) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const schemeFQs = fqs.get(scheme)!;
|
const schemeFQs = fqs.get(scheme)!;
|
||||||
let provider = this.getSearchProvider(query.type).get(scheme);
|
let provider = this.getSearchProvider(query.type).get(scheme);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -143,7 +143,7 @@ export class TestNativeHostService implements INativeHostService {
|
||||||
async closeWindow(): Promise<void> { }
|
async closeWindow(): Promise<void> { }
|
||||||
async quit(): Promise<void> { }
|
async quit(): Promise<void> { }
|
||||||
async exit(code: number): Promise<void> { }
|
async exit(code: number): Promise<void> { }
|
||||||
async openDevTools(options?: Partial<Electron.OpenDevToolsOptions> & INativeHostOptions | undefined): Promise<void> { }
|
async openDevTools(): Promise<void> { }
|
||||||
async toggleDevTools(): Promise<void> { }
|
async toggleDevTools(): Promise<void> { }
|
||||||
async resolveProxy(url: string): Promise<string | undefined> { return undefined; }
|
async resolveProxy(url: string): Promise<string | undefined> { return undefined; }
|
||||||
async lookupAuthorization(authInfo: AuthInfo): Promise<Credentials | undefined> { return undefined; }
|
async lookupAuthorization(authInfo: AuthInfo): Promise<Credentials | undefined> { return undefined; }
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,6 @@
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
//#region --- void
|
|
||||||
import 'vs/workbench/contrib/void/browser/void.contribution'
|
|
||||||
//#endregion
|
|
||||||
|
|
||||||
|
|
||||||
//#region --- editor/workbench core
|
//#region --- editor/workbench core
|
||||||
|
|
||||||
import 'vs/editor/editor.all';
|
import 'vs/editor/editor.all';
|
||||||
|
|
@ -15,7 +10,6 @@ import 'vs/editor/editor.all';
|
||||||
import 'vs/workbench/api/browser/extensionHost.contribution';
|
import 'vs/workbench/api/browser/extensionHost.contribution';
|
||||||
import 'vs/workbench/browser/workbench.contribution';
|
import 'vs/workbench/browser/workbench.contribution';
|
||||||
|
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue