commentsPanel.ts 10.5 KB
Newer Older
R
Rachel Macfarlane 已提交
1 2 3 4 5
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

R
Rachel Macfarlane 已提交
6
import 'vs/css!./media/panel';
7
import * as dom from 'vs/base/browser/dom';
8
import { IAction } from 'vs/base/common/actions';
J
Joao Moreno 已提交
9
import { Event } from 'vs/base/common/event';
10
import { CollapseAllAction, DefaultAccessibilityProvider, DefaultController, DefaultDragAndDrop } from 'vs/base/parts/tree/browser/treeDefaults';
B
Benjamin Pasero 已提交
11
import { isCodeEditor, isDiffEditor } from 'vs/editor/browser/editorBrowser';
12 13
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { TreeResourceNavigator, WorkbenchTree } from 'vs/platform/list/browser/listService';
R
Rachel Macfarlane 已提交
14 15
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
16
import { Panel } from 'vs/workbench/browser/panel';
17
import { CommentNode, CommentsModel, ResourceWithCommentThreads, ICommentThreadChangedEvent } from 'vs/workbench/parts/comments/common/commentModel';
P
Peng Lyu 已提交
18
import { ReviewController } from 'vs/workbench/parts/comments/electron-browser/commentsEditorContribution';
19
import { CommentsDataFilter, CommentsDataSource, CommentsModelRenderer } from 'vs/workbench/parts/comments/electron-browser/commentsTreeViewer';
20
import { ICommentService, IWorkspaceCommentThreadsEvent } from 'vs/workbench/parts/comments/electron-browser/commentService';
R
rebornix 已提交
21
import { IEditorService, ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
P
Peng Lyu 已提交
22
import { ICommandService } from 'vs/platform/commands/common/commands';
23
import { textLinkForeground, textLinkActiveForeground, focusBorder } from 'vs/platform/theme/common/colorRegistry';
24
import { IOpenerService } from 'vs/platform/opener/common/opener';
B
Benjamin Pasero 已提交
25
import { IStorageService } from 'vs/platform/storage/common/storage';
R
Rachel Macfarlane 已提交
26 27 28 29 30 31 32

export const COMMENTS_PANEL_ID = 'workbench.panel.comments';
export const COMMENTS_PANEL_TITLE = 'Comments';

export class CommentsPanel extends Panel {
	private tree: WorkbenchTree;
	private treeContainer: HTMLElement;
33 34
	private messageBoxContainer: HTMLElement;
	private messageBox: HTMLElement;
35
	private commentsModel: CommentsModel;
36
	private collapseAllAction: IAction;
R
Rachel Macfarlane 已提交
37 38 39 40

	constructor(
		@IInstantiationService private instantiationService: IInstantiationService,
		@ICommentService private commentService: ICommentService,
B
Benjamin Pasero 已提交
41
		@IEditorService private editorService: IEditorService,
42
		@ICommandService private commandService: ICommandService,
43
		@IOpenerService private openerService: IOpenerService,
R
Rachel Macfarlane 已提交
44
		@ITelemetryService telemetryService: ITelemetryService,
B
Benjamin Pasero 已提交
45
		@IThemeService themeService: IThemeService,
46
		@IStorageService storageService: IStorageService
R
Rachel Macfarlane 已提交
47
	) {
48
		super(COMMENTS_PANEL_ID, telemetryService, themeService, storageService);
R
Rachel Macfarlane 已提交
49 50
	}

I
isidor 已提交
51
	public create(parent: HTMLElement): void {
R
Rachel Macfarlane 已提交
52 53
		super.create(parent);

R
Rachel Macfarlane 已提交
54
		dom.addClass(parent, 'comments-panel');
R
Rachel Macfarlane 已提交
55

R
Rachel Macfarlane 已提交
56
		let container = dom.append(parent, dom.$('.comments-panel-container'));
R
Rachel Macfarlane 已提交
57
		this.treeContainer = dom.append(container, dom.$('.tree-container'));
58
		this.commentsModel = new CommentsModel();
R
Rachel Macfarlane 已提交
59 60

		this.createTree();
61
		this.createMessageBox(container);
R
Rachel Macfarlane 已提交
62

63
		this.commentService.onDidSetAllCommentThreads(this.onAllCommentsChanged, this);
64
		this.commentService.onDidUpdateCommentThreads(this.onCommentsUpdated, this);
R
Rachel Macfarlane 已提交
65

66 67 68 69 70 71
		const styleElement = dom.createStyleSheet(parent);
		this.applyStyles(styleElement);
		this.themeService.onThemeChange(_ => {
			this.applyStyles(styleElement);
		});

I
isidor 已提交
72
		this.render();
R
Rachel Macfarlane 已提交
73 74
	}

75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
	private applyStyles(styleElement: HTMLStyleElement) {
		const content: string[] = [];

		const theme = this.themeService.getTheme();
		const linkColor = theme.getColor(textLinkForeground);
		if (linkColor) {
			content.push(`.comments-panel .comments-panel-container a { color: ${linkColor}; }`);
		}

		const linkActiveColor = theme.getColor(textLinkActiveForeground);
		if (linkActiveColor) {
			content.push(`.comments-panel .comments-panel-container a:hover, a:active { color: ${linkActiveColor}; }`);
		}

		const focusColor = theme.getColor(focusBorder);
		if (focusColor) {
			content.push(`.comments-panel .commenst-panel-container a:focus { outline-color: ${focusColor}; }`);
		}

		styleElement.innerHTML = content.join('\n');
	}

S
Sandeep Somavarapu 已提交
97
	private async render(): Promise<void> {
98
		dom.toggleClass(this.treeContainer, 'hidden', !this.commentsModel.hasCommentThreads());
S
Sandeep Somavarapu 已提交
99 100
		await this.tree.setInput(this.commentsModel);
		this.renderMessage();
R
Rachel Macfarlane 已提交
101 102
	}

103 104 105
	public getActions(): IAction[] {
		if (!this.collapseAllAction) {
			this.collapseAllAction = this.instantiationService.createInstance(CollapseAllAction, this.tree, this.commentsModel.hasCommentThreads());
B
Benjamin Pasero 已提交
106
			this._register(this.collapseAllAction);
107 108 109 110 111
		}

		return [this.collapseAllAction];
	}

R
Rachel Macfarlane 已提交
112 113 114 115 116 117 118 119
	public layout(dimensions: dom.Dimension): void {
		this.tree.layout(dimensions.height, dimensions.width);
	}

	public getTitle(): string {
		return COMMENTS_PANEL_TITLE;
	}

120 121 122 123 124 125 126 127 128 129 130
	private createMessageBox(parent: HTMLElement): void {
		this.messageBoxContainer = dom.append(parent, dom.$('.message-box-container'));
		this.messageBox = dom.append(this.messageBoxContainer, dom.$('span'));
		this.messageBox.setAttribute('tabindex', '0');
	}

	private renderMessage(): void {
		this.messageBox.textContent = this.commentsModel.getMessage();
		dom.toggleClass(this.messageBoxContainer, 'hidden', this.commentsModel.hasCommentThreads());
	}

R
Rachel Macfarlane 已提交
131 132 133
	private createTree(): void {
		this.tree = this.instantiationService.createInstance(WorkbenchTree, this.treeContainer, {
			dataSource: new CommentsDataSource(),
134
			renderer: new CommentsModelRenderer(this.instantiationService, this.openerService),
R
Rachel Macfarlane 已提交
135 136 137
			accessibilityProvider: new DefaultAccessibilityProvider,
			controller: new DefaultController(),
			dnd: new DefaultDragAndDrop(),
138
			filter: new CommentsDataFilter()
R
Rachel Macfarlane 已提交
139 140 141 142
		}, {
				twistiePixels: 20,
				ariaLabel: COMMENTS_PANEL_TITLE
			});
143 144

		const commentsNavigator = this._register(new TreeResourceNavigator(this.tree, { openOnFocus: true }));
J
Joao Moreno 已提交
145
		this._register(Event.debounce(commentsNavigator.openResource, (last, event) => event, 100, true)(options => {
146
			this.openFile(options.element, options.editorOptions.pinned, options.editorOptions.preserveFocus, options.sideBySide);
147
		}));
R
Rachel Macfarlane 已提交
148
	}
149

150
	private openFile(element: any, pinned: boolean, preserveFocus: boolean, sideBySide: boolean): boolean {
151
		if (!element) {
152 153 154
			return false;
		}

155
		if (!(element instanceof ResourceWithCommentThreads || element instanceof CommentNode)) {
156 157 158 159
			return false;
		}

		const range = element instanceof ResourceWithCommentThreads ? element.commentThreads[0].range : element.range;
P
Peng Lyu 已提交
160

B
Benjamin Pasero 已提交
161 162
		const activeEditor = this.editorService.activeEditor;
		let currentActiveResource = activeEditor ? activeEditor.getResource() : void 0;
R
Rachel Macfarlane 已提交
163
		if (currentActiveResource && currentActiveResource.toString() === element.resource.toString()) {
P
Peng Lyu 已提交
164
			const threadToReveal = element instanceof ResourceWithCommentThreads ? element.commentThreads[0].threadId : element.threadId;
P
Peng Lyu 已提交
165
			const commentToReveal = element instanceof ResourceWithCommentThreads ? element.commentThreads[0].comment.commentId : element.comment.commentId;
B
Benjamin Pasero 已提交
166
			const control = this.editorService.activeTextEditorWidget;
P
Peng Lyu 已提交
167 168
			if (threadToReveal && isCodeEditor(control)) {
				const controller = ReviewController.get(control);
169
				controller.revealCommentThread(threadToReveal, commentToReveal, false);
P
Peng Lyu 已提交
170
			}
R
Rachel Macfarlane 已提交
171 172

			return true;
P
Peng Lyu 已提交
173 174 175
		}


176 177 178 179
		const threadToReveal = element instanceof ResourceWithCommentThreads ? element.commentThreads[0].threadId : element.threadId;
		const commentToReveal = element instanceof ResourceWithCommentThreads ? element.commentThreads[0].comment : element.comment;

		if (commentToReveal.command) {
180
			this.commandService.executeCommand(commentToReveal.command.id, ...commentToReveal.command.arguments).then(_ => {
B
Benjamin Pasero 已提交
181 182 183 184
				let activeWidget = this.editorService.activeTextEditorWidget;
				if (isDiffEditor(activeWidget)) {
					const originalEditorWidget = activeWidget.getOriginalEditor();
					const modifiedEditorWidget = activeWidget.getModifiedEditor();
185 186

					let controller;
B
Benjamin Pasero 已提交
187 188 189 190
					if (originalEditorWidget.getModel().uri.toString() === element.resource.toString()) {
						controller = ReviewController.get(originalEditorWidget);
					} else if (modifiedEditorWidget.getModel().uri.toString() === element.resource.toString()) {
						controller = ReviewController.get(modifiedEditorWidget);
191 192 193
					}

					if (controller) {
194
						controller.revealCommentThread(threadToReveal, commentToReveal.commentId, true);
195 196
					}
				} else {
B
Benjamin Pasero 已提交
197 198
					let activeEditor = this.editorService.activeEditor;
					let currentActiveResource = activeEditor ? activeEditor.getResource() : void 0;
199
					if (currentActiveResource && currentActiveResource.toString() === element.resource.toString()) {
B
Benjamin Pasero 已提交
200
						const control = this.editorService.activeTextEditorWidget;
201 202
						if (threadToReveal && isCodeEditor(control)) {
							const controller = ReviewController.get(control);
203
							controller.revealCommentThread(threadToReveal, commentToReveal.commentId, true);
204 205 206 207 208 209 210
						}
					}
				}

				return true;
			});
		} else {
211
			this.editorService.openEditor({
212 213 214 215 216 217
				resource: element.resource,
				options: {
					pinned: pinned,
					preserveFocus: preserveFocus,
					selection: range
				}
218
			}, sideBySide ? SIDE_GROUP : ACTIVE_GROUP).then(editor => {
219 220 221 222 223 224
				if (editor) {
					const control = editor.getControl();
					if (threadToReveal && isCodeEditor(control)) {
						const controller = ReviewController.get(control);
						controller.revealCommentThread(threadToReveal, commentToReveal.commentId, true);
					}
225 226 227 228
				}
			});
		}

229 230
		return true;
	}
231

I
isidor 已提交
232
	public setVisible(visible: boolean): void {
233
		const wasVisible = this.isVisible();
I
isidor 已提交
234 235 236 237 238 239
		super.setVisible(visible);
		if (this.isVisible()) {
			if (!wasVisible) {
				this.refresh();
			}
		}
240 241
	}

242 243 244 245 246 247 248 249 250 251 252 253 254
	private refresh(): void {
		if (this.isVisible()) {
			this.collapseAllAction.enabled = this.commentsModel.hasCommentThreads();

			dom.toggleClass(this.treeContainer, 'hidden', !this.commentsModel.hasCommentThreads());
			this.tree.refresh().then(() => {
				this.renderMessage();
			}, (e) => {
				console.log(e);
			});
		}
	}

255 256
	private onAllCommentsChanged(e: IWorkspaceCommentThreadsEvent): void {
		this.commentsModel.setCommentThreads(e.ownerId, e.commentThreads);
257 258 259
		this.refresh();
	}

260
	private onCommentsUpdated(e: ICommentThreadChangedEvent): void {
261 262 263 264
		const didUpdate = this.commentsModel.updateCommentThreads(e);
		if (didUpdate) {
			this.refresh();
		}
265
	}
R
Rachel Macfarlane 已提交
266
}