# frozen_string_literal: true require "rouge" # Add more common shell commands Rouge::Lexers::Shell::BUILTINS << "|bin/rails|brew|bundle|gem|git|node|rails|rake|ruby|sqlite3|yarn" module RailsGuides class Markdown class Renderer < Redcarpet::Render::HTML cattr_accessor :edge, :version def block_code(code, language) formatter = Rouge::Formatters::HTML.new lexer = ::Rouge::Lexer.find_fancy(lexer_language(language)) formatted_code = formatter.format(lexer.lex(code)) clipboard_id = "clipboard-#{SecureRandom.hex(16)}" <<~HTML
#{formatted_code}
(.*?)
\.?$}
%(Defined in #{$1}
.
#{linkback} #{$2}
) else text = convert_footnotes(text) "#{text}
" end end private def convert_footnotes(text) text.gsub(/\[(\d+)\]<\/sup>/i) do %() + %(#{$1}) end end def lexer_language(code_type) case code_type when "html+erb" "erb" when "bash" "console" when nil "plaintext" else ::Rouge::Lexer.find(code_type) ? code_type : "plaintext" end end def clipboard_content(code, language) if language == "bash" prompt_regexp = /^\$ / code = code.split("\n"). select { |line| line =~ prompt_regexp }. map { |line| line.gsub(prompt_regexp, "") }. join("\n") end ERB::Util.h(code) end 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, # if a bulleted list follows, the first item is not rendered # as a list item, but as a paragraph starting with a plain # asterisk. body.gsub(/^(TIP|IMPORTANT|CAUTION|WARNING|NOTE|INFO|TODO)[.:](.*?)(\n(?=\n)|\Z)/m) do css_class = \ case $1 when "CAUTION", "IMPORTANT" "warning" when "TIP" "info" else $1.downcase end %(#{$2.strip}