Browse Source

Improves auto-linking & pr association

main
Eric Amodio 4 years ago
parent
commit
ecd5dd8a3e
16 changed files with 245 additions and 141 deletions
  1. +1
    -1
      package.json
  2. +1
    -1
      src/annotations/annotations.ts
  3. +22
    -16
      src/annotations/autolinks.ts
  4. +3
    -7
      src/annotations/lineAnnotationController.ts
  5. +1
    -0
      src/constants.ts
  6. +60
    -28
      src/git/formatters/commitFormatter.ts
  7. +19
    -0
      src/git/formatters/formatter.ts
  8. +44
    -2
      src/git/gitService.ts
  9. +1
    -1
      src/git/models/commit.ts
  10. +6
    -0
      src/git/models/repository.ts
  11. +5
    -4
      src/hovers/hovers.ts
  12. +2
    -2
      src/statusbar/statusBarController.ts
  13. +6
    -0
      src/system/string.ts
  14. +42
    -51
      src/views/nodes/commitFileNode.ts
  15. +27
    -24
      src/views/nodes/commitNode.ts
  16. +5
    -4
      src/views/nodes/stashNode.ts

+ 1
- 1
package.json View File

@ -6600,7 +6600,7 @@
},
{
"command": "gitlens.copyShaToClipboard",
"when": "viewItem =~ /gitlens:file\\b(?=.*?\\b\\+committed\\b)\\b/",
"when": "viewItem =~ /gitlens:file\\b(?=.*?\\b\\+committed\\b)/",
"group": "inline@98",
"alt": "gitlens.copyMessageToClipboard"
},

+ 1
- 1
src/annotations/annotations.ts View File

@ -238,7 +238,7 @@ export class Annotations {
const message = CommitFormatter.fromTemplate(format, commit, {
...formatOptions,
// previousLineDiffUris: diffUris,
truncateMessageAtNewLine: true,
messageTruncateAtNewLine: true,
});
return {

+ 22
- 16
src/annotations/autolinks.ts View File

@ -9,16 +9,14 @@ import { GlyphChars } from '../constants';
const numRegex = /<num>/g;
const superscripts = ['\u00B9', '\u00B2', '\u00B3', '\u2074', '\u2075', '\u2076', '\u2077', '\u2078', '\u2079'];
export interface CacheableAutolinkReference extends AutolinkReference {
linkify?: ((text: string, markdown: boolean) => string) | null;
linkify?: ((text: string, markdown: boolean, footnotes?: Map<number, string>) => string) | null;
messageMarkdownRegex?: RegExp;
messageRegex?: RegExp;
}
export interface DynamicAutolinkReference {
linkify: (text: string, markdown: boolean) => string;
linkify: (text: string, markdown: boolean, footnotes?: Map<number, string>) => string;
}
function isDynamic(ref: AutolinkReference | DynamicAutolinkReference): ref is DynamicAutolinkReference {
@ -115,11 +113,12 @@ export class Autolinks implements Disposable {
markdown: boolean,
remotes?: GitRemote[],
issuesOrPullRequests?: Map<string, IssueOrPullRequest | Promises.CancellationError | undefined>,
footnotes?: Map<number, string>,
) {
for (const ref of this._references) {
if (this.ensureAutolinkCached(ref, issuesOrPullRequests)) {
if (ref.linkify != null) {
text = ref.linkify(text, markdown);
text = ref.linkify(text, markdown, footnotes);
}
}
}
@ -131,7 +130,7 @@ export class Autolinks implements Disposable {
for (const ref of r.provider.autolinks) {
if (this.ensureAutolinkCached(ref, issuesOrPullRequests)) {
if (ref.linkify != null) {
text = ref.linkify(text, markdown);
text = ref.linkify(text, markdown, footnotes);
}
}
}
@ -162,12 +161,12 @@ export class Autolinks implements Disposable {
ref.title ? ` "${ref.title.replace(numRegex, '$2')}"` : ''
})`;
ref.linkify = (text: string, markdown: boolean) =>
!markdown ? text : text.replace(ref.messageMarkdownRegex!, replacement);
markdown ? text.replace(ref.messageMarkdownRegex!, replacement) : text;
return true;
}
ref.linkify = (text: string, markdown: boolean) => {
ref.linkify = (text: string, markdown: boolean, footnotes?: Map<number, string>) => {
if (markdown) {
return text.replace(ref.messageMarkdownRegex!, (_substring, linkText, num) => {
const issue = issuesOrPullRequests?.get(num);
@ -195,19 +194,21 @@ export class Autolinks implements Disposable {
});
}
let footnotes: string[] | undefined;
let superscript;
const includeFootnotes = footnotes == null;
let index;
text = text.replace(ref.messageRegex!, (_substring, linkText, num) => {
const issue = issuesOrPullRequests?.get(num);
if (issue == null) return linkText;
if (footnotes === undefined) {
footnotes = [];
footnotes = new Map<number, string>();
}
superscript = superscripts[footnotes.length];
footnotes.push(
`${superscript} ${
index = footnotes.size + 1;
footnotes.set(
footnotes.size + 1,
`${linkText}: ${
issue instanceof Promises.CancellationError
? 'Details timed out'
: `${issue.title} ${GlyphChars.Dot} ${
@ -215,10 +216,15 @@ export class Autolinks implements Disposable {
}, ${Dates.getFormatter(issue.closedDate ?? issue.date).fromNow()}`
}`,
);
return `${linkText}${superscript}`;
return `${linkText}${Strings.getSuperscript(index)}`;
});
return footnotes == null || footnotes.length === 0 ? text : `${text}\n\n${footnotes.join('\n')}`;
return includeFootnotes && footnotes != null && footnotes.size !== 0
? `${text}\n${GlyphChars.Dash.repeat(2)}\n${Iterables.join(
Iterables.map(footnotes, ([i, footnote]) => `${Strings.getSuperscript(i)} ${footnote}`),
'\n',
)}`
: text;
};
} catch (ex) {
Logger.error(

+ 3
- 7
src/annotations/lineAnnotationController.ts View File

@ -148,13 +148,8 @@ export class LineAnnotationController implements Disposable {
) {
if (lines.length === 0) return undefined;
const remotes = await Container.git.getRemotes(repoPath);
const remote = remotes.find(r => r.default);
if (!remote?.provider?.hasApi()) return undefined;
const { provider } = remote;
const connected = provider.maybeConnected ?? (await provider.isConnected());
if (!connected) return undefined;
const remote = await Container.git.getRemoteWithApiProvider(repoPath);
if (remote?.provider == null) return undefined;
const refs = new Set<string>();
@ -164,6 +159,7 @@ export class LineAnnotationController implements Disposable {
if (refs.size === 0) return undefined;
const { provider } = remote;
const prs = await Promises.raceAll(
refs.values(),
ref => Container.git.getPullRequestForCommit(ref, provider),

+ 1
- 0
src/constants.ts View File

@ -37,6 +37,7 @@ export enum CommandContext {
Disabled = 'gitlens:disabled',
Enabled = 'gitlens:enabled',
HasRemotes = 'gitlens:hasRemotes',
HasConnectedRemotes = 'gitlens:hasConnectedRemotes',
Key = 'gitlens:key',
Readonly = 'gitlens:readonly',
ViewsCanCompare = 'gitlens:views:canCompare',

+ 60
- 28
src/git/formatters/commitFormatter.ts View File

@ -1,4 +1,5 @@
'use strict';
import { getPresenceDataUri } from '../../avatars';
import {
ConnectRemoteProviderCommand,
DiffWithCommand,
@ -11,6 +12,8 @@ import {
import { DateStyle, FileAnnotationType } from '../../configuration';
import { GlyphChars } from '../../constants';
import { Container } from '../../container';
import { emojify } from '../../emojis';
import { FormatOptions, Formatter } from './formatter';
import {
GitCommit,
GitLogCommit,
@ -21,27 +24,25 @@ import {
RemoteProvider,
} from '../git';
import { GitUri } from '../gitUri';
import { Promises, Strings } from '../../system';
import { FormatOptions, Formatter } from './formatter';
import { Iterables, Promises, Strings } from '../../system';
import { ContactPresence } from '../../vsls/vsls';
import { getPresenceDataUri } from '../../avatars';
import { emojify } from '../../emojis';
const emptyStr = '';
const hasTokenRegexMap = new Map<string, RegExp>();
export interface CommitFormatOptions extends FormatOptions {
autolinkedIssuesOrPullRequests?: Map<string, IssueOrPullRequest | Promises.CancellationError | undefined>;
dateStyle?: DateStyle;
footnotes?: Map<number, string>;
getBranchAndTagTips?: (sha: string) => string | undefined;
line?: number;
markdown?: boolean;
messageAutolinks?: boolean;
messageIndent?: number;
messageTruncateAtNewLine?: boolean;
pullRequestOrRemote?: PullRequest | Promises.CancellationError | GitRemote;
presence?: ContactPresence;
previousLineDiffUris?: { current: GitUri; previous: GitUri | undefined };
remotes?: GitRemote<RemoteProvider>[];
truncateMessageAtNewLine?: boolean;
tokenOptions?: {
ago?: Strings.TokenOptions;
@ -57,6 +58,7 @@ export interface CommitFormatOptions extends FormatOptions {
committerDate?: Strings.TokenOptions;
date?: Strings.TokenOptions;
email?: Strings.TokenOptions;
footnotes?: Strings.TokenOptions;
id?: Strings.TokenOptions;
message?: Strings.TokenOptions;
pullRequest?: Strings.TokenOptions;
@ -325,6 +327,18 @@ export class CommitFormatter extends Formatter {
return this._padOrTruncate(this._item.email ?? emptyStr, this._options.tokenOptions.email);
}
get footnotes() {
if (this._options.footnotes == null || this._options.footnotes.size === 0) return '';
return this._padOrTruncate(
Iterables.join(
Iterables.map(this._options.footnotes, ([i, footnote]) => `${Strings.getSuperscript(i)} ${footnote}`),
'\n',
),
this._options.tokenOptions.footnotes,
);
}
get id() {
return this._padOrTruncate(this._item.shortSha ?? emptyStr, this._options.tokenOptions.id);
}
@ -332,7 +346,8 @@ export class CommitFormatter extends Formatter {
get message() {
if (this._item.isUncommitted) {
const staged =
this._item.isUncommittedStaged || this._options.previousLineDiffUris?.current?.isUncommittedStaged;
this._item.isUncommittedStaged ||
(this._options.previousLineDiffUris?.current?.isUncommittedStaged ?? false);
return this._padOrTruncate(
`${this._options.markdown ? '\n> ' : ''}${staged ? 'Staged' : 'Uncommitted'} changes`,
@ -341,7 +356,7 @@ export class CommitFormatter extends Formatter {
}
let message = this._item.message;
if (this._options.truncateMessageAtNewLine) {
if (this._options.messageTruncateAtNewLine) {
const index = message.indexOf('\n');
if (index !== -1) {
message = `${message.substring(0, index)}${GlyphChars.Space}${GlyphChars.Ellipsis}`;
@ -351,15 +366,20 @@ export class CommitFormatter extends Formatter {
message = emojify(message);
message = this._padOrTruncate(message, this._options.tokenOptions.message);
if (Container.config.hovers.autolinks.enabled) {
if (this._options.messageAutolinks) {
message = Container.autolinks.linkify(
this._options.markdown ? Strings.escapeMarkdown(message, { quoted: true }) : message,
this._options.markdown ?? false,
this._options.remotes,
this._options.autolinkedIssuesOrPullRequests,
this._options.footnotes,
);
}
if (this._options.messageIndent != null && !this._options.markdown) {
message = message.replace(/^/gm, GlyphChars.Space.repeat(this._options.messageIndent));
}
return this._options.markdown ? `\n> ${message}` : message;
}
@ -369,11 +389,21 @@ export class CommitFormatter extends Formatter {
let text;
if (PullRequest.is(pr)) {
text = this._options.markdown
? `[PR #${pr.number}](${pr.url} "Open Pull Request \\#${pr.number} on ${
pr.provider
}\n${GlyphChars.Dash.repeat(2)}\n${pr.title}\n${pr.state}, ${pr.formatDateFromNow()}")`
: `PR #${pr.number}`;
if (this._options.markdown) {
text = `[PR #${pr.number}](${pr.url} "Open Pull Request \\#${pr.number} on ${
pr.provider
}\n${GlyphChars.Dash.repeat(2)}\n${pr.title}\n${pr.state}, ${pr.formatDateFromNow()}")`;
} else if (this._options.footnotes != null) {
const index = this._options.footnotes.size + 1;
this._options.footnotes.set(
index,
`PR #${pr.number}: ${pr.title} ${GlyphChars.Dot} ${pr.state}, ${pr.formatDateFromNow()}`,
);
text = `PR #${pr.number}${Strings.getSuperscript(index)}`;
} else {
text = `PR #${pr.number}`;
}
} else if (pr instanceof Promises.CancellationError) {
text = this._options.markdown
? `[PR ${GlyphChars.Ellipsis}](# "Searching for a Pull Request (if any) that introduced this commit...")`
@ -427,21 +457,23 @@ export class CommitFormatter extends Formatter {
commit: GitCommit,
dateFormatOrOptions?: string | null | CommitFormatOptions,
): string {
return super.fromTemplateCore(this, template, commit, dateFormatOrOptions);
}
static has(format: string, ...tokens: (keyof NonNullable<CommitFormatOptions['tokenOptions']>)[]) {
const token =
tokens.length === 1
? tokens[0]
: (`(${tokens.join('|')})` as keyof NonNullable<CommitFormatOptions['tokenOptions']>);
if (CommitFormatter.has(template, 'footnotes')) {
if (dateFormatOrOptions == null || typeof dateFormatOrOptions === 'string') {
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
dateFormatOrOptions = {
dateFormat: dateFormatOrOptions,
} as CommitFormatOptions;
}
let regex = hasTokenRegexMap.get(token);
if (regex == null) {
regex = new RegExp(`\\b${token}\\b`);
hasTokenRegexMap.set(token, regex);
if (dateFormatOrOptions.footnotes == null) {
dateFormatOrOptions.footnotes = new Map<number, string>();
}
}
return regex.test(format);
return super.fromTemplateCore(this, template, commit, dateFormatOrOptions);
}
static has(template: string, ...tokens: (keyof NonNullable<CommitFormatOptions['tokenOptions']>)[]): boolean {
return super.has<CommitFormatOptions>(template, ...tokens);
}
}

+ 19
- 0
src/git/formatters/formatter.ts View File

@ -10,6 +10,7 @@ export interface FormatOptions {
type Constructor<T = Record<string, unknown>> = new (...args: any[]) => T;
const hasTokenRegexMap = new Map<string, RegExp>();
const spaceReplacementRegex = / /g;
declare type RequiredTokenOptions<TOptions extends FormatOptions> = TOptions & Required<Pick<TOptions, 'tokenOptions'>>;
@ -138,4 +139,22 @@ export abstract class Formatter
return Strings.interpolate(template, this._formatter);
}
static has<TOptions extends FormatOptions>(
template: string,
...tokens: (keyof NonNullable<TOptions['tokenOptions']>)[]
): boolean {
const token =
tokens.length === 1
? (tokens[0] as string)
: ((`(${tokens.join('|')})` as keyof NonNullable<TOptions['tokenOptions']>) as string);
let regex = hasTokenRegexMap.get(token);
if (regex == null) {
regex = new RegExp(`\\b${token}\\b`);
hasTokenRegexMap.set(token, regex);
}
return regex.test(template);
}
}

+ 44
- 2
src/git/gitService.ts View File

@ -419,14 +419,23 @@ export class GitService implements Disposable {
await setCommandContext(CommandContext.Enabled, hasRepository);
let hasRemotes = false;
let hasConnectedRemotes = false;
if (hasRepository) {
for (const repo of repositoryTree.values()) {
hasRemotes = await repo.hasRemotes();
if (hasRemotes) break;
if (!hasConnectedRemotes) {
hasConnectedRemotes = await repo.hasConnectedRemotes();
}
if (!hasRemotes) {
hasRemotes = hasConnectedRemotes || (await repo.hasRemotes());
}
if (hasRemotes && hasConnectedRemotes) break;
}
}
await setCommandContext(CommandContext.HasRemotes, hasRemotes);
await setCommandContext(CommandContext.HasConnectedRemotes, hasConnectedRemotes);
// If we have no repositories setup a watcher in case one is initialized
if (!hasRepository) {
@ -2623,6 +2632,39 @@ export class GitService implements Disposable {
};
}
async getRemoteWithApiProvider(
repoPath: string | undefined,
options?: { includeDisconnected?: boolean },
): Promise<GitRemote<RemoteProviderWithApi> | undefined>;
async getRemoteWithApiProvider(
remotes: GitRemote[],
options?: { includeDisconnected?: boolean },
): Promise<GitRemote<RemoteProviderWithApi> | undefined>;
@log()
async getRemoteWithApiProvider(
remotesOrRepoPath: GitRemote[] | string | undefined,
{ includeDisconnected }: { includeDisconnected?: boolean } = {},
): Promise<GitRemote<RemoteProviderWithApi> | undefined> {
if (remotesOrRepoPath == null) return undefined;
const remotes = (typeof remotesOrRepoPath === 'string'
? await this.getRemotes(remotesOrRepoPath)
: remotesOrRepoPath
).filter(r => r.provider != null);
const remote =
remotes.length === 1 ? remotes[0] : remotes.find(r => r.default) ?? remotes.find(r => r.name === 'origin');
if (!remote?.provider?.hasApi()) return undefined;
const { provider } = remote;
if (!includeDisconnected) {
const connected = provider.maybeConnected ?? (await provider.isConnected());
if (!connected) return undefined;
}
return remote as GitRemote<RemoteProviderWithApi>;
}
@log()
async getRemotes(
repoPath: string | undefined,

+ 1
- 1
src/git/models/commit.ts View File

@ -224,7 +224,7 @@ export abstract class GitCommit implements GitRevisionReference {
@memoize()
getShortMessage() {
// eslint-disable-next-line no-template-curly-in-string
return CommitFormatter.fromTemplate('${message}', this, { truncateMessageAtNewLine: true });
return CommitFormatter.fromTemplate('${message}', this, { messageTruncateAtNewLine: true });
}
@memoize()

+ 6
- 0
src/git/models/repository.ts View File

@ -475,6 +475,12 @@ export class Repository implements Disposable {
return remotes?.length > 0;
}
async hasConnectedRemotes(): Promise<boolean> {
const remotes = await this.getRemotes();
const remote = await Container.git.getRemoteWithApiProvider(remotes);
return remote?.provider != null;
}
async hasTrackingBranch(): Promise<boolean> {
const branch = await this.getBranch();
return branch?.tracking != null;

+ 5
- 4
src/hovers/hovers.ts View File

@ -201,6 +201,7 @@ export namespace Hovers {
dateFormat: dateFormat,
line: editorLine,
markdown: true,
messageAutolinks: Container.config.hovers.autolinks.enabled,
pullRequestOrRemote: pr,
presence: presence,
previousLineDiffUris: previousLineDiffUris,
@ -242,8 +243,8 @@ export namespace Hovers {
return undefined;
}
const remote = remotes.find(r => r.default && r.provider != null);
if (remote == null) {
const remote = await Container.git.getRemoteWithApiProvider(remotes);
if (remote?.provider == null) {
Logger.debug(cc, `completed ${GlyphChars.Dot} ${Strings.getDurationMilliseconds(start)} ms`);
return undefined;
@ -321,8 +322,8 @@ export namespace Hovers {
return undefined;
}
const remote = remotes.find(r => r.default && r.provider != null);
if (!remote?.provider?.hasApi()) {
const remote = await Container.git.getRemoteWithApiProvider(remotes, { includeDisconnected: true });
if (remote?.provider == null) {
Logger.debug(cc, `completed ${GlyphChars.Dot} ${Strings.getDurationMilliseconds(start)} ms`);
return undefined;

+ 2
- 2
src/statusbar/statusBarController.ts View File

@ -132,8 +132,8 @@ export class StatusBarController implements Disposable {
if (!cfg.enabled || this._blameStatusBarItem == null || !isTextEditor(editor)) return;
this._blameStatusBarItem.text = `$(git-commit) ${CommitFormatter.fromTemplate(cfg.format, commit, {
truncateMessageAtNewLine: true,
dateFormat: cfg.dateFormat === null ? Container.config.defaultDateFormat : cfg.dateFormat,
messageTruncateAtNewLine: true,
dateFormat: cfg.dateFormat === null ? Container.config.defaultDateFormat : cfg.dateFormat
})}`;
switch (cfg.command) {

+ 6
- 0
src/system/string.ts View File

@ -63,6 +63,12 @@ export namespace Strings {
return secs * 1000 + Math.floor(nanosecs / 1000000);
}
const superscripts = ['\u00B9', '\u00B2', '\u00B3', '\u2074', '\u2075', '\u2076', '\u2077', '\u2078', '\u2079'];
export function getSuperscript(num: number) {
return superscripts[num - 1] ?? '';
}
const driveLetterNormalizeRegex = /(?<=^\/?)([A-Z])(?=:\/)/;
const pathNormalizeRegex = /\\/g;
const pathStripTrailingSlashRegex = /\/$/g;

+ 42
- 51
src/views/nodes/commitFileNode.ts View File

@ -59,7 +59,7 @@ export class CommitFileNode extends ViewRefFileNode {
}
const item = new TreeItem(this.label, TreeItemCollapsibleState.None);
item.contextValue = this.contextValye;
item.contextValue = this.contextValue;
item.description = this.description;
item.tooltip = this.tooltip;
@ -75,15 +75,13 @@ export class CommitFileNode extends ViewRefFileNode {
item.command = this.getCommand();
// Only cache the label/description/tooltip for a single refresh
// Only cache the label for a single refresh (its only cached because it is used externally for sorting)
this._label = undefined;
this._description = undefined;
this._tooltip = undefined;
return item;
}
protected get contextValye(): string {
protected get contextValue(): string {
if (!this.commit.isUncommitted) {
return `${ContextValues.File}+committed${this._options.inFileHistory ? '+history' : ''}`;
}
@ -91,19 +89,15 @@ export class CommitFileNode extends ViewRefFileNode {
return this.commit.isUncommittedStaged ? `${ContextValues.File}+staged` : `${ContextValues.File}+unstaged`;
}
private _description: string | undefined;
get description() {
if (this._description === undefined) {
this._description = this._options.displayAsCommit
? CommitFormatter.fromTemplate(this.getCommitDescriptionTemplate(), this.commit, {
truncateMessageAtNewLine: true,
dateFormat: Container.config.defaultDateFormat,
})
: StatusFileFormatter.fromTemplate(this.getCommitFileDescriptionTemplate(), this.file, {
relativePath: this.relativePath,
});
}
return this._description;
private get description() {
return this._options.displayAsCommit
? CommitFormatter.fromTemplate(this.getCommitDescriptionTemplate(), this.commit, {
messageTruncateAtNewLine: true,
dateFormat: Container.config.defaultDateFormat,
})
: StatusFileFormatter.fromTemplate(this.getCommitFileDescriptionTemplate(), this.file, {
relativePath: this.relativePath,
});
}
private _folderName: string | undefined;
@ -119,7 +113,7 @@ export class CommitFileNode extends ViewRefFileNode {
if (this._label === undefined) {
this._label = this._options.displayAsCommit
? CommitFormatter.fromTemplate(this.getCommitTemplate(), this.commit, {
truncateMessageAtNewLine: true,
messageTruncateAtNewLine: true,
dateFormat: Container.config.defaultDateFormat,
})
: StatusFileFormatter.fromTemplate(this.getCommitFileTemplate(), this.file, {
@ -136,39 +130,36 @@ export class CommitFileNode extends ViewRefFileNode {
set relativePath(value: string | undefined) {
this._relativePath = value;
this._label = undefined;
this._tooltip = undefined;
}
private _tooltip: string | undefined;
get tooltip() {
if (this._tooltip === undefined) {
if (this._options.displayAsCommit) {
// eslint-disable-next-line no-template-curly-in-string
const status = StatusFileFormatter.fromTemplate('${status}${ (originalPath)}', this.file); // lgtm [js/template-syntax-in-string-literal]
this._tooltip = CommitFormatter.fromTemplate(
this.commit.isUncommitted
? `\${author} ${GlyphChars.Dash} \${id}\n${status}\n\${ago} (\${date})`
: `\${author} ${
GlyphChars.Dash
} \${id}\n${status}\n\${ago} (\${date})\n\n\${message}${this.commit.getFormattedDiffStatus({
expand: true,
prefix: '\n\n',
separator: '\n',
})}`,
this.commit,
{
dateFormat: Container.config.defaultDateFormat,
},
);
} else {
this._tooltip = StatusFileFormatter.fromTemplate(
// eslint-disable-next-line no-template-curly-in-string
'${file}\n${directory}/\n\n${status}${ (originalPath)}',
this.file,
);
}
}
private get tooltip() {
if (this._options.displayAsCommit) {
// eslint-disable-next-line no-template-curly-in-string
const status = StatusFileFormatter.fromTemplate('${status}${ (originalPath)}', this.file); // lgtm [js/template-syntax-in-string-literal]
return CommitFormatter.fromTemplate(
this.commit.isUncommitted
? `\${author} ${GlyphChars.Dash} \${id}\n${status}\n\${ago} (\${date})`
: `\${author}\${ (email)}\${" via "pullRequest} ${
GlyphChars.Dash
} \${id}\n${status}\n\${ago} (\${date})\${\n\nmessage}${this.commit.getFormattedDiffStatus({
expand: true,
prefix: '\n\n',
separator: '\n',
})}\${\n\n${GlyphChars.Dash.repeat(2)}\nfootnotes}`,
this.commit,
{
dateFormat: Container.config.defaultDateFormat,
messageAutolinks: true,
messageIndent: 4,
},
);
}
return this._tooltip;
return StatusFileFormatter.fromTemplate(
// eslint-disable-next-line no-template-curly-in-string
'${file}\n${directory}/\n\n${status}${ (originalPath)}',
this.file,
);
}
protected getCommitTemplate() {

+ 27
- 24
src/views/nodes/commitNode.ts View File

@ -33,6 +33,27 @@ export class CommitNode extends ViewRefNode
return this.commit;
}
private get tooltip() {
return CommitFormatter.fromTemplate(
this.commit.isUncommitted
? `\${author} ${GlyphChars.Dash} \${id}\n\${ago} (\${date})`
: `\${author}\${ (email)}\${" via "pullRequest} ${
GlyphChars.Dash
} \${id}\${ (tips)}\n\${ago} (\${date})\${\n\nmessage}${this.commit.getFormattedDiffStatus({
expand: true,
prefix: '\n\n',
separator: '\n',
})}\${\n\n${GlyphChars.Dash.repeat(2)}\nfootnotes}`,
this.commit,
{
dateFormat: Container.config.defaultDateFormat,
getBranchAndTagTips: this.getBranchAndTagTips,
messageAutolinks: true,
messageIndent: 4,
},
);
}
getChildren(): ViewNode[] {
const commit = this.commit;
@ -62,43 +83,25 @@ export class CommitNode extends ViewRefNode
const label = CommitFormatter.fromTemplate(this.view.config.commitFormat, this.commit, {
dateFormat: Container.config.defaultDateFormat,
getBranchAndTagTips: this.getBranchAndTagTips,
truncateMessageAtNewLine: true,
messageTruncateAtNewLine: true,
});
const item = new TreeItem(
label,
this._options.expand ? TreeItemCollapsibleState.Expanded : TreeItemCollapsibleState.Collapsed,
);
item.contextValue = ContextValues.Commit;
if (this.branch?.current) {
item.contextValue += '+current';
}
item.contextValue = `${ContextValues.Commit}${this.branch?.current ? '+current' : ''}`;
item.description = CommitFormatter.fromTemplate(this.view.config.commitDescriptionFormat, this.commit, {
truncateMessageAtNewLine: true,
messageTruncateAtNewLine: true,
dateFormat: Container.config.defaultDateFormat,
});
item.iconPath =
!(this.view instanceof StashesView) && this.view.config.avatars
? this.commit.getAvatarUri(Container.config.defaultGravatarsStyle)
: new ThemeIcon('git-commit');
item.tooltip = CommitFormatter.fromTemplate(
this.commit.isUncommitted
? `\${author} ${GlyphChars.Dash} \${id}\n\${ago} (\${date})`
: `\${author} \${(email) }${GlyphChars.Dash} \${id}\${ (tips)}\n\${ago} (\${date})\n\n\${message}`,
this.commit,
{
dateFormat: Container.config.defaultDateFormat,
getBranchAndTagTips: this.getBranchAndTagTips,
},
);
if (!this.commit.isUncommitted) {
item.tooltip += this.commit.getFormattedDiffStatus({
expand: true,
prefix: '\n\n',
separator: '\n',
});
}
item.tooltip = this.tooltip;
return item;
}

+ 5
- 4
src/views/nodes/stashNode.ts View File

@ -59,20 +59,21 @@ export class StashNode extends ViewRefNode {
getTreeItem(): TreeItem {
const item = new TreeItem(
CommitFormatter.fromTemplate(this.view.config.stashFormat, this.commit, {
truncateMessageAtNewLine: true,
dateFormat: Container.config.defaultDateFormat,
messageTruncateAtNewLine: true,
dateFormat: Container.config.defaultDateFormat
}),
TreeItemCollapsibleState.Collapsed,
);
item.id = this.id;
item.description = CommitFormatter.fromTemplate(this.view.config.stashDescriptionFormat, this.commit, {
truncateMessageAtNewLine: true,
dateFormat: Container.config.defaultDateFormat,
messageTruncateAtNewLine: true,
dateFormat: Container.config.defaultDateFormat
});
item.contextValue = ContextValues.Stash;
// eslint-disable-next-line no-template-curly-in-string
item.tooltip = CommitFormatter.fromTemplate('${ago} (${date})\n\n${message}', this.commit, {
dateFormat: Container.config.defaultDateFormat,
messageAutolinks: true
});
return item;

Loading…
Cancel
Save