Skip to content

Commit bfad20b

Browse files
authored
Extract remote source provider registry into the vscode.git-base extension (microsoft#137656)
1 parent 483d6f1 commit bfad20b

35 files changed

+946
-258
lines changed

build/gulpfile.extensions.js

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ const compilations = [
3838
'emmet/tsconfig.json',
3939
'extension-editing/tsconfig.json',
4040
'git/tsconfig.json',
41+
'git-base/tsconfig.json',
4142
'github-authentication/tsconfig.json',
4243
'github/tsconfig.json',
4344
'grunt/tsconfig.json',

build/npm/dirs.js

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ exports.dirs = [
1717
'extensions/emmet',
1818
'extensions/extension-editing',
1919
'extensions/git',
20+
'extensions/git-base',
2021
'extensions/github',
2122
'extensions/github-authentication',
2223
'extensions/grunt',
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
//@ts-check
7+
8+
'use strict';
9+
10+
const withDefaults = require('../shared.webpack.config');
11+
12+
module.exports = withDefaults({
13+
context: __dirname,
14+
entry: {
15+
extension: './src/extension.ts'
16+
}
17+
});

extensions/git-base/package.json

+41
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,41 @@
88
"engines": {
99
"vscode": "0.10.x"
1010
},
11+
"categories": [
12+
"Other"
13+
],
14+
"activationEvents": [
15+
"*"
16+
],
17+
"main": "./out/extension.js",
18+
"icon": "resources/icons/git.png",
1119
"scripts": {
20+
"compile": "gulp compile-extension:git-base",
21+
"watch": "gulp watch-extension:git-base",
1222
"update-grammar": "node ./build/update-grammars.js"
1323
},
24+
"capabilities": {
25+
"virtualWorkspaces": true,
26+
"untrustedWorkspaces": {
27+
"supported": true
28+
}
29+
},
1430
"contributes": {
31+
"commands": [
32+
{
33+
"command": "git-base.api.getRemoteSources",
34+
"title": "%command.api.getRemoteSources%",
35+
"category": "Git Base API"
36+
}
37+
],
38+
"menus": {
39+
"commandPalette": [
40+
{
41+
"command": "git-base.api.getRemoteSources",
42+
"when": "false"
43+
}
44+
]
45+
},
1546
"languages": [
1647
{
1748
"id": "git-commit",
@@ -66,5 +97,15 @@
6697
"path": "./syntaxes/ignore.tmLanguage.json"
6798
}
6899
]
100+
},
101+
"dependencies": {
102+
"vscode-nls": "^4.0.0"
103+
},
104+
"devDependencies": {
105+
"@types/node": "14.x"
106+
},
107+
"repository": {
108+
"type": "git",
109+
"url": "https://github.com/microsoft/vscode.git"
69110
}
70111
}

extensions/git-base/package.nls.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
22
"displayName": "Git Base",
3-
"description": "Git static contributions and pickers."
3+
"description": "Git static contributions and pickers.",
4+
"command.api.getRemoteSources": "Get Remote Sources"
45
}
2.33 KB
Loading

extensions/git-base/src/api/api1.ts

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { Disposable, commands } from 'vscode';
7+
import { Model } from '../model';
8+
import { pickRemoteSource } from '../remoteSource';
9+
import { GitBaseExtensionImpl } from './extension';
10+
import { API, PickRemoteSourceOptions, PickRemoteSourceResult, RemoteSourceProvider } from './git-base';
11+
12+
export class ApiImpl implements API {
13+
14+
constructor(private _model: Model) { }
15+
16+
pickRemoteSource(options: PickRemoteSourceOptions): Promise<PickRemoteSourceResult | string | undefined> {
17+
return pickRemoteSource(this._model, options as any);
18+
}
19+
20+
registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable {
21+
return this._model.registerRemoteSourceProvider(provider);
22+
}
23+
}
24+
25+
export function registerAPICommands(extension: GitBaseExtensionImpl): Disposable {
26+
const disposables: Disposable[] = [];
27+
28+
disposables.push(commands.registerCommand('git-base.api.getRemoteSources', (opts?: PickRemoteSourceOptions) => {
29+
if (!extension.model) {
30+
return;
31+
}
32+
33+
return pickRemoteSource(extension.model, opts as any);
34+
}));
35+
36+
return Disposable.from(...disposables);
37+
}
+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { Model } from '../model';
7+
import { GitBaseExtension, API } from './git-base';
8+
import { Event, EventEmitter } from 'vscode';
9+
import { ApiImpl } from './api1';
10+
11+
export class GitBaseExtensionImpl implements GitBaseExtension {
12+
13+
enabled: boolean = false;
14+
15+
private _onDidChangeEnablement = new EventEmitter<boolean>();
16+
readonly onDidChangeEnablement: Event<boolean> = this._onDidChangeEnablement.event;
17+
18+
private _model: Model | undefined = undefined;
19+
20+
set model(model: Model | undefined) {
21+
this._model = model;
22+
23+
const enabled = !!model;
24+
25+
if (this.enabled === enabled) {
26+
return;
27+
}
28+
29+
this.enabled = enabled;
30+
this._onDidChangeEnablement.fire(this.enabled);
31+
}
32+
33+
get model(): Model | undefined {
34+
return this._model;
35+
}
36+
37+
constructor(model?: Model) {
38+
if (model) {
39+
this.enabled = true;
40+
this._model = model;
41+
}
42+
}
43+
44+
getAPI(version: number): API {
45+
if (!this._model) {
46+
throw new Error('Git model not found');
47+
}
48+
49+
if (version !== 1) {
50+
throw new Error(`No API version ${version} found.`);
51+
}
52+
53+
return new ApiImpl(this._model);
54+
}
55+
}
+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { Disposable, Event, ProviderResult, Uri } from 'vscode';
7+
export { ProviderResult } from 'vscode';
8+
9+
export interface API {
10+
registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable;
11+
pickRemoteSource(options: PickRemoteSourceOptions): Promise<string | PickRemoteSourceResult | undefined>;
12+
}
13+
14+
export interface GitBaseExtension {
15+
16+
readonly enabled: boolean;
17+
readonly onDidChangeEnablement: Event<boolean>;
18+
19+
/**
20+
* Returns a specific API version.
21+
*
22+
* Throws error if git-base extension is disabled. You can listed to the
23+
* [GitBaseExtension.onDidChangeEnablement](#GitBaseExtension.onDidChangeEnablement)
24+
* event to know when the extension becomes enabled/disabled.
25+
*
26+
* @param version Version number.
27+
* @returns API instance
28+
*/
29+
getAPI(version: 1): API;
30+
}
31+
32+
export interface PickRemoteSourceOptions {
33+
readonly providerLabel?: (provider: RemoteSourceProvider) => string;
34+
readonly urlLabel?: string;
35+
readonly providerName?: string;
36+
readonly branch?: boolean; // then result is PickRemoteSourceResult
37+
}
38+
39+
export interface PickRemoteSourceResult {
40+
readonly url: string;
41+
readonly branch?: string;
42+
}
43+
44+
export interface RemoteSource {
45+
readonly name: string;
46+
readonly description?: string;
47+
readonly url: string | string[];
48+
}
49+
50+
export interface RemoteSourceProvider {
51+
readonly name: string;
52+
/**
53+
* Codicon name
54+
*/
55+
readonly icon?: string;
56+
readonly supportsQuery?: boolean;
57+
58+
getBranches?(url: string): ProviderResult<string[]>;
59+
getRemoteSources(query?: string): ProviderResult<RemoteSource[]>;
60+
}

extensions/git-base/src/decorators.ts

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { done } from './util';
7+
8+
export function debounce(delay: number): Function {
9+
return decorate((fn, key) => {
10+
const timerKey = `$debounce$${key}`;
11+
12+
return function (this: any, ...args: any[]) {
13+
clearTimeout(this[timerKey]);
14+
this[timerKey] = setTimeout(() => fn.apply(this, args), delay);
15+
};
16+
});
17+
}
18+
19+
export const throttle = decorate(_throttle);
20+
21+
function _throttle<T>(fn: Function, key: string): Function {
22+
const currentKey = `$throttle$current$${key}`;
23+
const nextKey = `$throttle$next$${key}`;
24+
25+
const trigger = function (this: any, ...args: any[]) {
26+
if (this[nextKey]) {
27+
return this[nextKey];
28+
}
29+
30+
if (this[currentKey]) {
31+
this[nextKey] = done(this[currentKey]).then(() => {
32+
this[nextKey] = undefined;
33+
return trigger.apply(this, args);
34+
});
35+
36+
return this[nextKey];
37+
}
38+
39+
this[currentKey] = fn.apply(this, args) as Promise<T>;
40+
41+
const clear = () => this[currentKey] = undefined;
42+
done(this[currentKey]).then(clear, clear);
43+
44+
return this[currentKey];
45+
};
46+
47+
return trigger;
48+
}
49+
50+
function decorate(decorator: (fn: Function, key: string) => Function): Function {
51+
return (_target: any, key: string, descriptor: any) => {
52+
let fnKey: string | null = null;
53+
let fn: Function | null = null;
54+
55+
if (typeof descriptor.value === 'function') {
56+
fnKey = 'value';
57+
fn = descriptor.value;
58+
} else if (typeof descriptor.get === 'function') {
59+
fnKey = 'get';
60+
fn = descriptor.get;
61+
}
62+
63+
if (!fn || !fnKey) {
64+
throw new Error('not supported');
65+
}
66+
67+
descriptor[fnKey] = decorator(fn, key);
68+
};
69+
}

extensions/git-base/src/extension.ts

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { ExtensionContext } from 'vscode';
7+
import { registerAPICommands } from './api/api1';
8+
import { GitBaseExtensionImpl } from './api/extension';
9+
import { Model } from './model';
10+
11+
export function activate(context: ExtensionContext): GitBaseExtensionImpl {
12+
const apiImpl = new GitBaseExtensionImpl(new Model());
13+
context.subscriptions.push(registerAPICommands(apiImpl));
14+
15+
return apiImpl;
16+
}

extensions/git-base/src/model.ts

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { EventEmitter, Disposable } from 'vscode';
7+
import { toDisposable } from './util';
8+
import { RemoteSourceProvider } from './api/git-base';
9+
import { IRemoteSourceProviderRegistry } from './remoteProvider';
10+
11+
export class Model implements IRemoteSourceProviderRegistry {
12+
13+
private remoteSourceProviders = new Set<RemoteSourceProvider>();
14+
15+
private _onDidAddRemoteSourceProvider = new EventEmitter<RemoteSourceProvider>();
16+
readonly onDidAddRemoteSourceProvider = this._onDidAddRemoteSourceProvider.event;
17+
18+
private _onDidRemoveRemoteSourceProvider = new EventEmitter<RemoteSourceProvider>();
19+
readonly onDidRemoveRemoteSourceProvider = this._onDidRemoveRemoteSourceProvider.event;
20+
21+
registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable {
22+
this.remoteSourceProviders.add(provider);
23+
this._onDidAddRemoteSourceProvider.fire(provider);
24+
25+
return toDisposable(() => {
26+
this.remoteSourceProviders.delete(provider);
27+
this._onDidRemoveRemoteSourceProvider.fire(provider);
28+
});
29+
}
30+
31+
getRemoteProviders(): RemoteSourceProvider[] {
32+
return [...this.remoteSourceProviders.values()];
33+
}
34+
}

extensions/git/src/remoteProvider.ts renamed to extensions/git-base/src/remoteProvider.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import { Disposable, Event } from 'vscode';
7-
import { RemoteSourceProvider } from './api/git';
7+
import { RemoteSourceProvider } from './api/git-base';
88

99
export interface IRemoteSourceProviderRegistry {
1010
readonly onDidAddRemoteSourceProvider: Event<RemoteSourceProvider>;
1111
readonly onDidRemoveRemoteSourceProvider: Event<RemoteSourceProvider>;
12-
registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable;
12+
1313
getRemoteProviders(): RemoteSourceProvider[];
14+
registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable;
1415
}

0 commit comments

Comments
 (0)