markdown-ruby-china 0.1 → 0.2
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.
- data/.gitignore +4 -0
- data/Gemfile +5 -0
- data/README.markdown +16 -0
- data/lib/markdown-ruby-china.rb +244 -0
- data/markdown-ruby-china.gemspec +19 -0
- metadata +7 -2
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.markdown
ADDED
@@ -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 
|
128
|
+
def convert_bbcode_img(text)
|
129
|
+
text.gsub!(/\[img\](.+?)\[\/img\]/i) {""}
|
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.
|
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:
|