commentsView.ts 10.1 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 nls from 'vs/nls';
8
import * as dom from 'vs/base/browser/dom';
9 10
import { IAction, Action } from 'vs/base/common/actions';
import { CollapseAllAction } from 'vs/base/browser/ui/tree/treeDefaults';
11
import { isCodeEditor } from 'vs/editor/browser/editorBrowser';
12
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
13
import { TreeResourceNavigator } from 'vs/platform/list/browser/listService';
R
Rachel Macfarlane 已提交
14
import { IThemeService } from 'vs/platform/theme/common/themeService';
15
import { CommentNode, CommentsModel, ResourceWithCommentThreads, ICommentThreadChangedEvent } from 'vs/workbench/contrib/comments/common/commentModel';
16
import { CommentController } from 'vs/workbench/contrib/comments/browser/commentsEditorContribution';
17
import { IWorkspaceCommentThreadsEvent, ICommentService } from 'vs/workbench/contrib/comments/browser/commentService';
R
rebornix 已提交
18
import { IEditorService, ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
19
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
20
import { textLinkForeground, textLinkActiveForeground, focusBorder, textPreformatForeground } from 'vs/platform/theme/common/colorRegistry';
B
Benjamin Pasero 已提交
21
import { ResourceLabels } from 'vs/workbench/browser/labels';
22 23 24 25 26 27 28 29
import { CommentsList, COMMENTS_VIEW_ID, COMMENTS_VIEW_TITLE } from 'vs/workbench/contrib/comments/browser/commentsTreeViewer';
import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer';
import { IViewDescriptorService, IViewsService } from 'vs/workbench/common/views';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IOpenerService } from 'vs/platform/opener/common/opener';
30
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
R
Rachel Macfarlane 已提交
31 32


33
export class CommentsPanel extends ViewPane {
34 35 36 37 38 39 40
	private treeLabels!: ResourceLabels;
	private tree!: CommentsList;
	private treeContainer!: HTMLElement;
	private messageBoxContainer!: HTMLElement;
	private messageBox!: HTMLElement;
	private commentsModel!: CommentsModel;
	private collapseAllAction?: IAction;
R
Rachel Macfarlane 已提交
41

42 43
	readonly onDidChangeVisibility = this.onDidChangeBodyVisibility;

R
Rachel Macfarlane 已提交
44
	constructor(
45 46 47
		options: IViewPaneOptions,
		@IInstantiationService readonly instantiationService: IInstantiationService,
		@IViewDescriptorService viewDescriptorService: IViewDescriptorService,
48
		@IEditorService private readonly editorService: IEditorService,
49 50 51 52 53
		@IConfigurationService configurationService: IConfigurationService,
		@IContextKeyService contextKeyService: IContextKeyService,
		@IContextMenuService contextMenuService: IContextMenuService,
		@IKeybindingService keybindingService: IKeybindingService,
		@IOpenerService openerService: IOpenerService,
B
Benjamin Pasero 已提交
54
		@IThemeService themeService: IThemeService,
55 56
		@ICommentService private readonly commentService: ICommentService,
		@ITelemetryService telemetryService: ITelemetryService,
R
Rachel Macfarlane 已提交
57
	) {
58
		super({ ...(options as IViewPaneOptions), id: COMMENTS_VIEW_ID, ariaHeaderLabel: COMMENTS_VIEW_TITLE }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService);
R
Rachel Macfarlane 已提交
59 60
	}

61 62
	public renderBody(container: HTMLElement): void {
		super.renderBody(container);
R
Rachel Macfarlane 已提交
63

64
		dom.addClass(container, 'comments-panel');
R
Rachel Macfarlane 已提交
65

66 67
		let domContainer = dom.append(container, dom.$('.comments-panel-container'));
		this.treeContainer = dom.append(domContainer, dom.$('.tree-container'));
68
		this.commentsModel = new CommentsModel();
R
Rachel Macfarlane 已提交
69 70

		this.createTree();
71
		this.createMessageBox(domContainer);
R
Rachel Macfarlane 已提交
72

73 74
		this._register(this.commentService.onDidSetAllCommentThreads(this.onAllCommentsChanged, this));
		this._register(this.commentService.onDidUpdateCommentThreads(this.onCommentsUpdated, this));
R
Rachel Macfarlane 已提交
75

76
		const styleElement = dom.createStyleSheet(container);
77
		this.applyStyles(styleElement);
78 79
		this._register(this.themeService.onThemeChange(_ => this.applyStyles(styleElement)));

80
		this._register(this.onDidChangeBodyVisibility(visible => {
81 82 83 84
			if (visible) {
				this.refresh();
			}
		}));
85

86
		this.renderComments();
R
Rachel Macfarlane 已提交
87 88
	}

89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
	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}; }`);
		}

108 109 110 111 112
		const codeTextForegroundColor = theme.getColor(textPreformatForeground);
		if (codeTextForegroundColor) {
			content.push(`.comments-panel .comments-panel-container .text code { color: ${codeTextForegroundColor}; }`);
		}

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

116
	private async renderComments(): Promise<void> {
117
		dom.toggleClass(this.treeContainer, 'hidden', !this.commentsModel.hasCommentThreads());
S
Sandeep Somavarapu 已提交
118 119
		await this.tree.setInput(this.commentsModel);
		this.renderMessage();
R
Rachel Macfarlane 已提交
120 121
	}

122 123
	public getActions(): IAction[] {
		if (!this.collapseAllAction) {
124
			this.collapseAllAction = new Action('vs.tree.collapse', nls.localize('collapseAll', "Collapse All"), 'monaco-tree-action collapse-all', true, () => this.tree ? new CollapseAllAction<any, any>(this.tree, true).run() : Promise.resolve());
B
Benjamin Pasero 已提交
125
			this._register(this.collapseAllAction);
126 127 128 129 130
		}

		return [this.collapseAllAction];
	}

131 132
	public layoutBody(height: number, width: number): void {
		this.tree.layout(height, width);
R
Rachel Macfarlane 已提交
133 134 135
	}

	public getTitle(): string {
136
		return COMMENTS_VIEW_TITLE;
R
Rachel Macfarlane 已提交
137 138
	}

139 140 141 142 143 144 145 146 147 148 149
	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 已提交
150
	private createTree(): void {
151
		this.treeLabels = this._register(this.instantiationService.createInstance(ResourceLabels, this));
152
		this.tree = this._register(this.instantiationService.createInstance(CommentsList, this.treeLabels, this.treeContainer));
B
Benjamin Pasero 已提交
153

154
		const commentsNavigator = this._register(new TreeResourceNavigator(this.tree, { openOnFocus: true }));
155 156
		this._register(commentsNavigator.onDidOpenResource(e => {
			this.openFile(e.element, e.editorOptions.pinned, e.editorOptions.preserveFocus, e.sideBySide);
157
		}));
R
Rachel Macfarlane 已提交
158
	}
159

160
	private openFile(element: any, pinned?: boolean, preserveFocus?: boolean, sideBySide?: boolean): boolean {
161
		if (!element) {
162 163 164
			return false;
		}

165
		if (!(element instanceof ResourceWithCommentThreads || element instanceof CommentNode)) {
166 167 168 169
			return false;
		}

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

B
Benjamin Pasero 已提交
171
		const activeEditor = this.editorService.activeEditor;
172
		let currentActiveResource = activeEditor ? activeEditor.resource : undefined;
R
Rachel Macfarlane 已提交
173
		if (currentActiveResource && currentActiveResource.toString() === element.resource.toString()) {
P
Peng Lyu 已提交
174
			const threadToReveal = element instanceof ResourceWithCommentThreads ? element.commentThreads[0].threadId : element.threadId;
P
Peng Lyu 已提交
175
			const commentToReveal = element instanceof ResourceWithCommentThreads ? element.commentThreads[0].comment.uniqueIdInThread : element.comment.uniqueIdInThread;
B
Benjamin Pasero 已提交
176
			const control = this.editorService.activeTextEditorWidget;
P
Peng Lyu 已提交
177
			if (threadToReveal && isCodeEditor(control)) {
178
				const controller = CommentController.get(control);
179
				controller.revealCommentThread(threadToReveal, commentToReveal, false);
P
Peng Lyu 已提交
180
			}
R
Rachel Macfarlane 已提交
181 182

			return true;
P
Peng Lyu 已提交
183 184
		}

185 186 187
		const threadToReveal = element instanceof ResourceWithCommentThreads ? element.commentThreads[0].threadId : element.threadId;
		const commentToReveal = element instanceof ResourceWithCommentThreads ? element.commentThreads[0].comment : element.comment;

188 189 190 191 192 193 194 195 196 197 198
		this.editorService.openEditor({
			resource: element.resource,
			options: {
				pinned: pinned,
				preserveFocus: preserveFocus,
				selection: range
			}
		}, sideBySide ? SIDE_GROUP : ACTIVE_GROUP).then(editor => {
			if (editor) {
				const control = editor.getControl();
				if (threadToReveal && isCodeEditor(control)) {
199
					const controller = CommentController.get(control);
P
Peng Lyu 已提交
200
					controller.revealCommentThread(threadToReveal, commentToReveal.uniqueIdInThread, true);
201
				}
202 203
			}
		});
204

205 206
		return true;
	}
207 208 209

	private refresh(): void {
		if (this.isVisible()) {
210 211 212
			if (this.collapseAllAction) {
				this.collapseAllAction.enabled = this.commentsModel.hasCommentThreads();
			}
213 214

			dom.toggleClass(this.treeContainer, 'hidden', !this.commentsModel.hasCommentThreads());
215
			this.tree.updateChildren().then(() => {
216 217 218 219 220 221 222
				this.renderMessage();
			}, (e) => {
				console.log(e);
			});
		}
	}

223 224
	private onAllCommentsChanged(e: IWorkspaceCommentThreadsEvent): void {
		this.commentsModel.setCommentThreads(e.ownerId, e.commentThreads);
225 226 227
		this.refresh();
	}

228
	private onCommentsUpdated(e: ICommentThreadChangedEvent): void {
229 230 231 232
		const didUpdate = this.commentsModel.updateCommentThreads(e);
		if (didUpdate) {
			this.refresh();
		}
233
	}
R
Rachel Macfarlane 已提交
234
}
235 236 237

CommandsRegistry.registerCommand({
	id: 'workbench.action.focusCommentsPanel',
S
SteVen Batten 已提交
238
	handler: async (accessor) => {
239 240
		const viewsService = accessor.get(IViewsService);
		viewsService.openView(COMMENTS_VIEW_ID, true);
241
	}
242
});