Generated suggestion - testing UI when suggestion is in a new range

SuggestedProvider can provide a new suggestion in a new range
different from comment range.

For testing this, we implement simple warning UI. This is useful
for testing of my plugin that implements SuggestedProvider.

This warning will be soon replaced by final solution - where
an user will be able to change range of comment with suggestion.

This is under feature flag UiFeature__ml_suggested_edit
It is still prototype that will be tested. UI is still not final.

Screenshot: https://imgur.com/a/F8RNDtm
Release-Notes: skip
Google-Bug-Id: b/293257977
Change-Id: I3f844e130f9b8632e6131a80d987a2b03c892a81
diff --git a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.ts b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.ts
index 8e0eaf1..1e91345 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.ts
+++ b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.ts
@@ -17,7 +17,6 @@
 import '../gr-suggestion-diff-preview/gr-suggestion-diff-preview';
 import {getAppContext} from '../../../services/app-context';
 import {css, html, LitElement, nothing, PropertyValues} from 'lit';
-import {when} from 'lit/directives/when.js';
 import {customElement, property, query, state} from 'lit/decorators.js';
 import {provide, resolve} from '../../../models/dependency';
 import {GrTextarea} from '../gr-textarea/gr-textarea';
@@ -76,6 +75,7 @@
 } from '../gr-comment-model/gr-comment-model';
 import {formStyles} from '../../../styles/form-styles';
 import {Interaction} from '../../../constants/reporting';
+import {Suggestion} from '../../../api/suggestions';
 
 // visible for testing
 export const AUTO_SAVE_DEBOUNCE_DELAY_MS = 2000;
@@ -208,7 +208,7 @@
   generateSuggestion = true;
 
   @state()
-  generatedReplacement?: string;
+  generatedSuggestion?: Suggestion;
 
   @state()
   generatedReplacementId?: string;
@@ -542,6 +542,14 @@
           color: inherit;
           margin-right: var(--spacing-s);
         }
+        .info {
+          background-color: var(--info-background);
+          padding: var(--spacing-l) var(--spacing-xl);
+        }
+        .info gr-icon {
+          color: var(--selected-foreground);
+          margin-right: var(--spacing-xl);
+        }
       `,
     ];
   }
@@ -572,17 +580,7 @@
             <gr-endpoint-slot name="above-actions"></gr-endpoint-slot>
             ${this.renderHumanActions()} ${this.renderRobotActions()}
           </div>
-          ${when(
-            this.showGeneratedSuggestion() &&
-              this.generateSuggestion &&
-              this.generatedReplacement,
-            () =>
-              html`<gr-suggestion-diff-preview
-                .showAddSuggestionButton=${true}
-                .suggestion=${this.generatedReplacement}
-                .uuid=${this.generatedReplacementId}
-              ></gr-suggestion-diff-preview>`
-          )}
+          ${this.renderGeneratedSuggestionPreview()}
         </div>
       </gr-endpoint-decorator>
       ${this.renderConfirmDialog()}
@@ -932,11 +930,34 @@
     );
   }
 
+  private renderGeneratedSuggestionPreview() {
+    if (
+      !this.showGeneratedSuggestion() ||
+      !this.generateSuggestion ||
+      !this.generatedSuggestion
+    )
+      return nothing;
+    // TODO(milutin): This is temporary warning, will be removed, once we are
+    // able to change range of a comment
+    if (this.generatedSuggestion.newRange) {
+      const range = this.generatedSuggestion.newRange;
+      return html`<div class="info">
+        <gr-icon icon="info" filled></gr-icon>
+        There is a suggestion in range (${range.start_line}, ${range.end_line})
+      </div>`;
+    }
+    return html`<gr-suggestion-diff-preview
+      .showAddSuggestionButton=${true}
+      .suggestion=${this.generatedSuggestion?.replacement}
+      .uuid=${this.generatedReplacementId}
+    ></gr-suggestion-diff-preview>`;
+  }
+
   private renderGenerateSuggestEditButton() {
     if (!this.showGeneratedSuggestion()) {
       return nothing;
     }
-    const numberOfSuggestions = !this.generatedReplacement ? '' : ' (1)';
+    const numberOfSuggestions = !this.generatedSuggestion ? '' : ' (1)';
     return html`
       <div class="action">
         <label>
@@ -947,7 +968,7 @@
             @change=${() => {
               this.generateSuggestion = !this.generateSuggestion;
               if (!this.generateSuggestion) {
-                this.generatedReplacement = undefined;
+                this.generatedSuggestion = undefined;
               } else {
                 this.generateSuggestionTrigger$.next();
               }
@@ -988,21 +1009,28 @@
     this.reporting.reportInteraction(Interaction.GENERATE_SUGGESTION_REQUEST, {
       uuid: this.generatedReplacementId,
     });
-    const suggestion = await suggestionsPlugins[0].provider.suggestCode({
-      prompt: this.messageText,
-      changeNumber: this.changeNum,
-      patchsetNumber: this.comment?.patch_set,
-      filePath: this.comment.path,
-      range: this.comment.range,
-      lineNumber: this.comment.line,
-    });
+    const suggestionResponse = await suggestionsPlugins[0].provider.suggestCode(
+      {
+        prompt: this.messageText,
+        changeNumber: this.changeNum,
+        patchsetNumber: this.comment?.patch_set,
+        filePath: this.comment.path,
+        range: this.comment.range,
+        lineNumber: this.comment.line,
+      }
+    );
+    // TODO(milutin): The suggestionResponse can contain multiple suggestion
+    // options. We pick the first one for now. In future we shouldn't ignore
+    // other suggestions.
     this.reporting.reportInteraction(Interaction.GENERATE_SUGGESTION_RESPONSE, {
       uuid: this.generatedReplacementId,
-      response: suggestion.responseCode,
+      response: suggestionResponse.responseCode,
+      numSuggestions: suggestionResponse.suggestions.length,
+      hasNewRange: suggestionResponse.suggestions?.[0]?.newRange !== undefined,
     });
-    const replacement = suggestion.suggestions?.[0]?.replacement;
-    if (!replacement) return;
-    this.generatedReplacement = replacement;
+    const suggestion = suggestionResponse.suggestions?.[0];
+    if (!suggestion) return;
+    this.generatedSuggestion = suggestion;
   }
 
   private renderRobotActions() {
@@ -1107,7 +1135,7 @@
       if (
         !this.changeNum ||
         !this.comment ||
-        (!hasUserSuggestion(this.comment) && !this.generatedReplacement)
+        (!hasUserSuggestion(this.comment) && !this.generatedSuggestion)
       )
         return;
       (async () => {