diff --git a/package.json b/package.json index bd96569..2216c8c 100644 --- a/package.json +++ b/package.json @@ -447,6 +447,34 @@ ], "description": "Specifies the starting view (mode) of the `GitLens` custom view\n `history` - shows the commit history of the active file\n `repository` - shows a repository explorer" }, + "gitlens.remotes": { + "type": "array", + "default": null, + "items": { + "type": "object", + "required": [ + "type", + "domain" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Bitbucket", + "GitHub", + "GitLab" + ], + "description": "Specifies the type of the custom remote service\n `Bitbucket`, `GitHub`, or `GitLab`" + }, + "domain": { + "type": "string", + "description": "Specifies the domain name of the custom remote service" + } + } + }, + "uniqueItems": true, + "description": "Specifies the custom remote services (code-hosting)" + }, "gitlens.statusBar.enabled": { "type": "boolean", "default": true, diff --git a/src/configuration.ts b/src/configuration.ts index 2633f44..d53e2d9 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -7,7 +7,8 @@ import { OutputLevel } from './logger'; export { ExtensionKey } from './constants'; -export type CodeLensCommand = 'gitlens.toggleFileBlame' | +export type CodeLensCommand = + 'gitlens.toggleFileBlame' | 'gitlens.showBlameHistory' | 'gitlens.showFileHistory' | 'gitlens.diffWithPrevious' | @@ -41,7 +42,18 @@ export const LineHighlightLocations = { OverviewRuler: 'overviewRuler' as LineHighlightLocations }; -export type StatusBarCommand = 'gitlens.toggleFileBlame' | +export type CustomRemoteType = + 'Bitbucket' | + 'GitHub' | + 'GitLab'; +export const CustomRemoteType = { + Bitbucket: 'Bitbucket' as CustomRemoteType, + GitHub: 'GitHub' as CustomRemoteType, + GitLab: 'GitLab' as CustomRemoteType +}; + +export type StatusBarCommand = + 'gitlens.toggleFileBlame' | 'gitlens.showBlameHistory' | 'gitlens.showFileHistory' | 'gitlens.toggleCodeLens' | @@ -119,6 +131,11 @@ export interface ICodeLensLanguageLocation { customSymbols?: string[]; } +export interface IRemotesConfig { + type: CustomRemoteType; + domain: string; +} + export interface IThemeConfig { annotations: { file: { @@ -307,6 +324,8 @@ export interface IConfig { // dateFormat: string | null; }; + remotes: IRemotesConfig[]; + statusBar: { enabled: boolean; alignment: 'left' | 'right'; diff --git a/src/extension.ts b/src/extension.ts index 450b23c..ea4a0e8 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -19,6 +19,7 @@ import { CodeLensLocations, IConfig, LineHighlightLocations } from './configurat import { ApplicationInsightsKey, CommandContext, ExtensionKey, QualifiedExtensionId, setCommandContext, WorkspaceState } from './constants'; import { CodeLensController } from './codeLensController'; import { CurrentLineController, LineAnnotationType } from './currentLineController'; +import { RemoteProviderFactory } from './git/remotes/factory'; import { GitContentProvider } from './gitContentProvider'; import { GitExplorer } from './views/gitExplorer'; import { GitRevisionCodeLensProvider } from './gitRevisionCodeLensProvider'; @@ -33,6 +34,7 @@ export async function activate(context: ExtensionContext) { Logger.configure(context); Messages.configure(context); Telemetry.configure(ApplicationInsightsKey); + RemoteProviderFactory.configure(context); const gitlens = extensions.getExtension(QualifiedExtensionId)!; const gitlensVersion = gitlens.packageJSON.version; diff --git a/src/git/remotes/factory.ts b/src/git/remotes/factory.ts index b158010..48c260a 100644 --- a/src/git/remotes/factory.ts +++ b/src/git/remotes/factory.ts @@ -1,24 +1,64 @@ 'use strict'; -import { RemoteProvider } from './provider'; +import { ExtensionContext, workspace } from 'vscode'; import { BitbucketService } from './bitbucket'; +import { CustomRemoteType, IConfig, IRemotesConfig } from '../../configuration'; +import { ExtensionKey } from '../../constants'; import { GitHubService } from './github'; import { GitLabService } from './gitlab'; -import { VisualStudioService } from './visualStudio'; import { Logger } from '../../logger'; +import { RemoteProvider } from './provider'; +import { VisualStudioService } from './visualStudio'; +import { Objects } from '../../system'; export { RemoteProvider }; -const providerMap = new Map RemoteProvider>([ +const UrlRegex = /^(?:git:\/\/(.*?)\/|https:\/\/(.*?)\/|http:\/\/(.*?)\/|git@(.*):|ssh:\/\/(?:.*@)?(.*?)(?::.*?)?\/)(.*)$/; + +function getProviderKey(type: CustomRemoteType) { + switch (type) { + case CustomRemoteType.Bitbucket: return 'bitbucket.org'; + case CustomRemoteType.GitHub: return 'github.com'; + case CustomRemoteType.GitLab: return 'gitlab.com'; + } + return undefined; +} + +const defaultProviderMap = new Map RemoteProvider>([ ['bitbucket.org', (domain: string, path: string) => new BitbucketService(domain, path)], ['github.com', (domain: string, path: string) => new GitHubService(domain, path)], ['gitlab.com', (domain: string, path: string) => new GitLabService(domain, path)], ['visualstudio.com', (domain: string, path: string) => new VisualStudioService(domain, path)] ]); -const UrlRegex = /^(?:git:\/\/(.*?)\/|https:\/\/(.*?)\/|http:\/\/(.*?)\/|git@(.*):|ssh:\/\/(?:.*@)?(.*?)(?::.*?)?\/)(.*)$/; +let providerMap: Map RemoteProvider>; +let remotesCfg: IRemotesConfig[]; + +function onConfigurationChanged() { + const cfg = workspace.getConfiguration().get(ExtensionKey); + if (cfg === undefined) return; + + if (!Objects.areEquivalent(cfg.remotes, remotesCfg)) { + providerMap = new Map(defaultProviderMap); + + remotesCfg = cfg.remotes; + if (remotesCfg != null && remotesCfg.length > 0) { + for (const svc of remotesCfg) { + const key = getProviderKey(svc.type); + if (key === undefined) continue; + + providerMap.set(svc.domain.toLowerCase(), providerMap.get(key)!); + } + } + } +} export class RemoteProviderFactory { + static configure(context: ExtensionContext) { + context.subscriptions.push(workspace.onDidChangeConfiguration(onConfigurationChanged)); + onConfigurationChanged(); + } + static getRemoteProvider(url: string): RemoteProvider | undefined { try { const match = UrlRegex.exec(url); @@ -32,7 +72,7 @@ export class RemoteProviderFactory { : domain; const creator = providerMap.get(key.toLowerCase()); - if (!creator) return undefined; + if (creator === undefined) return undefined; return creator(domain, path); }