Class: RubyRich::Markdown::TerminalRenderer
- Inherits:
-
Redcarpet::Render::Base
- Object
- Redcarpet::Render::Base
- RubyRich::Markdown::TerminalRenderer
- Defined in:
- lib/ruby_rich/markdown.rb
Overview
Converts markdown to ANSI-styled terminal output. Uses Redcarpet for block parsing with custom inline processing.
Constant Summary collapse
- INLINE_MARKERS =
{ # triple-backtick must come before double-backtick %r{```(.+?)```}m => ->(m) { codespan_compat(Regexp.last_match(1)) }, %r{``(.+?)``}m => ->(m) { codespan_compat(Regexp.last_match(1)) }, %r{`(.+?)`} => ->(m) { codespan_compat(Regexp.last_match(1)) }, %r{\*\*\*(.+?)\*\*\*} => ->(m) { "#{AnsiCode.bold}#{AnsiCode.italic}#{Regexp.last_match(1)}#{AnsiCode.reset}" }, %r{\*\*(.+?)\*\*} => ->(m) { "#{AnsiCode.bold}#{Regexp.last_match(1)}#{AnsiCode.reset}" }, %r{(?<!\*)\*([^*]+)\*(?!\*)} => ->(m) { "#{AnsiCode.italic}#{Regexp.last_match(1)}#{AnsiCode.reset}" }, %r{~~(.+?)~~} => ->(m) { "#{AnsiCode.strikethrough}#{Regexp.last_match(1)}#{AnsiCode.reset}" }, %r{\[([^\]]+)\]\(([^)]+)\)} => ->(m) { link_text = Regexp.last_match(1) url = Regexp.last_match(2) "#{AnsiCode.color(:blue, true)}#{AnsiCode.underline}#{link_text}#{AnsiCode.reset} #{AnsiCode.color(:black, true)}(#{url})#{AnsiCode.reset}" } }.freeze
Class Method Summary collapse
Instance Method Summary collapse
- #block_code(code, language) ⇒ Object
- #block_quote(quote) ⇒ Object
- #codespan(code) ⇒ Object
- #double_emphasis(text) ⇒ Object
- #emphasis(text) ⇒ Object
- #header(text, level) ⇒ Object
- #hrule ⇒ Object
- #image(link, title, alt_text) ⇒ Object
-
#initialize(options = {}) ⇒ TerminalRenderer
constructor
A new instance of TerminalRenderer.
- #linebreak ⇒ Object
- #link(link, title, content) ⇒ Object
- #list(contents, list_type) ⇒ Object
- #list_item(text, list_type) ⇒ Object
-
#paragraph(text) ⇒ Object
—- block-level callbacks —-.
- #reset_table_state ⇒ Object
- #strikethrough(text) ⇒ Object
-
#table(header, body) ⇒ Object
—- table callbacks —-.
- #table_cell(content, alignment) ⇒ Object
- #table_row(content) ⇒ Object
Constructor Details
#initialize(options = {}) ⇒ TerminalRenderer
Returns a new instance of TerminalRenderer.
24 25 26 27 28 29 30 31 |
# File 'lib/ruby_rich/markdown.rb', line 24 def initialize( = {}) @options = { width: 80, indent: ' ' }.merge() super() reset_table_state end |
Class Method Details
Instance Method Details
#block_code(code, language) ⇒ Object
53 54 55 56 57 58 59 60 61 |
# File 'lib/ruby_rich/markdown.rb', line 53 def block_code(code, language) lang = language&.strip lang = nil if lang && lang.empty? highlighted = Syntax.highlight(code.strip, lang) bg = AnsiCode.background(:black, true) fg = AnsiCode.color(:white, true) pad = @options[:indent] "#{bg}#{fg}#{indent_lines(highlighted)}#{AnsiCode.reset}\n\n" end |
#block_quote(quote) ⇒ Object
67 68 69 70 71 |
# File 'lib/ruby_rich/markdown.rb', line 67 def block_quote(quote) lines = quote.strip.split("\n") quoted_lines = lines.map { |line| "#{AnsiCode.color(:black, true)}│ #{AnsiCode.color(:white, true)}#{process_inline(line.strip)}" } "#{quoted_lines.join("\n")}#{AnsiCode.reset}\n\n" end |
#codespan(code) ⇒ Object
63 64 65 |
# File 'lib/ruby_rich/markdown.rb', line 63 def codespan(code) "#{AnsiCode.background(:white)}#{AnsiCode.color(:black)} #{code} #{AnsiCode.reset}" end |
#double_emphasis(text) ⇒ Object
83 |
# File 'lib/ruby_rich/markdown.rb', line 83 def double_emphasis(text) = "#{AnsiCode.bold}#{text}#{AnsiCode.reset}" |
#emphasis(text) ⇒ Object
82 |
# File 'lib/ruby_rich/markdown.rb', line 82 def emphasis(text) = "#{AnsiCode.italic}#{text}#{AnsiCode.reset}" |
#header(text, level) ⇒ Object
43 44 45 46 47 48 49 50 51 |
# File 'lib/ruby_rich/markdown.rb', line 43 def header(text, level) processed = process_inline(text) case level when 1 then "#{AnsiCode.font(:cyan, font_bright: true, bold: true)}#{processed}#{AnsiCode.reset}\n#{AnsiCode.color(:cyan, true)}#{'=' * visible_width(text)}#{AnsiCode.reset}\n\n" when 2 then "#{AnsiCode.font(:blue, font_bright: true, bold: true)}#{processed}#{AnsiCode.reset}\n#{AnsiCode.color(:blue, true)}#{'-' * visible_width(text)}#{AnsiCode.reset}\n\n" when 3 then "#{AnsiCode.font(:yellow, font_bright: true, bold: true)}### #{processed}#{AnsiCode.reset}\n\n" else "#{AnsiCode.font(:black, font_bright: true, bold: true)}#{'#' * level} #{processed}#{AnsiCode.reset}\n\n" end end |
#hrule ⇒ Object
96 97 98 |
# File 'lib/ruby_rich/markdown.rb', line 96 def hrule "#{AnsiCode.color(:black, true)}#{"─" * @options[:width]}#{AnsiCode.reset}\n\n" end |
#image(link, title, alt_text) ⇒ Object
91 92 93 94 |
# File 'lib/ruby_rich/markdown.rb', line 91 def image(link, title, alt_text) title_part = title && !title.empty? ? " - #{title}" : "" "#{AnsiCode.color(:magenta, true)}[Image: #{alt_text}]#{AnsiCode.reset} #{AnsiCode.color(:black, true)}(#{link}#{title_part})#{AnsiCode.reset}" end |
#linebreak ⇒ Object
100 |
# File 'lib/ruby_rich/markdown.rb', line 100 def linebreak = "\n" |
#link(link, title, content) ⇒ Object
86 87 88 89 |
# File 'lib/ruby_rich/markdown.rb', line 86 def link(link, title, content) title_part = title && !title.empty? ? " - #{title}" : "" "#{AnsiCode.color(:blue, true)}#{AnsiCode.underline}#{content}#{AnsiCode.reset} #{AnsiCode.color(:black, true)}(#{link}#{title_part})#{AnsiCode.reset}" end |
#list(contents, list_type) ⇒ Object
78 79 80 |
# File 'lib/ruby_rich/markdown.rb', line 78 def list(contents, list_type) "#{contents}\n" end |
#list_item(text, list_type) ⇒ Object
73 74 75 76 |
# File 'lib/ruby_rich/markdown.rb', line 73 def list_item(text, list_type) marker = list_type == :ordered ? '1.' : '•' "#{AnsiCode.color(:cyan, true)}#{marker}#{AnsiCode.reset} #{process_inline(text.strip)}\n" end |
#paragraph(text) ⇒ Object
—- block-level callbacks —-
39 40 41 |
# File 'lib/ruby_rich/markdown.rb', line 39 def paragraph(text) "#{process_inline(text)}\n\n" end |
#reset_table_state ⇒ Object
33 34 35 |
# File 'lib/ruby_rich/markdown.rb', line 33 def reset_table_state @table_state = { current_row: [], all_rows: [] } end |
#strikethrough(text) ⇒ Object
84 |
# File 'lib/ruby_rich/markdown.rb', line 84 def strikethrough(text) = "#{AnsiCode.strikethrough}#{text}#{AnsiCode.reset}" |
#table(header, body) ⇒ Object
—- table callbacks —-
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
# File 'lib/ruby_rich/markdown.rb', line 104 def table(header, body) all_rows = @table_state[:all_rows] reset_table_state return "" if all_rows.empty? header_line_count = [header.to_s.strip.split("\n").size, 1].max header_rows = all_rows[0...header_line_count] body_rows = all_rows[header_line_count..] || [] return "" if header_rows.empty? || body_rows.empty? headers = header_rows.last.map { |c| process_inline(c) } begin tbl = RubyRich::Table.new(headers: headers, border_style: @options[:table_border_style] || :simple) body_rows.each do |row| processed = row.map { |c| process_inline(c) } padded = processed + Array.new([0, headers.length - processed.length].max, "") tbl.add_row(padded[0...headers.length]) end return "#{tbl.render}\n\n" rescue # fallback end result = "\n" result += "#{header.strip}\n" result += "#{"-" * [header.strip.length, 20].min}\n" result += "#{body.strip}\n" if body && !body.strip.empty? "#{result}\n" end |
#table_cell(content, alignment) ⇒ Object
141 142 143 144 |
# File 'lib/ruby_rich/markdown.rb', line 141 def table_cell(content, alignment) @table_state[:current_row] << content.strip content end |
#table_row(content) ⇒ Object
135 136 137 138 139 |
# File 'lib/ruby_rich/markdown.rb', line 135 def table_row(content) @table_state[:all_rows] << @table_state[:current_row].dup @table_state[:current_row] = [] "#{content}\n" end |