Fix display issue for code snippets in Gerrit UI

Addressed a bug where code snippets formatted as
"```<programming_language>
...
```" within ChatGPT suggestions were not properly shown in Gerrit UI.

Jira-Id: IT-103
Change-Id: If0be37d82ab9d10fa8659d97a978af264fa72070
Signed-off-by: Patrizio <patrizio.gelosi@amarulasolutions.com>
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/GerritClientComments.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/GerritClientComments.java
index d719bfe..950971b 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/GerritClientComments.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/GerritClientComments.java
@@ -24,6 +24,7 @@
 import java.util.regex.Pattern;
 
 import static com.googlesource.gerrit.plugins.chatgpt.utils.ReviewUtils.getTimeStamp;
+import static com.googlesource.gerrit.plugins.chatgpt.utils.ReviewUtils.processGerritMessage;
 import static java.net.HttpURLConnection.HTTP_OK;
 
 @Slf4j
@@ -127,7 +128,7 @@
         List<String> messages = new ArrayList<>();
         Map<String, List<Map<String, Object>>> comments = new HashMap<>();
         for (HashMap<String, Object> reviewBatch : reviewBatches) {
-            String message = (String) reviewBatch.get("content");
+            String message = processGerritMessage((String) reviewBatch.get("content"));
             if (message.trim().isEmpty()) {
                 log.info("Empty message from post comment not submitted.");
                 continue;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/utils/ReviewUtils.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/utils/ReviewUtils.java
index 09c8575..e2db193 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/utils/ReviewUtils.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/utils/ReviewUtils.java
@@ -6,6 +6,10 @@
 
 public class ReviewUtils {
 
+    public static String processGerritMessage(String message) {
+        return message.replaceAll( "```\\w+|(?<!\\n\\n)```", "\n\n```");
+    }
+
     public static long getTimeStamp(String updatedString) {
         DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSSSSS");
         LocalDateTime updatedDateTime = LocalDateTime.parse(updatedString, formatter);
diff --git a/src/test/resources/__files/chatGptResponseReview.json b/src/test/resources/__files/chatGptResponseReview.json
index d6f9b49..5cf75c0 100644
--- a/src/test/resources/__files/chatGptResponseReview.json
+++ b/src/test/resources/__files/chatGptResponseReview.json
@@ -11,7 +11,7 @@
             "type": "function",
             "function": {
               "name": "format_suggestions",
-              "arguments": "{\n  \"suggestions\": [\n    {\n      \"suggestion\": \"The commit message 'Test Commit Message' is too vague and does not provide information about the specific changes made. A more detailed message is necessary to understand what has been fixed.\"\n    },\n    {\n      \"suggestion\": \"Confirm that the method 'importclass' is meant to change its behavior when 'class_name' is None. The new lines suggest 'class_name' will be derived from the 'module_name' in such cases, which can have unintended effects if not explicitly intended.\",\n      \"filename\": \"test_file.py\",\n      \"lineNumber\": 19,\n      \"codeSnippet\": \"if not class_name:\nmodule_name,class_name=module_name.rsplit('.',1)\"\n    },\n    {\n      \"suggestion\": \"The added check to determine if 'class_name' is None seems to modify the 'module_name' by splitting it and taking the last element. There should be an assignment to 'class_name' since the class to be imported is meant to be the last part of 'module_name' after splitting.\",\n      \"filename\": \"test_file.py\",\n      \"lineNumber\": 20,\n      \"codeSnippet\": \"module_name, class_name = module_name.rsplit('.', 1)\"\n    },\n    {\n      \"suggestion\": \"The code line 'from types import Any, Callable, ...' should use 'typing' for imports instead of 'types'.\",\n      \"filename\": \"test_file.py\",\n      \"lineNumber\": 1,\n      \"codeSnippet\": \"from types import\"\n    },\n    {\n      \"suggestion\": \"There is a typo in the import statement. The correct function should be 'import_module' from the 'importlib' module, not 'importclass' which does not exist.\",\n      \"filename\": \"test_file.py\",\n      \"lineNumber\": 21,\n      \"codeSnippet\": \"loaded_module = importclass(module_name, fromlist=[class_name])\"\n    }\n  ]\n  }"
+              "arguments": "{\n  \"suggestions\": [\n    {\n      \"suggestion\": \"The commit message 'Test Commit Message' is too vague and does not provide information about the specific changes made. A more detailed message is necessary to understand what has been fixed.\"\n    },\n    {\n      \"suggestion\": \"Confirm that the method 'importclass' is meant to change its behavior when 'class_name' is None. The new lines suggest 'class_name' will be derived from the 'module_name' in such cases, which can have unintended effects if not explicitly intended.\",\n      \"filename\": \"test_file.py\",\n      \"lineNumber\": 19,\n      \"codeSnippet\": \"if not class_name:\nmodule_name,class_name=module_name.rsplit('.',1)\"\n    },\n    {\n      \"suggestion\": \"The added check to determine if 'class_name' is None seems to modify the 'module_name' by splitting it and taking the last element. There should be an assignment to 'class_name' since the class to be imported is meant to be the last part of 'module_name' after splitting.\",\n      \"filename\": \"test_file.py\",\n      \"lineNumber\": 20,\n      \"codeSnippet\": \"module_name, class_name = module_name.rsplit('.', 1)\"\n    },\n    {\n      \"suggestion\": \"The code line 'from types import Any, Callable, ...' should use 'typing' for imports instead of 'types'.\",\n      \"filename\": \"test_file.py\",\n      \"lineNumber\": 1,\n      \"codeSnippet\": \"from types import\"\n    },\n    {\n      \"suggestion\": \"There is a typo in the import statement. The correct function should be 'import_module' from the 'importlib' module, not 'importclass' which does not exist. Correct code:\n```python\nloaded_module = import_module(module_name, fromlist=[class_name])```\",\n      \"filename\": \"test_file.py\",\n      \"lineNumber\": 21,\n      \"codeSnippet\": \"loaded_module = importclass(module_name, fromlist=[class_name])\"\n    }\n  ]\n  }"
             }
           }
         ]
diff --git a/src/test/resources/__files/gerritPatchSetReview.json b/src/test/resources/__files/gerritPatchSetReview.json
index fb8384d..e3d50fe 100644
--- a/src/test/resources/__files/gerritPatchSetReview.json
+++ b/src/test/resources/__files/gerritPatchSetReview.json
@@ -1 +1 @@
-{"comments":{"test_file.py":[{"line":19,"range":{"start_line":19,"end_line":20,"start_character":4,"end_character":60},"message":"Confirm that the method \u0027importclass\u0027 is meant to change its behavior when \u0027class_name\u0027 is None. The new lines suggest \u0027class_name\u0027 will be derived from the \u0027module_name\u0027 in such cases, which can have unintended effects if not explicitly intended."},{"line":20,"range":{"start_line":20,"end_line":20,"start_character":8,"end_character":60},"message":"The added check to determine if \u0027class_name\u0027 is None seems to modify the \u0027module_name\u0027 by splitting it and taking the last element. There should be an assignment to \u0027class_name\u0027 since the class to be imported is meant to be the last part of \u0027module_name\u0027 after splitting."},{"line":1,"range":{"start_line":1,"end_line":1,"start_character":0,"end_character":17},"message":"The code line \u0027from types import Any, Callable, ...\u0027 should use \u0027typing\u0027 for imports instead of \u0027types\u0027."},{"line":21,"range":{"start_line":21,"end_line":21,"start_character":4,"end_character":67},"message":"There is a typo in the import statement. The correct function should be \u0027import_module\u0027 from the \u0027importlib\u0027 module, not \u0027importclass\u0027 which does not exist."}]},"message":"The commit message \u0027Test Commit Message\u0027 is too vague and does not provide information about the specific changes made. A more detailed message is necessary to understand what has been fixed."}
\ No newline at end of file
+{"comments":{"test_file.py":[{"line":19,"range":{"start_line":19,"end_line":20,"start_character":4,"end_character":60},"message":"Confirm that the method \u0027importclass\u0027 is meant to change its behavior when \u0027class_name\u0027 is None. The new lines suggest \u0027class_name\u0027 will be derived from the \u0027module_name\u0027 in such cases, which can have unintended effects if not explicitly intended."},{"line":20,"range":{"start_line":20,"end_line":20,"start_character":8,"end_character":60},"message":"The added check to determine if \u0027class_name\u0027 is None seems to modify the \u0027module_name\u0027 by splitting it and taking the last element. There should be an assignment to \u0027class_name\u0027 since the class to be imported is meant to be the last part of \u0027module_name\u0027 after splitting."},{"line":1,"range":{"start_line":1,"end_line":1,"start_character":0,"end_character":17},"message":"The code line \u0027from types import Any, Callable, ...\u0027 should use \u0027typing\u0027 for imports instead of \u0027types\u0027."},{"line":21,"range":{"start_line":21,"end_line":21,"start_character":4,"end_character":67},"message":"There is a typo in the import statement. The correct function should be \u0027import_module\u0027 from the \u0027importlib\u0027 module, not \u0027importclass\u0027 which does not exist. Correct code:\n\n\n```\nloaded_module \u003d import_module(module_name, fromlist\u003d[class_name])\n\n```"}]},"message":"The commit message \u0027Test Commit Message\u0027 is too vague and does not provide information about the specific changes made. A more detailed message is necessary to understand what has been fixed."}
\ No newline at end of file