Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
- Fixes [#3571](https://github.com/gitkraken/vscode-gitlens/issues/3571) - Gitlens fails to register buttons on top-right corner — thanks to [PR #3605](https://github.com/gitkraken/vscode-gitlens/pull/3605) by Jean Pierre ([@jeanp413](https://github.com/jeanp413))
- Fixes an issue where virtual repositories for GitHub PRs from forks wouldn't load properly
- Fixes an issue where deleting a worktree would not always remove the worktree from the view
- Fixes [#3617](https://github.com/gitkraken/vscode-gitlens/issues/3617) - Auto-links not working for alphanumberic issue numbers

### Engineering

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3410,7 +3410,7 @@
"properties": {
"prefix": {
"type": "string",
"description": "Specifies the short prefix to use to generate autolinks for the external resource"
"description": "Specifies the short prefix to match to generate autolinks for the external resource, e.g. `GH-` or `JIRA-`"
},
"title": {
"type": [
Expand Down
83 changes: 45 additions & 38 deletions src/annotations/autolinks.ts → src/autolinks.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,50 @@
import type { ConfigurationChangeEvent } from 'vscode';
import { Disposable } from 'vscode';
import type { AutolinkReference, AutolinkType } from '../config';
import { GlyphChars } from '../constants';
import type { IntegrationId } from '../constants.integrations';
import { IssueIntegrationId } from '../constants.integrations';
import type { Container } from '../container';
import type { IssueOrPullRequest } from '../git/models/issue';
import { getIssueOrPullRequestHtmlIcon, getIssueOrPullRequestMarkdownIcon } from '../git/models/issue';
import type { GitRemote } from '../git/models/remote';
import type { ProviderReference } from '../git/models/remoteProvider';
import type { ResourceDescriptor } from '../plus/integrations/integration';
import { fromNow } from '../system/date';
import { debug } from '../system/decorators/log';
import { encodeUrl } from '../system/encoding';
import { join, map } from '../system/iterable';
import { Logger } from '../system/logger';
import { escapeMarkdown } from '../system/markdown';
import type { MaybePausedResult } from '../system/promise';
import { capitalize, encodeHtmlWeak, escapeRegex, getSuperscript } from '../system/string';
import { configuration } from '../system/vscode/configuration';
import { GlyphChars } from './constants';
import type { IntegrationId } from './constants.integrations';
import { IssueIntegrationId } from './constants.integrations';
import type { Container } from './container';
import type { IssueOrPullRequest } from './git/models/issue';
import { getIssueOrPullRequestHtmlIcon, getIssueOrPullRequestMarkdownIcon } from './git/models/issue';
import type { GitRemote } from './git/models/remote';
import type { ProviderReference } from './git/models/remoteProvider';
import type { ResourceDescriptor } from './plus/integrations/integration';
import { fromNow } from './system/date';
import { debug } from './system/decorators/log';
import { encodeUrl } from './system/encoding';
import { join, map } from './system/iterable';
import { Logger } from './system/logger';
import { escapeMarkdown } from './system/markdown';
import type { MaybePausedResult } from './system/promise';
import { capitalize, encodeHtmlWeak, escapeRegex, getSuperscript } from './system/string';
import { configuration } from './system/vscode/configuration';

const emptyAutolinkMap = Object.freeze(new Map<string, Autolink>());

const numRegex = /<num>/g;

export interface Autolink {
export type AutolinkType = 'issue' | 'pullrequest';

export interface AutolinkReference {
/** Short prefix to match to generate autolinks for the external resource */
readonly prefix: string;
/** URL of the external resource to link to */
readonly url: string;
/** Whether alphanumeric characters should be allowed in `<num>` */
readonly alphanumeric: boolean;
/** Whether case should be ignored when matching the prefix */
readonly ignoreCase: boolean;
readonly title: string | undefined;

readonly type?: AutolinkType;
readonly description?: string;
readonly descriptor?: ResourceDescriptor;
}

export interface Autolink extends AutolinkReference {
provider?: ProviderReference;
id: string;
prefix: string;
title?: string;
url: string;

type?: AutolinkType;
description?: string;

descriptor?: ResourceDescriptor;
tokenize?:
| ((
text: string,
Expand Down Expand Up @@ -69,8 +79,10 @@ export function serializeAutolink(value: Autolink): Autolink {
: undefined,
id: value.id,
prefix: value.prefix,
title: value.title,
url: value.url,
alphanumeric: value.alphanumeric,
ignoreCase: value.ignoreCase,
title: value.title,
type: value.type,
description: value.description,
descriptor: value.descriptor,
Expand Down Expand Up @@ -140,18 +152,12 @@ export class Autolinks implements Disposable {
this._references =
autolinks
?.filter(a => a.prefix && a.url)
/**
* Only allow properties defined by {@link AutolinkReference}
*/
?.map(a => ({
prefix: a.prefix,
url: a.url,
title: a.title,
alphanumeric: a.alphanumeric,
ignoreCase: a.ignoreCase,
type: a.type,
description: a.description,
descriptor: a.descriptor,
alphanumeric: a.alphanumeric ?? false,
ignoreCase: a.ignoreCase ?? false,
title: a.title ?? undefined,
})) ?? [];
}
}
Expand Down Expand Up @@ -237,8 +243,9 @@ export class Autolinks implements Disposable {
id: num,
prefix: ref.prefix,
url: ref.url?.replace(numRegex, num),
alphanumeric: ref.alphanumeric,
ignoreCase: ref.ignoreCase,
title: ref.title?.replace(numRegex, num),

type: ref.type,
description: ref.description?.replace(numRegex, num),
descriptor: ref.descriptor,
Expand Down
20 changes: 9 additions & 11 deletions src/config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { SupportedAIModels, VSCodeAIModels } from './constants.ai';
import type { ResourceDescriptor } from './plus/integrations/integration';
import type { DateTimeFormat } from './system/date';
import type { LogLevel } from './system/logger.constants';

Expand All @@ -18,7 +17,7 @@ export interface Config {
};
};
};
readonly autolinks: AutolinkReference[] | null;
readonly autolinks: AutolinkConfig[] | null;
readonly blame: {
readonly avatars: boolean;
readonly compact: boolean;
Expand Down Expand Up @@ -251,18 +250,17 @@ export interface Config {
}

export type AnnotationsToggleMode = 'file' | 'window';
export type AutolinkType = 'issue' | 'pullrequest';

export interface AutolinkReference {
export interface AutolinkConfig {
/** Short prefix to match to generate autolinks for the external resource */
readonly prefix: string;
/** URL of the external resource to link to */
readonly url: string;
readonly title?: string;
readonly alphanumeric?: boolean;
readonly ignoreCase?: boolean;

readonly type?: AutolinkType;
readonly description?: string;
readonly descriptor?: ResourceDescriptor;
/** Whether alphanumeric characters should be allowed in `<num>` */
readonly alphanumeric: boolean;
/** Whether case should be ignored when matching the prefix */
readonly ignoreCase: boolean;
readonly title: string | null;
}

export type BlameHighlightLocations = 'gutter' | 'line' | 'overview';
Expand Down
2 changes: 1 addition & 1 deletion src/container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { getSupportedGitProviders, getSupportedRepositoryPathMappingProvider } f
import type { ConfigurationChangeEvent, Disposable, Event, ExtensionContext } from 'vscode';
import { EventEmitter, ExtensionMode, Uri } from 'vscode';
import type { AIProviderService } from './ai/aiProviderService';
import { Autolinks } from './annotations/autolinks';
import { FileAnnotationController } from './annotations/fileAnnotationController';
import { LineAnnotationController } from './annotations/lineAnnotationController';
import { ActionRunners } from './api/actionRunners';
import { Autolinks } from './autolinks';
import { setDefaultGravatarsStyle } from './avatars';
import { CacheProvider } from './cache';
import { GitCodeLensController } from './codelens/codeLensController';
Expand Down
2 changes: 1 addition & 1 deletion src/git/formatters/commitFormatter.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import type { Uri } from 'vscode';
import type { MaybeEnrichedAutolink } from '../../annotations/autolinks';
import type {
Action,
ActionContext,
HoverCommandsActionContext,
OpenPullRequestActionContext,
} from '../../api/gitlens';
import type { MaybeEnrichedAutolink } from '../../autolinks';
import { getPresenceDataUri } from '../../avatars';
import { Command } from '../../commands/base';
import { DiffWithCommand } from '../../commands/diffWith';
Expand Down
2 changes: 1 addition & 1 deletion src/git/models/commit.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Uri } from 'vscode';
import type { EnrichedAutolink } from '../../annotations/autolinks';
import type { EnrichedAutolink } from '../../autolinks';
import { getAvatarUri, getCachedAvatarUri } from '../../avatars';
import type { GravatarDefaultStyle } from '../../config';
import { GlyphChars } from '../../constants';
Expand Down
7 changes: 5 additions & 2 deletions src/git/remotes/azure-devops.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { Range, Uri } from 'vscode';
import type { DynamicAutolinkReference } from '../../annotations/autolinks';
import type { AutolinkReference } from '../../config';
import type { AutolinkReference, DynamicAutolinkReference } from '../../autolinks';
import type { GkProviderId } from '../../gk/models/repositoryIdentities';
import type { Brand, Unbrand } from '../../system/brand';
import type { Repository } from '../models/repository';
Expand Down Expand Up @@ -65,6 +64,8 @@ export class AzureDevOpsRemote extends RemoteProvider {
{
prefix: '#',
url: `${workUrl}/_workitems/edit/<num>`,
alphanumeric: false,
ignoreCase: false,
title: `Open Work Item #<num> on ${this.name}`,

type: 'issue',
Expand All @@ -74,6 +75,8 @@ export class AzureDevOpsRemote extends RemoteProvider {
// Default Pull request message when merging a PR in ADO. Will not catch commits & pushes following a different pattern.
prefix: 'PR ',
url: `${this.baseUrl}/pullrequest/<num>`,
alphanumeric: false,
ignoreCase: false,
title: `Open Pull Request #<num> on ${this.name}`,

type: 'pullrequest',
Expand Down
6 changes: 4 additions & 2 deletions src/git/remotes/bitbucket-server.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { Range, Uri } from 'vscode';
import type { DynamicAutolinkReference } from '../../annotations/autolinks';
import type { AutolinkReference } from '../../config';
import type { AutolinkReference, DynamicAutolinkReference } from '../../autolinks';
import type { GkProviderId } from '../../gk/models/repositoryIdentities';
import type { Brand, Unbrand } from '../../system/brand';
import { isSha } from '../models/reference';
Expand All @@ -23,13 +22,16 @@ export class BitbucketServerRemote extends RemoteProvider {
{
prefix: 'issue #',
url: `${this.baseUrl}/issues/<num>`,
alphanumeric: false,
ignoreCase: true,
title: `Open Issue #<num> on ${this.name}`,

type: 'issue',
description: `${this.name} Issue #<num>`,
},
{
prefix: 'pull request #',
alphanumeric: false,
ignoreCase: true,
url: `${this.baseUrl}/pull-requests/<num>`,
title: `Open Pull Request #<num> on ${this.name}`,
Expand Down
7 changes: 5 additions & 2 deletions src/git/remotes/bitbucket.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { Range, Uri } from 'vscode';
import type { DynamicAutolinkReference } from '../../annotations/autolinks';
import type { AutolinkReference } from '../../config';
import type { AutolinkReference, DynamicAutolinkReference } from '../../autolinks';
import type { GkProviderId } from '../../gk/models/repositoryIdentities';
import type { Brand, Unbrand } from '../../system/brand';
import { isSha } from '../models/reference';
Expand All @@ -23,6 +22,8 @@ export class BitbucketRemote extends RemoteProvider {
{
prefix: 'issue #',
url: `${this.baseUrl}/issues/<num>`,
alphanumeric: false,
ignoreCase: true,
title: `Open Issue #<num> on ${this.name}`,

type: 'issue',
Expand All @@ -31,6 +32,8 @@ export class BitbucketRemote extends RemoteProvider {
{
prefix: 'pull request #',
url: `${this.baseUrl}/pull-requests/<num>`,
alphanumeric: false,
ignoreCase: true,
title: `Open Pull Request #<num> on ${this.name}`,

type: 'pullrequest',
Expand Down
6 changes: 3 additions & 3 deletions src/git/remotes/gerrit.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { Range, Uri } from 'vscode';
import type { DynamicAutolinkReference } from '../../annotations/autolinks';
import type { AutolinkReference } from '../../config';
import type { AutolinkReference, DynamicAutolinkReference } from '../../autolinks';
import type { GkProviderId } from '../../gk/models/repositoryIdentities';
import { isSha } from '../models/reference';
import type { Repository } from '../models/repository';
Expand Down Expand Up @@ -41,8 +40,9 @@ export class GerritRemote extends RemoteProvider {
{
prefix: 'Change-Id: ',
url: `${this.baseReviewUrl}/q/<num>`,
title: `Open Change #<num> on ${this.name}`,
alphanumeric: true,
ignoreCase: true,
title: `Open Change #<num> on ${this.name}`,

description: `${this.name} Change #<num>`,
},
Expand Down
5 changes: 3 additions & 2 deletions src/git/remotes/gitea.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { Range, Uri } from 'vscode';
import type { DynamicAutolinkReference } from '../../annotations/autolinks';
import type { AutolinkReference } from '../../config';
import type { AutolinkReference, DynamicAutolinkReference } from '../../autolinks';
import type { GkProviderId } from '../../gk/models/repositoryIdentities';
import { isSha } from '../models/reference';
import type { Repository } from '../models/repository';
Expand All @@ -22,6 +21,8 @@ export class GiteaRemote extends RemoteProvider {
{
prefix: '#',
url: `${this.baseUrl}/issues/<num>`,
alphanumeric: false,
ignoreCase: false,
title: `Open Issue #<num> on ${this.name}`,

type: 'issue',
Expand Down
10 changes: 7 additions & 3 deletions src/git/remotes/github.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { Range } from 'vscode';
import { Uri } from 'vscode';
import type { Autolink, DynamicAutolinkReference, MaybeEnrichedAutolink } from '../../annotations/autolinks';
import type { AutolinkReference } from '../../config';
import type { Autolink, AutolinkReference, DynamicAutolinkReference, MaybeEnrichedAutolink } from '../../autolinks';
import { GlyphChars } from '../../constants';
import type { GkProviderId } from '../../gk/models/repositoryIdentities';
import type { GitHubRepositoryDescriptor } from '../../plus/integrations/providers/github';
Expand Down Expand Up @@ -41,15 +40,18 @@ export class GitHubRemote extends RemoteProvider<GitHubRepositoryDescriptor> {
{
prefix: '#',
url: `${this.baseUrl}/issues/<num>`,
alphanumeric: false,
ignoreCase: false,
title: `Open Issue or Pull Request #<num> on ${this.name}`,

description: `${this.name} Issue or Pull Request #<num>`,
},
{
prefix: 'gh-',
url: `${this.baseUrl}/issues/<num>`,
title: `Open Issue or Pull Request #<num> on ${this.name}`,
alphanumeric: false,
ignoreCase: true,
title: `Open Issue or Pull Request #<num> on ${this.name}`,

description: `${this.name} Issue or Pull Request #<num>`,
},
Expand Down Expand Up @@ -138,6 +140,8 @@ export class GitHubRemote extends RemoteProvider<GitHubRepositoryDescriptor> {
id: num,
prefix: `${ownerAndRepo}#`,
url: `${this.protocol}://${this.domain}/${ownerAndRepo}/issues/${num}`,
alphanumeric: false,
ignoreCase: true,
title: `Open Issue or Pull Request #<num> from ${ownerAndRepo} on ${this.name}`,

description: `${this.name} Issue or Pull Request ${ownerAndRepo}#${num}`,
Expand Down
Loading
Loading