patcito-maruku 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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