gitQuickOpen.ts 9.1 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5 6
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
'use strict';

J
Joao Moreno 已提交
7 8 9 10
import { localize } from 'vs/nls';
import { matchesContiguousSubString } from 'vs/base/common/filters';
import { TPromise } from 'vs/base/common/winjs.base';
import Severity from 'vs/base/common/severity';
J
Joao Moreno 已提交
11
import { IGitService, RefType, IRef } from 'vs/workbench/parts/git/common/git';
J
Joao Moreno 已提交
12 13 14 15 16
import { ICommand, CommandQuickOpenHandler } from 'vs/workbench/browser/quickopen';
import { Mode } from 'vs/base/parts/quickopen/common/quickOpen';
import { QuickOpenEntry, IHighlight, IContext, QuickOpenEntryGroup } from 'vs/base/parts/quickopen/browser/quickOpenModel';
import { IQuickOpenService } from 'vs/workbench/services/quickopen/common/quickOpenService';
import { IMessageService } from 'vs/platform/message/common/message';
E
Erich Gamma 已提交
17

J
Joao Moreno 已提交
18
class AbstractRefEntry extends QuickOpenEntry {
E
Erich Gamma 已提交
19

J
Joao Moreno 已提交
20
	protected gitService: IGitService;
E
Erich Gamma 已提交
21
	protected messageService: IMessageService;
J
Joao Moreno 已提交
22
	protected ref: IRef;
E
Erich Gamma 已提交
23

J
Joao Moreno 已提交
24
	constructor(gitService: IGitService, messageService: IMessageService, ref: IRef, highlights: IHighlight[]) {
E
Erich Gamma 已提交
25 26 27 28
		super(highlights);

		this.gitService = gitService;
		this.messageService = messageService;
J
Joao Moreno 已提交
29
		this.ref = ref;
E
Erich Gamma 已提交
30 31
	}

J
Joao Moreno 已提交
32 33 34
	getIcon(): string { return 'git'; }
	getLabel(): string { return this.ref.name; }
	getDescription(): string { return ''; }
J
Joao Moreno 已提交
35
	getAriaLabel(): string { return localize('refAriaLabel', "{0}, git", this.getLabel()); }
E
Erich Gamma 已提交
36

J
Johannes Rieken 已提交
37
	run(mode: Mode, context: IContext): boolean {
J
Joao Moreno 已提交
38
		if (mode === Mode.PREVIEW) {
E
Erich Gamma 已提交
39 40 41 42 43 44 45 46 47
			return false;
		}

		return true;
	}
}

class CheckoutHeadEntry extends AbstractRefEntry {

J
Joao Moreno 已提交
48
	getDescription(): string { return localize('checkoutBranch', "Branch at {0}", this.ref.commit.substr(0, 8)); }
J
Joao Moreno 已提交
49

J
Joao Moreno 已提交
50 51
	run(mode: Mode, context: IContext): boolean {
		if (mode === Mode.PREVIEW) {
J
Joao Moreno 已提交
52 53 54
			return false;
		}

J
Joao Moreno 已提交
55
		this.gitService.checkout(this.ref.name).done(null, e => this.messageService.show(Severity.Error, e));
J
Joao Moreno 已提交
56 57 58
		return true;
	}
}
E
Erich Gamma 已提交
59

J
Joao Moreno 已提交
60 61
class CheckoutRemoteHeadEntry extends AbstractRefEntry {

J
Joao Moreno 已提交
62
	getDescription(): string { return localize('checkoutRemoteBranch', "Remote branch at {0}", this.ref.commit.substr(0, 8)); }
J
Joao Moreno 已提交
63

J
Joao Moreno 已提交
64 65
	run(mode: Mode, context: IContext): boolean {
		if (mode === Mode.PREVIEW) {
E
Erich Gamma 已提交
66 67 68
			return false;
		}

J
Joao Moreno 已提交
69 70 71
		const match = /^[^/]+\/(.*)$/.exec(this.ref.name);
		const name = match ? match[1] : this.ref.name;

J
Joao Moreno 已提交
72
		this.gitService.checkout(name).done(null, e => this.messageService.show(Severity.Error, e));
E
Erich Gamma 已提交
73 74 75 76 77 78
		return true;
	}
}

class CheckoutTagEntry extends AbstractRefEntry {

J
Joao Moreno 已提交
79
	getDescription(): string { return localize('checkoutTag', "Tag at {0}", this.ref.commit.substr(0, 8)); }
E
Erich Gamma 已提交
80

J
Joao Moreno 已提交
81 82
	run(mode: Mode, context: IContext): boolean {
		if (mode === Mode.PREVIEW) {
E
Erich Gamma 已提交
83 84 85
			return false;
		}

J
Joao Moreno 已提交
86
		this.gitService.checkout(this.ref.name).done(null, e => this.messageService.show(Severity.Error, e));
E
Erich Gamma 已提交
87 88 89 90 91
		return true;
	}
}

class CurrentHeadEntry extends AbstractRefEntry {
J
Joao Moreno 已提交
92
	getDescription(): string { return localize('alreadyCheckedOut', "Branch {0} is already the current branch", this.ref.name); }
E
Erich Gamma 已提交
93 94
}

J
Joao Moreno 已提交
95
class BranchEntry extends QuickOpenEntry {
E
Erich Gamma 已提交
96

J
Joao Moreno 已提交
97
	private gitService: IGitService;
E
Erich Gamma 已提交
98 99 100
	private messageService: IMessageService;
	private name: string;

J
Joao Moreno 已提交
101
	constructor(gitService: IGitService, messageService: IMessageService, name: string) {
E
Erich Gamma 已提交
102 103 104 105
		super([{ start: 0, end: name.length }]);

		this.gitService = gitService;
		this.messageService = messageService;
J
Joao Moreno 已提交
106 107 108

		// sanitize name
		this.name = name.replace(/^\.|\/\.|\.\.|~|\^|:|\/$|\.lock$|\.lock\/|\\|\*|\s|^\s*$|\.$/g, '-');
E
Erich Gamma 已提交
109 110
	}

J
Joao Moreno 已提交
111 112
	getIcon(): string { return 'git'; }
	getLabel(): string { return this.name; }
J
Joao Moreno 已提交
113 114
	getAriaLabel(): string { return localize({ key: 'branchAriaLabel', comment: ['the branch name'] }, "{0}, git branch", this.getLabel()); }
	getDescription(): string { return localize('createBranch', "Create branch {0}", this.name); }
E
Erich Gamma 已提交
115

J
Johannes Rieken 已提交
116
	run(mode: Mode, context: IContext): boolean {
J
Joao Moreno 已提交
117
		if (mode === Mode.PREVIEW) {
E
Erich Gamma 已提交
118 119 120
			return false;
		}

J
Joao Moreno 已提交
121
		this.gitService.branch(this.name, true).done(null, e => this.messageService.show(Severity.Error, e));
E
Erich Gamma 已提交
122 123 124 125 126 127
		return true;
	}
}

// Commands

J
Joao Moreno 已提交
128
class CheckoutCommand implements ICommand {
E
Erich Gamma 已提交
129

J
Joao Moreno 已提交
130 131
	aliases = ['checkout', 'co'];
	icon = 'git';
E
Erich Gamma 已提交
132

J
Joao Moreno 已提交
133
	constructor(private gitService: IGitService, private messageService: IMessageService) {
E
Erich Gamma 已提交
134 135 136
		// noop
	}

J
Joao Moreno 已提交
137
	getResults(input: string): TPromise<QuickOpenEntry[]> {
E
Erich Gamma 已提交
138 139
		input = input.trim();

J
Joao Moreno 已提交
140 141 142 143 144 145
		const gitModel = this.gitService.getModel();
		const currentHead = gitModel.getHEAD();
		const refs = gitModel.getRefs();
		const heads = refs.filter(ref => ref.type === RefType.Head);
		const tags = refs.filter(ref => ref.type === RefType.Tag);
		const remoteHeads = refs.filter(ref => ref.type === RefType.RemoteHead);
E
Erich Gamma 已提交
146

J
Joao Moreno 已提交
147
		const headMatches = heads
J
Joao Moreno 已提交
148
			.map(head => ({ head, highlights: matchesContiguousSubString(input, head.name) }))
E
Erich Gamma 已提交
149 150
			.filter(({ highlights }) => !!highlights);

J
Joao Moreno 已提交
151
		const headEntries: QuickOpenEntry[] = headMatches
E
Erich Gamma 已提交
152 153 154
			.filter(({ head }) => head.name !== currentHead.name)
			.map(({ head, highlights }) => new CheckoutHeadEntry(this.gitService, this.messageService, head, highlights));

J
Joao Moreno 已提交
155
		const tagMatches = tags
J
Joao Moreno 已提交
156
			.map(head => ({ head, highlights: matchesContiguousSubString(input, head.name) }))
E
Erich Gamma 已提交
157 158
			.filter(({ highlights }) => !!highlights);

J
Joao Moreno 已提交
159 160 161
		const tagEntries = tagMatches
			.filter(({ head }) => head.name !== currentHead.name)
			.map(({ head, highlights }) => new CheckoutTagEntry(this.gitService, this.messageService, head, highlights));
E
Erich Gamma 已提交
162

J
Joao Moreno 已提交
163
		const checkoutEntries = headEntries
E
Erich Gamma 已提交
164 165 166
			.concat(tagEntries)
			.sort((a, b) => a.getLabel().localeCompare(b.getLabel()));

J
Joao Moreno 已提交
167
		const remoteHeadMatches = remoteHeads
J
Joao Moreno 已提交
168
			.map(head => ({ head, highlights: matchesContiguousSubString(input, head.name) }))
J
Joao Moreno 已提交
169 170
			.filter(({ highlights }) => !!highlights);

J
Joao Moreno 已提交
171
		const remoteHeadEntries: QuickOpenEntry[] = remoteHeadMatches
J
Joao Moreno 已提交
172 173 174 175 176
			.filter(({ head }) => head.name !== currentHead.name)
			.map(({ head, highlights }) => new CheckoutRemoteHeadEntry(this.gitService, this.messageService, head, highlights))
			.sort((a, b) => a.getLabel().localeCompare(b.getLabel()));

		if (checkoutEntries.length > 0) {
J
Joao Moreno 已提交
177
			checkoutEntries[0] = new QuickOpenEntryGroup(checkoutEntries[0], 'checkout', false);
E
Erich Gamma 已提交
178 179
		}

J
Joao Moreno 已提交
180
		if (remoteHeadEntries.length > 0) {
J
Joao Moreno 已提交
181
			remoteHeadEntries[0] = new QuickOpenEntryGroup(remoteHeadEntries[0], 'checkout remote', checkoutEntries.length > 0);
J
Joao Moreno 已提交
182 183 184 185 186 187 188 189 190
		}

		const entries = checkoutEntries
			.sort((a, b) => a.getLabel().localeCompare(b.getLabel()))
			.concat(remoteHeadEntries);

		const allMatches = headMatches.concat(tagMatches).concat(remoteHeadMatches);
		const exactMatches = allMatches.filter(({ head }) => head.name === input);
		const currentHeadMatches = exactMatches.filter(({ head }) => head.name === currentHead.name);
E
Erich Gamma 已提交
191 192 193 194

		if (currentHeadMatches.length > 0) {
			entries.unshift(new CurrentHeadEntry(this.gitService, this.messageService, currentHeadMatches[0].head, currentHeadMatches[0].highlights));

195
		} else if (exactMatches.length === 0 && input) {
J
Joao Moreno 已提交
196
			const branchEntry = new BranchEntry(this.gitService, this.messageService, input);
J
Joao Moreno 已提交
197
			entries.push(new QuickOpenEntryGroup(branchEntry, 'branch', checkoutEntries.length > 0 || remoteHeadEntries.length > 0));
E
Erich Gamma 已提交
198 199
		}

J
Joao Moreno 已提交
200
		return TPromise.as<QuickOpenEntry[]>(entries);
E
Erich Gamma 已提交
201 202
	}

J
Joao Moreno 已提交
203
	getEmptyLabel(input: string): string {
J
Joao Moreno 已提交
204
		return localize('noBranches', "No other branches");
E
Erich Gamma 已提交
205 206 207
	}
}

J
Joao Moreno 已提交
208
class BranchCommand implements ICommand {
E
Erich Gamma 已提交
209

J
Joao Moreno 已提交
210 211
	aliases = ['branch'];
	icon = 'git';
E
Erich Gamma 已提交
212

J
Joao Moreno 已提交
213
	constructor(private gitService: IGitService, private messageService: IMessageService) {
E
Erich Gamma 已提交
214 215 216
		// noop
	}

J
Joao Moreno 已提交
217
	getResults(input: string): TPromise<QuickOpenEntry[]> {
E
Erich Gamma 已提交
218 219
		input = input.trim();

220
		if (!input) {
J
Joao Moreno 已提交
221
			return TPromise.as([]);
E
Erich Gamma 已提交
222 223
		}

J
Joao Moreno 已提交
224 225
		const gitModel = this.gitService.getModel();
		const currentHead = gitModel.getHEAD();
E
Erich Gamma 已提交
226

J
Joao Moreno 已提交
227
		const matches = gitModel.getRefs()
J
Joao Moreno 已提交
228
			.map(head => ({ head, highlights: matchesContiguousSubString(input, head.name) }))
E
Erich Gamma 已提交
229 230
			.filter(({ highlights }) => !!highlights);

J
Joao Moreno 已提交
231 232
		const exactMatches = matches.filter(({ head }) => head.name === input);
		const headMatches = exactMatches.filter(({ head }) => head.name === currentHead.name);
E
Erich Gamma 已提交
233 234

		if (headMatches.length > 0) {
J
Joao Moreno 已提交
235
			return TPromise.as([new CurrentHeadEntry(this.gitService, this.messageService, headMatches[0].head, headMatches[0].highlights)]);
E
Erich Gamma 已提交
236
		} else if (exactMatches.length > 0) {
J
Joao Moreno 已提交
237
			return TPromise.as([new CheckoutHeadEntry(this.gitService, this.messageService, exactMatches[0].head, exactMatches[0].highlights)]);
E
Erich Gamma 已提交
238 239
		}

J
Joao Moreno 已提交
240
		const branchEntry = new BranchEntry(this.gitService, this.messageService, input);
J
Joao Moreno 已提交
241
		return TPromise.as([new QuickOpenEntryGroup(branchEntry, 'branch', false)]);
E
Erich Gamma 已提交
242 243
	}

J
Joao Moreno 已提交
244
	getEmptyLabel(input: string): string {
J
Joao Moreno 已提交
245
		return localize('notValidBranchName', "Please provide a valid branch name");
E
Erich Gamma 已提交
246 247 248
	}
}

J
Joao Moreno 已提交
249
export class GitCommandQuickOpenHandler extends CommandQuickOpenHandler {
E
Erich Gamma 已提交
250

J
Johannes Rieken 已提交
251
	constructor( @IQuickOpenService quickOpenService: IQuickOpenService, @IGitService gitService: IGitService, @IMessageService messageService: IMessageService) {
E
Erich Gamma 已提交
252 253 254 255 256 257 258 259 260
		super(quickOpenService, {
			prefix: 'git',
			commands: [
				new CheckoutCommand(gitService, messageService),
				new BranchCommand(gitService, messageService)
			]
		});
	}
}