markdown-ruby-china 0.1 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ # coding: utf-8
2
+
3
+ source 'https://rubygems.org'
4
+ gemspec
5
+
@@ -0,0 +1,16 @@
1
+ Markdown render from Ruby China
2
+ ============================
3
+
4
+ ## 如何使用
5
+ ### 添加到Gemfile
6
+ ```ruby
7
+ gem 'markdown-ruby-china'
8
+ ```
9
+
10
+ ### 调用
11
+ ```ruby
12
+ MarkdownTopicConverter.format(content)
13
+ ```
14
+
15
+ ## 新功能
16
+ 1. 代码高亮 自动识别程序语言,文件名,别名等多种格式。
@@ -0,0 +1,244 @@
1
+ # coding: utf-8
2
+
3
+ require 'rails'
4
+ require 'rails_autolink'
5
+ require 'redcarpet'
6
+ require 'singleton'
7
+ require 'md_emoji'
8
+ require 'pygments'
9
+ require "nokogiri"
10
+
11
+ Pygments::Lexer.create :name => "XML", :filenames => ["*.xaml"], :aliases => ["xml"], :mimetypes => ["application/xml+evoque"]
12
+
13
+ module Redcarpet
14
+ module Render
15
+ class HTMLwithSyntaxHighlight < HTML
16
+
17
+ def initialize(extensions={})
18
+ super(extensions.merge(:xhtml => true,
19
+ :no_styles => true,
20
+ :filter_html => true,
21
+ :hard_wrap => true))
22
+ end
23
+
24
+ # 如果换行符没识别,那么code代码块将不会被这里调用
25
+ def block_code(code, lexer)
26
+ lexer = begin
27
+ # 支持程序语言,文件名,别名等多种格式
28
+ lexer = (Pygments::Lexer.find(lexer) || Pygments::Lexer.find_by_extname(".#{lexer}") || Pygments::Lexer.find_by_alias(lexer.downcase))
29
+ lexer.aliases[0]
30
+ rescue
31
+ lexer.to_s.split('.')[-1] || 'text'
32
+ end
33
+
34
+ opts = {:lexer => lexer, :formatter => 'html', :options => {:encoding => 'utf-8', :linenos => 'table'}}
35
+ begin
36
+ Pygments.highlight(code, opts)
37
+ rescue => e
38
+ puts :language => lexer, :code => code
39
+ puts e
40
+ puts e.backtrace
41
+ Pygments.highlight(code, opts.merge(:lexer => 'text'))
42
+ end
43
+ end
44
+
45
+ def autolink(link, link_type)
46
+ # return link
47
+ if link_type.to_s == "email"
48
+ link
49
+ else
50
+ begin
51
+ # 防止 C 的 autolink 出来的内容有编码错误,万一有就直接跳过转换
52
+ # 比如这句:
53
+ # 此版本并非线上的http://yavaeye.com的源码.
54
+ link.match(/.+?/)
55
+ rescue
56
+ return link
57
+ end
58
+ # Fix Chinese neer the URL
59
+ bad_text = link.to_s.match(/[^\w:\/\-\,\$\!\.=\?&#+\|\%]+/im).to_s
60
+ link = link.to_s.gsub(bad_text, '')
61
+ "<a href=\"#{link}\" rel=\"nofollow\" target=\"_blank\">#{link}</a>#{bad_text}"
62
+ end
63
+ end
64
+ end
65
+
66
+ class HTMLwithTopic < HTMLwithSyntaxHighlight
67
+ # Topic 里面,所有的 head 改为 h4 显示
68
+ def header(text, header_level)
69
+ "<h4>#{text}</h4>"
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ class MarkdownConverter
76
+ include Singleton
77
+
78
+ def self.convert(text)
79
+ self.instance.convert(text)
80
+ end
81
+
82
+ def convert(text)
83
+ @converter.render(text)
84
+ end
85
+
86
+ private
87
+ def initialize
88
+ @converter = Redcarpet::Markdown.new(Redcarpet::Render::HTMLwithSyntaxHighlight, {
89
+ :autolink => true,
90
+ :fenced_code_blocks => true,
91
+ :no_intra_emphasis => true
92
+ })
93
+ end
94
+
95
+ end
96
+
97
+ class MarkdownTopicConverter < MarkdownConverter
98
+ def self.format(raw)
99
+ self.instance.format(raw)
100
+ end
101
+
102
+ def format(raw)
103
+ text = raw.clone
104
+ return '' if text.blank?
105
+
106
+ convert_bbcode_img(text)
107
+ users = nomalize_user_mentions(text)
108
+
109
+ # 如果 ``` 在刚刚换行的时候 Redcapter 无法生成正确,需要两个换行
110
+ text.gsub!("\n```","\n\n```")
111
+
112
+ # 调用MarkdownConverter父类来高亮代码
113
+ result = convert(text)
114
+
115
+ doc = Nokogiri::HTML.fragment(result)
116
+ link_mention_floor(doc)
117
+ link_mention_user(doc, users)
118
+ replace_emoji(doc)
119
+
120
+ return doc.to_html.strip
121
+ rescue => e
122
+ puts "MarkdownTopicConverter.format ERROR: #{e}"
123
+ return text
124
+ end
125
+
126
+ private
127
+ # convert bbcode-style image tag [img]url[/img] to markdown syntax ![alt](url)
128
+ def convert_bbcode_img(text)
129
+ text.gsub!(/\[img\](.+?)\[\/img\]/i) {"![#{image_alt $1}](#{$1})"}
130
+ end
131
+
132
+ def image_alt(src)
133
+ File.basename(src, '.*').capitalize
134
+ end
135
+
136
+ # borrow from html-pipeline
137
+ def has_ancestors?(node, tags)
138
+ while node = node.parent
139
+ if tags.include?(node.name.downcase)
140
+ break true
141
+ end
142
+ end
143
+ end
144
+
145
+ # convert '#N楼' to link
146
+ # Refer to emoji_filter in html-pipeline
147
+ def link_mention_floor(doc)
148
+ doc.search('text()').each do |node|
149
+ content = node.to_html
150
+ next if !content.include?('#')
151
+ next if has_ancestors?(node, %w(pre code))
152
+
153
+ html = content.gsub(/#(\d+)([楼樓Ff])/) {
154
+ %(<a href="#reply#{$1}" class="at_floor" data-floor="#{$1}">##{$1}#{$2}</a>)
155
+ }
156
+
157
+ next if html == content
158
+ node.replace(html)
159
+ end
160
+ end
161
+
162
+ NOMALIZE_USER_REGEXP = /(^|[^a-zA-Z0-9_!#\$%&*@@])@([a-zA-Z0-9_]{1,20})/io
163
+ LINK_USER_REGEXP = /(^|[^a-zA-Z0-9_!#\$%&*@@])@(user[0-9]{1,6})/io
164
+
165
+ # rename user name using incremental id
166
+ def nomalize_user_mentions(text)
167
+ users = []
168
+
169
+ text.gsub!(NOMALIZE_USER_REGEXP) do
170
+ prefix = $1
171
+ user = $2
172
+ users.push(user)
173
+ "#{prefix}@user#{users.size}"
174
+ end
175
+
176
+ users
177
+ end
178
+
179
+ # convert '@user' to link
180
+ # match any user even not exist.
181
+ def link_mention_user(doc, users)
182
+ doc.search('text()').each do |node|
183
+ content = node.to_html
184
+ next if !content.include?('@')
185
+ in_code = has_ancestors?(node, %w(pre code))
186
+ html = content.gsub(LINK_USER_REGEXP) {
187
+ prefix = $1
188
+ user_placeholder = $2
189
+ user_id = user_placeholder.sub(/^user/, '').to_i
190
+ user = users[user_id - 1] || user_placeholder
191
+
192
+ if in_code
193
+ "#{prefix}@#{user}"
194
+ else
195
+ %(#{prefix}<a href="/#{user}" class="at_user" title="@#{user}"><i>@</i>#{user}</a>)
196
+ end
197
+ }
198
+
199
+ node.replace(html)
200
+ end
201
+ end
202
+
203
+ def replace_emoji(doc)
204
+ doc.search('text()').each do |node|
205
+ content = node.to_html
206
+ next if !content.include?(':')
207
+ next if has_ancestors?(node, %w(pre code))
208
+
209
+ html = content.gsub(/:(\S+):/) do |emoji|
210
+
211
+ emoji_code = emoji #.gsub("|", "_")
212
+ emoji = emoji_code.gsub(":", "")
213
+
214
+ if MdEmoji::EMOJI.include?(emoji)
215
+ file_name = "#{emoji.gsub('+', 'plus')}.png"
216
+
217
+ %{<img src="#{upload_url}/assets/emojis/#{file_name}" class="emoji" } +
218
+ %{title="#{emoji_code}" alt="" />}
219
+ else
220
+ emoji_code
221
+ end
222
+ end
223
+
224
+ next if html == content
225
+ node.replace(html)
226
+ end
227
+ end
228
+
229
+ # for testing
230
+ def upload_url
231
+ Setting.upload_url
232
+ end
233
+
234
+ def initialize
235
+ @converter = Redcarpet::Markdown.new(Redcarpet::Render::HTMLwithTopic.new, {
236
+ :autolink => true,
237
+ :fenced_code_blocks => true,
238
+ :strikethrough => true,
239
+ :space_after_headers => true,
240
+ :no_intra_emphasis => true
241
+ })
242
+ @emoji = MdEmoji::Render.new
243
+ end
244
+ end
@@ -0,0 +1,19 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'markdown-ruby-china'
3
+ s.version = '0.2'
4
+ s.date = '2013-04-17'
5
+ s.summary = ""
6
+ s.description = ""
7
+ s.authors = ["Ruby-China team", "David Chen"]
8
+ s.email = 'mvjome@gmail.com'
9
+ s.homepage = 'https://github.com/eoecn/markdown-ruby-china'
10
+ s.require_paths = ["lib"]
11
+
12
+ s.add_dependency "rails_autolink"
13
+ s.add_dependency 'redcarpet'
14
+ s.add_dependency 'md_emoji'
15
+ s.add_dependency 'pygments.rb'
16
+ s.add_dependency "nokogiri"
17
+
18
+ s.files = `git ls-files`.split("\n")
19
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: markdown-ruby-china
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.1'
4
+ version: '0.2'
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -97,7 +97,12 @@ email: mvjome@gmail.com
97
97
  executables: []
98
98
  extensions: []
99
99
  extra_rdoc_files: []
100
- files: []
100
+ files:
101
+ - .gitignore
102
+ - Gemfile
103
+ - README.markdown
104
+ - lib/markdown-ruby-china.rb
105
+ - markdown-ruby-china.gemspec
101
106
  homepage: https://github.com/eoecn/markdown-ruby-china
102
107
  licenses: []
103
108
  post_install_message: