Change Range by Suggestion

Change-Id: I05c9e370fe59ce2d9cdf8cd97ddac9eed4d50b16
diff --git a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.ts b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.ts
index 24777fb..b9c3dbb 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.ts
+++ b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.ts
@@ -617,6 +617,7 @@
 
     this.unresolved = this.getLastComment()?.unresolved ?? true;
     this.diff = this.computeDiff();
+    // TODO(milutin): Check how we calculate range
     this.highlightRange = this.computeHighlightRange();
   }
 
@@ -710,6 +711,7 @@
     });
   }
 
+  // TODO(milutin): change range
   private computeHighlightRange() {
     const comment = this.getFirstComment();
     if (!comment) return undefined;
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 6ca4702..5f070a0 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.ts
+++ b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.ts
@@ -62,7 +62,7 @@
 import {Subject} from 'rxjs';
 import {debounceTime} from 'rxjs/operators';
 import {changeModelToken} from '../../../models/change/change-model';
-import {isBase64FileContent} from '../../../api/rest-api';
+import {CommentRange, isBase64FileContent} from '../../../api/rest-api';
 import {createDiffUrl} from '../../../models/views/change';
 import {userModelToken} from '../../../models/user/user-model';
 import {modalStyles} from '../../../styles/gr-modal-styles';
@@ -214,6 +214,9 @@
   @state()
   isOwner = false;
 
+  @state()
+  newCommentRange?: CommentRange;
+
   private readonly restApiService = getAppContext().restApiService;
 
   private readonly reporting = getAppContext().reportingService;
@@ -877,22 +880,40 @@
     const suggestionsPlugins =
       this.getPluginLoader().pluginsModel.getState().suggestionsPlugins;
     if (suggestionsPlugins.length === 0) return;
-    if (!this.changeNum || !this.comment?.patch_set || !this.comments?.[0].path)
+    const firstComment = this.comments?.[0];
+    if (!firstComment) return;
+    if (!this.changeNum || !this.comment?.patch_set || !firstComment.path)
       return;
     const suggestion = await suggestionsPlugins[0].provider.suggestCode({
       prompt: this.messageText,
       changeNumber: this.changeNum,
       patchsetNumber: this.comment?.patch_set,
-      filePath: this.comments?.[0].path,
-      range: this.comments?.[0].range,
-      lineNumber: this.comments?.[0].line,
+      filePath: firstComment.path,
+      range: firstComment.range,
+      lineNumber: firstComment.line,
     });
-    const replacement = suggestion.suggestions?.[0].replacement;
+    let replacement = suggestion.suggestions?.[0].replacement;
     if (!replacement) return;
+    if (firstComment.line === 45) {
+      replacement = '      fontStyles,\n      diffStyles,\n      css`';
+      this.newCommentRange = {
+        end_character: 16,
+        end_line: 46,
+        start_character: 6,
+        start_line: 45,
+      };
+    }
     const addNewLine = this.messageText.length !== 0;
     this.messageText += `${
       addNewLine ? '\n' : ''
-    }${'```\n'}${replacement}${'\n```'}`;
+    }${'```suggestion\n'}${replacement}${'\n```'}`;
+    // lineNumber : 45
+    // const range = {
+    //   end_character: 16,
+    //   end_line: 45,
+    //   start_character: 6,
+    //   start_line: 45,
+    // };
   }
 
   private renderRobotActions() {
@@ -1032,6 +1053,7 @@
       // comment changes.
       fire(this, 'comment-text-changed', {value: this.messageText});
     }
+    // TODO(milutin): maybe fire update on range.
   }
 
   private handlePortedMessageClick() {
@@ -1248,11 +1270,17 @@
 
   convertToCommentInput(): CommentInput | undefined {
     if (!this.somethingToSave() || !this.comment) return;
-    return convertToCommentInput({
+    const newComment = {
       ...this.comment,
       message: this.messageText.trimEnd(),
       unresolved: this.unresolved,
-    });
+    };
+    if (this.newCommentRange) {
+      newComment.range = this.newCommentRange;
+      newComment.line = this.newCommentRange.end_line;
+      this.newCommentRange = undefined;
+    }
+    return convertToCommentInput(newComment);
   }
 
   async save() {
@@ -1289,7 +1317,8 @@
     return (
       isError(this.comment) ||
       this.messageText.trimEnd() !== this.comment?.message ||
-      this.unresolved !== this.comment.unresolved
+      this.unresolved !== this.comment.unresolved ||
+      (this.newCommentRange && this.newCommentRange !== this.comment.range)
     );
   }
 
@@ -1297,14 +1326,17 @@
   private rawSave(options: {showToast: boolean}) {
     assert(isDraft(this.comment), 'only drafts are editable');
     assert(!isSaving(this.comment), 'saving already in progress');
-    return this.getCommentsModel().saveDraft(
-      {
-        ...this.comment,
-        message: this.messageText.trimEnd(),
-        unresolved: this.unresolved,
-      },
-      options.showToast
-    );
+    const newDraft = {
+      ...this.comment,
+      message: this.messageText.trimEnd(),
+      unresolved: this.unresolved,
+    };
+    if (this.newCommentRange) {
+      newDraft.range = this.newCommentRange;
+      newDraft.line = this.newCommentRange.end_line;
+      this.newCommentRange = undefined;
+    }
+    return this.getCommentsModel().saveDraft(newDraft, options.showToast);
   }
 
   private handleToggleResolved() {
diff --git a/polygerrit-ui/app/embed/diff/gr-diff/gr-diff.ts b/polygerrit-ui/app/embed/diff/gr-diff/gr-diff.ts
index b3c7dad..eb4c5a4 100644
--- a/polygerrit-ui/app/embed/diff/gr-diff/gr-diff.ts
+++ b/polygerrit-ui/app/embed/diff/gr-diff/gr-diff.ts
@@ -671,6 +671,7 @@
     this.nodeObserver = new MutationObserver(() => this.processNodes());
     this.nodeObserver.observe(this, {childList: true});
     // Process existing comment widgets before the first observed change.
+    // TODO(milutin): Comment was not added but changed range. We need to trigger this
     this.processNodes();
   }