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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'lib/banzai')
-rw-r--r--lib/banzai/filter/blockquote_fence_filter.rb6
-rw-r--r--lib/banzai/filter/kroki_filter.rb11
-rw-r--r--lib/banzai/filter/math_filter.rb100
-rw-r--r--lib/banzai/filter/plantuml_filter.rb3
-rw-r--r--lib/banzai/filter/references/reference_filter.rb12
-rw-r--r--lib/banzai/pipeline/markup_pipeline.rb4
6 files changed, 116 insertions, 20 deletions
diff --git a/lib/banzai/filter/blockquote_fence_filter.rb b/lib/banzai/filter/blockquote_fence_filter.rb
index e07cbfe8d85..e5cf20d00df 100644
--- a/lib/banzai/filter/blockquote_fence_filter.rb
+++ b/lib/banzai/filter/blockquote_fence_filter.rb
@@ -6,13 +6,13 @@ module Banzai
REGEX = %r{
#{::Gitlab::Regex.markdown_code_or_html_blocks}
|
- (?=^>>>\ *\n.*\n>>>\ *$)(?:
+ (?=(?<=^\n|\A)>>>\ *\n.*\n>>>\ *(?=\n$|\z))(?:
# Blockquote:
# >>>
# Anything, including code and HTML blocks
# >>>
- ^>>>\ *\n
+ (?<=^\n|\A)>>>\ *\n
(?<quote>
(?:
# Any character that doesn't introduce a code or HTML block
@@ -30,7 +30,7 @@ module Banzai
\g<html>
)+?
)
- \n>>>\ *$
+ \n>>>\ *(?=\n$|\z)
)
}mx.freeze
diff --git a/lib/banzai/filter/kroki_filter.rb b/lib/banzai/filter/kroki_filter.rb
index 845c7f2bc0a..713ff2439fc 100644
--- a/lib/banzai/filter/kroki_filter.rb
+++ b/lib/banzai/filter/kroki_filter.rb
@@ -14,7 +14,10 @@ module Banzai
return doc unless settings.kroki_enabled
diagram_selectors = ::Gitlab::Kroki.formats(settings)
- .map { |diagram_type| %(pre[lang="#{diagram_type}"] > code) }
+ .map do |diagram_type|
+ %(pre[lang="#{diagram_type}"] > code,
+ pre > code[lang="#{diagram_type}"])
+ end
.join(', ')
xpath = Gitlab::Utils::Nokogiri.css_to_xpath(diagram_selectors)
@@ -22,7 +25,7 @@ module Banzai
diagram_format = "svg"
doc.xpath(xpath).each do |node|
- diagram_type = node.parent['lang']
+ diagram_type = node.parent['lang'] || node['lang']
diagram_src = node.content
image_src = create_image_src(diagram_type, diagram_format, diagram_src)
img_tag = Nokogiri::HTML::DocumentFragment.parse(%(<img src="#{image_src}" />))
@@ -33,8 +36,8 @@ module Banzai
img_tag.set_attribute('hidden', '') if lazy_load
img_tag.set_attribute('class', 'js-render-kroki')
- img_tag.set_attribute('data-diagram', node.parent['lang'])
- img_tag.set_attribute('data-diagram-src', "data:text/plain;base64,#{Base64.strict_encode64(node.content)}")
+ img_tag.set_attribute('data-diagram', diagram_type)
+ img_tag.set_attribute('data-diagram-src', "data:text/plain;base64,#{Base64.strict_encode64(diagram_src)}")
node.parent.replace(img_tag)
end
diff --git a/lib/banzai/filter/math_filter.rb b/lib/banzai/filter/math_filter.rb
index 0ac506776be..1ca4b2c89db 100644
--- a/lib/banzai/filter/math_filter.rb
+++ b/lib/banzai/filter/math_filter.rb
@@ -7,7 +7,7 @@ require 'uri'
# - app/assets/javascripts/behaviors/markdown/nodes/code_block.js
module Banzai
module Filter
- # HTML filter that adds class="code math" and removes the dollar sign in $`2+2`$.
+ # HTML filter that implements our math syntax, adding class="code math"
#
class MathFilter < HTML::Pipeline::Filter
CSS_MATH = 'pre.code.language-math'
@@ -15,14 +15,42 @@ module Banzai
CSS_CODE = 'code'
XPATH_CODE = Gitlab::Utils::Nokogiri.css_to_xpath(CSS_CODE).freeze
+ # These are based on the Pandoc heuristics,
+ # https://pandoc.org/MANUAL.html#extension-tex_math_dollars
+ # Note: at this time, using a dollar sign literal, `\$` inside
+ # a math statement does not work correctly.
+ # Corresponds to the "$...$" syntax
+ DOLLAR_INLINE_PATTERN = %r{
+ (?<matched>\$(?<math>(?:\S[^$\n]*?\S|[^$\s]))\$)(?:[^\d]|$)
+ }x.freeze
+
+ # Corresponds to the "$$...$$" syntax
+ DOLLAR_DISPLAY_INLINE_PATTERN = %r{
+ (?<matched>\$\$\ *(?<math>[^$\n]+?)\ *\$\$)
+ }x.freeze
+
+ # Corresponds to the $$\n...\n$$ syntax
+ DOLLAR_DISPLAY_BLOCK_PATTERN = %r{
+ ^(?<matched>\$\$\ *\n(?<math>.*)\n\$\$\ *)$
+ }x.freeze
+
+ # Order dependent. Handle the `$$` syntax before the `$` syntax
+ DOLLAR_MATH_PIPELINE = [
+ { pattern: DOLLAR_DISPLAY_INLINE_PATTERN, tag: :code, style: :display },
+ { pattern: DOLLAR_DISPLAY_BLOCK_PATTERN, tag: :pre, style: :display },
+ { pattern: DOLLAR_INLINE_PATTERN, tag: :code, style: :inline }
+ ].freeze
+
+ # Do not recognize math inside these tags
+ IGNORED_ANCESTOR_TAGS = %w[pre code tt].to_set
+
# Attribute indicating inline or display math.
STYLE_ATTRIBUTE = 'data-math-style'
# Class used for tagging elements that should be rendered
TAG_CLASS = 'js-render-math'
- INLINE_CLASSES = "code math #{TAG_CLASS}"
-
+ MATH_CLASSES = "code math #{TAG_CLASS}"
DOLLAR_SIGN = '$'
# Limit to how many nodes can be marked as math elements.
@@ -31,8 +59,48 @@ module Banzai
RENDER_NODES_LIMIT = 50
def call
- nodes_count = 0
+ @nodes_count = 0
+
+ process_dollar_pipeline if Feature.enabled?(:markdown_dollar_math, group)
+
+ process_dollar_backtick_inline
+ process_math_codeblock
+
+ doc
+ end
+
+ def process_dollar_pipeline
+ doc.xpath('descendant-or-self::text()').each do |node|
+ next if has_ancestor?(node, IGNORED_ANCESTOR_TAGS)
+
+ node_html = node.to_html
+ next unless node_html.match?(DOLLAR_INLINE_PATTERN) ||
+ node_html.match?(DOLLAR_DISPLAY_INLINE_PATTERN) ||
+ node_html.match?(DOLLAR_DISPLAY_BLOCK_PATTERN)
+
+ temp_doc = Nokogiri::HTML.fragment(node_html)
+ DOLLAR_MATH_PIPELINE.each do |pipeline|
+ temp_doc.xpath('child::text()').each do |temp_node|
+ html = temp_node.to_html
+ temp_node.content.scan(pipeline[:pattern]).each do |matched, math|
+ html.sub!(matched, math_html(tag: pipeline[:tag], style: pipeline[:style], math: math))
+ @nodes_count += 1
+ break if @nodes_count >= RENDER_NODES_LIMIT
+ end
+
+ temp_node.replace(html)
+
+ break if @nodes_count >= RENDER_NODES_LIMIT
+ end
+ end
+
+ node.replace(temp_doc)
+ end
+ end
+
+ # Corresponds to the "$`...`$" syntax
+ def process_dollar_backtick_inline
doc.xpath(XPATH_CODE).each do |code|
closing = code.next
opening = code.previous
@@ -44,22 +112,38 @@ module Banzai
closing.content.first == DOLLAR_SIGN &&
opening.content.last == DOLLAR_SIGN
- code[:class] = INLINE_CLASSES
+ code[:class] = MATH_CLASSES
code[STYLE_ATTRIBUTE] = 'inline'
closing.content = closing.content[1..]
opening.content = opening.content[0..-2]
- nodes_count += 1
- break if nodes_count >= RENDER_NODES_LIMIT
+ @nodes_count += 1
+ break if @nodes_count >= RENDER_NODES_LIMIT
end
end
+ end
+ # corresponds to the "```math...```" syntax
+ def process_math_codeblock
doc.xpath(XPATH_MATH).each do |el|
el[STYLE_ATTRIBUTE] = 'display'
el[:class] += " #{TAG_CLASS}"
end
+ end
- doc
+ private
+
+ def math_html(tag:, math:, style:)
+ case tag
+ when :code
+ "<code class=\"#{MATH_CLASSES}\" data-math-style=\"#{style}\">#{math}</code>"
+ when :pre
+ "<pre class=\"#{MATH_CLASSES}\" data-math-style=\"#{style}\"><code>#{math}</code></pre>"
+ end
+ end
+
+ def group
+ context[:group] || context[:project]&.group
end
end
end
diff --git a/lib/banzai/filter/plantuml_filter.rb b/lib/banzai/filter/plantuml_filter.rb
index cbcd547120d..82f6247cf03 100644
--- a/lib/banzai/filter/plantuml_filter.rb
+++ b/lib/banzai/filter/plantuml_filter.rb
@@ -31,7 +31,8 @@ module Banzai
private
def lang_tag
- @lang_tag ||= Gitlab::Utils::Nokogiri.css_to_xpath('pre[lang="plantuml"] > code').freeze
+ @lang_tag ||= Gitlab::Utils::Nokogiri
+ .css_to_xpath('pre[lang="plantuml"] > code, pre > code[lang="plantuml"]').freeze
end
def settings
diff --git a/lib/banzai/filter/references/reference_filter.rb b/lib/banzai/filter/references/reference_filter.rb
index 97ef71036a2..37734f6a45a 100644
--- a/lib/banzai/filter/references/reference_filter.rb
+++ b/lib/banzai/filter/references/reference_filter.rb
@@ -15,6 +15,8 @@ module Banzai
include RequestStoreReferenceCache
include OutputSafety
+ REFERENCE_TYPE_DATA_ATTRIBUTE = 'data-reference-type='
+
class << self
# Implement in child class
# Example: self.reference_type = :merge_request
@@ -132,13 +134,19 @@ module Banzai
def data_attribute(attributes = {})
attributes = attributes.reject { |_, v| v.nil? }
- attributes[:reference_type] ||= self.class.reference_type
+ # "data-reference-type=" attribute got moved into a constant because we need
+ # to use it on ReferenceRewriter class to detect if the markdown contains any reference
+ reference_type_attribute = "#{REFERENCE_TYPE_DATA_ATTRIBUTE}#{escape_once(self.class.reference_type)} "
+
attributes[:container] ||= 'body'
attributes[:placement] ||= 'top'
attributes.delete(:original) if context[:no_original_data]
+
attributes.map do |key, value|
%Q(data-#{key.to_s.dasherize}="#{escape_once(value)}")
- end.join(' ')
+ end
+ .join(' ')
+ .prepend(reference_type_attribute)
end
def ignore_ancestor_query
diff --git a/lib/banzai/pipeline/markup_pipeline.rb b/lib/banzai/pipeline/markup_pipeline.rb
index 17a73f29afb..330914f7238 100644
--- a/lib/banzai/pipeline/markup_pipeline.rb
+++ b/lib/banzai/pipeline/markup_pipeline.rb
@@ -9,8 +9,8 @@ module Banzai
Filter::AssetProxyFilter,
Filter::ExternalLinkFilter,
Filter::PlantumlFilter,
- Filter::SyntaxHighlightFilter,
- Filter::KrokiFilter
+ Filter::KrokiFilter,
+ Filter::SyntaxHighlightFilter
]
end