2017-08-13 09:02:48 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2020-07-04 04:59:08 -04:00
|
|
|
require "rouge"
|
|
|
|
|
|
|
|
# Add more common shell commands
|
|
|
|
Rouge::Lexers::Shell::BUILTINS << "|bin/rails|brew|bundle|gem|git|node|rails|rake|ruby|sqlite3|yarn"
|
|
|
|
|
2012-08-31 12:01:06 -04:00
|
|
|
module RailsGuides
|
|
|
|
class Markdown
|
|
|
|
class Renderer < Redcarpet::Render::HTML
|
2017-02-12 04:21:20 -05:00
|
|
|
cattr_accessor :edge, :version
|
2012-08-31 12:01:06 -04:00
|
|
|
|
2012-08-31 17:49:17 -04:00
|
|
|
def block_code(code, language)
|
2020-07-04 04:59:08 -04:00
|
|
|
formatter = Rouge::Formatters::HTML.new
|
|
|
|
lexer = ::Rouge::Lexer.find_fancy(lexer_language(language))
|
2020-07-11 06:23:37 -04:00
|
|
|
formatted_code = formatter.format(lexer.lex(code))
|
|
|
|
clipboard_id = "clipboard-#{SecureRandom.hex(16)}"
|
|
|
|
<<~HTML
|
|
|
|
<div class="code_container">
|
|
|
|
<pre><code class="highlight #{lexer_language(language)}">#{formatted_code}</code></pre>
|
|
|
|
<textarea class="clipboard-content" id="#{clipboard_id}">#{clipboard_content(code, language)}</textarea>
|
|
|
|
<button class="clipboard-button" data-clipboard-target="##{clipboard_id}">Copy</button>
|
|
|
|
</div>
|
2019-11-23 19:54:47 -05:00
|
|
|
HTML
|
2012-08-31 17:49:17 -04:00
|
|
|
end
|
|
|
|
|
2016-11-30 13:27:35 -05:00
|
|
|
def link(url, title, content)
|
2019-04-01 01:47:34 -04:00
|
|
|
if %r{https?://api\.rubyonrails\.org}.match?(url)
|
2016-11-30 13:27:35 -05:00
|
|
|
%(<a href="#{api_link(url)}">#{content}</a>)
|
|
|
|
elsif title
|
|
|
|
%(<a href="#{url}" title="#{title}">#{content}</a>)
|
|
|
|
else
|
|
|
|
%(<a href="#{url}">#{content}</a>)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-05-13 17:30:33 -04:00
|
|
|
def header(text, header_level)
|
2015-12-21 16:36:35 -05:00
|
|
|
# Always increase the heading level by 1, so we can use h1, h2 heading in the document
|
2012-08-31 12:01:06 -04:00
|
|
|
header_level += 1
|
|
|
|
|
2018-09-23 06:57:36 -04:00
|
|
|
header_with_id = text.scan(/(.*){#(.*)}/)
|
|
|
|
unless header_with_id.empty?
|
2018-10-04 17:57:32 -04:00
|
|
|
%(<h#{header_level} id="#{header_with_id[0][1].strip}">#{header_with_id[0][0].strip}</h#{header_level}>)
|
2018-09-23 06:57:36 -04:00
|
|
|
else
|
|
|
|
%(<h#{header_level}>#{text}</h#{header_level}>)
|
|
|
|
end
|
2012-08-31 12:01:06 -04:00
|
|
|
end
|
|
|
|
|
2012-09-02 14:53:01 -04:00
|
|
|
def paragraph(text)
|
2017-01-29 07:16:10 -05:00
|
|
|
if text =~ %r{^NOTE:\s+Defined\s+in\s+<code>(.*?)</code>\.?$}
|
|
|
|
%(<div class="note"><p>Defined in <code><a href="#{github_file_url($1)}">#{$1}</a></code>.</p></div>)
|
2018-07-28 17:37:17 -04:00
|
|
|
elsif /^(TIP|IMPORTANT|CAUTION|WARNING|NOTE|INFO|TODO)[.:]/.match?(text)
|
2012-09-02 14:53:01 -04:00
|
|
|
convert_notes(text)
|
2016-08-06 13:21:59 -04:00
|
|
|
elsif text.include?("DO NOT READ THIS FILE ON GITHUB")
|
2012-09-17 00:46:26 -04:00
|
|
|
elsif text =~ /^\[<sup>(\d+)\]:<\/sup> (.+)$/
|
|
|
|
linkback = %(<a href="#footnote-#{$1}-ref"><sup>#{$1}</sup></a>)
|
|
|
|
%(<p class="footnote" id="footnote-#{$1}">#{linkback} #{$2}</p>)
|
2012-09-02 14:53:01 -04:00
|
|
|
else
|
2012-09-17 00:46:26 -04:00
|
|
|
text = convert_footnotes(text)
|
2012-09-02 14:53:01 -04:00
|
|
|
"<p>#{text}</p>"
|
|
|
|
end
|
2012-08-31 12:01:06 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
private
|
2012-09-17 00:46:26 -04:00
|
|
|
def convert_footnotes(text)
|
|
|
|
text.gsub(/\[<sup>(\d+)\]<\/sup>/i) do
|
|
|
|
%(<sup class="footnote" id="footnote-#{$1}-ref">) +
|
|
|
|
%(<a href="#footnote-#{$1}">#{$1}</a></sup>)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-07-04 04:59:08 -04:00
|
|
|
def lexer_language(code_type)
|
2012-08-31 17:49:17 -04:00
|
|
|
case code_type
|
2020-07-21 19:11:01 -04:00
|
|
|
when "html+erb"
|
Use prism.js for code highlighting in guides [ci skip]
Replace SyntaxHighlighter with prism.js. SyntaxHighlighter does not seem
to be maintained. Prism.js seems a nice alternative with support for
Ruby, javascript, erb and yaml. Prism.js uses the MIT License.
https://github.com/PrismJS/prism
For bash a customized definition of the bash+shell-session themes is
used with the following comamnds added commands:
brew, bundle, cat, git, gem, rackup, rake, rails, ruby, rubocop and
yarn
2019-12-14 15:10:26 -05:00
|
|
|
"erb"
|
|
|
|
when "bash"
|
2020-07-04 04:59:08 -04:00
|
|
|
"console"
|
2020-07-21 19:11:01 -04:00
|
|
|
when nil
|
2020-07-04 04:59:08 -04:00
|
|
|
"plaintext"
|
2020-07-21 19:11:01 -04:00
|
|
|
else
|
2020-07-28 05:24:35 -04:00
|
|
|
::Rouge::Lexer.find(code_type) ? code_type : "plaintext"
|
2012-08-31 17:49:17 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-07-11 06:23:37 -04:00
|
|
|
def clipboard_content(code, language)
|
2020-10-31 13:03:55 -04:00
|
|
|
prompt_regexp =
|
|
|
|
case language
|
|
|
|
when "bash"
|
|
|
|
/^\$ /
|
|
|
|
when "irb"
|
|
|
|
/^irb.*?> /
|
|
|
|
end
|
|
|
|
|
|
|
|
if prompt_regexp
|
|
|
|
code = code.lines.grep(prompt_regexp).join.gsub(prompt_regexp, "")
|
2020-07-11 06:23:37 -04:00
|
|
|
end
|
2020-10-31 13:03:55 -04:00
|
|
|
|
2020-07-11 06:23:37 -04:00
|
|
|
ERB::Util.h(code)
|
|
|
|
end
|
|
|
|
|
2012-08-31 12:01:06 -04:00
|
|
|
def convert_notes(body)
|
|
|
|
# The following regexp detects special labels followed by a
|
|
|
|
# paragraph, perhaps at the end of the document.
|
|
|
|
#
|
|
|
|
# It is important that we do not eat more than one newline
|
|
|
|
# because formatting may be wrong otherwise. For example,
|
2017-11-28 13:27:43 -05:00
|
|
|
# if a bulleted list follows, the first item is not rendered
|
2012-08-31 12:01:06 -04:00
|
|
|
# as a list item, but as a paragraph starting with a plain
|
|
|
|
# asterisk.
|
2013-04-13 03:49:54 -04:00
|
|
|
body.gsub(/^(TIP|IMPORTANT|CAUTION|WARNING|NOTE|INFO|TODO)[.:](.*?)(\n(?=\n)|\Z)/m) do
|
2016-09-01 18:43:33 -04:00
|
|
|
css_class = \
|
|
|
|
case $1
|
|
|
|
when "CAUTION", "IMPORTANT"
|
|
|
|
"warning"
|
|
|
|
when "TIP"
|
|
|
|
"info"
|
|
|
|
else
|
|
|
|
$1.downcase
|
|
|
|
end
|
2012-09-02 14:53:01 -04:00
|
|
|
%(<div class="#{css_class}"><p>#{$2.strip}</p></div>)
|
2012-08-31 12:01:06 -04:00
|
|
|
end
|
|
|
|
end
|
2016-11-30 13:27:35 -05:00
|
|
|
|
2017-01-29 07:16:10 -05:00
|
|
|
def github_file_url(file_path)
|
2017-02-12 04:21:20 -05:00
|
|
|
tree = version || edge
|
2017-01-29 07:16:10 -05:00
|
|
|
|
2017-05-12 23:54:11 -04:00
|
|
|
root = file_path[%r{(\w+)/}, 1]
|
2017-04-19 20:02:03 -04:00
|
|
|
path = \
|
|
|
|
case root
|
|
|
|
when "abstract_controller", "action_controller", "action_dispatch"
|
|
|
|
"actionpack/lib/#{file_path}"
|
|
|
|
when /\A(action|active)_/
|
|
|
|
"#{root.sub("_", "")}/lib/#{file_path}"
|
|
|
|
else
|
|
|
|
file_path
|
|
|
|
end
|
2017-01-29 07:16:10 -05:00
|
|
|
|
2017-02-12 04:21:20 -05:00
|
|
|
"https://github.com/rails/rails/tree/#{tree}/#{path}"
|
2016-11-30 13:27:35 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def api_link(url)
|
2019-04-01 01:47:34 -04:00
|
|
|
if %r{https?://api\.rubyonrails\.org/v\d+\.}.match?(url)
|
2017-02-12 04:21:20 -05:00
|
|
|
url
|
|
|
|
elsif edge
|
|
|
|
url.sub("api", "edgeapi")
|
|
|
|
else
|
|
|
|
url.sub(/(?<=\.org)/, "/#{version}")
|
2016-11-30 13:27:35 -05:00
|
|
|
end
|
|
|
|
end
|
2012-08-31 12:01:06 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|