Path: blob/main/src/vs/sessions/contrib/agentFeedback/browser/sessionEditorComments.ts
13401 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { IRange, Range } from '../../../../editor/common/core/range.js';6import { URI } from '../../../../base/common/uri.js';7import { IAgentFeedback } from './agentFeedbackService.js';8import { CodeReviewStateKind, ICodeReviewComment, ICodeReviewState, ICodeReviewSuggestion, IPRReviewComment, IPRReviewState, PRReviewStateKind } from '../../codeReview/browser/codeReviewService.js';910export const enum SessionEditorCommentSource {11AgentFeedback = 'agentFeedback',12CodeReview = 'codeReview',13PRReview = 'prReview',14}1516export interface ISessionEditorComment {17readonly id: string;18readonly sourceId: string;19readonly source: SessionEditorCommentSource;20readonly sessionResource: URI;21readonly resourceUri: URI;22readonly range: IRange;23readonly text: string;24readonly suggestion?: ICodeReviewSuggestion;25readonly severity?: string;26readonly canConvertToAgentFeedback: boolean;27}2829export function getCodeReviewComments(reviewState: ICodeReviewState): readonly ICodeReviewComment[] {30return reviewState.kind === CodeReviewStateKind.Result ? reviewState.comments : [];31}3233export function getPRReviewComments(prReviewState: IPRReviewState | undefined): readonly IPRReviewComment[] {34return prReviewState?.kind === PRReviewStateKind.Loaded ? prReviewState.comments : [];35}3637export function getSessionEditorComments(38sessionResource: URI,39agentFeedbackItems: readonly IAgentFeedback[],40reviewState: ICodeReviewState,41prReviewState?: IPRReviewState,42): readonly ISessionEditorComment[] {43const comments: ISessionEditorComment[] = [];4445for (const item of agentFeedbackItems) {46comments.push({47id: toSessionEditorCommentId(SessionEditorCommentSource.AgentFeedback, item.id),48sourceId: item.id,49source: SessionEditorCommentSource.AgentFeedback,50sessionResource,51resourceUri: item.resourceUri,52range: item.range,53text: item.text,54suggestion: item.suggestion,55canConvertToAgentFeedback: false,56});57}5859for (const item of getCodeReviewComments(reviewState)) {60comments.push({61id: toSessionEditorCommentId(SessionEditorCommentSource.CodeReview, item.id),62sourceId: item.id,63source: SessionEditorCommentSource.CodeReview,64sessionResource,65resourceUri: item.uri,66range: item.range,67text: item.body,68suggestion: item.suggestion,69severity: item.severity,70canConvertToAgentFeedback: true,71});72}7374for (const item of getPRReviewComments(prReviewState)) {75comments.push({76id: toSessionEditorCommentId(SessionEditorCommentSource.PRReview, item.id),77sourceId: item.id,78source: SessionEditorCommentSource.PRReview,79sessionResource,80resourceUri: item.uri,81range: item.range,82text: item.body,83canConvertToAgentFeedback: true,84});85}8687comments.sort(compareSessionEditorComments);88return comments;89}9091export function compareSessionEditorComments(a: ISessionEditorComment, b: ISessionEditorComment): number {92return a.resourceUri.toString().localeCompare(b.resourceUri.toString())93|| Range.compareRangesUsingStarts(Range.lift(a.range), Range.lift(b.range))94|| a.source.localeCompare(b.source)95|| a.sourceId.localeCompare(b.sourceId);96}9798export function groupNearbySessionEditorComments(items: readonly ISessionEditorComment[], lineThreshold: number = 5): ISessionEditorComment[][] {99if (items.length === 0) {100return [];101}102103const sorted = [...items].sort(compareSessionEditorComments);104const groups: ISessionEditorComment[][] = [];105let currentGroup: ISessionEditorComment[] = [sorted[0]];106107for (let i = 1; i < sorted.length; i++) {108const firstItem = currentGroup[0];109const currentItem = sorted[i];110111const sameResource = currentItem.resourceUri.toString() === firstItem.resourceUri.toString();112const verticalSpan = currentItem.range.startLineNumber - firstItem.range.startLineNumber;113114if (sameResource && verticalSpan <= lineThreshold) {115currentGroup.push(currentItem);116} else {117groups.push(currentGroup);118currentGroup = [currentItem];119}120}121122groups.push(currentGroup);123return groups;124}125126export function getResourceEditorComments(resourceUri: URI, comments: readonly ISessionEditorComment[]): readonly ISessionEditorComment[] {127const resource = resourceUri.toString();128return comments.filter(comment => comment.resourceUri.toString() === resource);129}130131export function toSessionEditorCommentId(source: SessionEditorCommentSource, sourceId: string): string {132return `${source}:${sourceId}`;133}134135export function hasAgentFeedbackComments(comments: readonly ISessionEditorComment[]): boolean {136return comments.some(comment => comment.source === SessionEditorCommentSource.AgentFeedback);137}138139140