3

Is there a way to use either Redcarpet or Bluecloth such that when it interpolates the markdown it won't make any headers?

For example:

#header 1

yields:

header 1

header 1 (preferred)

And:

##header 2

yields:

header 2

header 2 (preferred)

Community
  • 1
  • 1
Cyrus
  • 3,687
  • 5
  • 35
  • 67

4 Answers4

5

Well, you can escape characters in Markdown:

# header 1
\# header 1

## header 2
\## header 2

...gives:

header 1

# header 1

header 2

## header 2

If you don't want to have to do this, or you're parsing other people's Markdown and don't have a choice, I would recommend pre-processing the incoming Markdown to do the above for you:

def pound_filter text
  text.gsub /^#/, '\#'
end

Using Redcarpet you can verify that it works:

text = <<-END
  # Hello
  ## World
END

Markdown.new(text.to_html)
# =>  <h1>Hello</h1>
#
#     <h2>World</h2>

Markdown.new(pound_filter text).to_html
# =>  <p># Hello
#     ## World</p>

Of course since a line break in HTML doesn't actually render as such--it will appear as one line:

# Hello ## World"

...you might want to augment that:

def pound_filter text
  text.gsub( /((\A^)|([^\A]^))#/ ) {|match| "\n" == match[0] ? "\n\n\\#" : '\#' }
end

pound_filter text
# =>  \# Hello
#
#     \## World

Markdown.new(pound_filter text).to_html
# =>  <p>\# Hello</p>
#
#     <p>\## World</p>

This last would appear as:

# Hello

## World

Unfortunately you eventually get into weird territory like this, where a heading is inside a quote:

> ## Heading

...but I leave that as an exercise to the reader.

Jordan Running
  • 102,619
  • 17
  • 182
  • 182
3

Saw a similar solution here that went like this:

class RenderWithoutWrap < Redcarpet::Render::HTML
  def postprocess(full_document)
    Regexp.new(/\A<p>(.*)<\/p>\Z/m).match(full_document)[1] rescue full_document
  end
end

It removes all <p> & </p> tags. I used it like that and it worked. I placed that class in a new file called /config/initializers/render_without_wrap.rb. You could do something similar for all <h1>-<h6> tags

class RenderWithoutHeaders < Redcarpet::Render::HTML
  def postprocess(full_document)
    Regexp.new(/\A<h1>(.*)<\/h1>\Z/m).match(full_document)[1] rescue full_document
    Regexp.new(/\A<h2>(.*)<\/h2>\Z/m).match(full_document)[1] rescue full_document
    Regexp.new(/\A<h3>(.*)<\/h3>\Z/m).match(full_document)[1] rescue full_document
    ...(you get the idea)
  end
end

You could then use it like this

def custom_markdown_parse(text)
  markdown = Redcarpet::Markdown.new(RenderWithoutHeaders.new(
    filter_html: true,
    hard_wrap: true,
    other_options: its_your_call
  ))
  markdown.render(text).html_safe
end

I haven't tested it, but it's an idea.

Cruz Nunez
  • 2,949
  • 1
  • 23
  • 33
  • This is a good solution. However, for heading tags you might not want the \A and \Z in the regex, simply because heading tags are not necessarily wrapping the entire piece of text the way

    tags might. Also, this doesn't work if there are multiple h2s or multiple h3s. Any idea how to make the regex work in that case?

    – DelPiero Dec 18 '15 at 21:28
  • @DelPiero Not really. I was just regurgitating what I saw. I'm sure there's a way though. Research it and post it here! – Cruz Nunez Feb 15 '16 at 18:51
1

1. You should be able to escape your markdown source text with backslashes:

\# not a header

2. You could also monkey-patch it:

module RedCloth::Formatters::HTML

  [:h1, :h2, :h3, :h4, :h5, :h6].each do |m|
    define_method(m) do |opts|
      "#{opts[:text]}\n"
    end
  end

end
Andrew Vit
  • 18,961
  • 6
  • 77
  • 84
  • Unfortunately monkey-patching HTML formatter has a side-effect of also disabling underline-style (`----`/`====`) headers, which may not be the OP's intent. To prevent parsing `##`s without affecting other headers you'd have to actually patch BlueCloth/Sundown/Discount in C. – Jordan Running Sep 24 '11 at 08:36
0

Given that twiddling the Markdown pre-parsing is hard, and Markdown allows inserted HTML, how about stripping out heading elements from the resulting HTML instead?

Kevin Reid
  • 37,492
  • 13
  • 80
  • 108