diff --git a/lib/reverse_markdown/converters/base.rb b/lib/reverse_markdown/converters/base.rb index cfdf7c9..1d1e4fe 100644 --- a/lib/reverse_markdown/converters/base.rb +++ b/lib/reverse_markdown/converters/base.rb @@ -15,6 +15,26 @@ def escape_keychars(string) string.gsub(/(? '\*', '_' => '\_') end + # Wrap content with markers (e.g., ** or _), splitting at paragraph breaks + # so markers don't span across breaks (which breaks markdown rendering) + def wrap_with_markers(content, marker) + # Split on paragraph breaks, preserving the breaks + segments = content.split(/(\s*\n\s*\n\s*)/) + + segments.map.with_index do |segment, i| + if i.odd? # This is a break segment (captured delimiter) + segment + elsif segment.strip.empty? + segment + else + # Wrap with markers, preserving border whitespace + leading = segment[/\A\s*/] + trailing = segment[/\s*\z/] + "#{leading}#{marker}#{segment.strip}#{marker}#{trailing}" + end + end.join + end + def extract_title(node) title = escape_keychars(node['title'].to_s) title.empty? ? '' : %[ "#{title}"] diff --git a/lib/reverse_markdown/converters/em.rb b/lib/reverse_markdown/converters/em.rb index e31582a..5d7167a 100644 --- a/lib/reverse_markdown/converters/em.rb +++ b/lib/reverse_markdown/converters/em.rb @@ -6,7 +6,7 @@ def convert(node, state = {}) if content.strip.empty? || state[:already_italic] content else - "#{content[/^\s*/]}_#{content.strip}_#{content[/\s*$/]}" + wrap_with_markers(content, '_') end end end diff --git a/lib/reverse_markdown/converters/h.rb b/lib/reverse_markdown/converters/h.rb index 1aa50c3..bf929aa 100644 --- a/lib/reverse_markdown/converters/h.rb +++ b/lib/reverse_markdown/converters/h.rb @@ -3,7 +3,10 @@ module Converters class H < Base def convert(node, state = {}) prefix = '#' * node.name[/\d/].to_i - ["\n", prefix, ' ', treat_children(node, state), "\n"].join + content = treat_children(node, state).strip + # Merge lines into one (markdown headings can't span multiple lines) + content = content.split(/\s*\n\s*/).join(' ') + "\n#{prefix} #{content}\n" end end diff --git a/lib/reverse_markdown/converters/strong.rb b/lib/reverse_markdown/converters/strong.rb index b513096..6993939 100644 --- a/lib/reverse_markdown/converters/strong.rb +++ b/lib/reverse_markdown/converters/strong.rb @@ -6,7 +6,7 @@ def convert(node, state = {}) if content.strip.empty? || state[:already_strong] content else - "#{content[/^\s*/]}**#{content.strip}**#{content[/\s*$/]}" + wrap_with_markers(content, '**') end end end diff --git a/spec/lib/reverse_markdown/converters/em_spec.rb b/spec/lib/reverse_markdown/converters/em_spec.rb new file mode 100644 index 0000000..ab98c2e --- /dev/null +++ b/spec/lib/reverse_markdown/converters/em_spec.rb @@ -0,0 +1,28 @@ +require 'spec_helper' + +describe ReverseMarkdown::Converters::Em do + let(:converter) { ReverseMarkdown::Converters::Em.new } + + it 'returns an empty string if the node is empty' do + input = node_for('') + expect(converter.convert(input)).to eq '' + end + + it 'returns just the content if the em tag is nested in another em' do + input = node_for('foo') + expect(converter.convert(input.children.first, already_italic: true)).to eq 'foo' + end + + it 'moves border whitespaces outside of the delimiters tag' do + input = node_for(" \n foo ") + expect(converter.convert(input)).to eq " _foo_ " + end + + it 'splits markers at paragraph breaks' do + # Issue #95:

inside em creates a paragraph break + # Markers must be split so markdown renders correctly + result = ReverseMarkdown.convert('hello

world
') + expect(result).to include('_hello_') + expect(result).to include('_world_') + end +end diff --git a/spec/lib/reverse_markdown/converters/h_spec.rb b/spec/lib/reverse_markdown/converters/h_spec.rb new file mode 100644 index 0000000..9f87090 --- /dev/null +++ b/spec/lib/reverse_markdown/converters/h_spec.rb @@ -0,0 +1,16 @@ +require 'spec_helper' + +describe ReverseMarkdown::Converters::H do + let(:converter) { ReverseMarkdown::Converters::H.new } + + it 'merges line breaks into single line' do + # Markdown headings can't span multiple lines, so merge them + result = ReverseMarkdown.convert('

foo
bar

') + expect(result.strip).to eq '# foo bar' + end + + it 'handles multiple line breaks' do + result = ReverseMarkdown.convert('

a
b
c

') + expect(result.strip).to eq '## a b c' + end +end diff --git a/spec/lib/reverse_markdown/converters/strong_spec.rb b/spec/lib/reverse_markdown/converters/strong_spec.rb index ecee4e0..6d156ac 100644 --- a/spec/lib/reverse_markdown/converters/strong_spec.rb +++ b/spec/lib/reverse_markdown/converters/strong_spec.rb @@ -17,4 +17,12 @@ input = node_for(" \n foo ") expect(converter.convert(input)).to eq " **foo** " end + + it 'splits markers at paragraph breaks' do + # Issue #95:

inside strong creates a paragraph break + # Markers must be split so markdown renders correctly + result = ReverseMarkdown.convert('hello

world
') + expect(result).to include('**hello**') + expect(result).to include('**world**') + end end