From 131e74d10dafbf2b781ab5d5517e42a18e20a587 Mon Sep 17 00:00:00 2001 From: "Vitaliy @blackst0ne Klachkov" Date: Wed, 22 Nov 2017 14:12:04 +1100 Subject: Add support of Mermaid --- app/assets/javascripts/main.js | 1 + app/assets/javascripts/render_gfm.js | 3 +- app/assets/javascripts/render_mermaid.js | 30 +++++++++++++++ changelogs/unreleased/feature_add_mermaid.yml | 5 +++ config/initializers/math_lexer.rb | 2 - config/initializers/plantuml_lexer.rb | 2 - doc/user/markdown.md | 32 +++++++++++++++ lib/banzai/filter/mermaid_filter.rb | 20 ++++++++++ lib/banzai/filter/syntax_highlight_filter.rb | 35 ++++++++++------- lib/banzai/pipeline/gfm_pipeline.rb | 1 + lib/rouge/lexers/math.rb | 9 ----- lib/rouge/lexers/plantuml.rb | 9 ----- package.json | 1 + spec/features/markdown_spec.rb | 6 +++ spec/fixtures/markdown.md.erb | 34 ++++++++++++++++ spec/javascripts/notes_spec.js | 3 +- spec/lib/banzai/filter/mermaid_filter_spec.rb | 12 ++++++ .../banzai/filter/syntax_highlight_filter_spec.rb | 2 +- yarn.lock | 45 +++++++++++++++++++++- 19 files changed, 211 insertions(+), 41 deletions(-) create mode 100644 app/assets/javascripts/render_mermaid.js create mode 100644 changelogs/unreleased/feature_add_mermaid.yml delete mode 100644 config/initializers/math_lexer.rb delete mode 100644 config/initializers/plantuml_lexer.rb create mode 100644 lib/banzai/filter/mermaid_filter.rb delete mode 100644 lib/rouge/lexers/math.rb delete mode 100644 lib/rouge/lexers/plantuml.rb create mode 100644 spec/lib/banzai/filter/mermaid_filter_spec.rb diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js index cef79eec273..3ace25ad3fa 100644 --- a/app/assets/javascripts/main.js +++ b/app/assets/javascripts/main.js @@ -75,6 +75,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: +// +//
+//  graph TD;
+//    A-- > B;
+//    A-- > C;
+//    B-- > D;
+//    C-- > D;
+// 
+// + +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 `` 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 = %(
#{code}
) + highlighted = %(
#{code}
) # 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 cbcce40ffb9..591238dff73 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,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("
graph TD;\n  A-->B;\n
") + 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('
This is a test
') - expect(result.to_html).to eq('
This is a test
') + expect(result.to_html).to eq('
This is a test
') end end end diff --git a/yarn.lock b/yarn.lock index 1271c8a1ee3..1d6242d800b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -922,6 +922,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" @@ -1638,6 +1649,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" @@ -1654,6 +1669,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" @@ -2852,6 +2883,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" @@ -2946,7 +2983,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" @@ -3942,7 +3979,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" @@ -4156,6 +4193,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" -- cgit v1.2.1