patcito-maruku 0.6.0

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.
Files changed (162) hide show
  1. data/AUTHORS +23 -0
  2. data/LICENSE +340 -0
  3. data/README.md +73 -0
  4. data/bin/maruku +196 -0
  5. data/bin/marutex +4 -0
  6. data/data/entities.xml +261 -0
  7. data/docs/changelog.md +334 -0
  8. data/docs/div_syntax.md +36 -0
  9. data/docs/entity_test.md +23 -0
  10. data/docs/markdown_syntax.md +899 -0
  11. data/docs/maruku.md +346 -0
  12. data/docs/math.md +194 -0
  13. data/docs/other_stuff.md +51 -0
  14. data/docs/proposal.md +309 -0
  15. data/docs/website/src/bluecloth.md +25 -0
  16. data/docs/website/src/download.md +31 -0
  17. data/docs/website/src/maruku.md +261 -0
  18. data/docs/website/src/proposal.md +271 -0
  19. data/lib/maruku.rb +132 -0
  20. data/lib/maruku/attributes.rb +138 -0
  21. data/lib/maruku/defaults.rb +69 -0
  22. data/lib/maruku/errors.rb +89 -0
  23. data/lib/maruku/ext/div.rb +121 -0
  24. data/lib/maruku/ext/fenced_code.rb +78 -0
  25. data/lib/maruku/ext/math.rb +37 -0
  26. data/lib/maruku/ext/math/elements.rb +21 -0
  27. data/lib/maruku/ext/math/latex_fix.rb +12 -0
  28. data/lib/maruku/ext/math/mathml_engines/blahtex.rb +93 -0
  29. data/lib/maruku/ext/math/mathml_engines/itex2mml.rb +39 -0
  30. data/lib/maruku/ext/math/mathml_engines/none.rb +21 -0
  31. data/lib/maruku/ext/math/mathml_engines/ritex.rb +24 -0
  32. data/lib/maruku/ext/math/parsing.rb +125 -0
  33. data/lib/maruku/ext/math/to_html.rb +237 -0
  34. data/lib/maruku/ext/math/to_latex.rb +36 -0
  35. data/lib/maruku/ext/yaml.rb +43 -0
  36. data/lib/maruku/helpers.rb +214 -0
  37. data/lib/maruku/input/charsource.rb +326 -0
  38. data/lib/maruku/input/extensions.rb +69 -0
  39. data/lib/maruku/input/html_helper.rb +189 -0
  40. data/lib/maruku/input/linesource.rb +111 -0
  41. data/lib/maruku/input/parse_block.rb +608 -0
  42. data/lib/maruku/input/parse_doc.rb +240 -0
  43. data/lib/maruku/input/parse_span_better.rb +746 -0
  44. data/lib/maruku/input/rubypants.rb +225 -0
  45. data/lib/maruku/input/type_detection.rb +147 -0
  46. data/lib/maruku/input_textile2/t2_parser.rb +163 -0
  47. data/lib/maruku/maruku.rb +31 -0
  48. data/lib/maruku/output/s5/fancy.rb +756 -0
  49. data/lib/maruku/output/s5/to_s5.rb +138 -0
  50. data/lib/maruku/output/to_html.rb +994 -0
  51. data/lib/maruku/output/to_latex.rb +580 -0
  52. data/lib/maruku/output/to_latex_entities.rb +101 -0
  53. data/lib/maruku/output/to_latex_strings.rb +64 -0
  54. data/lib/maruku/output/to_markdown.rb +164 -0
  55. data/lib/maruku/output/to_s.rb +54 -0
  56. data/lib/maruku/string_utils.rb +185 -0
  57. data/lib/maruku/structures.rb +143 -0
  58. data/lib/maruku/structures_inspect.rb +51 -0
  59. data/lib/maruku/structures_iterators.rb +48 -0
  60. data/lib/maruku/textile2.rb +1 -0
  61. data/lib/maruku/toc.rb +214 -0
  62. data/lib/maruku/usage/example1.rb +33 -0
  63. data/lib/maruku/version +0 -0
  64. data/lib/maruku/version.rb +54 -0
  65. data/spec/block_docs/abbreviations.md +52 -0
  66. data/spec/block_docs/alt.md +17 -0
  67. data/spec/block_docs/attributes/att2.md +20 -0
  68. data/spec/block_docs/attributes/att3.md +28 -0
  69. data/spec/block_docs/attributes/attributes.md +57 -0
  70. data/spec/block_docs/attributes/circular.md +26 -0
  71. data/spec/block_docs/attributes/default.md +22 -0
  72. data/spec/block_docs/blank.md +24 -0
  73. data/spec/block_docs/blanks_in_code.md +75 -0
  74. data/spec/block_docs/bug_def.md +16 -0
  75. data/spec/block_docs/bug_table.md +46 -0
  76. data/spec/block_docs/code.md +34 -0
  77. data/spec/block_docs/code2.md +28 -0
  78. data/spec/block_docs/code3.md +71 -0
  79. data/spec/block_docs/data_loss.md +25 -0
  80. data/spec/block_docs/divs/div1.md +167 -0
  81. data/spec/block_docs/divs/div2.md +21 -0
  82. data/spec/block_docs/divs/div3_nest.md +45 -0
  83. data/spec/block_docs/easy.md +15 -0
  84. data/spec/block_docs/email.md +20 -0
  85. data/spec/block_docs/encoding/iso-8859-1.md +23 -0
  86. data/spec/block_docs/encoding/utf-8.md +18 -0
  87. data/spec/block_docs/entities.md +94 -0
  88. data/spec/block_docs/escaping.md +67 -0
  89. data/spec/block_docs/extra_dl.md +52 -0
  90. data/spec/block_docs/extra_header_id.md +63 -0
  91. data/spec/block_docs/extra_table1.md +37 -0
  92. data/spec/block_docs/footnotes.md +97 -0
  93. data/spec/block_docs/headers.md +37 -0
  94. data/spec/block_docs/hex_entities.md +37 -0
  95. data/spec/block_docs/hrule.md +39 -0
  96. data/spec/block_docs/html2.md +22 -0
  97. data/spec/block_docs/html3.md +31 -0
  98. data/spec/block_docs/html4.md +25 -0
  99. data/spec/block_docs/html5.md +23 -0
  100. data/spec/block_docs/ie.md +49 -0
  101. data/spec/block_docs/images.md +90 -0
  102. data/spec/block_docs/images2.md +31 -0
  103. data/spec/block_docs/inline_html.md +152 -0
  104. data/spec/block_docs/inline_html2.md +21 -0
  105. data/spec/block_docs/links.md +152 -0
  106. data/spec/block_docs/links2.md +22 -0
  107. data/spec/block_docs/list1.md +46 -0
  108. data/spec/block_docs/list12.md +28 -0
  109. data/spec/block_docs/list2.md +56 -0
  110. data/spec/block_docs/list3.md +64 -0
  111. data/spec/block_docs/list4.md +89 -0
  112. data/spec/block_docs/lists.md +192 -0
  113. data/spec/block_docs/lists10.md +34 -0
  114. data/spec/block_docs/lists11.md +23 -0
  115. data/spec/block_docs/lists6.md +41 -0
  116. data/spec/block_docs/lists9.md +64 -0
  117. data/spec/block_docs/lists_after_paragraph.md +208 -0
  118. data/spec/block_docs/lists_ol.md +262 -0
  119. data/spec/block_docs/loss.md +16 -0
  120. data/spec/block_docs/math/equations.md +45 -0
  121. data/spec/block_docs/math/inline.md +46 -0
  122. data/spec/block_docs/math/math2.md +45 -0
  123. data/spec/block_docs/math/notmath.md +25 -0
  124. data/spec/block_docs/math/table.md +25 -0
  125. data/spec/block_docs/math/table2.md +42 -0
  126. data/spec/block_docs/misc_sw.md +525 -0
  127. data/spec/block_docs/notyet/escape.md +21 -0
  128. data/spec/block_docs/notyet/header_after_par.md +58 -0
  129. data/spec/block_docs/notyet/ticks.md +18 -0
  130. data/spec/block_docs/notyet/triggering.md +157 -0
  131. data/spec/block_docs/olist.md +45 -0
  132. data/spec/block_docs/one.md +15 -0
  133. data/spec/block_docs/paragraph.md +16 -0
  134. data/spec/block_docs/paragraph_rules/dont_merge_ref.md +42 -0
  135. data/spec/block_docs/paragraph_rules/tab_is_blank.md +24 -0
  136. data/spec/block_docs/paragraphs.md +46 -0
  137. data/spec/block_docs/pending/amps.md +15 -0
  138. data/spec/block_docs/pending/empty_cells.md +37 -0
  139. data/spec/block_docs/pending/link.md +72 -0
  140. data/spec/block_docs/pending/ref.md +21 -0
  141. data/spec/block_docs/recover/recover_links.md +15 -0
  142. data/spec/block_docs/red_tests/abbrev.md +679 -0
  143. data/spec/block_docs/red_tests/lists7.md +32 -0
  144. data/spec/block_docs/red_tests/lists7b.md +65 -0
  145. data/spec/block_docs/red_tests/lists8.md +42 -0
  146. data/spec/block_docs/red_tests/ref.md +23 -0
  147. data/spec/block_docs/red_tests/xml.md +35 -0
  148. data/spec/block_docs/references/long_example.md +71 -0
  149. data/spec/block_docs/references/spaces_and_numbers.md +15 -0
  150. data/spec/block_docs/smartypants.md +114 -0
  151. data/spec/block_docs/syntax_hl.md +52 -0
  152. data/spec/block_docs/table_attributes.md +34 -0
  153. data/spec/block_docs/test.md +19 -0
  154. data/spec/block_docs/underscore_in_words.md +15 -0
  155. data/spec/block_docs/wrapping.md +67 -0
  156. data/spec/block_docs/xml2.md +19 -0
  157. data/spec/block_docs/xml3.md +26 -0
  158. data/spec/block_docs/xml_instruction.md +52 -0
  159. data/spec/block_spec.rb +49 -0
  160. data/spec/span_spec.rb +254 -0
  161. data/spec/spec_helper.rb +6 -0
  162. metadata +247 -0
@@ -0,0 +1,101 @@
1
+ #--
2
+ # Copyright (C) 2006 Andrea Censi <andrea (at) rubyforge.org>
3
+ #
4
+ # This file is part of Maruku.
5
+ #
6
+ # Maruku is free software; you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation; either version 2 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # Maruku is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with Maruku; if not, write to the Free Software
18
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19
+ #++
20
+
21
+
22
+ require 'rexml/document'
23
+
24
+ module MaRuKu; module Out; module Latex
25
+
26
+ include REXML
27
+
28
+ def to_latex_entity
29
+ MaRuKu::Out::Latex.need_entity_table
30
+
31
+ entity_name = self.entity_name
32
+
33
+ entity = ENTITY_TABLE[entity_name]
34
+ if not entity
35
+ maruku_error "I don't know how to translate entity '#{entity_name}' "+
36
+ "to LaTeX."
37
+ return ""
38
+ end
39
+ replace = entity.latex_string
40
+
41
+ entity.latex_packages.each do |p|
42
+ @doc.latex_require_package p
43
+ end
44
+
45
+ # if replace =~ /^\\/
46
+ # replace = replace + " "
47
+ # end
48
+
49
+ if replace
50
+ return replace + "{}"
51
+ else
52
+ tell_user "Cannot translate entity #{entity_name.inspect} to LaTeX."
53
+ return entity_name
54
+ end
55
+ end
56
+
57
+ class LatexEntity
58
+ attr_accessor :html_num
59
+ attr_accessor :html_entity
60
+ attr_accessor :latex_string
61
+ attr_accessor :latex_packages
62
+ end
63
+
64
+ def Latex.need_entity_table
65
+ Latex.init_entity_table if ENTITY_TABLE.empty?
66
+ end
67
+
68
+ # create hash @@entity_to_latex
69
+ def Latex.init_entity_table
70
+ # $stderr.write "Creating entity table.."
71
+ # $stderr.flush
72
+ doc = Document.new(File.read(File.dirname(__FILE__) + "/../../../data/entities.xml"))
73
+ doc.elements.each("//char") do |c|
74
+ num = c.attributes['num'].to_i
75
+ name = c.attributes['name']
76
+ package = c.attributes['package']
77
+
78
+ convert = c.attributes['convertTo']
79
+ convert.gsub!(/@DOUBLEQUOT/,'"')
80
+ convert.gsub!(/@QUOT/,"'")
81
+ convert.gsub!(/@GT/,">")
82
+ convert.gsub!(/@LT/,"<")
83
+ convert.gsub!(/@AMP/,"&")
84
+ convert.freeze
85
+
86
+ e = LatexEntity.new
87
+ e.html_num = num
88
+ e.html_entity = name
89
+ e.latex_string = convert
90
+ e.latex_packages = package ? package.split : []
91
+
92
+ ENTITY_TABLE[num] = e
93
+ ENTITY_TABLE[name] = e
94
+ end
95
+ # $stderr.puts "..done."
96
+ end
97
+
98
+ ENTITY_TABLE = {}
99
+
100
+ end end end
101
+
@@ -0,0 +1,64 @@
1
+ #--
2
+ # Copyright (C) 2006 Andrea Censi <andrea (at) rubyforge.org>
3
+ #
4
+ # This file is part of Maruku.
5
+ #
6
+ # Maruku is free software; you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation; either version 2 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # Maruku is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with Maruku; if not, write to the Free Software
18
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19
+ #++
20
+
21
+
22
+
23
+ class String
24
+
25
+ # These are TeX's special characters
26
+ LATEX_ADD_SLASH = [ '{', '}', '$', '&', '#', '_', '%']
27
+
28
+ # These, we transform to {\tt \char<ascii code>}
29
+ LATEX_TO_CHARCODE = [ '^', '~', '>', '<']
30
+
31
+ def escape_to_latex(s)
32
+ s2 = ""
33
+ s.each_char do |b|
34
+ if LATEX_TO_CHARCODE.include? b
35
+ s2 += "{\\tt \\symbol{#{b.ord}}}"
36
+ elsif LATEX_ADD_SLASH.include? b
37
+ s2 << ?\\ << b
38
+ elsif b == '\\'
39
+ # there is no backslash in cmr10 fonts
40
+ s2 += "$\\backslash$"
41
+ else
42
+ s2 << b
43
+ end
44
+ end
45
+ s2
46
+ end
47
+
48
+ # escapes special characters
49
+ def to_latex
50
+ s = escape_to_latex(self)
51
+ OtherGoodies.each do |k, v|
52
+ s.gsub!(k, v)
53
+ end
54
+ s
55
+ end
56
+
57
+ # other things that are good on the eyes
58
+ OtherGoodies = {
59
+ /(\s)LaTeX/ => '\1\\LaTeX\\xspace ', # XXX not if already \LaTeX
60
+ # 'HTML' => '\\textsc{html}\\xspace ',
61
+ # 'PDF' => '\\textsc{pdf}\\xspace '
62
+ }
63
+
64
+ end
@@ -0,0 +1,164 @@
1
+ #--
2
+ # Copyright (C) 2006 Andrea Censi <andrea (at) rubyforge.org>
3
+ #
4
+ # This file is part of Maruku.
5
+ #
6
+ # Maruku is free software; you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation; either version 2 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # Maruku is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with Maruku; if not, write to the Free Software
18
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19
+ #++
20
+
21
+
22
+ class String
23
+ # XXX: markdown escaping
24
+ def to_md(c=nil)
25
+ to_s
26
+ end
27
+
28
+ # " andrea censi " => [" andrea ", "censi "]
29
+ def mysplit
30
+ split.map{|x| x+" "}
31
+ end
32
+ end
33
+
34
+
35
+ module MaRuKu; module Out; module Markdown
36
+
37
+ DefaultLineLength = 40
38
+
39
+ def to_md(context={})
40
+ children_to_md(context)
41
+ end
42
+
43
+ def to_md_paragraph(context)
44
+ line_length = context[:line_length] || DefaultLineLength
45
+ wrap(@children, line_length, context)+"\n"
46
+ end
47
+
48
+ def to_md_li_span(context)
49
+ len = (context[:line_length] || DefaultLineLength) - 2
50
+ s = wrap(@children, len-2, context).rstrip.gsub(/^/, ' ')
51
+ s[0] = ?*
52
+ s + "\n"
53
+ end
54
+
55
+ def to_md_abbr_def(context)
56
+ "*[#{self.abbr}]: #{self.text}\n"
57
+ end
58
+
59
+ def to_md_ol(context)
60
+ len = (context[:line_length] || DefaultLineLength) - 2
61
+ md = ""
62
+ self.children.each_with_index do |li, i|
63
+ s = (w=wrap(li.children, len-2, context)).rstrip.gsub(/^/, ' ')+"\n"
64
+ s[0,4] = "#{i+1}. "[0,4]
65
+ # puts w.inspect
66
+ md += s
67
+ end
68
+ md + "\n"
69
+ end
70
+
71
+ def to_md_ul(context)
72
+ len = (context[:line_length] || DefaultLineLength) - 2
73
+ md = ""
74
+ self.children.each_with_index do |li, i|
75
+ w = wrap(li.children, len-2, context)
76
+ # puts "W: "+ w.inspect
77
+ s = add_indent(w)
78
+ # puts "S: " +s.inspect
79
+ s[0,1] = "-"
80
+ md += s
81
+ end
82
+ md + "\n"
83
+ end
84
+
85
+ def add_indent(s,char=" ")
86
+ t = s.split("\n").map{|x| char+x }.join("\n")
87
+ s << ?\n if t[-1] == ?\n
88
+ s
89
+ end
90
+
91
+ # Convert each child to html
92
+ def children_to_md(context)
93
+ array_to_md(@children, context)
94
+ end
95
+
96
+ def wrap(array, line_length, context)
97
+ out = ""
98
+ line = ""
99
+ array.each do |c|
100
+ if c.kind_of?(MDElement) && c.node_type == :linebreak
101
+ out << line.strip << " \n"; line="";
102
+ next
103
+ end
104
+
105
+ pieces =
106
+ if c.kind_of? String
107
+ c.to_md.mysplit
108
+ else
109
+ [c.to_md(context)].flatten
110
+ end
111
+
112
+ # puts "Pieces: #{pieces.inspect}"
113
+ pieces.each do |p|
114
+ if p.size + line.size > line_length
115
+ out << line.strip << "\n";
116
+ line = ""
117
+ end
118
+ line << p
119
+ end
120
+ end
121
+ out << line.strip << "\n" if line.size > 0
122
+ out << ?\n if not out[-1] == ?\n
123
+ out
124
+ end
125
+
126
+
127
+ def array_to_md(array, context, join_char='')
128
+ e = []
129
+ array.each do |c|
130
+ method = c.kind_of?(MDElement) ?
131
+ "to_md_#{c.node_type}" : "to_md"
132
+
133
+ if not c.respond_to?(method)
134
+ #raise "Object does not answer to #{method}: #{c.class} #{c.inspect[0,100]}"
135
+ # tell_user "Using default for #{c.node_type}"
136
+ method = 'to_md'
137
+ end
138
+
139
+ # puts "#{c.inspect} created with method #{method}"
140
+ h = c.send(method, context)
141
+
142
+ if h.nil?
143
+ raise "Nil md for #{c.inspect} created with method #{method}"
144
+ end
145
+
146
+ if h.kind_of?Array
147
+ e = e + h
148
+ else
149
+ e << h
150
+ end
151
+ end
152
+ e.join(join_char)
153
+ end
154
+
155
+ end end end
156
+
157
+ module MaRuKu; class MDDocument
158
+ alias old_md to_md
159
+ def to_md(context={})
160
+ s = old_md(context)
161
+ # puts s
162
+ s
163
+ end
164
+ end end
@@ -0,0 +1,54 @@
1
+ #--
2
+ # Copyright (C) 2006 Andrea Censi <andrea (at) rubyforge.org>
3
+ #
4
+ # This file is part of Maruku.
5
+ #
6
+ # Maruku is free software; you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation; either version 2 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # Maruku is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with Maruku; if not, write to the Free Software
18
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19
+ #++
20
+
21
+
22
+ module MaRuKu
23
+
24
+ class MDElement
25
+
26
+ # Strips all formatting from the string
27
+ def to_s
28
+ children_to_s
29
+ end
30
+
31
+ def children_to_s
32
+ @children.join
33
+ end
34
+
35
+ # Generate an id for headers. Assumes @children is set.
36
+ def generate_id
37
+ title = children_to_s
38
+ title.gsub!(/ /,'_')
39
+ title.downcase!
40
+ title.gsub!(/[^\w_]/,'')
41
+ title.strip!
42
+
43
+ if title.size == 0
44
+ $uid ||= 0
45
+ $uid += 1
46
+ title = "id#{$uid}"
47
+ end
48
+
49
+ @doc.id_counter += 1
50
+ title << "_" + @doc.id_counter.to_s
51
+ end
52
+ end
53
+
54
+ end
@@ -0,0 +1,185 @@
1
+ #--
2
+ # Copyright (C) 2006 Andrea Censi <andrea (at) rubyforge.org>
3
+ #
4
+ # This file is part of Maruku.
5
+ #
6
+ # Maruku is free software; you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation; either version 2 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # Maruku is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with Maruku; if not, write to the Free Software
18
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19
+ #++
20
+
21
+ require 'strscan'
22
+
23
+ module MaRuKu
24
+ # Utility functions for dealing with strings.
25
+ module Strings
26
+ TAB_SIZE = 4
27
+
28
+ # Split a string into multiple lines,
29
+ # on line feeds and/or carriage returns.
30
+ #
31
+ # @param s [String]
32
+ # @return [String]
33
+ def split_lines(s)
34
+ s.split(/\r\n|\r|\n/)
35
+ end
36
+
37
+ # Parses email headers, returning a hash.
38
+ # `hash[:data]` is the message;
39
+ # that is, anything past the headers.
40
+ #
41
+ # Keys are downcased and converted to symbols;
42
+ # spaces become underscores. For example:
43
+ #
44
+ # !!!plain
45
+ # My key: true
46
+ #
47
+ # becomes:
48
+ #
49
+ # {:my_key => true}
50
+ #
51
+ # @param s [String] The email
52
+ # @return [Symbol => String] The header values
53
+ def parse_email_headers(s)
54
+ headers = {}
55
+ scanner = StringScanner.new(s)
56
+
57
+ while scanner.scan(/(\w[\w\s\-]+): +(.*)\n/)
58
+ k, v = normalize_key_and_value(scanner[1], scanner[2])
59
+ headers[k.to_sym] = v
60
+ end
61
+
62
+ headers[:data] = scanner.rest
63
+ headers
64
+ end
65
+
66
+ # Returns the number of leading spaces,
67
+ # considering that a tab counts as {TAB_SIZE} spaces.
68
+ #
69
+ # @param s [String]
70
+ # @return [Fixnum]
71
+ def number_of_leading_spaces(s)
72
+ spaces = s.scan(/^\s*/).first
73
+ spaces.count(" ") + spaces.count("\t") * TAB_SIZE
74
+ end
75
+
76
+ # This returns the position of the first non-list character
77
+ # in a list item.
78
+ #
79
+ # @example
80
+ # spaces_before_first_char('*Hello') #=> 1
81
+ # spaces_before_first_char('* Hello') #=> 2
82
+ # spaces_before_first_char(' * Hello') #=> 3
83
+ # spaces_before_first_char(' * Hello') #=> 5
84
+ # spaces_before_first_char('1.Hello') #=> 2
85
+ # spaces_before_first_char(' 1. Hello') #=> 5
86
+ #
87
+ # @param s [String]
88
+ # @return [Fixnum]
89
+ def spaces_before_first_char(s)
90
+ match =
91
+ case s.md_type
92
+ when :ulist; s.match(/\s*.(\s*\{(.*?)\})?\s*/)
93
+ when :olist; s.match(/s*\d+.(\s*\{(.*?)\})?\s*/)
94
+ else
95
+ tell_user "MARUKU BUG: '#{s.inspect}' is not a list"
96
+ nil
97
+ end
98
+ match ? [match.end(0), match[0]] : [0, nil]
99
+ end
100
+
101
+ # Replace spaces with underscores and remove non-word characters.
102
+ #
103
+ # @param s [String]
104
+ # @return [String]
105
+ def sanitize_ref_id(s)
106
+ s.strip.downcase.gsub(' ', '_').gsub(/[^\w]/, '')
107
+ end
108
+
109
+ # Remove line-initial `>` characters for a quotation.
110
+ #
111
+ # @param s [String]
112
+ # @return [String]
113
+ def unquote(s)
114
+ s.gsub(/^>\s?/, '')
115
+ end
116
+
117
+ # Removes indentation from the beginning of `s`,
118
+ # up to at most `n` spaces.
119
+ # Tabs are counted as {TAB_SIZE} spaces.
120
+ #
121
+ # @param s [String]
122
+ # @param n [Fixnum]
123
+ # @return [String]
124
+ def strip_indent(s, n)
125
+ while n > 0
126
+ case s[0]
127
+ when ?\s; n -= 1
128
+ when ?\t; n -= TAB_SIZE
129
+ else; return s
130
+ end
131
+ s = s[1..-1]
132
+ end
133
+ return s
134
+ end
135
+
136
+ # Escapes a string so that it can be safely used in a Bourne shell command line.
137
+ #
138
+ # Note that a resulted string should be used unquoted
139
+ # and is not intended for use in double quotes nor in single quotes.
140
+ #
141
+ # This is a copy of the Shellwords.shellescape function in Ruby 1.8.7.
142
+ # It's included for Ruby 1.8.6 compatibility.
143
+ #
144
+ # @param str [String]
145
+ # @return [String]
146
+ def shellescape(str)
147
+ # An empty argument will be skipped, so return empty quotes.
148
+ return "''" if str.empty?
149
+
150
+ str = str.dup
151
+
152
+ # Process as a single byte sequence because not all shell
153
+ # implementations are multibyte aware.
154
+ str.gsub!(/([^A-Za-z0-9_\-.,:\/@\n])/n, "\\\\\\1")
155
+
156
+ # A LF cannot be escaped with a backslash because a backslash + LF
157
+ # combo is regarded as line continuation and simply ignored.
158
+ str.gsub!(/\n/, "'\n'")
159
+
160
+ return str
161
+ end
162
+
163
+ private
164
+
165
+ # Normalize the key/value pairs for email headers.
166
+ # Keys are downcased and converted to symbols;
167
+ # spaces become underscores.
168
+ #
169
+ # Values of `"yes"`, `"true"`, `"no"`, and `"false"`
170
+ # are converted to appropriate booleans.
171
+ #
172
+ # @param k [String]
173
+ # @param v [String]
174
+ # @return [Array(String, String or Boolean)]
175
+ def normalize_key_and_value(k, v)
176
+ k = k.strip.downcase.gsub(/\s+/, '_')
177
+ v = v.strip
178
+
179
+ # check synonyms
180
+ return k, true if %w[yes true].include?(v.downcase)
181
+ return k, false if %w[no false].include?(v.downcase)
182
+ return k, v
183
+ end
184
+ end
185
+ end