Files
vscode/extensions/git/src/commands.ts
2017-01-11 18:56:15 +01:00

228 lines
6.2 KiB
TypeScript

/*---------------------------------------------------------------------------------------------
* 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 { Uri, commands, scm, Disposable, SCMResourceGroup, SCMResource, window, workspace, QuickPickItem } from 'vscode';
import { IRef, RefType } from './git';
import { Model, Resource } from './model';
import { log } from './util';
import { decorate } from 'core-decorators';
import * as path from 'path';
type Command = (...args: any[]) => any;
function catchErrors(fn: (...args) => Promise<any>): (...args) => void {
return (...args) => fn.call(this, ...args).catch(err => console.log(err));
}
function resolveGitURI(uri: Uri): SCMResource | SCMResourceGroup | undefined {
if (uri.authority !== 'git') {
return;
}
return scm.getResourceFromURI(uri);
}
function resolveGitResource(uri: Uri): Resource | undefined {
const resource = resolveGitURI(uri);
if (!(resource instanceof Resource)) {
return;
}
return resource;
}
class CheckoutItem implements QuickPickItem {
protected get shortCommit(): string { return (this.ref.commit || '').substr(0, 8); }
protected get treeish(): string | undefined { return this.ref.name; }
get label(): string { return this.ref.name || this.shortCommit; }
get description(): string { return this.shortCommit; }
constructor(protected ref: IRef) { }
async run(model: Model): Promise<void> {
const ref = this.treeish;
if (!ref) {
return;
}
await model.checkout(ref);
}
}
class CheckoutTagItem extends CheckoutItem {
get description(): string { return `Tag at ${this.shortCommit}`; }
}
class CheckoutRemoteHeadItem extends CheckoutItem {
get description(): string { return `Remote branch at ${this.shortCommit}`; }
protected get treeish(): string | undefined {
if (!this.ref.name) {
return;
}
const match = /^[^/]+\/(.*)$/.exec(this.ref.name);
return match ? match[1] : this.ref.name;
}
}
class CommandCenter {
private disposables: Disposable[] = [];
constructor(private model: Model) {
this.disposables.push(
commands.registerCommand('git.refresh', this.refresh, this),
commands.registerCommand('git.openChange', this.openChange, this),
commands.registerCommand('git.openFile', this.openFile, this),
commands.registerCommand('git.stage', this.stage, this),
commands.registerCommand('git.stageAll', this.stageAll, this),
commands.registerCommand('git.unstage', this.unstage, this),
commands.registerCommand('git.unstageAll', this.unstageAll, this),
commands.registerCommand('git.clean', this.clean, this),
commands.registerCommand('git.cleanAll', this.cleanAll, this),
commands.registerCommand('git.checkout', this.checkout, this),
commands.registerCommand('git.publish', this.publish, this),
);
}
@decorate(catchErrors)
async refresh(): Promise<void> {
return await this.model.update();
}
openChange(uri: Uri): void {
const resource = resolveGitResource(uri);
log('open change', resource);
}
openFile(uri: Uri): void {
const resource = resolveGitResource(uri);
log('open file', resource);
}
@decorate(catchErrors)
async stage(uri: Uri): Promise<void> {
const resource = resolveGitResource(uri);
if (!resource) {
return;
}
return await this.model.stage(resource);
}
@decorate(catchErrors)
async stageAll(): Promise<void> {
return await this.model.stage();
}
@decorate(catchErrors)
async unstage(uri: Uri): Promise<void> {
const resource = resolveGitResource(uri);
if (!resource) {
return;
}
return await this.model.unstage(resource);
}
@decorate(catchErrors)
async unstageAll(): Promise<void> {
return await this.model.unstage();
}
@decorate(catchErrors)
async clean(uri: Uri): Promise<void> {
const resource = resolveGitResource(uri);
if (!resource) {
return;
}
const basename = path.basename(resource.uri.fsPath);
const message = `Are you sure you want to clean changes in ${basename}?`;
const yes = 'Yes';
const no = 'No, keep them';
const pick = await window.showQuickPick([no, yes], { placeHolder: message });
if (pick !== yes) {
return;
}
return await this.model.clean(resource);
}
@decorate(catchErrors)
async cleanAll(): Promise<void> {
const message = `Are you sure you want to clean all changes?`;
const yes = 'Yes';
const no = 'No, keep them';
const pick = await window.showQuickPick([no, yes], { placeHolder: message });
if (pick !== yes) {
return;
}
return await this.model.clean(...this.model.workingTreeGroup.resources);
}
@decorate(catchErrors)
async checkout(): Promise<void> {
const config = workspace.getConfiguration('git');
const checkoutType = config.get<string>('checkoutType');
const includeTags = checkoutType === 'all' || checkoutType === 'tags';
const includeRemotes = checkoutType === 'all' || checkoutType === 'remote';
const heads = this.model.refs.filter(ref => ref.type === RefType.Head)
.map(ref => new CheckoutItem(ref));
const tags = (includeTags ? this.model.refs.filter(ref => ref.type === RefType.Tag) : [])
.map(ref => new CheckoutTagItem(ref));
const remoteHeads = (includeRemotes ? this.model.refs.filter(ref => ref.type === RefType.RemoteHead) : [])
.map(ref => new CheckoutRemoteHeadItem(ref));
const picks = [...heads, ...tags, ...remoteHeads];
const placeHolder = 'Select a ref to checkout';
const choice = await window.showQuickPick<CheckoutItem>(picks, { placeHolder });
if (!choice) {
return;
}
await choice.run(this.model);
}
@decorate(catchErrors)
async publish(): Promise<void> {
const branchName = this.model.HEAD && this.model.HEAD.name || '';
const picks = this.model.remotes.map(r => r.name);
const placeHolder = `Pick a remote to publish the branch '${branchName}' to:`;
const choice = await window.showQuickPick(picks, { placeHolder });
if (!choice) {
return;
}
await this.model.push(choice, branchName, { setUpstream: true });
}
dispose(): void {
this.disposables.forEach(d => d.dispose());
}
}
export function registerCommands(model: Model): Disposable {
return new CommandCenter(model);
}