Class: Aspera::Markdown
- Inherits:
-
Object
- Object
- Aspera::Markdown
- Defined in:
- lib/aspera/markdown.rb
Overview
Formatting for Markdown
Constant Summary collapse
- FORMATS =
Matches: bold,
code, or an HTML entity (&, ©, 💩) /(?:\*\*(?<bold>[^*]+?)\*\*)|(?:`(?<code>[^`]+)`)|&(?<entity>(?:[A-Za-z][A-Za-z0-9]{1,31}|#\d{1,7}|#x[0-9A-Fa-f]{1,6}));/m- HTML_BREAK =
'<br/>'- COL_WIDTH =
80- HEADING_RE =
Extract the table of contents from a Markdown document.
/^(\#{1,6})\s+(.+)$/
Class Method Summary collapse
-
.admonition(lines, type: 'INFO') ⇒ String
Generate a GitHub-flavoured admonition block.
-
.code(lines, type: 'shell') ⇒ String
Generate a fenced code block.
-
.extract_section(content, anchor) ⇒ String?
Extract the content of a single section (heading + body until next heading of same/higher level).
-
.heading(title, level: 1) ⇒ String
Generate a markdown heading.
-
.heading_to_anchor(text, seen: nil) ⇒ String
Convert a Markdown heading text to a GitHub-flavoured anchor.
-
.icode(text) ⇒ String
Wrap text in inline code backticks.
-
.list(items) ⇒ String
Generate markdown list from the provided list.
-
.paragraph(text) ⇒ String
Wrap text in a markdown paragraph (trailing blank line).
-
.table(table) ⇒ String
Generate markdown from the provided 2D table.
- .toc(content) ⇒ Object
Class Method Details
.admonition(lines, type: 'INFO') ⇒ String
Generate a GitHub-flavoured admonition block
112 113 114 |
# File 'lib/aspera/markdown.rb', line 112 def admonition(lines, type: 'INFO') "> [!#{type}]\n#{lines.map { |l| "> #{l}" }.join("\n")}\n\n" end |
.code(lines, type: 'shell') ⇒ String
Generate a fenced code block
120 121 122 |
# File 'lib/aspera/markdown.rb', line 120 def code(lines, type: 'shell') "```#{type}\n#{lines.join("\n")}\n```\n\n" end |
.extract_section(content, anchor) ⇒ String?
Extract the content of a single section (heading + body until next heading of same/higher level).
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
# File 'lib/aspera/markdown.rb', line 55 def extract_section(content, anchor) seen = {} section_level = nil result = [] content.each_line do |line| m = line.match(HEADING_RE) if m slug = heading_to_anchor(m[2].strip, seen: seen) if section_level.nil? # not yet found: check if this heading matches next unless slug == anchor section_level = m[1].length elsif m[1].length <= section_level # already in section: stop at same/higher level heading break end end result << line if section_level end result.empty? ? nil : result.join end |
.heading(title, level: 1) ⇒ String
Generate a markdown heading
104 105 106 |
# File 'lib/aspera/markdown.rb', line 104 def heading(title, level: 1) "#{'#' * level} #{title}\n\n" end |
.heading_to_anchor(text, seen: nil) ⇒ String
Convert a Markdown heading text to a GitHub-flavoured anchor. Rules: downcase, keep letters/digits/spaces/hyphens, replace spaces with hyphens. Duplicate anchors are disambiguated by appending -1, -2, … (pass a seen Hash to track).
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
# File 'lib/aspera/markdown.rb', line 19 def heading_to_anchor(text, seen: nil) slug = text .downcase .gsub(/[`*_]/, '') # strip inline code/bold/italic markers .gsub(/&[a-z]+;/, '') # strip HTML entities .gsub(/[^\w\s-]/, '') # keep word chars, spaces, hyphens .gsub(/\s+/, '-') # spaces → hyphens .squeeze('-') # collapse consecutive hyphens .strip if seen count = seen[slug].to_i seen[slug] = count + 1 slug = "#{slug}-#{count}" if count > 0 end slug end |
.icode(text) ⇒ String
Wrap text in inline code backticks
127 128 129 |
# File 'lib/aspera/markdown.rb', line 127 def icode(text) "`#{text}`" end |
.list(items) ⇒ String
Generate markdown list from the provided list
96 97 98 |
# File 'lib/aspera/markdown.rb', line 96 def list(items) items.map { |i| "- #{i}" }.join("\n") end |
.paragraph(text) ⇒ String
Wrap text in a markdown paragraph (trailing blank line)
134 135 136 |
# File 'lib/aspera/markdown.rb', line 134 def paragraph(text) "#{text}\n\n" end |
.table(table) ⇒ String
Generate markdown from the provided 2D table
80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/aspera/markdown.rb', line 80 def table(table) # get max width of each columns col_widths = table.transpose.map do |col| [col.flat_map { |c| c.to_s.delete('`').split(HTML_BREAK).map(&:size) }.max, COL_WIDTH].min end headings = table.shift table.unshift(col_widths.map { |col_width| '-' * col_width }) table.unshift(headings) lines = table.map { |line| "| #{line.map { |i| i.to_s.gsub('\\', '\\\\').gsub('|', '\|') }.join(' | ')} |\n" } lines[1] = lines[1].tr(' ', '-') return lines.join.chomp end |
.toc(content) ⇒ Object
41 42 43 44 45 46 47 48 49 |
# File 'lib/aspera/markdown.rb', line 41 def toc(content) seen = {} content.each_line.filter_map do |line| m = line.match(HEADING_RE) next unless m title = m[2].strip {level: m[1].length, title: title, anchor: heading_to_anchor(title, seen: seen)} end end |