提交 3a297dc5 编写于 作者: R Rachel Macfarlane

Add provideAllComments method to comments provider

上级 bb8e244a
......@@ -136,6 +136,40 @@ export class ReviewMode {
commentsCache.set(changedItem.filePath.toString(), matchingComments);
});
function commentsToCommentThreads(uri: vscode.Uri, comments: Comment[]): vscode.CommentThread[] {
if (!comments || !comments.length) {
return [];
}
let sections = _.groupBy(comments, comment => comment.position);
let ret: vscode.CommentThread[] = [];
for (let i in sections) {
let comments = sections[i];
const comment = comments[0];
const commentAbsolutePosition = comment.diff_hunk_range.start + (comment.position - 1);
const pos = new vscode.Position(comment.currentPosition ? comment.currentPosition - 1 - 1 : commentAbsolutePosition - /* after line */ 1 - /* it's zero based*/ 1, 0);
const range = new vscode.Range(pos, pos);
ret.push({
threadId: comment.id,
resource: uri,
range,
comments: comments.map(comment => {
return {
body: new vscode.MarkdownString(comment.body),
userName: comment.user.login,
gravatar: comment.user.avatar_url
};
}),
actions: actions
});
}
return ret;
}
this._commentProvider = vscode.workspace.registerCommentProvider({
provideNewCommentRange: async (document: vscode.TextDocument, token: vscode.CancellationToken) => {
if (document.uri.scheme === 'review' || document.uri.scheme === 'file') {
......@@ -174,36 +208,12 @@ export class ReviewMode {
}
}
if (!matchingComments || !matchingComments.length) {
return [];
}
let sections = _.groupBy(matchingComments, comment => comment.position);
let ret: vscode.CommentThread[] = [];
for (let i in sections) {
let comments = sections[i];
const comment = comments[0];
const commentAbsolutePosition = comment.diff_hunk_range.start + (comment.position - 1);
const pos = new vscode.Position(comment.currentPosition ? comment.currentPosition - 1 - 1 : commentAbsolutePosition - /* after line */ 1 - /* it's zero based*/ 1, 0);
const range = new vscode.Range(pos, pos);
ret.push({
threadId: comment.id,
range,
comments: comments.map(comment => {
return {
body: new vscode.MarkdownString(comment.body),
userName: comment.user.login,
gravatar: comment.user.avatar_url
};
}),
actions: actions
});
}
return ret;
return commentsToCommentThreads(document.uri, matchingComments);
},
provideAllComments: async (token: vscode.CancellationToken) => {
return fileChanges
.map(fileChange => commentsToCommentThreads(fileChange.filePath, fileChange.comments))
.reduce((prev, curr) => prev.concat(curr), []);
}
});
......
......@@ -940,6 +940,7 @@ export interface Command {
export interface CommentThread {
readonly threadId: string;
readonly resource: string;
readonly range: IRange;
readonly comments: Comment[];
readonly actions: Command[];
......@@ -959,6 +960,7 @@ export interface Comment {
export interface CommentProvider {
provideComments(model: model.ITextModel, token: CancellationToken): CommentThread[];
provideNewCommentRange(model: model.ITextModel, token: CancellationToken): Promise<NewCommentAction>;
provideAllComments(token: CancellationToken): Promise<CommentThread[]>;
}
export interface ICodeLensSymbol {
......
......@@ -5030,6 +5030,7 @@ declare namespace monaco.languages {
export interface CommentThread {
readonly threadId: string;
readonly resource: string;
readonly range: IRange;
readonly comments: Comment[];
readonly actions: Command[];
......@@ -5049,6 +5050,7 @@ declare namespace monaco.languages {
export interface CommentProvider {
provideComments(model: editor.ITextModel, token: CancellationToken): CommentThread[];
provideNewCommentRange(model: editor.ITextModel, token: CancellationToken): Promise<NewCommentAction>;
provideAllComments(token: CancellationToken): Promise<CommentThread[]>;
}
export interface ICodeLensSymbol {
......
......@@ -772,6 +772,7 @@ declare module 'vscode' {
interface CommentThread {
threadId: string;
resource: Uri;
range: Range;
comments: Comment[];
actions?: Command[];
......@@ -795,6 +796,7 @@ declare module 'vscode' {
interface CommentProvider {
provideComments(document: TextDocument, token: CancellationToken): Promise<CommentThread[]>;
provideNewCommentRange(document: TextDocument, token: CancellationToken): Promise<NewCommentAction>;
provideAllComments?(token: CancellationToken): Promise<CommentThread[]>;
}
namespace workspace {
......
......@@ -50,8 +50,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments
const outerEditorURI = outerEditor.getModel().uri;
this.provideComments(outerEditorURI).then(commentThreads => {
controller.setComments(commentThreads);
this._commentService.setCommentsForResource(outerEditorURI, commentThreads);
this._commentService.setComments(outerEditorURI, commentThreads);
});
this.provideNewCommentRange(outerEditor.getModel()).then(newActions => {
controller.setNewCommentActions(newActions);
......@@ -61,11 +60,20 @@ export class MainThreadComments extends Disposable implements MainThreadComments
$registerCommentProvider(handle: number): void {
this._providers.set(handle, undefined);
this._panelService.setPanelEnablement(COMMENTS_PANEL_ID, true);
// Fetch all comments
this._proxy.$provideAllComments(handle).then(commentThreads => {
if (commentThreads) {
this._commentService.setAllComments(commentThreads);
this._panelService.setPanelEnablement(COMMENTS_PANEL_ID, true);
}
});
}
$unregisterCommentProvider(handle: number): void {
this._providers.delete(handle);
this._panelService.setPanelEnablement(COMMENTS_PANEL_ID, false);
}
dispose(): void {
......
......@@ -833,6 +833,7 @@ export interface ExtHostProgressShape {
export interface ExtHostCommentsShape {
$provideComments(handle: number, document: UriComponents): TPromise<modes.CommentThread[]>;
$provideNewCommentRange(handle: number, document: UriComponents): TPromise<modes.NewCommentAction>;
$provideAllComments(handle: number): TPromise<modes.CommentThread[]>;
}
// --- proxy identifiers
......
......@@ -58,6 +58,22 @@ export class ExtHostComments implements ExtHostCommentsShape {
.then(comments => comments.map(x => convertCommentThread(x, this._commandsConverter)));
}
$provideAllComments(handle: number): TPromise<modes.CommentThread[]> {
let provider = this._providers.get(handle);
// provideAllComments is an optional method
if (!provider.provideAllComments) {
return TPromise.as(null);
}
return asWinJsPromise(token => {
return provider.provideAllComments(token);
}).then(comments =>
comments.map(x => convertCommentThread(x, this._commandsConverter)
));
}
$provideNewCommentRange(handle: number, uri: UriComponents): TPromise<modes.NewCommentAction> {
const data = this._documents.getDocumentData(URI.revive(uri));
if (!data || !data.document) {
......@@ -87,6 +103,7 @@ function convertNewCommandAction(vscodeNewCommentAction: vscode.NewCommentAction
function convertCommentThread(vscodeCommentThread: vscode.CommentThread, commandsConverter: CommandsConverter): modes.CommentThread {
return {
threadId: vscodeCommentThread.threadId,
resource: vscodeCommentThread.resource.toString(),
range: extHostTypeConverter.fromRange(vscodeCommentThread.range),
comments: vscodeCommentThread.comments.map(convertComment),
actions: vscodeCommentThread.actions.map(commandsConverter.toInternal)
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import URI from 'vs/base/common/uri';
import { IRange } from 'vs/editor/common/core/range';
import { Comment, CommentThread } from 'vs/editor/common/modes';
import { groupBy } from 'vs/base/common/arrays';
export class CommentNode {
threadId: string;
range: IRange;
comment: Comment;
reply: CommentNode;
resource: URI;
constructor(threadId: string, resource: URI, comment: Comment, range: IRange) {
this.threadId = threadId;
this.comment = comment;
this.resource = resource;
this.range = range;
}
hasReply(): boolean {
return !!this.reply;
}
}
export class ResourceCommentThreads {
id: string;
comments: CommentNode[]; // The top level comments on the file. Replys are nested under each node.
resource: URI;
constructor(resource: URI, commentThreads: CommentThread[]) {
this.id = resource.toString();
this.resource = resource;
this.comments = commentThreads.map(thread => this.createCommentNode(resource, thread));
}
private createCommentNode(resource: URI, commentThread: CommentThread): CommentNode {
const { threadId, comments, range } = commentThread;
const commentNodes: CommentNode[] = comments.map(comment => new CommentNode(threadId, resource, comment, range));
for (var i = 0; i < commentNodes.length - 1; i++) {
const commentNode = commentNodes[i];
commentNode.reply = commentNodes[i + 1];
}
commentNodes.push(new CommentNode(threadId, resource, comments[comments.length - 1], range));
return commentNodes[0];
}
}
export class CommentsModel {
commentThreads: ResourceCommentThreads[];
commentThreadsByResource: Map<string, ResourceCommentThreads>;
constructor() {
this.commentThreads = [];
this.commentThreadsByResource = new Map<string, ResourceCommentThreads>();
}
public setCommentThreadsForResource(resource: URI, commentThreads: CommentThread[]): boolean {
if (!commentThreads.length || this.commentThreadsByResource.get(resource.toString())) {
return false;
}
this.commentThreadsByResource.set(resource.toString(), new ResourceCommentThreads(resource, commentThreads));
this.commentThreads = [];
this.commentThreadsByResource.forEach((v, i, m) => {
this.commentThreads.push(v);
});
return true;
}
public setCommentThreads(commentThreads: CommentThread[]) {
for (const group of groupBy(commentThreads, CommentsModel._compareURIs)) {
this.commentThreadsByResource.set(group[0].resource, new ResourceCommentThreads(URI.parse(group[0].resource), group));
}
this.commentThreadsByResource.forEach((v, i, m) => {
this.commentThreads.push(v);
});
}
private static _compareURIs(a: CommentThread, b: CommentThread) {
const resourceA = a.resource.toString();
const resourceB = b.resource.toString();
if (resourceA < resourceB) {
return -1;
} else if (resourceA > resourceB) {
return 1;
} else {
return 0;
}
}
}
\ No newline at end of file
......@@ -33,6 +33,7 @@ import { Emitter, Event } from 'vs/base/common/event';
import { editorBackground, editorForeground } from 'vs/platform/theme/common/colorRegistry';
import { ZoneWidget, IOptions } from 'vs/editor/contrib/zoneWidget/zoneWidget';
import { ReviewModel, ReviewStyle } from 'vs/workbench/parts/comments/common/reviewModel';
import { ICommentService } from '../../../services/comments/electron-browser/commentService';
export const ctxReviewPanelVisible = new RawContextKey<boolean>('reviewPanelVisible', false);
export const ID = 'editor.contrib.review';
......@@ -291,6 +292,7 @@ export class ReviewController implements IEditorContribution {
@IContextKeyService contextKeyService: IContextKeyService,
@IThemeService private themeService: IThemeService,
@ICommandService private commandService: ICommandService,
@ICommentService private commentService: ICommentService
) {
this.editor = editor;
this.globalToDispose = [];
......@@ -343,6 +345,13 @@ export class ReviewController implements IEditorContribution {
}
});
this.commentService.onDidSetResourceCommentThreads(e => {
const editorURI = this.editor && this.editor.getModel() && this.editor.getModel().uri;
if (editorURI && editorURI.toString() === e.resource.toString()) {
this.setComments(e.commentThreads);
}
});
this.globalToDispose.push(this.editor.onDidChangeModel(() => this.onModelChanged()));
}
......@@ -449,6 +458,7 @@ export class ReviewController implements IEditorContribution {
});
this._zoneWidget.display({
threadId: null,
resource: null,
comments: [],
range: {
startLineNumber: lineNumber,
......
......@@ -16,8 +16,10 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { ResourceLabel } from 'vs/workbench/browser/labels';
import { Panel } from 'vs/workbench/browser/panel';
import { CommentNode, CommentsModel, ICommentService, ResourceCommentThreads } from 'vs/workbench/services/comments/electron-browser/commentService';
import { ReviewController } from 'vs/workbench/parts/comments/electron-browser/commentsEditorContribution';
import { ICommentService } from 'vs/workbench/services/comments/electron-browser/commentService';
import { ResourceCommentThreads, CommentsModel, CommentNode } from 'vs/workbench/parts/comments/common/commentModel';
import { CommentThread } from 'vs/editor/common/modes';
export const COMMENTS_PANEL_ID = 'workbench.panel.comments';
export const COMMENTS_PANEL_TITLE = 'Comments';
......@@ -153,6 +155,7 @@ interface IResourceMarkersTemplateData {
export class CommentsPanel extends Panel {
private tree: WorkbenchTree;
private treeContainer: HTMLElement;
private commentsModel: CommentsModel;
constructor(
@IInstantiationService private instantiationService: IInstantiationService,
......@@ -171,15 +174,17 @@ export class CommentsPanel extends Panel {
let container = dom.append(parent, dom.$('.markers-panel-container'));
this.treeContainer = dom.append(container, dom.$('.tree-container'));
this.commentsModel = new CommentsModel();
this.createTree();
this.commentService.onDidChangeCommentThreads(this.onCommentThreadChanged, this);
this.commentService.onDidSetAllCommentThreads(this.onAllCommentsChanged, this);
return this.render();
}
private onCommentThreadChanged() {
private onAllCommentsChanged(e: CommentThread[]) {
this.commentsModel.setCommentThreads(e);
this.tree.refresh().then(() => {
console.log('tree refreshed');
}, (e) => {
......@@ -189,7 +194,7 @@ export class CommentsPanel extends Panel {
private render(): TPromise<void> {
dom.toggleClass(this.treeContainer, 'hidden', false);
return this.tree.setInput(this.commentService.commentsModel);
return this.tree.setInput(this.commentsModel);
}
public layout(dimensions: dom.Dimension): void {
......
......@@ -5,111 +5,46 @@
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import { CommentThread, Comment } from 'vs/editor/common/modes';
import { CommentThread } from 'vs/editor/common/modes';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { Event, Emitter } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import URI from 'vs/base/common/uri';
import { IRange } from 'vs/editor/common/core/range';
export const ICommentService = createDecorator<ICommentService>('commentService');
export class CommentNode {
threadId: string;
range: IRange;
comment: Comment;
reply: CommentNode;
export interface IResourceCommentThreadEvent {
resource: URI;
constructor(threadId: string, resource: URI, comment: Comment, range: IRange) {
this.threadId = threadId;
this.comment = comment;
this.resource = resource;
this.range = range;
}
hasReply(): boolean {
return !!this.reply;
}
}
export class ResourceCommentThreads {
id: string;
comments: CommentNode[]; // The top level comments on the file. Replys are nested under each node.
resource: URI;
constructor(resource: URI, commentThreads: CommentThread[]) {
this.id = resource.toString();
this.resource = resource;
this.comments = commentThreads.map(thread => this.createCommentNode(resource, thread));
}
private createCommentNode(resource: URI, commentThread: CommentThread): CommentNode {
const { threadId, comments, range } = commentThread;
const commentNodes: CommentNode[] = comments.map(comment => new CommentNode(threadId, resource, comment, range));
for (var i = 0; i < commentNodes.length - 1; i++) {
const commentNode = commentNodes[i];
commentNode.reply = commentNodes[i + 1];
}
commentNodes.push(new CommentNode(threadId, resource, comments[comments.length - 1], range));
return commentNodes[0];
}
}
export class CommentsModel {
commentThreads: ResourceCommentThreads[];
commentThreadsByResource: Map<string, ResourceCommentThreads>;
constructor() {
this.commentThreads = [];
this.commentThreadsByResource = new Map<string, ResourceCommentThreads>();
}
public setCommentThreadsForResource(resource: URI, commentThreads: CommentThread[]): boolean {
if (!commentThreads.length || this.commentThreadsByResource.get(resource.toString())) {
return false;
}
this.commentThreadsByResource.set(resource.toString(), new ResourceCommentThreads(resource, commentThreads));
this.commentThreads = [];
this.commentThreadsByResource.forEach((v, i, m) => {
this.commentThreads.push(v);
});
return true;
}
commentThreads: CommentThread[];
}
export interface ICommentService {
_serviceBrand: any;
readonly commentsModel;
readonly onDidChangeCommentThreads: Event<null>;
setCommentsForResource(resource: URI, commentThreads: CommentThread[]);
readonly onDidSetResourceCommentThreads: Event<IResourceCommentThreadEvent>;
readonly onDidSetAllCommentThreads: Event<CommentThread[]>;
setComments(resource: URI, commentThreads: CommentThread[]): void;
setAllComments(commentsByResource: CommentThread[]): void;
}
export class CommentService extends Disposable implements ICommentService {
_serviceBrand: any;
commentsModel: CommentsModel;
private readonly _onDidSetResourceCommentThreads: Emitter<IResourceCommentThreadEvent> = this._register(new Emitter<IResourceCommentThreadEvent>());
readonly onDidSetResourceCommentThreads: Event<IResourceCommentThreadEvent> = this._onDidSetResourceCommentThreads.event;
private readonly _onDidChangeCommentThreads: Emitter<null> = this._register(new Emitter<null>());
readonly onDidChangeCommentThreads: Event<null> = this._onDidChangeCommentThreads.event;
private readonly _onDidSetAllCommentThreads: Emitter<CommentThread[]> = this._register(new Emitter<CommentThread[]>());
readonly onDidSetAllCommentThreads: Event<CommentThread[]> = this._onDidSetAllCommentThreads.event;
constructor() {
super();
this.commentsModel = new CommentsModel();
}
setCommentsForResource(resource: URI, commentThreads: CommentThread[]): TPromise<void> {
if (this.commentsModel.setCommentThreadsForResource(resource, commentThreads)) {
this._onDidChangeCommentThreads.fire();
}
return TPromise.as(null);
setComments(resource: URI, commentThreads: CommentThread[]): void {
this._onDidSetResourceCommentThreads.fire({ resource, commentThreads });
}
setAllComments(commentsByResource: CommentThread[]): void {
this._onDidSetAllCommentThreads.fire(commentsByResource);
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册