11class Markdowner
22 # opts[:allow_images] allows <img> tags
33
4- COMMONMARKER_OPTIONS = {
5- parse : { smart : true } ,
6- render : { unsafe : true } ,
7- extension : { tagfilter : true , autolink : true , strikethrough : true }
8- } . freeze
9-
104 def self . to_html ( text , opts = { } )
115 if text . blank?
126 return ""
137 end
148
15- # Preprocess @mentions before markdown parsing
16- processed_text = preprocess_mentions ( text . to_s )
9+ exts = [ :tagfilter , :autolink , :strikethrough ]
10+ root = CommonMarker . render_doc ( text . to_s , [ :SMART ] , exts )
1711
18- html = Commonmarker . to_html ( processed_text , options : COMMONMARKER_OPTIONS )
12+ walk_text_nodes ( root ) { | n | postprocess_text_node ( n ) }
1913
20- ng = Nokogiri ::HTML ( html )
14+ ng = Nokogiri ::HTML ( root . to_html ( [ :DEFAULT ] , exts ) )
2115
2216 # change <h1>, <h2>, etc. headings to just bold tags
2317 ng . css ( "h1, h2, h3, h4, h5, h6" ) . each do |h |
@@ -39,13 +33,44 @@ def self.to_html(text, opts = {})
3933 end
4034 end
4135
42- def self . preprocess_mentions ( text )
43- text . gsub ( /\B (@#{ User ::VALID_USERNAME } )/ ) do |match |
44- user = match [ 1 ..-1 ]
45- if User . exists? ( username : user )
46- "[#{ match } ](#{ Rails . application . root_url } u/#{ user } )"
36+ def self . walk_text_nodes ( node , &block )
37+ return if node . type == :link
38+ return block . call ( node ) if node . type == :text
39+
40+ node . each do |child |
41+ walk_text_nodes ( child , &block )
42+ end
43+ end
44+
45+ def self . postprocess_text_node ( node )
46+ while node
47+ return unless node . string_content =~ /\B (@#{ User ::VALID_USERNAME } )/
48+ before , user , after = $`, $1, $'
49+
50+ node . string_content = before
51+
52+ if User . exists? ( :username => user [ 1 ..-1 ] )
53+ link = CommonMarker ::Node . new ( :link )
54+ link . url = Rails . application . root_url + "u/#{ user [ 1 ..-1 ] } "
55+ node . insert_after ( link )
56+
57+ link_text = CommonMarker ::Node . new ( :text )
58+ link_text . string_content = user
59+ link . append_child ( link_text )
60+
61+ node = link
62+ else
63+ node . string_content += user
64+ end
65+
66+ if after . length > 0
67+ remainder = CommonMarker ::Node . new ( :text )
68+ remainder . string_content = after
69+ node . insert_after ( remainder )
70+
71+ node = remainder
4772 else
48- match
73+ node = nil
4974 end
5075 end
5176 end
0 commit comments