referencesWidget.ts 22.4 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';
A
Alex Dima 已提交
9
import * as collections from 'vs/base/common/collections';
J
Johannes Rieken 已提交
10 11 12
import { onUnexpectedError } from 'vs/base/common/errors';
import { getPathLabel } from 'vs/base/common/labels';
import Event, { Emitter } from 'vs/base/common/event';
13
import { IDisposable, dispose, Disposables, empty as EmptyDisposable } from 'vs/base/common/lifecycle';
J
Johannes Rieken 已提交
14
import { Schemas } from 'vs/base/common/network';
A
Alex Dima 已提交
15
import * as strings from 'vs/base/common/strings';
J
Johannes Rieken 已提交
16 17
import { TPromise } from 'vs/base/common/winjs.base';
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';
40
import { ITextModelResolverService } from 'vs/editor/common/services/resolverService';
E
Erich Gamma 已提交
41

A
Alex Dima 已提交
42
class DecorationsManager implements IDisposable {
E
Erich Gamma 已提交
43

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

A
Alex Dima 已提交
49 50
	private _decorationSet = collections.createStringDictionary<OneReference>();
	private _decorationIgnoreSet = collections.createStringDictionary<OneReference>();
J
Johannes Rieken 已提交
51 52
	private _callOnDispose: IDisposable[] = [];
	private _callOnModelChange: IDisposable[] = [];
E
Erich Gamma 已提交
53

J
Johannes Rieken 已提交
54
	constructor(private editor: ICodeEditor, private model: ReferencesModel) {
A
Alex Dima 已提交
55
		this._callOnDispose.push(this.editor.onDidChangeModel(() => this._onModelChanged()));
56
		this._onModelChanged();
E
Erich Gamma 已提交
57 58 59
	}

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

J
Johannes Rieken 已提交
65
	private _onModelChanged(): void {
E
Erich Gamma 已提交
66 67

		this.removeDecorations();
A
Alex Dima 已提交
68
		this._callOnModelChange = dispose(this._callOnModelChange);
E
Erich Gamma 已提交
69 70

		var model = this.editor.getModel();
J
Johannes Rieken 已提交
71
		if (!model) {
E
Erich Gamma 已提交
72 73 74
			return;
		}

J
Johannes Rieken 已提交
75 76
		for (var i = 0, len = this.model.groups.length; i < len; i++) {
			if (this.model.groups[i].uri.toString() === model.uri.toString()) {
77
				this._addDecorations(this.model.groups[i]);
E
Erich Gamma 已提交
78 79 80 81 82
				return;
			}
		}
	}

J
Johannes Rieken 已提交
83
	private _addDecorations(reference: FileReferences): void {
A
Alex Dima 已提交
84
		this._callOnModelChange.push(this.editor.getModel().onDidChangeDecorations((event) => this._onDecorationChanged(event)));
E
Erich Gamma 已提交
85 86

		this.editor.getModel().changeDecorations((accessor) => {
A
Alex Dima 已提交
87
			var newDecorations: editorCommon.IModelDeltaDecoration[] = [];
E
Erich Gamma 已提交
88 89
			var newDecorationsActualIndex: number[] = [];

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

			var decorations = accessor.deltaDecorations([], newDecorations);

			for (var i = 0; i < decorations.length; i++) {
				this._decorationSet[decorations[i]] = reference.children[newDecorationsActualIndex[i]];
			}
		});
	}

J
Johannes Rieken 已提交
110
	private _onDecorationChanged(event: editorCommon.IModelDecorationsChangedEvent): void {
A
Alex Dima 已提交
111
		var addedOrChangedDecorations = event.addedOrChangedDecorations,
J
Johannes Rieken 已提交
112
			toRemove: string[] = [];
E
Erich Gamma 已提交
113

J
Johannes Rieken 已提交
114
		for (var i = 0, len = addedOrChangedDecorations.length; i < len; i++) {
A
Alex Dima 已提交
115
			var reference = collections.lookup(this._decorationSet, addedOrChangedDecorations[i].id);
J
Johannes Rieken 已提交
116
			if (!reference) {
E
Erich Gamma 已提交
117 118 119
				continue;
			}

A
Alex Dima 已提交
120
			var newRange = addedOrChangedDecorations[i].range,
E
Erich Gamma 已提交
121 122
				ignore = false;

J
Johannes Rieken 已提交
123
			if (Range.equalsRange(newRange, reference.range)) {
E
Erich Gamma 已提交
124 125
				continue;

J
Johannes Rieken 已提交
126
			} else if (Range.spansMultipleLines(newRange)) {
E
Erich Gamma 已提交
127 128 129 130 131 132
				ignore = true;

			} else {
				var lineLength = reference.range.endColumn - reference.range.startColumn,
					newLineLength = newRange.endColumn - newRange.startColumn;

J
Johannes Rieken 已提交
133
				if (lineLength !== newLineLength) {
E
Erich Gamma 已提交
134 135 136 137
					ignore = true;
				}
			}

J
Johannes Rieken 已提交
138
			if (ignore) {
E
Erich Gamma 已提交
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
				this._decorationIgnoreSet[reference.id] = reference;
				toRemove.push(addedOrChangedDecorations[i].id);
			} else {
				reference.range = newRange;
			}
		}

		this.editor.changeDecorations((accessor) => {
			for (let i = 0, len = toRemove.length; i < len; i++) {
				delete this._decorationSet[toRemove[i]];
			}
			accessor.deltaDecorations(toRemove, []);
		});
	}

J
Johannes Rieken 已提交
154
	public removeDecorations(): void {
E
Erich Gamma 已提交
155 156 157 158 159 160 161 162 163 164 165 166
		var keys = Object.keys(this._decorationSet);
		if (keys.length > 0) {
			this.editor.changeDecorations((accessor) => {
				accessor.deltaDecorations(keys, []);
			});
		}
		this._decorationSet = {};
	}
}

class DataSource implements tree.IDataSource {

167
	constructor(
168
		@ITextModelResolverService private _textModelResolverService: ITextModelResolverService
169 170 171 172
	) {
		//
	}

173 174
	public getId(tree: tree.ITree, element: any): string {
		if (element instanceof ReferencesModel) {
E
Erich Gamma 已提交
175
			return 'root';
176 177 178 179
		} else if (element instanceof FileReferences) {
			return (<FileReferences>element).id;
		} else if (element instanceof OneReference) {
			return (<OneReference>element).id;
E
Erich Gamma 已提交
180 181 182
		}
	}

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

192 193 194 195
	public getChildren(tree: tree.ITree, element: ReferencesModel | FileReferences): TPromise<any[]> {
		if (element instanceof ReferencesModel) {
			return TPromise.as(element.groups);
		} else if (element instanceof FileReferences) {
196
			return element.resolve(this._textModelResolverService).then(val => {
197 198 199 200 201 202 203
				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 已提交
204 205 206 207 208
		} else {
			return TPromise.as([]);
		}
	}

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

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

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

J
Johannes Rieken 已提交
228
	public onTap(tree: tree.ITree, element: any, event: GestureEvent): boolean {
229 230 231 232 233 234 235 236 237 238 239
		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 已提交
240
	public onMouseDown(tree: tree.ITree, element: any, event: IMouseEvent): boolean {
E
Erich Gamma 已提交
241
		if (event.leftButton) {
A
Alex Dima 已提交
242
			if (element instanceof FileReferences) {
E
Erich Gamma 已提交
243 244
				event.preventDefault();
				event.stopPropagation();
245
				return this._expandCollapse(tree, element);
E
Erich Gamma 已提交
246 247 248 249 250
			}

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

		return false;
	}

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

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

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

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

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

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

		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 已提交
299
	public onUp(tree: tree.ITree, event: IKeyboardEvent): boolean {
E
Erich Gamma 已提交
300
		super.onUp(tree, event);
301
		this._fakeFocus(tree, event);
E
Erich Gamma 已提交
302 303 304
		return true;
	}

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

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

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

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

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

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

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

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

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

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

		dom.clearNode(container);

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

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

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

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

				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));
				}

				return badge;
E
Erich Gamma 已提交
383
			});
A
tslint  
Alex Dima 已提交
384
			/* tslint:enable:no-unused-expression */
E
Erich Gamma 已提交
385 386 387

			fileReferencesContainer.appendTo(container);

388
		} else if (element instanceof OneReference) {
E
Erich Gamma 已提交
389

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

392
			$('.reference').innerHtml(
393 394 395 396 397
				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 已提交
398 399 400 401 402 403 404
		}

		return null;
	}

}

405 406 407 408 409 410 411 412 413
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 已提交
414
	constructor(container: HTMLElement, ratio: number) {
415 416 417 418 419 420
		this._ratio = ratio;
		this._sash = new Sash(container, <IVerticalSashLayoutProvider>{
			getVerticalSashLeft: () => this._width * this._ratio,
			getVerticalSashHeight: () => this._height
		});

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

		this._disposables.add(this._sash.addListener2('change', (e: ISashEvent) => {
429 430 431 432 433
			// 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;
434 435 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
				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}%`];
	}
465 466 467 468 469 470 471 472 473

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

export interface LayoutData {
	ratio: number;
	heightInLines: number;
474 475
}

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

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

487
	private _model: ReferencesModel;
488 489 490 491
	private _decorationsManager: DecorationsManager;

	private _disposeOnNewModel: IDisposable[] = [];
	private _onDidSelectReference = new Emitter<SelectionEvent>();
492 493 494

	private _tree: Tree;
	private _treeContainer: Builder;
495
	private _sash: VSash;
496
	private _preview: ICodeEditor;
497
	private _previewModelReference: IDisposable = EmptyDisposable;
498 499 500 501 502
	private _previewNotAvailableMessage: Model;
	private _previewContainer: Builder;
	private _messageContainer: Builder;

	constructor(
503 504
		editor: ICodeEditor,
		public layoutData: LayoutData,
505
		private _textModelResolverService: ITextModelResolverService,
506 507
		private _contextService: IWorkspaceContextService,
		private _instantiationService: IInstantiationService
508
	) {
509
		super(editor, { frameColor: '#007ACC', showFrame: false, showArrow: true, isResizeable: true });
E
Erich Gamma 已提交
510

511
		this._instantiationService = this._instantiationService.createChild(new ServiceCollection([IPeekViewService, this]));
E
Erich Gamma 已提交
512 513 514
		this.create();
	}

515 516 517 518 519 520
	public dispose(): void {
		this.setModel(null);
		dispose<IDisposable>(this._preview, this._previewNotAvailableMessage, this._tree, this._sash);
		super.dispose();
	}

521 522
	get onDidSelectReference(): Event<SelectionEvent> {
		return this._onDidSelectReference.event;
523 524
	}

525
	show(where: editorCommon.IRange) {
526
		this.editor.revealRangeInCenterIfOutsideViewport(where);
527 528 529
		super.show(where, this.layoutData.heightInLines || 18);
	}

530 531 532 533
	focus(): void {
		this._tree.DOMFocus();
	}

534
	protected _onTitleClick(e: MouseEvent): void {
535 536 537
		if (this._preview && this._preview.getModel()) {
			this._onDidSelectReference.fire({
				element: this._getFocusedReference(),
538 539
				kind: e.ctrlKey || e.metaKey ? 'side' : 'open',
				source: 'title'
540
			});
E
Erich Gamma 已提交
541 542 543
		}
	}

544
	protected _fillBody(containerElement: HTMLElement): void {
A
Alex Dima 已提交
545
		var container = $(containerElement);
E
Erich Gamma 已提交
546 547 548 549 550

		container.addClass('reference-zone-widget');

		// message pane
		container.div({ 'class': 'messages' }, div => {
551
			this._messageContainer = div.hide();
E
Erich Gamma 已提交
552 553 554
		});

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

557
			var options: editorCommon.IEditorOptions = {
E
Erich Gamma 已提交
558 559
				scrollBeyondLastLine: false,
				scrollbar: DefaultConfig.editor.scrollbar,
J
Joao Moreno 已提交
560 561
				overviewRulerLanes: 2,
				fixedOverflowWidgets: true
E
Erich Gamma 已提交
562 563
			};

564 565
			this._preview = this._instantiationService.createInstance(EmbeddedCodeEditorWidget, div.getHTMLElement(), options, this.editor);
			this._previewContainer = div.hide();
566
			this._previewNotAvailableMessage = Model.createFromString(nls.localize('missingPreviewMessage', "no preview available"));
E
Erich Gamma 已提交
567 568
		});

569
		// sash
570
		this._sash = new VSash(containerElement, this.layoutData.ratio || .8);
571 572
		this._sash.onDidChangePercentages(() => {
			let [left, right] = this._sash.percentages;
J
Johannes Rieken 已提交
573
			this._previewContainer.style({ width: left });
574 575 576
			this._treeContainer.style({ width: right });
			this._preview.layout();
			this._tree.layout();
577
			this.layoutData.ratio = this._sash.ratio;
578 579
		});

E
Erich Gamma 已提交
580
		// tree
581
		container.div({ 'class': 'ref-tree inline' }, (div: Builder) => {
E
Erich Gamma 已提交
582
			var config = {
583
				dataSource: this._instantiationService.createInstance(DataSource),
584
				renderer: this._instantiationService.createInstance(Renderer),
E
Erich Gamma 已提交
585 586 587 588 589 590
				//sorter: new Sorter(),
				controller: new Controller()
			};

			var options = {
				allowHorizontalScroll: false,
B
Benjamin Pasero 已提交
591 592
				twistiePixels: 20,
				ariaLabel: nls.localize('treeAriaLabel', "References")
E
Erich Gamma 已提交
593
			};
594
			this._tree = new Tree(div.getHTMLElement(), config, options);
E
Erich Gamma 已提交
595

596
			this._treeContainer = div.hide();
E
Erich Gamma 已提交
597 598 599
		});
	}

600 601
	protected _doLayoutBody(heightInPixel: number, widthInPixel: number): void {
		super._doLayoutBody(heightInPixel, widthInPixel);
E
Erich Gamma 已提交
602

603 604 605 606 607 608
		const height = heightInPixel + 'px';
		this._sash.height = heightInPixel;
		this._sash.width = widthInPixel;

		// set height/width
		const [left, right] = this._sash.percentages;
609
		this._previewContainer.style({ height, width: left });
610
		this._treeContainer.style({ height, width: right });
E
Erich Gamma 已提交
611 612

		// forward
613 614
		this._tree.layout(heightInPixel);
		this._preview.layout();
E
Erich Gamma 已提交
615

616 617 618 619 620
		// store layout data
		this.layoutData = {
			heightInLines: this._viewZone.heightInLines,
			ratio: this._sash.ratio
		};
E
Erich Gamma 已提交
621 622
	}

J
Johannes Rieken 已提交
623
	public _onWidth(widthInPixel: number): void {
624
		this._sash.width = widthInPixel;
625
		this._preview.layout();
E
Erich Gamma 已提交
626 627
	}

628 629
	public setSelection(selection: OneReference): TPromise<any> {
		return this._revealReference(selection);
630 631
	}

632
	public setModel(newModel: ReferencesModel): TPromise<any> {
E
Erich Gamma 已提交
633
		// clean up
634
		this._disposeOnNewModel = dispose(this._disposeOnNewModel);
635 636
		this._model = newModel;
		if (this._model) {
637
			return this._onNewModel();
E
Erich Gamma 已提交
638 639 640
		}
	}

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

643 644 645
		if (this._model.empty) {
			this.setTitle('');
			this._messageContainer.innerHtml(nls.localize('noResults', "No results")).show();
646
			return TPromise.as(void 0);
647
		}
E
Erich Gamma 已提交
648

649
		this._messageContainer.hide();
650
		this._decorationsManager = new DecorationsManager(this._preview, this._model);
651
		this._disposeOnNewModel.push(this._decorationsManager);
E
Erich Gamma 已提交
652 653

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

		// listen on selection and focus
657
		this._disposeOnNewModel.push(this._tree.addListener2(Controller.Events.FOCUSED, (element) => {
A
Alex Dima 已提交
658
			if (element instanceof OneReference) {
659
				this._revealReference(element);
660
				this._onDidSelectReference.fire({ element, kind: 'show', source: 'tree' });
E
Erich Gamma 已提交
661 662
			}
		}));
663
		this._disposeOnNewModel.push(this._tree.addListener2(Controller.Events.SELECTED, (element: any) => {
A
Alex Dima 已提交
664
			if (element instanceof OneReference) {
665
				this._revealReference(element);
666
				this._onDidSelectReference.fire({ element, kind: 'goto', source: 'tree' });
E
Erich Gamma 已提交
667 668
			}
		}));
669
		this._disposeOnNewModel.push(this._tree.addListener2(Controller.Events.OPEN_TO_SIDE, (element: any) => {
A
Alex Dima 已提交
670
			if (element instanceof OneReference) {
671
				this._onDidSelectReference.fire({ element, kind: 'side', source: 'tree' });
E
Erich Gamma 已提交
672 673 674 675
			}
		}));

		// listen on editor
A
Alex Dima 已提交
676
		this._disposeOnNewModel.push(this._preview.onMouseDown((e) => {
677
			if (e.event.detail === 2) {
678 679
				this._onDidSelectReference.fire({
					element: this._getFocusedReference(),
680 681
					kind: (e.event.ctrlKey || e.event.metaKey) ? 'side' : 'open',
					source: 'editor'
682
				});
E
Erich Gamma 已提交
683 684 685 686 687
			}
		}));

		// make sure things are rendered
		dom.addClass(this.container, 'results-loaded');
688 689 690 691
		this._treeContainer.show();
		this._previewContainer.show();
		this._preview.layout();
		this._tree.layout();
E
Erich Gamma 已提交
692 693
		this.focus();

694
		// pick input and a reference to begin with
695 696
		const input = this._model.groups.length === 1 ? this._model.groups[0] : this._model;
		return this._tree.setInput(input);
E
Erich Gamma 已提交
697 698
	}

699 700
	private _getFocusedReference(): OneReference {
		const element = this._tree.getFocus();
701
		if (element instanceof OneReference) {
702
			return element;
703
		} else if (element instanceof FileReferences) {
704 705
			if (element.children.length > 0) {
				return element.children[0];
E
Erich Gamma 已提交
706 707 708 709
			}
		}
	}

710
	private _revealReference(reference: OneReference) {
E
Erich Gamma 已提交
711

712
		// Update widget header
713
		if (reference.uri.scheme !== Schemas.inMemory) {
714 715 716 717
			this.setTitle(reference.name, getPathLabel(reference.directory, this._contextService));
		} else {
			this.setTitle(nls.localize('peekView.alternateTitle', "References"));
		}
E
Erich Gamma 已提交
718

J
Joao Moreno 已提交
719
		const promise = this._textModelResolverService.createModelReference(reference.uri);
J
Joao Moreno 已提交
720 721 722

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

724
			if (!this._model) {
J
Joao Moreno 已提交
725
				ref.dispose();
726 727 728
				// disposed
				return;
			}
E
Erich Gamma 已提交
729

J
Joao Moreno 已提交
730 731 732
			this._previewModelReference.dispose();
			this._previewModelReference = EmptyDisposable;

733
			// show in editor
J
Joao Moreno 已提交
734
			const model = ref.object;
735
			if (model) {
J
Joao Moreno 已提交
736
				this._previewModelReference = ref;
737
				this._preview.setModel(model.textEditorModel);
E
Erich Gamma 已提交
738
				var sel = Range.lift(reference.range).collapseToStart();
739 740
				this._preview.setSelection(sel);
				this._preview.revealRangeInCenter(sel);
E
Erich Gamma 已提交
741
			} else {
742
				this._preview.setModel(this._previewNotAvailableMessage);
J
Joao Moreno 已提交
743
				ref.dispose();
E
Erich Gamma 已提交
744 745
			}

746
			// show in tree
747 748
			this._tree.setSelection([reference]);
			this._tree.setFocus(reference);
749 750

		}, onUnexpectedError);
E
Erich Gamma 已提交
751
	}
752
}