Skip to content

Commit cee2da1

Browse files
committed
Improves commit details following/selection
1 parent 6187d28 commit cee2da1

File tree

7 files changed

+224
-75
lines changed

7 files changed

+224
-75
lines changed

src/context.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,22 @@
11
import { commands, EventEmitter } from 'vscode';
22
import type { ContextKeys } from './constants';
33
import { CoreCommands } from './constants';
4+
import type { WebviewIds } from './webviews/webviewBase';
5+
import type { WebviewViewIds } from './webviews/webviewViewBase';
46

57
const contextStorage = new Map<string, unknown>();
68

79
type WebviewContextKeys =
8-
| `${ContextKeys.WebviewPrefix}${string}:active`
9-
| `${ContextKeys.WebviewPrefix}${string}:focus`
10-
| `${ContextKeys.WebviewPrefix}${string}:inputFocus`;
10+
| `${ContextKeys.WebviewPrefix}${WebviewIds}:active`
11+
| `${ContextKeys.WebviewPrefix}${WebviewIds}:focus`
12+
| `${ContextKeys.WebviewPrefix}${WebviewIds}:inputFocus`
13+
| `${ContextKeys.WebviewPrefix}rebaseEditor:active`
14+
| `${ContextKeys.WebviewPrefix}rebaseEditor:focus`
15+
| `${ContextKeys.WebviewPrefix}rebaseEditor:inputFocus`;
1116

1217
type WebviewViewContextKeys =
13-
| `${ContextKeys.WebviewViewPrefix}${string}:focus`
14-
| `${ContextKeys.WebviewViewPrefix}${string}:inputFocus`;
18+
| `${ContextKeys.WebviewViewPrefix}${WebviewViewIds}:focus`
19+
| `${ContextKeys.WebviewViewPrefix}${WebviewViewIds}:inputFocus`;
1520

1621
type AllContextKeys =
1722
| ContextKeys
@@ -23,9 +28,9 @@ type AllContextKeys =
2328
const _onDidChangeContext = new EventEmitter<AllContextKeys>();
2429
export const onDidChangeContext = _onDidChangeContext.event;
2530

26-
export function getContext<T>(key: ContextKeys): T | undefined;
27-
export function getContext<T>(key: ContextKeys, defaultValue: T): T;
28-
export function getContext<T>(key: ContextKeys, defaultValue?: T): T | undefined {
31+
export function getContext<T>(key: AllContextKeys): T | undefined;
32+
export function getContext<T>(key: AllContextKeys, defaultValue: T): T;
33+
export function getContext<T>(key: AllContextKeys, defaultValue?: T): T | undefined {
2934
return (contextStorage.get(key) as T | undefined) ?? defaultValue;
3035
}
3136

src/plus/webviews/graph/graphWebview.ts

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,10 @@ export class GraphWebview extends WebviewBase<State> {
182182
return this._selection;
183183
}
184184

185+
get activeSelection(): GitRevisionReference | undefined {
186+
return this._selection?.[0];
187+
}
188+
185189
private _etagSubscription?: number;
186190
private _etagRepository?: number;
187191
private _firstSelection = true;
@@ -429,23 +433,55 @@ export class GraphWebview extends WebviewBase<State> {
429433
}
430434

431435
protected override onFocusChanged(focused: boolean): void {
432-
if (focused && this.selection != null) {
433-
void GitActions.Commit.showDetailsView(this.selection[0], {
434-
pin: false,
435-
preserveFocus: true,
436-
preserveVisibility: this._showDetailsView === false,
437-
});
436+
if (!focused || this.activeSelection == null) {
437+
this._showActiveSelectionDetailsDebounced?.cancel();
438+
return;
438439
}
440+
441+
this.showActiveSelectionDetails();
442+
}
443+
444+
private _showActiveSelectionDetailsDebounced: Deferrable<GraphWebview['showActiveSelectionDetails']> | undefined =
445+
undefined;
446+
447+
private showActiveSelectionDetails() {
448+
if (this._showActiveSelectionDetailsDebounced == null) {
449+
this._showActiveSelectionDetailsDebounced = debounce(this.showActiveSelectionDetailsCore.bind(this), 250);
450+
}
451+
452+
this._showActiveSelectionDetailsDebounced();
453+
}
454+
455+
private showActiveSelectionDetailsCore() {
456+
const { activeSelection } = this;
457+
if (activeSelection == null) return;
458+
459+
void GitActions.Commit.showDetailsView(activeSelection, {
460+
pin: false,
461+
preserveFocus: true,
462+
preserveVisibility: this._showDetailsView === false,
463+
});
439464
}
440465

441466
protected override onVisibilityChanged(visible: boolean): void {
467+
if (!visible) {
468+
this._showActiveSelectionDetailsDebounced?.cancel();
469+
}
470+
442471
if (visible && this.repository != null && this.repository.etag !== this._etagRepository) {
443472
this.updateState(true);
444473
return;
445474
}
446475

447-
if (this.isReady && visible) {
448-
this.sendPendingIpcNotifications();
476+
if (visible) {
477+
if (this.isReady) {
478+
this.sendPendingIpcNotifications();
479+
}
480+
481+
const { activeSelection } = this;
482+
if (activeSelection == null) return;
483+
484+
this.showActiveSelectionDetails();
449485
}
450486
}
451487

@@ -1944,7 +1980,7 @@ export class GraphWebview extends WebviewBase<State> {
19441980
refType?: 'revision' | 'stash',
19451981
): GitReference | undefined {
19461982
if (item == null) {
1947-
const ref = this.selection?.[0];
1983+
const ref = this.activeSelection;
19481984
return ref != null && (refType == null || refType === ref.refType) ? ref : undefined;
19491985
}
19501986

src/webviews/commitDetails/commitDetailsWebviewView.ts

Lines changed: 45 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import { Logger } from '../../logger';
2828
import type { ShowInCommitGraphCommandArgs } from '../../plus/webviews/graph/graphWebview';
2929
import { executeCommand, executeCoreCommand } from '../../system/command';
3030
import type { DateTimeFormat } from '../../system/date';
31-
import { debug, getLogScope } from '../../system/decorators/log';
31+
import { debug, getLogScope, log } from '../../system/decorators/log';
3232
import type { Deferrable } from '../../system/function';
3333
import { debounce } from '../../system/function';
3434
import { map, union } from '../../system/iterable';
@@ -116,6 +116,12 @@ export class CommitDetailsWebviewView extends WebviewViewBase<State, Serialized<
116116
);
117117
}
118118

119+
@log<CommitDetailsWebviewView['show']>({
120+
args: {
121+
0: o =>
122+
`{"commit":${o?.commit?.ref},"pin":${o?.pin},"preserveFocus":${o?.preserveFocus},"preserveVisibility":${o?.preserveVisibility}}`,
123+
},
124+
})
119125
override async show(options?: {
120126
commit?: GitRevisionReference | GitCommit;
121127
pin?: boolean;
@@ -255,7 +261,7 @@ export class CommitDetailsWebviewView extends WebviewViewBase<State, Serialized<
255261
stashesView.onDidChangeVisibility(this.onStashesViewVisibilityChanged, this),
256262
);
257263

258-
const commit = this.getBestCommitOrStash();
264+
const commit = this._pendingContext?.commit ?? this.getBestCommitOrStash();
259265
this.updateCommit(commit, { immediate: false });
260266
}
261267

@@ -333,8 +339,18 @@ export class CommitDetailsWebviewView extends WebviewViewBase<State, Serialized<
333339
private onActiveLinesChanged(e: LinesChangeEvent) {
334340
if (e.pending) return;
335341

336-
const commit =
337-
e.selections != null ? this.container.lineTracker.getState(e.selections[0].active)?.commit : undefined;
342+
let commit;
343+
if (e.editor == null) {
344+
if (getContext('gitlens:webview:graph:active') || getContext('gitlens:webview:rebaseEditor:active')) {
345+
commit = this._pendingContext?.commit ?? this._context.commit;
346+
if (commit == null) return;
347+
}
348+
}
349+
350+
if (commit == null) {
351+
commit =
352+
e.selections != null ? this.container.lineTracker.getState(e.selections[0].active)?.commit : undefined;
353+
}
338354
this.updateCommit(commit);
339355
}
340356

@@ -672,31 +688,35 @@ export class CommitDetailsWebviewView extends WebviewViewBase<State, Serialized<
672688

673689
let commit;
674690

675-
const { lineTracker } = this.container;
676-
const line = lineTracker.selections?.[0].active;
677-
if (line != null) {
678-
commit = lineTracker.getState(line)?.commit;
679-
}
691+
if (window.activeTextEditor == null) {
692+
if (getContext('gitlens:webview:graph:active') || getContext('gitlens:webview:rebaseEditor:active')) {
693+
commit = this._pendingContext?.commit ?? this._context.commit;
694+
if (commit != null) return commit;
695+
}
680696

681-
if (commit == null) {
682-
const { commitsView } = this.container;
683-
const node = commitsView.activeSelection;
684-
if (
685-
node != null &&
686-
(node instanceof CommitNode ||
687-
node instanceof FileRevisionAsCommitNode ||
688-
node instanceof CommitFileNode)
689-
) {
690-
commit = node.commit;
697+
const { lineTracker } = this.container;
698+
const line = lineTracker.selections?.[0].active;
699+
if (line != null) {
700+
commit = lineTracker.getState(line)?.commit;
701+
if (commit != null) return commit;
691702
}
692703
}
693704

694-
if (commit == null) {
695-
const { stashesView } = this.container;
696-
const node = stashesView.activeSelection;
697-
if (node != null && (node instanceof StashNode || node instanceof StashFileNode)) {
698-
commit = node.commit;
699-
}
705+
const { commitsView } = this.container;
706+
let node = commitsView.activeSelection;
707+
if (
708+
node != null &&
709+
(node instanceof CommitNode || node instanceof FileRevisionAsCommitNode || node instanceof CommitFileNode)
710+
) {
711+
commit = node.commit;
712+
if (commit != null) return commit;
713+
}
714+
715+
const { stashesView } = this.container;
716+
node = stashesView.activeSelection;
717+
if (node != null && (node instanceof StashNode || node instanceof StashFileNode)) {
718+
commit = node.commit;
719+
if (commit != null) return commit;
700720
}
701721

702722
return commit;

src/webviews/rebase/rebaseEditor.ts

Lines changed: 74 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
1-
import type { CancellationToken, CustomTextEditorProvider, TextDocument, WebviewPanel } from 'vscode';
1+
import type {
2+
CancellationToken,
3+
CustomTextEditorProvider,
4+
TextDocument,
5+
WebviewPanel,
6+
WebviewPanelOnDidChangeViewStateEvent,
7+
} from 'vscode';
28
import { ConfigurationTarget, Disposable, Position, Range, Uri, window, workspace, WorkspaceEdit } from 'vscode';
39
import { getNonce } from '@env/crypto';
410
import { ShowCommitsInViewCommand } from '../../commands';
511
import { GitActions } from '../../commands/gitCommands.actions';
612
import { configuration } from '../../configuration';
7-
import { CoreCommands } from '../../constants';
13+
import { ContextKeys, CoreCommands } from '../../constants';
814
import type { Container } from '../../container';
15+
import { setContext } from '../../context';
916
import { emojify } from '../../emojis';
1017
import type { GitCommit } from '../../git/models/commit';
1118
import { GitReference } from '../../git/models/reference';
@@ -18,8 +25,8 @@ import type { Deferrable } from '../../system/function';
1825
import { debounce } from '../../system/function';
1926
import { join, map } from '../../system/iterable';
2027
import { normalizePath } from '../../system/path';
21-
import type { IpcMessage } from '../protocol';
22-
import { onIpc } from '../protocol';
28+
import type { IpcMessage, WebviewFocusChangedParams } from '../protocol';
29+
import { onIpc, WebviewFocusChangedCommandType } from '../protocol';
2330
import type {
2431
Author,
2532
ChangeEntryParams,
@@ -125,6 +132,10 @@ export class RebaseEditorProvider implements CustomTextEditorProvider, Disposabl
125132
this._disposable.dispose();
126133
}
127134

135+
private get contextKeyPrefix() {
136+
return `${ContextKeys.WebviewPrefix}rebaseEditor` as const;
137+
}
138+
128139
get enabled(): boolean {
129140
const associations = configuration.inspectAny<
130141
{ [key: string]: string } | { viewType: string; filenamePattern: string }[]
@@ -198,13 +209,11 @@ export class RebaseEditorProvider implements CustomTextEditorProvider, Disposabl
198209

199210
subscriptions.push(
200211
panel.onDidDispose(() => {
201-
Disposable.from(...subscriptions).dispose();
202-
}),
203-
panel.onDidChangeViewState(() => {
204-
if (!context.pendingChange) return;
212+
this.resetContextKeys();
205213

206-
this.updateState(context);
214+
Disposable.from(...subscriptions).dispose();
207215
}),
216+
panel.onDidChangeViewState(e => this.onViewStateChanged(context, e)),
208217
panel.webview.onDidReceiveMessage(e => this.onMessageReceived(context, e)),
209218
workspace.onDidChangeTextDocument(e => {
210219
if (e.contentChanges.length === 0 || e.document.uri.toString() !== document.uri.toString()) return;
@@ -237,6 +246,55 @@ export class RebaseEditorProvider implements CustomTextEditorProvider, Disposabl
237246
}
238247
}
239248

249+
private resetContextKeys(): void {
250+
void setContext(`${this.contextKeyPrefix}:inputFocus`, false);
251+
void setContext(`${this.contextKeyPrefix}:focus`, false);
252+
void setContext(`${this.contextKeyPrefix}:active`, false);
253+
}
254+
255+
private setContextKeys(active: boolean | undefined, focus?: boolean, inputFocus?: boolean): void {
256+
if (active != null) {
257+
void setContext(`${this.contextKeyPrefix}:active`, active);
258+
259+
if (!active) {
260+
focus = false;
261+
inputFocus = false;
262+
}
263+
}
264+
if (focus != null) {
265+
void setContext(`${this.contextKeyPrefix}:focus`, focus);
266+
}
267+
if (inputFocus != null) {
268+
void setContext(`${this.contextKeyPrefix}:inputFocus`, inputFocus);
269+
}
270+
}
271+
272+
@debug<RebaseEditorProvider['onViewFocusChanged']>({
273+
args: { 0: e => `focused=${e.focused}, inputFocused=${e.inputFocused}` },
274+
})
275+
protected onViewFocusChanged(e: WebviewFocusChangedParams): void {
276+
this.setContextKeys(e.focused, e.focused, e.inputFocused);
277+
}
278+
279+
@debug<RebaseEditorProvider['onViewStateChanged']>({
280+
args: {
281+
0: c => `${c.id}:${c.document.uri.toString(true)}`,
282+
1: e => `active=${e.webviewPanel.active}, visible=${e.webviewPanel.visible}`,
283+
},
284+
})
285+
protected onViewStateChanged(context: RebaseEditorContext, e: WebviewPanelOnDidChangeViewStateEvent): void {
286+
const { active, visible } = e.webviewPanel;
287+
if (visible) {
288+
this.setContextKeys(active);
289+
} else {
290+
this.resetContextKeys();
291+
}
292+
293+
if (!context.pendingChange) return;
294+
295+
this.updateState(context);
296+
}
297+
240298
private async parseState(context: RebaseEditorContext): Promise<State> {
241299
if (context.branchName === undefined) {
242300
const branch = await this.container.git.getBranch(context.repoPath);
@@ -268,6 +326,13 @@ export class RebaseEditorProvider implements CustomTextEditorProvider, Disposabl
268326

269327
// break;
270328

329+
case WebviewFocusChangedCommandType.method:
330+
onIpc(WebviewFocusChangedCommandType, e, params => {
331+
this.onViewFocusChanged(params);
332+
});
333+
334+
break;
335+
271336
case AbortCommandType.method:
272337
onIpc(AbortCommandType, e, () => this.abort(context));
273338

0 commit comments

Comments
 (0)