mirror of
https://github.com/microsoft/vscode.git
synced 2026-08-26 03:03:50 +01:00
Merge branch 'main' into tyriar/hangul
This commit is contained in:
@@ -737,6 +737,7 @@
|
||||
"--tab-sizing-fixed-min-width",
|
||||
"--tab-sizing-fixed-max-width",
|
||||
"--editor-group-tab-height",
|
||||
"--editor-group-tabs-height",
|
||||
"--testMessageDecorationFontFamily",
|
||||
"--testMessageDecorationFontSize",
|
||||
"--title-border-bottom-color",
|
||||
@@ -783,4 +784,4 @@
|
||||
"--z-index-notebook-sticky-scroll",
|
||||
"--zoom-factor"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,9 @@ export class TestCommitMessageProvider implements CommitMessageProvider {
|
||||
readonly icon = new ThemeIcon('rocket');
|
||||
readonly title = 'Generate Commit Message (Test)';
|
||||
|
||||
async provideCommitMessage(_: ApiRepository, __: string[], token: CancellationToken): Promise<string | undefined> {
|
||||
async provideCommitMessage(repository: ApiRepository, _: string[], token: CancellationToken): Promise<string | undefined> {
|
||||
console.log('Repository: ', repository.rootUri.fsPath);
|
||||
|
||||
if (token.isCancellationRequested) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -62,12 +64,20 @@ export class GenerateCommitMessageActionButton {
|
||||
return this.state.isGenerating ?
|
||||
{
|
||||
icon: new ThemeIcon('debug-stop'),
|
||||
command: { title: l10n.t('Cancel'), command: 'git.generateCommitMessageCancel' },
|
||||
enabled: this.state.enabled
|
||||
command: {
|
||||
title: l10n.t('Cancel'),
|
||||
command: 'git.generateCommitMessageCancel',
|
||||
arguments: [this.repository.sourceControl]
|
||||
},
|
||||
enabled: this.state.enabled,
|
||||
} :
|
||||
{
|
||||
icon: this.commitMessageProviderRegistry.commitMessageProvider.icon ?? new ThemeIcon('sparkle'),
|
||||
command: { title: this.commitMessageProviderRegistry.commitMessageProvider.title, command: 'git.generateCommitMessage' },
|
||||
command: {
|
||||
title: this.commitMessageProviderRegistry.commitMessageProvider.title,
|
||||
command: 'git.generateCommitMessage',
|
||||
arguments: [this.repository.sourceControl]
|
||||
},
|
||||
enabled: this.state.enabled
|
||||
};
|
||||
}
|
||||
|
||||
@@ -267,10 +267,9 @@ export interface IGitErrorData {
|
||||
gitArgs?: string[];
|
||||
}
|
||||
|
||||
export class GitError {
|
||||
export class GitError extends Error {
|
||||
|
||||
error?: Error;
|
||||
message: string;
|
||||
stdout?: string;
|
||||
stderr?: string;
|
||||
exitCode?: number;
|
||||
@@ -279,15 +278,9 @@ export class GitError {
|
||||
gitArgs?: string[];
|
||||
|
||||
constructor(data: IGitErrorData) {
|
||||
if (data.error) {
|
||||
this.error = data.error;
|
||||
this.message = data.error.message;
|
||||
} else {
|
||||
this.error = undefined;
|
||||
this.message = '';
|
||||
}
|
||||
super(data.error?.message || data.message || 'Git error');
|
||||
|
||||
this.message = this.message || data.message || 'Git error';
|
||||
this.error = data.error;
|
||||
this.stdout = data.stdout;
|
||||
this.stderr = data.stderr;
|
||||
this.exitCode = data.exitCode;
|
||||
@@ -296,7 +289,7 @@ export class GitError {
|
||||
this.gitArgs = data.gitArgs;
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
override toString(): string {
|
||||
let result = this.message + ' ' + JSON.stringify({
|
||||
exitCode: this.exitCode,
|
||||
gitErrorCode: this.gitErrorCode,
|
||||
@@ -1173,7 +1166,9 @@ export class Repository {
|
||||
const element = elements.filter(file => file.file.toLowerCase() === relativePathLowercase)[0];
|
||||
|
||||
if (!element) {
|
||||
throw new GitError({ message: 'Git relative path not found.' });
|
||||
throw new GitError({
|
||||
message: `Git relative path not found. Was looking for ${relativePathLowercase} among ${JSON.stringify(elements.map(({ file }) => file), null, 2)}`,
|
||||
});
|
||||
}
|
||||
|
||||
return element.file;
|
||||
|
||||
@@ -17,11 +17,15 @@ import { FileAccess, RemoteAuthorities, Schemas } from 'vs/base/common/network';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
export const { registerWindow, getWindows, onDidRegisterWindow } = (function () {
|
||||
export const { registerWindow, getWindows, onDidRegisterWindow, onWillUnregisterWindow, onDidUnregisterWindow } = (function () {
|
||||
const windows = new Set([window]);
|
||||
const onDidRegisterWindow = new event.Emitter<{ window: Window & typeof globalThis; disposableStore: DisposableStore }>();
|
||||
const onDidUnregisterWindow = new event.Emitter<Window & typeof globalThis>();
|
||||
const onWillUnregisterWindow = new event.Emitter<Window & typeof globalThis>();
|
||||
return {
|
||||
onDidRegisterWindow: onDidRegisterWindow.event,
|
||||
onWillUnregisterWindow: onWillUnregisterWindow.event,
|
||||
onDidUnregisterWindow: onDidUnregisterWindow.event,
|
||||
registerWindow(window: Window & typeof globalThis): IDisposable {
|
||||
if (windows.has(window)) {
|
||||
return Disposable.None;
|
||||
@@ -32,10 +36,15 @@ export const { registerWindow, getWindows, onDidRegisterWindow } = (function ()
|
||||
const disposableStore = new DisposableStore();
|
||||
disposableStore.add(toDisposable(() => {
|
||||
windows.delete(window);
|
||||
onDidUnregisterWindow.fire(window);
|
||||
}));
|
||||
|
||||
onDidRegisterWindow.fire({ window, disposableStore });
|
||||
|
||||
disposableStore.add(addDisposableListener(window, 'beforeunload', () => {
|
||||
onWillUnregisterWindow.fire(window);
|
||||
}));
|
||||
|
||||
return disposableStore;
|
||||
},
|
||||
getWindows(): Iterable<Window & typeof globalThis> {
|
||||
|
||||
@@ -51,6 +51,7 @@ export interface GridLeafNode<T extends IView> {
|
||||
readonly view: T;
|
||||
readonly box: Box;
|
||||
readonly cachedVisibleSize: number | undefined;
|
||||
readonly maximized: boolean;
|
||||
}
|
||||
|
||||
export interface GridBranchNode<T extends IView> {
|
||||
@@ -288,6 +289,7 @@ export class Grid<T extends IView = IView> extends Disposable {
|
||||
|
||||
private didLayout = false;
|
||||
|
||||
readonly onDidChangeViewMaximized: Event<boolean>;
|
||||
/**
|
||||
* Create a new {@link Grid}. A grid must *always* have a view
|
||||
* inside.
|
||||
@@ -313,6 +315,7 @@ export class Grid<T extends IView = IView> extends Disposable {
|
||||
|
||||
this.onDidChange = this.gridview.onDidChange;
|
||||
this.onDidScroll = this.gridview.onDidScroll;
|
||||
this.onDidChangeViewMaximized = this.gridview.onDidChangeViewMaximized;
|
||||
}
|
||||
|
||||
style(styles: IGridStyles): void {
|
||||
@@ -545,9 +548,28 @@ export class Grid<T extends IView = IView> extends Disposable {
|
||||
*
|
||||
* @param view The reference {@link IView view}.
|
||||
*/
|
||||
isViewSizeMaximized(view: T): boolean {
|
||||
isViewExpanded(view: T): boolean {
|
||||
const location = this.getViewLocation(view);
|
||||
return this.gridview.isViewSizeMaximized(location);
|
||||
return this.gridview.isViewExpanded(location);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the {@link IView view} is maximized.
|
||||
*
|
||||
* @param view The reference {@link IView view}.
|
||||
*/
|
||||
isViewMaximized(view: T): boolean {
|
||||
const location = this.getViewLocation(view);
|
||||
return this.gridview.isViewMaximized(location);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the {@link IView view} is maximized.
|
||||
*
|
||||
* @param view The reference {@link IView view}.
|
||||
*/
|
||||
hasMaximizedView(): boolean {
|
||||
return this.gridview.hasMaximizedView();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -577,14 +599,30 @@ export class Grid<T extends IView = IView> extends Disposable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Maximize the size of a {@link IView view} by collapsing all other views
|
||||
* Maximizes the specified view and hides all other views.
|
||||
* @param view The view to maximize.
|
||||
*/
|
||||
maximizeView(view: T) {
|
||||
if (this.views.size < 2) {
|
||||
throw new Error('At least two views are required to maximize a view');
|
||||
}
|
||||
const location = this.getViewLocation(view);
|
||||
this.gridview.maximizeView(location);
|
||||
}
|
||||
|
||||
exitMaximizedView(): void {
|
||||
this.gridview.exitMaximizedView();
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand the size of a {@link IView view} by collapsing all other views
|
||||
* to their minimum sizes.
|
||||
*
|
||||
* @param view The {@link IView view}.
|
||||
*/
|
||||
maximizeViewSize(view: T): void {
|
||||
expandView(view: T): void {
|
||||
const location = this.getViewLocation(view);
|
||||
this.gridview.maximizeViewSize(location);
|
||||
this.gridview.expandView(location);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -713,12 +751,14 @@ export interface ISerializedLeafNode {
|
||||
data: any;
|
||||
size: number;
|
||||
visible?: boolean;
|
||||
maximized?: boolean;
|
||||
}
|
||||
|
||||
export interface ISerializedBranchNode {
|
||||
type: 'branch';
|
||||
data: ISerializedNode[];
|
||||
size: number;
|
||||
visible?: boolean;
|
||||
}
|
||||
|
||||
export type ISerializedNode = ISerializedLeafNode | ISerializedBranchNode;
|
||||
@@ -739,14 +779,23 @@ export class SerializableGrid<T extends ISerializableView> extends Grid<T> {
|
||||
const size = orientation === Orientation.VERTICAL ? node.box.width : node.box.height;
|
||||
|
||||
if (!isGridBranchNode(node)) {
|
||||
const serializedLeafNode: ISerializedLeafNode = { type: 'leaf', data: node.view.toJSON(), size };
|
||||
|
||||
if (typeof node.cachedVisibleSize === 'number') {
|
||||
return { type: 'leaf', data: node.view.toJSON(), size: node.cachedVisibleSize, visible: false };
|
||||
serializedLeafNode.size = node.cachedVisibleSize;
|
||||
serializedLeafNode.visible = false;
|
||||
} else if (node.maximized) {
|
||||
serializedLeafNode.maximized = true;
|
||||
}
|
||||
|
||||
return { type: 'leaf', data: node.view.toJSON(), size };
|
||||
return serializedLeafNode;
|
||||
}
|
||||
|
||||
return { type: 'branch', data: node.children.map(c => SerializableGrid.serializeNode(c, orthogonal(orientation))), size };
|
||||
const data = node.children.map(c => SerializableGrid.serializeNode(c, orthogonal(orientation)));
|
||||
if (data.some(c => c.visible !== false)) {
|
||||
return { type: 'branch', data: data, size };
|
||||
}
|
||||
return { type: 'branch', data: data, size, visible: false };
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -148,12 +148,14 @@ export interface ISerializedLeafNode {
|
||||
data: any;
|
||||
size: number;
|
||||
visible?: boolean;
|
||||
maximized?: boolean;
|
||||
}
|
||||
|
||||
export interface ISerializedBranchNode {
|
||||
type: 'branch';
|
||||
data: ISerializedNode[];
|
||||
size: number;
|
||||
visible?: boolean;
|
||||
}
|
||||
|
||||
export type ISerializedNode = ISerializedLeafNode | ISerializedBranchNode;
|
||||
@@ -180,6 +182,7 @@ export interface GridLeafNode {
|
||||
readonly view: IView;
|
||||
readonly box: Box;
|
||||
readonly cachedVisibleSize: number | undefined;
|
||||
readonly maximized: boolean;
|
||||
}
|
||||
|
||||
export interface GridBranchNode {
|
||||
@@ -284,11 +287,11 @@ class BranchNode implements ISplitView<ILayoutContext>, IDisposable {
|
||||
}
|
||||
|
||||
get minimumSize(): number {
|
||||
return this.children.length === 0 ? 0 : Math.max(...this.children.map(c => c.minimumOrthogonalSize));
|
||||
return this.children.length === 0 ? 0 : Math.max(...this.children.map((c, index) => this.splitview.isViewVisible(index) ? c.minimumOrthogonalSize : 0));
|
||||
}
|
||||
|
||||
get maximumSize(): number {
|
||||
return Math.min(...this.children.map(c => c.maximumOrthogonalSize));
|
||||
return Math.min(...this.children.map((c, index) => this.splitview.isViewVisible(index) ? c.maximumOrthogonalSize : Number.POSITIVE_INFINITY));
|
||||
}
|
||||
|
||||
get priority(): LayoutPriority {
|
||||
@@ -342,6 +345,10 @@ class BranchNode implements ISplitView<ILayoutContext>, IDisposable {
|
||||
private readonly _onDidChange = new Emitter<number | undefined>();
|
||||
readonly onDidChange: Event<number | undefined> = this._onDidChange.event;
|
||||
|
||||
private readonly _onDidVisibilityChange = new Emitter<boolean>();
|
||||
readonly onDidVisibilityChange: Event<boolean> = this._onDidVisibilityChange.event;
|
||||
private readonly childrenVisibilityChangeDisposable: DisposableStore = new DisposableStore();
|
||||
|
||||
private _onDidScroll = new Emitter<void>();
|
||||
private onDidScrollDisposable: IDisposable = Disposable.None;
|
||||
readonly onDidScroll: Event<void> = this._onDidScroll.event;
|
||||
@@ -427,7 +434,7 @@ class BranchNode implements ISplitView<ILayoutContext>, IDisposable {
|
||||
return {
|
||||
view: childDescriptor.node,
|
||||
size: childDescriptor.node.size,
|
||||
visible: childDescriptor.node instanceof LeafNode && childDescriptor.visible !== undefined ? childDescriptor.visible : true
|
||||
visible: childDescriptor.visible !== false
|
||||
};
|
||||
}),
|
||||
size: this.orthogonalSize
|
||||
@@ -579,8 +586,8 @@ class BranchNode implements ISplitView<ILayoutContext>, IDisposable {
|
||||
this.splitview.resizeView(index, size);
|
||||
}
|
||||
|
||||
isChildSizeMaximized(index: number): boolean {
|
||||
return this.splitview.isViewSizeMaximized(index);
|
||||
isChildExpanded(index: number): boolean {
|
||||
return this.splitview.isViewExpanded(index);
|
||||
}
|
||||
|
||||
distributeViewSizes(recursive = false): void {
|
||||
@@ -614,7 +621,15 @@ class BranchNode implements ISplitView<ILayoutContext>, IDisposable {
|
||||
return;
|
||||
}
|
||||
|
||||
const wereAllChildrenHidden = this.splitview.contentSize === 0;
|
||||
this.splitview.setViewVisible(index, visible);
|
||||
const areAllChildrenHidden = this.splitview.contentSize === 0;
|
||||
|
||||
// If all children are hidden then the parent should hide the entire splitview
|
||||
// If the entire splitview is hidden then the parent should show the splitview when a child is shown
|
||||
if ((visible && wereAllChildrenHidden) || (!visible && areAllChildrenHidden)) {
|
||||
this._onDidVisibilityChange.fire(visible);
|
||||
}
|
||||
}
|
||||
|
||||
getChildCachedVisibleSize(index: number): number | undefined {
|
||||
@@ -651,6 +666,15 @@ class BranchNode implements ISplitView<ILayoutContext>, IDisposable {
|
||||
const onDidScroll = Event.any(Event.signal(this.splitview.onDidScroll), ...this.children.map(c => c.onDidScroll));
|
||||
this.onDidScrollDisposable.dispose();
|
||||
this.onDidScrollDisposable = onDidScroll(this._onDidScroll.fire, this._onDidScroll);
|
||||
|
||||
this.childrenVisibilityChangeDisposable.clear();
|
||||
this.children.forEach((child, index) => {
|
||||
if (child instanceof BranchNode) {
|
||||
this.childrenVisibilityChangeDisposable.add(child.onDidVisibilityChange((visible) => {
|
||||
this.setChildVisible(index, visible);
|
||||
}));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
trySet2x2(other: BranchNode): IDisposable {
|
||||
@@ -714,7 +738,9 @@ class BranchNode implements ISplitView<ILayoutContext>, IDisposable {
|
||||
|
||||
this._onDidChange.dispose();
|
||||
this._onDidSashReset.dispose();
|
||||
this._onDidVisibilityChange.dispose();
|
||||
|
||||
this.childrenVisibilityChangeDisposable.dispose();
|
||||
this.splitviewSashResetDisposable.dispose();
|
||||
this.childrenSashResetDisposable.dispose();
|
||||
this.childrenChangeDisposable.dispose();
|
||||
@@ -1128,6 +1154,11 @@ export class GridView implements IDisposable {
|
||||
this.root.edgeSnapping = edgeSnapping;
|
||||
}
|
||||
|
||||
private maximizedNode: LeafNode | undefined = undefined;
|
||||
|
||||
private readonly _onDidChangeViewMaximized = new Emitter<boolean>();
|
||||
readonly onDidChangeViewMaximized = this._onDidChangeViewMaximized.event;
|
||||
|
||||
/**
|
||||
* Create a new {@link GridView} instance.
|
||||
*
|
||||
@@ -1173,6 +1204,10 @@ export class GridView implements IDisposable {
|
||||
* @param location The {@link GridLocation location} to insert the view on.
|
||||
*/
|
||||
addView(view: IView, size: number | Sizing, location: GridLocation): void {
|
||||
if (this.hasMaximizedView()) {
|
||||
this.exitMaximizedView();
|
||||
}
|
||||
|
||||
this.disposable2x2.dispose();
|
||||
this.disposable2x2 = Disposable.None;
|
||||
|
||||
@@ -1226,6 +1261,10 @@ export class GridView implements IDisposable {
|
||||
* @param sizing Whether to distribute other {@link IView view}'s sizes.
|
||||
*/
|
||||
removeView(location: GridLocation, sizing?: DistributeSizing | AutoSizing): IView {
|
||||
if (this.hasMaximizedView()) {
|
||||
this.exitMaximizedView();
|
||||
}
|
||||
|
||||
this.disposable2x2.dispose();
|
||||
this.disposable2x2 = Disposable.None;
|
||||
|
||||
@@ -1312,6 +1351,10 @@ export class GridView implements IDisposable {
|
||||
* @param to The index where the {@link IView view} should move to.
|
||||
*/
|
||||
moveView(parentLocation: GridLocation, from: number, to: number): void {
|
||||
if (this.hasMaximizedView()) {
|
||||
this.exitMaximizedView();
|
||||
}
|
||||
|
||||
const [, parent] = this.getNode(parentLocation);
|
||||
|
||||
if (!(parent instanceof BranchNode)) {
|
||||
@@ -1330,6 +1373,10 @@ export class GridView implements IDisposable {
|
||||
* @param to The {@link GridLocation location} of another view.
|
||||
*/
|
||||
swapViews(from: GridLocation, to: GridLocation): void {
|
||||
if (this.hasMaximizedView()) {
|
||||
this.exitMaximizedView();
|
||||
}
|
||||
|
||||
const [fromRest, fromIndex] = tail(from);
|
||||
const [, fromParent] = this.getNode(fromRest);
|
||||
|
||||
@@ -1378,6 +1425,10 @@ export class GridView implements IDisposable {
|
||||
* @param size The size the view should be. Optionally provide a single dimension.
|
||||
*/
|
||||
resizeView(location: GridLocation, size: Partial<IViewSize>): void {
|
||||
if (this.hasMaximizedView()) {
|
||||
this.exitMaximizedView();
|
||||
}
|
||||
|
||||
const [rest, index] = tail(location);
|
||||
const [pathToParent, parent] = this.getNode(rest);
|
||||
|
||||
@@ -1443,7 +1494,11 @@ export class GridView implements IDisposable {
|
||||
*
|
||||
* @param location The {@link GridLocation location} of the view.
|
||||
*/
|
||||
maximizeViewSize(location: GridLocation): void {
|
||||
expandView(location: GridLocation): void {
|
||||
if (this.hasMaximizedView()) {
|
||||
this.exitMaximizedView();
|
||||
}
|
||||
|
||||
const [ancestors, node] = this.getNode(location);
|
||||
|
||||
if (!(node instanceof LeafNode)) {
|
||||
@@ -1460,7 +1515,12 @@ export class GridView implements IDisposable {
|
||||
*
|
||||
* @param location The {@link GridLocation location} of the view.
|
||||
*/
|
||||
isViewSizeMaximized(location: GridLocation): boolean {
|
||||
isViewExpanded(location: GridLocation): boolean {
|
||||
if (this.hasMaximizedView()) {
|
||||
// No view can be expanded when a view is maximized
|
||||
return false;
|
||||
}
|
||||
|
||||
const [ancestors, node] = this.getNode(location);
|
||||
|
||||
if (!(node instanceof LeafNode)) {
|
||||
@@ -1468,7 +1528,7 @@ export class GridView implements IDisposable {
|
||||
}
|
||||
|
||||
for (let i = 0; i < ancestors.length; i++) {
|
||||
if (!ancestors[i].isChildSizeMaximized(location[i])) {
|
||||
if (!ancestors[i].isChildExpanded(location[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1476,6 +1536,80 @@ export class GridView implements IDisposable {
|
||||
return true;
|
||||
}
|
||||
|
||||
maximizeView(location: GridLocation) {
|
||||
const [, nodeToMaximize] = this.getNode(location);
|
||||
if (!(nodeToMaximize instanceof LeafNode)) {
|
||||
throw new Error('Location is not a LeafNode');
|
||||
}
|
||||
|
||||
if (this.maximizedNode === nodeToMaximize) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.hasMaximizedView()) {
|
||||
this.exitMaximizedView();
|
||||
}
|
||||
|
||||
function hideAllViewsBut(parent: BranchNode, exclude: LeafNode): void {
|
||||
for (let i = 0; i < parent.children.length; i++) {
|
||||
const child = parent.children[i];
|
||||
if (child instanceof LeafNode) {
|
||||
if (child !== exclude) {
|
||||
parent.setChildVisible(i, false);
|
||||
}
|
||||
} else {
|
||||
hideAllViewsBut(child, exclude);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hideAllViewsBut(this.root, nodeToMaximize);
|
||||
|
||||
this.maximizedNode = nodeToMaximize;
|
||||
this._onDidChangeViewMaximized.fire(true);
|
||||
}
|
||||
|
||||
exitMaximizedView(): void {
|
||||
if (!this.maximizedNode) {
|
||||
return;
|
||||
}
|
||||
this.maximizedNode = undefined;
|
||||
|
||||
// When hiding a view, it's previous size is cached.
|
||||
// To restore the sizes of all views, they need to be made visible in reverse order.
|
||||
function showViewsInReverseOrder(parent: BranchNode): void {
|
||||
for (let index = parent.children.length - 1; index >= 0; index--) {
|
||||
const child = parent.children[index];
|
||||
if (child instanceof LeafNode) {
|
||||
parent.setChildVisible(index, true);
|
||||
} else {
|
||||
showViewsInReverseOrder(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
showViewsInReverseOrder(this.root);
|
||||
|
||||
this._onDidChangeViewMaximized.fire(false);
|
||||
}
|
||||
|
||||
hasMaximizedView(): boolean {
|
||||
return this.maximizedNode !== undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the {@link IView view} is maximized.
|
||||
*
|
||||
* @param location The {@link GridLocation location} of the view.
|
||||
*/
|
||||
isViewMaximized(location: GridLocation): boolean {
|
||||
const [, node] = this.getNode(location);
|
||||
if (!(node instanceof LeafNode)) {
|
||||
throw new Error('Location is not a LeafNode');
|
||||
}
|
||||
return node === this.maximizedNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Distribute the size among all {@link IView views} within the entire
|
||||
* grid or within a single {@link SplitView}.
|
||||
@@ -1486,6 +1620,10 @@ export class GridView implements IDisposable {
|
||||
* in the entire grid.
|
||||
*/
|
||||
distributeViewSizes(location?: GridLocation): void {
|
||||
if (this.hasMaximizedView()) {
|
||||
this.exitMaximizedView();
|
||||
}
|
||||
|
||||
if (!location) {
|
||||
this.root.distributeViewSizes(true);
|
||||
return;
|
||||
@@ -1523,6 +1661,11 @@ export class GridView implements IDisposable {
|
||||
* @param location The {@link GridLocation location} of the view.
|
||||
*/
|
||||
setViewVisible(location: GridLocation, visible: boolean): void {
|
||||
if (this.hasMaximizedView()) {
|
||||
this.exitMaximizedView();
|
||||
return;
|
||||
}
|
||||
|
||||
const [rest, index] = tail(location);
|
||||
const [, parent] = this.getNode(rest);
|
||||
|
||||
@@ -1596,6 +1739,10 @@ export class GridView implements IDisposable {
|
||||
result = new BranchNode(orientation, this.layoutController, this.styles, this.proportionalLayout, node.size, orthogonalSize, undefined, children);
|
||||
} else {
|
||||
result = new LeafNode(deserializer.fromJSON(node.data), orientation, this.layoutController, orthogonalSize, node.size);
|
||||
if (node.maximized && !this.maximizedNode) {
|
||||
this.maximizedNode = result;
|
||||
this._onDidChangeViewMaximized.fire(true);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -1605,7 +1752,7 @@ export class GridView implements IDisposable {
|
||||
const box = { top: node.top, left: node.left, width: node.width, height: node.height };
|
||||
|
||||
if (node instanceof LeafNode) {
|
||||
return { view: node.view, box, cachedVisibleSize };
|
||||
return { view: node.view, box, cachedVisibleSize, maximized: this.maximizedNode === node };
|
||||
}
|
||||
|
||||
const children: GridNode[] = [];
|
||||
|
||||
@@ -442,7 +442,7 @@ export class SplitView<TLayoutContext = undefined, TView extends IView<TLayoutCo
|
||||
private scrollableElement: SmoothScrollableElement;
|
||||
private size = 0;
|
||||
private layoutContext: TLayoutContext | undefined;
|
||||
private contentSize = 0;
|
||||
private _contentSize = 0;
|
||||
private proportions: (number | undefined)[] | undefined = undefined;
|
||||
private viewItems: ViewItem<TLayoutContext, TView>[] = [];
|
||||
sashItems: ISashItem[] = []; // used in tests
|
||||
@@ -459,6 +459,11 @@ export class SplitView<TLayoutContext = undefined, TView extends IView<TLayoutCo
|
||||
private _startSnappingEnabled = true;
|
||||
private _endSnappingEnabled = true;
|
||||
|
||||
/**
|
||||
* The sum of all views' sizes.
|
||||
*/
|
||||
get contentSize(): number { return this._contentSize; }
|
||||
|
||||
/**
|
||||
* Fires whenever the user resizes a {@link Sash sash}.
|
||||
*/
|
||||
@@ -624,7 +629,7 @@ export class SplitView<TLayoutContext = undefined, TView extends IView<TLayoutCo
|
||||
});
|
||||
|
||||
// Initialize content size and proportions for first layout
|
||||
this.contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);
|
||||
this._contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);
|
||||
this.saveProportions();
|
||||
}
|
||||
}
|
||||
@@ -834,7 +839,7 @@ export class SplitView<TLayoutContext = undefined, TView extends IView<TLayoutCo
|
||||
* @param layoutContext An optional layout context to pass along to {@link IView views}.
|
||||
*/
|
||||
layout(size: number, layoutContext?: TLayoutContext): void {
|
||||
const previousSize = Math.max(this.size, this.contentSize);
|
||||
const previousSize = Math.max(this.size, this._contentSize);
|
||||
this.size = size;
|
||||
this.layoutContext = layoutContext;
|
||||
|
||||
@@ -862,7 +867,7 @@ export class SplitView<TLayoutContext = undefined, TView extends IView<TLayoutCo
|
||||
const item = this.viewItems[i];
|
||||
const proportion = this.proportions[i];
|
||||
|
||||
if (typeof proportion === 'number') {
|
||||
if (typeof proportion === 'number' && total > 0) {
|
||||
item.size = clamp(Math.round(proportion * size / total), item.minimumSize, item.maximumSize);
|
||||
}
|
||||
}
|
||||
@@ -873,8 +878,8 @@ export class SplitView<TLayoutContext = undefined, TView extends IView<TLayoutCo
|
||||
}
|
||||
|
||||
private saveProportions(): void {
|
||||
if (this.proportionalLayout && this.contentSize > 0) {
|
||||
this.proportions = this.viewItems.map(i => i.proportionalLayout ? i.size / this.contentSize : undefined);
|
||||
if (this.proportionalLayout && this._contentSize > 0) {
|
||||
this.proportions = this.viewItems.map(v => v.proportionalLayout && v.visible ? v.size / this._contentSize : undefined);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1052,7 +1057,7 @@ export class SplitView<TLayoutContext = undefined, TView extends IView<TLayoutCo
|
||||
/**
|
||||
* Returns whether all other {@link IView views} are at their minimum size.
|
||||
*/
|
||||
isViewSizeMaximized(index: number): boolean {
|
||||
isViewExpanded(index: number): boolean {
|
||||
if (index < 0 || index >= this.viewItems.length) {
|
||||
return false;
|
||||
}
|
||||
@@ -1347,7 +1352,7 @@ export class SplitView<TLayoutContext = undefined, TView extends IView<TLayoutCo
|
||||
|
||||
private layoutViews(): void {
|
||||
// Save new content size
|
||||
this.contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);
|
||||
this._contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);
|
||||
|
||||
// Layout views
|
||||
let offset = 0;
|
||||
@@ -1367,12 +1372,12 @@ export class SplitView<TLayoutContext = undefined, TView extends IView<TLayoutCo
|
||||
if (this.orientation === Orientation.VERTICAL) {
|
||||
this.scrollableElement.setScrollDimensions({
|
||||
height: this.size,
|
||||
scrollHeight: this.contentSize
|
||||
scrollHeight: this._contentSize
|
||||
});
|
||||
} else {
|
||||
this.scrollableElement.setScrollDimensions({
|
||||
width: this.size,
|
||||
scrollWidth: this.contentSize
|
||||
scrollWidth: this._contentSize
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1411,7 +1416,7 @@ export class SplitView<TLayoutContext = undefined, TView extends IView<TLayoutCo
|
||||
|
||||
if (snappedBefore && collapsesUp[index] && (position > 0 || this.startSnappingEnabled)) {
|
||||
sash.state = SashState.AtMinimum;
|
||||
} else if (snappedAfter && collapsesDown[index] && (position < this.contentSize || this.endSnappingEnabled)) {
|
||||
} else if (snappedAfter && collapsesDown[index] && (position < this._contentSize || this.endSnappingEnabled)) {
|
||||
sash.state = SashState.AtMaximum;
|
||||
} else {
|
||||
sash.state = SashState.Disabled;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { isTypedArray, isObject, isUndefinedOrNull } from 'vs/base/common/types';
|
||||
import { isTypedArray, isObject, isUndefinedOrNull, OptionalBooleanKey, OptionalNumberKey, OptionalStringKey } from 'vs/base/common/types';
|
||||
|
||||
export function deepClone<T>(obj: T): T {
|
||||
if (!obj || typeof obj !== 'object') {
|
||||
@@ -261,3 +261,34 @@ export function createProxyObject<T extends object>(methodNames: string[], invok
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function ensureOptionalBooleanValue<T extends object>(obj: T, key: OptionalBooleanKey<T>, defaultValue: boolean | undefined): void {
|
||||
if (typeof key !== 'string') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj[key] !== undefined && typeof obj[key] !== 'boolean') {
|
||||
obj[key] = defaultValue as any;
|
||||
}
|
||||
}
|
||||
|
||||
export function ensureOptionalNumberValue<T extends object>(obj: T, key: OptionalNumberKey<T>, defaultValue: number | undefined): void {
|
||||
if (typeof key !== 'string') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj[key] !== undefined && typeof obj[key] !== 'number') {
|
||||
obj[key] = defaultValue as any;
|
||||
}
|
||||
}
|
||||
|
||||
export function ensureOptionalStringValue<T extends object>(obj: T, key: OptionalStringKey<T>, allowed: string[], defaultValue: string | undefined): void {
|
||||
if (typeof key !== 'string') {
|
||||
return;
|
||||
}
|
||||
|
||||
const value = obj[key];
|
||||
if (value !== undefined && (typeof value !== 'string' || !allowed.includes(value))) {
|
||||
obj[key] = defaultValue as any;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,6 +231,15 @@ export function transaction(fn: (tx: ITransaction) => void, getDebugName?: () =>
|
||||
}
|
||||
}
|
||||
|
||||
export async function asyncTransaction(fn: (tx: ITransaction) => Promise<void>, getDebugName?: () => string): Promise<void> {
|
||||
const tx = new TransactionImpl(fn, getDebugName);
|
||||
try {
|
||||
await fn(tx);
|
||||
} finally {
|
||||
tx.finish();
|
||||
}
|
||||
}
|
||||
|
||||
export function subtransaction(tx: ITransaction | undefined, fn: (tx: ITransaction) => void, getDebugName?: () => string): void {
|
||||
if (!tx) {
|
||||
transaction(fn, getDebugName);
|
||||
|
||||
@@ -227,3 +227,15 @@ export type Mutable<T> = {
|
||||
* A single object or an array of the objects.
|
||||
*/
|
||||
export type SingleOrMany<T> = T | T[];
|
||||
|
||||
export type OptionalBooleanKey<T> = {
|
||||
[K in keyof T]: T[K] extends boolean | undefined ? K : never;
|
||||
}[keyof T];
|
||||
|
||||
export type OptionalNumberKey<T> = {
|
||||
[K in keyof T]: T[K] extends number | undefined ? K : never;
|
||||
}[keyof T];
|
||||
|
||||
export type OptionalStringKey<T> = {
|
||||
[K in keyof T]: T[K] extends string | undefined ? K : never;
|
||||
}[keyof T];
|
||||
|
||||
@@ -7,7 +7,7 @@ import * as assert from 'assert';
|
||||
import { createSerializedGrid, Direction, getRelativeLocation, Grid, GridNode, GridNodeDescriptor, ISerializableView, isGridBranchNode, IViewDeserializer, Orientation, sanitizeGridNodeDescriptor, SerializableGrid, Sizing } from 'vs/base/browser/ui/grid/grid';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { deepClone } from 'vs/base/common/objects';
|
||||
import { nodesToArrays, TestView } from './util';
|
||||
import { nodesToArrays, TestView } from 'vs/base/test/browser/ui/grid/util';
|
||||
import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
|
||||
@@ -464,6 +464,206 @@ suite('Grid', function () {
|
||||
|
||||
assert.deepStrictEqual(grid.getNeighborViews(view1, Direction.Right), [view2, view3]);
|
||||
});
|
||||
|
||||
test('hiding splitviews and restoring sizes', function () {
|
||||
const view1 = store.add(new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE));
|
||||
const grid = store.add(new Grid(view1));
|
||||
container.appendChild(grid.element);
|
||||
|
||||
grid.layout(800, 600);
|
||||
|
||||
const view2 = store.add(new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE));
|
||||
grid.addView(view2, Sizing.Distribute, view1, Direction.Right);
|
||||
|
||||
const view3 = store.add(new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE));
|
||||
grid.addView(view3, Sizing.Distribute, view2, Direction.Down);
|
||||
|
||||
const view4 = store.add(new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE));
|
||||
grid.addView(view4, Sizing.Distribute, view2, Direction.Right);
|
||||
|
||||
const size1 = view1.size;
|
||||
const size2 = view2.size;
|
||||
const size3 = view3.size;
|
||||
const size4 = view4.size;
|
||||
|
||||
grid.maximizeView(view1);
|
||||
|
||||
// Views 2, 3, 4 are hidden
|
||||
// Splitview (2,4) and ((2,4),3) are hidden
|
||||
assert.deepStrictEqual(view1.size, [800, 600]);
|
||||
assert.deepStrictEqual(view2.size, [0, 0]);
|
||||
assert.deepStrictEqual(view3.size, [0, 0]);
|
||||
assert.deepStrictEqual(view4.size, [0, 0]);
|
||||
|
||||
grid.exitMaximizedView();
|
||||
|
||||
assert.deepStrictEqual(view1.size, size1);
|
||||
assert.deepStrictEqual(view2.size, size2);
|
||||
assert.deepStrictEqual(view3.size, size3);
|
||||
assert.deepStrictEqual(view4.size, size4);
|
||||
|
||||
// Views 1, 3, 4 are hidden
|
||||
// All splitviews are still visible => only orthogonalsize is 0
|
||||
grid.maximizeView(view2);
|
||||
|
||||
assert.deepStrictEqual(view1.size, [0, 600]);
|
||||
assert.deepStrictEqual(view2.size, [800, 600]);
|
||||
assert.deepStrictEqual(view3.size, [800, 0]);
|
||||
assert.deepStrictEqual(view4.size, [0, 600]);
|
||||
|
||||
grid.exitMaximizedView();
|
||||
|
||||
assert.deepStrictEqual(view1.size, size1);
|
||||
assert.deepStrictEqual(view2.size, size2);
|
||||
assert.deepStrictEqual(view3.size, size3);
|
||||
assert.deepStrictEqual(view4.size, size4);
|
||||
});
|
||||
|
||||
test('hasMaximizedView', function () {
|
||||
const view1 = store.add(new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE));
|
||||
const grid = store.add(new Grid(view1));
|
||||
container.appendChild(grid.element);
|
||||
|
||||
grid.layout(800, 600);
|
||||
|
||||
const view2 = store.add(new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE));
|
||||
grid.addView(view2, Sizing.Distribute, view1, Direction.Right);
|
||||
|
||||
const view3 = store.add(new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE));
|
||||
grid.addView(view3, Sizing.Distribute, view2, Direction.Down);
|
||||
|
||||
const view4 = store.add(new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE));
|
||||
grid.addView(view4, Sizing.Distribute, view2, Direction.Right);
|
||||
|
||||
function checkIsMaximized(view: TestView) {
|
||||
grid.maximizeView(view);
|
||||
|
||||
assert.deepStrictEqual(grid.hasMaximizedView(), true);
|
||||
|
||||
// When a view is maximized, no view can be expanded even if it is maximized
|
||||
assert.deepStrictEqual(grid.isViewExpanded(view1), false);
|
||||
assert.deepStrictEqual(grid.isViewExpanded(view2), false);
|
||||
assert.deepStrictEqual(grid.isViewExpanded(view3), false);
|
||||
assert.deepStrictEqual(grid.isViewExpanded(view4), false);
|
||||
|
||||
grid.exitMaximizedView();
|
||||
|
||||
assert.deepStrictEqual(grid.hasMaximizedView(), false);
|
||||
}
|
||||
|
||||
checkIsMaximized(view1);
|
||||
checkIsMaximized(view2);
|
||||
checkIsMaximized(view3);
|
||||
checkIsMaximized(view4);
|
||||
});
|
||||
|
||||
test('Changes to the grid unmaximize the view', function () {
|
||||
const view1 = store.add(new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE));
|
||||
const grid = store.add(new Grid(view1));
|
||||
container.appendChild(grid.element);
|
||||
|
||||
grid.layout(800, 600);
|
||||
|
||||
const view2 = store.add(new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE));
|
||||
grid.addView(view2, Sizing.Distribute, view1, Direction.Right);
|
||||
|
||||
const view3 = store.add(new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE));
|
||||
grid.addView(view3, Sizing.Distribute, view2, Direction.Down);
|
||||
|
||||
const view4 = store.add(new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE));
|
||||
|
||||
// Adding a view unmaximizes the view
|
||||
grid.maximizeView(view1);
|
||||
assert.deepStrictEqual(grid.hasMaximizedView(), true);
|
||||
grid.addView(view4, Sizing.Distribute, view2, Direction.Right);
|
||||
|
||||
assert.deepStrictEqual(grid.hasMaximizedView(), false);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view1), true);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view2), true);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view3), true);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view4), true);
|
||||
|
||||
// Removing a view unmaximizes the view
|
||||
grid.maximizeView(view1);
|
||||
assert.deepStrictEqual(grid.hasMaximizedView(), true);
|
||||
grid.removeView(view4);
|
||||
|
||||
assert.deepStrictEqual(grid.hasMaximizedView(), false);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view1), true);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view2), true);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view3), true);
|
||||
|
||||
// Changing the visibility of any view while a view is maximized, unmaximizes the view
|
||||
grid.maximizeView(view1);
|
||||
assert.deepStrictEqual(grid.hasMaximizedView(), true);
|
||||
grid.setViewVisible(view3, true);
|
||||
|
||||
assert.deepStrictEqual(grid.hasMaximizedView(), false);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view1), true);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view2), true);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view3), true);
|
||||
});
|
||||
|
||||
test('Changes to the grid sizing unmaximize the view', function () {
|
||||
const view1 = store.add(new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE));
|
||||
const grid = store.add(new Grid(view1));
|
||||
container.appendChild(grid.element);
|
||||
|
||||
grid.layout(800, 600);
|
||||
|
||||
const view2 = store.add(new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE));
|
||||
grid.addView(view2, Sizing.Distribute, view1, Direction.Right);
|
||||
|
||||
const view3 = store.add(new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE));
|
||||
grid.addView(view3, Sizing.Distribute, view2, Direction.Down);
|
||||
|
||||
const view4 = store.add(new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE));
|
||||
grid.addView(view4, Sizing.Distribute, view2, Direction.Right);
|
||||
|
||||
// Maximizing a different view unmaximizes the current one and maximizes the new one
|
||||
grid.maximizeView(view1);
|
||||
assert.deepStrictEqual(grid.hasMaximizedView(), true);
|
||||
grid.maximizeView(view2);
|
||||
|
||||
assert.deepStrictEqual(grid.hasMaximizedView(), true);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view1), false);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view2), true);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view3), false);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view4), false);
|
||||
|
||||
// Distributing the size unmaximizes the view
|
||||
grid.maximizeView(view1);
|
||||
assert.deepStrictEqual(grid.hasMaximizedView(), true);
|
||||
grid.distributeViewSizes();
|
||||
|
||||
assert.deepStrictEqual(grid.hasMaximizedView(), false);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view1), true);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view2), true);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view3), true);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view4), true);
|
||||
|
||||
// Expanding a different view unmaximizes the view
|
||||
grid.maximizeView(view1);
|
||||
assert.deepStrictEqual(grid.hasMaximizedView(), true);
|
||||
grid.expandView(view2);
|
||||
|
||||
assert.deepStrictEqual(grid.hasMaximizedView(), false);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view1), true);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view2), true);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view3), true);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view4), true);
|
||||
|
||||
// Expanding the maximized view unmaximizes the view
|
||||
grid.maximizeView(view1);
|
||||
assert.deepStrictEqual(grid.hasMaximizedView(), true);
|
||||
grid.expandView(view1);
|
||||
|
||||
assert.deepStrictEqual(grid.hasMaximizedView(), false);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view1), true);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view2), true);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view3), true);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view4), true);
|
||||
});
|
||||
});
|
||||
|
||||
class TestSerializableView extends TestView implements ISerializableView {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import * as assert from 'assert';
|
||||
import { $ } from 'vs/base/browser/dom';
|
||||
import { GridView, IView, Orientation, Sizing } from 'vs/base/browser/ui/grid/gridview';
|
||||
import { nodesToArrays, TestView } from './util';
|
||||
import { nodesToArrays, TestView } from 'vs/base/test/browser/ui/grid/util';
|
||||
import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils';
|
||||
|
||||
suite('Gridview', function () {
|
||||
|
||||
@@ -227,4 +227,84 @@ suite('Objects', () => {
|
||||
assert.strictEqual(obj1.mIxEdCaSe, objects.getCaseInsensitive(obj1, 'MIXEDCASE'));
|
||||
assert.strictEqual(obj1.mIxEdCaSe, objects.getCaseInsensitive(obj1, 'mixedcase'));
|
||||
});
|
||||
|
||||
test('ensureOptionalBooleanValue', () => {
|
||||
const obj: any = {
|
||||
a: true,
|
||||
b: false,
|
||||
c: undefined,
|
||||
d: 5,
|
||||
e: 'foo'
|
||||
};
|
||||
|
||||
objects.ensureOptionalBooleanValue(obj, 'a', false);
|
||||
assert.strictEqual(obj.a, true);
|
||||
|
||||
objects.ensureOptionalBooleanValue(obj, 'b', true);
|
||||
assert.strictEqual(obj.b, false);
|
||||
|
||||
objects.ensureOptionalBooleanValue(obj, 'c', true);
|
||||
assert.strictEqual(obj.c, undefined);
|
||||
|
||||
objects.ensureOptionalBooleanValue(obj, 'd', true);
|
||||
assert.strictEqual(obj.d, true);
|
||||
|
||||
objects.ensureOptionalBooleanValue(obj, 'e', true);
|
||||
assert.strictEqual(obj.e, true);
|
||||
});
|
||||
|
||||
test('ensureOptionalNumberValue', () => {
|
||||
const obj: any = {
|
||||
a: 1,
|
||||
b: 0,
|
||||
c: undefined,
|
||||
d: true,
|
||||
e: 'foo'
|
||||
};
|
||||
|
||||
objects.ensureOptionalNumberValue(obj, 'a', 0);
|
||||
assert.strictEqual(obj.a, 1);
|
||||
|
||||
objects.ensureOptionalNumberValue(obj, 'b', 1);
|
||||
assert.strictEqual(obj.b, 0);
|
||||
|
||||
objects.ensureOptionalNumberValue(obj, 'c', 1);
|
||||
assert.strictEqual(obj.c, undefined);
|
||||
|
||||
objects.ensureOptionalNumberValue(obj, 'd', 1);
|
||||
assert.strictEqual(obj.d, 1);
|
||||
|
||||
objects.ensureOptionalNumberValue(obj, 'e', 1);
|
||||
assert.strictEqual(obj.e, 1);
|
||||
});
|
||||
|
||||
test('ensureOptionalStringValue', () => {
|
||||
const obj: any = {
|
||||
a: 'hello',
|
||||
b: 'world',
|
||||
c: undefined,
|
||||
d: 'earth',
|
||||
e: 5,
|
||||
f: true
|
||||
};
|
||||
|
||||
objects.ensureOptionalStringValue(obj, 'a', ['hello', 'world'], 'world');
|
||||
assert.strictEqual(obj.a, 'hello');
|
||||
|
||||
objects.ensureOptionalStringValue(obj, 'b', ['hello', 'world'], 'hello');
|
||||
assert.strictEqual(obj.b, 'world');
|
||||
|
||||
objects.ensureOptionalStringValue(obj, 'c', ['hello', 'world'], 'world');
|
||||
assert.strictEqual(obj.c, undefined);
|
||||
|
||||
objects.ensureOptionalStringValue(obj, 'd', ['hello', 'world'], 'world');
|
||||
assert.strictEqual(obj.d, 'world');
|
||||
|
||||
objects.ensureOptionalStringValue(obj, 'e', ['hello', 'world'], 'world');
|
||||
assert.strictEqual(obj.e, 'world');
|
||||
|
||||
objects.ensureOptionalStringValue(obj, 'f', ['hello', 'world'], 'world');
|
||||
assert.strictEqual(obj.f, 'world');
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { transaction } from 'vs/base/common/observable';
|
||||
import { asyncTransaction } from 'vs/base/common/observableInternal/base';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { EditorAction, ServicesAccessor } from 'vs/editor/browser/editorExtensions';
|
||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
@@ -72,7 +73,11 @@ export class TriggerInlineSuggestionAction extends EditorAction {
|
||||
|
||||
public async run(accessor: ServicesAccessor | undefined, editor: ICodeEditor): Promise<void> {
|
||||
const controller = InlineCompletionsController.get(editor);
|
||||
controller?.model.get()?.triggerExplicitly();
|
||||
await asyncTransaction(async tx => {
|
||||
/** @description triggerExplicitly from command */
|
||||
await controller?.model.get()?.triggerExplicitly(tx);
|
||||
controller?.playAudioCue(tx);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { alert } from 'vs/base/browser/ui/aria/aria';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ITransaction, autorun, constObservable, disposableObservableValue, observableFromEvent, observableValue, transaction } from 'vs/base/common/observable';
|
||||
import { ITransaction, autorun, autorunHandleChanges, constObservable, disposableObservableValue, observableFromEvent, observableSignal, observableValue, transaction } from 'vs/base/common/observable';
|
||||
import { CoreEditingCommands } from 'vs/editor/browser/coreCommands';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
@@ -38,9 +38,9 @@ export class InlineCompletionsController extends Disposable {
|
||||
}
|
||||
|
||||
public readonly model = disposableObservableValue<InlineCompletionsModel | undefined>('inlineCompletionModel', undefined);
|
||||
private readonly textModelVersionId = observableValue<number, VersionIdChangeReason>(this, -1);
|
||||
private readonly cursorPosition = observableValue<Position>(this, new Position(1, 1));
|
||||
private readonly suggestWidgetAdaptor = this._register(new SuggestWidgetAdaptor(
|
||||
private readonly _textModelVersionId = observableValue<number, VersionIdChangeReason>(this, -1);
|
||||
private readonly _cursorPosition = observableValue<Position>(this, new Position(1, 1));
|
||||
private readonly _suggestWidgetAdaptor = this._register(new SuggestWidgetAdaptor(
|
||||
this.editor,
|
||||
() => this.model.get()?.selectedInlineCompletion.get()?.toSingleTextEdit(undefined),
|
||||
(tx) => this.updateObservables(tx, VersionIdChangeReason.Other),
|
||||
@@ -54,32 +54,34 @@ export class InlineCompletionsController extends Disposable {
|
||||
));
|
||||
private readonly _enabled = observableFromEvent(this.editor.onDidChangeConfiguration, () => this.editor.getOption(EditorOption.inlineSuggest).enabled);
|
||||
|
||||
private ghostTextWidget = this._register(this.instantiationService.createInstance(GhostTextWidget, this.editor, {
|
||||
private _ghostTextWidget = this._register(this._instantiationService.createInstance(GhostTextWidget, this.editor, {
|
||||
ghostText: this.model.map((v, reader) => /** ghostText */ v?.ghostText.read(reader)),
|
||||
minReservedLineCount: constObservable(0),
|
||||
targetTextModel: this.model.map(v => v?.textModel),
|
||||
}));
|
||||
|
||||
private readonly _debounceValue = this.debounceService.for(
|
||||
this.languageFeaturesService.inlineCompletionsProvider,
|
||||
private readonly _debounceValue = this._debounceService.for(
|
||||
this._languageFeaturesService.inlineCompletionsProvider,
|
||||
'InlineCompletionsDebounce',
|
||||
{ min: 50, max: 50 }
|
||||
);
|
||||
|
||||
private readonly _playAudioCueSignal = observableSignal(this);
|
||||
|
||||
constructor(
|
||||
public readonly editor: ICodeEditor,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IContextKeyService private readonly contextKeyService: IContextKeyService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@ICommandService private readonly commandService: ICommandService,
|
||||
@ILanguageFeatureDebounceService private readonly debounceService: ILanguageFeatureDebounceService,
|
||||
@ILanguageFeaturesService private readonly languageFeaturesService: ILanguageFeaturesService,
|
||||
@IAudioCueService private readonly audioCueService: IAudioCueService,
|
||||
@IKeybindingService private readonly _keybindingService: IKeybindingService
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService,
|
||||
@ICommandService private readonly _commandService: ICommandService,
|
||||
@ILanguageFeatureDebounceService private readonly _debounceService: ILanguageFeatureDebounceService,
|
||||
@ILanguageFeaturesService private readonly _languageFeaturesService: ILanguageFeaturesService,
|
||||
@IAudioCueService private readonly _audioCueService: IAudioCueService,
|
||||
@IKeybindingService private readonly _keybindingService: IKeybindingService,
|
||||
) {
|
||||
super();
|
||||
|
||||
this._register(new InlineCompletionContextKeys(this.contextKeyService, this.model));
|
||||
this._register(new InlineCompletionContextKeys(this._contextKeyService, this.model));
|
||||
this._register(Event.runAndSubscribe(editor.onDidChangeModel, () => transaction(tx => {
|
||||
/** @description onDidChangeModel */
|
||||
this.model.set(undefined, tx);
|
||||
@@ -87,12 +89,12 @@ export class InlineCompletionsController extends Disposable {
|
||||
|
||||
const textModel = editor.getModel();
|
||||
if (textModel) {
|
||||
const model = instantiationService.createInstance(
|
||||
const model = _instantiationService.createInstance(
|
||||
InlineCompletionsModel,
|
||||
textModel,
|
||||
this.suggestWidgetAdaptor.selectedItem,
|
||||
this.cursorPosition,
|
||||
this.textModelVersionId,
|
||||
this._suggestWidgetAdaptor.selectedItem,
|
||||
this._cursorPosition,
|
||||
this._textModelVersionId,
|
||||
this._debounceValue,
|
||||
observableFromEvent(editor.onDidChangeConfiguration, () => editor.getOption(EditorOption.suggest).preview),
|
||||
observableFromEvent(editor.onDidChangeConfiguration, () => editor.getOption(EditorOption.suggest).previewMode),
|
||||
@@ -130,7 +132,7 @@ export class InlineCompletionsController extends Disposable {
|
||||
}
|
||||
})));
|
||||
|
||||
this._register(this.commandService.onDidExecuteCommand((e) => {
|
||||
this._register(this._commandService.onDidExecuteCommand((e) => {
|
||||
// These commands don't trigger onDidType.
|
||||
const commands = new Set([
|
||||
CoreEditingCommands.Tab.id,
|
||||
@@ -149,7 +151,7 @@ export class InlineCompletionsController extends Disposable {
|
||||
|
||||
this._register(this.editor.onDidBlurEditorWidget(() => {
|
||||
// This is a hidden setting very useful for debugging
|
||||
if (this.contextKeyService.getContextKeyValue<boolean>('accessibleViewIsShown') || this.configurationService.getValue('editor.inlineSuggest.keepOnBlur') ||
|
||||
if (this._contextKeyService.getContextKeyValue<boolean>('accessibleViewIsShown') || this._configurationService.getValue('editor.inlineSuggest.keepOnBlur') ||
|
||||
editor.getOption(EditorOption.inlineSuggest).keepOnBlur) {
|
||||
return;
|
||||
}
|
||||
@@ -167,19 +169,28 @@ export class InlineCompletionsController extends Disposable {
|
||||
const state = this.model.read(reader)?.state.read(reader);
|
||||
if (state?.suggestItem) {
|
||||
if (state.ghostText.lineCount >= 2) {
|
||||
this.suggestWidgetAdaptor.forceRenderingAbove();
|
||||
this._suggestWidgetAdaptor.forceRenderingAbove();
|
||||
}
|
||||
} else {
|
||||
this.suggestWidgetAdaptor.stopForceRenderingAbove();
|
||||
this._suggestWidgetAdaptor.stopForceRenderingAbove();
|
||||
}
|
||||
}));
|
||||
this._register(toDisposable(() => {
|
||||
this.suggestWidgetAdaptor.stopForceRenderingAbove();
|
||||
this._suggestWidgetAdaptor.stopForceRenderingAbove();
|
||||
}));
|
||||
|
||||
let lastInlineCompletionId: string | undefined = undefined;
|
||||
this._register(autorun(reader => {
|
||||
this._register(autorunHandleChanges({
|
||||
handleChange: (context, changeSummary) => {
|
||||
if (context.didChange(this._playAudioCueSignal)) {
|
||||
lastInlineCompletionId = undefined;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
}, async reader => {
|
||||
/** @description play audio cue & read suggestion */
|
||||
this._playAudioCueSignal.read(reader);
|
||||
|
||||
const model = this.model.read(reader);
|
||||
const state = model?.state.read(reader);
|
||||
if (!model || !state || !state.inlineCompletion) {
|
||||
@@ -190,7 +201,7 @@ export class InlineCompletionsController extends Disposable {
|
||||
if (state.inlineCompletion.semanticId !== lastInlineCompletionId) {
|
||||
lastInlineCompletionId = state.inlineCompletion.semanticId;
|
||||
const lineText = model.textModel.getLineContent(state.ghostText.lineNumber);
|
||||
this.audioCueService.playAudioCue(AudioCue.inlineSuggestion).then(() => {
|
||||
this._audioCueService.playAudioCue(AudioCue.inlineSuggestion).then(() => {
|
||||
if (this.editor.getOption(EditorOption.screenReaderAnnounceInlineSuggestion)) {
|
||||
this.provideScreenReaderUpdate(state.ghostText.renderForScreenReader(lineText));
|
||||
}
|
||||
@@ -198,17 +209,21 @@ export class InlineCompletionsController extends Disposable {
|
||||
}
|
||||
}));
|
||||
|
||||
this._register(new InlineCompletionsHintsWidget(this.editor, this.model, this.instantiationService));
|
||||
this._register(this.configurationService.onDidChangeConfiguration(e => {
|
||||
this._register(new InlineCompletionsHintsWidget(this.editor, this.model, this._instantiationService));
|
||||
this._register(this._configurationService.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration('accessibility.verbosity.inlineCompletions')) {
|
||||
this.editor.updateOptions({ inlineCompletionsAccessibilityVerbose: this.configurationService.getValue('accessibility.verbosity.inlineCompletions') });
|
||||
this.editor.updateOptions({ inlineCompletionsAccessibilityVerbose: this._configurationService.getValue('accessibility.verbosity.inlineCompletions') });
|
||||
}
|
||||
}));
|
||||
this.editor.updateOptions({ inlineCompletionsAccessibilityVerbose: this.configurationService.getValue('accessibility.verbosity.inlineCompletions') });
|
||||
this.editor.updateOptions({ inlineCompletionsAccessibilityVerbose: this._configurationService.getValue('accessibility.verbosity.inlineCompletions') });
|
||||
}
|
||||
|
||||
public playAudioCue(tx: ITransaction) {
|
||||
this._playAudioCueSignal.trigger(tx);
|
||||
}
|
||||
|
||||
private provideScreenReaderUpdate(content: string): void {
|
||||
const accessibleViewShowing = this.contextKeyService.getContextKeyValue<boolean>('accessibleViewIsShown');
|
||||
const accessibleViewShowing = this._contextKeyService.getContextKeyValue<boolean>('accessibleViewIsShown');
|
||||
const accessibleViewKeybinding = this._keybindingService.lookupKeybinding('editor.action.accessibleView');
|
||||
let hint: string | undefined;
|
||||
if (!accessibleViewShowing && accessibleViewKeybinding && this.editor.getOption(EditorOption.inlineCompletionsAccessibilityVerbose)) {
|
||||
@@ -224,8 +239,8 @@ export class InlineCompletionsController extends Disposable {
|
||||
*/
|
||||
private updateObservables(tx: ITransaction, changeReason: VersionIdChangeReason): void {
|
||||
const newModel = this.editor.getModel();
|
||||
this.textModelVersionId.set(newModel?.getVersionId() ?? -1, tx, changeReason);
|
||||
this.cursorPosition.set(this.editor.getPosition() ?? new Position(1, 1), tx);
|
||||
this._textModelVersionId.set(newModel?.getVersionId() ?? -1, tx, changeReason);
|
||||
this._cursorPosition.set(this.editor.getPosition() ?? new Position(1, 1), tx);
|
||||
}
|
||||
|
||||
public shouldShowHoverAt(range: Range) {
|
||||
@@ -237,7 +252,7 @@ export class InlineCompletionsController extends Disposable {
|
||||
}
|
||||
|
||||
public shouldShowHoverAtViewZone(viewZoneId: string): boolean {
|
||||
return this.ghostTextWidget.ownsViewZone(viewZoneId);
|
||||
return this._ghostTextWidget.ownsViewZone(viewZoneId);
|
||||
}
|
||||
|
||||
public hide() {
|
||||
|
||||
@@ -35,7 +35,7 @@ export enum VersionIdChangeReason {
|
||||
export class InlineCompletionsModel extends Disposable {
|
||||
private readonly _source = this._register(this._instantiationService.createInstance(InlineCompletionsSource, this.textModel, this.textModelVersionId, this._debounceValue));
|
||||
private readonly _isActive = observableValue<boolean, InlineCompletionTriggerKind | void>(this, false);
|
||||
private readonly _forceUpdate = observableSignal<InlineCompletionTriggerKind>('forceUpdate');
|
||||
readonly _forceUpdateSignal = observableSignal<InlineCompletionTriggerKind>('forceUpdate');
|
||||
|
||||
// We use a semantic id to keep the same inline completion selected even if the provider reorders the completions.
|
||||
private readonly _selectedInlineCompletionId = observableValue<string | undefined>(this, undefined);
|
||||
@@ -92,13 +92,13 @@ export class InlineCompletionsModel extends Disposable {
|
||||
/** @description fetch inline completions */
|
||||
if (ctx.didChange(this.textModelVersionId) && this._preserveCurrentCompletionReasons.has(ctx.change)) {
|
||||
changeSummary.preserveCurrentCompletion = true;
|
||||
} else if (ctx.didChange(this._forceUpdate)) {
|
||||
} else if (ctx.didChange(this._forceUpdateSignal)) {
|
||||
changeSummary.inlineCompletionTriggerKind = ctx.change;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
}, (reader, changeSummary) => {
|
||||
this._forceUpdate.read(reader);
|
||||
this._forceUpdateSignal.read(reader);
|
||||
const shouldUpdate = (this._enabled.read(reader) && this.selectedSuggestItem.read(reader)) || this._isActive.read(reader);
|
||||
if (!shouldUpdate) {
|
||||
this._source.cancelUpdate();
|
||||
@@ -140,7 +140,7 @@ export class InlineCompletionsModel extends Disposable {
|
||||
public async triggerExplicitly(tx?: ITransaction): Promise<void> {
|
||||
subtransaction(tx, tx => {
|
||||
this._isActive.set(true, tx);
|
||||
this._forceUpdate.trigger(tx, InlineCompletionTriggerKind.Explicit);
|
||||
this._forceUpdateSignal.trigger(tx, InlineCompletionTriggerKind.Explicit);
|
||||
});
|
||||
await this._fetchInlineCompletions.get();
|
||||
}
|
||||
|
||||
@@ -110,7 +110,6 @@ export class MenuId {
|
||||
static readonly SCMResourceFolderContext = new MenuId('SCMResourceFolderContext');
|
||||
static readonly SCMResourceGroupContext = new MenuId('SCMResourceGroupContext');
|
||||
static readonly SCMSourceControl = new MenuId('SCMSourceControl');
|
||||
static readonly SCMInputBox = new MenuId('SCMInputBox');
|
||||
static readonly SCMTitle = new MenuId('SCMTitle');
|
||||
static readonly SearchContext = new MenuId('SearchContext');
|
||||
static readonly SearchActionMenu = new MenuId('SearchActionContext');
|
||||
|
||||
@@ -59,6 +59,15 @@ export class QuickInputController extends Disposable {
|
||||
this.parentElement = options.container;
|
||||
this.styles = options.styles;
|
||||
this._register(Event.runAndSubscribe(dom.onDidRegisterWindow, ({ window, disposableStore }) => this.registerKeyModsListeners(window, disposableStore), { window, disposableStore: this._store }));
|
||||
this._register(dom.onWillUnregisterWindow(window => {
|
||||
if (this.ui && dom.getWindow(this.ui.container) === window) {
|
||||
// The window this quick input is contained in is about to
|
||||
// close, so we have to make sure to reparent it back to an
|
||||
// existing parent to not loose functionality.
|
||||
// (https://github.com/microsoft/vscode/issues/195870)
|
||||
this.reparentUI(this.layoutService.container);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private registerKeyModsListeners(window: Window, disposables: DisposableStore): void {
|
||||
@@ -74,11 +83,10 @@ export class QuickInputController extends Disposable {
|
||||
|
||||
private getUI() {
|
||||
if (this.ui) {
|
||||
// In order to support aux windows, re-parent the controller if the original event is
|
||||
// from a different document
|
||||
// In order to support aux windows, re-parent the controller
|
||||
// if the original event is from a different document
|
||||
if (this.parentElement.ownerDocument !== this.layoutService.activeContainer.ownerDocument) {
|
||||
this.parentElement = this.layoutService.activeContainer;
|
||||
dom.append(this.parentElement, this.ui.container);
|
||||
this.reparentUI(this.layoutService.activeContainer);
|
||||
}
|
||||
|
||||
return this.ui;
|
||||
@@ -306,6 +314,13 @@ export class QuickInputController extends Disposable {
|
||||
return this.ui;
|
||||
}
|
||||
|
||||
private reparentUI(container: HTMLElement): void {
|
||||
if (this.ui) {
|
||||
this.parentElement = container;
|
||||
dom.append(this.parentElement, this.ui.container);
|
||||
}
|
||||
}
|
||||
|
||||
pick<T extends IQuickPickItem, O extends IPickOptions<T>>(picks: Promise<QuickPickInput<T>[]> | QuickPickInput<T>[], options: O = <O>{}, token: CancellationToken = CancellationToken.None): Promise<(O extends { canPickMany: true } ? T[] : T) | undefined> {
|
||||
type R = (O extends { canPickMany: true } ? T[] : T) | undefined;
|
||||
return new Promise<R>((doResolve, reject) => {
|
||||
|
||||
@@ -489,7 +489,7 @@ export class HideEditorTabsAction extends Action2 {
|
||||
});
|
||||
}
|
||||
|
||||
override run(accessor: ServicesAccessor): Promise<void> {
|
||||
run(accessor: ServicesAccessor): Promise<void> {
|
||||
const configurationService = accessor.get(IConfigurationService);
|
||||
return configurationService.updateValue('workbench.editor.showTabs', 'none');
|
||||
}
|
||||
@@ -515,7 +515,7 @@ export class ShowMultipleEditorTabsAction extends Action2 {
|
||||
});
|
||||
}
|
||||
|
||||
override run(accessor: ServicesAccessor): Promise<void> {
|
||||
run(accessor: ServicesAccessor): Promise<void> {
|
||||
const configurationService = accessor.get(IConfigurationService);
|
||||
return configurationService.updateValue('workbench.editor.showTabs', 'multiple');
|
||||
}
|
||||
@@ -541,7 +541,7 @@ export class ShowSingleEditorTabAction extends Action2 {
|
||||
});
|
||||
}
|
||||
|
||||
override run(accessor: ServicesAccessor): Promise<void> {
|
||||
run(accessor: ServicesAccessor): Promise<void> {
|
||||
const configurationService = accessor.get(IConfigurationService);
|
||||
return configurationService.updateValue('workbench.editor.showTabs', 'single');
|
||||
}
|
||||
@@ -550,13 +550,11 @@ registerAction2(ShowSingleEditorTabAction);
|
||||
|
||||
// --- Toggle Pinned Tabs On Separate Row
|
||||
|
||||
export class ToggleSeparatePinnedTabsAction extends Action2 {
|
||||
|
||||
static readonly ID = 'workbench.action.toggleSeparatePinnedEditorTabs';
|
||||
registerAction2(class extends Action2 {
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: ToggleSeparatePinnedTabsAction.ID,
|
||||
id: 'workbench.action.toggleSeparatePinnedEditorTabs',
|
||||
title: {
|
||||
value: localize('toggleSeparatePinnedEditorTabs', "Separate Pinned Editor Tabs"),
|
||||
original: 'Separate Pinned Editor Tabs'
|
||||
@@ -567,7 +565,7 @@ export class ToggleSeparatePinnedTabsAction extends Action2 {
|
||||
});
|
||||
}
|
||||
|
||||
override run(accessor: ServicesAccessor): Promise<void> {
|
||||
run(accessor: ServicesAccessor): Promise<void> {
|
||||
const configurationService = accessor.get(IConfigurationService);
|
||||
|
||||
const oldettingValue = configurationService.getValue<string>('workbench.editor.pinnedTabsOnSeparateRow');
|
||||
@@ -575,8 +573,7 @@ export class ToggleSeparatePinnedTabsAction extends Action2 {
|
||||
|
||||
return configurationService.updateValue('workbench.editor.pinnedTabsOnSeparateRow', newSettingValue);
|
||||
}
|
||||
}
|
||||
registerAction2(ToggleSeparatePinnedTabsAction);
|
||||
});
|
||||
|
||||
// --- Toggle Zen Mode
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Event } from 'vs/base/common/event';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IContextKeyService, IContextKey, setConstant as setConstantContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { InputFocusedContext, IsMacContext, IsLinuxContext, IsWindowsContext, IsWebContext, IsMacNativeContext, IsDevelopmentContext, IsIOSContext, ProductQualityContext, IsMobileContext } from 'vs/platform/contextkey/common/contextkeys';
|
||||
import { SplitEditorsVertically, InEditorZenModeContext, ActiveEditorCanRevertContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, EmbedderIdentifierContext, EditorTabsVisibleContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorReadonlyContext, EditorAreaVisibleContext, ActiveEditorAvailableEditorIdsContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext, ActiveEditorCanToggleReadonlyContext, applyAvailableEditorIds, TitleBarVisibleContext } from 'vs/workbench/common/contextkeys';
|
||||
import { SplitEditorsVertically, InEditorZenModeContext, ActiveEditorCanRevertContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, EmbedderIdentifierContext, EditorTabsVisibleContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorReadonlyContext, EditorAreaVisibleContext, ActiveEditorAvailableEditorIdsContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext, ActiveEditorCanToggleReadonlyContext, applyAvailableEditorIds, MaximizedEditorGroupContext, TitleBarVisibleContext } from 'vs/workbench/common/contextkeys';
|
||||
import { TEXT_DIFF_EDITOR_ID, EditorInputCapabilities, SIDE_BY_SIDE_EDITOR_ID, EditorResourceAccessor, SideBySideEditor } from 'vs/workbench/common/editor';
|
||||
import { trackFocus, addDisposableListener, EventType, onDidRegisterWindow } from 'vs/base/browser/dom';
|
||||
import { preferredSideBySideGroupDirection, GroupDirection, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
@@ -44,6 +44,7 @@ export class WorkbenchContextKeysHandler extends Disposable {
|
||||
private activeEditorGroupLast: IContextKey<boolean>;
|
||||
private activeEditorGroupLocked: IContextKey<boolean>;
|
||||
private multipleEditorGroupsContext: IContextKey<boolean>;
|
||||
private maximizedEditorGroupContext: IContextKey<boolean>;
|
||||
|
||||
private editorsVisibleContext: IContextKey<boolean>;
|
||||
|
||||
@@ -136,6 +137,7 @@ export class WorkbenchContextKeysHandler extends Disposable {
|
||||
this.activeEditorGroupLast = ActiveEditorGroupLastContext.bindTo(this.contextKeyService);
|
||||
this.activeEditorGroupLocked = ActiveEditorGroupLockedContext.bindTo(this.contextKeyService);
|
||||
this.multipleEditorGroupsContext = MultipleEditorGroupsContext.bindTo(this.contextKeyService);
|
||||
this.maximizedEditorGroupContext = MaximizedEditorGroupContext.bindTo(this.contextKeyService);
|
||||
|
||||
// Working Copies
|
||||
this.dirtyWorkingCopiesContext = DirtyWorkingCopiesContext.bindTo(this.contextKeyService);
|
||||
@@ -233,6 +235,8 @@ export class WorkbenchContextKeysHandler extends Disposable {
|
||||
this._register(this.editorGroupService.onDidChangeActiveGroup(() => this.updateEditorGroupContextKeys()));
|
||||
this._register(this.editorGroupService.onDidChangeGroupLocked(() => this.updateEditorGroupContextKeys()));
|
||||
|
||||
this._register(this.editorGroupService.onDidChangeGroupMaximized((maximized) => this.maximizedEditorGroupContext.set(maximized)));
|
||||
|
||||
this._register(this.editorGroupService.onDidChangeEditorPartOptions(() => this.updateEditorAreaContextKeys()));
|
||||
|
||||
this._register(Event.runAndSubscribe(onDidRegisterWindow, ({ window, disposableStore }) => disposableStore.add(addDisposableListener(window, EventType.FOCUS_IN, () => this.updateInputContextKeys(window.document), true)), { window, disposableStore: this._store }));
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
}
|
||||
|
||||
.monaco-workbench .part > .content > .monaco-progress-container,
|
||||
.monaco-workbench .part.editor > .content .monaco-progress-container {
|
||||
.monaco-workbench .part.editor > .content .editor-group-container > .monaco-progress-container {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 33px; /* at the bottom of the 35px height title container */
|
||||
@@ -96,10 +96,6 @@
|
||||
}
|
||||
|
||||
.monaco-workbench .part > .content > .monaco-progress-container .progress-bit,
|
||||
.monaco-workbench
|
||||
.part.editor
|
||||
> .content
|
||||
.monaco-progress-container
|
||||
.progress-bit {
|
||||
.monaco-workbench .part.editor > .content .editor-group-container > .monaco-progress-container .progress-bit {
|
||||
height: 2px;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import { IEditorFactoryRegistry, EditorExtensions } from 'vs/workbench/common/ed
|
||||
import {
|
||||
TextCompareEditorActiveContext, ActiveEditorPinnedContext, EditorGroupEditorsCountContext, ActiveEditorStickyContext, ActiveEditorAvailableEditorIdsContext,
|
||||
MultipleEditorGroupsContext, ActiveEditorDirtyContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext,
|
||||
EditorTabsVisibleContext, ActiveEditorLastInGroupContext, EditorPinnedAndUnpinnedTabsContext
|
||||
EditorTabsVisibleContext, ActiveEditorLastInGroupContext, MaximizedEditorGroupContext
|
||||
} from 'vs/workbench/common/contextkeys';
|
||||
import { SideBySideEditorInput, SideBySideEditorInputSerializer } from 'vs/workbench/common/editor/sideBySideEditorInput';
|
||||
import { TextResourceEditor } from 'vs/workbench/browser/parts/editor/textResourceEditor';
|
||||
@@ -40,13 +40,14 @@ import {
|
||||
QuickAccessPreviousRecentlyUsedEditorAction, OpenPreviousRecentlyUsedEditorInGroupAction, OpenNextRecentlyUsedEditorInGroupAction, QuickAccessLeastRecentlyUsedEditorAction, QuickAccessLeastRecentlyUsedEditorInGroupAction,
|
||||
ReOpenInTextEditorAction, DuplicateGroupDownAction, DuplicateGroupLeftAction, DuplicateGroupRightAction, DuplicateGroupUpAction, ToggleEditorTypeAction, SplitEditorToAboveGroupAction, SplitEditorToBelowGroupAction,
|
||||
SplitEditorToFirstGroupAction, SplitEditorToLastGroupAction, SplitEditorToLeftGroupAction, SplitEditorToNextGroupAction, SplitEditorToPreviousGroupAction, SplitEditorToRightGroupAction, NavigateForwardInEditsAction,
|
||||
NavigateBackwardsInEditsAction, NavigateForwardInNavigationsAction, NavigateBackwardsInNavigationsAction, NavigatePreviousInNavigationsAction, NavigatePreviousInEditsAction, NavigateToLastNavigationLocationAction, ExperimentalMoveEditorIntoNewWindowAction
|
||||
NavigateBackwardsInEditsAction, NavigateForwardInNavigationsAction, NavigateBackwardsInNavigationsAction, NavigatePreviousInNavigationsAction, NavigatePreviousInEditsAction, NavigateToLastNavigationLocationAction,
|
||||
MaximizeGroupHideSidebarAction, UnmaximizeEditorGroupAction, ExperimentalMoveEditorIntoNewWindowAction
|
||||
} from 'vs/workbench/browser/parts/editor/editorActions';
|
||||
import {
|
||||
CLOSE_EDITORS_AND_GROUP_COMMAND_ID, CLOSE_EDITORS_IN_GROUP_COMMAND_ID, CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID, CLOSE_EDITOR_COMMAND_ID, CLOSE_EDITOR_GROUP_COMMAND_ID, CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID,
|
||||
CLOSE_PINNED_EDITOR_COMMAND_ID, CLOSE_SAVED_EDITORS_COMMAND_ID, GOTO_NEXT_CHANGE, GOTO_PREVIOUS_CHANGE, KEEP_EDITOR_COMMAND_ID, PIN_EDITOR_COMMAND_ID, SHOW_EDITORS_IN_GROUP, SPLIT_EDITOR_DOWN, SPLIT_EDITOR_LEFT,
|
||||
SPLIT_EDITOR_RIGHT, SPLIT_EDITOR_UP, TOGGLE_DIFF_IGNORE_TRIM_WHITESPACE, TOGGLE_DIFF_SIDE_BY_SIDE, TOGGLE_KEEP_EDITORS_COMMAND_ID, UNPIN_EDITOR_COMMAND_ID, setup as registerEditorCommands, REOPEN_WITH_COMMAND_ID,
|
||||
TOGGLE_LOCK_GROUP_COMMAND_ID, UNLOCK_GROUP_COMMAND_ID, SPLIT_EDITOR_IN_GROUP, JOIN_EDITOR_IN_GROUP, FOCUS_FIRST_SIDE_EDITOR, FOCUS_SECOND_SIDE_EDITOR, TOGGLE_SPLIT_EDITOR_IN_GROUP_LAYOUT, SPLIT_EDITOR
|
||||
TOGGLE_LOCK_GROUP_COMMAND_ID, UNLOCK_GROUP_COMMAND_ID, SPLIT_EDITOR_IN_GROUP, JOIN_EDITOR_IN_GROUP, FOCUS_FIRST_SIDE_EDITOR, FOCUS_SECOND_SIDE_EDITOR, TOGGLE_SPLIT_EDITOR_IN_GROUP_LAYOUT, SPLIT_EDITOR, MAXIMIZE_EDITOR_GROUP, UNMAXIMIZE_EDITOR_GROUP
|
||||
} from 'vs/workbench/browser/parts/editor/editorCommands';
|
||||
import { inQuickPickContext, getQuickNavigateHandler } from 'vs/workbench/browser/quickaccess';
|
||||
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
@@ -64,7 +65,7 @@ import { Codicon } from 'vs/base/common/codicons';
|
||||
import { registerIcon } from 'vs/platform/theme/common/iconRegistry';
|
||||
import { UntitledTextEditorInputSerializer, UntitledTextEditorWorkingCopyEditorHandler } from 'vs/workbench/services/untitled/common/untitledTextEditorHandler';
|
||||
import { DynamicEditorConfigurations } from 'vs/workbench/browser/parts/editor/editorConfiguration';
|
||||
import { HideEditorTabsAction, ShowMultipleEditorTabsAction, ShowSingleEditorTabAction, ToggleSeparatePinnedTabsAction } from 'vs/workbench/browser/actions/layoutActions';
|
||||
import { HideEditorTabsAction, ShowMultipleEditorTabsAction, ShowSingleEditorTabAction } from 'vs/workbench/browser/actions/layoutActions';
|
||||
import product from 'vs/platform/product/common/product';
|
||||
import { ICommandAction } from 'vs/platform/action/common/action';
|
||||
|
||||
@@ -214,6 +215,8 @@ registerAction2(NavigateBetweenGroupsAction);
|
||||
registerAction2(ResetGroupSizesAction);
|
||||
registerAction2(ToggleGroupSizesAction);
|
||||
registerAction2(MaximizeGroupAction);
|
||||
registerAction2(UnmaximizeEditorGroupAction);
|
||||
registerAction2(MaximizeGroupHideSidebarAction);
|
||||
registerAction2(MinimizeOtherGroupsAction);
|
||||
|
||||
registerAction2(MoveEditorLeftInGroupAction);
|
||||
@@ -354,11 +357,10 @@ MenuRegistry.appendMenuItem(MenuId.EditorTabsBarContext, { command: { id: SPLIT_
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTabsBarContext, { command: { id: SPLIT_EDITOR_DOWN, title: localize('splitDown', "Split Down") }, group: '2_split', order: 20 });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTabsBarContext, { command: { id: SPLIT_EDITOR_LEFT, title: localize('splitLeft', "Split Left") }, group: '2_split', order: 30 });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTabsBarContext, { command: { id: SPLIT_EDITOR_RIGHT, title: localize('splitRight', "Split Right") }, group: '2_split', order: 40 });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTabsBarContext, { submenu: MenuId.EditorTabsBarShowTabsSubmenu, title: localize('showTabs', "Show Tabs"), group: '3_config', order: 10 });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTabsBarContext, { submenu: MenuId.EditorTabsBarShowTabsSubmenu, title: localize('tabBar', "Tab bar"), group: '3_config', order: 10 });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTabsBarShowTabsSubmenu, { command: { id: ShowMultipleEditorTabsAction.ID, title: localize('multipleTabs', "Multiple Tabs"), toggled: ContextKeyExpr.equals('config.workbench.editor.showTabs', 'multiple') }, group: '1_config', order: 10 });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTabsBarShowTabsSubmenu, { command: { id: ShowSingleEditorTabAction.ID, title: localize('singleTab', "Single Tab"), toggled: ContextKeyExpr.equals('config.workbench.editor.showTabs', 'single') }, group: '1_config', order: 20 });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTabsBarShowTabsSubmenu, { command: { id: HideEditorTabsAction.ID, title: localize('hideTabBar', "Hide Tab Bar"), toggled: ContextKeyExpr.equals('config.workbench.editor.showTabs', 'none') }, group: '1_config', order: 30 });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTabsBarContext, { command: { id: ToggleSeparatePinnedTabsAction.ID, title: localize('toggleSeparatePinnedEditorTabs', "Separate Pinned Editor Tabs"), toggled: ContextKeyExpr.has('config.workbench.editor.pinnedTabsOnSeparateRow') }, when: EditorPinnedAndUnpinnedTabsContext, group: '3_config', order: 20 });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTabsBarShowTabsSubmenu, { command: { id: HideEditorTabsAction.ID, title: localize('hideTabBar', "Hide"), toggled: ContextKeyExpr.equals('config.workbench.editor.showTabs', 'none') }, group: '1_config', order: 30 });
|
||||
|
||||
// Editor Title Context Menu
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: CLOSE_EDITOR_COMMAND_ID, title: localize('close', "Close") }, group: '1_close', order: 10 });
|
||||
@@ -383,7 +385,9 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: SHOW_EDITORS_IN
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: localize('closeAll', "Close All") }, group: '5_close', order: 10 });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: CLOSE_SAVED_EDITORS_COMMAND_ID, title: localize('closeAllSaved', "Close Saved") }, group: '5_close', order: 20 });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: TOGGLE_KEEP_EDITORS_COMMAND_ID, title: localize('togglePreviewMode', "Enable Preview Editors"), toggled: ContextKeyExpr.has('config.workbench.editor.enablePreview') }, group: '7_settings', order: 10 });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: TOGGLE_LOCK_GROUP_COMMAND_ID, title: localize('lockGroup', "Lock Group"), toggled: ActiveEditorGroupLockedContext }, group: '8_lock', order: 10, when: MultipleEditorGroupsContext });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: MAXIMIZE_EDITOR_GROUP, title: localize('maximizeGroup', "Maximize Group") }, group: '8_group_operations', order: 5, when: ContextKeyExpr.and(MaximizedEditorGroupContext.negate(), MultipleEditorGroupsContext) });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: UNMAXIMIZE_EDITOR_GROUP, title: localize('unmaximizeGroup', "Unmaximize Group") }, group: '8_group_operations', order: 5, when: MaximizedEditorGroupContext });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: TOGGLE_LOCK_GROUP_COMMAND_ID, title: localize('lockGroup', "Lock Group"), toggled: ActiveEditorGroupLockedContext }, group: '8_group_operations', order: 10, when: MultipleEditorGroupsContext });
|
||||
|
||||
function appendEditorToolItem(primary: ICommandAction, when: ContextKeyExpression | undefined, order: number, alternative?: ICommandAction, precondition?: ContextKeyExpression | undefined): void {
|
||||
const item: IMenuItem = {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { GroupIdentifier, IWorkbenchEditorConfiguration, IEditorIdentifier, IEditorCloseEvent, IEditorPartOptions, IEditorPartOptionsChangeEvent, SideBySideEditor, EditorCloseContext, IEditorPane } from 'vs/workbench/common/editor';
|
||||
import { GroupIdentifier, IWorkbenchEditorConfiguration, IEditorIdentifier, IEditorCloseEvent, IEditorPartOptions, IEditorPartOptionsChangeEvent, SideBySideEditor, EditorCloseContext, IEditorPane, IEditorPartLimitConfiguration, IEditorPartDecorationsConfiguration } from 'vs/workbench/common/editor';
|
||||
import { EditorInput } from 'vs/workbench/common/editor/editorInput';
|
||||
import { IEditorGroup, GroupDirection, IMergeGroupOptions, GroupsOrder, GroupsArrangement } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
@@ -13,9 +13,10 @@ import { IConfigurationChangeEvent, IConfigurationService } from 'vs/platform/co
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { ISerializableView } from 'vs/base/browser/ui/grid/grid';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { isObject } from 'vs/base/common/types';
|
||||
import { OptionalBooleanKey, OptionalNumberKey, OptionalStringKey, isObject } from 'vs/base/common/types';
|
||||
import { IEditorOptions } from 'vs/platform/editor/common/editor';
|
||||
import { IWindowsConfiguration } from 'vs/platform/window/common/window';
|
||||
import { ensureOptionalBooleanValue, ensureOptionalNumberValue, ensureOptionalStringValue } from 'vs/base/common/objects';
|
||||
|
||||
export interface IEditorPartCreationOptions {
|
||||
readonly restorePreviousState: boolean;
|
||||
@@ -47,7 +48,7 @@ export const DEFAULT_EDITOR_PART_OPTIONS: IEditorPartOptions = {
|
||||
splitSizing: 'auto',
|
||||
splitOnDragAndDrop: true,
|
||||
centeredLayoutFixedWidth: false,
|
||||
doubleClickTabToToggleEditorGroupSizes: true,
|
||||
doubleClickTabToToggleEditorGroupSizes: 'expand',
|
||||
};
|
||||
|
||||
export function impactsEditorPartOptions(event: IConfigurationChangeEvent): boolean {
|
||||
@@ -90,13 +91,102 @@ export function getEditorPartOptions(configurationService: IConfigurationService
|
||||
return options;
|
||||
}
|
||||
|
||||
function validateEditorPartOptions(options: IEditorPartOptions) {
|
||||
// showTabs ensure correct enum value
|
||||
function validateEditorPartOptions(options: IEditorPartOptions): void {
|
||||
|
||||
// Migrate: Show tabs (config migration kicks in very late and can cause flicker otherwise)
|
||||
if (typeof options.showTabs === 'boolean') {
|
||||
// Migration service kicks in very late and can cause a flicker otherwise
|
||||
options.showTabs = options.showTabs ? 'multiple' : 'single';
|
||||
} else if (options.showTabs !== 'multiple' && options.showTabs !== 'single' && options.showTabs !== 'none') {
|
||||
options.showTabs = 'multiple';
|
||||
}
|
||||
|
||||
// Boolean options
|
||||
const booleanOptions: Array<OptionalBooleanKey<IEditorPartOptions>> = [
|
||||
'wrapTabs',
|
||||
'scrollToSwitchTabs',
|
||||
'highlightModifiedTabs',
|
||||
'pinnedTabsOnSeparateRow',
|
||||
'focusRecentEditorAfterClose',
|
||||
'showIcons',
|
||||
'enablePreview',
|
||||
'enablePreviewFromQuickOpen',
|
||||
'enablePreviewFromCodeNavigation',
|
||||
'closeOnFileDelete',
|
||||
'closeEmptyGroups',
|
||||
'revealIfOpen',
|
||||
'mouseBackForwardToNavigate',
|
||||
'restoreViewState',
|
||||
'splitOnDragAndDrop',
|
||||
'centeredLayoutFixedWidth',
|
||||
];
|
||||
for (const option of booleanOptions) {
|
||||
if (typeof option === 'string') {
|
||||
ensureOptionalBooleanValue(options, option, Boolean(DEFAULT_EDITOR_PART_OPTIONS[option]));
|
||||
}
|
||||
}
|
||||
|
||||
// Number options
|
||||
const numberOptions: Array<OptionalNumberKey<IEditorPartOptions>> = [
|
||||
'tabSizingFixedMinWidth',
|
||||
'tabSizingFixedMaxWidth'
|
||||
];
|
||||
for (const option of numberOptions) {
|
||||
if (typeof option === 'string') {
|
||||
ensureOptionalNumberValue(options, option, Number(DEFAULT_EDITOR_PART_OPTIONS[option]));
|
||||
}
|
||||
}
|
||||
|
||||
// String options
|
||||
const stringOptions: Array<[OptionalStringKey<IEditorPartOptions>, Array<string>]> = [
|
||||
['showTabs', ['multiple', 'single', 'none']],
|
||||
['tabCloseButton', ['left', 'right', 'off']],
|
||||
['tabSizing', ['fit', 'shrink', 'fixed']],
|
||||
['pinnedTabSizing', ['normal', 'compact', 'shrink']],
|
||||
['tabHeight', ['default', 'compact']],
|
||||
['preventPinnedEditorClose', ['keyboardAndMouse', 'keyboard', 'mouse', 'never']],
|
||||
['titleScrollbarSizing', ['default', 'large']],
|
||||
['openPositioning', ['left', 'right', 'first', 'last']],
|
||||
['openSideBySideDirection', ['right', 'down']],
|
||||
['labelFormat', ['default', 'short', 'medium', 'long']],
|
||||
['splitInGroupLayout', ['vertical', 'horizontal']],
|
||||
['splitSizing', ['distribute', 'split', 'auto']],
|
||||
['doubleClickTabToToggleEditorGroupSizes', ['maximize', 'expand', 'off']]
|
||||
];
|
||||
for (const [option, allowed] of stringOptions) {
|
||||
if (typeof option === 'string') {
|
||||
ensureOptionalStringValue(options, option, allowed, String(DEFAULT_EDITOR_PART_OPTIONS[option]));
|
||||
}
|
||||
}
|
||||
|
||||
// Complex options
|
||||
if (options.autoLockGroups && !(options.autoLockGroups instanceof Set)) {
|
||||
options.autoLockGroups = undefined;
|
||||
}
|
||||
if (options.limit && !isObject(options.limit)) {
|
||||
options.limit = undefined;
|
||||
} else if (options.limit) {
|
||||
const booleanLimitOptions: Array<OptionalBooleanKey<IEditorPartLimitConfiguration>> = [
|
||||
'enabled',
|
||||
'excludeDirty',
|
||||
'perEditorGroup'
|
||||
];
|
||||
for (const option of booleanLimitOptions) {
|
||||
if (typeof option === 'string') {
|
||||
ensureOptionalBooleanValue(options.limit, option, undefined);
|
||||
}
|
||||
}
|
||||
ensureOptionalNumberValue(options.limit, 'value', undefined);
|
||||
}
|
||||
if (options.decorations && !isObject(options.decorations)) {
|
||||
options.decorations = undefined;
|
||||
} else if (options.decorations) {
|
||||
const booleanDecorationOptions: Array<OptionalBooleanKey<IEditorPartDecorationsConfiguration>> = [
|
||||
'badges',
|
||||
'colors'
|
||||
];
|
||||
for (const option of booleanDecorationOptions) {
|
||||
if (typeof option === 'string') {
|
||||
ensureOptionalBooleanValue(options.decorations, option, undefined);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,6 +235,8 @@ export interface IEditorGroupsView {
|
||||
removeGroup(group: IEditorGroupView | GroupIdentifier): void;
|
||||
|
||||
arrangeGroups(arrangement: GroupsArrangement, target?: IEditorGroupView | GroupIdentifier): void;
|
||||
toggleMaximizeGroup(group?: IEditorGroupView | GroupIdentifier): void;
|
||||
toggleExpandGroup(group?: IEditorGroupView | GroupIdentifier): void;
|
||||
}
|
||||
|
||||
export interface IEditorGroupTitleHeight {
|
||||
|
||||
@@ -13,7 +13,7 @@ import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/bro
|
||||
import { GoFilter, IHistoryService } from 'vs/workbench/services/history/common/history';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { CLOSE_EDITOR_COMMAND_ID, MOVE_ACTIVE_EDITOR_COMMAND_ID, ActiveEditorMoveCopyArguments, SPLIT_EDITOR_LEFT, SPLIT_EDITOR_RIGHT, SPLIT_EDITOR_UP, SPLIT_EDITOR_DOWN, splitEditor, LAYOUT_EDITOR_GROUPS_COMMAND_ID, UNPIN_EDITOR_COMMAND_ID, COPY_ACTIVE_EDITOR_COMMAND_ID, SPLIT_EDITOR } from 'vs/workbench/browser/parts/editor/editorCommands';
|
||||
import { CLOSE_EDITOR_COMMAND_ID, MOVE_ACTIVE_EDITOR_COMMAND_ID, ActiveEditorMoveCopyArguments, SPLIT_EDITOR_LEFT, SPLIT_EDITOR_RIGHT, SPLIT_EDITOR_UP, SPLIT_EDITOR_DOWN, splitEditor, LAYOUT_EDITOR_GROUPS_COMMAND_ID, UNPIN_EDITOR_COMMAND_ID, COPY_ACTIVE_EDITOR_COMMAND_ID, SPLIT_EDITOR, UNMAXIMIZE_EDITOR_GROUP, MAXIMIZE_EDITOR_GROUP, resolveCommandsContext, getCommandsContext } from 'vs/workbench/browser/parts/editor/editorCommands';
|
||||
import { IEditorGroupsService, IEditorGroup, GroupsArrangement, GroupLocation, GroupDirection, preferredSideBySideGroupDirection, IFindGroupScope, GroupOrientation, EditorGroupLayout, GroupsOrder } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
@@ -33,7 +33,8 @@ import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { Categories } from 'vs/platform/action/common/actionCommonCategories';
|
||||
import { ActiveEditorAvailableEditorIdsContext, ActiveEditorContext, ActiveEditorGroupEmptyContext } from 'vs/workbench/common/contextkeys';
|
||||
import { ActiveEditorAvailableEditorIdsContext, ActiveEditorContext, ActiveEditorGroupEmptyContext, MaximizedEditorGroupContext, MultipleEditorGroupsContext } from 'vs/workbench/common/contextkeys';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { getActiveDocument } from 'vs/base/browser/dom';
|
||||
|
||||
class ExecuteCommandAction extends Action2 {
|
||||
@@ -1014,7 +1015,7 @@ export class MinimizeOtherGroupsAction extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.minimizeOtherEditors',
|
||||
title: { value: localize('minimizeOtherEditorGroups', "Maximize Editor Group"), original: 'Maximize Editor Group' },
|
||||
title: { value: localize('minimizeOtherEditorGroups', "Expand Editor Group"), original: 'Expand Editor Group' },
|
||||
f1: true,
|
||||
category: Categories.View
|
||||
});
|
||||
@@ -1023,7 +1024,7 @@ export class MinimizeOtherGroupsAction extends Action2 {
|
||||
override async run(accessor: ServicesAccessor): Promise<void> {
|
||||
const editorGroupService = accessor.get(IEditorGroupsService);
|
||||
|
||||
editorGroupService.arrangeGroups(GroupsArrangement.MAXIMIZE);
|
||||
editorGroupService.arrangeGroups(GroupsArrangement.EXPAND);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1059,7 +1060,7 @@ export class ToggleGroupSizesAction extends Action2 {
|
||||
override async run(accessor: ServicesAccessor): Promise<void> {
|
||||
const editorGroupService = accessor.get(IEditorGroupsService);
|
||||
|
||||
editorGroupService.arrangeGroups(GroupsArrangement.TOGGLE);
|
||||
editorGroupService.toggleExpandGroup();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1067,10 +1068,34 @@ export class MaximizeGroupAction extends Action2 {
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.maximizeEditor',
|
||||
title: { value: localize('maximizeEditor', "Maximize Editor Group and Hide Side Bars"), original: 'Maximize Editor Group and Hide Side Bars' },
|
||||
id: MAXIMIZE_EDITOR_GROUP,
|
||||
title: { value: localize('maximizeEditor', "Maximize Editor Group"), original: 'Maximize Editor Group' },
|
||||
f1: true,
|
||||
category: Categories.View
|
||||
category: Categories.View,
|
||||
precondition: ContextKeyExpr.and(MaximizedEditorGroupContext.negate(), MultipleEditorGroupsContext),
|
||||
keybinding: {
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyCode.KeyM),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
override async run(accessor: ServicesAccessor, resourceOrContext?: URI | IEditorCommandsContext, context?: IEditorCommandsContext): Promise<void> {
|
||||
const editorsGroupService = accessor.get(IEditorGroupsService);
|
||||
const { group } = resolveCommandsContext(editorsGroupService, getCommandsContext(resourceOrContext, context));
|
||||
editorsGroupService.arrangeGroups(GroupsArrangement.MAXIMIZE, group);
|
||||
}
|
||||
}
|
||||
|
||||
export class MaximizeGroupHideSidebarAction extends Action2 {
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.maximizeEditorHideSidebar',
|
||||
title: { value: localize('maximizeEditorHideSidebar', "Maximize Editor Group and Hide Side Bars"), original: 'Maximize Editor Group and Hide Side Bars' },
|
||||
f1: true,
|
||||
category: Categories.View,
|
||||
precondition: ContextKeyExpr.and(MaximizedEditorGroupContext.negate(), MultipleEditorGroupsContext)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1087,6 +1112,36 @@ export class MaximizeGroupAction extends Action2 {
|
||||
}
|
||||
}
|
||||
|
||||
export class UnmaximizeEditorGroupAction extends Action2 {
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: UNMAXIMIZE_EDITOR_GROUP,
|
||||
title: { value: localize('UnmaximizeEditorGroup', "Unmaximize Editor Group"), original: 'Unmaximize Editor Group' },
|
||||
f1: true,
|
||||
category: Categories.View,
|
||||
precondition: MaximizedEditorGroupContext,
|
||||
keybinding: {
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyCode.KeyM),
|
||||
},
|
||||
menu: {
|
||||
id: MenuId.EditorTitle,
|
||||
order: -10000, // towards the front
|
||||
group: 'navigation',
|
||||
when: MaximizedEditorGroupContext
|
||||
},
|
||||
icon: Codicon.screenFull,
|
||||
toggled: MaximizedEditorGroupContext,
|
||||
});
|
||||
}
|
||||
|
||||
override async run(accessor: ServicesAccessor): Promise<void> {
|
||||
const editorGroupService = accessor.get(IEditorGroupsService);
|
||||
editorGroupService.toggleMaximizeGroup();
|
||||
}
|
||||
}
|
||||
|
||||
abstract class AbstractNavigateEditorAction extends Action2 {
|
||||
|
||||
override async run(accessor: ServicesAccessor): Promise<void> {
|
||||
|
||||
@@ -80,6 +80,9 @@ export const SPLIT_EDITOR_DOWN = 'workbench.action.splitEditorDown';
|
||||
export const SPLIT_EDITOR_LEFT = 'workbench.action.splitEditorLeft';
|
||||
export const SPLIT_EDITOR_RIGHT = 'workbench.action.splitEditorRight';
|
||||
|
||||
export const MAXIMIZE_EDITOR_GROUP = 'workbench.action.maximizeEditorGroup';
|
||||
export const UNMAXIMIZE_EDITOR_GROUP = 'workbench.action.unmaximizeEditorGroup';
|
||||
|
||||
export const SPLIT_EDITOR_IN_GROUP = 'workbench.action.splitEditorInGroup';
|
||||
export const TOGGLE_SPLIT_EDITOR_IN_GROUP = 'workbench.action.toggleSplitEditorInGroup';
|
||||
export const JOIN_EDITOR_IN_GROUP = 'workbench.action.joinEditorInGroup';
|
||||
@@ -104,7 +107,8 @@ export const EDITOR_CORE_NAVIGATION_COMMANDS = [
|
||||
SPLIT_EDITOR,
|
||||
CLOSE_EDITOR_COMMAND_ID,
|
||||
UNPIN_EDITOR_COMMAND_ID,
|
||||
UNLOCK_GROUP_COMMAND_ID
|
||||
UNLOCK_GROUP_COMMAND_ID,
|
||||
UNMAXIMIZE_EDITOR_GROUP
|
||||
];
|
||||
|
||||
export interface ActiveEditorMoveCopyArguments {
|
||||
@@ -1464,7 +1468,7 @@ function getEditorsContext(accessor: ServicesAccessor, resourceOrContext?: URI |
|
||||
};
|
||||
}
|
||||
|
||||
function getCommandsContext(resourceOrContext?: URI | IEditorCommandsContext, context?: IEditorCommandsContext): IEditorCommandsContext | undefined {
|
||||
export function getCommandsContext(resourceOrContext?: URI | IEditorCommandsContext, context?: IEditorCommandsContext): IEditorCommandsContext | undefined {
|
||||
if (URI.isUri(resourceOrContext)) {
|
||||
return context;
|
||||
}
|
||||
@@ -1480,7 +1484,7 @@ function getCommandsContext(resourceOrContext?: URI | IEditorCommandsContext, co
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function resolveCommandsContext(editorGroupService: IEditorGroupsService, context?: IEditorCommandsContext): { group: IEditorGroup; editor?: EditorInput } {
|
||||
export function resolveCommandsContext(editorGroupService: IEditorGroupsService, context?: IEditorCommandsContext): { group: IEditorGroup; editor?: EditorInput } {
|
||||
|
||||
// Resolve from context
|
||||
let group = context && typeof context.groupId === 'number' ? editorGroupService.getGroup(context.groupId) : undefined;
|
||||
|
||||
@@ -700,6 +700,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
// Title control switch between singleEditorTabs, multiEditorTabs and multiRowEditorTabs
|
||||
if (
|
||||
event.oldPartOptions.showTabs !== event.newPartOptions.showTabs ||
|
||||
event.oldPartOptions.tabHeight !== event.newPartOptions.tabHeight ||
|
||||
(event.oldPartOptions.showTabs === 'multiple' && event.oldPartOptions.pinnedTabsOnSeparateRow !== event.newPartOptions.pinnedTabsOnSeparateRow)
|
||||
) {
|
||||
|
||||
@@ -1930,6 +1931,8 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
available: new Dimension(width, height - this.editorPane.minimumHeight)
|
||||
});
|
||||
|
||||
this.element.style.setProperty('--editor-group-tabs-height', `${this.titleHeight.offset}px`);
|
||||
|
||||
// Pass the container width and remaining height to the editor layout
|
||||
const editorHeight = Math.max(0, height - titleControlSize.height);
|
||||
this.editorContainer.style.height = `${editorHeight}px`;
|
||||
|
||||
@@ -101,6 +101,9 @@ export class EditorPart extends Part implements IEditorPart {
|
||||
private readonly _onDidChangeGroupLocked = this._register(new Emitter<IEditorGroupView>());
|
||||
readonly onDidChangeGroupLocked = this._onDidChangeGroupLocked.event;
|
||||
|
||||
private readonly _onDidChangeGroupMaximized = this._register(new Emitter<boolean>());
|
||||
readonly onDidChangeGroupMaximized = this._onDidChangeGroupMaximized.event;
|
||||
|
||||
private readonly _onDidActivateGroup = this._register(new Emitter<IEditorGroupView>());
|
||||
readonly onDidActivateGroup = this._onDidActivateGroup.event;
|
||||
|
||||
@@ -137,6 +140,7 @@ export class EditorPart extends Part implements IEditorPart {
|
||||
private centeredLayoutWidget!: CenteredViewLayout;
|
||||
|
||||
private gridWidget!: SerializableGrid<IEditorGroupView>;
|
||||
private readonly gridWidgetDisposables = this._register(new DisposableStore());
|
||||
private readonly gridWidgetView = this._register(new GridWidgetView<IEditorGroupView>());
|
||||
|
||||
constructor(
|
||||
@@ -147,7 +151,7 @@ export class EditorPart extends Part implements IEditorPart {
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IStorageService storageService: IStorageService,
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
|
||||
) {
|
||||
super(id, { hasTitle: false }, themeService, storageService, layoutService);
|
||||
|
||||
@@ -369,21 +373,48 @@ export class EditorPart extends Part implements IEditorPart {
|
||||
this.gridWidget.distributeViewSizes();
|
||||
break;
|
||||
case GroupsArrangement.MAXIMIZE:
|
||||
this.gridWidget.maximizeViewSize(target);
|
||||
break;
|
||||
case GroupsArrangement.TOGGLE:
|
||||
if (this.isGroupMaximized(target)) {
|
||||
this.arrangeGroups(GroupsArrangement.EVEN);
|
||||
} else {
|
||||
this.arrangeGroups(GroupsArrangement.MAXIMIZE);
|
||||
if (this.groups.length < 2) {
|
||||
return; // need at least 2 groups to be maximized
|
||||
}
|
||||
|
||||
this.gridWidget.maximizeView(target);
|
||||
this.doSetGroupActive(target);
|
||||
break;
|
||||
case GroupsArrangement.EXPAND:
|
||||
this.gridWidget.expandView(target);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
isGroupMaximized(targetGroup: IEditorGroupView): boolean {
|
||||
return this.gridWidget.isViewSizeMaximized(targetGroup);
|
||||
toggleMaximizeGroup(target: IEditorGroupView = this.activeGroup): void {
|
||||
if (this.hasMaximizedGroup()) {
|
||||
this.unmaximizeGroup();
|
||||
} else {
|
||||
this.arrangeGroups(GroupsArrangement.MAXIMIZE, target);
|
||||
}
|
||||
}
|
||||
|
||||
toggleExpandGroup(target: IEditorGroupView = this.activeGroup): void {
|
||||
if (this.isGroupExpanded(this.activeGroup)) {
|
||||
this.arrangeGroups(GroupsArrangement.EVEN);
|
||||
} else {
|
||||
this.arrangeGroups(GroupsArrangement.EXPAND, target);
|
||||
}
|
||||
}
|
||||
|
||||
private unmaximizeGroup(): void {
|
||||
this.gridWidget.exitMaximizedView();
|
||||
}
|
||||
|
||||
private hasMaximizedGroup(): boolean {
|
||||
return this.gridWidget.hasMaximizedView();
|
||||
}
|
||||
|
||||
private isGroupMaximized(targetGroup: IEditorGroupView): boolean {
|
||||
return this.gridWidget.isViewMaximized(targetGroup);
|
||||
}
|
||||
|
||||
isGroupExpanded(targetGroup: IEditorGroupView): boolean {
|
||||
return this.gridWidget.isViewExpanded(targetGroup);
|
||||
}
|
||||
|
||||
setGroupOrientation(orientation: GroupOrientation): void {
|
||||
@@ -524,7 +555,7 @@ export class EditorPart extends Part implements IEditorPart {
|
||||
if (locationView.groupsView === this) {
|
||||
const restoreFocus = this.shouldRestoreFocus(locationView.element);
|
||||
|
||||
const shouldMaximize = this.groupViews.size > 1 && this.isGroupMaximized(locationView);
|
||||
const shouldExpand = this.groupViews.size > 1 && this.isGroupExpanded(locationView);
|
||||
newGroupView = this.doCreateGroupView(groupToCopy);
|
||||
|
||||
// Add to grid widget
|
||||
@@ -544,9 +575,9 @@ export class EditorPart extends Part implements IEditorPart {
|
||||
// Notify group index change given a new group was added
|
||||
this.notifyGroupIndexChange();
|
||||
|
||||
// Maximize new group, if the reference view was previously maximized
|
||||
if (shouldMaximize) {
|
||||
this.arrangeGroups(GroupsArrangement.MAXIMIZE, newGroupView);
|
||||
// Expand new group, if the reference view was previously expanded
|
||||
if (shouldExpand) {
|
||||
this.arrangeGroups(GroupsArrangement.EXPAND, newGroupView);
|
||||
}
|
||||
|
||||
// Restore focus if we had it previously after completing the grid
|
||||
@@ -642,7 +673,7 @@ export class EditorPart extends Part implements IEditorPart {
|
||||
// Mark group as new active
|
||||
group.setActive(true);
|
||||
|
||||
// Maximize the group if it is currently minimized
|
||||
// Expand the group if it is currently minimized
|
||||
this.doRestoreGroup(group);
|
||||
|
||||
// Event
|
||||
@@ -657,9 +688,13 @@ export class EditorPart extends Part implements IEditorPart {
|
||||
|
||||
private doRestoreGroup(group: IEditorGroupView): void {
|
||||
if (this.gridWidget) {
|
||||
if (this.hasMaximizedGroup() && !this.isGroupMaximized(group)) {
|
||||
this.unmaximizeGroup();
|
||||
}
|
||||
|
||||
const viewSize = this.gridWidget.getViewSize(group);
|
||||
if (viewSize.width === group.minimumWidth || viewSize.height === group.minimumHeight) {
|
||||
this.arrangeGroups(GroupsArrangement.MAXIMIZE, group);
|
||||
this.arrangeGroups(GroupsArrangement.EXPAND, group);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1032,6 +1067,10 @@ export class EditorPart extends Part implements IEditorPart {
|
||||
}
|
||||
|
||||
centerLayout(active: boolean): void {
|
||||
if (this.hasMaximizedGroup()) {
|
||||
this.unmaximizeGroup();
|
||||
}
|
||||
|
||||
this.centeredLayoutWidget.activate(active);
|
||||
|
||||
this._activeGroup.focus();
|
||||
@@ -1158,6 +1197,10 @@ export class EditorPart extends Part implements IEditorPart {
|
||||
|
||||
this._onDidChangeSizeConstraints.input = gridWidget.onDidChange;
|
||||
this._onDidScroll.input = gridWidget.onDidScroll;
|
||||
this.gridWidgetDisposables.clear();
|
||||
this.gridWidgetDisposables.add(gridWidget.onDidChangeViewMaximized(maximized => this._onDidChangeGroupMaximized.fire(maximized)));
|
||||
|
||||
this._onDidChangeGroupMaximized.fire(this.hasMaximizedGroup());
|
||||
|
||||
this.onDidSetGridWidget.fire(undefined);
|
||||
}
|
||||
|
||||
@@ -97,6 +97,7 @@ export class EditorParts extends Disposable implements IEditorGroupsService, IEd
|
||||
disposables.add(part.onDidRemoveGroup(group => this._onDidRemoveGroup.fire(group)));
|
||||
disposables.add(part.onDidMoveGroup(group => this._onDidMoveGroup.fire(group)));
|
||||
disposables.add(part.onDidActivateGroup(group => this._onDidActivateGroup.fire(group)));
|
||||
disposables.add(part.onDidChangeGroupMaximized(maximized => this._onDidChangeGroupMaximized.fire(maximized)));
|
||||
|
||||
disposables.add(part.onDidChangeGroupIndex(group => this._onDidChangeGroupIndex.fire(group)));
|
||||
disposables.add(part.onDidChangeGroupLocked(group => this._onDidChangeGroupLocked.fire(group)));
|
||||
@@ -176,6 +177,9 @@ export class EditorParts extends Disposable implements IEditorGroupsService, IEd
|
||||
private readonly _onDidChangeGroupLocked = this._register(new Emitter<IEditorGroupView>());
|
||||
readonly onDidChangeGroupLocked = this._onDidChangeGroupLocked.event;
|
||||
|
||||
private readonly _onDidChangeGroupMaximized = this._register(new Emitter<boolean>());
|
||||
readonly onDidChangeGroupMaximized = this._onDidChangeGroupMaximized.event;
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Editor Groups Service
|
||||
@@ -227,11 +231,19 @@ export class EditorParts extends Disposable implements IEditorGroupsService, IEd
|
||||
}
|
||||
|
||||
setSize(group: IEditorGroupView | GroupIdentifier, size: { width: number; height: number }): void {
|
||||
return this.getPart(group).setSize(group, size);
|
||||
this.getPart(group).setSize(group, size);
|
||||
}
|
||||
|
||||
arrangeGroups(arrangement: GroupsArrangement): void {
|
||||
return this.activePart.arrangeGroups(arrangement);
|
||||
arrangeGroups(arrangement: GroupsArrangement, group?: IEditorGroupView): void {
|
||||
(group !== undefined ? this.getPart(group) : this.activePart).arrangeGroups(arrangement, group);
|
||||
}
|
||||
|
||||
toggleMaximizeGroup(group?: IEditorGroupView): void {
|
||||
(group !== undefined ? this.getPart(group) : this.activePart).toggleMaximizeGroup(group);
|
||||
}
|
||||
|
||||
toggleExpandGroup(group?: IEditorGroupView): void {
|
||||
(group !== undefined ? this.getPart(group) : this.activePart).toggleExpandGroup(group);
|
||||
}
|
||||
|
||||
restoreGroup(group: IEditorGroupView | GroupIdentifier): IEditorGroupView {
|
||||
@@ -239,7 +251,7 @@ export class EditorParts extends Disposable implements IEditorGroupsService, IEd
|
||||
}
|
||||
|
||||
applyLayout(layout: EditorGroupLayout): void {
|
||||
return this.activePart.applyLayout(layout);
|
||||
this.activePart.applyLayout(layout);
|
||||
}
|
||||
|
||||
getLayout(): EditorGroupLayout {
|
||||
@@ -247,7 +259,7 @@ export class EditorParts extends Disposable implements IEditorGroupsService, IEd
|
||||
}
|
||||
|
||||
centerLayout(active: boolean): void {
|
||||
return this.activePart.centerLayout(active);
|
||||
this.activePart.centerLayout(active);
|
||||
}
|
||||
|
||||
isLayoutCentered(): boolean {
|
||||
@@ -259,7 +271,7 @@ export class EditorParts extends Disposable implements IEditorGroupsService, IEd
|
||||
}
|
||||
|
||||
setGroupOrientation(orientation: GroupOrientation): void {
|
||||
return this.activePart.setGroupOrientation(orientation);
|
||||
this.activePart.setGroupOrientation(orientation);
|
||||
}
|
||||
|
||||
findGroup(scope: IFindGroupScope, source?: IEditorGroupView | GroupIdentifier, wrap?: boolean): IEditorGroupView | undefined {
|
||||
@@ -275,7 +287,7 @@ export class EditorParts extends Disposable implements IEditorGroupsService, IEd
|
||||
}
|
||||
|
||||
removeGroup(group: IEditorGroupView | GroupIdentifier): void {
|
||||
return this.getPart(group).removeGroup(group);
|
||||
this.getPart(group).removeGroup(group);
|
||||
}
|
||||
|
||||
moveGroup(group: IEditorGroupView | GroupIdentifier, location: IEditorGroupView | GroupIdentifier, direction: GroupDirection): IEditorGroupView {
|
||||
|
||||
@@ -185,3 +185,7 @@
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .editor-group-container > .monaco-progress-container {
|
||||
top: max(calc(var(--editor-group-tabs-height) - 2px), 0px); /* Override top position of progress bar which defined in vs/workbench/browser/media/part.css */
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ import { activeContrastBorder, contrastBorder, editorBackground } from 'vs/platf
|
||||
import { ResourcesDropHandler, DraggedEditorIdentifier, DraggedEditorGroupIdentifier, extractTreeDropData } from 'vs/workbench/browser/dnd';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { MergeGroupMode, IMergeGroupOptions, GroupsArrangement, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { MergeGroupMode, IMergeGroupOptions, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { addDisposableListener, EventType, EventHelper, Dimension, scheduleAtNextAnimationFrame, findParentWithClass, clearNode, DragAndDropObserver, isMouseEvent } from 'vs/base/browser/dom';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IEditorGroupsView, EditorServiceImpl, IEditorGroupView, IInternalEditorOpenOptions, IEditorPartsView } from 'vs/workbench/browser/parts/editor/editor';
|
||||
@@ -992,9 +992,17 @@ export class MultiEditorTabsControl extends EditorTabsControl {
|
||||
|
||||
const editor = this.tabsModel.getEditorByIndex(tabIndex);
|
||||
if (editor && this.tabsModel.isPinned(editor)) {
|
||||
if (this.groupsView.partOptions.doubleClickTabToToggleEditorGroupSizes) {
|
||||
this.groupsView.arrangeGroups(GroupsArrangement.TOGGLE, this.groupView);
|
||||
switch (this.groupsView.partOptions.doubleClickTabToToggleEditorGroupSizes) {
|
||||
case 'maximize':
|
||||
this.groupsView.toggleMaximizeGroup(this.groupView);
|
||||
break;
|
||||
case 'expand':
|
||||
this.groupsView.toggleExpandGroup(this.groupView);
|
||||
break;
|
||||
case 'off':
|
||||
break;
|
||||
}
|
||||
|
||||
} else {
|
||||
this.groupView.pinEditor(editor);
|
||||
}
|
||||
|
||||
@@ -53,3 +53,19 @@
|
||||
.monaco-workbench .part.panel > .title > .composite-bar-container >.composite-bar > .monaco-action-bar .action-item:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* Rotate icons when panel is on right */
|
||||
.monaco-workbench .part.basepanel.right .title-actions .codicon-split-horizontal::before,
|
||||
.monaco-workbench .part.basepanel.right .global-actions .codicon-panel-maximize::before,
|
||||
.monaco-workbench .part.basepanel.right .global-actions .codicon-panel-restore::before {
|
||||
display: inline-block;
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
/* Rotate icons when panel is on left */
|
||||
.monaco-workbench .part.basepanel.left .title-actions .codicon-split-horizontal::before,
|
||||
.monaco-workbench .part.basepanel.left .global-actions .codicon-panel-maximize::before,
|
||||
.monaco-workbench .part.basepanel.left .global-actions .codicon-panel-restore::before {
|
||||
display: inline-block;
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
@@ -322,9 +322,15 @@ const registry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Con
|
||||
'description': localize('centeredLayoutDynamicWidth', "Controls whether the centered layout tries to maintain constant width when the window is resized.")
|
||||
},
|
||||
'workbench.editor.doubleClickTabToToggleEditorGroupSizes': {
|
||||
'type': 'boolean',
|
||||
'default': true,
|
||||
'markdownDescription': localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'doubleClickTabToToggleEditorGroupSizes' }, "Controls whether to maximize/restore the editor group when double clicking on a tab. This value is ignored when `#workbench.editor.showTabs#` is not set to `multiple`.")
|
||||
'type': 'string',
|
||||
'enum': ['maximize', 'expand', 'off'],
|
||||
'default': 'expand',
|
||||
'markdownDescription': localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'doubleClickTabToToggleEditorGroupSizes' }, "Controls how the editor group is resized when double clicking on a tab. This value is ignored when `#workbench.editor.showTabs#` is not set to `multiple`."),
|
||||
'enumDescriptions': [
|
||||
localize('workbench.editor.doubleClickTabToToggleEditorGroupSizes.maximize', "All other editor groups are hidden and the current editor group is maximized to take up the entire editor area."),
|
||||
localize('workbench.editor.doubleClickTabToToggleEditorGroupSizes.expand', "The editor group takes as much space as possible by making all other editor groups as small as possible."),
|
||||
localize('workbench.editor.doubleClickTabToToggleEditorGroupSizes.off', "No editor group is resized when double clicking on a tab.")
|
||||
]
|
||||
},
|
||||
'workbench.editor.limit.enabled': {
|
||||
'type': 'boolean',
|
||||
@@ -780,16 +786,20 @@ Registry.as<IConfigurationMigrationRegistry>(Extensions.ConfigurationMigration)
|
||||
|
||||
Registry.as<IConfigurationMigrationRegistry>(Extensions.ConfigurationMigration)
|
||||
.registerConfigurationMigrations([{
|
||||
key: 'workbench.editor.doubleClickTabToToggleEditorGroupSizes', migrateFn: (value: any) => {
|
||||
if (typeof value === 'boolean') {
|
||||
value = value ? 'expand' : 'off';
|
||||
}
|
||||
return [['workbench.editor.doubleClickTabToToggleEditorGroupSizes', { value: value }]];
|
||||
}
|
||||
}, {
|
||||
key: 'workbench.editor.showTabs', migrateFn: (value: any) => {
|
||||
if (typeof value === 'boolean') {
|
||||
value = value ? 'multiple' : 'single';
|
||||
}
|
||||
return [['workbench.editor.showTabs', { value: value }]];
|
||||
}
|
||||
}]);
|
||||
|
||||
Registry.as<IConfigurationMigrationRegistry>(Extensions.ConfigurationMigration)
|
||||
.registerConfigurationMigrations([{
|
||||
}, {
|
||||
key: 'zenMode.hideTabs', migrateFn: (value: any) => {
|
||||
const result: ConfigurationKeyValuePairs = [['zenMode.hideTabs', { value: undefined }]];
|
||||
if (value === true) {
|
||||
|
||||
@@ -79,6 +79,7 @@ export const SplitEditorsVertically = new RawContextKey<boolean>('splitEditorsVe
|
||||
export const EditorAreaVisibleContext = new RawContextKey<boolean>('editorAreaVisible', true, localize('editorAreaVisible', "Whether the editor area is visible"));
|
||||
export const EditorTabsVisibleContext = new RawContextKey<boolean>('editorTabsVisible', true, localize('editorTabsVisible', "Whether editor tabs are visible"));
|
||||
export const EditorPinnedAndUnpinnedTabsContext = new RawContextKey<boolean>('editorPinnedAndUnpinnedTabsVisible', false, true);
|
||||
export const MaximizedEditorGroupContext = new RawContextKey<boolean>('maximizedEditorGroup', false, localize('editorGroupMaximized', "Editor group is maximized"));
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
@@ -1095,6 +1095,18 @@ export interface IWorkbenchEditorConfiguration {
|
||||
};
|
||||
}
|
||||
|
||||
export interface IEditorPartLimitConfiguration {
|
||||
enabled?: boolean;
|
||||
excludeDirty?: boolean;
|
||||
value?: number;
|
||||
perEditorGroup?: boolean;
|
||||
}
|
||||
|
||||
export interface IEditorPartDecorationsConfiguration {
|
||||
badges?: boolean;
|
||||
colors?: boolean;
|
||||
}
|
||||
|
||||
interface IEditorPartConfiguration {
|
||||
showTabs?: 'multiple' | 'single' | 'none';
|
||||
wrapTabs?: boolean;
|
||||
@@ -1127,17 +1139,9 @@ interface IEditorPartConfiguration {
|
||||
splitSizing?: 'auto' | 'split' | 'distribute';
|
||||
splitOnDragAndDrop?: boolean;
|
||||
centeredLayoutFixedWidth?: boolean;
|
||||
doubleClickTabToToggleEditorGroupSizes?: boolean;
|
||||
limit?: {
|
||||
enabled?: boolean;
|
||||
excludeDirty?: boolean;
|
||||
value?: number;
|
||||
perEditorGroup?: boolean;
|
||||
};
|
||||
decorations?: {
|
||||
badges?: boolean;
|
||||
colors?: boolean;
|
||||
};
|
||||
doubleClickTabToToggleEditorGroupSizes?: 'maximize' | 'expand' | 'off';
|
||||
limit?: IEditorPartLimitConfiguration;
|
||||
decorations?: IEditorPartDecorationsConfiguration;
|
||||
}
|
||||
|
||||
export interface IEditorPartOptions extends IEditorPartConfiguration {
|
||||
|
||||
@@ -31,7 +31,7 @@ export function getAccessibilityHelpText(accessor: ServicesAccessor, type: 'pane
|
||||
} else {
|
||||
const startChatKeybinding = keybindingService.lookupKeybinding('inlineChat.start')?.getAriaLabel();
|
||||
content.push(localize('inlineChat.overview', "Inline chat occurs within a code editor and takes into account the current selection. It is useful for making changes to the current editor. For example, fixing diagnostics, documenting or refactoring code. Keep in mind that AI generated code may be incorrect."));
|
||||
content.push(localize('inlineChat.access', "It can be activated via code actions or directly using the command: Inline Chat: Start Code Chat ({0}).", startChatKeybinding));
|
||||
content.push(localize('inlineChat.access', "It can be activated via code actions or directly using the command: Inline Chat: Start Inline Chat ({0}).", startChatKeybinding));
|
||||
const upHistoryKeybinding = keybindingService.lookupKeybinding('inlineChat.previousFromHistory')?.getAriaLabel();
|
||||
const downHistoryKeybinding = keybindingService.lookupKeybinding('inlineChat.nextFromHistory')?.getAriaLabel();
|
||||
if (upHistoryKeybinding && downHistoryKeybinding) {
|
||||
|
||||
@@ -12,17 +12,25 @@
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear animation styles when hovering.
|
||||
* Clear animation styles when hovering or when reduced motion is enabled.
|
||||
*/
|
||||
.monaco-workbench .interactive-input-part .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled):hover,
|
||||
.monaco-workbench .inline-chat .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled):hover {
|
||||
animation: none;
|
||||
}
|
||||
.monaco-workbench.reduce-motion .interactive-input-part .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled),
|
||||
.monaco-workbench.reduce-motion .inline-chat .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled) {
|
||||
animation: none;
|
||||
}
|
||||
|
||||
/*
|
||||
* Replace with "stop" icon when hovering.
|
||||
* Replace with "stop" icon when hovering or when reduced motion is enabled.
|
||||
*/
|
||||
.monaco-workbench .interactive-input-part .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled):hover::before,
|
||||
.monaco-workbench .inline-chat .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled):hover::before {
|
||||
content: "\ead7"; /* use `debug-stop` icon unicode for hovering over running voice recording */
|
||||
content: "\ead7";
|
||||
}
|
||||
.monaco-workbench.reduce-motion .interactive-input-part .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled)::before,
|
||||
.monaco-workbench.reduce-motion .inline-chat .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled)::before {
|
||||
content: "\ead7";
|
||||
}
|
||||
|
||||
@@ -683,8 +683,8 @@ registerThemingParticipant((theme, collector) => {
|
||||
|
||||
// Show a "microphone" icon when recording is in progress that glows via outline.
|
||||
collector.addRule(`
|
||||
.monaco-workbench .interactive-input-part .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled):not(:hover),
|
||||
.monaco-workbench .inline-chat .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled):not(:hover) {
|
||||
.monaco-workbench:not(.reduce-motion) .interactive-input-part .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled):not(:hover),
|
||||
.monaco-workbench:not(.reduce-motion) .inline-chat .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled):not(:hover) {
|
||||
color: ${activeRecordingColor};
|
||||
outline: 1px solid ${activeRecordingColor};
|
||||
outline-offset: -1px;
|
||||
|
||||
@@ -1237,7 +1237,7 @@ export class ExtensionEditor extends EditorPane {
|
||||
return $('tr', undefined,
|
||||
$('td', undefined, $('code', undefined, key)),
|
||||
$('td', undefined, description),
|
||||
$('td', undefined, $('code', undefined, `${isUndefined(properties[key].default) ? getDefaultValue(properties[key].type) : properties[key].default}`)));
|
||||
$('td', undefined, $('code', undefined, `${isUndefined(properties[key].default) ? getDefaultValue(properties[key].type) : JSON.stringify(properties[key].default)}`)));
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
@@ -13,7 +13,7 @@ import { EnablementState, IExtensionManagementServerService, IWorkbenchExtension
|
||||
import { IExtensionIgnoredRecommendationsService, IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations';
|
||||
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { VIEWLET_ID, IExtensionsWorkbenchService, IExtensionsViewPaneContainer, TOGGLE_IGNORE_EXTENSION_ACTION_ID, INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID, WORKSPACE_RECOMMENDATIONS_VIEW_ID, IWorkspaceRecommendedExtensionsView, AutoUpdateConfigurationKey, HasOutdatedExtensionsContext, SELECT_INSTALL_VSIX_EXTENSION_COMMAND_ID, LIST_WORKSPACE_UNSUPPORTED_EXTENSIONS_COMMAND_ID, ExtensionEditorTab, THEME_ACTIONS_GROUP, INSTALL_ACTIONS_GROUP, OUTDATED_EXTENSIONS_VIEW_ID, CONTEXT_HAS_GALLERY } from 'vs/workbench/contrib/extensions/common/extensions';
|
||||
import { VIEWLET_ID, IExtensionsWorkbenchService, IExtensionsViewPaneContainer, TOGGLE_IGNORE_EXTENSION_ACTION_ID, INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID, WORKSPACE_RECOMMENDATIONS_VIEW_ID, IWorkspaceRecommendedExtensionsView, AutoUpdateConfigurationKey, HasOutdatedExtensionsContext, SELECT_INSTALL_VSIX_EXTENSION_COMMAND_ID, LIST_WORKSPACE_UNSUPPORTED_EXTENSIONS_COMMAND_ID, ExtensionEditorTab, THEME_ACTIONS_GROUP, INSTALL_ACTIONS_GROUP, OUTDATED_EXTENSIONS_VIEW_ID, CONTEXT_HAS_GALLERY, IExtension } from 'vs/workbench/contrib/extensions/common/extensions';
|
||||
import { ReinstallAction, InstallSpecificVersionOfExtensionAction, ConfigureWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction, PromptExtensionInstallFailureAction, SearchExtensionsAction, SwitchToPreReleaseVersionAction, SwitchToReleasedVersionAction, SetColorThemeAction, SetFileIconThemeAction, SetProductIconThemeAction, ClearLanguageAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions';
|
||||
import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput';
|
||||
import { ExtensionEditor } from 'vs/workbench/contrib/extensions/browser/extensionEditor';
|
||||
@@ -672,14 +672,17 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi
|
||||
}
|
||||
],
|
||||
icon: installWorkspaceRecommendedIcon,
|
||||
run: () => {
|
||||
return Promise.all(this.extensionsWorkbenchService.outdated.map(async extension => {
|
||||
try {
|
||||
await this.extensionsWorkbenchService.install(extension, extension.local?.preRelease ? { installPreReleaseVersion: true } : undefined);
|
||||
} catch (err) {
|
||||
runAction(this.instantiationService.createInstance(PromptExtensionInstallFailureAction, extension, extension.latestVersion, InstallOperation.Update, err));
|
||||
run: async () => {
|
||||
const outdated = this.extensionsWorkbenchService.outdated;
|
||||
const results = await this.extensionsWorkbenchService.updateAll();
|
||||
results.forEach((result) => {
|
||||
if (result.error) {
|
||||
const extension: IExtension | undefined = outdated.find((extension) => areSameExtensions(extension.identifier, result.identifier));
|
||||
if (extension) {
|
||||
runAction(this.instantiationService.createInstance(PromptExtensionInstallFailureAction, extension, extension.latestVersion, InstallOperation.Update, result.error));
|
||||
}
|
||||
}
|
||||
}));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ export class InstallCountWidget extends ExtensionWidget {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.small && this.extension.state === ExtensionState.Installed) {
|
||||
if (this.small && this.extension.state !== ExtensionState.Uninstalled) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ export class RatingsWidget extends ExtensionWidget {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.small && this.extension.state === ExtensionState.Installed) {
|
||||
if (this.small && this.extension.state !== ExtensionState.Uninstalled) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import {
|
||||
IExtensionGalleryService, ILocalExtension, IGalleryExtension, IQueryOptions,
|
||||
InstallExtensionEvent, DidUninstallExtensionEvent, InstallOperation, InstallOptions, WEB_EXTENSION_TAG, InstallExtensionResult,
|
||||
IExtensionsControlManifest, InstallVSIXOptions, IExtensionInfo, IExtensionQueryOptions, IDeprecationInfo, isTargetPlatformCompatible
|
||||
IExtensionsControlManifest, InstallVSIXOptions, IExtensionInfo, IExtensionQueryOptions, IDeprecationInfo, isTargetPlatformCompatible, InstallExtensionInfo
|
||||
} from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, IWorkbenchExtensionManagementService, DefaultIconPath } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
|
||||
import { getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, areSameExtensions, groupByExtension, ExtensionKey, getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
@@ -1364,6 +1364,23 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
|
||||
}
|
||||
}
|
||||
|
||||
async updateAll(): Promise<InstallExtensionResult[]> {
|
||||
const toUpdate: InstallExtensionInfo[] = [];
|
||||
this.outdated.forEach((extension) => {
|
||||
if (extension.gallery) {
|
||||
toUpdate.push({
|
||||
extension: extension.gallery,
|
||||
options: {
|
||||
operation: InstallOperation.Update,
|
||||
installPreReleaseVersion: extension.local?.isPreReleaseVersion,
|
||||
profileLocation: this.userDataProfileService.currentProfile.extensionsResource,
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
return this.extensionManagementService.installGalleryExtensions(toUpdate);
|
||||
}
|
||||
|
||||
private async syncInstalledExtensionsWithGallery(gallery: IGalleryExtension[]): Promise<void> {
|
||||
const extensions: Extensions[] = [];
|
||||
if (this.localExtensions) {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IPager } from 'vs/base/common/paging';
|
||||
import { IQueryOptions, ILocalExtension, IGalleryExtension, IExtensionIdentifier, InstallOptions, InstallVSIXOptions, IExtensionInfo, IExtensionQueryOptions, IDeprecationInfo } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { IQueryOptions, ILocalExtension, IGalleryExtension, IExtensionIdentifier, InstallOptions, InstallVSIXOptions, IExtensionInfo, IExtensionQueryOptions, IDeprecationInfo, InstallExtensionResult } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { EnablementState, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
||||
@@ -116,6 +116,7 @@ export interface IExtensionsWorkbenchService {
|
||||
open(extension: IExtension | string, options?: IExtensionEditorOptions): Promise<void>;
|
||||
checkForUpdates(): Promise<void>;
|
||||
getExtensionStatus(extension: IExtension): IExtensionsStatus | undefined;
|
||||
updateAll(): Promise<InstallExtensionResult[]>;
|
||||
|
||||
// Sync APIs
|
||||
isExtensionIgnoredToSync(extension: IExtension): boolean;
|
||||
|
||||
@@ -37,7 +37,7 @@ export class StartSessionAction extends EditorAction2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'inlineChat.start',
|
||||
title: { value: localize('run', 'Start Code Chat'), original: 'Start Code Chat' },
|
||||
title: { value: localize('run', 'Start Inline Chat'), original: 'Start Inline Chat' },
|
||||
category: AbstractInlineChatAction.category,
|
||||
f1: true,
|
||||
precondition: ContextKeyExpr.and(CTX_INLINE_CHAT_HAS_PROVIDER, EditorContextKeys.writable),
|
||||
@@ -64,7 +64,7 @@ export class UnstashSessionAction extends EditorAction2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'inlineChat.unstash',
|
||||
title: { value: localize('unstash', 'Resume Last Dismissed Code Chat'), original: 'Resume Last Dismissed Code Chat' },
|
||||
title: { value: localize('unstash', 'Resume Last Dismissed Inline Chat'), original: 'Resume Last Dismissed Inline Chat' },
|
||||
category: AbstractInlineChatAction.category,
|
||||
precondition: ContextKeyExpr.and(CTX_INLINE_CHAT_HAS_STASHED_SESSION, EditorContextKeys.writable),
|
||||
keybinding: {
|
||||
|
||||
@@ -260,6 +260,10 @@
|
||||
outline-offset: -1px;
|
||||
}
|
||||
|
||||
.scm-view .scm-input .actions .action-label.codicon.codicon-debug-stop {
|
||||
color: var(--vscode-icon-foreground) !important;
|
||||
}
|
||||
|
||||
.scm-view .scm-editor-container .monaco-editor {
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
@@ -161,16 +161,6 @@ export class SCMRepositoryMenus implements ISCMRepositoryMenus, IDisposable {
|
||||
return this._repositoryMenu;
|
||||
}
|
||||
|
||||
private _inputBoxMenu: IMenu | undefined;
|
||||
get inputBoxMenu(): IMenu {
|
||||
if (!this._inputBoxMenu) {
|
||||
this._inputBoxMenu = this.menuService.createMenu(MenuId.SCMInputBox, this.contextKeyService);
|
||||
this.disposables.add(this._inputBoxMenu);
|
||||
}
|
||||
|
||||
return this._inputBoxMenu;
|
||||
}
|
||||
|
||||
private readonly disposables = new DisposableStore();
|
||||
|
||||
constructor(
|
||||
|
||||
@@ -1959,9 +1959,10 @@ class SCMInputWidget {
|
||||
updateEnablement(input.enabled);
|
||||
|
||||
// ActionBar
|
||||
this.actionBar.context = input.repository.provider;
|
||||
|
||||
const onDidChangeActionButton = () => {
|
||||
// Update placeholder width to accommodate for the action bar
|
||||
this.placeholderTextContainer.style.width = input.actionButton ? 'calc(100% - 26px)' : '100%';
|
||||
|
||||
this.actionBar.clear();
|
||||
if (!input.actionButton) {
|
||||
return;
|
||||
@@ -1975,9 +1976,6 @@ class SCMInputWidget {
|
||||
() => this.commandService.executeCommand(input.actionButton!.command.id, ...(input.actionButton!.command.arguments || [])));
|
||||
|
||||
this.actionBar.push(action, { icon: true, label: false });
|
||||
|
||||
// Update placeholder width to accommodate for the action bar
|
||||
this.placeholderTextContainer.style.width = input.actionButton ? 'calc(100% - 26px)' : '100%';
|
||||
};
|
||||
|
||||
this.repositoryDisposables.add(input.onDidChangeActionButton(onDidChangeActionButton, this));
|
||||
|
||||
@@ -181,7 +181,6 @@ export interface ISCMTitleMenu {
|
||||
export interface ISCMRepositoryMenus {
|
||||
readonly titleMenu: ISCMTitleMenu;
|
||||
readonly repositoryMenu: IMenu;
|
||||
readonly inputBoxMenu: IMenu;
|
||||
getResourceGroupMenu(group: ISCMResourceGroup): IMenu;
|
||||
getResourceMenu(resource: ISCMResource): IMenu;
|
||||
getResourceFolderMenu(group: ISCMResourceGroup): IMenu;
|
||||
|
||||
@@ -124,12 +124,6 @@ const apiMenus: IAPIMenu[] = [
|
||||
id: MenuId.SCMSourceControl,
|
||||
description: localize('menus.scmSourceControl', "The Source Control menu")
|
||||
},
|
||||
{
|
||||
key: 'scm/inputBox',
|
||||
id: MenuId.SCMInputBox,
|
||||
description: localize('menus.scmInputBox', "The Source Control input box menu"),
|
||||
proposed: 'contribSourceControlInputBoxMenu'
|
||||
},
|
||||
{
|
||||
key: 'scm/resourceState/context',
|
||||
id: MenuId.SCMResourceContext,
|
||||
|
||||
@@ -43,23 +43,22 @@ export interface IFindGroupScope {
|
||||
}
|
||||
|
||||
export const enum GroupsArrangement {
|
||||
/**
|
||||
* Make the current active group consume the entire
|
||||
* editor area.
|
||||
*/
|
||||
MAXIMIZE,
|
||||
|
||||
/**
|
||||
* Make the current active group consume the maximum
|
||||
* amount of space possible.
|
||||
*/
|
||||
MAXIMIZE,
|
||||
EXPAND,
|
||||
|
||||
/**
|
||||
* Size all groups evenly.
|
||||
*/
|
||||
EVEN,
|
||||
|
||||
/**
|
||||
* Will behave like MINIMIZE_OTHERS if the active
|
||||
* group is not already maximized and EVEN otherwise
|
||||
*/
|
||||
TOGGLE
|
||||
EVEN
|
||||
}
|
||||
|
||||
export interface GroupLayoutArgument {
|
||||
@@ -215,6 +214,11 @@ export interface IEditorGroupsContainer {
|
||||
*/
|
||||
readonly onDidChangeGroupLocked: Event<IEditorGroup>;
|
||||
|
||||
/**
|
||||
* An event for when the maximized state of a group changes.
|
||||
*/
|
||||
readonly onDidChangeGroupMaximized: Event<boolean>;
|
||||
|
||||
/**
|
||||
* An active group is the default location for new editors to open.
|
||||
*/
|
||||
@@ -273,7 +277,17 @@ export interface IEditorGroupsContainer {
|
||||
/**
|
||||
* Arrange all groups in the container according to the provided arrangement.
|
||||
*/
|
||||
arrangeGroups(arrangement: GroupsArrangement): void;
|
||||
arrangeGroups(arrangement: GroupsArrangement, target?: IEditorGroup | GroupIdentifier): void;
|
||||
|
||||
/**
|
||||
* Toggles the target goup size to maximize/unmaximize.
|
||||
*/
|
||||
toggleMaximizeGroup(group?: IEditorGroup | GroupIdentifier): void;
|
||||
|
||||
/**
|
||||
* Toggles the target goup size to expand/distribute even.
|
||||
*/
|
||||
toggleExpandGroup(group?: IEditorGroup | GroupIdentifier): void;
|
||||
|
||||
/**
|
||||
* Applies the provided layout by either moving existing groups or creating new groups.
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { workbenchInstantiationService, registerTestEditor, TestFileEditorInput, TestEditorPart, TestServiceAccessor, createEditorPart, ITestInstantiationService, workbenchTeardown } from 'vs/workbench/test/browser/workbenchTestServices';
|
||||
import { GroupDirection, GroupsOrder, MergeGroupMode, GroupOrientation, GroupLocation, isEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { GroupDirection, GroupsOrder, MergeGroupMode, GroupOrientation, GroupLocation, isEditorGroup, IEditorGroupsService, GroupsArrangement } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { CloseDirection, IEditorPartOptions, EditorsOrder, EditorInputCapabilities, GroupModelChangeKind, SideBySideEditor } from 'vs/workbench/common/editor';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
@@ -1718,5 +1718,44 @@ suite('EditorGroupsService', () => {
|
||||
assert.strictEqual(rootGroup.isLocked, false);
|
||||
});
|
||||
|
||||
test('maximize editor group', async () => {
|
||||
const instantiationService = workbenchInstantiationService(undefined, disposables);
|
||||
const [part] = await createPart(instantiationService);
|
||||
|
||||
const rootGroup = part.activeGroup;
|
||||
const editorPartSize = part.getSize(rootGroup);
|
||||
|
||||
const rightGroup = part.addGroup(rootGroup, GroupDirection.RIGHT);
|
||||
const rightBottomGroup = part.addGroup(rightGroup, GroupDirection.DOWN);
|
||||
|
||||
const sizeRootGroup = part.getSize(rootGroup);
|
||||
const sizeRightGroup = part.getSize(rightGroup);
|
||||
const sizeRightBottomGroup = part.getSize(rightBottomGroup);
|
||||
|
||||
let maximizedValue;
|
||||
const maxiizeGroupEventDisposable = part.onDidChangeGroupMaximized((maximized) => {
|
||||
maximizedValue = maximized;
|
||||
});
|
||||
|
||||
part.arrangeGroups(GroupsArrangement.MAXIMIZE, rootGroup);
|
||||
|
||||
// getSize()
|
||||
assert.deepStrictEqual(part.getSize(rootGroup), editorPartSize);
|
||||
assert.deepStrictEqual(part.getSize(rightGroup), { width: 0, height: 0 });
|
||||
assert.deepStrictEqual(part.getSize(rightBottomGroup), { width: 0, height: 0 });
|
||||
|
||||
assert.deepStrictEqual(maximizedValue, true);
|
||||
|
||||
part.toggleMaximizeGroup();
|
||||
|
||||
// Size is restored
|
||||
assert.deepStrictEqual(part.getSize(rootGroup), sizeRootGroup);
|
||||
assert.deepStrictEqual(part.getSize(rightGroup), sizeRightGroup);
|
||||
assert.deepStrictEqual(part.getSize(rightBottomGroup), sizeRightBottomGroup);
|
||||
|
||||
assert.deepStrictEqual(maximizedValue, false);
|
||||
maxiizeGroupEventDisposable.dispose();
|
||||
});
|
||||
|
||||
ensureNoDisposablesAreLeakedInTestSuite();
|
||||
});
|
||||
|
||||
@@ -1691,7 +1691,7 @@ suite('EditorService', () => {
|
||||
editor = await service.openEditor(input2, { pinned: true, activation: EditorActivation.ACTIVATE }, sideGroup);
|
||||
assert.strictEqual(part.activeGroup, sideGroup);
|
||||
|
||||
part.arrangeGroups(GroupsArrangement.MAXIMIZE);
|
||||
part.arrangeGroups(GroupsArrangement.EXPAND);
|
||||
editor = await service.openEditor(input1, { pinned: true, preserveFocus: true, activation: EditorActivation.RESTORE }, rootGroup);
|
||||
assert.strictEqual(part.activeGroup, sideGroup);
|
||||
});
|
||||
@@ -1711,13 +1711,13 @@ suite('EditorService', () => {
|
||||
assert.strictEqual(part.activeGroup, sideGroup);
|
||||
assert.notStrictEqual(rootGroup, sideGroup);
|
||||
|
||||
part.arrangeGroups(GroupsArrangement.MAXIMIZE, part.activeGroup);
|
||||
part.arrangeGroups(GroupsArrangement.EXPAND, part.activeGroup);
|
||||
|
||||
await rootGroup.closeEditor(input2);
|
||||
assert.strictEqual(part.activeGroup, sideGroup);
|
||||
|
||||
assert(!part.isGroupMaximized(rootGroup));
|
||||
assert(part.isGroupMaximized(part.activeGroup));
|
||||
assert(!part.isGroupExpanded(rootGroup));
|
||||
assert(part.isGroupExpanded(part.activeGroup));
|
||||
});
|
||||
|
||||
test('active editor change / visible editor change events', async function () {
|
||||
|
||||
@@ -31,7 +31,6 @@ export const allApiProposals = Object.freeze({
|
||||
contribNotebookStaticPreloads: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribNotebookStaticPreloads.d.ts',
|
||||
contribRemoteHelp: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribRemoteHelp.d.ts',
|
||||
contribShareMenu: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribShareMenu.d.ts',
|
||||
contribSourceControlInputBoxMenu: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribSourceControlInputBoxMenu.d.ts',
|
||||
contribStatusBarItems: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribStatusBarItems.d.ts',
|
||||
contribViewsRemote: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribViewsRemote.d.ts',
|
||||
contribViewsWelcome: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribViewsWelcome.d.ts',
|
||||
|
||||
@@ -824,6 +824,7 @@ export class TestEditorGroupsService implements IEditorGroupsService {
|
||||
onDidMoveGroup: Event<IEditorGroup> = Event.None;
|
||||
onDidChangeGroupIndex: Event<IEditorGroup> = Event.None;
|
||||
onDidChangeGroupLocked: Event<IEditorGroup> = Event.None;
|
||||
onDidChangeGroupMaximized: Event<boolean> = Event.None;
|
||||
onDidLayout: Event<IDimension> = Event.None;
|
||||
onDidChangeEditorPartOptions = Event.None;
|
||||
onDidScroll = Event.None;
|
||||
@@ -850,6 +851,8 @@ export class TestEditorGroupsService implements IEditorGroupsService {
|
||||
getSize(_group: number | IEditorGroup): { width: number; height: number } { return { width: 100, height: 100 }; }
|
||||
setSize(_group: number | IEditorGroup, _size: { width: number; height: number }): void { }
|
||||
arrangeGroups(_arrangement: GroupsArrangement): void { }
|
||||
toggleMaximizeGroup(): void { }
|
||||
toggleExpandGroup(): void { }
|
||||
applyLayout(_layout: EditorGroupLayout): void { }
|
||||
getLayout(): EditorGroupLayout { throw new Error('not implemented'); }
|
||||
setGroupOrientation(_orientation: GroupOrientation): void { }
|
||||
@@ -966,6 +969,8 @@ export class TestEditorGroupAccessor implements IEditorGroupsView {
|
||||
copyGroup(group: number | IEditorGroupView, location: number | IEditorGroupView, direction: GroupDirection): IEditorGroupView { throw new Error('Method not implemented.'); }
|
||||
removeGroup(group: number | IEditorGroupView): void { throw new Error('Method not implemented.'); }
|
||||
arrangeGroups(arrangement: GroupsArrangement, target?: number | IEditorGroupView | undefined): void { throw new Error('Method not implemented.'); }
|
||||
toggleMaximizeGroup(group: number | IEditorGroupView): void { throw new Error('Method not implemented.'); }
|
||||
toggleExpandGroup(group: number | IEditorGroupView): void { throw new Error('Method not implemented.'); }
|
||||
}
|
||||
|
||||
export class TestEditorService implements EditorServiceImpl {
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
// empty placeholder declaration for the `scm/inputBox` menu contribution point
|
||||
// https://github.com/microsoft/vscode/issues/195474
|
||||
Reference in New Issue
Block a user