diff --git a/src/annotations/autolinks.ts b/src/annotations/autolinks.ts index ae2db617b0d9c..5da26c386aa84 100644 --- a/src/annotations/autolinks.ts +++ b/src/annotations/autolinks.ts @@ -10,7 +10,7 @@ import { fromNow } from '../system/date'; import { debug } from '../system/decorators/log'; import { encodeUrl } from '../system/encoding'; import { every, join, map } from '../system/iterable'; -import { PromiseCancelledError, raceAll } from '../system/promise'; +import { PromiseCancelledError, PromiseCancelledErrorWithId, raceAll } from '../system/promise'; import { escapeMarkdown, escapeRegex, getSuperscript } from '../system/string'; const emptyAutolinkMap = Object.freeze(new Map()); @@ -135,6 +135,24 @@ export class Autolinks implements Disposable { return autolinks; } + async getLinkedIssuesAndPullRequests( + message: string, + remote: GitRemote, + options?: { autolinks?: Map; timeout?: never }, + ): Promise | undefined>; + async getLinkedIssuesAndPullRequests( + message: string, + remote: GitRemote, + options: { autolinks?: Map; timeout: number }, + ): Promise< + | Map< + string, + | IssueOrPullRequest + | PromiseCancelledErrorWithId> + | undefined + > + | undefined + >; @debug({ args: { 0: '', diff --git a/src/git/models/pullRequest.ts b/src/git/models/pullRequest.ts index d70b16df6881d..9acae94c96466 100644 --- a/src/git/models/pullRequest.ts +++ b/src/git/models/pullRequest.ts @@ -4,6 +4,7 @@ import { Colors } from '../../constants'; import { Container } from '../../container'; import { formatDate, fromNow } from '../../system/date'; import { memoize } from '../../system/decorators/memoize'; +import { IssueOrPullRequest, IssueOrPullRequestType } from './issue'; import type { RemoteProviderReference } from './remoteProvider'; export const enum PullRequestState { @@ -12,7 +13,7 @@ export const enum PullRequestState { Merged = 'Merged', } -export class PullRequest { +export class PullRequest implements IssueOrPullRequest { static is(pr: any): pr is PullRequest { return pr instanceof PullRequest; } @@ -49,6 +50,8 @@ export class PullRequest { } } + readonly type = IssueOrPullRequestType.PullRequest; + constructor( public readonly provider: RemoteProviderReference, public readonly author: { @@ -65,6 +68,10 @@ export class PullRequest { public readonly mergedDate?: Date, ) {} + get closed(): boolean { + return this.state === PullRequestState.Closed; + } + get formattedDate(): string { return Container.instance.PullRequestDateFormatting.dateStyle === DateStyle.Absolute ? this.formatDate(Container.instance.PullRequestDateFormatting.dateFormat) diff --git a/src/webviews/commitDetails/commitDetailsWebviewView.ts b/src/webviews/commitDetails/commitDetailsWebviewView.ts index 2fb5103d1a3d8..3da0d764b3e7a 100644 --- a/src/webviews/commitDetails/commitDetailsWebviewView.ts +++ b/src/webviews/commitDetails/commitDetailsWebviewView.ts @@ -10,9 +10,9 @@ import type { Container } from '../../container'; import { GitUri } from '../../git/gitUri'; import type { GitCommit } from '../../git/models/commit'; import { GitFile } from '../../git/models/file'; -import type { IssueOrPullRequest } from '../../git/models/issue'; import { executeCommand, executeCoreCommand } from '../../system/command'; import { debug } from '../../system/decorators/log'; +import { getSettledValue } from '../../system/promise'; import { IpcMessage, onIpc } from '../protocol'; import { WebviewViewBase } from '../webviewViewBase'; import { @@ -29,7 +29,6 @@ import { PickCommitCommandType, RichCommitDetails, RichContentNotificationType, - SearchCommitCommandType, State, } from './protocol'; @@ -88,11 +87,6 @@ export class CommitDetailsWebviewView extends WebviewViewBase { this.showCommitPicker(); }); break; - case SearchCommitCommandType.method: - onIpc(SearchCommitCommandType, e, _params => { - this.showCommitSearch(); - }); - break; case AutolinkSettingsCommandType.method: onIpc(AutolinkSettingsCommandType, e, _params => { this.showAutolinkSettings(); @@ -200,57 +194,46 @@ export class CommitDetailsWebviewView extends WebviewViewBase { private copyRemoteFileUrl() {} private async getRichContent(selected: GitCommit): Promise { - const pullRequest = selected != null ? await selected.getAssociatedPullRequest() : undefined; - console.log('CommitDetailsWebview pullRequest', pullRequest); - - const issues: Record[] = []; - let formattedMessage; - if (selected?.message !== undefined && typeof selected.message === 'string') { - const remote = await this.container.git.getBestRemoteWithRichProvider(selected.repoPath); - console.log('CommitDetailsWebview remote', remote); - - if (remote != null) { - const issueSearch = await this.container.autolinks.getLinkedIssuesAndPullRequests( - selected.message, - remote, - ); - // TODO: add HTML formatting option to linkify - // formattedMessage = this.container.autolinks.linkify( - // escapeMarkdown(selected.message, { quoted: true }), - // true, - // [remote], - // // issueSearch, - // ); - formattedMessage = this.container.autolinks.linkify( - encodeMarkup(selected.message), - true, - [remote], - // issueSearch, - ); - - let filteredIssues; - if (issueSearch != null) { - if (pullRequest !== undefined) { - issueSearch.delete(pullRequest.id); - } - - filteredIssues = Array.from(issueSearch.values()).filter( - value => value != null, - ) as IssueOrPullRequest[]; - } - - console.log('CommitDetailsWebview filteredIssues', filteredIssues); - - if (filteredIssues !== undefined) { - issues.push(...filteredIssues); - } - } + const remotes = await this.container.git.getRemotesWithProviders(selected.repoPath, { sort: true }); + const remote = await this.container.git.getBestRemoteWithRichProvider(remotes); + + if (selected.message == null) { + await selected.ensureFullDetails(); + } + + let autolinkedIssuesOrPullRequests; + let pr; + + if (remote?.provider != null) { + const [autolinkedIssuesOrPullRequestsResult, prResult] = await Promise.allSettled([ + this.container.autolinks.getLinkedIssuesAndPullRequests(selected.message ?? selected.summary, remote), + selected.getAssociatedPullRequest({ remote: remote }), + ]); + + autolinkedIssuesOrPullRequests = getSettledValue(autolinkedIssuesOrPullRequestsResult); + pr = getSettledValue(prResult); + } + + // TODO: add HTML formatting option to linkify + const formattedMessage = this.container.autolinks.linkify( + encodeMarkup(selected.message!), + true, + remote != null ? [remote] : undefined, + autolinkedIssuesOrPullRequests, + ); + + // Remove possible duplicate pull request + if (pr != null) { + autolinkedIssuesOrPullRequests?.delete(pr.id); } return { formattedMessage: formattedMessage, - pullRequest: pullRequest, - issues: issues?.length ? issues : undefined, + pullRequest: pr, + issues: + autolinkedIssuesOrPullRequests != null + ? [...autolinkedIssuesOrPullRequests.values()].filter((i: T | undefined): i is T => i != null) + : undefined, }; } diff --git a/src/webviews/commitDetails/protocol.ts b/src/webviews/commitDetails/protocol.ts index e26bfb200868a..7b4a12ea70185 100644 --- a/src/webviews/commitDetails/protocol.ts +++ b/src/webviews/commitDetails/protocol.ts @@ -1,3 +1,4 @@ +import type { IssueOrPullRequest } from '../../git/models/issue'; import { IpcCommandType, IpcNotificationType } from '../protocol'; export type CommitSummary = { @@ -17,8 +18,8 @@ export type CommitDetails = { export type RichCommitDetails = { formattedMessage?: string; - pullRequest?: Record; - issues?: Record[]; + pullRequest?: IssueOrPullRequest; + issues?: IssueOrPullRequest[]; }; export type State = {