summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/main.js1
-rw-r--r--app/assets/javascripts/render_gfm.js3
-rw-r--r--app/assets/javascripts/render_mermaid.js30
-rw-r--r--changelogs/unreleased/feature_add_mermaid.yml5
-rw-r--r--config/initializers/math_lexer.rb2
-rw-r--r--config/initializers/plantuml_lexer.rb2
-rw-r--r--doc/user/markdown.md32
-rw-r--r--lib/banzai/filter/mermaid_filter.rb20
-rw-r--r--lib/banzai/filter/syntax_highlight_filter.rb35
-rw-r--r--lib/banzai/pipeline/gfm_pipeline.rb1
-rw-r--r--lib/rouge/lexers/math.rb9
-rw-r--r--lib/rouge/lexers/plantuml.rb9
-rw-r--r--package.json1
-rw-r--r--spec/features/markdown_spec.rb6
-rw-r--r--spec/fixtures/markdown.md.erb34
-rw-r--r--spec/javascripts/notes_spec.js3
-rw-r--r--spec/lib/banzai/filter/mermaid_filter_spec.rb12
-rw-r--r--spec/lib/banzai/filter/syntax_highlight_filter_spec.rb2
-rw-r--r--yarn.lock45
19 files changed, 211 insertions, 41 deletions
diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js
index b7ef1ecd923..d908452399c 100644
--- a/app/assets/javascripts/main.js
+++ b/app/assets/javascripts/main.js
@@ -70,6 +70,7 @@ import './projects_dropdown';
import './projects_list';
import './syntax_highlight';
import './render_math';
+import './render_mermaid';
import './render_gfm';
import './right_sidebar';
import './search';
diff --git a/app/assets/javascripts/render_gfm.js b/app/assets/javascripts/render_gfm.js
index bcdc0fd67b8..bf6fc0ec305 100644
--- a/app/assets/javascripts/render_gfm.js
+++ b/app/assets/javascripts/render_gfm.js
@@ -2,12 +2,13 @@
// Render Gitlab flavoured Markdown
//
-// Delegates to syntax highlight and render math
+// Delegates to syntax highlight and render math & mermaid diagrams.
//
(function() {
$.fn.renderGFM = function() {
this.find('.js-syntax-highlight').syntaxHighlight();
this.find('.js-render-math').renderMath();
+ this.find('.js-render-mermaid').renderMermaid();
return this;
};
diff --git a/app/assets/javascripts/render_mermaid.js b/app/assets/javascripts/render_mermaid.js
new file mode 100644
index 00000000000..a253601f8e8
--- /dev/null
+++ b/app/assets/javascripts/render_mermaid.js
@@ -0,0 +1,30 @@
+// Renders diagrams and flowcharts from text using Mermaid in any element with the
+// `js-render-mermaid` class.
+//
+// Example markup:
+//
+// <pre class="js-render-mermaid">
+// graph TD;
+// A-- > B;
+// A-- > C;
+// B-- > D;
+// C-- > D;
+// </pre>
+//
+
+import Flash from './flash';
+
+$.fn.renderMermaid = function renderMermaid() {
+ if (this.length === 0) return;
+
+ import(/* webpackChunkName: 'mermaid' */ 'blackst0ne-mermaid').then((mermaid) => {
+ mermaid.initialize({
+ loadOnStart: false,
+ theme: 'neutral',
+ });
+
+ mermaid.init(undefined, this);
+ }).catch((err) => {
+ Flash(`Can't load mermaid module: ${err}`);
+ });
+};
diff --git a/changelogs/unreleased/feature_add_mermaid.yml b/changelogs/unreleased/feature_add_mermaid.yml
new file mode 100644
index 00000000000..caeb5d3470d
--- /dev/null
+++ b/changelogs/unreleased/feature_add_mermaid.yml
@@ -0,0 +1,5 @@
+---
+title: 'Add support of Mermaid (generation of diagrams and flowcharts from text)'
+merge_request: 15107
+author: Vitaliy @blackst0ne Klachkov
+type: added
diff --git a/config/initializers/math_lexer.rb b/config/initializers/math_lexer.rb
deleted file mode 100644
index 8a3388a267e..00000000000
--- a/config/initializers/math_lexer.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-# Touch the lexers so it is registered with Rouge
-Rouge::Lexers::Math
diff --git a/config/initializers/plantuml_lexer.rb b/config/initializers/plantuml_lexer.rb
deleted file mode 100644
index e8a77b146fa..00000000000
--- a/config/initializers/plantuml_lexer.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-# Touch the lexers so it is registered with Rouge
-Rouge::Lexers::Plantuml
diff --git a/doc/user/markdown.md b/doc/user/markdown.md
index 454988b9b80..fb61e360996 100644
--- a/doc/user/markdown.md
+++ b/doc/user/markdown.md
@@ -368,6 +368,37 @@ _Be advised that KaTeX only supports a [subset][katex-subset] of LaTeX._
>**Note:**
This also works for the asciidoctor `:stem: latexmath`. For details see the [asciidoctor user manual][asciidoctor-manual].
+### Mermaid
+
+> If this is not rendered correctly, see
+https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#mermaid
+
+It is possible to generate diagrams and flowcharts from text using [Mermaid][mermaid].
+
+In order to generate a diagram or flowchart, you should write your text inside the `mermaid` block.
+
+Example:
+
+ ```mermaid
+ graph TD;
+ A-->B;
+ A-->C;
+ B-->D;
+ C-->D;
+ ```
+
+Becomes:
+
+```mermaid
+graph TD;
+ A-->B;
+ A-->C;
+ B-->D;
+ C-->D;
+```
+
+For details see the [Mermaid official page][mermaid].
+
## Standard Markdown
### Headers
@@ -814,6 +845,7 @@ A link starting with a `/` is relative to the wiki root.
[^2]: This is my awesome footnote.
[markdown.md]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md
+[mermaid]: https://mermaidjs.github.io/ "Mermaid website"
[rouge]: http://rouge.jneen.net/ "Rouge website"
[redcarpet]: https://github.com/vmg/redcarpet "Redcarpet website"
[katex]: https://github.com/Khan/KaTeX "KaTeX website"
diff --git a/lib/banzai/filter/mermaid_filter.rb b/lib/banzai/filter/mermaid_filter.rb
new file mode 100644
index 00000000000..b545b947a2c
--- /dev/null
+++ b/lib/banzai/filter/mermaid_filter.rb
@@ -0,0 +1,20 @@
+module Banzai
+ module Filter
+ class MermaidFilter < HTML::Pipeline::Filter
+ def call
+ doc.css('pre[lang="mermaid"]').add_class('mermaid')
+ doc.css('pre[lang="mermaid"]').add_class('js-render-mermaid')
+
+ # The `<code></code>` blocks are added in the lib/banzai/filter/syntax_highlight_filter.rb
+ # We want to keep context and consistency, so we the blocks are added for all filters.
+ # Details: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/15107/diffs?diff_id=7962900#note_45495859
+ doc.css('pre[lang="mermaid"]').each do |pre|
+ document = pre.at('code')
+ document.replace(document.content)
+ end
+
+ doc
+ end
+ end
+ end
+end
diff --git a/lib/banzai/filter/syntax_highlight_filter.rb b/lib/banzai/filter/syntax_highlight_filter.rb
index 7da565043d1..a79a0154846 100644
--- a/lib/banzai/filter/syntax_highlight_filter.rb
+++ b/lib/banzai/filter/syntax_highlight_filter.rb
@@ -14,23 +14,26 @@ module Banzai
end
def highlight_node(node)
- language = node.attr('lang')
code = node.text
- css_classes = "code highlight"
- lexer = lexer_for(language)
- lang = lexer.tag
-
- begin
- code = Rouge::Formatters::HTMLGitlab.format(lex(lexer, code), tag: lang)
-
- css_classes << " js-syntax-highlight #{lang}"
- rescue
- lang = nil
- # Gracefully handle syntax highlighter bugs/errors to ensure
- # users can still access an issue/comment/etc.
+ css_classes = 'code highlight js-syntax-highlight'
+ language = node.attr('lang')
+
+ if use_rouge?(language)
+ lexer = lexer_for(language)
+ language = lexer.tag
+
+ begin
+ code = Rouge::Formatters::HTMLGitlab.format(lex(lexer, code), tag: language)
+ css_classes << " #{language}"
+ rescue
+ # Gracefully handle syntax highlighter bugs/errors to ensure
+ # users can still access an issue/comment/etc.
+
+ language = nil
+ end
end
- highlighted = %(<pre class="#{css_classes}" lang="#{lang}" v-pre="true"><code>#{code}</code></pre>)
+ highlighted = %(<pre class="#{css_classes}" lang="#{language}" v-pre="true"><code>#{code}</code></pre>)
# Extracted to a method to measure it
replace_parent_pre_element(node, highlighted)
@@ -51,6 +54,10 @@ module Banzai
# Replace the parent `pre` element with the entire highlighted block
node.parent.replace(highlighted)
end
+
+ def use_rouge?(language)
+ %w(math mermaid plantuml).exclude?(language)
+ end
end
end
end
diff --git a/lib/banzai/pipeline/gfm_pipeline.rb b/lib/banzai/pipeline/gfm_pipeline.rb
index 3208abfc538..55874ad50a3 100644
--- a/lib/banzai/pipeline/gfm_pipeline.rb
+++ b/lib/banzai/pipeline/gfm_pipeline.rb
@@ -14,6 +14,7 @@ module Banzai
Filter::SyntaxHighlightFilter,
Filter::MathFilter,
+ Filter::MermaidFilter,
Filter::UploadLinkFilter,
Filter::VideoLinkFilter,
Filter::ImageLazyLoadFilter,
diff --git a/lib/rouge/lexers/math.rb b/lib/rouge/lexers/math.rb
deleted file mode 100644
index 939b23a3421..00000000000
--- a/lib/rouge/lexers/math.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-module Rouge
- module Lexers
- class Math < PlainText
- title "A passthrough lexer used for LaTeX input"
- desc "PLEASE REFACTOR - this should be handled by SyntaxHighlightFilter"
- tag 'math'
- end
- end
-end
diff --git a/lib/rouge/lexers/plantuml.rb b/lib/rouge/lexers/plantuml.rb
deleted file mode 100644
index 63c461764fc..00000000000
--- a/lib/rouge/lexers/plantuml.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-module Rouge
- module Lexers
- class Plantuml < PlainText
- title "A passthrough lexer used for PlantUML input"
- desc "PLEASE REFACTOR - this should be handled by SyntaxHighlightFilter"
- tag 'plantuml'
- end
- end
-end
diff --git a/package.json b/package.json
index 21e04724441..16a6e45e820 100644
--- a/package.json
+++ b/package.json
@@ -22,6 +22,7 @@
"babel-plugin-transform-define": "^1.2.0",
"babel-preset-latest": "^6.24.0",
"babel-preset-stage-2": "^6.22.0",
+ "blackst0ne-mermaid": "^7.1.0-fixed",
"bootstrap-sass": "^3.3.6",
"brace-expansion": "^1.1.8",
"compression-webpack-plugin": "^1.0.0",
diff --git a/spec/features/markdown_spec.rb b/spec/features/markdown_spec.rb
index b70d3060f05..cc1b187ff54 100644
--- a/spec/features/markdown_spec.rb
+++ b/spec/features/markdown_spec.rb
@@ -69,6 +69,12 @@ describe 'GitLab Markdown' do
end
end
+ it 'parses mermaid code block' do
+ aggregate_failures do
+ expect(doc).to have_selector('pre.code.js-render-mermaid')
+ end
+ end
+
it 'parses strikethroughs' do
expect(doc).to have_selector(%{del:contains("and this text doesn't")})
end
diff --git a/spec/fixtures/markdown.md.erb b/spec/fixtures/markdown.md.erb
index 4f46e40ce7a..638cd8b07c8 100644
--- a/spec/fixtures/markdown.md.erb
+++ b/spec/fixtures/markdown.md.erb
@@ -268,3 +268,37 @@ However the wrapping tags can not be mixed as such -
### Videos
![My Video](/assets/videos/gitlab-demo.mp4)
+
+### Mermaid
+
+> If this is not rendered correctly, see
+https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#mermaid
+
+It is possible to generate diagrams and flowcharts from text using [Mermaid][mermaid].
+
+In order to generate a diagram or flowchart, you should write your text inside the `mermaid` block.
+
+Example:
+
+ ```mermaid
+ graph TD;
+ A-->B;
+ A-->C;
+ B-->D;
+ C-->D;
+ ```
+
+Becomes:
+
+```mermaid
+graph TD;
+ A-->B;
+ A-->C;
+ B-->D;
+ C-->D;
+```
+
+For details see the [Mermaid official page][mermaid].
+
+[mermaid]: https://mermaidjs.github.io/ "Mermaid website"
+
diff --git a/spec/javascripts/notes_spec.js b/spec/javascripts/notes_spec.js
index 928a4b461cc..63abac222c4 100644
--- a/spec/javascripts/notes_spec.js
+++ b/spec/javascripts/notes_spec.js
@@ -4,8 +4,9 @@
import 'autosize';
import '~/gl_form';
import '~/lib/utils/text_utility';
-import '~/render_gfm';
import '~/render_math';
+import '~/render_mermaid';
+import '~/render_gfm';
import '~/notes';
(function() {
diff --git a/spec/lib/banzai/filter/mermaid_filter_spec.rb b/spec/lib/banzai/filter/mermaid_filter_spec.rb
new file mode 100644
index 00000000000..532d25e121d
--- /dev/null
+++ b/spec/lib/banzai/filter/mermaid_filter_spec.rb
@@ -0,0 +1,12 @@
+require 'spec_helper'
+
+describe Banzai::Filter::MermaidFilter do
+ include FilterSpecHelper
+
+ it 'adds `js-render-mermaid` class to the `pre` tag' do
+ doc = filter("<pre class='code highlight js-syntax-highlight mermaid' lang='mermaid' v-pre='true'><code>graph TD;\n A--&gt;B;\n</code></pre>")
+ result = doc.xpath('descendant-or-self::pre').first
+
+ expect(result[:class]).to include('js-render-mermaid')
+ end
+end
diff --git a/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb b/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb
index 5a23e0e70cc..9f2efa05a01 100644
--- a/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb
+++ b/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb
@@ -31,7 +31,7 @@ describe Banzai::Filter::SyntaxHighlightFilter do
it "highlights as plaintext" do
result = filter('<pre><code lang="ruby">This is a test</code></pre>')
- expect(result.to_html).to eq('<pre class="code highlight" lang="" v-pre="true"><code>This is a test</code></pre>')
+ expect(result.to_html).to eq('<pre class="code highlight js-syntax-highlight" lang="" v-pre="true"><code>This is a test</code></pre>')
end
end
end
diff --git a/yarn.lock b/yarn.lock
index a73aebbf180..9bdf5e0f64b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -928,6 +928,17 @@ binary-extensions@^1.0.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.10.0.tgz#9aeb9a6c5e88638aad171e167f5900abe24835d0"
+blackst0ne-mermaid@^7.1.0-fixed:
+ version "7.1.0-fixed"
+ resolved "https://registry.yarnpkg.com/blackst0ne-mermaid/-/blackst0ne-mermaid-7.1.0-fixed.tgz#3707b3a113d78610e3068e18a588f46b4688de49"
+ dependencies:
+ d3 "3.5.17"
+ dagre-d3-renderer "^0.4.24"
+ dagre-layout "^0.8.0"
+ he "^1.1.1"
+ lodash "^4.17.4"
+ moment "^2.18.1"
+
blob@0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.4.tgz#bcf13052ca54463f30f9fc7e95b9a47630a94921"
@@ -1644,6 +1655,10 @@ custom-event@~1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/custom-event/-/custom-event-1.0.1.tgz#5d02a46850adf1b4a317946a3928fccb5bfd0425"
+d3@3.5.17:
+ version "3.5.17"
+ resolved "https://registry.yarnpkg.com/d3/-/d3-3.5.17.tgz#bc46748004378b21a360c9fc7cf5231790762fb8"
+
d3@^3.5.11:
version "3.5.11"
resolved "https://registry.yarnpkg.com/d3/-/d3-3.5.11.tgz#d130750eed0554db70e8432102f920a12407b69c"
@@ -1660,6 +1675,22 @@ d@^0.1.1:
dependencies:
es5-ext "~0.10.2"
+dagre-d3-renderer@^0.4.24:
+ version "0.4.24"
+ resolved "https://registry.yarnpkg.com/dagre-d3-renderer/-/dagre-d3-renderer-0.4.24.tgz#b36ce2fe4ea20de43e7698627c6ede2a9f15ec45"
+ dependencies:
+ d3 "3.5.17"
+ dagre-layout "^0.8.0"
+ graphlib "^2.1.1"
+ lodash "^4.17.4"
+
+dagre-layout@^0.8.0:
+ version "0.8.0"
+ resolved "https://registry.yarnpkg.com/dagre-layout/-/dagre-layout-0.8.0.tgz#7147b6afb655602f855158dfea171db9aa98d4ff"
+ dependencies:
+ graphlib "^2.1.1"
+ lodash "^4.17.4"
+
dashdash@^1.12.0:
version "1.14.1"
resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
@@ -2858,6 +2889,12 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9:
version "1.0.1"
resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725"
+graphlib@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/graphlib/-/graphlib-2.1.1.tgz#42352c52ba2f4d035cb566eb91f7395f76ebc951"
+ dependencies:
+ lodash "^4.11.1"
+
gzip-size@3.0.0, gzip-size@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-3.0.0.tgz#546188e9bdc337f673772f81660464b389dce520"
@@ -2952,7 +2989,7 @@ hawk@~3.1.3:
hoek "2.x.x"
sntp "1.x.x"
-he@^1.1.0:
+he@^1.1.0, he@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd"
@@ -3948,7 +3985,7 @@ lodash@^3.8.0:
version "3.10.1"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
-lodash@^4.0.0, lodash@^4.14.0, lodash@^4.17.2, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.3.0, lodash@^4.5.0:
+lodash@^4.0.0, lodash@^4.11.1, lodash@^4.14.0, lodash@^4.17.2, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.3.0, lodash@^4.5.0:
version "4.17.4"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
@@ -4162,6 +4199,10 @@ moment@2.x:
version "2.17.1"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.17.1.tgz#fed9506063f36b10f066c8b59a144d7faebe1d82"
+moment@^2.18.1:
+ version "2.19.2"
+ resolved "https://registry.yarnpkg.com/moment/-/moment-2.19.2.tgz#8a7f774c95a64550b4c7ebd496683908f9419dbe"
+
monaco-editor@0.10.0:
version "0.10.0"
resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.10.0.tgz#6604932585fe9c1f993f000a503d0d20fbe5896a"