Browse Source

Refactors/renames remote provider code

- No functional changes, only file splitting, renaming, etc
main
Eric Amodio 2 years ago
parent
commit
4d6bb309a6
43 changed files with 954 additions and 935 deletions
  1. +1
    -1
      src/codelens/codeLensProvider.ts
  2. +3
    -2
      src/commands/createPullRequestOnRemote.ts
  3. +1
    -1
      src/commands/openBranchOnRemote.ts
  4. +1
    -1
      src/commands/openBranchesOnRemote.ts
  5. +1
    -1
      src/commands/openCommitOnRemote.ts
  6. +1
    -1
      src/commands/openComparisonOnRemote.ts
  7. +1
    -1
      src/commands/openCurrentBranchOnRemote.ts
  8. +1
    -1
      src/commands/openFileOnRemote.ts
  9. +3
    -2
      src/commands/openOnRemote.ts
  10. +1
    -1
      src/commands/openRepoOnRemote.ts
  11. +1
    -1
      src/commands/quickCommand.steps.ts
  12. +1
    -1
      src/commands/remoteProviders.ts
  13. +5
    -4
      src/env/node/git/localGitProvider.ts
  14. +1
    -1
      src/git/formatters/commitFormatter.ts
  15. +3
    -2
      src/git/gitProvider.ts
  16. +10
    -5
      src/git/gitProviderService.ts
  17. +2
    -1
      src/git/models/branch.ts
  18. +1
    -1
      src/git/models/commit.ts
  19. +3
    -3
      src/git/models/remote.ts
  20. +83
    -0
      src/git/models/remoteResource.ts
  21. +4
    -4
      src/git/models/repository.ts
  22. +3
    -3
      src/git/parsers/remoteParser.ts
  23. +1
    -1
      src/git/remotes/azure-devops.ts
  24. +1
    -1
      src/git/remotes/bitbucket-server.ts
  25. +1
    -1
      src/git/remotes/bitbucket.ts
  26. +1
    -1
      src/git/remotes/custom.ts
  27. +1
    -1
      src/git/remotes/gerrit.ts
  28. +1
    -1
      src/git/remotes/gitea.ts
  29. +1
    -1
      src/git/remotes/github.ts
  30. +1
    -1
      src/git/remotes/gitlab.ts
  31. +157
    -0
      src/git/remotes/remoteProvider.ts
  32. +1
    -1
      src/git/remotes/remoteProviders.ts
  33. +5
    -238
      src/git/remotes/richRemoteProvider.ts
  34. +1
    -1
      src/plus/github/github.ts
  35. +4
    -3
      src/plus/github/githubGitProvider.ts
  36. +1
    -1
      src/plus/github/models.ts
  37. +1
    -1
      src/plus/gitlab/gitlab.ts
  38. +1
    -1
      src/plus/gitlab/models.ts
  39. +3
    -2
      src/quickpicks/remoteProviderPicker.ts
  40. +1
    -1
      src/views/nodes/commitNode.ts
  41. +1
    -1
      src/views/nodes/remoteNode.ts

+ 1
- 1
src/codelens/codeLensProvider.ts View File

@ -25,7 +25,7 @@ import type { Container } from '../container';
import type { GitUri } from '../git/gitUri';
import type { GitBlame, GitBlameLines } from '../git/models/blame';
import type { GitCommit } from '../git/models/commit';
import { RemoteResourceType } from '../git/remotes/provider';
import { RemoteResourceType } from '../git/models/remoteResource';
import { Logger } from '../logger';
import { asCommand, executeCoreCommand } from '../system/command';
import { is, once } from '../system/function';

+ 3
- 2
src/commands/createPullRequestOnRemote.ts View File

@ -1,8 +1,9 @@
import { Commands } from '../constants';
import type { Container } from '../container';
import type { GitRemote } from '../git/models/remote';
import type { RemoteProvider, RemoteResource } from '../git/remotes/provider';
import { RemoteResourceType } from '../git/remotes/provider';
import type { RemoteResource } from '../git/models/remoteResource';
import { RemoteResourceType } from '../git/models/remoteResource';
import type { RemoteProvider } from '../git/remotes/remoteProvider';
import { command, executeCommand } from '../system/command';
import { Command } from './base';
import type { OpenOnRemoteCommandArgs } from './openOnRemote';

+ 1
- 1
src/commands/openBranchOnRemote.ts View File

@ -3,7 +3,7 @@ import { window } from 'vscode';
import { Commands } from '../constants';
import type { Container } from '../container';
import { GitUri } from '../git/gitUri';
import { RemoteResourceType } from '../git/remotes/provider';
import { RemoteResourceType } from '../git/models/remoteResource';
import { Logger } from '../logger';
import { CommandQuickPickItem } from '../quickpicks/items/common';
import { ReferencePicker, ReferencesQuickPickIncludes } from '../quickpicks/referencePicker';

+ 1
- 1
src/commands/openBranchesOnRemote.ts View File

@ -3,7 +3,7 @@ import { window } from 'vscode';
import { Commands } from '../constants';
import type { Container } from '../container';
import { GitUri } from '../git/gitUri';
import { RemoteResourceType } from '../git/remotes/provider';
import { RemoteResourceType } from '../git/models/remoteResource';
import { Logger } from '../logger';
import { RepositoryPicker } from '../quickpicks/repositoryPicker';
import { command, executeCommand } from '../system/command';

+ 1
- 1
src/commands/openCommitOnRemote.ts View File

@ -4,7 +4,7 @@ import { Commands } from '../constants';
import type { Container } from '../container';
import { GitUri } from '../git/gitUri';
import { GitRevision } from '../git/models/reference';
import { RemoteResourceType } from '../git/remotes/provider';
import { RemoteResourceType } from '../git/models/remoteResource';
import { Logger } from '../logger';
import { showFileNotUnderSourceControlWarningMessage } from '../messages';
import { command, executeCommand } from '../system/command';

+ 1
- 1
src/commands/openComparisonOnRemote.ts View File

@ -1,7 +1,7 @@
import { window } from 'vscode';
import { Commands } from '../constants';
import type { Container } from '../container';
import { RemoteResourceType } from '../git/remotes/provider';
import { RemoteResourceType } from '../git/models/remoteResource';
import { Logger } from '../logger';
import { command, executeCommand } from '../system/command';
import { ResultsCommitsNode } from '../views/nodes/resultsCommitsNode';

+ 1
- 1
src/commands/openCurrentBranchOnRemote.ts View File

@ -3,7 +3,7 @@ import { window } from 'vscode';
import { Commands } from '../constants';
import type { Container } from '../container';
import { GitUri } from '../git/gitUri';
import { RemoteResourceType } from '../git/remotes/provider';
import { RemoteResourceType } from '../git/models/remoteResource';
import { Logger } from '../logger';
import { RepositoryPicker } from '../quickpicks/repositoryPicker';
import { command, executeCommand } from '../system/command';

+ 1
- 1
src/commands/openFileOnRemote.ts View File

@ -7,7 +7,7 @@ import type { Container } from '../container';
import { GitUri } from '../git/gitUri';
import { getBranchNameWithoutRemote, getRemoteNameFromBranchName } from '../git/models/branch';
import { GitRevision } from '../git/models/reference';
import { RemoteResourceType } from '../git/remotes/provider';
import { RemoteResourceType } from '../git/models/remoteResource';
import { Logger } from '../logger';
import { ReferencePicker } from '../quickpicks/referencePicker';
import { command, executeCommand } from '../system/command';

+ 3
- 2
src/commands/openOnRemote.ts View File

@ -2,8 +2,9 @@ import { Commands, GlyphChars } from '../constants';
import type { Container } from '../container';
import { GitRevision } from '../git/models/reference';
import { GitRemote } from '../git/models/remote';
import type { RemoteProvider, RemoteResource } from '../git/remotes/provider';
import { RemoteResourceType } from '../git/remotes/provider';
import type { RemoteResource } from '../git/models/remoteResource';
import { RemoteResourceType } from '../git/models/remoteResource';
import type { RemoteProvider } from '../git/remotes/remoteProvider';
import { Logger } from '../logger';
import { showGenericErrorMessage } from '../messages';
import { RemoteProviderPicker } from '../quickpicks/remoteProviderPicker';

+ 1
- 1
src/commands/openRepoOnRemote.ts View File

@ -3,7 +3,7 @@ import { window } from 'vscode';
import { Commands } from '../constants';
import type { Container } from '../container';
import { GitUri } from '../git/gitUri';
import { RemoteResourceType } from '../git/remotes/provider';
import { RemoteResourceType } from '../git/models/remoteResource';
import { Logger } from '../logger';
import { RepositoryPicker } from '../quickpicks/repositoryPicker';
import { command, executeCommand } from '../system/command';

+ 1
- 1
src/commands/quickCommand.steps.ts View File

@ -13,13 +13,13 @@ import type { GitLog } from '../git/models/log';
import type { GitBranchReference, GitRevisionReference, GitTagReference } from '../git/models/reference';
import { GitReference, GitRevision } from '../git/models/reference';
import { GitRemote } from '../git/models/remote';
import { RemoteResourceType } from '../git/models/remoteResource';
import { Repository } from '../git/models/repository';
import type { GitStash } from '../git/models/stash';
import type { GitStatus } from '../git/models/status';
import type { GitTag, TagSortOptions } from '../git/models/tag';
import { sortTags } from '../git/models/tag';
import type { GitWorktree } from '../git/models/worktree';
import { RemoteResourceType } from '../git/remotes/provider';
import {
CommitApplyFileChangesCommandQuickPickItem,
CommitBrowseRepositoryFromHereCommandQuickPickItem,

+ 1
- 1
src/commands/remoteProviders.ts View File

@ -3,7 +3,7 @@ import type { Container } from '../container';
import type { GitCommit } from '../git/models/commit';
import { GitRemote } from '../git/models/remote';
import type { Repository } from '../git/models/repository';
import type { RichRemoteProvider } from '../git/remotes/provider';
import type { RichRemoteProvider } from '../git/remotes/richRemoteProvider';
import { RepositoryPicker } from '../quickpicks/repositoryPicker';
import { command } from '../system/command';
import { first } from '../system/iterable';

+ 5
- 4
src/env/node/git/localGitProvider.ts View File

@ -71,6 +71,7 @@ import type { GitBranchReference } from '../../../git/models/reference';
import { GitReference, GitRevision } from '../../../git/models/reference';
import type { GitReflog } from '../../../git/models/reflog';
import { getRemoteIconUri, GitRemote } from '../../../git/models/remote';
import { RemoteResourceType } from '../../../git/models/remoteResource';
import type { RepositoryChangeEvent } from '../../../git/models/repository';
import { Repository, RepositoryChange, RepositoryChangeComparisonMode } from '../../../git/models/repository';
import type { GitStash } from '../../../git/models/stash';
@ -92,10 +93,10 @@ import { GitStatusParser } from '../../../git/parsers/statusParser';
import { GitTagParser } from '../../../git/parsers/tagParser';
import { GitTreeParser } from '../../../git/parsers/treeParser';
import { GitWorktreeParser } from '../../../git/parsers/worktreeParser';
import type { RemoteProviders } from '../../../git/remotes/factory';
import { getRemoteProviderMatcher, loadRemoteProviders } from '../../../git/remotes/factory';
import type { RemoteProvider, RichRemoteProvider } from '../../../git/remotes/provider';
import { RemoteResourceType } from '../../../git/remotes/provider';
import type { RemoteProvider } from '../../../git/remotes/remoteProvider';
import type { RemoteProviders } from '../../../git/remotes/remoteProviders';
import { getRemoteProviderMatcher, loadRemoteProviders } from '../../../git/remotes/remoteProviders';
import type { RichRemoteProvider } from '../../../git/remotes/richRemoteProvider';
import { SearchPattern } from '../../../git/search';
import { Logger } from '../../../logger';
import type { LogScope } from '../../../logger';

+ 1
- 1
src/git/formatters/commitFormatter.ts View File

@ -31,7 +31,7 @@ import type { IssueOrPullRequest } from '../models/issue';
import { PullRequest } from '../models/pullRequest';
import { GitRevision } from '../models/reference';
import { GitRemote } from '../models/remote';
import type { RemoteProvider } from '../remotes/provider';
import type { RemoteProvider } from '../remotes/remoteProvider';
import type { FormatOptions } from './formatter';
import { Formatter } from './formatter';

+ 3
- 2
src/git/gitProvider.ts View File

@ -23,8 +23,9 @@ import type { GitTag, TagSortOptions } from './models/tag';
import type { GitTreeEntry } from './models/tree';
import type { GitUser } from './models/user';
import type { GitWorktree } from './models/worktree';
import type { RemoteProviders } from './remotes/factory';
import type { RemoteProvider, RichRemoteProvider } from './remotes/provider';
import type { RemoteProvider } from './remotes/remoteProvider';
import type { RemoteProviders } from './remotes/remoteProviders';
import type { RichRemoteProvider } from './remotes/richRemoteProvider';
import type { SearchPattern } from './search';
export const enum GitProviderId {

+ 10
- 5
src/git/gitProviderService.ts View File

@ -18,6 +18,7 @@ import { setContext } from '../context';
import { AccessDeniedError, ProviderNotFoundError } from '../errors';
import type { FeatureAccess, Features } from '../features';
import { PlusFeatures } from '../features';
import type { RemoteProvider } from '../git/remotes/remoteProvider';
import { Logger } from '../logger';
import type { SubscriptionChangeEvent } from '../plus/subscription/subscriptionService';
import type { RepoComparisonKey } from '../repositories';
@ -66,9 +67,9 @@ import type { GitTag, TagSortOptions } from './models/tag';
import type { GitTreeEntry } from './models/tree';
import type { GitUser } from './models/user';
import type { GitWorktree } from './models/worktree';
import type { RemoteProviders } from './remotes/factory';
import type { RemoteProvider, RichRemoteProvider } from './remotes/provider';
import { RichRemoteProviders } from './remotes/remoteProviderConnections';
import type { RemoteProviders } from './remotes/remoteProviders';
import type { RichRemoteProvider } from './remotes/richRemoteProvider';
import type { SearchPattern } from './search';
const maxDefaultBranchWeight = 100;
@ -1590,7 +1591,7 @@ export class GitProviderService implements Disposable {
let provider;
if (GitRemote.is(remoteOrProvider)) {
({ provider } = remoteOrProvider);
if (!provider?.hasRichApi()) return undefined;
if (!provider?.hasRichIntegration()) return undefined;
} else {
provider = remoteOrProvider;
}
@ -1645,7 +1646,7 @@ export class GitProviderService implements Disposable {
let provider;
if (GitRemote.is(remoteOrProvider)) {
({ provider } = remoteOrProvider);
if (!provider?.hasRichApi()) return undefined;
if (!provider?.hasRichIntegration()) return undefined;
} else {
provider = remoteOrProvider;
}
@ -1880,7 +1881,11 @@ export class GitProviderService implements Disposable {
? repository.getRemotes(options)
: this.getRemotes(repoPath, options));
return remotes.filter(r => r.provider != null) as GitRemote<RemoteProvider | RichRemoteProvider>[];
return remotes.filter(
(
r: GitRemote<RemoteProvider | RichRemoteProvider | undefined>,
): r is GitRemote<RemoteProvider | RichRemoteProvider> => r.provider != null,
);
}
getBestRepository(): Repository | undefined;

+ 2
- 1
src/git/models/branch.ts View File

@ -5,7 +5,8 @@ import { debug } from '../../system/decorators/log';
import { memoize } from '../../system/decorators/memoize';
import { cancellable } from '../../system/promise';
import { sortCompare } from '../../system/string';
import type { RemoteProvider, RichRemoteProvider } from '../remotes/provider';
import type { RemoteProvider } from '../remotes/remoteProvider';
import type { RichRemoteProvider } from '../remotes/richRemoteProvider';
import type { PullRequest, PullRequestState } from './pullRequest';
import type { GitBranchReference, GitReference } from './reference';
import { GitRevision } from './reference';

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

@ -11,7 +11,7 @@ import { cancellable } from '../../system/promise';
import { pad, pluralize } from '../../system/string';
import type { PreviousLineComparisonUrisResult } from '../gitProvider';
import { GitUri } from '../gitUri';
import type { RichRemoteProvider } from '../remotes/provider';
import type { RichRemoteProvider } from '../remotes/richRemoteProvider';
import type { GitFile } from './file';
import { GitFileChange, GitFileWorkingTreeStatus } from './file';
import type { PullRequest } from './pullRequest';

+ 3
- 3
src/git/models/remote.ts View File

@ -3,8 +3,8 @@ import { Uri, window } from 'vscode';
import { Container } from '../../container';
import { sortCompare } from '../../system/string';
import { isLightTheme } from '../../system/utils';
import type { RemoteProvider } from '../remotes/provider';
import { RichRemoteProvider } from '../remotes/provider';
import type { RemoteProvider } from '../remotes/remoteProvider';
import type { RichRemoteProvider } from '../remotes/richRemoteProvider';
export const enum GitRemoteType {
Fetch = 'fetch',
@ -82,7 +82,7 @@ export class GitRemote
}
hasRichProvider(): this is GitRemote<RichRemoteProvider> {
return RichRemoteProvider.is(this.provider);
return this.provider?.hasRichIntegration() ?? false;
}
async setAsDefault(value: boolean = true) {

+ 83
- 0
src/git/models/remoteResource.ts View File

@ -0,0 +1,83 @@
import type { Range } from 'vscode';
import type { GitCommit } from './commit';
export const enum RemoteResourceType {
Branch = 'branch',
Branches = 'branches',
Commit = 'commit',
Comparison = 'comparison',
CreatePullRequest = 'createPullRequest',
File = 'file',
Repo = 'repo',
Revision = 'revision',
}
export type RemoteResource =
| {
type: RemoteResourceType.Branch;
branch: string;
}
| {
type: RemoteResourceType.Branches;
}
| {
type: RemoteResourceType.Commit;
sha: string;
}
| {
type: RemoteResourceType.Comparison;
base: string;
compare: string;
notation?: '..' | '...';
}
| {
type: RemoteResourceType.CreatePullRequest;
base: {
branch?: string;
remote: { path: string; url: string };
};
compare: {
branch: string;
remote: { path: string; url: string };
};
}
| {
type: RemoteResourceType.File;
branchOrTag?: string;
fileName: string;
range?: Range;
}
| {
type: RemoteResourceType.Repo;
}
| {
type: RemoteResourceType.Revision;
branchOrTag?: string;
commit?: GitCommit;
fileName: string;
range?: Range;
sha?: string;
};
export function getNameFromRemoteResource(resource: RemoteResource) {
switch (resource.type) {
case RemoteResourceType.Branch:
return 'Branch';
case RemoteResourceType.Branches:
return 'Branches';
case RemoteResourceType.Commit:
return 'Commit';
case RemoteResourceType.Comparison:
return 'Comparison';
case RemoteResourceType.CreatePullRequest:
return 'Create Pull Request';
case RemoteResourceType.File:
return 'File';
case RemoteResourceType.Repo:
return 'Repository';
case RemoteResourceType.Revision:
return 'File';
default:
return '';
}
}

+ 4
- 4
src/git/models/repository.ts View File

@ -20,9 +20,9 @@ import { updateRecordValue } from '../../system/object';
import { basename, normalizePath } from '../../system/path';
import { runGitCommandInTerminal } from '../../terminal';
import type { GitProviderDescriptor } from '../gitProvider';
import type { RemoteProviders } from '../remotes/factory';
import { loadRemoteProviders } from '../remotes/factory';
import { RichRemoteProvider } from '../remotes/provider';
import { loadRemoteProviders } from '../remotes/remoteProviders';
import type { RemoteProviders } from '../remotes/remoteProviders';
import type { RichRemoteProvider } from '../remotes/richRemoteProvider';
import type { SearchPattern } from '../search';
import type { BranchSortOptions, GitBranch } from './branch';
import { getBranchNameWithoutRemote, getRemoteNameFromBranchName } from './branch';
@ -598,7 +598,7 @@ export class Repository implements Disposable {
this._remotesDisposable = Disposable.from(
...filterMap(await remotes, r => {
if (!RichRemoteProvider.is(r.provider)) return undefined;
if (!r.provider?.hasRichIntegration()) return undefined;
return r.provider.onDidChange(() => this.fireChange(RepositoryChange.RemoteProviders));
}),

+ 3
- 3
src/git/parsers/remoteParser.ts View File

@ -1,7 +1,7 @@
import { debug } from '../../system/decorators/log';
import type { GitRemoteType } from '../models/remote';
import { GitRemote } from '../models/remote';
import type { RemoteProvider } from '../remotes/provider';
import type { getRemoteProviderMatcher } from '../remotes/remoteProviders';
const emptyStr = '';
@ -53,7 +53,7 @@ export class GitRemoteParser {
static parse(
data: string,
repoPath: string,
providerFactory: (url: string, domain: string, path: string) => RemoteProvider | undefined,
remoteProviderMatcher: ReturnType<typeof getRemoteProviderMatcher>,
): GitRemote[] | undefined {
if (!data) return undefined;
@ -86,7 +86,7 @@ export class GitRemoteParser {
uniqueness = `${domain ? `${domain}/` : ''}${path}`;
remote = groups[uniqueness];
if (remote === undefined) {
const provider = providerFactory(url, domain, path);
const provider = remoteProviderMatcher(url, domain, path);
remote = new GitRemote(
repoPath,

+ 1
- 1
src/git/remotes/azure-devops.ts View File

@ -3,7 +3,7 @@ import type { DynamicAutolinkReference } from '../../annotations/autolinks';
import type { AutolinkReference } from '../../config';
import { AutolinkType } from '../../config';
import type { Repository } from '../models/repository';
import { RemoteProvider } from './provider';
import { RemoteProvider } from './remoteProvider';
const gitRegex = /\/_git\/?/i;
const legacyDefaultCollectionRegex = /^DefaultCollection\//i;

+ 1
- 1
src/git/remotes/bitbucket-server.ts View File

@ -4,7 +4,7 @@ import type { AutolinkReference } from '../../config';
import { AutolinkType } from '../../config';
import { GitRevision } from '../models/reference';
import type { Repository } from '../models/repository';
import { RemoteProvider } from './provider';
import { RemoteProvider } from './remoteProvider';
const fileRegex = /^\/([^/]+)\/([^/]+?)\/src(.+)$/i;
const rangeRegex = /^lines-(\d+)(?::(\d+))?$/;

+ 1
- 1
src/git/remotes/bitbucket.ts View File

@ -4,7 +4,7 @@ import type { AutolinkReference } from '../../config';
import { AutolinkType } from '../../config';
import { GitRevision } from '../models/reference';
import type { Repository } from '../models/repository';
import { RemoteProvider } from './provider';
import { RemoteProvider } from './remoteProvider';
const fileRegex = /^\/([^/]+)\/([^/]+?)\/src(.+)$/i;
const rangeRegex = /^lines-(\d+)(?::(\d+))?$/;

+ 1
- 1
src/git/remotes/custom.ts View File

@ -2,7 +2,7 @@ import type { Range, Uri } from 'vscode';
import type { RemotesUrlsConfig } from '../../configuration';
import { interpolate } from '../../system/string';
import type { Repository } from '../models/repository';
import { RemoteProvider } from './provider';
import { RemoteProvider } from './remoteProvider';
export class CustomRemote extends RemoteProvider {
private readonly urls: RemotesUrlsConfig;

+ 1
- 1
src/git/remotes/gerrit.ts View File

@ -3,7 +3,7 @@ import type { DynamicAutolinkReference } from '../../annotations/autolinks';
import type { AutolinkReference } from '../../config';
import { GitRevision } from '../models/reference';
import type { Repository } from '../models/repository';
import { RemoteProvider } from './provider';
import { RemoteProvider } from './remoteProvider';
const fileRegex = /^\/([^/]+)\/\+(.+)$/i;
const rangeRegex = /^(\d+)$/;

+ 1
- 1
src/git/remotes/gitea.ts View File

@ -4,7 +4,7 @@ import type { AutolinkReference } from '../../config';
import { AutolinkType } from '../../config';
import { GitRevision } from '../models/reference';
import type { Repository } from '../models/repository';
import { RemoteProvider } from './provider';
import { RemoteProvider } from './remoteProvider';
const fileRegex = /^\/([^/]+)\/([^/]+?)\/src(.+)$/i;
const rangeRegex = /^L(\d+)(?:-L(\d+))?$/;

+ 1
- 1
src/git/remotes/github.ts View File

@ -13,7 +13,7 @@ import type { IssueOrPullRequest } from '../models/issue';
import type { PullRequest, PullRequestState } from '../models/pullRequest';
import { GitRevision } from '../models/reference';
import type { Repository } from '../models/repository';
import { RichRemoteProvider } from './provider';
import { RichRemoteProvider } from './richRemoteProvider';
const autolinkFullIssuesRegex = /\b(?<repo>[^/\s]+\/[^/\s]+)#(?<num>[0-9]+)\b(?!]\()/g;
const fileRegex = /^\/([^/]+)\/([^/]+?)\/blob(.+)$/i;

+ 1
- 1
src/git/remotes/gitlab.ts View File

@ -17,7 +17,7 @@ import type { IssueOrPullRequest } from '../models/issue';
import type { PullRequest, PullRequestState } from '../models/pullRequest';
import { GitRevision } from '../models/reference';
import type { Repository } from '../models/repository';
import { RichRemoteProvider } from './provider';
import { RichRemoteProvider } from './richRemoteProvider';
const autolinkFullIssuesRegex = /\b(?<repo>[^/\s]+\/[^/\s]+)#(?<num>[0-9]+)\b(?!]\()/g;
const autolinkFullMergeRequestsRegex = /\b(?<repo>[^/\s]+\/[^/\s]+)!(?<num>[0-9]+)\b(?!]\()/g;

+ 157
- 0
src/git/remotes/remoteProvider.ts View File

@ -0,0 +1,157 @@
import { env, Uri } from 'vscode';
import type { Range } from 'vscode';
import type { DynamicAutolinkReference } from '../../annotations/autolinks';
import type { AutolinkReference } from '../../config';
import { encodeUrl } from '../../system/encoding';
import type { RemoteProviderReference } from '../models/remoteProvider';
import type { RemoteResource } from '../models/remoteResource';
import { RemoteResourceType } from '../models/remoteResource';
import type { Repository } from '../models/repository';
import type { RichRemoteProvider } from './richRemoteProvider';
export abstract class RemoteProvider implements RemoteProviderReference {
readonly type: 'simple' | 'rich' = 'simple';
protected readonly _name: string | undefined;
constructor(
public readonly domain: string,
public readonly path: string,
public readonly protocol: string = 'https',
name?: string,
public readonly custom: boolean = false,
) {
this._name = name;
}
get autolinks(): (AutolinkReference | DynamicAutolinkReference)[] {
return [];
}
get avatarUri(): Uri | undefined {
return undefined;
}
get displayPath(): string {
return this.path;
}
get icon(): string {
return 'remote';
}
abstract get id(): string;
abstract get name(): string;
async copy(resource: RemoteResource): Promise<void> {
const url = this.url(resource);
if (url == null) {
return;
}
await env.clipboard.writeText(url);
}
hasRichIntegration(): this is RichRemoteProvider {
return this.type === 'rich';
}
abstract getLocalInfoFromRemoteUri(
repository: Repository,
uri: Uri,
options?: { validate?: boolean },
): Promise<{ uri: Uri; startLine?: number; endLine?: number } | undefined>;
open(resource: RemoteResource): Promise<boolean | undefined> {
return this.openUrl(this.url(resource));
}
url(resource: RemoteResource): string | undefined {
switch (resource.type) {
case RemoteResourceType.Branch:
return this.getUrlForBranch(resource.branch);
case RemoteResourceType.Branches:
return this.getUrlForBranches();
case RemoteResourceType.Commit:
return this.getUrlForCommit(resource.sha);
case RemoteResourceType.Comparison: {
return this.getUrlForComparison?.(resource.base, resource.compare, resource.notation ?? '...');
}
case RemoteResourceType.CreatePullRequest: {
return this.getUrlForCreatePullRequest?.(resource.base, resource.compare);
}
case RemoteResourceType.File:
return this.getUrlForFile(
resource.fileName,
resource.branchOrTag != null ? resource.branchOrTag : undefined,
undefined,
resource.range,
);
case RemoteResourceType.Repo:
return this.getUrlForRepository();
case RemoteResourceType.Revision:
return this.getUrlForFile(
resource.fileName,
resource.branchOrTag != null ? resource.branchOrTag : undefined,
resource.sha != null ? resource.sha : undefined,
resource.range,
);
default:
return undefined;
}
}
protected get baseUrl(): string {
return `${this.protocol}://${this.domain}/${this.path}`;
}
protected formatName(name: string) {
if (this._name != null) {
return this._name;
}
return `${name}${this.custom ? ` (${this.domain})` : ''}`;
}
protected splitPath(): [string, string] {
const index = this.path.indexOf('/');
return [this.path.substring(0, index), this.path.substring(index + 1)];
}
protected abstract getUrlForBranch(branch: string): string;
protected abstract getUrlForBranches(): string;
protected abstract getUrlForCommit(sha: string): string;
protected getUrlForComparison?(base: string, compare: string, notation: '..' | '...'): string | undefined;
protected getUrlForCreatePullRequest?(
base: { branch?: string; remote: { path: string; url: string } },
compare: { branch: string; remote: { path: string; url: string } },
): string | undefined;
protected abstract getUrlForFile(fileName: string, branch?: string, sha?: string, range?: Range): string;
protected getUrlForRepository(): string {
return this.baseUrl;
}
private async openUrl(url?: string): Promise<boolean | undefined> {
if (url == null) {
return undefined;
}
const uri = Uri.parse(url);
// Pass a string to openExternal to avoid double encoding issues: https://github.com/microsoft/vscode/issues/85930
if (uri.path.includes('#')) {
// .d.ts currently says it only supports a Uri, but it actually accepts a string too
return (env.openExternal as unknown as (target: string) => Thenable<boolean>)(uri.toString());
}
return env.openExternal(uri);
}
protected encodeUrl(url: string): string;
protected encodeUrl(url: string | undefined): string | undefined;
protected encodeUrl(url: string | undefined): string | undefined {
return encodeUrl(url)?.replace(/#/g, '%23');
}
}

src/git/remotes/factory.ts → src/git/remotes/remoteProviders.ts View File

@ -11,7 +11,7 @@ import { GiteaRemote } from './gitea';
import { GitHubRemote } from './github';
import { GitLabRemote } from './gitlab';
import { GoogleSourceRemote } from './google-source';
import type { RemoteProvider } from './provider';
import type { RemoteProvider } from './remoteProvider';
export type RemoteProviders = {
custom: boolean;

src/git/remotes/provider.ts → src/git/remotes/richRemoteProvider.ts View File

@ -1,9 +1,7 @@
import type { AuthenticationSession, AuthenticationSessionsChangeEvent, Event, MessageItem, Range } from 'vscode';
import { authentication, env, EventEmitter, Uri, window } from 'vscode';
import { authentication, EventEmitter, window } from 'vscode';
import type { AuthenticationSession, AuthenticationSessionsChangeEvent, Event, MessageItem } from 'vscode';
import { wrapForForcedInsecureSSL } from '@env/fetch';
import { isWeb } from '@env/platform';
import type { DynamicAutolinkReference } from '../../annotations/autolinks';
import type { AutolinkReference } from '../../config';
import { configuration } from '../../configuration';
import type { Container } from '../../container';
import { AuthenticationError, ProviderRequestClientError } from '../../errors';
@ -12,248 +10,19 @@ import { showIntegrationDisconnectedTooManyFailedRequestsWarningMessage } from '
import type { IntegrationAuthenticationSessionDescriptor } from '../../plus/integrationAuthentication';
import { gate } from '../../system/decorators/gate';
import { debug, getLogScope, log } from '../../system/decorators/log';
import { encodeUrl } from '../../system/encoding';
import { isPromise } from '../../system/promise';
import type { Account } from '../models/author';
import type { GitCommit } from '../models/commit';
import type { DefaultBranch } from '../models/defaultBranch';
import type { IssueOrPullRequest } from '../models/issue';
import type { PullRequest, PullRequestState } from '../models/pullRequest';
import type { RemoteProviderReference } from '../models/remoteProvider';
import type { Repository } from '../models/repository';
import { RemoteProvider } from './remoteProvider';
import { RichRemoteProviders } from './remoteProviderConnections';
export const enum RemoteResourceType {
Branch = 'branch',
Branches = 'branches',
Commit = 'commit',
Comparison = 'comparison',
CreatePullRequest = 'createPullRequest',
File = 'file',
Repo = 'repo',
Revision = 'revision',
}
export type RemoteResource =
| {
type: RemoteResourceType.Branch;
branch: string;
}
| {
type: RemoteResourceType.Branches;
}
| {
type: RemoteResourceType.Commit;
sha: string;
}
| {
type: RemoteResourceType.Comparison;
base: string;
compare: string;
notation?: '..' | '...';
}
| {
type: RemoteResourceType.CreatePullRequest;
base: {
branch?: string;
remote: { path: string; url: string };
};
compare: {
branch: string;
remote: { path: string; url: string };
};
}
| {
type: RemoteResourceType.File;
branchOrTag?: string;
fileName: string;
range?: Range;
}
| {
type: RemoteResourceType.Repo;
}
| {
type: RemoteResourceType.Revision;
branchOrTag?: string;
commit?: GitCommit;
fileName: string;
range?: Range;
sha?: string;
};
export function getNameFromRemoteResource(resource: RemoteResource) {
switch (resource.type) {
case RemoteResourceType.Branch:
return 'Branch';
case RemoteResourceType.Branches:
return 'Branches';
case RemoteResourceType.Commit:
return 'Commit';
case RemoteResourceType.Comparison:
return 'Comparison';
case RemoteResourceType.CreatePullRequest:
return 'Create Pull Request';
case RemoteResourceType.File:
return 'File';
case RemoteResourceType.Repo:
return 'Repository';
case RemoteResourceType.Revision:
return 'File';
default:
return '';
}
}
export abstract class RemoteProvider implements RemoteProviderReference {
readonly type: 'simple' | 'rich' = 'simple';
protected readonly _name: string | undefined;
constructor(
public readonly domain: string,
public readonly path: string,
public readonly protocol: string = 'https',
name?: string,
public readonly custom: boolean = false,
) {
this._name = name;
}
get autolinks(): (AutolinkReference | DynamicAutolinkReference)[] {
return [];
}
get avatarUri(): Uri | undefined {
return undefined;
}
get displayPath(): string {
return this.path;
}
get icon(): string {
return 'remote';
}
abstract get id(): string;
abstract get name(): string;
async copy(resource: RemoteResource): Promise<void> {
const url = this.url(resource);
if (url == null) return;
await env.clipboard.writeText(url);
}
hasRichApi(): this is RichRemoteProvider {
return RichRemoteProvider.is(this);
}
abstract getLocalInfoFromRemoteUri(
repository: Repository,
uri: Uri,
options?: { validate?: boolean },
): Promise<{ uri: Uri; startLine?: number; endLine?: number } | undefined>;
open(resource: RemoteResource): Promise<boolean | undefined> {
return this.openUrl(this.url(resource));
}
url(resource: RemoteResource): string | undefined {
switch (resource.type) {
case RemoteResourceType.Branch:
return this.getUrlForBranch(resource.branch);
case RemoteResourceType.Branches:
return this.getUrlForBranches();
case RemoteResourceType.Commit:
return this.getUrlForCommit(resource.sha);
case RemoteResourceType.Comparison: {
return this.getUrlForComparison?.(resource.base, resource.compare, resource.notation ?? '...');
}
case RemoteResourceType.CreatePullRequest: {
return this.getUrlForCreatePullRequest?.(resource.base, resource.compare);
}
case RemoteResourceType.File:
return this.getUrlForFile(
resource.fileName,
resource.branchOrTag != null ? resource.branchOrTag : undefined,
undefined,
resource.range,
);
case RemoteResourceType.Repo:
return this.getUrlForRepository();
case RemoteResourceType.Revision:
return this.getUrlForFile(
resource.fileName,
resource.branchOrTag != null ? resource.branchOrTag : undefined,
resource.sha != null ? resource.sha : undefined,
resource.range,
);
default:
return undefined;
}
}
protected get baseUrl(): string {
return `${this.protocol}://${this.domain}/${this.path}`;
}
protected formatName(name: string) {
if (this._name != null) return this._name;
return `${name}${this.custom ? ` (${this.domain})` : ''}`;
}
protected splitPath(): [string, string] {
const index = this.path.indexOf('/');
return [this.path.substring(0, index), this.path.substring(index + 1)];
}
protected abstract getUrlForBranch(branch: string): string;
protected abstract getUrlForBranches(): string;
protected abstract getUrlForCommit(sha: string): string;
protected getUrlForComparison?(base: string, compare: string, notation: '..' | '...'): string | undefined;
protected getUrlForCreatePullRequest?(
base: { branch?: string; remote: { path: string; url: string } },
compare: { branch: string; remote: { path: string; url: string } },
): string | undefined;
protected abstract getUrlForFile(fileName: string, branch?: string, sha?: string, range?: Range): string;
protected getUrlForRepository(): string {
return this.baseUrl;
}
private async openUrl(url?: string): Promise<boolean | undefined> {
if (url == null) return undefined;
const uri = Uri.parse(url);
// Pass a string to openExternal to avoid double encoding issues: https://github.com/microsoft/vscode/issues/85930
if (uri.path.includes('#')) {
// .d.ts currently says it only supports a Uri, but it actually accepts a string too
return (env.openExternal as unknown as (target: string) => Thenable<boolean>)(uri.toString());
}
return env.openExternal(uri);
}
protected encodeUrl(url: string): string;
protected encodeUrl(url: string | undefined): string | undefined;
protected encodeUrl(url: string | undefined): string | undefined {
return encodeUrl(url)?.replace(/#/g, '%23');
}
}
// TODO@eamodio revisit how once authenticated, all remotes are always connected, even after a restart
export abstract class RichRemoteProvider extends RemoteProvider {
override readonly type: 'simple' | 'rich' = 'rich';
static is(provider: RemoteProvider | undefined): provider is RichRemoteProvider {
return provider?.type === 'rich';
}
private readonly _onDidChange = new EventEmitter<void>();
get onDidChange(): Event<void> {
return this._onDidChange.event;
@ -269,7 +38,7 @@ export abstract class RichRemoteProvider extends RemoteProvider {
) {
super(domain, path, protocol, name, custom);
this.container.context.subscriptions.push(
container.context.subscriptions.push(
configuration.onDidChange(e => {
if (configuration.changed(e, 'remotes')) {
this._ignoreSSLErrors.clear();
@ -304,9 +73,7 @@ export abstract class RichRemoteProvider extends RemoteProvider {
}
get maybeConnected(): boolean | undefined {
if (this._session === undefined) return undefined;
return this._session !== null;
return this._session === undefined ? undefined : this._session !== null;
}
protected _session: AuthenticationSession | null | undefined;

+ 1
- 1
src/plus/github/github.ts View File

@ -25,7 +25,7 @@ import type { PullRequest } from '../../git/models/pullRequest';
import { GitRevision } from '../../git/models/reference';
import type { GitUser } from '../../git/models/user';
import { getGitHubNoReplyAddressParts } from '../../git/remotes/github';
import type { RichRemoteProvider } from '../../git/remotes/provider';
import type { RichRemoteProvider } from '../../git/remotes/richRemoteProvider';
import type { LogScope } from '../../logger';
import { Logger, LogLevel } from '../../logger';
import {

+ 4
- 3
src/plus/github/githubGitProvider.ts View File

@ -60,9 +60,10 @@ import { GitTag, sortTags } from '../../git/models/tag';
import type { GitTreeEntry } from '../../git/models/tree';
import type { GitUser } from '../../git/models/user';
import { isUserMatch } from '../../git/models/user';
import type { RemoteProviders } from '../../git/remotes/factory';
import { getRemoteProviderMatcher, loadRemoteProviders } from '../../git/remotes/factory';
import type { RemoteProvider, RichRemoteProvider } from '../../git/remotes/provider';
import type { RemoteProvider } from '../../git/remotes/remoteProvider';
import type { RemoteProviders } from '../../git/remotes/remoteProviders';
import { getRemoteProviderMatcher, loadRemoteProviders } from '../../git/remotes/remoteProviders';
import type { RichRemoteProvider } from '../../git/remotes/richRemoteProvider';
import { SearchPattern } from '../../git/search';
import type { LogScope } from '../../logger';
import { Logger } from '../../logger';

+ 1
- 1
src/plus/github/models.ts View File

@ -2,7 +2,7 @@ import type { Endpoints } from '@octokit/types';
import { GitFileIndexStatus } from '../../git/models/file';
import type { IssueOrPullRequestType } from '../../git/models/issue';
import { PullRequest, PullRequestState } from '../../git/models/pullRequest';
import type { RichRemoteProvider } from '../../git/remotes/provider';
import type { RichRemoteProvider } from '../../git/remotes/richRemoteProvider';
export interface GitHubBlame {
ranges: GitHubBlameRange[];

+ 1
- 1
src/plus/gitlab/gitlab.ts View File

@ -18,7 +18,7 @@ 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 { RichRemoteProvider } from '../../git/remotes/provider';
import type { RichRemoteProvider } from '../../git/remotes/richRemoteProvider';
import type { LogScope } from '../../logger';
import { Logger, LogLevel } from '../../logger';
import {

+ 1
- 1
src/plus/gitlab/models.ts View File

@ -1,5 +1,5 @@
import { PullRequest, PullRequestState } from '../../git/models/pullRequest';
import type { RichRemoteProvider } from '../../git/remotes/provider';
import type { RichRemoteProvider } from '../../git/remotes/richRemoteProvider';
export interface GitLabUser {
id: number;

+ 3
- 2
src/quickpicks/remoteProviderPicker.ts View File

@ -5,8 +5,9 @@ import { Commands, GlyphChars } from '../constants';
import { Container } from '../container';
import { getBranchNameWithoutRemote, getRemoteNameFromBranchName } from '../git/models/branch';
import { GitRemote } from '../git/models/remote';
import type { RemoteProvider, RemoteResource } from '../git/remotes/provider';
import { getNameFromRemoteResource, RemoteResourceType } from '../git/remotes/provider';
import type { RemoteResource } from '../git/models/remoteResource';
import { getNameFromRemoteResource, RemoteResourceType } from '../git/models/remoteResource';
import type { RemoteProvider } from '../git/remotes/remoteProvider';
import type { Keys } from '../keyboard';
import { CommandQuickPickItem } from '../quickpicks/items/common';
import { getSettledValue } from '../system/promise';

+ 1
- 1
src/views/nodes/commitNode.ts View File

@ -9,7 +9,7 @@ import type { GitCommit } from '../../git/models/commit';
import type { PullRequest } from '../../git/models/pullRequest';
import type { GitRevisionReference } from '../../git/models/reference';
import type { GitRemote } from '../../git/models/remote';
import type { RichRemoteProvider } from '../../git/remotes/provider';
import type { RichRemoteProvider } from '../../git/remotes/richRemoteProvider';
import { makeHierarchical } from '../../system/array';
import { gate } from '../../system/decorators/gate';
import { joinPaths, normalizePath } from '../../system/path';

+ 1
- 1
src/views/nodes/remoteNode.ts View File

@ -125,7 +125,7 @@ export class RemoteNode extends ViewNode {
light: this.view.container.context.asAbsolutePath(`images/light/icon-${provider.icon}.svg`),
};
if (provider.hasRichApi()) {
if (provider.hasRichIntegration()) {
const connected = provider.maybeConnected ?? (await provider.isConnected());
item.contextValue = `${ContextValues.Remote}${connected ? '+connected' : '+disconnected'}`;

Loading…
Cancel
Save