diff --git a/extensions/git/package.json b/extensions/git/package.json index 6f7cd689eda..90987f334dc 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -91,9 +91,9 @@ ] }, "dependencies": { + "core-decorators": "^0.14.0", "denodeify": "^1.2.1", "lodash": "^4.17.2", - "mime": "^1.3.4", "vscode-nls": "^2.0.1" } -} +} \ No newline at end of file diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index b05d7555643..8a9972e83c5 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -14,7 +14,6 @@ import { IDisposable, toDisposable, dispose } from './util'; import * as _ from 'lodash'; import { EventEmitter, Event } from 'vscode'; import * as nls from 'vscode-nls'; -import * as mime from 'mime'; const localize = nls.loadMessageBundle(__filename); const readdir = denodeify(fs.readdir); @@ -33,7 +32,6 @@ export interface IFileStatus { x: string; y: string; path: string; - mimetype: string; rename?: string; } @@ -743,8 +741,7 @@ export class Repository { current = { x: status.charAt(i++), y: status.charAt(i++), - path: '', - mimetype: '' + path: '' }; i++; @@ -754,7 +751,6 @@ export class Repository { } current.path = readName(); - current.mimetype = mime.lookup(current.path); // If path ends with slash, it must be a nested git repo if (current.path[current.path.length - 1] === '/') { diff --git a/extensions/git/src/main.ts b/extensions/git/src/main.ts index faf1ca88dd9..0b846f921f3 100644 --- a/extensions/git/src/main.ts +++ b/extensions/git/src/main.ts @@ -8,6 +8,7 @@ import { scm, ExtensionContext, workspace, Uri, window, Disposable } from 'vscode'; import * as path from 'path'; import { findGit, Git } from './git'; +import { Model } from './model'; import { registerCommands } from './commands'; import * as nls from 'vscode-nls'; @@ -61,6 +62,12 @@ async function init(disposables: Disposable[]): Promise { const pathHint = workspace.getConfiguration('git').get('path'); const info = await findGit(pathHint); const git = new Git({ gitPath: info.path, version: info.version }); + const repository = git.open(rootPath); + const model = new Model(repository); + + model.onDidChange(() => { + console.log(model.status); + }); const outputChannel = window.createOutputChannel('git'); outputChannel.appendLine(`Using git ${info.version} from ${info.path}`); diff --git a/extensions/git/src/model.ts b/extensions/git/src/model.ts new file mode 100644 index 00000000000..203d81639b2 --- /dev/null +++ b/extensions/git/src/model.ts @@ -0,0 +1,76 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { EventEmitter, Event } from 'vscode'; +import { Repository, IRef, IFileStatus, IRemote } from './git'; +import { throttle } from './util'; +import { decorate, debounce } from 'core-decorators'; + +export class Model { + + private _onDidChange = new EventEmitter(); + readonly onDidChange: Event = this._onDidChange.event; + + constructor(private repository: Repository) { + + } + + private _status: IFileStatus[]; + get status(): IFileStatus[] { + return this._status; + } + + private _HEAD: IRef | undefined; + get HEAD(): IRef | undefined { + return this._HEAD; + } + + private _refs: IRef[]; + get refs(): IRef[] { + return this._refs; + } + + private _remotes: IRemote[]; + get remotes(): IRemote[] { + return this._remotes; + } + + @debounce(500) + triggerUpdate(): void { + this.update(); + } + + @decorate(throttle) + private async update(): Promise { + console.log('START'); + const status = await this.repository.getStatus(); + let HEAD: IRef | undefined; + + try { + HEAD = await this.repository.getHEAD(); + + if (HEAD.name) { + try { + HEAD = await this.repository.getBranch(HEAD.name); + } catch (err) { + // noop + } + } + } catch (err) { + // noop + } + + const [refs, remotes] = await Promise.all([this.repository.getRefs(), this.repository.getRemotes()]); + + this._status = status; + this._HEAD = HEAD; + this._refs = refs; + this._remotes = remotes; + console.log('END'); + this._onDidChange.fire(); + } +} \ No newline at end of file diff --git a/extensions/git/src/typings.json b/extensions/git/src/typings.json index 4423da2e6d7..a938defdec2 100644 --- a/extensions/git/src/typings.json +++ b/extensions/git/src/typings.json @@ -1,7 +1,7 @@ { "globalDependencies": { + "core-decorators": "registry:dt/core-decorators#0.10.0+20160316155526", "denodeify": "registry:dt/denodeify#1.2.1+20160316155526", - "lodash": "registry:dt/lodash#4.14.0+20161110215204", - "mime": "registry:dt/mime#0.0.0+20160316155526" + "lodash": "registry:dt/lodash#4.14.0+20161110215204" } -} +} \ No newline at end of file diff --git a/extensions/git/src/typings/globals/core-decorators/index.d.ts b/extensions/git/src/typings/globals/core-decorators/index.d.ts new file mode 100644 index 00000000000..72d0b17b1e6 --- /dev/null +++ b/extensions/git/src/typings/globals/core-decorators/index.d.ts @@ -0,0 +1,133 @@ +// Generated by typings +// Source: https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/56295f5058cac7ae458540423c50ac2dcf9fc711/core-decorators/core-decorators.d.ts +declare module "core-decorators" { + export interface ClassDecorator { + (target: TFunction): TFunction|void; + } + + export interface ParameterDecorator { + (target: Object, propertyKey: string|symbol, parameterIndex: number): void; + } + + export interface PropertyDecorator { + (target: Object, propertyKey: string|symbol): void; + } + + export interface MethodDecorator { + (target: Object, propertyKey: string|symbol, descriptor: TypedPropertyDescriptor): TypedPropertyDescriptor|void; + } + + export interface PropertyOrMethodDecorator extends MethodDecorator, PropertyDecorator { + (target: Object, propertyKey: string|symbol): void; + } + + export interface Deprecate extends MethodDecorator { + (message?: string, option?: DeprecateOption): MethodDecorator; + } + + export interface DeprecateOption { + url: string; + } + + export interface ThrottleOptions { + /** allows to trigger function on the leading. */ + leading?: boolean; + /** allows to trigger function on the trailing edge of the wait interval. */ + trailing?: boolean; + } + + export interface Console { + log(message?: any, ...optionalParams: any[]): void; + time(timerName?: string): void; + timeEnd(timerName?: string): void; + } + + /** + * Forces invocations of this function to always have this refer to the class instance, + * even if the function is passed around or would otherwise lose its this context. e.g. var fn = context.method; + */ + var autobind: Function; + /** + * Marks a property or method as not being writable. + */ + var readonly: PropertyOrMethodDecorator; + /** + * Checks that the marked method indeed overrides a function with the same signature somewhere on the prototype chain. + */ + var override: MethodDecorator; + /** + * Calls console.warn() with a deprecation message. Provide a custom message to override the default one. You can also provide an options hash with a url, for further reading. + */ + var deprecate: Deprecate; + /** + * Calls console.warn() with a deprecation message. Provide a custom message to override the default one. You can also provide an options hash with a url, for further reading. + */ + var deprecated: Deprecate; + /** + * Creates a new debounced function which will be invoked after wait milliseconds since the time it was invoked. Default timeout is 300 ms. + */ + var debounce: (wait: number) => MethodDecorator; + /** + * Creates a new throttled function which will be invoked in every wait milliseconds. Default timeout is 300 ms. + */ + var throttle: (wait: number, options?: ThrottleOptions) => MethodDecorator; + /** + * Suppresses any JavaScript console.warn() call while the decorated function is called. (i.e. on the stack) + */ + var suppressWarnings: MethodDecorator; + /** + * Marks a property or method as not being enumerable. + */ + var nonenumerable: PropertyOrMethodDecorator; + /** + * Marks a property or method as not being writable. + */ + var nonconfigurable: PropertyOrMethodDecorator; + /** + * Initial implementation included, likely slow. WIP. + */ + var memoize: MethodDecorator; + /** + * Immediately applies the provided function and arguments to the method, allowing you to wrap methods with arbitrary helpers like those provided by lodash. + * The first argument is the function to apply, all further arguments will be passed to that decorating function. + */ + var decorate: (func: Function, ...args: any[]) => MethodDecorator; + /** + * Prevents a property initializer from running until the decorated property is actually looked up. + * Useful to prevent excess allocations that might otherwise not be used, but be careful not to over-optimize things. + */ + var lazyInitialize: PropertyDecorator; + /** + * Mixes in all property descriptors from the provided Plain Old JavaScript Objects (aka POJOs) as arguments. + * Mixins are applied in the order they are passed, but do not override descriptors already on the class, including those inherited traditionally. + */ + var mixin: (...mixins: any[]) => ClassDecorator; + /** + * Mixes in all property descriptors from the provided Plain Old JavaScript Objects (aka POJOs) as arguments. + * Mixins are applied in the order they are passed, but do not override descriptors already on the class, including those inherited traditionally. + */ + var mixins: (...mixins: any[]) => ClassDecorator; + /** + * Uses console.time and console.timeEnd to provide function timings with a unique label whose default prefix is ClassName.method. Supply a first argument to override the prefix: + */ + var time: (label: string, console?: Console) => MethodDecorator; + + export { + autobind, + readonly, + override, + deprecate, + deprecated, + debounce, + throttle, + suppressWarnings, + nonenumerable, + nonconfigurable, + memoize, + decorate, + lazyInitialize, + mixin, + mixins, + time, + }; +} diff --git a/extensions/git/src/typings/globals/core-decorators/typings.json b/extensions/git/src/typings/globals/core-decorators/typings.json new file mode 100644 index 00000000000..3d14e2d9d81 --- /dev/null +++ b/extensions/git/src/typings/globals/core-decorators/typings.json @@ -0,0 +1,8 @@ +{ + "resolution": "main", + "tree": { + "src": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/56295f5058cac7ae458540423c50ac2dcf9fc711/core-decorators/core-decorators.d.ts", + "raw": "registry:dt/core-decorators#0.10.0+20160316155526", + "typings": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/56295f5058cac7ae458540423c50ac2dcf9fc711/core-decorators/core-decorators.d.ts" + } +} \ No newline at end of file diff --git a/extensions/git/src/typings/globals/mime/index.d.ts b/extensions/git/src/typings/globals/mime/index.d.ts deleted file mode 100644 index b3eb501ce9e..00000000000 --- a/extensions/git/src/typings/globals/mime/index.d.ts +++ /dev/null @@ -1,15 +0,0 @@ -// Generated by typings -// Source: https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/56295f5058cac7ae458540423c50ac2dcf9fc711/mime/mime.d.ts -declare module "mime" { - export function lookup(path: string): string; - export function extension(mime: string): string; - export function load(filepath: string): void; - export function define(mimes: Object): void; - - interface Charsets { - lookup(mime: string): string; - } - - export var charsets: Charsets; - export var default_type: string; -} diff --git a/extensions/git/src/typings/globals/mime/typings.json b/extensions/git/src/typings/globals/mime/typings.json deleted file mode 100644 index cfc44e3e661..00000000000 --- a/extensions/git/src/typings/globals/mime/typings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "resolution": "main", - "tree": { - "src": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/56295f5058cac7ae458540423c50ac2dcf9fc711/mime/mime.d.ts", - "raw": "registry:dt/mime#0.0.0+20160316155526", - "typings": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/56295f5058cac7ae458540423c50ac2dcf9fc711/mime/mime.d.ts" - } -} diff --git a/extensions/git/src/typings/index.d.ts b/extensions/git/src/typings/index.d.ts index 3845b4f5c11..fc78f2e5c28 100644 --- a/extensions/git/src/typings/index.d.ts +++ b/extensions/git/src/typings/index.d.ts @@ -1,3 +1,3 @@ +/// /// /// -/// diff --git a/extensions/git/src/util.ts b/extensions/git/src/util.ts index 2cf2ec01029..77d82092250 100644 --- a/extensions/git/src/util.ts +++ b/extensions/git/src/util.ts @@ -34,4 +34,38 @@ export function filterEvent(event: Event, filter: (e: T) => boolean): Even export function anyEvent(...events: Event[]): Event { return (listener, thisArgs = null, disposables?) => combinedDisposable(events.map(event => event(i => listener.call(thisArgs, i), disposables))); +} + +export function done(promise: Promise): Promise { + return promise.then(() => null, () => null); +} + +export function throttle(fn: () => Promise): () => Promise { + let current: Promise | undefined; + let next: Promise | undefined; + + const trigger = () => { + if (next) { + return next; + } + + if (current) { + next = done(current).then(() => { + next = undefined; + return trigger(); + }); + + return next; + } + + current = fn.call(this) as Promise; + + done(current).then(() => { + current = undefined; + }); + + return current; + }; + + return trigger; } \ No newline at end of file diff --git a/extensions/git/tsconfig.json b/extensions/git/tsconfig.json index 6ccededff4b..bed30f8826c 100644 --- a/extensions/git/tsconfig.json +++ b/extensions/git/tsconfig.json @@ -1,10 +1,13 @@ { "compilerOptions": { "target": "es6", - "lib": ["es2016"], + "lib": [ + "es2016" + ], "module": "commonjs", "outDir": "./out", - "strictNullChecks": true + "strictNullChecks": true, + "experimentalDecorators": true }, "exclude": [ "node_modules"