markbridge 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/markbridge/ast/table.rb +67 -0
- data/lib/markbridge/ast.rb +1 -0
- data/lib/markbridge/parsers/bbcode/handler_registry.rb +5 -0
- data/lib/markbridge/parsers/bbcode/handlers/image_handler.rb +13 -11
- data/lib/markbridge/parsers/bbcode/handlers/quote_handler.rb +40 -33
- data/lib/markbridge/parsers/bbcode/handlers/table_cell_handler.rb +26 -0
- data/lib/markbridge/parsers/bbcode/handlers/table_handler.rb +32 -0
- data/lib/markbridge/parsers/bbcode/handlers/table_row_handler.rb +35 -0
- data/lib/markbridge/parsers/bbcode/parser.rb +1 -1
- data/lib/markbridge/parsers/bbcode.rb +3 -0
- data/lib/markbridge/parsers/html/handler_registry.rb +5 -0
- data/lib/markbridge/parsers/html/handlers/base_handler.rb +0 -1
- data/lib/markbridge/parsers/html/handlers/table_cell_handler.rb +24 -0
- data/lib/markbridge/parsers/html/handlers/table_handler.rb +24 -0
- data/lib/markbridge/parsers/html/handlers/table_row_handler.rb +24 -0
- data/lib/markbridge/parsers/html/parser.rb +13 -2
- data/lib/markbridge/parsers/html.rb +3 -0
- data/lib/markbridge/parsers/media_wiki/inline_parser.rb +105 -130
- data/lib/markbridge/parsers/media_wiki/parser.rb +128 -0
- data/lib/markbridge/parsers/text_formatter/handler_registry.rb +6 -0
- data/lib/markbridge/parsers/text_formatter/handlers/table_cell_handler.rb +26 -0
- data/lib/markbridge/parsers/text_formatter.rb +1 -0
- data/lib/markbridge/processors/discourse_markdown/code_block_tracker.rb +96 -84
- data/lib/markbridge/processors/discourse_markdown/detectors/base.rb +12 -0
- data/lib/markbridge/processors/discourse_markdown/detectors/event.rb +0 -10
- data/lib/markbridge/processors/discourse_markdown/detectors/poll.rb +0 -10
- data/lib/markbridge/processors/discourse_markdown/scanner.rb +19 -16
- data/lib/markbridge/renderers/discourse/markdown_escaper.rb +237 -180
- data/lib/markbridge/renderers/discourse/renderer.rb +1 -0
- data/lib/markbridge/renderers/discourse/tags/align_tag.rb +1 -1
- data/lib/markbridge/renderers/discourse/tags/attachment_tag.rb +1 -1
- data/lib/markbridge/renderers/discourse/tags/code_tag.rb +2 -1
- data/lib/markbridge/renderers/discourse/tags/event_tag.rb +3 -5
- data/lib/markbridge/renderers/discourse/tags/image_tag.rb +1 -1
- data/lib/markbridge/renderers/discourse/tags/mention_tag.rb +1 -1
- data/lib/markbridge/renderers/discourse/tags/poll_tag.rb +3 -5
- data/lib/markbridge/renderers/discourse/tags/quote_tag.rb +15 -11
- data/lib/markbridge/renderers/discourse/tags/table_cell_tag.rb +18 -0
- data/lib/markbridge/renderers/discourse/tags/table_row_tag.rb +18 -0
- data/lib/markbridge/renderers/discourse/tags/table_tag.rb +124 -0
- data/lib/markbridge/renderers/discourse/tags/upload_tag.rb +1 -1
- data/lib/markbridge/renderers/discourse.rb +3 -0
- data/lib/markbridge/version.rb +1 -1
- data/lib/markbridge.rb +20 -55
- metadata +12 -1
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Markbridge
|
|
4
|
+
module Renderers
|
|
5
|
+
module Discourse
|
|
6
|
+
module Tags
|
|
7
|
+
# Tag for rendering table rows (passthrough - renders children only)
|
|
8
|
+
# The TableTag handles rows directly; this is a safety net for standalone rendering.
|
|
9
|
+
class TableRowTag < Tag
|
|
10
|
+
def render(element, interface)
|
|
11
|
+
child_context = interface.with_parent(element)
|
|
12
|
+
interface.render_children(element, context: child_context)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Markbridge
|
|
4
|
+
module Renderers
|
|
5
|
+
module Discourse
|
|
6
|
+
module Tags
|
|
7
|
+
# Tag for rendering tables as Markdown pipe tables with HTML fallback
|
|
8
|
+
class TableTag < Tag
|
|
9
|
+
def render(element, interface)
|
|
10
|
+
child_context = interface.with_parent(element)
|
|
11
|
+
rows_data = extract_rows(element, interface, child_context)
|
|
12
|
+
|
|
13
|
+
return "" if rows_data.empty?
|
|
14
|
+
|
|
15
|
+
if markdown_compatible?(rows_data, interface)
|
|
16
|
+
render_markdown(rows_data)
|
|
17
|
+
else
|
|
18
|
+
render_html(rows_data)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
# Extract rendered cell data from each row
|
|
25
|
+
# @return [Array<Hash>] array of {cells: [{content:, header:}], ...}
|
|
26
|
+
def extract_rows(element, interface, child_context)
|
|
27
|
+
element.children.filter_map do |child|
|
|
28
|
+
next unless child.is_a?(AST::TableRow)
|
|
29
|
+
|
|
30
|
+
cells =
|
|
31
|
+
child.children.filter_map do |cell|
|
|
32
|
+
next unless cell.is_a?(AST::TableCell)
|
|
33
|
+
|
|
34
|
+
cell_context = child_context.with_parent(child)
|
|
35
|
+
content = interface.render_children(cell, context: cell_context).strip
|
|
36
|
+
{ content:, header: cell.header? }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
{ cells: } unless cells.empty?
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Check if the table can be rendered as Markdown
|
|
44
|
+
def markdown_compatible?(rows_data, interface)
|
|
45
|
+
return false if rows_data.empty?
|
|
46
|
+
return false if interface.has_parent?(AST::Table)
|
|
47
|
+
|
|
48
|
+
cell_count = rows_data.first[:cells].length
|
|
49
|
+
rows_data.all? do |row|
|
|
50
|
+
row[:cells].length == cell_count &&
|
|
51
|
+
row[:cells].none? { |c| c[:content].include?("\n") }
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Render as Markdown pipe table
|
|
56
|
+
def render_markdown(rows_data)
|
|
57
|
+
header_idx = rows_data.index { |r| r[:cells].all? { |c| c[:header] } }
|
|
58
|
+
header_row = header_idx ? rows_data[header_idx] : rows_data.first
|
|
59
|
+
data_rows =
|
|
60
|
+
(
|
|
61
|
+
if header_idx
|
|
62
|
+
rows_data[0...header_idx] + rows_data[(header_idx + 1)..]
|
|
63
|
+
else
|
|
64
|
+
rows_data[1..]
|
|
65
|
+
end
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
col_count = header_row[:cells].length
|
|
69
|
+
lines = []
|
|
70
|
+
lines << format_row(header_row[:cells])
|
|
71
|
+
lines << "| #{(["---"] * col_count).join(" | ")} |"
|
|
72
|
+
data_rows.each { |row| lines << format_row(row[:cells]) }
|
|
73
|
+
|
|
74
|
+
"\n\n#{lines.join("\n")}\n\n"
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Format a single row as a Markdown pipe row
|
|
78
|
+
def format_row(cells)
|
|
79
|
+
# Pipe characters in cell content are already escaped by the markdown escaper
|
|
80
|
+
"| #{cells.map { |c| c[:content] }.join(" | ")} |"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Render as HTML table
|
|
84
|
+
def render_html(rows_data)
|
|
85
|
+
has_header = rows_data.any? { |r| r[:cells].any? { |c| c[:header] } }
|
|
86
|
+
lines = ["<table>"]
|
|
87
|
+
|
|
88
|
+
if has_header
|
|
89
|
+
header_rows, body_rows = rows_data.partition { |r| r[:cells].all? { |c| c[:header] } }
|
|
90
|
+
|
|
91
|
+
unless header_rows.empty?
|
|
92
|
+
lines << "<thead>"
|
|
93
|
+
header_rows.each { |row| lines << html_row(row, force_header: true) }
|
|
94
|
+
lines << "</thead>"
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
unless body_rows.empty?
|
|
98
|
+
lines << "<tbody>"
|
|
99
|
+
body_rows.each { |row| lines << html_row(row) }
|
|
100
|
+
lines << "</tbody>"
|
|
101
|
+
end
|
|
102
|
+
else
|
|
103
|
+
rows_data.each { |row| lines << html_row(row) }
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
lines << "</table>"
|
|
107
|
+
"\n\n#{lines.join("\n")}\n\n"
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Render a single HTML table row
|
|
111
|
+
def html_row(row, force_header: false)
|
|
112
|
+
cells_html =
|
|
113
|
+
row[:cells].map do |cell|
|
|
114
|
+
tag = (cell[:header] || force_header) ? "th" : "td"
|
|
115
|
+
"<#{tag}>#{cell[:content]}</#{tag}>"
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
"<tr>#{cells_html.join}</tr>"
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
@@ -30,6 +30,9 @@ require_relative "discourse/tags/spoiler_tag"
|
|
|
30
30
|
require_relative "discourse/tags/strikethrough_tag"
|
|
31
31
|
require_relative "discourse/tags/subscript_tag"
|
|
32
32
|
require_relative "discourse/tags/superscript_tag"
|
|
33
|
+
require_relative "discourse/tags/table_tag"
|
|
34
|
+
require_relative "discourse/tags/table_row_tag"
|
|
35
|
+
require_relative "discourse/tags/table_cell_tag"
|
|
33
36
|
require_relative "discourse/tags/underline_tag"
|
|
34
37
|
require_relative "discourse/tags/url_tag"
|
|
35
38
|
|
data/lib/markbridge/version.rb
CHANGED
data/lib/markbridge.rb
CHANGED
|
@@ -16,13 +16,8 @@ module Markbridge
|
|
|
16
16
|
# @param handlers [HandlerRegistry, nil] custom handler registry or use default
|
|
17
17
|
# @return [AST::Document]
|
|
18
18
|
def parse_bbcode(input, handlers: nil)
|
|
19
|
-
raise ArgumentError, "input cannot be nil" if input.nil?
|
|
20
|
-
|
|
21
|
-
input = input.to_s # Coerce to string
|
|
22
19
|
handlers ||= default_handlers
|
|
23
|
-
|
|
24
|
-
parser = Parsers::BBCode::Parser.new(handlers:)
|
|
25
|
-
parser.parse(input)
|
|
20
|
+
parse_with(Parsers::BBCode::Parser, input, handlers:)
|
|
26
21
|
end
|
|
27
22
|
|
|
28
23
|
# Convert BBCode to Discourse Markdown
|
|
@@ -31,17 +26,8 @@ module Markbridge
|
|
|
31
26
|
# @param tag_library [TagLibrary, nil] custom tag library or use default
|
|
32
27
|
# @return [String] Markdown output
|
|
33
28
|
def bbcode_to_markdown(input, handlers: nil, tag_library: nil)
|
|
34
|
-
raise ArgumentError, "input cannot be nil" if input.nil?
|
|
35
|
-
|
|
36
|
-
handlers ||= default_handlers
|
|
37
|
-
tag_library ||= default_tag_library
|
|
38
|
-
|
|
39
29
|
ast = parse_bbcode(input, handlers:)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
# Clean up output
|
|
43
|
-
result = renderer.render(ast)
|
|
44
|
-
cleanup_markdown(result)
|
|
30
|
+
render_to_markdown(ast, tag_library:)
|
|
45
31
|
end
|
|
46
32
|
|
|
47
33
|
# Parse HTML to AST
|
|
@@ -49,13 +35,8 @@ module Markbridge
|
|
|
49
35
|
# @param handlers [HandlerRegistry, nil] custom handler registry or use default
|
|
50
36
|
# @return [AST::Document]
|
|
51
37
|
def parse_html(input, handlers: nil)
|
|
52
|
-
raise ArgumentError, "input cannot be nil" if input.nil?
|
|
53
|
-
|
|
54
|
-
input = input.to_s # Coerce to string
|
|
55
38
|
handlers ||= default_html_handlers
|
|
56
|
-
|
|
57
|
-
parser = Parsers::HTML::Parser.new(handlers:)
|
|
58
|
-
parser.parse(input)
|
|
39
|
+
parse_with(Parsers::HTML::Parser, input, handlers:)
|
|
59
40
|
end
|
|
60
41
|
|
|
61
42
|
# Convert HTML to Discourse Markdown
|
|
@@ -64,17 +45,8 @@ module Markbridge
|
|
|
64
45
|
# @param tag_library [TagLibrary, nil] custom tag library or use default
|
|
65
46
|
# @return [String] Markdown output
|
|
66
47
|
def html_to_markdown(input, handlers: nil, tag_library: nil)
|
|
67
|
-
raise ArgumentError, "input cannot be nil" if input.nil?
|
|
68
|
-
|
|
69
|
-
handlers ||= default_html_handlers
|
|
70
|
-
tag_library ||= default_tag_library
|
|
71
|
-
|
|
72
48
|
ast = parse_html(input, handlers:)
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
# Clean up output
|
|
76
|
-
result = renderer.render(ast)
|
|
77
|
-
cleanup_markdown(result)
|
|
49
|
+
render_to_markdown(ast, tag_library:)
|
|
78
50
|
end
|
|
79
51
|
|
|
80
52
|
# Parse s9e/TextFormatter XML to AST
|
|
@@ -82,13 +54,8 @@ module Markbridge
|
|
|
82
54
|
# @param handlers [Parsers::TextFormatter::HandlerRegistry, nil] custom handler registry or use default
|
|
83
55
|
# @return [AST::Document]
|
|
84
56
|
def parse_text_formatter_xml(input, handlers: nil)
|
|
85
|
-
raise ArgumentError, "input cannot be nil" if input.nil?
|
|
86
|
-
|
|
87
|
-
input = input.to_s
|
|
88
57
|
handlers ||= default_text_formatter_handlers
|
|
89
|
-
|
|
90
|
-
parser = Parsers::TextFormatter::Parser.new(handlers:)
|
|
91
|
-
parser.parse(input)
|
|
58
|
+
parse_with(Parsers::TextFormatter::Parser, input, handlers:)
|
|
92
59
|
end
|
|
93
60
|
|
|
94
61
|
# Convert s9e/TextFormatter XML to Discourse Markdown
|
|
@@ -97,16 +64,8 @@ module Markbridge
|
|
|
97
64
|
# @param tag_library [TagLibrary, nil] custom tag library or use default
|
|
98
65
|
# @return [String] Markdown output
|
|
99
66
|
def text_formatter_xml_to_markdown(input, handlers: nil, tag_library: nil)
|
|
100
|
-
raise ArgumentError, "input cannot be nil" if input.nil?
|
|
101
|
-
|
|
102
|
-
handlers ||= default_text_formatter_handlers
|
|
103
|
-
tag_library ||= default_tag_library
|
|
104
|
-
|
|
105
67
|
ast = parse_text_formatter_xml(input, handlers:)
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
result = renderer.render(ast)
|
|
109
|
-
cleanup_markdown(result)
|
|
68
|
+
render_to_markdown(ast, tag_library:)
|
|
110
69
|
end
|
|
111
70
|
|
|
112
71
|
# Parse MediaWiki wikitext to AST
|
|
@@ -125,15 +84,8 @@ module Markbridge
|
|
|
125
84
|
# @param tag_library [TagLibrary, nil] custom tag library or use default
|
|
126
85
|
# @return [String] Markdown output
|
|
127
86
|
def mediawiki_to_markdown(input, tag_library: nil)
|
|
128
|
-
raise ArgumentError, "input cannot be nil" if input.nil?
|
|
129
|
-
|
|
130
|
-
tag_library ||= default_tag_library
|
|
131
|
-
|
|
132
87
|
ast = parse_mediawiki(input)
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
result = renderer.render(ast)
|
|
136
|
-
cleanup_markdown(result)
|
|
88
|
+
render_to_markdown(ast, tag_library:)
|
|
137
89
|
end
|
|
138
90
|
|
|
139
91
|
# Get default handler registry
|
|
@@ -183,6 +135,19 @@ module Markbridge
|
|
|
183
135
|
|
|
184
136
|
private
|
|
185
137
|
|
|
138
|
+
def parse_with(parser_class, input, handlers:)
|
|
139
|
+
raise ArgumentError, "input cannot be nil" if input.nil?
|
|
140
|
+
|
|
141
|
+
parser = parser_class.new(handlers:)
|
|
142
|
+
parser.parse(input.to_s)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def render_to_markdown(ast, tag_library: nil)
|
|
146
|
+
tag_library ||= default_tag_library
|
|
147
|
+
renderer = build_renderer(tag_library:)
|
|
148
|
+
cleanup_markdown(renderer.render(ast))
|
|
149
|
+
end
|
|
150
|
+
|
|
186
151
|
def build_renderer(tag_library:)
|
|
187
152
|
escaper =
|
|
188
153
|
Renderers::Discourse::MarkdownEscaper.new(
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: markbridge
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Discourse Team
|
|
@@ -47,6 +47,7 @@ files:
|
|
|
47
47
|
- lib/markbridge/ast/strikethrough.rb
|
|
48
48
|
- lib/markbridge/ast/subscript.rb
|
|
49
49
|
- lib/markbridge/ast/superscript.rb
|
|
50
|
+
- lib/markbridge/ast/table.rb
|
|
50
51
|
- lib/markbridge/ast/text.rb
|
|
51
52
|
- lib/markbridge/ast/underline.rb
|
|
52
53
|
- lib/markbridge/ast/upload.rb
|
|
@@ -75,6 +76,9 @@ files:
|
|
|
75
76
|
- lib/markbridge/parsers/bbcode/handlers/simple_handler.rb
|
|
76
77
|
- lib/markbridge/parsers/bbcode/handlers/size_handler.rb
|
|
77
78
|
- lib/markbridge/parsers/bbcode/handlers/spoiler_handler.rb
|
|
79
|
+
- lib/markbridge/parsers/bbcode/handlers/table_cell_handler.rb
|
|
80
|
+
- lib/markbridge/parsers/bbcode/handlers/table_handler.rb
|
|
81
|
+
- lib/markbridge/parsers/bbcode/handlers/table_row_handler.rb
|
|
78
82
|
- lib/markbridge/parsers/bbcode/handlers/url_handler.rb
|
|
79
83
|
- lib/markbridge/parsers/bbcode/parser.rb
|
|
80
84
|
- lib/markbridge/parsers/bbcode/parser_state.rb
|
|
@@ -96,6 +100,9 @@ files:
|
|
|
96
100
|
- lib/markbridge/parsers/html/handlers/quote_handler.rb
|
|
97
101
|
- lib/markbridge/parsers/html/handlers/raw_handler.rb
|
|
98
102
|
- lib/markbridge/parsers/html/handlers/simple_handler.rb
|
|
103
|
+
- lib/markbridge/parsers/html/handlers/table_cell_handler.rb
|
|
104
|
+
- lib/markbridge/parsers/html/handlers/table_handler.rb
|
|
105
|
+
- lib/markbridge/parsers/html/handlers/table_row_handler.rb
|
|
99
106
|
- lib/markbridge/parsers/html/handlers/url_handler.rb
|
|
100
107
|
- lib/markbridge/parsers/html/parser.rb
|
|
101
108
|
- lib/markbridge/parsers/media_wiki.rb
|
|
@@ -112,6 +119,7 @@ files:
|
|
|
112
119
|
- lib/markbridge/parsers/text_formatter/handlers/list_handler.rb
|
|
113
120
|
- lib/markbridge/parsers/text_formatter/handlers/quote_handler.rb
|
|
114
121
|
- lib/markbridge/parsers/text_formatter/handlers/simple_handler.rb
|
|
122
|
+
- lib/markbridge/parsers/text_formatter/handlers/table_cell_handler.rb
|
|
115
123
|
- lib/markbridge/parsers/text_formatter/handlers/url_handler.rb
|
|
116
124
|
- lib/markbridge/parsers/text_formatter/parser.rb
|
|
117
125
|
- lib/markbridge/processors.rb
|
|
@@ -154,6 +162,9 @@ files:
|
|
|
154
162
|
- lib/markbridge/renderers/discourse/tags/strikethrough_tag.rb
|
|
155
163
|
- lib/markbridge/renderers/discourse/tags/subscript_tag.rb
|
|
156
164
|
- lib/markbridge/renderers/discourse/tags/superscript_tag.rb
|
|
165
|
+
- lib/markbridge/renderers/discourse/tags/table_cell_tag.rb
|
|
166
|
+
- lib/markbridge/renderers/discourse/tags/table_row_tag.rb
|
|
167
|
+
- lib/markbridge/renderers/discourse/tags/table_tag.rb
|
|
157
168
|
- lib/markbridge/renderers/discourse/tags/underline_tag.rb
|
|
158
169
|
- lib/markbridge/renderers/discourse/tags/upload_tag.rb
|
|
159
170
|
- lib/markbridge/renderers/discourse/tags/url_tag.rb
|