referencesWidget.ts 28.8 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';

7
import 'vs/css!./referencesWidget';
A
Alex Dima 已提交
8
import * as nls from 'vs/nls';
J
Johannes Rieken 已提交
9 10 11
import { onUnexpectedError } from 'vs/base/common/errors';
import { getPathLabel } from 'vs/base/common/labels';
import Event, { Emitter } from 'vs/base/common/event';
J
Johannes Rieken 已提交
12
import { IDisposable, dispose, Disposables, IReference } from 'vs/base/common/lifecycle';
J
Johannes Rieken 已提交
13
import { Schemas } from 'vs/base/common/network';
A
Alex Dima 已提交
14
import * as strings from 'vs/base/common/strings';
J
Johannes Rieken 已提交
15
import { TPromise } from 'vs/base/common/winjs.base';
16
import { Color } from "vs/base/common/color";
J
Johannes Rieken 已提交
17
import { $, Builder } from 'vs/base/browser/builder';
A
Alex Dima 已提交
18
import * as dom from 'vs/base/browser/dom';
J
Johannes Rieken 已提交
19 20 21 22 23 24 25
import { Sash, ISashEvent, IVerticalSashLayoutProvider } from 'vs/base/browser/ui/sash/sash';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
import { GestureEvent } from 'vs/base/browser/touch';
import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge';
import { FileLabel } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { LeftRightWidget } from 'vs/base/browser/ui/leftRightWidget/leftRightWidget';
A
Alex Dima 已提交
26
import * as tree from 'vs/base/parts/tree/browser/tree';
J
Johannes Rieken 已提交
27 28 29 30 31 32 33
import { DefaultController, LegacyRenderer } from 'vs/base/parts/tree/browser/treeDefaults';
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { DefaultConfig } from 'vs/editor/common/config/defaultConfig';
import { Range } from 'vs/editor/common/core/range';
A
Alex Dima 已提交
34
import * as editorCommon from 'vs/editor/common/editorCommon';
J
Johannes Rieken 已提交
35 36 37 38 39
import { Model } from 'vs/editor/common/model/model';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget';
import { PeekViewWidget, IPeekViewService } from 'vs/editor/contrib/zoneWidget/browser/peekViewWidget';
import { FileReferences, OneReference, ReferencesModel } from './referencesModel';
J
Johannes Rieken 已提交
40
import { ITextModelResolverService, ITextEditorModel } from 'vs/editor/common/services/resolverService';
41
import { registerColor, highContrastOutline } from 'vs/platform/theme/common/colorRegistry';
42
import { registerThemingParticipant, ITheme, IThemeService } from 'vs/platform/theme/common/themeService';
43

A
Alex Dima 已提交
44
class DecorationsManager implements IDisposable {
E
Erich Gamma 已提交
45

J
Johannes Rieken 已提交
46
	private static DecorationOptions: editorCommon.IModelDecorationOptions = {
A
Alex Dima 已提交
47
		stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
E
Erich Gamma 已提交
48 49 50
		className: 'reference-decoration'
	};

51 52
	private _decorations = new Map<string, OneReference>();
	private _decorationIgnoreSet = new Set<string>();
J
Johannes Rieken 已提交
53 54
	private _callOnDispose: IDisposable[] = [];
	private _callOnModelChange: IDisposable[] = [];
E
Erich Gamma 已提交
55

56 57
	constructor(private _editor: ICodeEditor, private _model: ReferencesModel) {
		this._callOnDispose.push(this._editor.onDidChangeModel(() => this._onModelChanged()));
58
		this._onModelChanged();
E
Erich Gamma 已提交
59 60 61
	}

	public dispose(): void {
A
Alex Dima 已提交
62 63
		this._callOnModelChange = dispose(this._callOnModelChange);
		this._callOnDispose = dispose(this._callOnDispose);
E
Erich Gamma 已提交
64 65 66
		this.removeDecorations();
	}

J
Johannes Rieken 已提交
67
	private _onModelChanged(): void {
A
Alex Dima 已提交
68
		this._callOnModelChange = dispose(this._callOnModelChange);
69 70 71 72 73 74 75
		const model = this._editor.getModel();
		if (model) {
			for (const ref of this._model.groups) {
				if (ref.uri.toString() === model.uri.toString()) {
					this._addDecorations(ref);
					return;
				}
E
Erich Gamma 已提交
76 77 78 79
			}
		}
	}

J
Johannes Rieken 已提交
80
	private _addDecorations(reference: FileReferences): void {
81
		this._callOnModelChange.push(this._editor.getModel().onDidChangeDecorations((event) => this._onDecorationChanged(event)));
E
Erich Gamma 已提交
82

83
		this._editor.changeDecorations(accessor => {
84

85 86
			const newDecorations: editorCommon.IModelDeltaDecoration[] = [];
			const newDecorationsActualIndex: number[] = [];
E
Erich Gamma 已提交
87

J
Johannes Rieken 已提交
88
			for (let i = 0, len = reference.children.length; i < len; i++) {
E
Erich Gamma 已提交
89
				let oneReference = reference.children[i];
90
				if (this._decorationIgnoreSet.has(oneReference.id)) {
E
Erich Gamma 已提交
91 92 93 94 95 96 97 98 99
					continue;
				}
				newDecorations.push({
					range: oneReference.range,
					options: DecorationsManager.DecorationOptions
				});
				newDecorationsActualIndex.push(i);
			}

100 101
			const decorations = accessor.deltaDecorations([], newDecorations);
			for (let i = 0; i < decorations.length; i++) {
102
				this._decorations.set(decorations[i], reference.children[newDecorationsActualIndex[i]]);
E
Erich Gamma 已提交
103 104 105 106
			}
		});
	}

J
Johannes Rieken 已提交
107
	private _onDecorationChanged(event: editorCommon.IModelDecorationsChangedEvent): void {
108
		const changedDecorations = event.changedDecorations,
J
Johannes Rieken 已提交
109
			toRemove: string[] = [];
E
Erich Gamma 已提交
110

111
		for (let i = 0, len = changedDecorations.length; i < len; i++) {
112
			let reference = this._decorations.get(changedDecorations[i]);
J
Johannes Rieken 已提交
113
			if (!reference) {
E
Erich Gamma 已提交
114 115 116
				continue;
			}

117 118
			const newRange = this._editor.getModel().getDecorationRange(changedDecorations[i]);
			let ignore = false;
E
Erich Gamma 已提交
119

J
Johannes Rieken 已提交
120
			if (Range.equalsRange(newRange, reference.range)) {
E
Erich Gamma 已提交
121 122
				continue;

J
Johannes Rieken 已提交
123
			} else if (Range.spansMultipleLines(newRange)) {
E
Erich Gamma 已提交
124 125 126
				ignore = true;

			} else {
127 128
				const lineLength = reference.range.endColumn - reference.range.startColumn;
				const newLineLength = newRange.endColumn - newRange.startColumn;
E
Erich Gamma 已提交
129

J
Johannes Rieken 已提交
130
				if (lineLength !== newLineLength) {
E
Erich Gamma 已提交
131 132 133 134
					ignore = true;
				}
			}

J
Johannes Rieken 已提交
135
			if (ignore) {
136
				this._decorationIgnoreSet.add(reference.id);
137
				toRemove.push(changedDecorations[i]);
E
Erich Gamma 已提交
138 139 140 141 142
			} else {
				reference.range = newRange;
			}
		}

143
		this._editor.changeDecorations((accessor) => {
E
Erich Gamma 已提交
144
			for (let i = 0, len = toRemove.length; i < len; i++) {
K
katainaka0503 已提交
145
				this._decorations.delete(toRemove[i]);
E
Erich Gamma 已提交
146 147 148 149 150
			}
			accessor.deltaDecorations(toRemove, []);
		});
	}

J
Johannes Rieken 已提交
151
	public removeDecorations(): void {
152
		this._editor.changeDecorations(accessor => {
153 154
			this._decorations.forEach((value, key) => {
				accessor.removeDecoration(key);
E
Erich Gamma 已提交
155
			});
156 157
			this._decorations.clear();
		});
E
Erich Gamma 已提交
158 159 160 161 162
	}
}

class DataSource implements tree.IDataSource {

163
	constructor(
164
		@ITextModelResolverService private _textModelResolverService: ITextModelResolverService
165 166 167 168
	) {
		//
	}

169 170
	public getId(tree: tree.ITree, element: any): string {
		if (element instanceof ReferencesModel) {
E
Erich Gamma 已提交
171
			return 'root';
172 173 174 175
		} else if (element instanceof FileReferences) {
			return (<FileReferences>element).id;
		} else if (element instanceof OneReference) {
			return (<OneReference>element).id;
E
Erich Gamma 已提交
176
		}
M
Matt Bierner 已提交
177
		return undefined;
E
Erich Gamma 已提交
178 179
	}

180 181 182 183 184 185 186
	public hasChildren(tree: tree.ITree, element: any): boolean {
		if (element instanceof ReferencesModel) {
			return true;
		}
		if (element instanceof FileReferences && !(<FileReferences>element).failure) {
			return true;
		}
M
Matt Bierner 已提交
187
		return false;
E
Erich Gamma 已提交
188 189
	}

190 191 192 193
	public getChildren(tree: tree.ITree, element: ReferencesModel | FileReferences): TPromise<any[]> {
		if (element instanceof ReferencesModel) {
			return TPromise.as(element.groups);
		} else if (element instanceof FileReferences) {
194
			return element.resolve(this._textModelResolverService).then(val => {
195 196 197 198 199 200 201
				if (element.failure) {
					// refresh the element on failure so that
					// we can update its rendering
					return tree.refresh(element).then(() => val.children);
				}
				return val.children;
			});
E
Erich Gamma 已提交
202 203 204 205 206
		} else {
			return TPromise.as([]);
		}
	}

207 208 209 210
	public getParent(tree: tree.ITree, element: any): TPromise<any> {
		var result: any = null;
		if (element instanceof FileReferences) {
			result = (<FileReferences>element).parent;
A
Alex Dima 已提交
211
		} else if (element instanceof OneReference) {
212
			result = (<OneReference>element).parent;
E
Erich Gamma 已提交
213 214 215 216 217
		}
		return TPromise.as(result);
	}
}

A
Alex Dima 已提交
218
class Controller extends DefaultController {
E
Erich Gamma 已提交
219 220 221 222 223 224 225

	static Events = {
		FOCUSED: 'events/custom/focused',
		SELECTED: 'events/custom/selected',
		OPEN_TO_SIDE: 'events/custom/opentoside'
	};

J
Johannes Rieken 已提交
226
	public onTap(tree: tree.ITree, element: any, event: GestureEvent): boolean {
227 228 229 230 231 232 233 234 235 236 237
		if (element instanceof FileReferences) {
			event.preventDefault();
			event.stopPropagation();
			return this._expandCollapse(tree, element);
		}

		var result = super.onTap(tree, element, event);
		tree.emit(Controller.Events.FOCUSED, element);
		return result;
	}

J
Johannes Rieken 已提交
238
	public onMouseDown(tree: tree.ITree, element: any, event: IMouseEvent): boolean {
E
Erich Gamma 已提交
239
		if (event.leftButton) {
A
Alex Dima 已提交
240
			if (element instanceof FileReferences) {
E
Erich Gamma 已提交
241 242
				event.preventDefault();
				event.stopPropagation();
243
				return this._expandCollapse(tree, element);
E
Erich Gamma 已提交
244 245 246 247 248
			}

			var result = super.onClick(tree, element, event);
			if (event.ctrlKey || event.metaKey) {
				tree.emit(Controller.Events.OPEN_TO_SIDE, element);
J
Johannes Rieken 已提交
249
			} else if (event.detail === 2) {
E
Erich Gamma 已提交
250 251 252 253 254 255 256 257 258 259
				tree.emit(Controller.Events.SELECTED, element);
			} else {
				tree.emit(Controller.Events.FOCUSED, element);
			}
			return result;
		}

		return false;
	}

J
Johannes Rieken 已提交
260
	public onClick(tree: tree.ITree, element: any, event: IMouseEvent): boolean {
E
Erich Gamma 已提交
261 262 263 264 265 266 267
		if (event.leftButton) {
			return false; // Already handled by onMouseDown
		}

		return super.onClick(tree, element, event);
	}

J
Johannes Rieken 已提交
268
	private _expandCollapse(tree: tree.ITree, element: any): boolean {
E
Erich Gamma 已提交
269 270

		if (tree.isExpanded(element)) {
A
Alex Dima 已提交
271
			tree.collapse(element).done(null, onUnexpectedError);
E
Erich Gamma 已提交
272
		} else {
A
Alex Dima 已提交
273
			tree.expand(element).done(null, onUnexpectedError);
E
Erich Gamma 已提交
274 275 276 277
		}
		return true;
	}

J
Johannes Rieken 已提交
278
	public onEscape(tree: tree.ITree, event: IKeyboardEvent): boolean {
E
Erich Gamma 已提交
279 280 281
		return false;
	}

J
Johannes Rieken 已提交
282
	public onEnter(tree: tree.ITree, event: IKeyboardEvent): boolean {
E
Erich Gamma 已提交
283
		var element = tree.getFocus();
A
Alex Dima 已提交
284
		if (element instanceof FileReferences) {
285
			return this._expandCollapse(tree, element);
E
Erich Gamma 已提交
286 287 288 289 290 291 292 293 294 295 296
		}

		var result = super.onEnter(tree, event);
		if (event.ctrlKey || event.metaKey) {
			tree.emit(Controller.Events.OPEN_TO_SIDE, element);
		} else {
			tree.emit(Controller.Events.SELECTED, element);
		}
		return result;
	}

J
Johannes Rieken 已提交
297
	public onUp(tree: tree.ITree, event: IKeyboardEvent): boolean {
E
Erich Gamma 已提交
298
		super.onUp(tree, event);
299
		this._fakeFocus(tree, event);
E
Erich Gamma 已提交
300 301 302
		return true;
	}

J
Johannes Rieken 已提交
303
	public onPageUp(tree: tree.ITree, event: IKeyboardEvent): boolean {
E
Erich Gamma 已提交
304
		super.onPageUp(tree, event);
305
		this._fakeFocus(tree, event);
E
Erich Gamma 已提交
306 307 308
		return true;
	}

J
Johannes Rieken 已提交
309
	public onLeft(tree: tree.ITree, event: IKeyboardEvent): boolean {
E
Erich Gamma 已提交
310
		super.onLeft(tree, event);
311
		this._fakeFocus(tree, event);
E
Erich Gamma 已提交
312 313 314
		return true;
	}

J
Johannes Rieken 已提交
315
	public onDown(tree: tree.ITree, event: IKeyboardEvent): boolean {
E
Erich Gamma 已提交
316
		super.onDown(tree, event);
317
		this._fakeFocus(tree, event);
E
Erich Gamma 已提交
318 319 320
		return true;
	}

J
Johannes Rieken 已提交
321
	public onPageDown(tree: tree.ITree, event: IKeyboardEvent): boolean {
E
Erich Gamma 已提交
322
		super.onPageDown(tree, event);
323
		this._fakeFocus(tree, event);
E
Erich Gamma 已提交
324 325 326
		return true;
	}

J
Johannes Rieken 已提交
327
	public onRight(tree: tree.ITree, event: IKeyboardEvent): boolean {
E
Erich Gamma 已提交
328
		super.onRight(tree, event);
329
		this._fakeFocus(tree, event);
E
Erich Gamma 已提交
330 331 332
		return true;
	}

J
Johannes Rieken 已提交
333
	private _fakeFocus(tree: tree.ITree, event: IKeyboardEvent): void {
E
Erich Gamma 已提交
334 335 336 337 338 339 340 341
		// focus next item
		var focus = tree.getFocus();
		tree.setSelection([focus]);
		// send out event
		tree.emit(Controller.Events.FOCUSED, focus);
	}
}

A
Alex Dima 已提交
342
class Renderer extends LegacyRenderer {
J
Johannes Rieken 已提交
343
	private _contextService: IWorkspaceContextService;
E
Erich Gamma 已提交
344

345
	constructor( @IWorkspaceContextService contextService: IWorkspaceContextService) {
E
Erich Gamma 已提交
346 347 348 349
		super();
		this._contextService = contextService;
	}

350 351
	public getHeight(tree: tree.ITree, element: any): number {
		return 22;
E
Erich Gamma 已提交
352 353
	}

354
	protected render(tree: tree.ITree, element: FileReferences | OneReference, container: HTMLElement): tree.IElementCallback {
E
Erich Gamma 已提交
355 356 357

		dom.clearNode(container);

358 359
		if (element instanceof FileReferences) {
			const fileReferencesContainer = $('.reference-file');
E
Erich Gamma 已提交
360

A
tslint  
Alex Dima 已提交
361
			/* tslint:disable:no-unused-expression */
A
Alex Dima 已提交
362
			new LeftRightWidget(fileReferencesContainer, (left: HTMLElement) => {
E
Erich Gamma 已提交
363

364 365
				new FileLabel(left, element.uri, this._contextService);
				return <IDisposable>null;
E
Erich Gamma 已提交
366 367

			}, (right: HTMLElement) => {
368 369 370 371 372 373 374 375 376 377 378 379

				const len = element.children.length;
				const badge = new CountBadge(right, len);

				if (element.failure) {
					badge.setTitleFormat(nls.localize('referencesFailre', "Failed to resolve file."));
				} else if (len > 1) {
					badge.setTitleFormat(nls.localize('referencesCount', "{0} references", len));
				} else {
					badge.setTitleFormat(nls.localize('referenceCount', "{0} reference", len));
				}

J
Joao Moreno 已提交
380
				return null;
E
Erich Gamma 已提交
381
			});
A
tslint  
Alex Dima 已提交
382
			/* tslint:enable:no-unused-expression */
E
Erich Gamma 已提交
383 384 385

			fileReferencesContainer.appendTo(container);

386
		} else if (element instanceof OneReference) {
E
Erich Gamma 已提交
387

388
			const preview = element.parent.preview.preview(element.range);
E
Erich Gamma 已提交
389

J
Joao Moreno 已提交
390
			if (!preview) {
M
Matt Bierner 已提交
391
				return undefined;
J
Joao Moreno 已提交
392 393
			}

394
			$('.reference').innerHtml(
395 396 397 398 399
				strings.format(
					'<span>{0}</span><span class="referenceMatch">{1}</span><span>{2}</span>',
					strings.escape(preview.before),
					strings.escape(preview.inside),
					strings.escape(preview.after))).appendTo(container);
E
Erich Gamma 已提交
400 401 402 403 404 405 406
		}

		return null;
	}

}

407 408 409 410 411 412 413 414 415
class VSash {

	private _disposables = new Disposables();
	private _sash: Sash;
	private _ratio: number;
	private _height: number;
	private _width: number;
	private _onDidChangePercentages = new Emitter<VSash>();

J
Johannes Rieken 已提交
416
	constructor(container: HTMLElement, ratio: number) {
417 418 419 420 421 422
		this._ratio = ratio;
		this._sash = new Sash(container, <IVerticalSashLayoutProvider>{
			getVerticalSashLeft: () => this._width * this._ratio,
			getVerticalSashHeight: () => this._height
		});

423 424 425
		// compute the current widget clientX postion since
		// the sash works with clientX when dragging
		let clientX: number;
426
		this._disposables.add(this._sash.addListener2('start', (e: ISashEvent) => {
427
			clientX = e.startX - (this._width * this.ratio);
428 429 430
		}));

		this._disposables.add(this._sash.addListener2('change', (e: ISashEvent) => {
431 432 433 434 435
			// compute the new position of the sash and from that
			// compute the new ratio that we are using
			let newLeft = e.currentX - clientX;
			if (newLeft > 20 && newLeft + 20 < this._width) {
				this._ratio = newLeft / this._width;
436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466
				this._sash.layout();
				this._onDidChangePercentages.fire(this);
			}
		}));
	}

	dispose() {
		this._sash.dispose();
		this._onDidChangePercentages.dispose();
		this._disposables.dispose();
	}

	get onDidChangePercentages() {
		return this._onDidChangePercentages.event;
	}

	set width(value: number) {
		this._width = value;
		this._sash.layout();
	}

	set height(value: number) {
		this._height = value;
		this._sash.layout();
	}

	get percentages() {
		let left = 100 * this._ratio;
		let right = 100 - left;
		return [`${left}%`, `${right}%`];
	}
467 468 469 470 471 472 473 474 475

	get ratio() {
		return this._ratio;
	}
}

export interface LayoutData {
	ratio: number;
	heightInLines: number;
476 477
}

478 479
export interface SelectionEvent {
	kind: 'goto' | 'show' | 'side' | 'open';
480
	source: 'editor' | 'tree' | 'title';
481 482 483
	element: OneReference;
}

E
Erich Gamma 已提交
484 485 486
/**
 * ZoneWidget that is shown inside the editor
 */
A
Alex Dima 已提交
487
export class ReferenceWidget extends PeekViewWidget {
E
Erich Gamma 已提交
488

489
	private _model: ReferencesModel;
490 491 492
	private _decorationsManager: DecorationsManager;

	private _disposeOnNewModel: IDisposable[] = [];
493
	private _callOnDispose: IDisposable[] = [];
494
	private _onDidSelectReference = new Emitter<SelectionEvent>();
495 496 497

	private _tree: Tree;
	private _treeContainer: Builder;
498
	private _sash: VSash;
499
	private _preview: ICodeEditor;
J
Johannes Rieken 已提交
500
	private _previewModelReference: IReference<ITextEditorModel>;
501 502 503 504 505
	private _previewNotAvailableMessage: Model;
	private _previewContainer: Builder;
	private _messageContainer: Builder;

	constructor(
506 507
		editor: ICodeEditor,
		public layoutData: LayoutData,
508
		private _textModelResolverService: ITextModelResolverService,
509
		private _contextService: IWorkspaceContextService,
510
		private _themeService: IThemeService,
511
		private _instantiationService: IInstantiationService
512
	) {
513
		super(editor, { showFrame: false, showArrow: true, isResizeable: true });
E
Erich Gamma 已提交
514

515
		this._applyTheme(_themeService.getTheme());
516
		this._callOnDispose.push(_themeService.onThemeChange(this._applyTheme.bind(this)));
517

518
		this._instantiationService = this._instantiationService.createChild(new ServiceCollection([IPeekViewService, this]));
E
Erich Gamma 已提交
519 520 521
		this.create();
	}

522 523 524 525 526 527 528 529 530 531 532
	private _applyTheme(theme: ITheme) {
		let borderColor = theme.getColor(editorPeekBorders) || Color.transparent;
		this.style({
			arrowColor: borderColor,
			frameColor: borderColor,
			headerBackgroundColor: theme.getColor(editorPeekTitleBackground) || Color.transparent,
			primaryHeadingColor: theme.getColor(editorPeekTitle),
			secondaryHeadingColor: theme.getColor(editorPeekTitleInfo)
		});
	}

533 534
	public dispose(): void {
		this.setModel(null);
535
		this._callOnDispose = dispose(this._callOnDispose);
J
Johannes Rieken 已提交
536
		dispose<IDisposable>(this._preview, this._previewNotAvailableMessage, this._tree, this._sash, this._previewModelReference);
537 538 539
		super.dispose();
	}

540 541
	get onDidSelectReference(): Event<SelectionEvent> {
		return this._onDidSelectReference.event;
542 543
	}

544
	show(where: editorCommon.IRange) {
545
		this.editor.revealRangeInCenterIfOutsideViewport(where);
546 547 548
		super.show(where, this.layoutData.heightInLines || 18);
	}

549 550 551 552
	focus(): void {
		this._tree.DOMFocus();
	}

553
	protected _onTitleClick(e: MouseEvent): void {
554 555 556
		if (this._preview && this._preview.getModel()) {
			this._onDidSelectReference.fire({
				element: this._getFocusedReference(),
557 558
				kind: e.ctrlKey || e.metaKey ? 'side' : 'open',
				source: 'title'
559
			});
E
Erich Gamma 已提交
560 561 562
		}
	}

563
	protected _fillBody(containerElement: HTMLElement): void {
A
Alex Dima 已提交
564
		var container = $(containerElement);
E
Erich Gamma 已提交
565

566
		this.setCssClass('reference-zone-widget');
E
Erich Gamma 已提交
567 568 569

		// message pane
		container.div({ 'class': 'messages' }, div => {
570
			this._messageContainer = div.hide();
E
Erich Gamma 已提交
571 572 573
		});

		// editor
574
		container.div({ 'class': 'preview inline' }, (div: Builder) => {
E
Erich Gamma 已提交
575

576
			var options: editorCommon.IEditorOptions = {
E
Erich Gamma 已提交
577 578
				scrollBeyondLastLine: false,
				scrollbar: DefaultConfig.editor.scrollbar,
J
Joao Moreno 已提交
579
				overviewRulerLanes: 2,
580 581 582 583
				fixedOverflowWidgets: true,
				minimap: {
					enabled: false
				}
E
Erich Gamma 已提交
584 585
			};

586 587
			this._preview = this._instantiationService.createInstance(EmbeddedCodeEditorWidget, div.getHTMLElement(), options, this.editor);
			this._previewContainer = div.hide();
588
			this._previewNotAvailableMessage = Model.createFromString(nls.localize('missingPreviewMessage', "no preview available"));
E
Erich Gamma 已提交
589 590
		});

591
		// sash
592
		this._sash = new VSash(containerElement, this.layoutData.ratio || .8);
593 594
		this._sash.onDidChangePercentages(() => {
			let [left, right] = this._sash.percentages;
J
Johannes Rieken 已提交
595
			this._previewContainer.style({ width: left });
596 597 598
			this._treeContainer.style({ width: right });
			this._preview.layout();
			this._tree.layout();
599
			this.layoutData.ratio = this._sash.ratio;
600 601
		});

E
Erich Gamma 已提交
602
		// tree
603
		container.div({ 'class': 'ref-tree inline' }, (div: Builder) => {
E
Erich Gamma 已提交
604
			var config = {
605
				dataSource: this._instantiationService.createInstance(DataSource),
606
				renderer: this._instantiationService.createInstance(Renderer),
E
Erich Gamma 已提交
607 608 609 610 611 612
				//sorter: new Sorter(),
				controller: new Controller()
			};

			var options = {
				allowHorizontalScroll: false,
B
Benjamin Pasero 已提交
613 614
				twistiePixels: 20,
				ariaLabel: nls.localize('treeAriaLabel', "References")
E
Erich Gamma 已提交
615
			};
616
			this._tree = new Tree(div.getHTMLElement(), config, options);
E
Erich Gamma 已提交
617

618
			this._treeContainer = div.hide();
E
Erich Gamma 已提交
619 620 621
		});
	}

622 623
	protected _doLayoutBody(heightInPixel: number, widthInPixel: number): void {
		super._doLayoutBody(heightInPixel, widthInPixel);
E
Erich Gamma 已提交
624

625 626 627 628 629 630
		const height = heightInPixel + 'px';
		this._sash.height = heightInPixel;
		this._sash.width = widthInPixel;

		// set height/width
		const [left, right] = this._sash.percentages;
631
		this._previewContainer.style({ height, width: left });
632
		this._treeContainer.style({ height, width: right });
E
Erich Gamma 已提交
633 634

		// forward
635 636
		this._tree.layout(heightInPixel);
		this._preview.layout();
E
Erich Gamma 已提交
637

638 639 640 641 642
		// store layout data
		this.layoutData = {
			heightInLines: this._viewZone.heightInLines,
			ratio: this._sash.ratio
		};
E
Erich Gamma 已提交
643 644
	}

J
Johannes Rieken 已提交
645
	public _onWidth(widthInPixel: number): void {
646
		this._sash.width = widthInPixel;
647
		this._preview.layout();
E
Erich Gamma 已提交
648 649
	}

650 651
	public setSelection(selection: OneReference): TPromise<any> {
		return this._revealReference(selection);
652 653
	}

654
	public setModel(newModel: ReferencesModel): TPromise<any> {
E
Erich Gamma 已提交
655
		// clean up
656
		this._disposeOnNewModel = dispose(this._disposeOnNewModel);
657 658
		this._model = newModel;
		if (this._model) {
659
			return this._onNewModel();
E
Erich Gamma 已提交
660
		}
M
Matt Bierner 已提交
661
		return undefined;
E
Erich Gamma 已提交
662 663
	}

664
	private _onNewModel(): TPromise<any> {
E
Erich Gamma 已提交
665

666 667 668
		if (this._model.empty) {
			this.setTitle('');
			this._messageContainer.innerHtml(nls.localize('noResults', "No results")).show();
669
			return TPromise.as(void 0);
670
		}
E
Erich Gamma 已提交
671

672
		this._messageContainer.hide();
673
		this._decorationsManager = new DecorationsManager(this._preview, this._model);
674
		this._disposeOnNewModel.push(this._decorationsManager);
E
Erich Gamma 已提交
675 676

		// listen on model changes
677
		this._disposeOnNewModel.push(this._model.onDidChangeReferenceRange(reference => this._tree.refresh(reference)));
E
Erich Gamma 已提交
678 679

		// listen on selection and focus
680
		this._disposeOnNewModel.push(this._tree.addListener2(Controller.Events.FOCUSED, (element) => {
A
Alex Dima 已提交
681
			if (element instanceof OneReference) {
682
				this._revealReference(element);
683
				this._onDidSelectReference.fire({ element, kind: 'show', source: 'tree' });
E
Erich Gamma 已提交
684 685
			}
		}));
686
		this._disposeOnNewModel.push(this._tree.addListener2(Controller.Events.SELECTED, (element: any) => {
A
Alex Dima 已提交
687
			if (element instanceof OneReference) {
688
				this._onDidSelectReference.fire({ element, kind: 'goto', source: 'tree' });
E
Erich Gamma 已提交
689 690
			}
		}));
691
		this._disposeOnNewModel.push(this._tree.addListener2(Controller.Events.OPEN_TO_SIDE, (element: any) => {
A
Alex Dima 已提交
692
			if (element instanceof OneReference) {
693
				this._onDidSelectReference.fire({ element, kind: 'side', source: 'tree' });
E
Erich Gamma 已提交
694 695 696 697
			}
		}));

		// listen on editor
A
Alex Dima 已提交
698
		this._disposeOnNewModel.push(this._preview.onMouseDown((e) => {
699
			if (e.event.detail === 2) {
700 701
				this._onDidSelectReference.fire({
					element: this._getFocusedReference(),
702 703
					kind: (e.event.ctrlKey || e.event.metaKey) ? 'side' : 'open',
					source: 'editor'
704
				});
E
Erich Gamma 已提交
705 706 707 708 709
			}
		}));

		// make sure things are rendered
		dom.addClass(this.container, 'results-loaded');
710 711 712 713
		this._treeContainer.show();
		this._previewContainer.show();
		this._preview.layout();
		this._tree.layout();
E
Erich Gamma 已提交
714 715
		this.focus();

716
		// pick input and a reference to begin with
717 718
		const input = this._model.groups.length === 1 ? this._model.groups[0] : this._model;
		return this._tree.setInput(input);
E
Erich Gamma 已提交
719 720
	}

721 722
	private _getFocusedReference(): OneReference {
		const element = this._tree.getFocus();
723
		if (element instanceof OneReference) {
724
			return element;
725
		} else if (element instanceof FileReferences) {
726 727
			if (element.children.length > 0) {
				return element.children[0];
E
Erich Gamma 已提交
728 729
			}
		}
M
Matt Bierner 已提交
730
		return undefined;
E
Erich Gamma 已提交
731 732
	}

733
	private _revealReference(reference: OneReference) {
E
Erich Gamma 已提交
734

735
		// Update widget header
736
		if (reference.uri.scheme !== Schemas.inMemory) {
737 738 739 740
			this.setTitle(reference.name, getPathLabel(reference.directory, this._contextService));
		} else {
			this.setTitle(nls.localize('peekView.alternateTitle', "References"));
		}
E
Erich Gamma 已提交
741

J
Joao Moreno 已提交
742
		const promise = this._textModelResolverService.createModelReference(reference.uri);
J
Joao Moreno 已提交
743 744 745

		return TPromise.join([promise, this._tree.reveal(reference)]).then(values => {
			const ref = values[0];
746

747
			if (!this._model) {
J
Joao Moreno 已提交
748
				ref.dispose();
749 750 751
				// disposed
				return;
			}
E
Erich Gamma 已提交
752

J
Johannes Rieken 已提交
753
			dispose(this._previewModelReference);
J
Joao Moreno 已提交
754

755
			// show in editor
J
Joao Moreno 已提交
756
			const model = ref.object;
757
			if (model) {
J
Joao Moreno 已提交
758
				this._previewModelReference = ref;
759
				this._preview.setModel(model.textEditorModel);
E
Erich Gamma 已提交
760
				var sel = Range.lift(reference.range).collapseToStart();
761 762
				this._preview.setSelection(sel);
				this._preview.revealRangeInCenter(sel);
E
Erich Gamma 已提交
763
			} else {
764
				this._preview.setModel(this._previewNotAvailableMessage);
J
Joao Moreno 已提交
765
				ref.dispose();
E
Erich Gamma 已提交
766 767
			}

768
			// show in tree
769 770
			this._tree.setSelection([reference]);
			this._tree.setFocus(reference);
771 772

		}, onUnexpectedError);
E
Erich Gamma 已提交
773
	}
774
}
775

M
Martin Aeschlimann 已提交
776 777
// theming

778 779 780
export const editorPeekTitleBackground = registerColor('editorPeekTitleBackground', { dark: '#1E1E1E', light: '#FFFFFF', hc: '#0C141F' }, nls.localize('editorPeekTitleBackground', 'Editor peek view title area background'));
export const editorPeekTitle = registerColor('editorPeekTitle', { dark: '#FFFFFF', light: '#333333', hc: '#FFFFFF' }, nls.localize('editorPeekTitle', 'Editor peek view title color'));
export const editorPeekTitleInfo = registerColor('editorPeekTitleInfo', { dark: '#ccccccb3', light: '#6c6c6cb3', hc: '#FFFFFF99' }, nls.localize('editorPeekTitleInfo', 'Editor peek view title info color'));
781
export const editorPeekBorders = registerColor('editorPeekBorder', { dark: '#007acc', light: '#007acc', hc: '#6FC3DF' }, nls.localize('editorPeekBorder', 'Editor peek view borders'));
M
Martin Aeschlimann 已提交
782

783 784 785 786 787 788
export const editorPeekResultsBackground = registerColor('editorPeekResultsBackground', { dark: '#252526', light: '#F3F3F3', hc: Color.black }, nls.localize('editorPeekResultsBackground', 'List background in the editor peek view'));
export const editorPeekResultsMatchForeground = registerColor('editorPeekResultsMatchForeground', { dark: '#bbbbbb', light: '#646465', hc: Color.white }, nls.localize('editorPeekResultsMatchForeground', 'Match entry foreground in the editor peek view'));
export const editorPeekResultsFileForeground = registerColor('editorPeekResultsFileForeground', { dark: Color.white, light: '#1E1E1E', hc: Color.white }, nls.localize('editorPeekResultsFileForeground', 'File entry foreground in the editor peek view'));
export const editorPeekResultsSelectedBackground = registerColor('editorPeekResultsSelectedBackground', { dark: '#3399ff33', light: '#3399ff33', hc: null }, nls.localize('editorPeekResultsSelectedBackground', 'Selected entry background in the editor peek view'));
export const editorPeekResultsSelectedForeground = registerColor('editorPeekResultsSelectedForeground', { dark: Color.white, light: '#6C6C6C', hc: Color.white }, nls.localize('editorPeekResultsSelectedForeground', 'Selected entry foreground in the editor peek view'));
export const editorPeekEditorBackground = registerColor('editorPeekEditorBackground', { dark: '#001F33', light: '#F2F8FC', hc: '#0C141F' }, nls.localize('editorPeekEditorBackground', 'Editor background in the editor peek view'));
M
Martin Aeschlimann 已提交
789

790 791
export const editorPeekFindMatchHighlight = registerColor('editorPeekFindMatchHighlight', { dark: '#ea5c004d', light: '#ea5c004d', hc: null }, nls.localize('editorPeekFindMatchHighlight', 'Match highlight color in the peek view matches list'));
export const editorPeekReferenceHighlight = registerColor('editorPeekReferenceHighlight', { dark: '#ff8f0099', light: '#f5d802de', hc: null }, nls.localize('editorPeekReferenceHighlight', 'Match highlight color in the peek view editor'));
792

M
Martin Aeschlimann 已提交
793

794
registerThemingParticipant((theme, collector) => {
M
Martin Aeschlimann 已提交
795 796 797
	let findMatchHighlightColor = theme.getColor(editorPeekFindMatchHighlight);
	if (findMatchHighlightColor) {
		collector.addRule(`.monaco-editor.${theme.selector} .reference-zone-widget .ref-tree .referenceMatch { background-color: ${findMatchHighlightColor}; }`);
798
	}
M
Martin Aeschlimann 已提交
799 800 801
	let referenceHighlightColor = theme.getColor(editorPeekReferenceHighlight);
	if (referenceHighlightColor) {
		collector.addRule(`.monaco-editor.${theme.selector} .reference-zone-widget .preview .reference-decoration { background-color: ${referenceHighlightColor}; }`);
802
	}
803 804 805 806 807
	let hcOutline = theme.getColor(highContrastOutline);
	if (hcOutline) {
		collector.addRule(`.monaco-editor.${theme.selector} .reference-zone-widget .ref-tree .referenceMatch { border: 1px dotted ${hcOutline}; box-sizing: border-box; }`);
		collector.addRule(`.monaco-editor.${theme.selector} .reference-zone-widget .preview .reference-decoration { border: 2px solid ${hcOutline}; box-sizing: border-box; }`);
	}
M
Martin Aeschlimann 已提交
808
	let resultsBackground = theme.getColor(editorPeekResultsBackground);
809 810 811
	if (resultsBackground) {
		collector.addRule(`.monaco-editor.${theme.selector} .reference-zone-widget .ref-tree { background-color: ${resultsBackground}; }`);
	}
M
Martin Aeschlimann 已提交
812
	let resultsMatchForeground = theme.getColor(editorPeekResultsMatchForeground);
813 814 815
	if (resultsMatchForeground) {
		collector.addRule(`.monaco-editor.${theme.selector} .reference-zone-widget .ref-tree { color: ${resultsMatchForeground}; }`);
	}
M
Martin Aeschlimann 已提交
816
	let resultsFileForeground = theme.getColor(editorPeekResultsFileForeground);
817 818 819
	if (resultsFileForeground) {
		collector.addRule(`.monaco-editor.${theme.selector} .reference-zone-widget .ref-tree .reference-file { color: ${resultsFileForeground}; }`);
	}
M
Martin Aeschlimann 已提交
820
	let resultsSelectedBackground = theme.getColor(editorPeekResultsSelectedBackground);
821 822 823
	if (resultsSelectedBackground) {
		collector.addRule(`.monaco-editor.${theme.selector} .reference-zone-widget .ref-tree .monaco-tree.focused .monaco-tree-rows > .monaco-tree-row.selected:not(.highlighted) { background-color: ${resultsSelectedBackground}; }`);
	}
M
Martin Aeschlimann 已提交
824
	let resultsSelectedForeground = theme.getColor(editorPeekResultsSelectedForeground);
825 826 827
	if (resultsSelectedForeground) {
		collector.addRule(`.monaco-editor.${theme.selector} .reference-zone-widget .ref-tree .monaco-tree.focused .monaco-tree-rows > .monaco-tree-row.selected:not(.highlighted) { color: ${resultsSelectedForeground} !important; }`);
	}
M
Martin Aeschlimann 已提交
828
	let editorBackground = theme.getColor(editorPeekEditorBackground);
829 830 831 832 833 834 835 836 837
	if (editorBackground) {
		collector.addRule(
			`.monaco-editor.${theme.selector} .reference-zone-widget .preview .monaco-editor,` +
			`.monaco-editor.${theme.selector} .reference-zone-widget .preview .glyph-margin,` +
			`.monaco-editor.${theme.selector} .reference-zone-widget .preview .monaco-editor-background,` +
			`.monaco-editor.${theme.selector} .reference-zone-widget .preview .monaco-editor .margin .view-line {` +
			`	background-color: ${editorBackground};` +
			`}`);
	}
838
});