Repository: reviewdog/reviewdog
Pull request #1022 (2021-08-07 23:14:33)

Improve gitlab suggestion
I implemented this https://github.com/reviewdog/reviewdog/pull/1017#issuecomment-894608528

> Thank you for your review.
> 
> > Can you create a helper function to get # of backticks for suggestions considering there are duplicated code?
> 
> I created it in the commentutil package.
> Because we might need same fix with Gitlab suggestions in merge request comments(#1012).
> I think using quadruple backticks(`` ```` ``) is very very rarely, but linters can.
> I'm going to do it in another pull request.
> 

- [ ] Updated Unreleased section in [CHANGELOG](https://github.com/reviewdog/reviewdog/blob/master/CHANGELOG.md) or it's not notable changes.




Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a16ad00e..09dcd241 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 ### :sparkles: Release Note <!-- optional -->
 
 ### :rocket: Enhancements
-- ...
+- [#1012](https://github.com/reviewdog/reviewdog/pull/1012) Use GitLab suggestions in merge request comments 
 
 ### :bug: Fixes
 - [#1014](https://github.com/reviewdog/reviewdog/pull/1014) Fix incorrect detection of the `GITHUB_TOKEN` permissions. fixes [#1010](https://github.com/reviewdog/reviewdog/issues/1010)
diff --git a/service/gitlab/gitlab_mr_discussion.go b/service/gitlab/gitlab_mr_discussion.go
index de7a11fb..676d021f 100644
--- a/service/gitlab/gitlab_mr_discussion.go
+++ b/service/gitlab/gitlab_mr_discussion.go
@@ -4,6 +4,7 @@ import (
 	"context"
 	"fmt"
 	"path/filepath"
+	"strconv"
 	"strings"
 	"sync"
 
@@ -189,16 +190,27 @@ func buildSuggestions(c *reviewdog.Comment) string {
 func buildSingleSuggestion(c *reviewdog.Comment, s *rdf.Suggestion) (string, error) {
 	var sb strings.Builder
 
-	// user 4 backticks for safety: https://docs.gitlab.com/ee/user/project/merge_requests/reviews/suggestions.html#code-block-nested-in-suggestions
-	sb.WriteString("````suggestion:-0+")
-	sb.WriteString(fmt.Sprintf("%d", s.Range.End.Line-s.Range.Start.Line))
+	// we might need to use 4 or more backticks
+	//
+	// https://docs.gitlab.com/ee/user/project/merge_requests/reviews/suggestions.html#code-block-nested-in-suggestions
+	// > If you need to make a suggestion that involves a fenced code block, wrap your suggestion in four backticks instead of the usual three.
+	//
+	// The documentation doesn't explicitly say anything about cases more than 4 backticks,
+	// however it seems to be handled as intended.
+	txt := s.GetText()
+	backticks := commentutil.GetCodeFenceLength(txt)
+
+	lines := strconv.Itoa(int(s.Range.End.Line - s.Range.Start.Line))
+	sb.Grow(backticks + len("suggestion:-0+\n") + len(lines) + len(txt) + len("\n") + backticks)
+	commentutil.WriteCodeFence(&sb, backticks)
+	sb.WriteString("suggestion:-0+")
+	sb.WriteString(lines)
 	sb.WriteString("\n")
-
-	if txt := s.GetText(); txt != "" {
+	if txt != "" {
 		sb.WriteString(txt)
 		sb.WriteString("\n")
 	}
+	commentutil.WriteCodeFence(&sb, backticks)
 
-	sb.WriteString("````")
 	return sb.String(), nil
 }


Test patch:
diff --git a/service/gitlab/gitlab_mr_discussion_test.go b/service/gitlab/gitlab_mr_discussion_test.go
index 3b7fa028..f70ca5bc 100644
--- a/service/gitlab/gitlab_mr_discussion_test.go
+++ b/service/gitlab/gitlab_mr_discussion_test.go
@@ -333,10 +333,10 @@ func TestBuildSuggestions(t *testing.T) {
 				},
 			),
 			want: strings.Join([]string{
-				"````suggestion:-0+0",
+				"```suggestion:-0+0",
 				"line1-fixed",
 				"line2-fixed",
-				"````",
+				"```",
 				"",
 			}, "\n"),
 		},
@@ -349,13 +349,29 @@ func TestBuildSuggestions(t *testing.T) {
 				},
 			),
 			want: strings.Join([]string{
-				"````suggestion:-0+1",
+				"```suggestion:-0+1",
 				"line1-fixed",
 				"line2-fixed",
-				"````",
-				"````suggestion:-0+1",
+				"```",
+				"```suggestion:-0+1",
 				"line3-fixed",
 				"line4-fixed",
+				"```",
+				"",
+			}, "\n"),
+		},
+		{
+			in: buildTestComment(
+				"a suggestion that has fenced code block",
+				[]*rdf.Suggestion{
+					buildTestsSuggestion("```shell\ngit config --global receive.advertisepushoptions true\n```", 10, 12),
+				},
+			),
+			want: strings.Join([]string{
+				"````suggestion:-0+2",
+				"```shell",
+				"git config --global receive.advertisepushoptions true",
+				"```",
 				"````",
 				"",
 			}, "\n"),
@@ -385,10 +401,10 @@ func TestBuildSuggestionsInvalid(t *testing.T) {
 				},
 			),
 			want: strings.Join([]string{
-				"````suggestion:-0+1",
+				"```suggestion:-0+1",
 				"line1-fixed",
 				"line2-fixed",
-				"````",
+				"```",
 				"",
 			}, "\n"),
 		},
@@ -407,10 +423,10 @@ func TestBuildSuggestionsInvalid(t *testing.T) {
 					buildTestsSuggestion("line1-fixed\nline2-fixed", 10, 11),
 				}),
 			want: strings.Join([]string{
-				"````suggestion:-0+1",
+				"```suggestion:-0+1",
 				"line1-fixed",
 				"line2-fixed",
-				"````",
+				"```",
 				"",
 			}, "\n"),
 		},