kramdown 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of kramdown might be problematic. Click here for more details.
- data/ChangeLog +276 -0
- data/Rakefile +2 -2
- data/VERSION +1 -1
- data/benchmark/benchmark.rb +1 -1
- data/benchmark/historic-jruby-1.4.0.dat +7 -7
- data/benchmark/historic-ruby-1.8.6.dat +7 -7
- data/benchmark/historic-ruby-1.8.7.dat +7 -7
- data/benchmark/historic-ruby-1.9.1p243.dat +7 -7
- data/benchmark/historic-ruby-1.9.2dev.dat +7 -7
- data/bin/kramdown +46 -1
- data/doc/index.page +2 -2
- data/doc/syntax.page +7 -3
- data/lib/kramdown/converter.rb +6 -285
- data/lib/kramdown/converter/base.rb +75 -0
- data/lib/kramdown/converter/html.rb +325 -0
- data/lib/kramdown/converter/latex.rb +516 -0
- data/lib/kramdown/document.rb +36 -66
- data/lib/kramdown/options.rb +262 -0
- data/lib/kramdown/parser/kramdown.rb +36 -17
- data/lib/kramdown/parser/kramdown/attribute_list.rb +1 -1
- data/lib/kramdown/parser/kramdown/autolink.rb +1 -1
- data/lib/kramdown/parser/kramdown/codespan.rb +1 -1
- data/lib/kramdown/parser/kramdown/emphasis.rb +2 -2
- data/lib/kramdown/parser/kramdown/escaped_chars.rb +2 -2
- data/lib/kramdown/parser/kramdown/extension.rb +46 -2
- data/lib/kramdown/parser/kramdown/footnote.rb +1 -1
- data/lib/kramdown/parser/kramdown/html.rb +2 -2
- data/lib/kramdown/parser/kramdown/html_entity.rb +4 -5
- data/lib/kramdown/parser/kramdown/line_break.rb +1 -1
- data/lib/kramdown/parser/kramdown/link.rb +2 -2
- data/lib/kramdown/parser/kramdown/smart_quotes.rb +213 -0
- data/lib/kramdown/parser/kramdown/typographic_symbol.rb +1 -1
- data/lib/kramdown/version.rb +1 -1
- data/test/testcases/encoding.html +46 -0
- data/test/testcases/encoding.text +28 -0
- data/test/testcases/span/01_link/inline.html +1 -1
- data/test/testcases/span/01_link/reference.html +2 -2
- data/test/testcases/span/escaped_chars/normal.html +4 -0
- data/test/testcases/span/escaped_chars/normal.text +4 -0
- data/test/testcases/span/text_substitutions/entities.html +1 -1
- data/test/testcases/span/text_substitutions/typography.html +12 -0
- data/test/testcases/span/text_substitutions/typography.text +12 -0
- metadata +9 -3
- data/lib/kramdown/extension.rb +0 -98
@@ -42,7 +42,7 @@ module Kramdown
|
|
42
42
|
el = Element.new(elem)
|
43
43
|
stop_re = /#{Regexp.escape(delim)}/
|
44
44
|
found = parse_spans(el, stop_re) do
|
45
|
-
(@src.
|
45
|
+
(@src.pre_match[-1, 1] !~ /\s/) &&
|
46
46
|
(elem != :em || !@src.match?(/#{Regexp.escape(delim*2)}(?!#{Regexp.escape(delim)})/)) &&
|
47
47
|
(type != '_' || !@src.match?(/#{Regexp.escape(delim)}[[:alpha:]]/)) && el.children.size > 0
|
48
48
|
end
|
@@ -62,7 +62,7 @@ module Kramdown
|
|
62
62
|
add_text(result)
|
63
63
|
end
|
64
64
|
end
|
65
|
-
define_parser(:emphasis, EMPHASIS_START)
|
65
|
+
define_parser(:emphasis, EMPHASIS_START, '\*|_')
|
66
66
|
|
67
67
|
end
|
68
68
|
end
|
@@ -24,14 +24,14 @@ module Kramdown
|
|
24
24
|
module Parser
|
25
25
|
class Kramdown
|
26
26
|
|
27
|
-
ESCAPED_CHARS = /\\([\\.*_+`()\[\]{}
|
27
|
+
ESCAPED_CHARS = /\\([\\.*_+`()\[\]{}#!:|"'-])/
|
28
28
|
|
29
29
|
# Parse the backslash-escaped character at the current location.
|
30
30
|
def parse_escaped_chars
|
31
31
|
@src.pos += @src.matched_size
|
32
32
|
add_text(@src[1])
|
33
33
|
end
|
34
|
-
define_parser(:escaped_chars, ESCAPED_CHARS)
|
34
|
+
define_parser(:escaped_chars, ESCAPED_CHARS, '\\\\')
|
35
35
|
|
36
36
|
end
|
37
37
|
end
|
@@ -26,6 +26,50 @@ module Kramdown
|
|
26
26
|
module Parser
|
27
27
|
class Kramdown
|
28
28
|
|
29
|
+
# The base extension class.
|
30
|
+
#
|
31
|
+
# This class provides implementations for the default extensions defined in the kramdown
|
32
|
+
# specification.
|
33
|
+
#
|
34
|
+
# An extension is a method called <tt>parse_EXTNAME</tt> where +EXTNAME+ is the extension name.
|
35
|
+
# These methods are called with three parameters:
|
36
|
+
#
|
37
|
+
# [+parser+]
|
38
|
+
# The parser instance from which the extension method is called.
|
39
|
+
# [+opts+]
|
40
|
+
# A hash containing the options set in the extension.
|
41
|
+
# [+body+]
|
42
|
+
# A string containing the body of the extension. If no body is available, this is +nil+.
|
43
|
+
class Extension
|
44
|
+
|
45
|
+
# Just ignore everything and do nothing.
|
46
|
+
def parse_comment(parser, opts, body)
|
47
|
+
nil
|
48
|
+
end
|
49
|
+
|
50
|
+
# Add the body (if available) as <tt>:raw</tt> Element to the +parser.tree+.
|
51
|
+
def parse_nomarkdown(parser, opts, body)
|
52
|
+
parser.tree.children << Element.new(:raw, body) if body.kind_of?(String)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Update the document and parser options with the options set in +opts+.
|
56
|
+
def parse_options(parser, opts, body)
|
57
|
+
opts.select do |k,v|
|
58
|
+
k = k.to_sym
|
59
|
+
if Kramdown::Options.defined?(k)
|
60
|
+
parser.doc.options[k] = Kramdown::Options.parse(k, v) rescue parser.doc.options[k]
|
61
|
+
false
|
62
|
+
else
|
63
|
+
true
|
64
|
+
end
|
65
|
+
end.each do |k,v|
|
66
|
+
parser.warning("Unknown kramdown option '#{k}'")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
|
29
73
|
EXT_BLOCK_START_STR = "^#{OPT_SPACE}\\{::(%s):(:)?(#{ALD_ANY_CHARS}*)\\}\s*?\n"
|
30
74
|
EXT_BLOCK_START = /#{EXT_BLOCK_START_STR % ALD_ID_NAME}/
|
31
75
|
|
@@ -38,7 +82,7 @@ module Kramdown
|
|
38
82
|
body = nil
|
39
83
|
parse_attribute_list(@src[3], opts)
|
40
84
|
|
41
|
-
if !@
|
85
|
+
if !@extension.public_methods.map {|m| m.to_s}.include?("parse_#{ext}")
|
42
86
|
warning("No extension named '#{ext}' found - ignoring extension block")
|
43
87
|
body = :invalid
|
44
88
|
end
|
@@ -54,7 +98,7 @@ module Kramdown
|
|
54
98
|
end
|
55
99
|
end
|
56
100
|
|
57
|
-
@
|
101
|
+
@extension.send("parse_#{ext}", self, opts, body) if body != :invalid
|
58
102
|
|
59
103
|
true
|
60
104
|
end
|
@@ -146,7 +146,7 @@ module Kramdown
|
|
146
146
|
end
|
147
147
|
elsif parse_type == :span
|
148
148
|
if result = @src.scan_until(/(?=<\/#{el.value}\s*>)/m)
|
149
|
-
add_text(
|
149
|
+
add_text(extract_string(curpos...@src.pos), el)
|
150
150
|
@src.scan(HTML_TAG_CLOSE_RE)
|
151
151
|
else
|
152
152
|
add_text(@src.scan(/.*/m), el)
|
@@ -246,7 +246,7 @@ module Kramdown
|
|
246
246
|
add_text(@src.scan(/./))
|
247
247
|
end
|
248
248
|
end
|
249
|
-
define_parser(:span_html, HTML_SPAN_START)
|
249
|
+
define_parser(:span_html, HTML_SPAN_START, '<')
|
250
250
|
|
251
251
|
end
|
252
252
|
end
|
@@ -20,19 +20,18 @@
|
|
20
20
|
#++
|
21
21
|
#
|
22
22
|
|
23
|
-
require 'rexml/parsers/baseparser'
|
24
|
-
|
25
23
|
module Kramdown
|
26
24
|
module Parser
|
27
25
|
class Kramdown
|
28
26
|
|
27
|
+
HTML_ENTITY_RE = /&([\w:][\-\w\d\.:]*);|&#(\d+);|&\#x([0-9a-fA-F]+);/
|
28
|
+
|
29
29
|
# Parse the HTML entity at the current location.
|
30
30
|
def parse_html_entity
|
31
31
|
@src.pos += @src.matched_size
|
32
|
-
@tree.children << Element.new(:entity, @src.
|
32
|
+
@tree.children << Element.new(:entity, @src[1] || (@src[2] && @src[2].to_i) || @src[3].hex)
|
33
33
|
end
|
34
|
-
define_parser(:html_entity,
|
35
|
-
|
34
|
+
define_parser(:html_entity, HTML_ENTITY_RE, '&')
|
36
35
|
|
37
36
|
end
|
38
37
|
end
|
@@ -91,7 +91,7 @@ module Kramdown
|
|
91
91
|
add_text(result)
|
92
92
|
return
|
93
93
|
end
|
94
|
-
alt_text =
|
94
|
+
alt_text = extract_string(reset_pos...@src.pos)
|
95
95
|
conv_link_id = alt_text.gsub(/(\s|\n)+/m, ' ').gsub(LINK_ID_NON_CHARS, '').downcase
|
96
96
|
@src.scan(stop_re)
|
97
97
|
|
@@ -146,7 +146,7 @@ module Kramdown
|
|
146
146
|
add_text(result)
|
147
147
|
end
|
148
148
|
end
|
149
|
-
define_parser(:link, LINK_START)
|
149
|
+
define_parser(:link, LINK_START, '!?\[')
|
150
150
|
|
151
151
|
end
|
152
152
|
end
|
@@ -0,0 +1,213 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
#
|
3
|
+
#--
|
4
|
+
# Copyright (C) 2009 Thomas Leitner <t_leitner@gmx.at>
|
5
|
+
#
|
6
|
+
# This file is part of kramdown.
|
7
|
+
#
|
8
|
+
# kramdown is free software: you can redistribute it and/or modify
|
9
|
+
# it under the terms of the GNU General Public License as published by
|
10
|
+
# the Free Software Foundation, either version 3 of the License, or
|
11
|
+
# (at your option) any later version.
|
12
|
+
#
|
13
|
+
# This program is distributed in the hope that it will be useful,
|
14
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
15
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
16
|
+
# GNU General Public License for more details.
|
17
|
+
#
|
18
|
+
# You should have received a copy of the GNU General Public License
|
19
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
20
|
+
#
|
21
|
+
#
|
22
|
+
# Parts of this file are based on code from Maruku by Andrea Censi.
|
23
|
+
# The needed license statements follow:
|
24
|
+
#
|
25
|
+
# Copyright (C) 2006 Andrea Censi <andrea (at) rubyforge.org>
|
26
|
+
#
|
27
|
+
# Maruku is free software; you can redistribute it and/or modify
|
28
|
+
# it under the terms of the GNU General Public License as published by
|
29
|
+
# the Free Software Foundation; either version 2 of the License, or
|
30
|
+
# (at your option) any later version.
|
31
|
+
#
|
32
|
+
# Maruku is distributed in the hope that it will be useful,
|
33
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
34
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
35
|
+
# GNU General Public License for more details.
|
36
|
+
#
|
37
|
+
# You should have received a copy of the GNU General Public License
|
38
|
+
# along with Maruku; if not, write to the Free Software
|
39
|
+
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
40
|
+
#
|
41
|
+
# NOTA BENE:
|
42
|
+
#
|
43
|
+
# The following algorithm is a rip-off of RubyPants written by
|
44
|
+
# Christian Neukirchen.
|
45
|
+
#
|
46
|
+
# RubyPants is a Ruby port of SmartyPants written by John Gruber.
|
47
|
+
#
|
48
|
+
# This file is distributed under the GPL, which I guess is compatible
|
49
|
+
# with the terms of the RubyPants license.
|
50
|
+
#
|
51
|
+
# -- Andrea Censi
|
52
|
+
#
|
53
|
+
# = RubyPants -- SmartyPants ported to Ruby
|
54
|
+
#
|
55
|
+
# Ported by Christian Neukirchen <mailto:chneukirchen@gmail.com>
|
56
|
+
# Copyright (C) 2004 Christian Neukirchen
|
57
|
+
#
|
58
|
+
# Incooporates ideas, comments and documentation by Chad Miller
|
59
|
+
# Copyright (C) 2004 Chad Miller
|
60
|
+
#
|
61
|
+
# Original SmartyPants by John Gruber
|
62
|
+
# Copyright (C) 2003 John Gruber
|
63
|
+
#
|
64
|
+
#
|
65
|
+
# = RubyPants -- SmartyPants ported to Ruby
|
66
|
+
#
|
67
|
+
#
|
68
|
+
# [snip]
|
69
|
+
#
|
70
|
+
# == Authors
|
71
|
+
#
|
72
|
+
# John Gruber did all of the hard work of writing this software in
|
73
|
+
# Perl for Movable Type and almost all of this useful documentation.
|
74
|
+
# Chad Miller ported it to Python to use with Pyblosxom.
|
75
|
+
#
|
76
|
+
# Christian Neukirchen provided the Ruby port, as a general-purpose
|
77
|
+
# library that follows the *Cloth API.
|
78
|
+
#
|
79
|
+
#
|
80
|
+
# == Copyright and License
|
81
|
+
#
|
82
|
+
# === SmartyPants license:
|
83
|
+
#
|
84
|
+
# Copyright (c) 2003 John Gruber
|
85
|
+
# (http://daringfireball.net)
|
86
|
+
# All rights reserved.
|
87
|
+
#
|
88
|
+
# Redistribution and use in source and binary forms, with or without
|
89
|
+
# modification, are permitted provided that the following conditions
|
90
|
+
# are met:
|
91
|
+
#
|
92
|
+
# * Redistributions of source code must retain the above copyright
|
93
|
+
# notice, this list of conditions and the following disclaimer.
|
94
|
+
#
|
95
|
+
# * Redistributions in binary form must reproduce the above copyright
|
96
|
+
# notice, this list of conditions and the following disclaimer in
|
97
|
+
# the documentation and/or other materials provided with the
|
98
|
+
# distribution.
|
99
|
+
#
|
100
|
+
# * Neither the name "SmartyPants" nor the names of its contributors
|
101
|
+
# may be used to endorse or promote products derived from this
|
102
|
+
# software without specific prior written permission.
|
103
|
+
#
|
104
|
+
# This software is provided by the copyright holders and contributors
|
105
|
+
# "as is" and any express or implied warranties, including, but not
|
106
|
+
# limited to, the implied warranties of merchantability and fitness
|
107
|
+
# for a particular purpose are disclaimed. In no event shall the
|
108
|
+
# copyright owner or contributors be liable for any direct, indirect,
|
109
|
+
# incidental, special, exemplary, or consequential damages (including,
|
110
|
+
# but not limited to, procurement of substitute goods or services;
|
111
|
+
# loss of use, data, or profits; or business interruption) however
|
112
|
+
# caused and on any theory of liability, whether in contract, strict
|
113
|
+
# liability, or tort (including negligence or otherwise) arising in
|
114
|
+
# any way out of the use of this software, even if advised of the
|
115
|
+
# possibility of such damage.
|
116
|
+
#
|
117
|
+
# === RubyPants license
|
118
|
+
#
|
119
|
+
# RubyPants is a derivative work of SmartyPants and smartypants.py.
|
120
|
+
#
|
121
|
+
# Redistribution and use in source and binary forms, with or without
|
122
|
+
# modification, are permitted provided that the following conditions
|
123
|
+
# are met:
|
124
|
+
#
|
125
|
+
# * Redistributions of source code must retain the above copyright
|
126
|
+
# notice, this list of conditions and the following disclaimer.
|
127
|
+
#
|
128
|
+
# * Redistributions in binary form must reproduce the above copyright
|
129
|
+
# notice, this list of conditions and the following disclaimer in
|
130
|
+
# the documentation and/or other materials provided with the
|
131
|
+
# distribution.
|
132
|
+
#
|
133
|
+
# This software is provided by the copyright holders and contributors
|
134
|
+
# "as is" and any express or implied warranties, including, but not
|
135
|
+
# limited to, the implied warranties of merchantability and fitness
|
136
|
+
# for a particular purpose are disclaimed. In no event shall the
|
137
|
+
# copyright owner or contributors be liable for any direct, indirect,
|
138
|
+
# incidental, special, exemplary, or consequential damages (including,
|
139
|
+
# but not limited to, procurement of substitute goods or services;
|
140
|
+
# loss of use, data, or profits; or business interruption) however
|
141
|
+
# caused and on any theory of liability, whether in contract, strict
|
142
|
+
# liability, or tort (including negligence or otherwise) arising in
|
143
|
+
# any way out of the use of this software, even if advised of the
|
144
|
+
# possibility of such damage.
|
145
|
+
#
|
146
|
+
# == Links
|
147
|
+
#
|
148
|
+
# John Gruber:: http://daringfireball.net
|
149
|
+
# SmartyPants:: http://daringfireball.net/projects/smartypants
|
150
|
+
#
|
151
|
+
# Chad Miller:: http://web.chad.org
|
152
|
+
#
|
153
|
+
# Christian Neukirchen:: http://kronavita.de/chris
|
154
|
+
#
|
155
|
+
#++
|
156
|
+
#
|
157
|
+
|
158
|
+
module Kramdown
|
159
|
+
module Parser
|
160
|
+
class Kramdown
|
161
|
+
|
162
|
+
SQ_PUNCT = '[!"#\$\%\'()*+,\-.\/:;<=>?\@\[\\\\\]\^_`{|}~]'
|
163
|
+
SQ_CLOSE = %![^\ \\\\\t\r\n\\[{(-]!
|
164
|
+
|
165
|
+
SQ_RULES = [
|
166
|
+
[/("|')(?=#{SQ_PUNCT}\B)/, [:rquote1]],
|
167
|
+
# Special case for double sets of quotes, e.g.:
|
168
|
+
# <p>He said, "'Quoted' words in a larger quote."</p>
|
169
|
+
[/(\s?)"'(?=\w)/, [1, :ldquo, :lsquo]],
|
170
|
+
[/(\s?)'"(?=\w)/, [1, :lsquo, :ldquo]],
|
171
|
+
# Special case for decade abbreviations (the '80s):
|
172
|
+
[/(\s?)'(?=\d\ds)/, [1, :rsquo]],
|
173
|
+
|
174
|
+
# Get most opening single/double quotes:
|
175
|
+
[/(\s)('|")(?=\w)/, [1, :lquote2]],
|
176
|
+
# Single/double closing quotes:
|
177
|
+
[/(#{SQ_CLOSE})('|")/, [1, :rquote2]],
|
178
|
+
# Special case for e.g. "<i>Custer</i>'s Last Stand."
|
179
|
+
[/("|')(\s|s\b|$)/, [:rquote1, 2]],
|
180
|
+
# Any remaining single quotes should be opening ones:
|
181
|
+
[/(.?)'/, [1, :lsquo]],
|
182
|
+
[/(.?)"/, [1, :ldquo]],
|
183
|
+
] #'"
|
184
|
+
|
185
|
+
SQ_SUBSTS = {
|
186
|
+
[:rquote1, '"'] => :rdquo,
|
187
|
+
[:rquote1, "'"] => :rsquo,
|
188
|
+
[:rquote2, '"'] => :rdquo,
|
189
|
+
[:rquote2, "'"] => :rsquo,
|
190
|
+
[:lquote1, '"'] => :ldquo,
|
191
|
+
[:lquote1, "'"] => :lsquo,
|
192
|
+
[:lquote2, '"'] => :ldquo,
|
193
|
+
[:lquote2, "'"] => :lsquo,
|
194
|
+
}
|
195
|
+
SMART_QUOTES_RE = /[^\\]?["']/
|
196
|
+
|
197
|
+
# Parse the smart quotes at current location.
|
198
|
+
def parse_smart_quotes
|
199
|
+
regexp, substs = SQ_RULES.find {|reg, subst| @src.scan(reg)}
|
200
|
+
substs.each do |subst|
|
201
|
+
if subst.kind_of?(Integer)
|
202
|
+
add_text(@src[subst].to_s)
|
203
|
+
else
|
204
|
+
val = SQ_SUBSTS[[subst, @src[subst.to_s[-1,1].to_i]]] || subst
|
205
|
+
@tree.children << Element.new(:smart_quote, val)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
define_parser(:smart_quotes, SMART_QUOTES_RE, '[^\\\\]?["\']')
|
210
|
+
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|