Browse Source

Adds a navigation stack to the Commit Details

main
Eric Amodio 1 year ago
parent
commit
520558a6c0
6 changed files with 237 additions and 51 deletions
  1. +51
    -0
      src/system/mru.ts
  2. +70
    -48
      src/webviews/apps/commitDetails/commitDetails.html
  3. +30
    -1
      src/webviews/apps/commitDetails/commitDetails.scss
  4. +43
    -1
      src/webviews/apps/commitDetails/commitDetails.ts
  5. +34
    -1
      src/webviews/commitDetails/commitDetailsWebview.ts
  6. +9
    -0
      src/webviews/commitDetails/protocol.ts

+ 51
- 0
src/system/mru.ts View File

@ -0,0 +1,51 @@
export class MRU<T> {
private stack: T[] = [];
constructor(public readonly maxSize: number = 10, private readonly compare?: (a: T, b: T) => boolean) {}
get count(): number {
return this.stack.length;
}
private _position: number = 0;
get position(): number {
return this._position;
}
add(item: T): void {
const index =
this.compare != null ? this.stack.findIndex(i => this.compare!(item, i)) : this.stack.indexOf(item);
if (this._position != null && this._position > 0) {
this.stack = this.stack.slice(0, this._position + 1);
this._position = 0;
}
if (index !== -1) {
this.stack.splice(index, 1);
} else if (this.stack.length === this.maxSize) {
this.stack.pop();
}
this.stack.unshift(item);
this._position = 0;
}
get(): T | undefined {
return this.stack.length > 0 ? this.stack[0] : undefined;
}
navigate(direction: 'back' | 'forward'): T | undefined {
if (this.stack.length <= 1) return undefined;
if (direction === 'back') {
if (this._position >= this.stack.length - 1) return undefined;
this._position += 1;
} else {
if (this._position <= 0) return undefined;
this._position -= 1;
}
return this.stack[this._position];
}
}

+ 70
- 48
src/webviews/apps/commitDetails/commitDetails.html View File

@ -97,59 +97,81 @@
</div>
<div class="commit-details__top">
<div class="commit-details__top-menu">
<div class="commit-details__top-actions">
<div class="commit-details__top-actions-left">
<a
class="commit-details__commit-action"
href="#"
data-action="back"
aria-label="Back"
title="Back"
><code-icon icon="arrow-left" data-region="commit-back"></code-icon
></a>
<a
class="commit-details__commit-action"
href="#"
data-action="forward"
aria-label="Forward"
title="Forward"
><code-icon icon="arrow-right" data-region="commit-forward"></code-icon
></a>
<a
class="commit-details__commit-action"
href="#"
data-action="pin"
aria-label="Pin this Commit"
title="Pin this Commit"
><code-icon icon="pin" data-region="commit-pin"></code-icon
></a>
</div>
<div class="commit-details__top-actions-right">
<a
class="commit-details__commit-action"
href="#"
data-action="commit-actions"
data-action-type="sha"
aria-label="Copy SHA
[⌥] Pick Commit..."
title="Copy SHA
[⌥] Pick Commit..."
>
<code-icon icon="git-commit"></code-icon>
<span class="commit-details__sha" data-region="shortsha"></span
></a>
<a
class="commit-details__commit-action"
href="#"
data-action="commit-actions"
data-action-type="scm"
aria-label="Open SCM view"
title="Open SCM view"
><code-icon icon="source-control"></code-icon
></a>
<a
class="commit-details__commit-action"
href="#"
data-action="commit-actions"
data-action-type="graph"
aria-label="Open in Commit Graph"
title="Open in Commit Graph"
><code-icon icon="gl-graph"></code-icon
></a>
<a
class="commit-details__commit-action"
href="#"
data-action="commit-actions"
data-action-type="more"
aria-label="Show Commit Actions"
title="Show Commit Actions"
><code-icon icon="kebab-vertical"></code-icon
></a>
</div>
</div>
<ul class="commit-details__authors" aria-label="Authors">
<li class="commit-details__author" data-region="author">
<skeleton-loader></skeleton-loader>
</li>
</ul>
<a
class="commit-details__commit-action"
href="#"
data-action="pin"
aria-label="Pin Commit to View"
title="Pin Commit to View"
><code-icon icon="pin" data-region="commit-pin"></code-icon
></a>
<a
class="commit-details__commit-action"
href="#"
data-action="commit-actions"
data-action-type="sha"
aria-label="Copy SHA
[⌥] Pick Commit..."
title="Copy SHA
[⌥] Pick Commit..."
>
<code-icon icon="git-commit"></code-icon>
<span class="commit-details__sha" data-region="shortsha"></span
></a>
<a
class="commit-details__commit-action"
href="#"
data-action="commit-actions"
data-action-type="scm"
aria-label="Open SCM view"
title="Open SCM view"
><code-icon icon="source-control"></code-icon
></a>
<a
class="commit-details__commit-action"
href="#"
data-action="commit-actions"
data-action-type="graph"
aria-label="Open in Commit Graph"
title="Open in Commit Graph"
><code-icon icon="gl-graph"></code-icon
></a>
<a
class="commit-details__commit-action"
href="#"
data-action="commit-actions"
data-action-type="more"
aria-label="Show Commit Actions"
title="Show Commit Actions"
><code-icon icon="kebab-vertical"></code-icon
></a>
</div>
</div>
<div class="commit-details__message">

+ 30
- 1
src/webviews/apps/commitDetails/commitDetails.scss View File

@ -348,7 +348,7 @@ ul {
top: 0;
z-index: 1;
padding: {
top: 1rem;
top: 0;
left: var(--gitlens-gutter-width);
right: var(--gitlens-scrollbar-gutter-width);
bottom: 0.5rem;
@ -358,9 +358,28 @@ ul {
&-menu {
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: flex-start;
justify-content: space-between;
}
&-actions {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: space-between;
flex: 100% 0 0;
&-left {
display: flex;
flex: 100% 2 1;
}
&-right {
display: flex;
flex: auto 0 0;
}
}
}
&__message {
@ -431,6 +450,15 @@ ul {
background-color: var(--color-background--darken-10);
}
}
&.is-disabled {
opacity: 0.5;
pointer-events: none;
}
&.is-hidden {
display: none;
}
}
&__sha {
@ -439,6 +467,7 @@ ul {
&__authors {
flex-basis: 100%;
padding-top: 0.5rem;
}
&__author {
& + & {

+ 43
- 1
src/webviews/apps/commitDetails/commitDetails.ts View File

@ -10,6 +10,7 @@ import {
DidChangeNotificationType,
FileActionsCommandType,
messageHeadlineSplitterToken,
NavigateCommitCommandType,
OpenFileCommandType,
OpenFileComparePreviousCommandType,
OpenFileCompareWorkingCommandType,
@ -78,6 +79,8 @@ export class CommitDetailsApp extends App> {
DOM.on('[data-action="autolink-settings"]', 'click', e => this.onAutolinkSettings(e)),
DOM.on('[data-switch-value]', 'click', e => this.onToggleFilesLayout(e)),
DOM.on('[data-action="pin"]', 'click', e => this.onTogglePin(e)),
DOM.on('[data-action="back"]', 'click', e => this.onNavigate('back', e)),
DOM.on('[data-action="forward"]', 'click', e => this.onNavigate('forward', e)),
DOM.on<WebviewPane, WebviewPaneExpandedChangeEventDetail>(
'[data-region="rich-pane"]',
'expanded-change',
@ -171,6 +174,11 @@ export class CommitDetailsApp extends App> {
this.sendCommand(PreferencesCommandType, { autolinksExpanded: e.expanded });
}
private onNavigate(direction: 'back' | 'forward', e: Event) {
e.preventDefault();
this.sendCommand(NavigateCommitCommandType, { direction: direction });
}
private onTogglePin(e: MouseEvent) {
e.preventDefault();
this.sendCommand(PinCommitCommandType, { pin: !this.state.pinned });
@ -244,6 +252,7 @@ export class CommitDetailsApp extends App> {
this.renderBanner(this.state);
this.renderActions(this.state);
this.renderNavigation(this.state);
this.renderPin(this.state);
this.renderSha(this.state);
this.renderMessage(this.state);
@ -277,11 +286,44 @@ export class CommitDetailsApp extends App> {
document.querySelector('[data-action-type="scm"]')?.setAttribute('aria-hidden', (!isUncommitted).toString());
}
renderNavigation(state: CommitState) {
const $back = document.querySelector<HTMLElement>('[data-action="back"]');
const $forward = document.querySelector<HTMLElement>('[data-action="forward"]');
if ($back == null || $forward == null) return;
if (state.navigationStack.count <= 1) {
$back.setAttribute('aria-disabled', 'true');
$back.classList.toggle('is-disabled', true);
$forward.setAttribute('aria-hidden', 'true');
$forward.classList.toggle('is-hidden', true);
} else if (state.navigationStack.position === 0) {
$back.setAttribute('aria-disabled', 'false');
$back.classList.toggle('is-disabled', false);
$forward.setAttribute('aria-hidden', 'true');
$forward.classList.toggle('is-hidden', true);
} else if (state.navigationStack.position === state.navigationStack.count - 1) {
$back.setAttribute('aria-disabled', 'true');
$back.classList.toggle('is-disabled', true);
$forward.setAttribute('aria-hidden', 'false');
$forward.classList.toggle('is-hidden', false);
} else {
$back.setAttribute('aria-disabled', 'false');
$back.classList.toggle('is-disabled', false);
$forward.setAttribute('aria-hidden', 'false');
$forward.classList.toggle('is-hidden', false);
}
}
renderPin(state: CommitState) {
const $el = document.querySelector<HTMLElement>('[data-action="pin"]');
if ($el == null) return;
const label = state.pinned ? 'Unpin this Commit' : 'Pin this Commit';
const label = state.pinned
? 'Unpin this Commit\nRestores Automatic Following'
: 'Pin this Commit\nSuspends Automatic Following';
$el.setAttribute('aria-label', label);
$el.setAttribute('title', label);
$el.classList.toggle('is-active', state.pinned);

+ 34
- 1
src/webviews/commitDetails/commitDetailsWebview.ts View File

@ -36,6 +36,7 @@ import { debounce } from '../../system/function';
import { map, union } from '../../system/iterable';
import { Logger } from '../../system/logger';
import { getLogScope } from '../../system/logger.scope';
import { MRU } from '../../system/mru';
import type { PromiseCancelledError } from '../../system/promise';
import { getSettledValue } from '../../system/promise';
import type { Serialized } from '../../system/serialize';
@ -52,6 +53,7 @@ import {
DidChangeNotificationType,
FileActionsCommandType,
messageHeadlineSplitterToken,
NavigateCommitCommandType,
OpenFileCommandType,
OpenFileComparePreviousCommandType,
OpenFileCompareWorkingCommandType,
@ -75,6 +77,10 @@ interface Context {
dateFormat: DateTimeFormat | string;
// indent: number;
indentGuides: 'none' | 'onHover' | 'always';
navigationStack: {
count: number;
position: number;
};
}
export class CommitDetailsWebviewProvider implements WebviewProvider<State, Serialized<State>> {
@ -86,6 +92,7 @@ export class CommitDetailsWebviewProvider implements WebviewProvider
private readonly _disposable: Disposable;
private _pinned = false;
private _focused = false;
private _commitStack = new MRU<GitRevisionReference>(10, (a, b) => a.ref === b.ref);
constructor(
readonly container: Container,
@ -108,6 +115,10 @@ export class CommitDetailsWebviewProvider implements WebviewProvider
dateFormat: configuration.get('defaultDateFormat') ?? 'MMMM Do, YYYY h:mma',
// indent: configuration.getAny('workbench.tree.indent') ?? 8,
indentGuides: configuration.getAny('workbench.tree.renderIndentGuides') ?? 'onHover',
navigationStack: {
count: 0,
position: 0,
},
};
this._disposable = Disposable.from(
@ -341,12 +352,22 @@ export class CommitDetailsWebviewProvider implements WebviewProvider
case PinCommitCommandType.method:
onIpc(PinCommitCommandType, e, params => this.updatePinned(params.pin ?? false, true));
break;
case NavigateCommitCommandType.method:
onIpc(NavigateCommitCommandType, e, params => this.navigateStack(params.direction));
break;
case PreferencesCommandType.method:
onIpc(PreferencesCommandType, e, params => this.updatePreferences(params));
break;
}
}
private navigateStack(direction: 'back' | 'forward') {
const commit = this._commitStack.navigate(direction);
if (commit == null) return;
void this.updateCommit(commit, { immediate: true, skipStack: true });
}
private onActiveEditorLinesChanged(e: LinesChangeEvent) {
if (e.pending || e.editor == null) return;
@ -394,6 +415,7 @@ export class CommitDetailsWebviewProvider implements WebviewProvider
dateFormat: current.dateFormat,
// indent: current.indent,
indentGuides: current.indentGuides,
navigationStack: current.navigationStack,
});
return state;
}
@ -457,7 +479,7 @@ export class CommitDetailsWebviewProvider implements WebviewProvider
private async updateCommit(
commitish: GitCommit | GitRevisionReference | undefined,
options?: { force?: boolean; pinned?: boolean; immediate?: boolean },
options?: { force?: boolean; pinned?: boolean; immediate?: boolean; skipStack?: boolean },
) {
// this.commits = [commit];
if (!options?.force && this._context.commit?.sha === commitish?.ref) return;
@ -507,6 +529,17 @@ export class CommitDetailsWebviewProvider implements WebviewProvider
this.ensureTrackers();
}
if (commit != null) {
if (!options?.skipStack) {
this._commitStack.add(getReferenceFromRevision(commit));
}
this.updatePendingContext({
navigationStack: {
count: this._commitStack.count,
position: this._commitStack.position,
},
});
}
this.updateState(options?.immediate ?? true);
}

+ 9
- 0
src/webviews/commitDetails/protocol.ts View File

@ -48,6 +48,10 @@ export type State = {
dateFormat: string;
// indent: number;
indentGuides: 'none' | 'onHover' | 'always';
navigationStack: {
count: number;
position: number;
};
};
export type ShowCommitDetailsViewCommandArgs = string[];
@ -81,6 +85,11 @@ export interface PinParams {
}
export const PinCommitCommandType = new IpcCommandType<PinParams>('commit/pin');
export interface NavigateParams {
direction: 'back' | 'forward';
}
export const NavigateCommitCommandType = new IpcCommandType<NavigateParams>('commit/navigate');
export interface PreferenceParams {
autolinksExpanded?: boolean;
avatars?: boolean;

Loading…
Cancel
Save