From 1b2fddd714a783d138af09b8c5de0e07f9e6007b Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Tue, 26 Sep 2023 02:56:51 -0400 Subject: [PATCH] Fixes duplicated autolinks/issues/prs Adds state prop to issues & prs Removes some enums in favor of union types --- src/annotations/autolinks.ts | 52 ++++++---- src/cache.ts | 3 +- src/config.ts | 5 +- src/git/formatters/commitFormatter.ts | 16 +-- src/git/models/issue.ts | 110 +++++++++++++-------- src/git/models/pullRequest.ts | 57 +---------- src/git/models/reference.ts | 5 +- src/git/remotes/azure-devops.ts | 5 +- src/git/remotes/bitbucket-server.ts | 5 +- src/git/remotes/bitbucket.ts | 4 +- src/git/remotes/gitea.ts | 3 +- src/git/remotes/gitlab.ts | 9 +- src/hovers/hovers.ts | 5 - src/plus/github/github.ts | 10 +- src/plus/github/models.ts | 18 ++-- src/plus/gitlab/gitlab.ts | 27 ++--- src/plus/gitlab/models.ts | 22 +---- src/system/string.ts | 4 + src/views/nodes/autolinkedItemNode.ts | 22 ++--- src/views/nodes/branchNode.ts | 5 +- src/views/nodes/commitNode.ts | 5 - src/views/nodes/fileRevisionAsCommitNode.ts | 5 - src/views/nodes/pullRequestNode.ts | 11 +-- src/views/nodes/rebaseStatusNode.ts | 5 - src/views/nodes/worktreeNode.ts | 5 +- .../commitDetails/components/commit-details-app.ts | 97 ++++++++++++------ .../shared/components/rich/issue-pull-request.ts | 37 ++++--- src/webviews/commitDetails/commitDetailsWebview.ts | 5 - src/webviews/settings/settingsWebview.ts | 4 +- 29 files changed, 278 insertions(+), 283 deletions(-) diff --git a/src/annotations/autolinks.ts b/src/annotations/autolinks.ts index 226bfbf..6e7b440 100644 --- a/src/annotations/autolinks.ts +++ b/src/annotations/autolinks.ts @@ -15,7 +15,7 @@ import { debug } from '../system/decorators/log'; import { encodeUrl } from '../system/encoding'; import { join, map } from '../system/iterable'; import { Logger } from '../system/logger'; -import { encodeHtmlWeak, escapeMarkdown, escapeRegex, getSuperscript } from '../system/string'; +import { capitalize, encodeHtmlWeak, escapeMarkdown, escapeRegex, getSuperscript } from '../system/string'; const emptyAutolinkMap = Object.freeze(new Map()); @@ -69,6 +69,7 @@ export interface CacheableAutolinkReference extends AutolinkReference { outputFormat: 'html' | 'markdown' | 'plaintext', tokenMapping: Map, enrichedAutolinks?: Map, + prs?: Set, footnotes?: Map, ) => string) | null; @@ -85,6 +86,7 @@ export interface DynamicAutolinkReference { outputFormat: 'html' | 'markdown' | 'plaintext', tokenMapping: Map, enrichedAutolinks?: Map, + prs?: Set, footnotes?: Map, ) => string) | null; @@ -260,6 +262,7 @@ export class Autolinks implements Disposable { outputFormat: 'html' | 'markdown' | 'plaintext', remotes?: GitRemote[], enrichedAutolinks?: Map, + prs?: Set, footnotes?: Map, ): string { const includeFootnotesInText = outputFormat === 'plaintext' && footnotes == null; @@ -272,7 +275,7 @@ export class Autolinks implements Disposable { for (const ref of this._references) { if (this.ensureAutolinkCached(ref)) { if (ref.tokenize != null) { - text = ref.tokenize(text, outputFormat, tokenMapping, enrichedAutolinks, footnotes); + text = ref.tokenize(text, outputFormat, tokenMapping, enrichedAutolinks, prs, footnotes); } } } @@ -289,7 +292,7 @@ export class Autolinks implements Disposable { for (const ref of r.provider.autolinks) { if (this.ensureAutolinkCached(ref)) { if (ref.tokenize != null) { - text = ref.tokenize(text, outputFormat, tokenMapping, enrichedAutolinks, footnotes); + text = ref.tokenize(text, outputFormat, tokenMapping, enrichedAutolinks, prs, footnotes); } } } @@ -324,6 +327,7 @@ export class Autolinks implements Disposable { outputFormat: 'html' | 'markdown' | 'plaintext', tokenMapping: Map, enrichedAutolinks?: Map, + prs?: Set, footnotes?: Map, ) => { let footnoteIndex: number; @@ -343,7 +347,7 @@ export class Autolinks implements Disposable { const issueResult = enrichedAutolinks?.get(num)?.[0]; if (issueResult?.value != null) { if (issueResult.paused) { - if (footnotes != null) { + if (footnotes != null && !prs?.has(num)) { let name = ref.description?.replace(numRegex, num); if (name == null) { name = `Custom Autolink ${ref.prefix}${num}`; @@ -361,7 +365,7 @@ export class Autolinks implements Disposable { const issueTitle = escapeMarkdown(issue.title.trim()); const issueTitleQuoteEscaped = issueTitle.replace(/"/g, '\\"'); - if (footnotes != null) { + if (footnotes != null && !prs?.has(num)) { footnoteIndex = footnotes.size + 1; footnotes.set( footnoteIndex, @@ -369,17 +373,19 @@ export class Autolinks implements Disposable { issue, )} **${issueTitle}**](${url}${title}")\\\n${GlyphChars.Space.repeat( 5, - )}${linkText} ${issue.closed ? 'closed' : 'opened'} ${fromNow( + )}${linkText} ${issue.state} ${fromNow( issue.closedDate ?? issue.date, )}`, ); } - title += `\n${GlyphChars.Dash.repeat(2)}\n${issueTitleQuoteEscaped}\n${ - issue.closed ? 'Closed' : 'Opened' - }, ${fromNow(issue.closedDate ?? issue.date)}`; + title += `\n${GlyphChars.Dash.repeat( + 2, + )}\n${issueTitleQuoteEscaped}\n${capitalize(issue.state)}, ${fromNow( + issue.closedDate ?? issue.date, + )}`; } - } else if (footnotes != null) { + } else if (footnotes != null && !prs?.has(num)) { let name = ref.description?.replace(numRegex, num); if (name == null) { name = `Custom Autolink ${ref.prefix}${num}`; @@ -413,7 +419,7 @@ export class Autolinks implements Disposable { const issueResult = enrichedAutolinks?.get(num)?.[0]; if (issueResult?.value != null) { if (issueResult.paused) { - if (footnotes != null) { + if (footnotes != null && !prs?.has(num)) { let name = ref.description?.replace(numRegex, num); if (name == null) { name = `Custom Autolink ${ref.prefix}${num}`; @@ -431,7 +437,7 @@ export class Autolinks implements Disposable { const issueTitle = encodeHtmlWeak(issue.title.trim()); const issueTitleQuoteEscaped = issueTitle.replace(/"/g, '"'); - if (footnotes != null) { + if (footnotes != null && !prs?.has(num)) { footnoteIndex = footnotes.size + 1; footnotes.set( footnoteIndex, @@ -439,17 +445,19 @@ export class Autolinks implements Disposable { issue, )} ${issueTitle}
${GlyphChars.Space.repeat( 5, - )}${linkText} ${issue.closed ? 'closed' : 'opened'} ${fromNow( + )}${linkText} ${issue.state} ${fromNow( issue.closedDate ?? issue.date, )}`, ); } - title += `\n${GlyphChars.Dash.repeat(2)}\n${issueTitleQuoteEscaped}\n${ - issue.closed ? 'Closed' : 'Opened' - }, ${fromNow(issue.closedDate ?? issue.date)}`; + title += `\n${GlyphChars.Dash.repeat( + 2, + )}\n${issueTitleQuoteEscaped}\n${capitalize(issue.state)}, ${fromNow( + issue.closedDate ?? issue.date, + )}`; } - } else if (footnotes != null) { + } else if (footnotes != null && !prs?.has(num)) { let name = ref.description?.replace(numRegex, num); if (name == null) { name = `Custom Autolink ${ref.prefix}${num}`; @@ -477,16 +485,18 @@ export class Autolinks implements Disposable { const issueResult = enrichedAutolinks?.get(num)?.[0]; if (issueResult?.value == null) return linkText; - if (footnotes != null) { + if (footnotes != null && !prs?.has(num)) { footnoteIndex = footnotes.size + 1; footnotes.set( footnoteIndex, `${linkText}: ${ issueResult.paused ? 'Loading...' - : `${issueResult.value.title} ${GlyphChars.Dot} ${ - issueResult.value.closed ? 'Closed' : 'Opened' - }, ${fromNow(issueResult.value.closedDate ?? issueResult.value.date)}` + : `${issueResult.value.title} ${GlyphChars.Dot} ${capitalize( + issueResult.value.state, + )}, ${fromNow( + issueResult.value.closedDate ?? issueResult.value.date, + )}` }`, ); } diff --git a/src/cache.ts b/src/cache.ts index e048c82..7e77eb7 100644 --- a/src/cache.ts +++ b/src/cache.ts @@ -4,7 +4,6 @@ import type { Container } from './container'; import type { DefaultBranch } from './git/models/defaultBranch'; import type { IssueOrPullRequest } from './git/models/issue'; import type { PullRequest } from './git/models/pullRequest'; -import { PullRequestState } from './git/models/pullRequest'; import type { GitRemote } from './git/models/remote'; import type { RepositoryMetadata } from './git/models/repositoryMetadata'; import type { RemoteProvider } from './git/remotes/remoteProvider'; @@ -201,7 +200,7 @@ function getExpiresAt(cache: T, value: CacheValue | undefine // Open prs expire after 1 hour, but closed/merge prs expire after 12 hours unless recently updated and then expire in 1 hour const pr = value as CacheValue<'prsBySha'>; - if (pr.state === PullRequestState.Open) return defaultExpiresAt; + if (pr.state === 'opened') return defaultExpiresAt; const updatedAgo = now - (pr.closedDate ?? pr.mergedDate ?? pr.date).getTime(); return now + (updatedAgo > 14 * 24 * 60 * 60 * 1000 ? 12 : 1) * 60 * 60 * 1000; diff --git a/src/config.ts b/src/config.ts index 867cfc6..34a1d7c 100644 --- a/src/config.ts +++ b/src/config.ts @@ -206,10 +206,7 @@ export const enum AnnotationsToggleMode { Window = 'window', } -export const enum AutolinkType { - Issue = 'Issue', - PullRequest = 'PullRequest', -} +export type AutolinkType = 'issue' | 'pullrequest'; export interface AutolinkReference { prefix: string; diff --git a/src/git/formatters/commitFormatter.ts b/src/git/formatters/commitFormatter.ts index 0848a12..61693d5 100644 --- a/src/git/formatters/commitFormatter.ts +++ b/src/git/formatters/commitFormatter.ts @@ -33,6 +33,7 @@ import type { PreviousLineComparisonUrisResult } from '../gitProvider'; import type { GitCommit } from '../models/commit'; import { isCommit } from '../models/commit'; import { uncommitted, uncommittedStaged } from '../models/constants'; +import { getIssueOrPullRequestMarkdownIcon } from '../models/issue'; import { PullRequest } from '../models/pullRequest'; import { getReferenceFromRevision, isUncommittedStaged, shortenRevision } from '../models/reference'; import { GitRemote } from '../models/remote'; @@ -657,6 +658,9 @@ export class CommitFormatter extends Formatter { outputFormat, this._options.remotes, this._options.enrichedAutolinks, + this._options.pullRequest != null && !isPromise(this._options.pullRequest) + ? new Set([this._options.pullRequest.id]) + : undefined, this._options.footnotes, ); } @@ -687,8 +691,6 @@ export class CommitFormatter extends Formatter { let text; if (PullRequest.is(pr)) { if (this._options.outputFormat === 'markdown') { - const prTitle = escapeMarkdown(pr.title).replace(/"/g, '\\"').trim(); - text = `PR [**#${pr.id}**](${getMarkdownActionCommand('openPullRequest', { repoPath: this._item.repoPath, provider: { id: pr.provider.id, name: pr.provider.name, domain: pr.provider.domain }, @@ -700,14 +702,16 @@ export class CommitFormatter extends Formatter { }, ${pr.formatDateFromNow()}")`; if (this._options.footnotes != null) { + const prTitle = escapeMarkdown(pr.title).replace(/"/g, '\\"').trim(); + const index = this._options.footnotes.size + 1; this._options.footnotes.set( index, - `${PullRequest.getMarkdownIcon(pr)} [**${prTitle}**](${pr.url} "Open Pull Request \\#${ - pr.id - } on ${pr.provider.name}")\\\n${GlyphChars.Space.repeat(4)} #${ + `${getIssueOrPullRequestMarkdownIcon(pr)} [**${prTitle}**](${pr.url} "Open Pull Request \\#${ pr.id - } ${pr.state.toLocaleLowerCase()} ${pr.formatDateFromNow()}`, + } on ${pr.provider.name}")\\\n${GlyphChars.Space.repeat(4)} #${pr.id} ${ + pr.state + } ${pr.formatDateFromNow()}`, ); } } else if (this._options.footnotes != null) { diff --git a/src/git/models/issue.ts b/src/git/models/issue.ts index 729449f..1860991 100644 --- a/src/git/models/issue.ts +++ b/src/git/models/issue.ts @@ -2,10 +2,8 @@ import { ColorThemeKind, ThemeColor, ThemeIcon, window } from 'vscode'; import type { Colors } from '../../constants'; import type { RemoteProviderReference } from './remoteProvider'; -export const enum IssueOrPullRequestType { - Issue = 'Issue', - PullRequest = 'PullRequest', -} +export type IssueOrPullRequestType = 'issue' | 'pullrequest'; +export type IssueOrPullRequestState = 'opened' | 'closed' | 'merged'; export interface IssueOrPullRequest { readonly type: IssueOrPullRequestType; @@ -16,6 +14,7 @@ export interface IssueOrPullRequest { readonly date: Date; readonly closedDate?: Date; readonly closed: boolean; + readonly state: IssueOrPullRequestState; } export interface IssueLabel { @@ -64,6 +63,7 @@ export function serializeIssueOrPullRequest(value: IssueOrPullRequest): IssueOrP date: value.date, closedDate: value.closedDate, closed: value.closed, + state: value.state, }; return serialized; } @@ -75,25 +75,33 @@ export function getIssueOrPullRequestHtmlIcon(issue?: IssueOrPullRequest): strin };">`; } - if (issue.type === IssueOrPullRequestType.PullRequest) { + if (issue.type === 'pullrequest') { + switch (issue.state) { + case 'merged': + return ``; + case 'closed': + return ``; + case 'opened': + return ``; + default: + return ``; + } + } else { if (issue.closed) { - return ``; } - return ``; } - - if (issue.closed) { - return ``; - } - return ``; } export function getIssueOrPullRequestMarkdownIcon(issue?: IssueOrPullRequest): string { @@ -103,25 +111,33 @@ export function getIssueOrPullRequestMarkdownIcon(issue?: IssueOrPullRequest): s };">$(link)`; } - if (issue.type === IssueOrPullRequestType.PullRequest) { + if (issue.type === 'pullrequest') { + switch (issue.state) { + case 'merged': + return `$(git-merge)`; + case 'closed': + return `$(git-pull-request-closed)`; + case 'opened': + return `$(git-pull-request)`; + default: + return `$(git-pull-request)`; + } + } else { if (issue.closed) { return `$(git-pull-request)`; + };">$(pass)`; } return `$(git-pull-request)`; + };">$(issues)`; } - - if (issue.closed) { - return `$(pass)`; - } - return `$(issues)`; } export function getIssueOrPullRequestThemeIcon(issue?: IssueOrPullRequest): ThemeIcon { @@ -129,20 +145,32 @@ export function getIssueOrPullRequestThemeIcon(issue?: IssueOrPullRequest): Them return new ThemeIcon('link', new ThemeColor('gitlens.closedAutolinkedIssueIconColor' satisfies Colors)); } - if (issue.type === IssueOrPullRequestType.PullRequest) { + if (issue.type === 'pullrequest') { + switch (issue.state) { + case 'merged': + return new ThemeIcon( + 'git-merge', + new ThemeColor('gitlens.mergedPullRequestIconColor' satisfies Colors), + ); + case 'closed': + return new ThemeIcon( + 'git-pull-request-closed', + new ThemeColor('gitlens.closedPullRequestIconColor' satisfies Colors), + ); + case 'opened': + return new ThemeIcon( + 'git-pull-request', + new ThemeColor('gitlens.openPullRequestIconColor' satisfies Colors), + ); + default: + return new ThemeIcon('git-pull-request'); + } + } else { if (issue.closed) { - return new ThemeIcon( - 'git-pull-request', - new ThemeColor('gitlens.mergedPullRequestIconColor' satisfies Colors), - ); + return new ThemeIcon('pass', new ThemeColor('gitlens.closedAutolinkedIssueIconColor' satisfies Colors)); } - return new ThemeIcon('git-pull-request', new ThemeColor('gitlens.openPullRequestIconColor' satisfies Colors)); - } - - if (issue.closed) { - return new ThemeIcon('pass', new ThemeColor('gitlens.closedAutolinkedIssueIconColor' satisfies Colors)); + return new ThemeIcon('issues', new ThemeColor('gitlens.openAutolinkedIssueIconColor' satisfies Colors)); } - return new ThemeIcon('issues', new ThemeColor('gitlens.openAutolinkedIssueIconColor' satisfies Colors)); } export function serializeIssue(value: IssueShape): IssueShape { @@ -160,6 +188,7 @@ export function serializeIssue(value: IssueShape): IssueShape { date: value.date, closedDate: value.closedDate, closed: value.closed, + state: value.state, updatedDate: value.updatedDate, author: { name: value.author.name, @@ -189,7 +218,7 @@ export function serializeIssue(value: IssueShape): IssueShape { } export class Issue implements IssueShape { - readonly type = IssueOrPullRequestType.Issue; + readonly type = 'issue'; constructor( public readonly provider: RemoteProviderReference, @@ -198,6 +227,7 @@ export class Issue implements IssueShape { public readonly url: string, public readonly date: Date, public readonly closed: boolean, + public readonly state: IssueOrPullRequestState, public readonly updatedDate: Date, public readonly author: IssueMember, public readonly repository: IssueRepository, diff --git a/src/git/models/pullRequest.ts b/src/git/models/pullRequest.ts index 9bf8e1e..915555b 100644 --- a/src/git/models/pullRequest.ts +++ b/src/git/models/pullRequest.ts @@ -1,18 +1,11 @@ -import { ColorThemeKind, ThemeColor, ThemeIcon, window } from 'vscode'; import { DateStyle } from '../../config'; -import type { Colors } from '../../constants'; import { Container } from '../../container'; import { formatDate, fromNow } from '../../system/date'; import { memoize } from '../../system/decorators/memoize'; -import type { IssueOrPullRequest } from './issue'; -import { IssueOrPullRequestType } from './issue'; +import type { IssueOrPullRequest, IssueOrPullRequestState as PullRequestState } from './issue'; import type { RemoteProviderReference } from './remoteProvider'; -export const enum PullRequestState { - Open = 'Open', - Closed = 'Closed', - Merged = 'Merged', -} +export type { PullRequestState }; export const enum PullRequestReviewDecision { Approved = 'Approved', @@ -54,7 +47,6 @@ export interface PullRequestReviewer { export interface PullRequestShape extends IssueOrPullRequest { readonly author: PullRequestMember; - readonly state: PullRequestState; readonly mergedDate?: Date; readonly refs?: PullRequestRefs; readonly isDraft?: boolean; @@ -132,48 +124,7 @@ export class PullRequest implements PullRequestShape { return pr instanceof PullRequest; } - static getMarkdownIcon(pullRequest: PullRequest): string { - switch (pullRequest.state) { - case PullRequestState.Open: - return `$(git-pull-request)`; - case PullRequestState.Closed: - return `$(git-pull-request-closed)`; - case PullRequestState.Merged: - return `$(git-merge)`; - default: - return '$(git-pull-request)'; - } - } - - static getThemeIcon(pullRequest: PullRequest): ThemeIcon { - switch (pullRequest.state) { - case PullRequestState.Open: - return new ThemeIcon( - 'git-pull-request', - new ThemeColor('gitlens.openPullRequestIconColor' satisfies Colors), - ); - case PullRequestState.Closed: - return new ThemeIcon( - 'git-pull-request-closed', - new ThemeColor('gitlens.closedPullRequestIconColor' satisfies Colors), - ); - case PullRequestState.Merged: - return new ThemeIcon( - 'git-merge', - new ThemeColor('gitlens.mergedPullRequestIconColor' satisfies Colors), - ); - default: - return new ThemeIcon('git-pull-request'); - } - } - - readonly type = IssueOrPullRequestType.PullRequest; + readonly type = 'pullrequest'; constructor( public readonly provider: RemoteProviderReference, @@ -201,7 +152,7 @@ export class PullRequest implements PullRequestShape { ) {} get closed(): boolean { - return this.state === PullRequestState.Closed; + return this.state === 'closed'; } get formattedDate(): string { diff --git a/src/git/models/reference.ts b/src/git/models/reference.ts index b43db0e..b6cab92 100644 --- a/src/git/models/reference.ts +++ b/src/git/models/reference.ts @@ -1,5 +1,6 @@ import { GlyphChars } from '../../constants'; import { configuration } from '../../system/configuration'; +import { capitalize } from '../../system/string'; import { getBranchNameWithoutRemote, getRemoteNameFromBranchName, getRemoteNameSlashIndex } from './branch'; import { deletedOrMissing, uncommitted, uncommittedStaged } from './constants'; @@ -379,9 +380,7 @@ export function getReferenceLabel( } } - return options.capitalize && options.expand && options.label !== false - ? `${result[0].toLocaleUpperCase()}${result.substring(1)}` - : result; + return options.capitalize && options.expand && options.label !== false ? capitalize(result) : result; } const expanded = options.expand ? ` (${refs.map(r => r.name).join(', ')})` : ''; diff --git a/src/git/remotes/azure-devops.ts b/src/git/remotes/azure-devops.ts index fbc72cb..2d9558c 100644 --- a/src/git/remotes/azure-devops.ts +++ b/src/git/remotes/azure-devops.ts @@ -1,7 +1,6 @@ import type { Range, Uri } from 'vscode'; import type { DynamicAutolinkReference } from '../../annotations/autolinks'; import type { AutolinkReference } from '../../config'; -import { AutolinkType } from '../../config'; import type { Repository } from '../models/repository'; import { RemoteProvider } from './remoteProvider'; @@ -53,7 +52,7 @@ export class AzureDevOpsRemote extends RemoteProvider { url: `${workUrl}/_workitems/edit/`, title: `Open Work Item # on ${this.name}`, - type: AutolinkType.Issue, + type: 'issue', description: `${this.name} Work Item #`, }, { @@ -62,7 +61,7 @@ export class AzureDevOpsRemote extends RemoteProvider { url: `${this.baseUrl}/pullrequest/`, title: `Open Pull Request # on ${this.name}`, - type: AutolinkType.PullRequest, + type: 'pullrequest', description: `${this.name} Pull Request #`, }, ]; diff --git a/src/git/remotes/bitbucket-server.ts b/src/git/remotes/bitbucket-server.ts index d32bead..97cda9d 100644 --- a/src/git/remotes/bitbucket-server.ts +++ b/src/git/remotes/bitbucket-server.ts @@ -1,7 +1,6 @@ import type { Range, Uri } from 'vscode'; import type { DynamicAutolinkReference } from '../../annotations/autolinks'; import type { AutolinkReference } from '../../config'; -import { AutolinkType } from '../../config'; import { isSha } from '../models/reference'; import type { Repository } from '../models/repository'; import { RemoteProvider } from './remoteProvider'; @@ -23,7 +22,7 @@ export class BitbucketServerRemote extends RemoteProvider { url: `${this.baseUrl}/issues/`, title: `Open Issue # on ${this.name}`, - type: AutolinkType.Issue, + type: 'issue', description: `${this.name} Issue #`, }, { @@ -32,7 +31,7 @@ export class BitbucketServerRemote extends RemoteProvider { url: `${this.baseUrl}/pull-requests/`, title: `Open Pull Request # on ${this.name}`, - type: AutolinkType.PullRequest, + type: 'pullrequest', description: `${this.name} Pull Request #`, }, ]; diff --git a/src/git/remotes/bitbucket.ts b/src/git/remotes/bitbucket.ts index fd4984d..65bc13f 100644 --- a/src/git/remotes/bitbucket.ts +++ b/src/git/remotes/bitbucket.ts @@ -23,7 +23,7 @@ export class BitbucketRemote extends RemoteProvider { url: `${this.baseUrl}/issues/`, title: `Open Issue # on ${this.name}`, - type: AutolinkType.Issue, + type: 'issue', description: `${this.name} Issue #`, }, { @@ -31,7 +31,7 @@ export class BitbucketRemote extends RemoteProvider { url: `${this.baseUrl}/pull-requests/`, title: `Open Pull Request # on ${this.name}`, - type: AutolinkType.PullRequest, + type: 'pullrequest', description: `${this.name} Pull Request #`, }, ]; diff --git a/src/git/remotes/gitea.ts b/src/git/remotes/gitea.ts index 43a0021..6f78f94 100644 --- a/src/git/remotes/gitea.ts +++ b/src/git/remotes/gitea.ts @@ -1,7 +1,6 @@ import type { Range, Uri } from 'vscode'; import type { DynamicAutolinkReference } from '../../annotations/autolinks'; import type { AutolinkReference } from '../../config'; -import { AutolinkType } from '../../config'; import { isSha } from '../models/reference'; import type { Repository } from '../models/repository'; import { RemoteProvider } from './remoteProvider'; @@ -23,7 +22,7 @@ export class GiteaRemote extends RemoteProvider { url: `${this.baseUrl}/issues/`, title: `Open Issue # on ${this.name}`, - type: AutolinkType.Issue, + type: 'issue', description: `${this.name} Issue #`, }, ]; diff --git a/src/git/remotes/gitlab.ts b/src/git/remotes/gitlab.ts index 087073f..0a31e9c 100644 --- a/src/git/remotes/gitlab.ts +++ b/src/git/remotes/gitlab.ts @@ -2,7 +2,6 @@ import type { AuthenticationSession, Disposable, QuickInputButton, Range } from import { env, ThemeIcon, Uri, window } from 'vscode'; import type { Autolink, DynamicAutolinkReference } from '../../annotations/autolinks'; import type { AutolinkReference } from '../../config'; -import { AutolinkType } from '../../config'; import type { Container } from '../../container'; import type { IntegrationAuthenticationProvider, @@ -57,7 +56,7 @@ export class GitLabRemote extends RichRemoteProvider { url: `${this.baseUrl}/-/issues/`, title: `Open Issue # on ${this.name}`, - type: AutolinkType.Issue, + type: 'issue', description: `${this.name} Issue #`, }, { @@ -65,7 +64,7 @@ export class GitLabRemote extends RichRemoteProvider { url: `${this.baseUrl}/-/merge_requests/`, title: `Open Merge Request ! on ${this.name}`, - type: AutolinkType.PullRequest, + type: 'pullrequest', description: `${this.name} Merge Request !`, }, { @@ -108,7 +107,7 @@ export class GitLabRemote extends RichRemoteProvider { url: `${this.protocol}://${this.domain}/${repo}/-/issues/${num}`, title: `Open Issue # from ${repo} on ${this.name}`, - type: AutolinkType.Issue, + type: 'issue', description: `${this.name} Issue ${repo}#${num}`, }); } while (true); @@ -159,7 +158,7 @@ export class GitLabRemote extends RichRemoteProvider { url: `${this.protocol}://${this.domain}/${repo}/-/merge_requests/${num}`, title: `Open Merge Request ! from ${repo} on ${this.name}`, - type: AutolinkType.PullRequest, + type: 'pullrequest', description: `Merge Request !${num} from ${repo} on ${this.name}`, }); } while (true); diff --git a/src/hovers/hovers.ts b/src/hovers/hovers.ts index cecc1c4..6debcc6 100644 --- a/src/hovers/hovers.ts +++ b/src/hovers/hovers.ts @@ -268,11 +268,6 @@ export async function detailsMessage( const presence = getSettledValue(presenceResult); const previousLineComparisonUris = getSettledValue(previousLineComparisonUrisResult); - // Remove possible duplicate pull request - if (pr?.value != null && !pr.paused && enrichedResult?.value != null && !enrichedResult.paused) { - enrichedResult.value?.delete(pr.value.id); - } - const details = await CommitFormatter.fromTemplateAsync(options.format, commit, { enrichedAutolinks: enrichedResult?.value != null && !enrichedResult.paused ? enrichedResult.value : undefined, dateFormat: options.dateFormat === null ? 'MMMM Do, YYYY h:mma' : options.dateFormat, diff --git a/src/plus/github/github.ts b/src/plus/github/github.ts index f981ce4..4d1352b 100644 --- a/src/plus/github/github.ts +++ b/src/plus/github/github.ts @@ -59,7 +59,12 @@ import type { GitHubPullRequestState, GitHubTag, } from './models'; -import { fromGitHubIssueDetailed, fromGitHubPullRequest, fromGitHubPullRequestDetailed } from './models'; +import { + fromGitHubIssueDetailed, + fromGitHubPullRequest, + fromGitHubPullRequestDetailed, + fromGitHubPullRequestState, +} from './models'; const emptyPagedResult: PagedResult = Object.freeze({ values: [] }); const emptyBlameResult: GitHubBlame = Object.freeze({ ranges: [] }); @@ -487,6 +492,7 @@ export class GitHubApi implements Disposable { closedAt title url + state } ... on PullRequest { createdAt @@ -494,6 +500,7 @@ export class GitHubApi implements Disposable { closedAt title url + state } } } @@ -524,6 +531,7 @@ export class GitHubApi implements Disposable { closed: issue.closed, closedDate: issue.closedAt == null ? undefined : new Date(issue.closedAt), url: issue.url, + state: fromGitHubPullRequestState(issue.state), }; } catch (ex) { if (ex instanceof ProviderRequestNotFoundError) return undefined; diff --git a/src/plus/github/models.ts b/src/plus/github/models.ts index 03bc4d3..6ad2707 100644 --- a/src/plus/github/models.ts +++ b/src/plus/github/models.ts @@ -2,12 +2,8 @@ import type { Endpoints } from '@octokit/types'; import { GitFileIndexStatus } from '../../git/models/file'; import type { IssueLabel, IssueMember, IssueOrPullRequestType } from '../../git/models/issue'; import { Issue } from '../../git/models/issue'; -import { - PullRequest, - PullRequestMergeableState, - PullRequestReviewDecision, - PullRequestState, -} from '../../git/models/pullRequest'; +import type { PullRequestState } from '../../git/models/pullRequest'; +import { PullRequest, PullRequestMergeableState, PullRequestReviewDecision } from '../../git/models/pullRequest'; import type { RichRemoteProvider } from '../../git/remotes/richRemoteProvider'; export interface GitHubBlame { @@ -56,6 +52,7 @@ export interface GitHubIssueOrPullRequest { closedAt: string | null; title: string; url: string; + state: GitHubPullRequestState; } export interface GitHubPagedResult { @@ -185,15 +182,11 @@ export function fromGitHubPullRequest(pr: GitHubPullRequest, provider: RichRemot } export function fromGitHubPullRequestState(state: GitHubPullRequestState): PullRequestState { - return state === 'MERGED' - ? PullRequestState.Merged - : state === 'CLOSED' - ? PullRequestState.Closed - : PullRequestState.Open; + return state === 'MERGED' ? 'merged' : state === 'CLOSED' ? 'closed' : 'opened'; } export function toGitHubPullRequestState(state: PullRequestState): GitHubPullRequestState { - return state === PullRequestState.Merged ? 'MERGED' : state === PullRequestState.Closed ? 'CLOSED' : 'OPEN'; + return state === 'merged' ? 'MERGED' : state === 'closed' ? 'CLOSED' : 'OPEN'; } export function fromGitHubPullRequestReviewDecision( @@ -320,6 +313,7 @@ export function fromGitHubIssueDetailed(value: GitHubIssueDetailed, provider: Ri value.url, new Date(value.createdAt), value.closed, + fromGitHubPullRequestState(value.state), new Date(value.updatedAt), { name: value.author.login, diff --git a/src/plus/gitlab/gitlab.ts b/src/plus/gitlab/gitlab.ts index 1ec3d79..0c14952 100644 --- a/src/plus/gitlab/gitlab.ts +++ b/src/plus/gitlab/gitlab.ts @@ -18,7 +18,6 @@ import { import type { Account } from '../../git/models/author'; import type { DefaultBranch } from '../../git/models/defaultBranch'; import type { IssueOrPullRequest } from '../../git/models/issue'; -import { IssueOrPullRequestType } from '../../git/models/issue'; import { PullRequest } from '../../git/models/pullRequest'; import type { RepositoryMetadata } from '../../git/models/repositoryMetadata'; import type { RichRemoteProvider } from '../../git/remotes/richRemoteProvider'; @@ -38,10 +37,11 @@ import type { GitLabIssue, GitLabMergeRequest, GitLabMergeRequestREST, + GitLabMergeRequestState, GitLabProjectREST, GitLabUser, } from './models'; -import { fromGitLabMergeRequestREST, fromGitLabMergeRequestState, GitLabMergeRequestState } from './models'; +import { fromGitLabMergeRequestREST, fromGitLabMergeRequestState } from './models'; export class GitLabApi implements Disposable { private readonly _disposable: Disposable; @@ -318,13 +318,14 @@ export class GitLabApi implements Disposable { const issue = rsp.data.project.issue; return { provider: provider, - type: IssueOrPullRequestType.Issue, + type: 'issue', id: issue.iid, date: new Date(issue.createdAt), title: issue.title, closed: issue.state === 'closed', closedDate: issue.closedAt == null ? undefined : new Date(issue.closedAt), url: issue.webUrl, + state: issue.state === 'locked' ? 'closed' : issue.state, }; } @@ -332,7 +333,7 @@ export class GitLabApi implements Disposable { const mergeRequest = rsp.data.project.mergeRequest; return { provider: provider, - type: IssueOrPullRequestType.PullRequest, + type: 'pullrequest', id: mergeRequest.iid, date: new Date(mergeRequest.createdAt), title: mergeRequest.title, @@ -340,6 +341,7 @@ export class GitLabApi implements Disposable { // TODO@eamodio this isn't right, but GitLab doesn't seem to provide a closedAt on merge requests in GraphQL closedDate: mergeRequest.state === 'closed' ? new Date(mergeRequest.updatedAt) : undefined, url: mergeRequest.webUrl, + state: mergeRequest.state === 'locked' ? 'closed' : mergeRequest.state, }; } @@ -417,21 +419,21 @@ export class GitLabApi implements Disposable { : '' } ${ - options?.include?.includes(GitLabMergeRequestState.OPEN) + options?.include?.includes('opened') ? `opened: mergeRequests(sourceBranches: $branches state: opened sort: UPDATED_DESC first: 1) { ${fragment} }` : '' } ${ - options?.include?.includes(GitLabMergeRequestState.MERGED) + options?.include?.includes('merged') ? `merged: mergeRequests(sourceBranches: $branches state: merged sort: UPDATED_DESC first: 1) { ${fragment} }` : '' } ${ - options?.include?.includes(GitLabMergeRequestState.CLOSED) + options?.include?.includes('closed') ? `closed: mergeRequests(sourceBranches: $branches state: closed sort: UPDATED_DESC first: 1) { ${fragment} }` @@ -461,11 +463,11 @@ export class GitLabApi implements Disposable { } else { for (const state of options.include) { let mr; - if (state === GitLabMergeRequestState.OPEN) { + if (state === 'opened') { mr = rsp?.data?.project?.opened?.nodes?.[0]; - } else if (state === GitLabMergeRequestState.MERGED) { + } else if (state === 'merged') { mr = rsp?.data?.project?.merged?.nodes?.[0]; - } else if (state === GitLabMergeRequestState.CLOSED) { + } else if (state === 'closed') { mr = rsp?.data?.project?.closed?.nodes?.[0]; } @@ -490,7 +492,7 @@ export class GitLabApi implements Disposable { fromGitLabMergeRequestState(pr.state), new Date(pr.updatedAt), // TODO@eamodio this isn't right, but GitLab doesn't seem to provide a closedAt on merge requests in GraphQL - pr.state !== GitLabMergeRequestState.CLOSED ? undefined : new Date(pr.updatedAt), + pr.state !== 'closed' ? undefined : new Date(pr.updatedAt), pr.mergedAt == null ? undefined : new Date(pr.mergedAt), ); } catch (ex) { @@ -536,8 +538,7 @@ export class GitLabApi implements Disposable { if (mrs.length > 1) { mrs.sort( (a, b) => - (a.state === GitLabMergeRequestState.OPEN ? -1 : 1) - - (b.state === GitLabMergeRequestState.OPEN ? -1 : 1) || + (a.state === 'opened' ? -1 : 1) - (b.state === 'opened' ? -1 : 1) || new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime(), ); } diff --git a/src/plus/gitlab/models.ts b/src/plus/gitlab/models.ts index 1ddd7cf..f319ac1 100644 --- a/src/plus/gitlab/models.ts +++ b/src/plus/gitlab/models.ts @@ -1,4 +1,5 @@ -import { PullRequest, PullRequestState } from '../../git/models/pullRequest'; +import type { PullRequestState } from '../../git/models/pullRequest'; +import { PullRequest } from '../../git/models/pullRequest'; import type { RichRemoteProvider } from '../../git/remotes/richRemoteProvider'; export interface GitLabUser { @@ -60,27 +61,14 @@ export interface GitLabMergeRequest { webUrl: string; } -export enum GitLabMergeRequestState { - OPEN = 'opened', - CLOSED = 'closed', - MERGED = 'merged', - LOCKED = 'locked', -} +export type GitLabMergeRequestState = 'opened' | 'closed' | 'locked' | 'merged'; export function fromGitLabMergeRequestState(state: GitLabMergeRequestState): PullRequestState { - return state === GitLabMergeRequestState.MERGED - ? PullRequestState.Merged - : state === GitLabMergeRequestState.CLOSED || state === GitLabMergeRequestState.LOCKED - ? PullRequestState.Closed - : PullRequestState.Open; + return state === 'locked' ? 'closed' : state; } export function toGitLabMergeRequestState(state: PullRequestState): GitLabMergeRequestState { - return state === PullRequestState.Merged - ? GitLabMergeRequestState.MERGED - : state === PullRequestState.Closed - ? GitLabMergeRequestState.CLOSED - : GitLabMergeRequestState.OPEN; + return state; } export interface GitLabMergeRequestREST { diff --git a/src/system/string.ts b/src/system/string.ts index d35bec3..6129fa1 100644 --- a/src/system/string.ts +++ b/src/system/string.ts @@ -4,6 +4,10 @@ import { CharCode } from '../constants'; export { fromBase64, base64 } from '@env/base64'; +export function capitalize(s: string) { + return `${s[0].toLocaleUpperCase()}${s.slice(1)}`; +} + let compareCollator: Intl.Collator | undefined; export function compareIgnoreCase(a: string, b: string): 0 | -1 | 1 { if (compareCollator == null) { diff --git a/src/views/nodes/autolinkedItemNode.ts b/src/views/nodes/autolinkedItemNode.ts index 95256e9..2da9817 100644 --- a/src/views/nodes/autolinkedItemNode.ts +++ b/src/views/nodes/autolinkedItemNode.ts @@ -1,13 +1,8 @@ import { MarkdownString, ThemeIcon, TreeItem, TreeItemCollapsibleState } from 'vscode'; import type { Autolink } from '../../annotations/autolinks'; -import { AutolinkType } from '../../config'; import { GitUri } from '../../git/gitUri'; import type { IssueOrPullRequest } from '../../git/models/issue'; -import { - getIssueOrPullRequestMarkdownIcon, - getIssueOrPullRequestThemeIcon, - IssueOrPullRequestType, -} from '../../git/models/issue'; +import { getIssueOrPullRequestMarkdownIcon, getIssueOrPullRequestThemeIcon } from '../../git/models/issue'; import { fromNow } from '../../system/date'; import { isPromise } from '../../system/promise'; import type { ViewsWithCommits } from '../viewBase'; @@ -62,7 +57,7 @@ export class AutolinkedItemNode extends ViewNode { ? 'loading~spin' : autolink.type == null ? 'link' - : autolink.type === AutolinkType.PullRequest + : autolink.type === 'pullrequest' ? 'git-pull-request' : 'issues', ); @@ -74,7 +69,7 @@ export class AutolinkedItemNode extends ViewNode { : `${ autolink.type == null ? 'Autolinked' - : autolink.type === AutolinkType.PullRequest + : autolink.type === 'pullrequest' ? 'Autolinked Pull Request' : 'Autolinked Issue' } ${autolink.prefix}${autolink.id}` @@ -88,14 +83,11 @@ export class AutolinkedItemNode extends ViewNode { const item = new TreeItem(`${enriched.id}: ${enriched.title}`, TreeItemCollapsibleState.None); item.description = relativeTime; item.iconPath = getIssueOrPullRequestThemeIcon(enriched); - item.contextValue = - enriched.type === IssueOrPullRequestType.PullRequest - ? ContextValues.PullRequest - : ContextValues.AutolinkedIssue; + item.contextValue = enriched.type === 'pullrequest' ? ContextValues.PullRequest : ContextValues.AutolinkedIssue; - const linkTitle = ` "Open ${ - enriched.type === IssueOrPullRequestType.PullRequest ? 'Pull Request' : 'Issue' - } \\#${enriched.id} on ${enriched.provider.name}"`; + const linkTitle = ` "Open ${enriched.type === 'pullrequest' ? 'Pull Request' : 'Issue'} \\#${enriched.id} on ${ + enriched.provider.name + }"`; const tooltip = new MarkdownString( `${getIssueOrPullRequestMarkdownIcon(enriched)} [**${enriched.title.trim()}**](${ enriched.url diff --git a/src/views/nodes/branchNode.ts b/src/views/nodes/branchNode.ts index 720fb03..6a5dd2c 100644 --- a/src/views/nodes/branchNode.ts +++ b/src/views/nodes/branchNode.ts @@ -6,8 +6,7 @@ import { GlyphChars } from '../../constants'; import type { GitUri } from '../../git/gitUri'; import type { GitBranch } from '../../git/models/branch'; import type { GitLog } from '../../git/models/log'; -import type { PullRequest } from '../../git/models/pullRequest'; -import { PullRequestState } from '../../git/models/pullRequest'; +import type { PullRequest , PullRequestState } from '../../git/models/pullRequest'; import type { GitBranchReference } from '../../git/models/reference'; import { GitRemote, GitRemoteType } from '../../git/models/remote'; import type { Repository } from '../../git/models/repository'; @@ -152,7 +151,7 @@ export class BranchNode extends ViewRefNode(); const prPromise = this.getAssociatedPullRequest( branch, - this.root ? { include: [PullRequestState.Open, PullRequestState.Merged] } : undefined, + this.root ? { include: ['opened', 'merged'] } : undefined, ); queueMicrotask(async () => { diff --git a/src/views/nodes/commitNode.ts b/src/views/nodes/commitNode.ts index 3739478..d70d6d5 100644 --- a/src/views/nodes/commitNode.ts +++ b/src/views/nodes/commitNode.ts @@ -256,11 +256,6 @@ export class CommitNode extends ViewRefNode { item.id = this.id; item.contextValue = ContextValues.PullRequest; item.description = `${this.pullRequest.state}, ${this.pullRequest.formatDateFromNow()}`; - item.iconPath = PullRequest.getThemeIcon(this.pullRequest); + item.iconPath = getIssueOrPullRequestThemeIcon(this.pullRequest); const tooltip = new MarkdownString('', true); tooltip.supportHtml = true; @@ -69,15 +70,13 @@ export class PullRequestNode extends ViewNode { const linkTitle = ` "Open Pull Request \\#${this.pullRequest.id} on ${this.pullRequest.provider.name}"`; tooltip.appendMarkdown( - `${PullRequest.getMarkdownIcon(this.pullRequest)} [**${this.pullRequest.title.trim()}**](${ + `${getIssueOrPullRequestMarkdownIcon(this.pullRequest)} [**${this.pullRequest.title.trim()}**](${ this.pullRequest.url }${linkTitle}) \\\n[#${this.pullRequest.id}](${this.pullRequest.url}${linkTitle}) by [@${ this.pullRequest.author.name }](${this.pullRequest.author.url} "Open @${this.pullRequest.author.name} on ${ this.pullRequest.provider.name - }") was ${ - this.pullRequest.state === PullRequestState.Open ? 'opened' : this.pullRequest.state.toLowerCase() - } ${this.pullRequest.formatDateFromNow()}`, + }") was ${this.pullRequest.state.toLowerCase()} ${this.pullRequest.formatDateFromNow()}`, ); item.tooltip = tooltip; diff --git a/src/views/nodes/rebaseStatusNode.ts b/src/views/nodes/rebaseStatusNode.ts index a2f9680..6a4511d 100644 --- a/src/views/nodes/rebaseStatusNode.ts +++ b/src/views/nodes/rebaseStatusNode.ts @@ -215,11 +215,6 @@ export class RebaseCommitNode extends ViewRefNode { if (pullRequest === undefined && this.getState('pendingPullRequest') === undefined) { onCompleted = defer(); const prPromise = this.getAssociatedPullRequest(branch, { - include: [PullRequestState.Open, PullRequestState.Merged], + include: ['opened', 'merged'], }); queueMicrotask(async () => { diff --git a/src/webviews/apps/commitDetails/components/commit-details-app.ts b/src/webviews/apps/commitDetails/components/commit-details-app.ts index 2c3fc22..446efe1 100644 --- a/src/webviews/apps/commitDetails/components/commit-details-app.ts +++ b/src/webviews/apps/commitDetails/components/commit-details-app.ts @@ -3,7 +3,10 @@ import { html, LitElement, nothing } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; import { unsafeHTML } from 'lit/directives/unsafe-html.js'; import { when } from 'lit/directives/when.js'; +import type { Autolink } from '../../../../annotations/autolinks'; import { ViewFilesLayout } from '../../../../config'; +import type { IssueOrPullRequest } from '../../../../git/models/issue'; +import type { PullRequestShape } from '../../../../git/models/pullRequest'; import type { HierarchicalItem } from '../../../../system/array'; import { makeHierarchical } from '../../../../system/array'; import type { Serialized } from '../../../../system/serialize'; @@ -147,21 +150,44 @@ export class GlCommitDetailsApp extends LitElement { return undefined; } - const autolinkedIssuesCount = this.state?.autolinkedIssues?.length ?? 0; - let autolinksCount = this.state?.selected?.autolinks?.length ?? 0; - let count = autolinksCount; - const hasPullRequest = this.state?.pullRequest != null; - const hasAutolinks = hasPullRequest || autolinkedIssuesCount > 0 || autolinksCount > 0; - - let dedupedAutolinks = this.state?.selected?.autolinks; - if (hasAutolinks) { - if (dedupedAutolinks?.length && autolinkedIssuesCount) { - dedupedAutolinks = dedupedAutolinks.filter( - autolink => !this.state?.autolinkedIssues?.some(issue => issue.url === autolink.url), - ); - - autolinksCount = dedupedAutolinks?.length ?? 0; - count = (hasPullRequest ? 1 : 0) + autolinkedIssuesCount + autolinksCount; + const deduped = new Map< + string, + | { type: 'autolink'; value: Serialized } + | { type: 'issue'; value: Serialized } + | { type: 'pr'; value: Serialized } + >(); + + if (this.state?.selected?.autolinks != null) { + for (const autolink of this.state.selected.autolinks) { + deduped.set(autolink.id, { type: 'autolink', value: autolink }); + } + } + + if (this.state?.autolinkedIssues != null) { + for (const issue of this.state.autolinkedIssues) { + deduped.set(issue.id, { type: 'issue', value: issue }); + } + } + + if (this.state?.pullRequest != null) { + deduped.set(this.state.pullRequest.id, { type: 'pr', value: this.state.pullRequest }); + } + + const autolinks: Serialized[] = []; + const issues: Serialized[] = []; + const prs: Serialized[] = []; + + for (const item of deduped.values()) { + switch (item.type) { + case 'autolink': + autolinks.push(item.value); + break; + case 'issue': + issues.push(item.value); + break; + case 'pr': + prs.push(item.value); + break; } } @@ -174,7 +200,7 @@ export class GlCommitDetailsApp extends LitElement { > Autolinks ${this.state?.includeRichContent || autolinksCount ? `${count} found ` : ''}${this.state + >${this.state?.includeRichContent || deduped.size ? `${deduped.size} found ` : ''}${this.state ?.includeRichContent ? '' : '…'} `, () => { - if (!hasAutolinks || count === 0) { + if (deduped.size === 0) { return html`

@@ -211,20 +237,21 @@ export class GlCommitDetailsApp extends LitElement { } return html`

- ${dedupedAutolinks != null && dedupedAutolinks.length > 0 + ${autolinks.length ? html` ` : undefined} - ${this.state?.autolinkedIssues?.length + ${issues.length ? html`
- ${this.state.autolinkedIssues.map( + ${issues.map( issue => html` `, )} diff --git a/src/webviews/apps/shared/components/rich/issue-pull-request.ts b/src/webviews/apps/shared/components/rich/issue-pull-request.ts index 4e37796..5580e9c 100644 --- a/src/webviews/apps/shared/components/rich/issue-pull-request.ts +++ b/src/webviews/apps/shared/components/rich/issue-pull-request.ts @@ -50,10 +50,13 @@ export class IssuePullRequest extends LitElement { date = ''; @property() - status = 'merged'; + status: 'opened' | 'closed' | 'merged' = 'merged'; @property() - key = '#1999'; + type: 'autolink' | 'issue' | 'pr' = 'autolink'; + + @property() + key = ''; renderDate() { if (this.date === '') { @@ -63,16 +66,28 @@ export class IssuePullRequest extends LitElement { } override render() { - let icon = 'issues'; - switch (this.status.toLowerCase()) { - case '': - icon = 'link'; + let icon; + switch (this.type) { + case 'issue': + icon = this.status === 'closed' ? 'pass' : 'issues'; break; - case 'merged': - icon = 'git-merge'; + case 'pr': + switch (this.status) { + case 'merged': + icon = 'git-merge'; + break; + case 'closed': + icon = 'git-pull-request-closed'; + break; + case 'opened': + default: + icon = 'git-pull-request'; + break; + } break; - case 'closed': - icon = 'pass'; + case 'autolink': + default: + icon = 'link'; break; } @@ -81,7 +96,7 @@ export class IssuePullRequest extends LitElement {

${this.name}

-

${this.key} ${this.status === '' ? this.status : nothing} ${this.renderDate()}

+

${this.key} ${this.status ? this.status : nothing} ${this.renderDate()}

`; } } diff --git a/src/webviews/commitDetails/commitDetailsWebview.ts b/src/webviews/commitDetails/commitDetailsWebview.ts index fc6430d..e61216f 100644 --- a/src/webviews/commitDetails/commitDetailsWebview.ts +++ b/src/webviews/commitDetails/commitDetailsWebview.ts @@ -523,11 +523,6 @@ export class CommitDetailsWebviewProvider implements WebviewProvider { '1', 'Supercharged', 'https://github.com/gitkraken/vscode-gitlens/pulls/1', - PullRequestState.Merged, + 'merged', new Date('Sat, 12 Nov 2016 19:41:00 GMT'), undefined, new Date('Sat, 12 Nov 2016 20:41:00 GMT'),