BigW Consortium Gitlab

reference_rewriter.rb 2.65 KB
Newer Older
1 2 3
module Gitlab
  module Gfm
    ##
4
    # Class that unfolds local references in text.
5
    #
6 7 8 9
    # The initializer takes text in Markdown and project this text is valid
    # in context of.
    #
    # `unfold` method tries to find all local references and unfold each of
10 11 12
    # those local references to cross reference format, assuming that the
    # argument passed to this method is a project that references will be
    # viewed from (see `Referable#to_reference method).
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
    #
    # Examples:
    #
    # 'Hello, this issue is related to #123 and
    #  other issues labeled with ~"label"', will be converted to:
    #
    # 'Hello, this issue is related to gitlab-org/gitlab-ce#123 and
    #  other issue labeled with gitlab-org/gitlab-ce~"label"'.
    #
    # It does respect markdown lexical rules, so text in code block will not be
    # replaced, see another example:
    #
    # 'Merge request for issue #1234, see also link:
    #  http://gitlab.com/some/link/#1234, and code `puts #1234`' =>
    #
    # 'Merge request for issue gitlab-org/gitlab-ce#1234, se also link:
    #  http://gitlab.com/some/link/#1234, and code `puts #1234`'
30
    #
31 32
    class ReferenceRewriter
      def initialize(text, source_project, current_user)
33
        @text = text
34 35 36
        @source_project = source_project
        @current_user = current_user
        @original_html = markdown(text)
37
        @pattern = Gitlab::ReferenceExtractor.references_pattern
38 39
      end

40
      def rewrite(target_project)
41 42
        return @text unless needs_rewrite?

43
        @text.gsub(@pattern) do |reference|
44
          unfold_reference(reference, Regexp.last_match, target_project)
45 46 47
        end
      end

48
      def needs_rewrite?
49
        @text =~ @pattern
50 51
      end

52 53
      private

54
      def unfold_reference(reference, match, target_project)
55
        before = @text[0...match.begin(0)]
56
        after = @text[match.end(0)..-1]
57

58
        referable = find_referable(reference)
59
        return reference unless referable
60

61
        cross_reference = referable.to_reference(target_project)
62
        return reference if reference == cross_reference
63

64
        new_text = before + cross_reference + after
65 66 67
        substitution_valid?(new_text) ? cross_reference : reference
      end

68
      def find_referable(reference)
69 70
        extractor = Gitlab::ReferenceExtractor.new(@source_project,
                                                   @current_user)
71 72
        extractor.analyze(reference)
        extractor.all.first
73 74 75
      end

      def substitution_valid?(substituted)
76
        @original_html == markdown(substituted)
77 78 79
      end

      def markdown(text)
80
        Banzai.render(text, project: @source_project, no_original_data: true)
81 82 83 84
      end
    end
  end
end