Merge "Add "Resolve comments" prompt to AI dialog"
diff --git a/polygerrit-ui/app/elements/change/gr-ai-prompt-dialog/gr-ai-prompt-dialog.ts b/polygerrit-ui/app/elements/change/gr-ai-prompt-dialog/gr-ai-prompt-dialog.ts
index a990898..1b73243 100644
--- a/polygerrit-ui/app/elements/change/gr-ai-prompt-dialog/gr-ai-prompt-dialog.ts
+++ b/polygerrit-ui/app/elements/change/gr-ai-prompt-dialog/gr-ai-prompt-dialog.ts
@@ -16,6 +16,9 @@
import {subscribe} from '../../lit/subscription-controller';
import {resolve} from '../../../models/dependency';
import {changeModelToken} from '../../../models/change/change-model';
+import {commentsModelToken} from '../../../models/comments/comments-model';
+import {CommentThread} from '../../../types/common';
+import {isUnresolved} from '../../../utils/comment-util';
import {ParsedChangeInfo} from '../../../types/types';
import {PatchSetNum} from '../../../types/common';
import {HELP_ME_REVIEW_PROMPT, IMPROVE_COMMIT_MESSAGE} from './prompts';
@@ -42,6 +45,11 @@
label: 'Just patch content',
prompt: '{{patch}}',
},
+ RESOLVE_COMMENTS: {
+ id: 'resolve_comments',
+ label: 'Unresolved Comments',
+ prompt: '{{comments}}',
+ },
};
const CONTEXT_OPTIONS = [
@@ -76,12 +84,17 @@
@state() private context = 3;
+ // private but used in tests
+ @state() threads: CommentThread[] = [];
+
@state() private promptContent = '';
@state() private promptSize = '';
private readonly getChangeModel = resolve(this, changeModelToken);
+ private readonly getCommentsModel = resolve(this, commentsModelToken);
+
private readonly restApiService = getAppContext().restApiService;
constructor() {
@@ -96,6 +109,11 @@
() => this.getChangeModel().patchNum$,
x => (this.patchNum = x)
);
+ subscribe(
+ this,
+ () => this.getCommentsModel().threads$,
+ x => (this.threads = x)
+ );
}
static override get styles() {
@@ -297,7 +315,8 @@
override willUpdate(changedProperties: PropertyValues) {
if (
changedProperties.has('patchContent') ||
- changedProperties.has('selectedTemplate')
+ changedProperties.has('selectedTemplate') ||
+ changedProperties.has('threads')
) {
this.updatePromptContent();
}
@@ -338,6 +357,29 @@
this.patchContent = content;
}
+ private getUnresolvedCommentsFormatted(): string {
+ const unresolvedThreads = this.threads.filter(isUnresolved);
+ if (unresolvedThreads.length === 0) return 'No unresolved comments.';
+
+ return unresolvedThreads
+ .map(thread => {
+ const comments = thread.comments.map(
+ c => `${c.author?.name ?? 'Unknown'}:\n${c.message}`
+ );
+ let loc = '';
+ if (thread.line) {
+ loc = `Line ${thread.line}`;
+ } else if (thread.range) {
+ loc = `Lines ${thread.range.start_line}-${thread.range.end_line}`;
+ } else {
+ loc = 'File level';
+ }
+ return `* File: ${thread.path} (${loc})
+${comments.join('\n\n')}`;
+ })
+ .join('\n\n');
+ }
+
private updatePromptContent() {
if (!this.patchContent) {
this.promptContent = '';
@@ -349,6 +391,12 @@
'{{patch}}',
this.patchContent
);
+ if (this.selectedTemplate === 'RESOLVE_COMMENTS') {
+ this.promptContent = this.promptContent.replace(
+ '{{comments}}',
+ this.getUnresolvedCommentsFormatted()
+ );
+ }
// Inserts a space before each capital letter to handle CamelCase
const textWithSpaces = this.promptContent.replace(/([A-Z])/g, ' $1');
diff --git a/polygerrit-ui/app/elements/change/gr-ai-prompt-dialog/gr-ai-prompt-dialog_test.ts b/polygerrit-ui/app/elements/change/gr-ai-prompt-dialog/gr-ai-prompt-dialog_test.ts
index 8165a0f..6ac9971 100644
--- a/polygerrit-ui/app/elements/change/gr-ai-prompt-dialog/gr-ai-prompt-dialog_test.ts
+++ b/polygerrit-ui/app/elements/change/gr-ai-prompt-dialog/gr-ai-prompt-dialog_test.ts
@@ -10,6 +10,9 @@
import {createParsedChange} from '../../../test/test-data-generators';
import {CommitId, PatchSetNum} from '../../../api/rest-api';
import {stubRestApi, waitUntil} from '../../../test/test-utils';
+import {testResolver} from '../../../test/common-test-setup';
+import {commentsModelToken} from '../../../models/comments/comments-model';
+import {of} from 'rxjs';
suite('gr-ai-prompt-dialog test', () => {
let element: GrAiPromptDialog;
@@ -17,6 +20,12 @@
setup(async () => {
getPatchContentStub = stubRestApi('getPatchContent');
getPatchContentStub.resolves('test code');
+ const commentsModel = testResolver(commentsModelToken);
+ Object.defineProperty(commentsModel, 'threads$', {
+ value: of([]),
+ writable: true,
+ });
+
element = await fixture(html`<gr-ai-prompt-dialog></gr-ai-prompt-dialog>`);
element.change = createParsedChange();
element.change.revisions['abc'].commit!.parents = [
@@ -69,6 +78,14 @@
</md-radio>
Just patch content
</label>
+ <label class="template-option">
+ <md-radio
+ name="template"
+ tabindex="-1"
+ >
+ </md-radio>
+ Unresolved Comments
+ </label>
</div>
</div>
<div class="context-selector">
@@ -167,4 +184,42 @@
)
);
});
+ test('renders help review prompt', async () => {
+ element.selectedTemplate = 'HELP_REVIEW';
+ await element.updateComplete;
+ assert.include(
+ (element as any).promptContent,
+ 'You are a highly experienced code reviewer'
+ );
+ });
+
+ test('renders resolve comments prompt', async () => {
+ element.selectedTemplate = 'RESOLVE_COMMENTS';
+ await element.updateComplete;
+ assert.include((element as any).promptContent, 'No unresolved comments.');
+ });
+
+ test('renders resolve comments prompt with comments', async () => {
+ element.threads = [
+ {
+ comments: [
+ {
+ message: 'test comment',
+ author: {name: 'Tester'},
+ updated: '2025-01-01 10:00:00.000000000',
+ unresolved: true,
+ },
+ ],
+ path: 'test.txt',
+ line: 1,
+ rootId: '1',
+ },
+ ] as any[];
+ element.selectedTemplate = 'RESOLVE_COMMENTS';
+ await element.updateComplete;
+ const expected = `* File: test.txt (Line 1)
+Tester:
+test comment`;
+ assert.include((element as any).promptContent, expected);
+ });
});