Welcome to mirror list, hosted at ThFree Co, Russian Federation.

reference_filter.rb « markdown « gitlab « lib - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: a84bacd3d4f744411e029ec16b25150f0c786cb2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
require 'active_support/core_ext/string/output_safety'
require 'html/pipeline/filter'

module Gitlab
  module Markdown
    # Base class for GitLab Flavored Markdown reference filters.
    #
    # References within <pre>, <code>, <a>, and <style> elements are ignored.
    #
    # Context options:
    #   :project (required) - Current project, ignored if reference is cross-project.
    #   :reference_class    - Custom CSS class added to reference links.
    #   :only_path          - Generate path-only links.
    #
    # Results:
    #   :references - A Hash of references that were found and replaced.
    class ReferenceFilter < HTML::Pipeline::Filter
      def initialize(*args)
        super

        result[:references] = Hash.new { |hash, type| hash[type] = [] }
      end

      def escape_once(html)
        ERB::Util.html_escape_once(html)
      end

      def ignore_parents
        @ignore_parents ||= begin
          # Don't look for references in text nodes that are children of these
          # elements.
          parents = %w(pre code a style)
          parents << 'blockquote' if context[:ignore_blockquotes]
          parents.to_set
        end
      end

      def ignored_ancestry?(node)
        has_ancestor?(node, ignore_parents)
      end

      def project
        context[:project]
      end

      # Add a reference to the pipeline's result Hash
      #
      # type   - Singular Symbol reference type (e.g., :issue, :user, etc.)
      # values - One or more Objects to add
      def push_result(type, *values)
        return if values.empty?

        result[:references][type].push(*values)
      end

      def reference_class(type)
        "gfm gfm-#{type} #{context[:reference_class]}".strip
      end

      # Iterate through the document's text nodes, yielding the current node's
      # content if:
      #
      # * The `project` context value is present AND
      # * The node's content matches `pattern` AND
      # * The node is not an ancestor of an ignored node type
      #
      # pattern - Regex pattern against which to match the node's content
      #
      # Yields the current node's String contents. The result of the block will
      # replace the node's existing content and update the current document.
      #
      # Returns the updated Nokogiri::XML::Document object.
      def replace_text_nodes_matching(pattern)
        return doc if project.nil?

        search_text_nodes(doc).each do |node|
          content = node.to_html

          next unless content.match(pattern)
          next if ignored_ancestry?(node)

          html = yield content

          next if html == content

          node.replace(html)
        end

        doc
      end

      # Ensure that a :project key exists in context
      #
      # Note that while the key might exist, its value could be nil!
      def validate
        needs :project
      end
    end
  end
end