Browse Source

Improves multi-instance webview splitting

Fixes issues with multi-instance timeline
Changes `*.experimental.allowMultipleInstances` to `*.allowMultiple`
main
Eric Amodio 1 year ago
parent
commit
2fd2bbbe32
10 changed files with 94 additions and 57 deletions
  1. +34
    -23
      package.json
  2. +3
    -9
      src/config.ts
  3. +1
    -0
      src/constants.ts
  4. +2
    -2
      src/plus/webviews/focus/registration.ts
  5. +7
    -3
      src/plus/webviews/graph/graphWebview.ts
  6. +3
    -4
      src/plus/webviews/graph/registration.ts
  7. +3
    -2
      src/plus/webviews/timeline/registration.ts
  8. +8
    -11
      src/plus/webviews/timeline/timelineWebview.ts
  9. +13
    -2
      src/webviews/webviewController.ts
  10. +20
    -1
      src/webviews/webviewsController.ts

+ 34
- 23
package.json View File

@ -2428,6 +2428,13 @@
"title": "Commit Graph",
"order": 105,
"properties": {
"gitlens.graph.allowMultiple": {
"type": "boolean",
"default": true,
"markdownDescription": "Specifies whether to allow opening multiple instances of the _Commit Graph_ in the editor area",
"scope": "window",
"order": 5
},
"gitlens.graph.defaultItemLimit": {
"type": "number",
"default": 500,
@ -2676,13 +2683,6 @@
"markdownDescription": "Specifies additional markers to show on the minimap in the _Commit Graph_",
"scope": "window",
"order": 102
},
"gitlens.graph.experimental.allowMultipleInstances": {
"type": "boolean",
"default": true,
"markdownDescription": "Specifies whether to allow opening multiple instances of the _Commit Graph_ in the editor area",
"scope": "window",
"order": 110
}
}
},
@ -2691,7 +2691,7 @@
"title": "Focus View",
"order": 106,
"properties": {
"gitlens.focus.experimental.allowMultipleInstances": {
"gitlens.focus.allowMultiple": {
"type": "boolean",
"default": true,
"markdownDescription": "Specifies whether to allow opening multiple instances of the _Focus View_",
@ -2705,7 +2705,7 @@
"title": "Visual File History",
"order": 106,
"properties": {
"gitlens.visualHistory.experimental.allowMultipleInstances": {
"gitlens.visualHistory.allowMultiple": {
"type": "boolean",
"default": true,
"markdownDescription": "Specifies whether to allow opening multiple instances of the _Visual File History_ in the editor area",
@ -2716,7 +2716,8 @@
"type": "number",
"default": 20,
"markdownDescription": "Specifies the limit on the how many commits can be queried for statistics in the _Visual File History_, because of rate limits. Only applies to virtual workspaces.",
"scope": "window"
"scope": "window",
"order": 20
}
}
},
@ -5137,6 +5138,12 @@
},
{
"command": "gitlens.showTimelinePage",
"title": "Show Visual File History",
"category": "GitLens",
"icon": "$(graph-scatter)"
},
{
"command": "gitlens.showInTimeline",
"title": "Open Visual File History",
"category": "GitLens",
"icon": "$(graph-scatter)"
@ -8351,7 +8358,7 @@
},
{
"command": "gitlens.focus.split",
"when": "gitlens:enabled && config.gitlens.focus.experimental.allowMultipleInstances"
"when": "gitlens:enabled && config.gitlens.focus.allowMultiple"
},
{
"command": "gitlens.showGraph",
@ -8363,7 +8370,7 @@
},
{
"command": "gitlens.graph.split",
"when": "gitlens:enabled && config.gitlens.graph.experimental.allowMultipleInstances"
"when": "gitlens:enabled && config.gitlens.graph.allowMultiple"
},
{
"command": "gitlens.showGraphView",
@ -8471,11 +8478,15 @@
},
{
"command": "gitlens.showTimelinePage",
"when": "gitlens:enabled"
},
{
"command": "gitlens.showInTimeline",
"when": "gitlens:enabled && gitlens:activeFileStatus =~ /tracked/"
},
{
"command": "gitlens.timeline.split",
"when": "gitlens:enabled && config.gitlens.visualHistory.experimental.allowMultipleInstances"
"when": "gitlens:enabled && config.gitlens.visualHistory.allowMultiple"
},
{
"command": "gitlens.showTimelineView",
@ -10659,17 +10670,17 @@
},
{
"command": "gitlens.focus.split",
"when": "resourceScheme == webview-panel && activeWebviewPanelId === gitlens.focus && config.gitlens.focus.experimental.allowMultipleInstances",
"when": "resourceScheme == webview-panel && activeWebviewPanelId === gitlens.focus && config.gitlens.focus.allowMultiple",
"group": "navigation@-97"
},
{
"command": "gitlens.graph.split",
"when": "resourceScheme == webview-panel && activeWebviewPanelId === gitlens.graph && config.gitlens.graph.experimental.allowMultipleInstances",
"when": "resourceScheme == webview-panel && activeWebviewPanelId === gitlens.graph && config.gitlens.graph.allowMultiple",
"group": "navigation@-97"
},
{
"command": "gitlens.timeline.split",
"when": "resourceScheme == webview-panel && activeWebviewPanelId === gitlens.timeline && config.gitlens.visualHistory.experimental.allowMultipleInstances",
"when": "resourceScheme == webview-panel && activeWebviewPanelId === gitlens.timeline && config.gitlens.visualHistory.allowMultiple",
"group": "navigation@-97"
}
],
@ -10706,17 +10717,17 @@
},
{
"command": "gitlens.focus.split",
"when": "resourceScheme == webview-panel && activeWebviewPanelId === gitlens.focus && config.gitlens.focus.experimental.allowMultipleInstances",
"when": "resourceScheme == webview-panel && activeWebviewPanelId === gitlens.focus && config.gitlens.focus.allowMultiple",
"group": "6_split_in_group_gitlens@2"
},
{
"command": "gitlens.graph.split",
"when": "resourceScheme == webview-panel && activeWebviewPanelId === gitlens.graph && config.gitlens.graph.experimental.allowMultipleInstances",
"when": "resourceScheme == webview-panel && activeWebviewPanelId === gitlens.graph && config.gitlens.graph.allowMultiple",
"group": "6_split_in_group_gitlens@2"
},
{
"command": "gitlens.timeline.split",
"when": "resourceScheme == webview-panel && activeWebviewPanelId === gitlens.timeline && config.gitlens.visualHistory.experimental.allowMultipleInstances",
"when": "resourceScheme == webview-panel && activeWebviewPanelId === gitlens.timeline && config.gitlens.visualHistory.allowMultiple",
"group": "6_split_in_group_gitlens@2"
}
],
@ -13756,7 +13767,7 @@
"group": "1_gitlens@1"
},
{
"command": "gitlens.showTimelinePage",
"command": "gitlens.showInTimeline",
"group": "1_gitlens@2"
},
{
@ -13900,7 +13911,7 @@
"group": "1_gitlens@1"
},
{
"command": "gitlens.showTimelinePage",
"command": "gitlens.showInTimeline",
"group": "1_gitlens@2"
},
{
@ -13995,7 +14006,7 @@
"group": "1_gitlens@1"
},
{
"command": "gitlens.showTimelinePage",
"command": "gitlens.showInTimeline",
"when": "!explorerResourceIsRoot && !explorerResourceIsFolder && gitlens:enabled",
"group": "1_gitlens@2"
},
@ -14113,7 +14124,7 @@
"group": "1_gitlens@1"
},
{
"command": "gitlens.showTimelinePage",
"command": "gitlens.showInTimeline",
"group": "1_gitlens@2"
},
{

+ 3
- 9
src/config.ts View File

@ -69,9 +69,7 @@ export interface Config {
readonly command: string | null;
};
readonly focus: {
readonly experimental: {
allowMultipleInstances: boolean;
};
readonly allowMultiple: boolean;
};
readonly gitCommands: {
readonly closeOnFocusOut: boolean;
@ -196,10 +194,8 @@ export interface Config {
readonly enabled: boolean;
};
readonly visualHistory: {
readonly allowMultiple: boolean;
readonly queryLimit: number;
readonly experimental: {
allowMultipleInstances: boolean;
};
};
readonly worktrees: {
readonly defaultLocation: string | null;
@ -323,15 +319,13 @@ export interface AdvancedConfig {
}
export interface GraphConfig {
readonly allowMultiple: boolean;
readonly avatars: boolean;
readonly commitOrdering: 'date' | 'author-date' | 'topo';
readonly dateFormat: DateTimeFormat | string | null;
readonly dateStyle: DateStyle | null;
readonly defaultItemLimit: number;
readonly dimMergeCommits: boolean;
readonly experimental: {
allowMultipleInstances: boolean;
};
readonly minimap: {
readonly enabled: boolean;
readonly dataType: 'commits' | 'lines';

+ 1
- 0
src/constants.ts View File

@ -247,6 +247,7 @@ export const enum Commands {
ShowInCommitGraph = 'gitlens.showInCommitGraph',
ShowInCommitGraphView = 'gitlens.showInCommitGraphView',
ShowInDetailsView = 'gitlens.showInDetailsView',
ShowInTimeline = 'gitlens.showInTimeline',
ShowLastQuickPick = 'gitlens.showLastQuickPick',
ShowLineCommitInView = 'gitlens.showLineCommitInView',
ShowLineHistoryView = 'gitlens.showLineHistoryView',

+ 2
- 2
src/plus/webviews/focus/registration.ts View File

@ -21,7 +21,7 @@ export function registerFocusWebviewPanel(controller: WebviewsController) {
retainContextWhenHidden: true,
enableFindWidget: true,
},
allowMultipleInstances: configuration.get('focus.experimental.allowMultipleInstances'),
allowMultipleInstances: configuration.get('focus.allowMultiple'),
},
async (container, host) => {
const { FocusWebviewProvider } = await import(/* webpackChunkName: "focus" */ './focusWebview');
@ -35,7 +35,7 @@ export function registerFocusWebviewCommands(panels: WebviewPanelsProxy) {
registerCommand(`${panels.id}.refresh`, () => void panels.getActiveInstance()?.refresh(true)),
registerCommand(
`${panels.id}.split`,
() => void panels.show({ preserveInstance: false, column: ViewColumn.Beside }),
() => void panels.splitActiveInstance({ preserveInstance: false, column: ViewColumn.Beside }),
),
);
}

+ 7
- 3
src/plus/webviews/graph/graphWebview.ts View File

@ -84,7 +84,7 @@ import { RepositoryFolderNode } from '../../../views/nodes/viewNode';
import type { IpcMessage, IpcNotificationType } from '../../../webviews/protocol';
import { onIpc } from '../../../webviews/protocol';
import type { WebviewController, WebviewProvider } from '../../../webviews/webviewController';
import type { WebviewPanelShowCommandArgs, WebviewShowOptions } from '../../../webviews/webviewsController';
import type { WebviewPanelShowCommandArgs } from '../../../webviews/webviewsController';
import { isSerializedState } from '../../../webviews/webviewsController';
import type { SubscriptionChangeEvent } from '../../subscription/subscriptionService';
import type {
@ -289,7 +289,7 @@ export class GraphWebviewProvider implements WebviewProvider {
this._disposable.dispose();
}
canReuseInstance(_options?: WebviewShowOptions, ...args: unknown[]): boolean | undefined {
canReuseInstance(...args: unknown[]): boolean | undefined {
if (this.container.git.openRepositoryCount === 1) return true;
const [arg] = args;
@ -303,7 +303,11 @@ export class GraphWebviewProvider implements WebviewProvider {
repository = this.container.git.getRepository(arg.state.selectedRepository);
}
return repository == null ? undefined : repository.uri.toString() === this.repository?.uri.toString();
return repository?.uri.toString() === this.repository?.uri.toString() ? true : undefined;
}
getSplitArgs(): unknown[] {
return [this.repository];
}
async onShowing(

+ 3
- 4
src/plus/webviews/graph/registration.ts View File

@ -33,7 +33,7 @@ export function registerGraphWebviewPanel(controller: WebviewsController) {
retainContextWhenHidden: true,
enableFindWidget: false,
},
allowMultipleInstances: configuration.get('graph.experimental.allowMultipleInstances'),
allowMultipleInstances: configuration.get('graph.allowMultiple'),
},
async (container, host) => {
const { GraphWebviewProvider } = await import(/* webpackChunkName: "graph" */ './graphWebview');
@ -122,8 +122,7 @@ export function registerGraphWebviewCommands(container: Container, panels: Webvi
if (configuration.get('graph.layout') === 'panel') {
void container.graphView.show({ preserveFocus: preserveFocus }, args);
} else {
// const active = panels.getActiveInstance()?.instanceId;
void panels.show({ preserveFocus: preserveFocus /*preserveInstance: active ?? true*/ }, args);
void panels.show({ preserveFocus: preserveFocus }, args);
}
},
),
@ -146,7 +145,7 @@ export function registerGraphWebviewCommands(container: Container, panels: Webvi
registerCommand(`${panels.id}.refresh`, () => void panels.getActiveInstance()?.refresh(true)),
registerCommand(
`${panels.id}.split`,
() => void panels.show({ preserveInstance: false, column: ViewColumn.Beside }),
() => void panels.splitActiveInstance({ preserveInstance: false, column: ViewColumn.Beside }),
),
);
}

+ 3
- 2
src/plus/webviews/timeline/registration.ts View File

@ -21,7 +21,7 @@ export function registerTimelineWebviewPanel(controller: WebviewsController) {
retainContextWhenHidden: false,
enableFindWidget: false,
},
allowMultipleInstances: configuration.get('visualHistory.experimental.allowMultipleInstances'),
allowMultipleInstances: configuration.get('visualHistory.allowMultiple'),
},
async (container, host) => {
const { TimelineWebviewProvider } = await import(/* webpackChunkName: "timeline" */ './timelineWebview');
@ -52,10 +52,11 @@ export function registerTimelineWebviewView(controller: WebviewsController) {
export function registerTimelineWebviewCommands(panels: WebviewPanelsProxy) {
return Disposable.from(
registerCommand(Commands.ShowInTimeline, (...args: unknown[]) => void panels.show(undefined, ...args)),
registerCommand(`${panels.id}.refresh`, () => void panels.getActiveInstance()?.refresh(true)),
registerCommand(
`${panels.id}.split`,
() => void panels.show({ preserveInstance: false, column: ViewColumn.Beside }),
() => void panels.splitActiveInstance({ preserveInstance: false, column: ViewColumn.Beside }),
),
);
}

+ 8
- 11
src/plus/webviews/timeline/timelineWebview.ts View File

@ -23,7 +23,7 @@ import type { IpcMessage } from '../../../webviews/protocol';
import { onIpc } from '../../../webviews/protocol';
import type { WebviewController, WebviewProvider } from '../../../webviews/webviewController';
import { updatePendingContext } from '../../../webviews/webviewController';
import type { WebviewPanelShowCommandArgs, WebviewShowOptions } from '../../../webviews/webviewsController';
import type { WebviewShowOptions } from '../../../webviews/webviewsController';
import { isSerializedState } from '../../../webviews/webviewsController';
import type { SubscriptionChangeEvent } from '../../subscription/subscriptionService';
import type { Commit, Period, State } from './protocol';
@ -87,10 +87,7 @@ export class TimelineWebviewProvider implements WebviewProvider {
void this.notifyDidChangeState(true);
}
canReuseInstance(
_options?: WebviewShowOptions,
...args: [Uri | ViewFileNode | { state: Partial<State> }] | unknown[]
): boolean | undefined {
canReuseInstance(...args: [Uri | ViewFileNode | { state: Partial<State> }] | unknown[]): boolean | undefined {
let uri: Uri | undefined;
const [arg] = args;
@ -106,7 +103,11 @@ export class TimelineWebviewProvider implements WebviewProvider {
uri = window.activeTextEditor?.document.uri;
}
return uri == null ? undefined : uri.toString() === this._context.uri?.toString();
return uri?.toString() === this._context.uri?.toString() ? true : undefined;
}
getSplitArgs(): unknown[] {
return [this._context.uri];
}
onShowing(
@ -158,11 +159,7 @@ export class TimelineWebviewProvider implements WebviewProvider {
() => {
if (this._context.uri == null) return;
void executeCommand<WebviewPanelShowCommandArgs>(
Commands.ShowTimelinePage,
{ _type: 'WebviewPanelShowOptions' },
this._context.uri,
);
void executeCommand(Commands.ShowInTimeline, this._context.uri);
},
this,
),

+ 13
- 2
src/webviews/webviewController.ts View File

@ -41,7 +41,12 @@ type GetParentType = T
: never;
export interface WebviewProvider<State, SerializedState = State> extends Disposable {
canReuseInstance?(options?: WebviewShowOptions, ...args: unknown[]): boolean | undefined;
/**
* Determines whether the webview instance can be reused
* @returns `true` if the webview should be reused, `false` if it should NOT be reused, and `undefined` if it *could* be reused but not ideal
*/
canReuseInstance?(...args: unknown[]): boolean | undefined;
getSplitArgs?(): unknown[];
onShowing?(loading: boolean, options: WebviewShowOptions, ...args: unknown[]): boolean | Promise<boolean>;
registerCommands?(): Disposable[];
@ -266,7 +271,13 @@ export class WebviewController<
if (!this.isEditor()) return undefined;
if (options?.column != null && options.column !== this.parent.viewColumn) return false;
return this.provider.canReuseInstance?.(options, ...args);
return this.provider.canReuseInstance?.(...args);
}
getSplitArgs(): unknown[] {
if (!this.isEditor()) return [];
return this.provider.getSplitArgs?.() ?? [];
}
@debug({ args: false })

+ 20
- 1
src/webviews/webviewsController.ts View File

@ -59,6 +59,7 @@ export interface WebviewPanelsProxy extends Disposable {
readonly instances: Iterable<WebviewPanelProxy>;
getActiveInstance(): WebviewPanelProxy | undefined;
show(options?: WebviewPanelsShowOptions, ...args: unknown[]): Promise<void>;
splitActiveInstance(options?: WebviewPanelsShowOptions): Promise<void>;
}
export interface WebviewViewDescriptor {
@ -285,9 +286,19 @@ export class WebviewsController implements Disposable {
active = c;
}
if (c.canReuseInstance(options, ...args) === true) {
const canReuse = c.canReuseInstance(options, ...args);
if (canReuse === true) {
// If the webview says it should be reused, use it
controller = c;
break;
} else if (canReuse === false) {
// If the webview says it should not be reused don't and clear it from being first/active
if (first === c) {
first = undefined;
}
if (active === c) {
active = undefined;
}
}
}
@ -404,6 +415,14 @@ export class WebviewsController implements Disposable {
const controller = find(registration.controllers.values(), c => c.active ?? false);
return controller != null ? convertToWebviewPanelProxy(controller) : undefined;
},
splitActiveInstance: function (options?: WebviewPanelsShowOptions) {
const controller =
registration.controllers != null
? find(registration.controllers.values(), c => c.active ?? false)
: undefined;
const args = controller?.getSplitArgs() ?? [];
return show({ ...options, preserveInstance: false }, ...args);
},
dispose: function () {
disposable.dispose();
},

Loading…
Cancel
Save