From 61e616ce6e93fde3a5a5ff5abd5b02fdde89f805 Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Mon, 2 Nov 2020 00:53:28 -0500 Subject: [PATCH] Adds real-time preview of format strings --- src/webviews/apps/scss/popup.scss | 2 +- src/webviews/apps/settings/partials/blame.html | 9 +++ .../apps/settings/partials/current-line.html | 14 ++++ .../apps/settings/partials/status-bar.html | 9 +++ src/webviews/apps/settings/settings.html | 35 +++++++++- src/webviews/apps/shared/appWithConfigBase.ts | 73 +++++++++++++++++++-- src/webviews/apps/shared/dom.ts | 12 +++- src/webviews/protocol.ts | 21 ++++++ src/webviews/webviewBase.ts | 74 +++++++++++++++++++++- 9 files changed, 239 insertions(+), 10 deletions(-) diff --git a/src/webviews/apps/scss/popup.scss b/src/webviews/apps/scss/popup.scss index 077cbe8..9d34088 100644 --- a/src/webviews/apps/scss/popup.scss +++ b/src/webviews/apps/scss/popup.scss @@ -3,7 +3,7 @@ cursor: default; padding: 1em; position: absolute; - top: 46px; + top: 72px; width: 80vw; min-width: 373px; max-width: 472px; diff --git a/src/webviews/apps/settings/partials/blame.html b/src/webviews/apps/settings/partials/blame.html index 6a6354e..7f64151 100644 --- a/src/webviews/apps/settings/partials/blame.html +++ b/src/webviews/apps/settings/partials/blame.html @@ -58,6 +58,7 @@ type="text" placeholder="${message|50?} ${agoOrDate|14-}" data-setting + data-setting-preview data-default-value="${message|50?} ${agoOrDate|14-}" data-popup-trigger /> @@ -66,6 +67,14 @@ + Example: + +
diff --git a/src/webviews/apps/settings/partials/current-line.html b/src/webviews/apps/settings/partials/current-line.html index 69edbb2..4f08b82 100644 --- a/src/webviews/apps/settings/partials/current-line.html +++ b/src/webviews/apps/settings/partials/current-line.html @@ -66,6 +66,7 @@ type="text" placeholder="${author, }${agoOrDate}${' via 'pullRequest}${ • message|50?}" data-setting + data-setting-preview data-default-value="${author, }${agoOrDate}${' via 'pullRequest}${ • message|50?}" data-popup-trigger disabled @@ -75,6 +76,14 @@
+ Example: + +
@@ -106,6 +115,11 @@ /> + diff --git a/src/webviews/apps/settings/partials/status-bar.html b/src/webviews/apps/settings/partials/status-bar.html index d011822..c9ff86d 100644 --- a/src/webviews/apps/settings/partials/status-bar.html +++ b/src/webviews/apps/settings/partials/status-bar.html @@ -48,6 +48,7 @@ type="text" placeholder="${author}, ${agoOrDate}${' via 'pullRequest}" data-setting + data-setting-preview data-default-value="${author}, ${agoOrDate}${' via 'pullRequest}" data-popup-trigger disabled @@ -57,6 +58,14 @@
+ Example: + +
diff --git a/src/webviews/apps/settings/settings.html b/src/webviews/apps/settings/settings.html index 4be75ec..0b65ade 100644 --- a/src/webviews/apps/settings/settings.html +++ b/src/webviews/apps/settings/settings.html @@ -10,7 +10,7 @@ - + @@ -18,6 +18,10 @@ + + + + @@ -51,6 +55,15 @@ + + + + @@ -63,6 +76,10 @@ + + + + @@ -75,6 +92,22 @@ + + + + + + + + + + + + diff --git a/src/webviews/apps/shared/appWithConfigBase.ts b/src/webviews/apps/shared/appWithConfigBase.ts index 6a20bd4..925ef62 100644 --- a/src/webviews/apps/shared/appWithConfigBase.ts +++ b/src/webviews/apps/shared/appWithConfigBase.ts @@ -3,8 +3,10 @@ import { AppStateWithConfig, DidChangeConfigurationNotificationType, + DidPreviewConfigurationNotificationType, IpcMessage, onIpcNotification, + PreviewConfigurationCommandType, UpdateConfigurationCommandType, } from '../../protocol'; import { App } from './appBase'; @@ -13,6 +15,17 @@ import { getDateFormatter } from '../shared/date'; const dateFormatter = getDateFormatter(new Date('Wed Jul 25 2018 19:18:00 GMT-0400')); +let ipcSequence = 0; +function nextIpcId() { + if (ipcSequence === Number.MAX_SAFE_INTEGER) { + ipcSequence = 1; + } else { + ipcSequence++; + } + + return `${ipcSequence}`; +} + export abstract class AppWithConfig extends App { private _changes = Object.create(null) as Record; private _updating: boolean = false; @@ -213,8 +226,6 @@ export abstract class AppWithConfig extends A } protected onPopupMouseDown(element: HTMLElement, e: MouseEvent) { - // e.stopPropagation(); - // e.stopImmediatePropagation(); e.preventDefault(); const el = e.target as HTMLElement; @@ -234,7 +245,25 @@ export abstract class AppWithConfig extends A const input = setting.querySelector('input[type=text], input:not([type])'); if (input == null) return; - input.value += `\${${element.dataset.token}}`; + const token = `\${${element.dataset.token}}`; + let selectionStart = input.selectionStart; + if (selectionStart != null) { + input.value = `${input.value.substring(0, selectionStart)}${token}${input.value.substr( + input.selectionEnd ?? selectionStart, + )}`; + + selectionStart += token.length; + } else { + selectionStart = input.value.length; + } + + input.focus(); + input.setSelectionRange(selectionStart, selectionStart); + if (selectionStart === input.value.length) { + input.scrollLeft = input.scrollWidth; + } + + setTimeout(() => input.focus(), 250); e.stopPropagation(); e.stopImmediatePropagation(); @@ -363,7 +392,7 @@ export abstract class AppWithConfig extends A private updatePreview(el: HTMLSpanElement, value?: string) { switch (el.dataset.settingPreviewType) { - case 'date': + case 'date': { if (value === undefined) { value = this.getSettingValue(el.dataset.settingPreview!); } @@ -374,7 +403,43 @@ export abstract class AppWithConfig extends A el.innerText = value == null ? '' : dateFormatter.format(value); break; + } + case 'commit': { + if (value === undefined) { + value = this.getSettingValue(el.dataset.settingPreview!); + } + if (value == null || value.length === 0) { + value = el.dataset.settingPreviewDefault; + } + + if (value == null) { + el.innerText = ''; + + return; + } + + const id = nextIpcId(); + const disposable = DOM.on(window, 'message', (e: MessageEvent) => { + const msg = e.data as IpcMessage; + + if (msg.method === DidPreviewConfigurationNotificationType.method && msg.params.id === id) { + disposable.dispose(); + onIpcNotification(DidPreviewConfigurationNotificationType, msg, params => { + el.innerText = params.preview ?? ''; + }); + } + }); + + this.sendCommand(PreviewConfigurationCommandType, { + key: el.dataset.settingPreview!, + type: 'commit', + id: id, + format: value, + }); + + break; + } default: break; } diff --git a/src/webviews/apps/shared/dom.ts b/src/webviews/apps/shared/dom.ts index 93b1455..1a5998c 100644 --- a/src/webviews/apps/shared/dom.ts +++ b/src/webviews/apps/shared/dom.ts @@ -19,10 +19,16 @@ export namespace DOM { listener: (this: T, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions, ): Disposable; - export function on( - selectorOrElement: string | Document | Element, + export function on( + el: Window, name: K, - listener: (this: T, ev: DocumentEventMap[K]) => any, + listener: (this: T, ev: WindowEventMap[K]) => any, + options?: boolean | AddEventListenerOptions, + ): Disposable; + export function on( + selectorOrElement: string | Window | Document | Element, + name: K, + listener: (this: T, ev: (DocumentEventMap | WindowEventMap)[K]) => any, options?: boolean | AddEventListenerOptions, el?: Element, ): Disposable { diff --git a/src/webviews/protocol.ts b/src/webviews/protocol.ts index bc95021..6532c3a 100644 --- a/src/webviews/protocol.ts +++ b/src/webviews/protocol.ts @@ -56,6 +56,27 @@ export const UpdateConfigurationCommandType = new IpcCommandType( + 'configuration/preview', +); + +export interface DidPreviewConfigurationNotificationParams { + id: string; + preview: string; +} +export const DidPreviewConfigurationNotificationType = new IpcNotificationType< + DidPreviewConfigurationNotificationParams +>('configuration/didPreview'); + export interface SettingsDidRequestJumpToNotificationParams { anchor: string; } diff --git a/src/webviews/webviewBase.ts b/src/webviews/webviewBase.ts index e4b8d93..06846fa 100644 --- a/src/webviews/webviewBase.ts +++ b/src/webviews/webviewBase.ts @@ -13,18 +13,21 @@ import { window, workspace, } from 'vscode'; +import { Commands } from '../commands'; import { configuration } from '../configuration'; import { Container } from '../container'; +import { CommitFormatter, GitBlameCommit, PullRequest, PullRequestState } from '../git/git'; import { Logger } from '../logger'; import { DidChangeConfigurationNotificationType, + DidPreviewConfigurationNotificationType, IpcMessage, IpcNotificationParamsOf, IpcNotificationType, onIpcCommand, + PreviewConfigurationCommandType, UpdateConfigurationCommandType, } from './protocol'; -import { Commands } from '../commands'; let ipcSequence = 0; function nextIpcId() { @@ -131,6 +134,75 @@ export abstract class WebviewBase implements Disposable { }); break; + + case PreviewConfigurationCommandType.method: + onIpcCommand(PreviewConfigurationCommandType, e, async params => { + switch (params.type) { + case 'commit': { + const commit = new GitBlameCommit( + '~/code/eamodio/vscode-gitlens-demo', + 'fe26af408293cba5b4bfd77306e1ac9ff7ccaef8', + 'You', + 'eamodio@gmail.com', + new Date('2016-11-12T20:41:00.000Z'), + new Date('2020-11-01T06:57:21.000Z'), + 'Supercharged', + 'code.ts', + undefined, + '3ac1d3f51d7cf5f438cc69f25f6740536ad80fef', + 'code.ts', + [], + ); + + let includePullRequest = false; + switch (params.key) { + case configuration.name('currentLine', 'format'): + includePullRequest = Container.config.currentLine.pullRequests.enabled; + break; + case configuration.name('statusBar', 'format'): + includePullRequest = Container.config.statusBar.pullRequests.enabled; + break; + } + + let pr: PullRequest | undefined; + if (includePullRequest) { + pr = new PullRequest( + 'GitHub', + { + name: 'Eric Amodio', + avatarUrl: 'https://avatars1.githubusercontent.com/u/641685?s=32&v=4', + url: 'https://github.com/eamodio', + }, + 1, + 'Supercharged', + 'https://github.com/eamodio/vscode-gitlens/pulls/1', + PullRequestState.Merged, + new Date('Sat, 12 Nov 2016 19:41:00 GMT'), + undefined, + new Date('Sat, 12 Nov 2016 20:41:00 GMT'), + ); + } + + let preview; + try { + preview = CommitFormatter.fromTemplate(params.format, commit, { + dateFormat: Container.config.defaultDateFormat, + pullRequestOrRemote: pr, + messageTruncateAtNewLine: true, + }); + } catch { + preview = 'Invalid format'; + } + + await this.notify(DidPreviewConfigurationNotificationType, { + id: params.id, + preview: preview, + }); + } + } + }); + break; + default: break; }
Commit IdCommit SHA id
author
Commit Author (except you)authorNotYou
Commit Author E-mail email
agoOrDate
+ Commit or Authored Date (Short)
Short relative or absolute based on date setting
Committed vs Authored based on + setting
+
agoOrDateShort
Authored Date
Relative date
authorAgo
authorAgoOrDate
Authored Date (Short)
Short relative or absolute date based on date setting
authorAgoOrDateShort
Commit Date
Relative date
committerAgo
committerAgoOrDate
Commit Date (Short)
Short relative or absolute date based on date setting
committerAgoOrDateShort
Pull Request
Pull request (if any) that introduced the commit
pullRequest
+ Pull Request State
State (open, merged, closed) of the pull request (if any) that introduced the commit +
pullRequestState
Branch & Tag Tips
Indicates if the commit is a tip of any branches or tags
tips