From 6a477b9bfc5bf9b32c9a961269066694d1216dce Mon Sep 17 00:00:00 2001
From: Douwe Maan <douwe@selenight.nl>
Date: Wed, 27 Apr 2016 23:38:33 +0200
Subject: [PATCH] Add blockquote fence syntax to Markdown

---
 doc/markdown/markdown.md                      |  34 ++++-
 lib/banzai/filter/blockquote_fence_filter.rb  |  50 +++++++
 lib/banzai/pipeline/pre_process_pipeline.rb   |   3 +-
 spec/fixtures/blockquote_fence_after.md       | 115 +++++++++++++++
 spec/fixtures/blockquote_fence_before.md      | 131 ++++++++++++++++++
 .../filter/blockquote_fence_filter_spec.rb    |  14 ++
 6 files changed, 345 insertions(+), 2 deletions(-)
 create mode 100644 lib/banzai/filter/blockquote_fence_filter.rb
 create mode 100644 spec/fixtures/blockquote_fence_after.md
 create mode 100644 spec/fixtures/blockquote_fence_before.md
 create mode 100644 spec/lib/banzai/filter/blockquote_fence_filter_spec.rb

diff --git a/doc/markdown/markdown.md b/doc/markdown/markdown.md
index 236eb7b12c4..fb2dd582754 100644
--- a/doc/markdown/markdown.md
+++ b/doc/markdown/markdown.md
@@ -7,11 +7,12 @@
 * [Newlines](#newlines)
 * [Multiple underscores in words](#multiple-underscores-in-words)
 * [URL auto-linking](#url-auto-linking)
+* [Multiline Blockquote](#multiline-blockquote)
 * [Code and Syntax Highlighting](#code-and-syntax-highlighting)
 * [Inline Diff](#inline-diff)
 * [Emoji](#emoji)
 * [Special GitLab references](#special-gitlab-references)
-* [Task lists](#task-lists)
+* [Task Lists](#task-lists)
 
 **[Standard Markdown](#standard-markdown)**
 
@@ -89,6 +90,37 @@ GFM will autolink almost any URL you copy and paste into your text.
 * irc://irc.freenode.net/gitlab
 * http://localhost:3000
 
+## Multiline Blockquote
+
+On top of standard Markdown [blockquotes](#blockquotes), which require prepending `>` to quoted lines,
+GFM supports multiline blockquotes fenced by <code>>>></code>.
+
+```no-highlight
+>>>
+If you paste a message from somewhere else
+
+that
+
+spans
+
+multiple lines,
+
+you can quote that without having to manually prepend `>` to every line!
+>>>
+```
+
+>>>
+If you paste a message from somewhere else
+
+that
+
+spans
+
+multiple lines,
+
+you can quote that without having to manually prepend `>` to every line!
+>>>
+
 ## Code and Syntax Highlighting
 
 _GitLab uses the [Rouge Ruby library][rouge] for syntax highlighting. For a
diff --git a/lib/banzai/filter/blockquote_fence_filter.rb b/lib/banzai/filter/blockquote_fence_filter.rb
new file mode 100644
index 00000000000..fb815c2d837
--- /dev/null
+++ b/lib/banzai/filter/blockquote_fence_filter.rb
@@ -0,0 +1,50 @@
+module Banzai
+  module Filter
+    class BlockquoteFenceFilter < HTML::Pipeline::TextFilter
+      REGEX = %r{
+          (?<code>
+            # Code blocks:
+            # ```
+            # Anything, including ignored `>>>` blocks
+            # ```
+            ^```.+?\n```$
+          )
+        |
+          (?<html>
+            # HTML:
+            # <tag>
+            # Anything, including ignored `>>>` blocks
+            # </tag>
+            ^<[^>]+?>.+?\n<\/[^>]+?>$
+          )
+        |
+          (
+            ^>>>\n(?<quote>
+              (?:
+                  (?!^```|^<[^>]+?>).
+                |
+                  \g<code>
+                |
+                  \g<html>
+              )
+            +?)\n>>>$
+          )
+      }mx.freeze
+
+      def initialize(text, context = nil, result = nil)
+        super text, context, result
+        @text = @text.delete "\r"
+      end
+
+      def call
+        @text.gsub(REGEX) do
+          if $~[:quote]
+            $~[:quote].gsub(/^/, "> ").gsub(/^> $/, ">")
+          else
+            $~[0]
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/lib/banzai/pipeline/pre_process_pipeline.rb b/lib/banzai/pipeline/pre_process_pipeline.rb
index 50dc978b452..6cf219661d3 100644
--- a/lib/banzai/pipeline/pre_process_pipeline.rb
+++ b/lib/banzai/pipeline/pre_process_pipeline.rb
@@ -3,7 +3,8 @@ module Banzai
     class PreProcessPipeline < BasePipeline
       def self.filters
         FilterArray[
-          Filter::YamlFrontMatterFilter
+          Filter::YamlFrontMatterFilter,
+          Filter::BlockquoteFenceFilter,
         ]
       end
 
diff --git a/spec/fixtures/blockquote_fence_after.md b/spec/fixtures/blockquote_fence_after.md
new file mode 100644
index 00000000000..5ab136f76c3
--- /dev/null
+++ b/spec/fixtures/blockquote_fence_after.md
@@ -0,0 +1,115 @@
+Single `>>>` inside code block:
+
+```
+# Code
+>>>
+# Code
+```
+
+Double `>>>` inside code block:
+
+```
+# Code
+>>>
+# Code
+>>>
+# Code
+```
+
+Blockquote outside code block:
+
+> Quote
+
+Code block inside blockquote:
+
+> Quote
+>
+> ```
+> # Code
+> ```
+>
+> Quote
+
+Single `>>>` inside code block inside blockquote:
+
+> Quote
+>
+> ```
+> # Code
+> >>>
+> # Code
+> ```
+>
+> Quote
+
+Double `>>>` inside code block inside blockquote:
+
+> Quote
+>
+> ```
+> # Code
+> >>>
+> # Code
+> >>>
+> # Code
+> ```
+>
+> Quote
+
+Single `>>>` inside HTML:
+
+<pre>
+# Code
+>>>
+# Code
+</pre>
+
+Double `>>>` inside HTML:
+
+<pre>
+# Code
+>>>
+# Code
+>>>
+# Code
+</pre>
+
+Blockquote outside HTML:
+
+> Quote
+
+HTML inside blockquote:
+
+> Quote
+>
+> <pre>
+> # Code
+> </pre>
+>
+> Quote
+
+Single `>>>` inside HTML inside blockquote:
+
+> Quote
+>
+> <pre>
+> # Code
+> >>>
+> # Code
+> </pre>
+>
+> Quote
+
+Double `>>>` inside HTML inside blockquote:
+
+> Quote
+>
+> <pre>
+> # Code
+> >>>
+> # Code
+> >>>
+> # Code
+> </pre>
+>
+> Quote
diff --git a/spec/fixtures/blockquote_fence_before.md b/spec/fixtures/blockquote_fence_before.md
new file mode 100644
index 00000000000..e6689b6c5dd
--- /dev/null
+++ b/spec/fixtures/blockquote_fence_before.md
@@ -0,0 +1,131 @@
+Single `>>>` inside code block:
+
+```
+# Code
+>>>
+# Code
+```
+
+Double `>>>` inside code block:
+
+```
+# Code
+>>>
+# Code
+>>>
+# Code
+```
+
+Blockquote outside code block:
+
+>>>
+Quote
+>>>
+
+Code block inside blockquote:
+
+>>>
+Quote
+
+```
+# Code
+```
+
+Quote
+>>>
+
+Single `>>>` inside code block inside blockquote:
+
+>>>
+Quote
+
+```
+# Code
+>>>
+# Code
+```
+
+Quote
+>>>
+
+Double `>>>` inside code block inside blockquote:
+
+>>>
+Quote
+
+```
+# Code
+>>>
+# Code
+>>>
+# Code
+```
+
+Quote
+>>>
+
+Single `>>>` inside HTML:
+
+<pre>
+# Code
+>>>
+# Code
+</pre>
+
+Double `>>>` inside HTML:
+
+<pre>
+# Code
+>>>
+# Code
+>>>
+# Code
+</pre>
+
+Blockquote outside HTML:
+
+>>>
+Quote
+>>>
+
+HTML inside blockquote:
+
+>>>
+Quote
+
+<pre>
+# Code
+</pre>
+
+Quote
+>>>
+
+Single `>>>` inside HTML inside blockquote:
+
+>>>
+Quote
+
+<pre>
+# Code
+>>>
+# Code
+</pre>
+
+Quote
+>>>
+
+Double `>>>` inside HTML inside blockquote:
+
+>>>
+Quote
+
+<pre>
+# Code
+>>>
+# Code
+>>>
+# Code
+</pre>
+
+Quote
+>>>
diff --git a/spec/lib/banzai/filter/blockquote_fence_filter_spec.rb b/spec/lib/banzai/filter/blockquote_fence_filter_spec.rb
new file mode 100644
index 00000000000..19543bde838
--- /dev/null
+++ b/spec/lib/banzai/filter/blockquote_fence_filter_spec.rb
@@ -0,0 +1,14 @@
+require 'rails_helper'
+
+describe Banzai::Filter::BlockquoteFenceFilter, lib: true do
+  include FilterSpecHelper
+
+  it 'convers blockquote fences to blockquote lines' do
+    content = File.read(Rails.root.join('spec/fixtures/blockquote_fence_before.md'))
+    expected = File.read(Rails.root.join('spec/fixtures/blockquote_fence_after.md'))
+
+    output = filter(content)
+
+    expect(output).to eq(expected)
+  end
+end
-- 
GitLab