1# frozen_string_literal: true 2 3# This class replaces Github markdown suggestion tag with 4# a Gitlab suggestion tag. The difference between 5# Github's and Gitlab's suggestion tags is that Gitlab 6# includes the range of the suggestion in the tag, while Github 7# uses other note attributes to position the suggestion. 8module Gitlab 9 module GithubImport 10 module Representation 11 module DiffNotes 12 class SuggestionFormatter 13 include Gitlab::Utils::StrongMemoize 14 15 # A github suggestion: 16 # - the ```suggestion tag must be the first text of the line 17 # - it might have up to 3 spaces before the ```suggestion tag 18 # - extra text on the ```suggestion tag line will be ignored 19 GITHUB_SUGGESTION = /^\ {,3}(?<suggestion>```suggestion\b).*(?<eol>\R)/.freeze 20 21 def initialize(note:, start_line: nil, end_line: nil) 22 @note = note 23 @start_line = start_line 24 @end_line = end_line 25 end 26 27 # Returns a tuple with: 28 # - a boolean indicating if the note has suggestions 29 # - the note with the suggestion formatted for Gitlab 30 def formatted_note 31 @formatted_note ||= 32 if contains_suggestion? 33 note.gsub( 34 GITHUB_SUGGESTION, 35 "\\k<suggestion>:#{suggestion_range}\\k<eol>" 36 ) 37 else 38 note 39 end 40 end 41 42 def contains_suggestion? 43 strong_memoize(:contain_suggestion) do 44 note.to_s.match?(GITHUB_SUGGESTION) 45 end 46 end 47 48 private 49 50 attr_reader :note, :start_line, :end_line 51 52 # Github always saves the comment on the _last_ line of the range. 53 # Therefore, the diff hunk will always be related to lines before 54 # the comment itself. 55 def suggestion_range 56 "-#{line_count}+0" 57 end 58 59 def line_count 60 if start_line.present? 61 end_line - start_line 62 else 63 0 64 end 65 end 66 end 67 end 68 end 69 end 70end 71