Skip to content

Commit

Permalink
Extract remote source provider registry into the vscode.git-base exte…
Browse files Browse the repository at this point in the history
…nsion (#137656)
  • Loading branch information
lszomoru authored Nov 24, 2021
1 parent 483d6f1 commit bfad20b
Show file tree
Hide file tree
Showing 35 changed files with 946 additions and 258 deletions.
1 change: 1 addition & 0 deletions build/gulpfile.extensions.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const compilations = [
'emmet/tsconfig.json',
'extension-editing/tsconfig.json',
'git/tsconfig.json',
'git-base/tsconfig.json',
'github-authentication/tsconfig.json',
'github/tsconfig.json',
'grunt/tsconfig.json',
Expand Down
1 change: 1 addition & 0 deletions build/npm/dirs.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ exports.dirs = [
'extensions/emmet',
'extensions/extension-editing',
'extensions/git',
'extensions/git-base',
'extensions/github',
'extensions/github-authentication',
'extensions/grunt',
Expand Down
17 changes: 17 additions & 0 deletions extensions/git-base/extension.webpack.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

//@ts-check

'use strict';

const withDefaults = require('../shared.webpack.config');

module.exports = withDefaults({
context: __dirname,
entry: {
extension: './src/extension.ts'
}
});
41 changes: 41 additions & 0 deletions extensions/git-base/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,41 @@
"engines": {
"vscode": "0.10.x"
},
"categories": [
"Other"
],
"activationEvents": [
"*"
],
"main": "./out/extension.js",
"icon": "resources/icons/git.png",
"scripts": {
"compile": "gulp compile-extension:git-base",
"watch": "gulp watch-extension:git-base",
"update-grammar": "node ./build/update-grammars.js"
},
"capabilities": {
"virtualWorkspaces": true,
"untrustedWorkspaces": {
"supported": true
}
},
"contributes": {
"commands": [
{
"command": "git-base.api.getRemoteSources",
"title": "%command.api.getRemoteSources%",
"category": "Git Base API"
}
],
"menus": {
"commandPalette": [
{
"command": "git-base.api.getRemoteSources",
"when": "false"
}
]
},
"languages": [
{
"id": "git-commit",
Expand Down Expand Up @@ -66,5 +97,15 @@
"path": "./syntaxes/ignore.tmLanguage.json"
}
]
},
"dependencies": {
"vscode-nls": "^4.0.0"
},
"devDependencies": {
"@types/node": "14.x"
},
"repository": {
"type": "git",
"url": "https://github.com/microsoft/vscode.git"
}
}
3 changes: 2 additions & 1 deletion extensions/git-base/package.nls.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"displayName": "Git Base",
"description": "Git static contributions and pickers."
"description": "Git static contributions and pickers.",
"command.api.getRemoteSources": "Get Remote Sources"
}
Binary file added extensions/git-base/resources/icons/git.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
37 changes: 37 additions & 0 deletions extensions/git-base/src/api/api1.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { Disposable, commands } from 'vscode';
import { Model } from '../model';
import { pickRemoteSource } from '../remoteSource';
import { GitBaseExtensionImpl } from './extension';
import { API, PickRemoteSourceOptions, PickRemoteSourceResult, RemoteSourceProvider } from './git-base';

export class ApiImpl implements API {

constructor(private _model: Model) { }

pickRemoteSource(options: PickRemoteSourceOptions): Promise<PickRemoteSourceResult | string | undefined> {
return pickRemoteSource(this._model, options as any);
}

registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable {
return this._model.registerRemoteSourceProvider(provider);
}
}

export function registerAPICommands(extension: GitBaseExtensionImpl): Disposable {
const disposables: Disposable[] = [];

disposables.push(commands.registerCommand('git-base.api.getRemoteSources', (opts?: PickRemoteSourceOptions) => {
if (!extension.model) {
return;
}

return pickRemoteSource(extension.model, opts as any);
}));

return Disposable.from(...disposables);
}
55 changes: 55 additions & 0 deletions extensions/git-base/src/api/extension.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { Model } from '../model';
import { GitBaseExtension, API } from './git-base';
import { Event, EventEmitter } from 'vscode';
import { ApiImpl } from './api1';

export class GitBaseExtensionImpl implements GitBaseExtension {

enabled: boolean = false;

private _onDidChangeEnablement = new EventEmitter<boolean>();
readonly onDidChangeEnablement: Event<boolean> = this._onDidChangeEnablement.event;

private _model: Model | undefined = undefined;

set model(model: Model | undefined) {
this._model = model;

const enabled = !!model;

if (this.enabled === enabled) {
return;
}

this.enabled = enabled;
this._onDidChangeEnablement.fire(this.enabled);
}

get model(): Model | undefined {
return this._model;
}

constructor(model?: Model) {
if (model) {
this.enabled = true;
this._model = model;
}
}

getAPI(version: number): API {
if (!this._model) {
throw new Error('Git model not found');
}

if (version !== 1) {
throw new Error(`No API version ${version} found.`);
}

return new ApiImpl(this._model);
}
}
60 changes: 60 additions & 0 deletions extensions/git-base/src/api/git-base.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { Disposable, Event, ProviderResult, Uri } from 'vscode';
export { ProviderResult } from 'vscode';

export interface API {
registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable;
pickRemoteSource(options: PickRemoteSourceOptions): Promise<string | PickRemoteSourceResult | undefined>;
}

export interface GitBaseExtension {

readonly enabled: boolean;
readonly onDidChangeEnablement: Event<boolean>;

/**
* Returns a specific API version.
*
* Throws error if git-base extension is disabled. You can listed to the
* [GitBaseExtension.onDidChangeEnablement](#GitBaseExtension.onDidChangeEnablement)
* event to know when the extension becomes enabled/disabled.
*
* @param version Version number.
* @returns API instance
*/
getAPI(version: 1): API;
}

export interface PickRemoteSourceOptions {
readonly providerLabel?: (provider: RemoteSourceProvider) => string;
readonly urlLabel?: string;
readonly providerName?: string;
readonly branch?: boolean; // then result is PickRemoteSourceResult
}

export interface PickRemoteSourceResult {
readonly url: string;
readonly branch?: string;
}

export interface RemoteSource {
readonly name: string;
readonly description?: string;
readonly url: string | string[];
}

export interface RemoteSourceProvider {
readonly name: string;
/**
* Codicon name
*/
readonly icon?: string;
readonly supportsQuery?: boolean;

getBranches?(url: string): ProviderResult<string[]>;
getRemoteSources(query?: string): ProviderResult<RemoteSource[]>;
}
69 changes: 69 additions & 0 deletions extensions/git-base/src/decorators.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { done } from './util';

export function debounce(delay: number): Function {
return decorate((fn, key) => {
const timerKey = `$debounce$${key}`;

return function (this: any, ...args: any[]) {
clearTimeout(this[timerKey]);
this[timerKey] = setTimeout(() => fn.apply(this, args), delay);
};
});
}

export const throttle = decorate(_throttle);

function _throttle<T>(fn: Function, key: string): Function {
const currentKey = `$throttle$current$${key}`;
const nextKey = `$throttle$next$${key}`;

const trigger = function (this: any, ...args: any[]) {
if (this[nextKey]) {
return this[nextKey];
}

if (this[currentKey]) {
this[nextKey] = done(this[currentKey]).then(() => {
this[nextKey] = undefined;
return trigger.apply(this, args);
});

return this[nextKey];
}

this[currentKey] = fn.apply(this, args) as Promise<T>;

const clear = () => this[currentKey] = undefined;
done(this[currentKey]).then(clear, clear);

return this[currentKey];
};

return trigger;
}

function decorate(decorator: (fn: Function, key: string) => Function): Function {
return (_target: any, key: string, descriptor: any) => {
let fnKey: string | null = null;
let fn: Function | null = null;

if (typeof descriptor.value === 'function') {
fnKey = 'value';
fn = descriptor.value;
} else if (typeof descriptor.get === 'function') {
fnKey = 'get';
fn = descriptor.get;
}

if (!fn || !fnKey) {
throw new Error('not supported');
}

descriptor[fnKey] = decorator(fn, key);
};
}
16 changes: 16 additions & 0 deletions extensions/git-base/src/extension.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { ExtensionContext } from 'vscode';
import { registerAPICommands } from './api/api1';
import { GitBaseExtensionImpl } from './api/extension';
import { Model } from './model';

export function activate(context: ExtensionContext): GitBaseExtensionImpl {
const apiImpl = new GitBaseExtensionImpl(new Model());
context.subscriptions.push(registerAPICommands(apiImpl));

return apiImpl;
}
34 changes: 34 additions & 0 deletions extensions/git-base/src/model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { EventEmitter, Disposable } from 'vscode';
import { toDisposable } from './util';
import { RemoteSourceProvider } from './api/git-base';
import { IRemoteSourceProviderRegistry } from './remoteProvider';

export class Model implements IRemoteSourceProviderRegistry {

private remoteSourceProviders = new Set<RemoteSourceProvider>();

private _onDidAddRemoteSourceProvider = new EventEmitter<RemoteSourceProvider>();
readonly onDidAddRemoteSourceProvider = this._onDidAddRemoteSourceProvider.event;

private _onDidRemoveRemoteSourceProvider = new EventEmitter<RemoteSourceProvider>();
readonly onDidRemoveRemoteSourceProvider = this._onDidRemoveRemoteSourceProvider.event;

registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable {
this.remoteSourceProviders.add(provider);
this._onDidAddRemoteSourceProvider.fire(provider);

return toDisposable(() => {
this.remoteSourceProviders.delete(provider);
this._onDidRemoveRemoteSourceProvider.fire(provider);
});
}

getRemoteProviders(): RemoteSourceProvider[] {
return [...this.remoteSourceProviders.values()];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
*--------------------------------------------------------------------------------------------*/

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

export interface IRemoteSourceProviderRegistry {
readonly onDidAddRemoteSourceProvider: Event<RemoteSourceProvider>;
readonly onDidRemoveRemoteSourceProvider: Event<RemoteSourceProvider>;
registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable;

getRemoteProviders(): RemoteSourceProvider[];
registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable;
}
Loading

0 comments on commit bfad20b

Please sign in to comment.