mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-11 18:48:33 -05:00
161 lines
6.0 KiB
TypeScript
161 lines
6.0 KiB
TypeScript
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
|
|
import { URI } from 'vs/base/common/uri';
|
|
import { IRange } from 'vs/editor/common/core/range';
|
|
import { Comment, CommentThread, CommentThreadChangedEvent } from 'vs/editor/common/modes';
|
|
import { groupBy, firstIndex, flatten } from 'vs/base/common/arrays';
|
|
import { localize } from 'vs/nls';
|
|
import { values } from 'vs/base/common/map';
|
|
|
|
export interface ICommentThreadChangedEvent extends CommentThreadChangedEvent {
|
|
owner: string;
|
|
}
|
|
|
|
export class CommentNode {
|
|
threadId: string;
|
|
range: IRange;
|
|
comment: Comment;
|
|
replies: 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.replies && this.replies.length !== 0;
|
|
}
|
|
}
|
|
|
|
export class ResourceWithCommentThreads {
|
|
id: string;
|
|
commentThreads: 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.commentThreads = commentThreads.filter(thread => thread.comments && thread.comments.length).map(thread => ResourceWithCommentThreads.createCommentNode(resource, thread));
|
|
}
|
|
|
|
public static createCommentNode(resource: URI, commentThread: CommentThread): CommentNode {
|
|
const { threadId, comments, range } = commentThread;
|
|
const commentNodes: CommentNode[] = comments!.map(comment => new CommentNode(threadId!, resource, comment, range));
|
|
if (commentNodes.length > 1) {
|
|
commentNodes[0].replies = commentNodes.slice(1, commentNodes.length);
|
|
}
|
|
|
|
return commentNodes[0];
|
|
}
|
|
}
|
|
|
|
export class CommentsModel {
|
|
resourceCommentThreads: ResourceWithCommentThreads[];
|
|
commentThreadsMap: Map<string, ResourceWithCommentThreads[]>;
|
|
|
|
constructor() {
|
|
this.resourceCommentThreads = [];
|
|
this.commentThreadsMap = new Map<string, ResourceWithCommentThreads[]>();
|
|
}
|
|
|
|
public setCommentThreads(owner: string, commentThreads: CommentThread[]): void {
|
|
this.commentThreadsMap.set(owner, this.groupByResource(commentThreads));
|
|
this.resourceCommentThreads = flatten(values(this.commentThreadsMap));
|
|
}
|
|
|
|
public updateCommentThreads(event: ICommentThreadChangedEvent): boolean {
|
|
const { owner, removed, changed, added } = event;
|
|
|
|
let threadsForOwner = this.commentThreadsMap.get(owner) || [];
|
|
|
|
removed.forEach(thread => {
|
|
// Find resource that has the comment thread
|
|
const matchingResourceIndex = firstIndex(threadsForOwner, (resourceData) => resourceData.id === thread.resource);
|
|
const matchingResourceData = threadsForOwner[matchingResourceIndex];
|
|
|
|
// Find comment node on resource that is that thread and remove it
|
|
const index = firstIndex(matchingResourceData.commentThreads, (commentThread) => commentThread.threadId === thread.threadId);
|
|
matchingResourceData.commentThreads.splice(index, 1);
|
|
|
|
// If the comment thread was the last thread for a resource, remove that resource from the list
|
|
if (matchingResourceData.commentThreads.length === 0) {
|
|
threadsForOwner.splice(matchingResourceIndex, 1);
|
|
}
|
|
});
|
|
|
|
changed.forEach(thread => {
|
|
// Find resource that has the comment thread
|
|
const matchingResourceIndex = firstIndex(threadsForOwner, (resourceData) => resourceData.id === thread.resource);
|
|
const matchingResourceData = threadsForOwner[matchingResourceIndex];
|
|
|
|
// Find comment node on resource that is that thread and replace it
|
|
const index = firstIndex(matchingResourceData.commentThreads, (commentThread) => commentThread.threadId === thread.threadId);
|
|
if (index >= 0) {
|
|
matchingResourceData.commentThreads[index] = ResourceWithCommentThreads.createCommentNode(URI.parse(matchingResourceData.id), thread);
|
|
} else {
|
|
matchingResourceData.commentThreads.push(ResourceWithCommentThreads.createCommentNode(URI.parse(matchingResourceData.id), thread));
|
|
}
|
|
});
|
|
|
|
added.forEach(thread => {
|
|
const existingResource = threadsForOwner.filter(resourceWithThreads => resourceWithThreads.resource.toString() === thread.resource);
|
|
if (existingResource.length) {
|
|
const resource = existingResource[0];
|
|
if (thread.comments && thread.comments.length) {
|
|
resource.commentThreads.push(ResourceWithCommentThreads.createCommentNode(resource.resource, thread));
|
|
}
|
|
} else {
|
|
threadsForOwner.push(new ResourceWithCommentThreads(URI.parse(thread.resource!), [thread]));
|
|
}
|
|
});
|
|
|
|
this.commentThreadsMap.set(owner, threadsForOwner);
|
|
this.resourceCommentThreads = flatten(values(this.commentThreadsMap));
|
|
|
|
return removed.length > 0 || changed.length > 0 || added.length > 0;
|
|
}
|
|
|
|
public hasCommentThreads(): boolean {
|
|
return !!this.resourceCommentThreads.length;
|
|
}
|
|
|
|
public getMessage(): string {
|
|
if (!this.resourceCommentThreads.length) {
|
|
return localize('noComments', "There are no comments on this review.");
|
|
} else {
|
|
return '';
|
|
}
|
|
}
|
|
|
|
private groupByResource(commentThreads: CommentThread[]): ResourceWithCommentThreads[] {
|
|
const resourceCommentThreads: ResourceWithCommentThreads[] = [];
|
|
const commentThreadsByResource = new Map<string, ResourceWithCommentThreads>();
|
|
for (const group of groupBy(commentThreads, CommentsModel._compareURIs)) {
|
|
commentThreadsByResource.set(group[0].resource!, new ResourceWithCommentThreads(URI.parse(group[0].resource!), group));
|
|
}
|
|
|
|
commentThreadsByResource.forEach((v, i, m) => {
|
|
resourceCommentThreads.push(v);
|
|
});
|
|
|
|
return resourceCommentThreads;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
} |