Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

use maruku for markdown and vendor it

  • Loading branch information...
commit 128f72d96dae6175011f1fc82d5ca1346f8eb57c 1 parent 9efe3fb
Adam Wiggins authored

Showing 49 changed files with 9,169 additions and 4 deletions. Show diff stats Hide diff stats

  1. 4  lib/post.rb
  2. 4  spec/post_spec.rb
  3. 141  vendor/maruku/maruku.rb
  4. 462  vendor/maruku/maruku/attic/parse_span.rb.txt
  5. 227  vendor/maruku/maruku/attributes.rb
  6. 70  vendor/maruku/maruku/defaults.rb
  7. 92  vendor/maruku/maruku/errors_management.rb
  8. 100  vendor/maruku/maruku/ext/div.rb
  9. 41  vendor/maruku/maruku/ext/math.rb
  10. 27  vendor/maruku/maruku/ext/math/elements.rb
  11. 11  vendor/maruku/maruku/ext/math/latex_fix.rb
  12. 104  vendor/maruku/maruku/ext/math/mathml_engines/blahtex.rb
  13. 29  vendor/maruku/maruku/ext/math/mathml_engines/itex2mml.rb
  14. 20  vendor/maruku/maruku/ext/math/mathml_engines/none.rb
  15. 24  vendor/maruku/maruku/ext/math/mathml_engines/ritex.rb
  16. 105  vendor/maruku/maruku/ext/math/parsing.rb
  17. 170  vendor/maruku/maruku/ext/math/to_html.rb
  18. 22  vendor/maruku/maruku/ext/math/to_latex.rb
  19. 260  vendor/maruku/maruku/helpers.rb
  20. 326  vendor/maruku/maruku/input/charsource.rb
  21. 69  vendor/maruku/maruku/input/extensions.rb
  22. 189  vendor/maruku/maruku/input/html_helper.rb
  23. 111  vendor/maruku/maruku/input/linesource.rb
  24. 613  vendor/maruku/maruku/input/parse_block.rb
  25. 227  vendor/maruku/maruku/input/parse_doc.rb
  26. 732  vendor/maruku/maruku/input/parse_span_better.rb
  27. 225  vendor/maruku/maruku/input/rubypants.rb
  28. 144  vendor/maruku/maruku/input/type_detection.rb
  29. 163  vendor/maruku/maruku/input_textile2/t2_parser.rb
  30. 33  vendor/maruku/maruku/maruku.rb
  31. 756  vendor/maruku/maruku/output/s5/fancy.rb
  32. 125  vendor/maruku/maruku/output/s5/to_s5.rb
  33. 971  vendor/maruku/maruku/output/to_html.rb
  34. 563  vendor/maruku/maruku/output/to_latex.rb
  35. 367  vendor/maruku/maruku/output/to_latex_entities.rb
  36. 64  vendor/maruku/maruku/output/to_latex_strings.rb
  37. 164  vendor/maruku/maruku/output/to_markdown.rb
  38. 53  vendor/maruku/maruku/output/to_s.rb
  39. 191  vendor/maruku/maruku/string_utils.rb
  40. 165  vendor/maruku/maruku/structures.rb
  41. 87  vendor/maruku/maruku/structures_inspect.rb
  42. 61  vendor/maruku/maruku/structures_iterators.rb
  43. 82  vendor/maruku/maruku/tests/benchmark.rb
  44. 370  vendor/maruku/maruku/tests/new_parser.rb
  45. 136  vendor/maruku/maruku/tests/tests.rb
  46. 1  vendor/maruku/maruku/textile2.rb
  47. 199  vendor/maruku/maruku/toc.rb
  48. 33  vendor/maruku/maruku/usage/example1.rb
  49. 40  vendor/maruku/maruku/version.rb
4  lib/post.rb
... ...
@@ -1,4 +1,4 @@
1  
-require 'rdiscount'
  1
+require File.dirname(__FILE__) + '/../vendor/maruku/maruku'
2 2
 require 'syntax/convertors/html'
3 3
 
4 4
 class Post < Sequel::Model
@@ -52,7 +52,7 @@ def self.make_slug(title)
52 52
 	########
53 53
 
54 54
 	def to_html(markdown)
55  
-		h = RDiscount.new(markdown).to_html
  55
+		h = Maruku.new(markdown).to_html
56 56
 		h.gsub(/<code>([^<]+)<\/code>/m) do
57 57
 			convertor = Syntax::Convertors::HTML.for_syntax "ruby"
58 58
 			highlighted = convertor.convert($1)
4  spec/post_spec.rb
@@ -20,11 +20,11 @@
20 20
 
21 21
 	it "produces html from the markdown body" do
22 22
 		@post.body = "* Bullet"
23  
-		@post.body_html.should == "<ul>\n<li>Bullet</li>\n</ul>\n\n"
  23
+		@post.body_html.should == "<ul>\n<li>Bullet</li>\n</ul>"
24 24
 	end
25 25
 
26 26
 	it "syntax highlights code blocks" do
27  
-		@post.to_html("<code>\none\ntwo</code>").should == "<p><code><pre>\n<span class=\"ident\">one</span>\n<span class=\"ident\">two</span></pre></code></p>\n"
  27
+		@post.to_html("<code>\none\ntwo</code>").should == "<code><pre>\n<span class=\"ident\">one</span>\n<span class=\"ident\">two</span></pre></code>"
28 28
 	end
29 29
 
30 30
 	it "makes the tags into links to the tag search" do
141  vendor/maruku/maruku.rb
... ...
@@ -0,0 +1,141 @@
  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
+$:.unshift File.dirname(__FILE__)
  21
+
  22
+require 'rexml/document'
  23
+
  24
+# :include:MaRuKu.txt
  25
+module MaRuKu
  26
+
  27
+	module In
  28
+		module Markdown
  29
+			module SpanLevelParser; end
  30
+			module BlockLevelParser; end
  31
+		end
  32
+		# more to come?
  33
+	end
  34
+
  35
+	module Out
  36
+		# Functions for exporting to MarkDown.
  37
+		module Markdown; end
  38
+		# Functions for exporting to HTML.
  39
+		module HTML; end
  40
+		# Functions for exporting to Latex
  41
+		module Latex; end
  42
+	end
  43
+		
  44
+	# These are strings utilities.
  45
+	module Strings; end
  46
+
  47
+	module Helpers; end
  48
+
  49
+	module Errors; end
  50
+		
  51
+	class MDElement
  52
+		include REXML
  53
+		include MaRuKu
  54
+		include Out::Markdown
  55
+		include Out::HTML
  56
+		include Out::Latex
  57
+		include Strings
  58
+		include Helpers
  59
+		include Errors
  60
+	end
  61
+	
  62
+	
  63
+	class MDDocument < MDElement
  64
+		include In::Markdown
  65
+		include In::Markdown::SpanLevelParser
  66
+		include In::Markdown::BlockLevelParser
  67
+	end
  68
+end
  69
+
  70
+# This is the public interface
  71
+class Maruku < MaRuKu::MDDocument; end
  72
+
  73
+
  74
+
  75
+require 'rexml/document'
  76
+
  77
+# Structures definition
  78
+require 'maruku/structures'
  79
+require 'maruku/structures_inspect'
  80
+
  81
+require 'maruku/defaults'
  82
+# Less typing
  83
+require 'maruku/helpers'
  84
+
  85
+# Code for parsing whole Markdown documents
  86
+require 'maruku/input/parse_doc'
  87
+
  88
+# Ugly things kept in a closet
  89
+require 'maruku/string_utils'
  90
+require 'maruku/input/linesource'
  91
+require 'maruku/input/type_detection'
  92
+
  93
+# A class for reading and sanitizing inline HTML
  94
+require 'maruku/input/html_helper'
  95
+
  96
+# Code for parsing Markdown block-level elements
  97
+require 'maruku/input/parse_block'
  98
+
  99
+# Code for parsing Markdown span-level elements
  100
+require 'maruku/input/charsource'
  101
+require 'maruku/input/parse_span_better'
  102
+require 'maruku/input/rubypants'
  103
+
  104
+require 'maruku/input/extensions'
  105
+
  106
+require 'maruku/attributes'
  107
+
  108
+require 'maruku/structures_iterators'
  109
+
  110
+require 'maruku/errors_management'
  111
+
  112
+# Code for creating a table of contents
  113
+require 'maruku/toc'
  114
+
  115
+# Version and URL
  116
+require 'maruku/version'
  117
+
  118
+
  119
+# Exporting to html
  120
+require 'maruku/output/to_html'
  121
+
  122
+# Exporting to latex
  123
+require 'maruku/output/to_latex'
  124
+require 'maruku/output/to_latex_strings'
  125
+require 'maruku/output/to_latex_entities'
  126
+
  127
+# Pretty print
  128
+require 'maruku/output/to_markdown'
  129
+
  130
+# S5 slides
  131
+require 'maruku/output/s5/to_s5'
  132
+require 'maruku/output/s5/fancy'
  133
+
  134
+# Exporting to text: strips all formatting (not complete)
  135
+require 'maruku/output/to_s'
  136
+
  137
+# class Maruku is the global interface
  138
+require 'maruku/maruku'
  139
+
  140
+# require the new DIV syntax, by default
  141
+require 'maruku/ext/div'
462  vendor/maruku/maruku/attic/parse_span.rb.txt
... ...
@@ -0,0 +1,462 @@
  1
+#   Copyright (C) 2006  Andrea Censi  <andrea (at) rubyforge.org>
  2
+#
  3
+# This file is part of Maruku.
  4
+# 
  5
+#   Maruku is free software; you can redistribute it and/or modify
  6
+#   it under the terms of the GNU General Public License as published by
  7
+#   the Free Software Foundation; either version 2 of the License, or
  8
+#   (at your option) any later version.
  9
+# 
  10
+#   Maruku is distributed in the hope that it will be useful,
  11
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of
  12
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13
+#   GNU General Public License for more details.
  14
+# 
  15
+#   You should have received a copy of the GNU General Public License
  16
+#   along with Maruku; if not, write to the Free Software
  17
+#   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  18
+
  19
+
  20
+
  21
+
  22
+#  NOTE: this is the old span-level regexp-based parser.
  23
+#
  24
+#  The new parser is a real parser and is defined with functions in parse_span_better.rb
  25
+#  The new parser is faster, handles syntax errors, but it's absolutely not readable.
  26
+#
  27
+#  Also, regexp parsers simply CANNOT handle inline HTML properly.
  28
+
  29
+
  30
+
  31
+# There are two black-magic methods `match_couple_of` and `map_match`,
  32
+# defined at the end of the file, that make the function 
  33
+# `parse_lines_as_span` so elegant.
  34
+
  35
+class Maruku
  36
+
  37
+	# Takes care of all span-level formatting, links, images, etc.
  38
+	#
  39
+	# Lines must not contain block-level elements.
  40
+	def parse_lines_as_span(lines)
  41
+		
  42
+		# first, get rid of linebreaks
  43
+		res = resolve_linebreaks(lines)
  44
+
  45
+		span = MDElement.new(:dummy, res)
  46
+
  47
+		# encode all escapes
  48
+		span.replace_each_string { |s| s.escape_md_special }
  49
+
  50
+
  51
+# The order of processing is significant: 
  52
+# 1. inline code
  53
+# 2. immediate links
  54
+# 3. inline HTML 
  55
+# 4. everything else
  56
+
  57
+		# search for ``code`` markers
  58
+		span.match_couple_of('``') { |children, match1, match2| 
  59
+			e = create_md_element(:inline_code)
  60
+			# this is now opaque to processing
  61
+			e.meta[:raw_code] = children.join('').it_was_a_code_block
  62
+			e
  63
+		}
  64
+
  65
+		# Search for `single tick`  code markers
  66
+		span.match_couple_of('`') { |children, match1, match2|
  67
+			e = create_md_element(:inline_code)
  68
+			# this is now opaque to processing
  69
+			e.meta[:raw_code] = children.join('').it_was_a_code_block
  70
+			# this is now opaque to processing
  71
+			e
  72
+		}
  73
+		
  74
+		# Detect any immediate link: <http://www.google.com>
  75
+		# we expect an http: or something: at the beginning
  76
+		span.map_match( /<(\w+:[^\>]+)>/) { |match| 
  77
+			url = match[1]
  78
+			
  79
+			e = create_md_element(:immediate_link, [])
  80
+			e.meta[:url] = url
  81
+			e
  82
+		}
  83
+		
  84
+		# Search for inline HTML (the support is pretty basic for now)
  85
+		
  86
+		# this searches for a matching block
  87
+		inlineHTML1 = %r{
  88
+			(   # put everything in 1 
  89
+			<   # open
  90
+			(\w+) # opening tag in 2
  91
+			>   # close
  92
+			.*  # anything
  93
+			</\2> # match closing tag
  94
+			)
  95
+		}x
  96
+
  97
+		# this searches for only one block
  98
+		inlineHTML2 = %r{
  99
+			(   # put everything in 1 
  100
+			<   # open
  101
+			\w+ # 
  102
+			    # close
  103
+			[^<>]*  # anything except
  104
+			/> # closing tag
  105
+			)
  106
+		}x
  107
+		
  108
+		for reg in [inlineHTML1, inlineHTML2]
  109
+			span.map_match(reg) { |match| 
  110
+				raw_html = match[1]
  111
+				convert_raw_html_in_list(raw_html)
  112
+			}
  113
+		end
  114
+		
  115
+		# Detect footnotes references: [^1]
  116
+		span.map_match(/\[(\^[^\]]+)\]/) { |match| 
  117
+			id = match[1].strip.downcase
  118
+			e = create_md_element(:footnote_reference)
  119
+			e.meta[:footnote_id] = id
  120
+			e
  121
+		}
  122
+
  123
+		# Detect any image like ![Alt text][url]
  124
+		span.map_match(/\!\[([^\]]+)\]\s?\[([^\]]*)\]/) { |match|
  125
+			alt = match[1]
  126
+			id = match[2].strip.downcase
  127
+			
  128
+			if id.size == 0
  129
+				id = text.strip.downcase
  130
+			end
  131
+			
  132
+			e = create_md_element(:image)
  133
+			e.meta[:ref_id] = id
  134
+			e
  135
+		}
  136
+
  137
+		# Detect any immage with immediate url: ![Alt](url "title")
  138
+		# a dummy ref is created and put in the symbol table
  139
+		link1 = /!\[([^\]]+)\]\s?\(([^\s\)]*)(?:\s+["'](.*)["'])?\)/
  140
+		span.map_match(link1) { |match|
  141
+			alt = match[1]
  142
+			url = match[2]
  143
+			title = match[3]
  144
+			
  145
+			url = url.strip
  146
+			# create a dummy id
  147
+			id="dummy_#{@refs.size}"
  148
+			@refs[id] = {:url=>url, :title=>title}
  149
+			
  150
+			e = create_md_element(:image)
  151
+			e.meta[:ref_id] = id
  152
+			e
  153
+		}
  154
+
  155
+		# an id reference: "[id]",  "[ id  ]"
  156
+		reg_id_ref = %r{
  157
+			\[ # opening bracket 
  158
+			([^\]]*) # 0 or more non-closing bracket (this is too permissive)
  159
+			\] # closing bracket
  160
+			}x
  161
+			
  162
+		
  163
+		# validates a url, only $1 is set to the url
  164
+ 		reg_url = 
  165
+			/((?:\w+):\/\/(?:\w+:{0,1}\w*@)?(?:\S+)(?::[0-9]+)?(?:\/|\/([\w#!:.?+=&%@!\-\/]))?)/
  166
+		reg_url = %r{([^\s\]\)]+)}
  167
+		
  168
+		# A string enclosed in quotes.
  169
+		reg_title = %r{
  170
+			" # opening
  171
+			[^"]*   # anything = 1
  172
+			" # closing
  173
+			}x
  174
+		
  175
+		# [bah](http://www.google.com "Google.com"), 
  176
+		# [bah](http://www.google.com),
  177
+		# [empty]()
  178
+		reg_url_and_title = %r{
  179
+			\(  # opening
  180
+			\s* # whitespace 
  181
+			#{reg_url}?  # url = 1 might be  empty
  182
+			(?:\s+["'](.*)["'])? # optional title  = 2
  183
+			\s* # whitespace 
  184
+			\) # closing
  185
+		}x
  186
+		
  187
+		# Detect a link like ![Alt text][id]
  188
+		span.map_match(/\[([^\]]+)\]\s?\[([^\]]*)\]/) { |match|
  189
+			text = match[1]
  190
+			id = match[2].strip.downcase
  191
+			
  192
+			if id.size == 0
  193
+				id = text.strip.downcase
  194
+			end
  195
+
  196
+			children = parse_lines_as_span(text)
  197
+			e = create_md_element(:link, children)
  198
+			e.meta[:ref_id] = id
  199
+			e
  200
+		}
  201
+		
  202
+		# Detect any immage with immediate url: ![Alt](url "title")
  203
+		# a dummy ref is created and put in the symbol table
  204
+		link1 = /!\[([^\]]+)\]\s?\(([^\s\)]*)(?:\s+["'](.*)["'])?\)/
  205
+		span.map_match(link1) { |match|
  206
+			text = match[1]
  207
+			children = parse_lines_as_span(text)
  208
+			
  209
+			url = match[2]
  210
+			title = match[3]
  211
+			
  212
+			url = url.strip
  213
+			# create a dummy id
  214
+			id="dummy_#{@refs.size}"
  215
+			@refs[id] = {:url=>url, :title=>title}
  216
+			@refs[id][:title] = title if title
  217
+			
  218
+			e = create_md_element(:link, children)
  219
+			e.meta[:ref_id] = id
  220
+			e
  221
+		}
  222
+		
  223
+
  224
+		# Detect any link like [Google engine][google]
  225
+		span.match_couple_of('[',  # opening bracket
  226
+			%r{\]                   # closing bracket
  227
+			[ ]?                    # optional whitespace
  228
+			#{reg_id_ref} # ref id, with $1 being the reference 
  229
+			}x
  230
+				) { |children, match1, match2| 
  231
+			id = match2[1]
  232
+			id = id.strip.downcase
  233
+			
  234
+			if id.size == 0
  235
+				id = children.join.strip.downcase
  236
+			end
  237
+			
  238
+			e = create_md_element(:link, children)
  239
+			e.meta[:ref_id] = id
  240
+			e
  241
+		}
  242
+
  243
+		# Detect any link with immediate url: [Google](http://www.google.com)
  244
+		# XXX Note that the url can be empty: [Empty]()
  245
+		# a dummy ref is created and put in the symbol table
  246
+		span.match_couple_of('[',  # opening bracket
  247
+				%r{\]                   # closing bracket
  248
+				[ ]?                    # optional whitespace
  249
+				#{reg_url_and_title}    # ref id, with $1 being the url and $2 being the title
  250
+				}x
  251
+					) { |children, match1, match2| 
  252
+			
  253
+			url   = match2[1]
  254
+			title = match2[3] # XXX? Is it a bug? I would use [2]
  255
+			 
  256
+			# create a dummy id
  257
+			id="dummy_#{@refs.size}"
  258
+			@refs[id] = {:url=>url}
  259
+			@refs[id][:title] = title if title
  260
+
  261
+			e = create_md_element(:link, children)
  262
+			e.meta[:ref_id] = id
  263
+			e
  264
+		}
  265
+
  266
+		# Detect an email address <andrea@invalid.it>
  267
+		span.map_match(EMailAddress) { |match| 
  268
+			email = match[1]
  269
+			e = create_md_element(:email_address, [])
  270
+			e.meta[:email] = email
  271
+			e
  272
+		}
  273
+		
  274
+		# Detect HTML entitis
  275
+		span.map_match(/&([\w\d]+);/) { |match| 
  276
+			entity_name = match[1]
  277
+
  278
+			e = create_md_element(:entity, [])
  279
+			e.meta[:entity_name] = entity_name
  280
+			e
  281
+		}
  282
+
  283
+
  284
+		# And now the easy stuff
  285
+
  286
+		# search for ***strong and em***
  287
+		span.match_couple_of('***') { |children,m1,m2|  
  288
+			create_md_element(:strong, [create_md_element(:emphasis, children)] ) }
  289
+
  290
+		span.match_couple_of('___') { |children,m1,m2|  
  291
+			create_md_element(:strong, [create_md_element(:emphasis, children)] ) }
  292
+	
  293
+		# search for **strong**
  294
+		span.match_couple_of('**') { |children,m1,m2|  create_md_element(:strong,   children) }
  295
+
  296
+		# search for __strong__
  297
+		span.match_couple_of('__') { |children,m1,m2|  create_md_element(:strong,   children) }
  298
+
  299
+		# search for *emphasis*
  300
+		span.match_couple_of('*')  { |children,m1,m2|  create_md_element(:emphasis, children) }
  301
+		
  302
+		# search for _emphasis_
  303
+		span.match_couple_of('_')  { |children,m1,m2|  create_md_element(:emphasis, children) }
  304
+		
  305
+		# finally, unescape the special characters
  306
+		span.replace_each_string { |s|  s.unescape_md_special}
  307
+		
  308
+		span.children
  309
+	end
  310
+	
  311
+	# returns array containing Strings or :linebreak elements
  312
+	def resolve_linebreaks(lines)
  313
+		res = []
  314
+		s = ""
  315
+		lines.each do |l| 
  316
+			s += (s.size>0 ? " " : "") + l.strip
  317
+			if force_linebreak?(l)
  318
+				res << s
  319
+				res << create_md_element(:linebreak)
  320
+				s = ""
  321
+			end
  322
+		end
  323
+		res << s if s.size > 0
  324
+		res
  325
+	end
  326
+
  327
+	# raw_html is something like 
  328
+	#  <em> A</em> dopwkk *maruk* <em>A</em>  
  329
+	def convert_raw_html_in_list(raw_html)
  330
+		e = create_md_element(:raw_html)
  331
+		e.meta[:raw_html]  = raw_html
  332
+		begin
  333
+			e.meta[:parsed_html] = Document.new(raw_html)
  334
+		rescue 
  335
+			$stderr.puts "convert_raw_html_in_list Malformed HTML:\n#{raw_html}"
  336
+		end
  337
+		e
  338
+	end
  339
+
  340
+end
  341
+
  342
+# And now the black magic that makes the part above so elegant
  343
+class MDElement	
  344
+	
  345
+	# Try to match the regexp to each string in the hierarchy
  346
+	# (using `replace_each_string`). If the regexp match, eliminate
  347
+	# the matching string and substitute it with the pre_match, the
  348
+	# result of the block, and the post_match
  349
+	#
  350
+	#   ..., matched_string, ... -> ..., pre_match, block.call(match), post_match
  351
+	#
  352
+	# the block might return arrays.
  353
+	#
  354
+	def map_match(regexp, &block)
  355
+		replace_each_string { |s| 
  356
+			processed = []
  357
+			while (match = regexp.match(s))
  358
+				# save the pre_match
  359
+				processed << match.pre_match if match.pre_match && match.pre_match.size>0
  360
+				# transform match
  361
+				result = block.call(match)
  362
+				# and append as processed
  363
+				[*result].each do |e| processed << e end
  364
+				# go on with the rest of the string
  365
+				s = match.post_match 
  366
+			end
  367
+			processed << s if s.size > 0
  368
+			processed
  369
+		}
  370
+	end
  371
+	
  372
+	# Finds couple of delimiters in a hierarchy of Strings and MDElements
  373
+	#
  374
+	# Open and close are two delimiters (like '[' and ']'), or two Regexp.
  375
+	#
  376
+	# If you don't pass close, it defaults to open.
  377
+	#
  378
+	# Each block is called with |contained children, match1, match2|
  379
+	def match_couple_of(open, close=nil, &block)
  380
+		close = close || open
  381
+		 open_regexp =  open.kind_of?(Regexp) ?  open : Regexp.new(Regexp.escape(open))
  382
+		close_regexp = close.kind_of?(Regexp) ? close : Regexp.new(Regexp.escape(close))
  383
+		
  384
+		# Do the same to children first
  385
+		for c in @children; if c.kind_of? MDElement
  386
+			c.match_couple_of(open_regexp, close_regexp, &block)
  387
+		end end
  388
+		
  389
+		processed_children = []
  390
+		
  391
+		until @children.empty?
  392
+			c = @children.shift
  393
+			if c.kind_of? String
  394
+				match1 = open_regexp.match(c)
  395
+				if not match1
  396
+					processed_children << c
  397
+				else # we found opening, now search closing
  398
+#					puts "Found opening (#{marker}) in #{c.inspect}"
  399
+					# pre match is processed
  400
+					processed_children.push match1.pre_match if 
  401
+						match1.pre_match && match1.pre_match.size > 0
  402
+					# we will process again the post_match
  403
+					@children.unshift match1.post_match if 
  404
+						match1.post_match && match1.post_match.size>0
  405
+					
  406
+					contained = []; found_closing = false
  407
+					until @children.empty?  || found_closing
  408
+						c = @children.shift
  409
+						if c.kind_of? String
  410
+							match2 = close_regexp.match(c)
  411
+							if not match2 
  412
+								contained << c
  413
+							else
  414
+								# we found closing
  415
+								found_closing = true
  416
+								# pre match is contained
  417
+								contained.push match2.pre_match if 
  418
+									match2.pre_match && match2.pre_match.size>0
  419
+								# we will process again the post_match
  420
+								@children.unshift match2.post_match if 
  421
+									match2.post_match && match2.post_match.size>0
  422
+
  423
+								# And now we call the block
  424
+								substitute = block.call(contained, match1, match2) 
  425
+								processed_children  << substitute
  426
+								
  427
+#								puts "Found closing (#{marker}) in #{c.inspect}"
  428
+#								puts "Children: #{contained.inspect}"
  429
+#								puts "Substitute: #{substitute.inspect}"
  430
+							end
  431
+						else
  432
+							contained << c
  433
+						end
  434
+					end
  435
+					
  436
+					if not found_closing
  437
+						# $stderr.puts "##### Could not find closing for #{open}, #{close} -- ignoring"
  438
+						processed_children << match1.to_s
  439
+						contained.reverse.each do |c|
  440
+							@children.unshift c
  441
+						end
  442
+					end
  443
+				end
  444
+			else
  445
+				processed_children << c
  446
+			end
  447
+		end
  448
+		
  449
+		raise "BugBug" unless @children.empty?
  450
+		
  451
+		rebuilt = []
  452
+		# rebuild strings
  453
+		processed_children.each do |c|
  454
+			if c.kind_of?(String) && rebuilt.last && rebuilt.last.kind_of?(String)
  455
+				rebuilt.last << c
  456
+			else
  457
+				rebuilt << c
  458
+			end
  459
+		end
  460
+		@children = rebuilt
  461
+	end
  462
+end
227  vendor/maruku/maruku/attributes.rb
... ...
@@ -0,0 +1,227 @@
  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
+	def quote_if_needed
  24
+		if /[\s\'\"]/.match self
  25
+			inspect
  26
+		else
  27
+			self
  28
+		end
  29
+	end
  30
+end
  31
+
  32
+module MaRuKu; 
  33
+	MagicChar = ':'
  34
+	
  35
+	class AttributeList < Array
  36
+		
  37
+		# An attribute list becomes 
  38
+		# {#id .cl key="val" ref}
  39
+		# [ [:id, 'id'], [:class, 'id'], ['key', 'val'], [ :ref, 'ref' ]]
  40
+
  41
+		private :push
  42
+		
  43
+		def push_key_val(key, val); 
  44
+			raise "Bad #{key.inspect}=#{val.inspect}" if not key and val
  45
+			push [key, val] 
  46
+		end
  47
+		def push_ref(ref_id);       
  48
+			
  49
+			raise "Bad :ref #{ref_id.inspect}" if not ref_id
  50
+			push [:ref, ref_id+""] 
  51
+
  52
+#			p "Now ", self ########################################
  53
+		end
  54
+		def push_class(val);        
  55
+			raise "Bad :id #{val.inspect}" if not val
  56
+			push [:class,  val] 
  57
+		end
  58
+		def push_id(val);           
  59
+			raise "Bad :id #{val.inspect}" if not val
  60
+			push [:id,  val] 
  61
+		end
  62
+		
  63
+		def to_s
  64
+			map do |k,v|
  65
+				case k
  66
+				when :id;    "#" + v.quote_if_needed
  67
+				when :class; "." + v.quote_if_needed
  68
+				when :ref;    v.quote_if_needed
  69
+				else k.quote_if_needed + "=" + v.quote_if_needed
  70
+				end
  71
+			end . join(' ')
  72
+		end
  73
+		alias to_md to_s 
  74
+	end
  75
+	
  76
+end
  77
+
  78
+module MaRuKu; module In; module Markdown; module SpanLevelParser
  79
+	
  80
+	def unit_tests_for_attribute_lists
  81
+		[
  82
+			[ "",     [], "Empty lists are allowed" ], 
  83
+			[ "=",    :throw, "Bad char to begin a list with." ], 
  84
+			[ "a =b", :throw, "No whitespace before `=`." ], 
  85
+			[ "a= b", :throw, "No whitespace after `=`." ], 
  86
+
  87
+			[ "a b", [[:ref, 'a'],[:ref, 'b']], "More than one ref" ], 
  88
+			[ "a b c", [[:ref, 'a'],[:ref, 'b'],[:ref, 'c']], "More than one ref" ], 
  89
+			[ "hello notfound", [[:ref, 'hello'],[:ref, 'notfound']]], 
  90
+
  91
+			[ "'a'",  [[:ref, 'a']], "Quoted value." ], 
  92
+			[ '"a"'   ], 
  93
+
  94
+			[ "a=b",  [['a','b']], "Simple key/val" ], 
  95
+			[ "'a'=b"   ], 
  96
+			[ "'a'='b'" ], 
  97
+			[ "a='b'"   ], 
  98
+
  99
+			[ 'a="b\'"',  [['a',"b\'"]], "Key/val with quotes" ],
  100
+			[ 'a=b\''],
  101
+			[ 'a="\\\'b\'"',  [['a',"\'b\'"]], "Key/val with quotes" ], 
  102
+			
  103
+			['"', :throw, "Unclosed quotes"],
  104
+			["'"],
  105
+			["'a "],
  106
+			['"a '],
  107
+			
  108
+			[ "#a",  [[:id, 'a']], "Simple ID" ], 
  109
+			[ "#'a'" ], 
  110
+			[ '#"a"' ], 
  111
+
  112
+			[ "#",  :throw, "Unfinished '#'." ], 
  113
+			[ ".",  :throw, "Unfinished '.'." ], 
  114
+			[ "# a",  :throw, "No white-space after '#'." ], 
  115
+			[ ". a",  :throw, "No white-space after '.' ." ], 
  116
+			
  117
+			[ "a=b c=d",  [['a','b'],['c','d']], "Tabbing" ], 
  118
+			[ " \ta=b \tc='d' "],
  119
+			[ "\t a=b\t c='d'\t\t"],
  120
+			
  121
+			[ ".\"a'",  :throw, "Mixing quotes is bad." ], 
  122
+			
  123
+		].map { |s, expected, comment| 
  124
+			@expected = (expected ||= @expected)
  125
+			@comment  = (comment  ||= (last=@comment) )
  126
+			(comment == last && (comment += (@count+=1).to_s)) || @count = 1
  127
+			expected = [md_ial(expected)] if expected.kind_of? Array
  128
+			["{#{MagicChar}#{s}}", expected, "Attributes: #{comment}"]
  129
+		}
  130
+	end
  131
+	
  132
+	def md_al(s=[]); AttributeList.new(s) end
  133
+
  134
+	# returns nil or an AttributeList
  135
+	def read_attribute_list(src, con, break_on_chars)
  136
+		
  137
+		separators = break_on_chars + [?=,?\ ,?\t]
  138
+		escaped = Maruku::EscapedCharInQuotes
  139
+			
  140
+		al = AttributeList.new
  141
+		while true
  142
+			src.consume_whitespace
  143
+			break if break_on_chars.include? src.cur_char
  144
+	
  145
+			case src.cur_char
  146
+			when nil 
  147
+				maruku_error "Attribute list terminated by EOF:\n "+
  148
+				             "#{al.inspect}" , src, con
  149
+				tell_user "I try to continue and return partial attribute list:\n"+
  150
+					al.inspect
  151
+				break
  152
+			when ?=     # error
  153
+				maruku_error "In attribute lists, cannot start identifier with `=`."
  154
+				tell_user "I try to continue"
  155
+				src.ignore_char
  156
+			when ?#     # id definition
  157
+				src.ignore_char
  158
+				if id = read_quoted_or_unquoted(src, con, escaped, separators)
  159
+					al.push_id id
  160
+				else
  161
+					maruku_error 'Could not read `id` attribute.', src, con
  162
+					tell_user 'Trying to ignore bad `id` attribute.'
  163
+				end
  164
+			when ?.     # class definition
  165
+				src.ignore_char
  166
+				if klass = read_quoted_or_unquoted(src, con, escaped, separators)
  167
+					al.push_class klass
  168
+				else
  169
+					maruku_error 'Could not read `class` attribute.', src, con
  170
+					tell_user 'Trying to ignore bad `class` attribute.'
  171
+				end
  172
+			else
  173
+				if key = read_quoted_or_unquoted(src, con, escaped, separators)
  174
+					if src.cur_char == ?=
  175
+						src.ignore_char # skip the =
  176
+						if val = read_quoted_or_unquoted(src, con, escaped, separators)
  177
+							al.push_key_val(key, val)
  178
+						else
  179
+							maruku_error "Could not read value for key #{key.inspect}.",
  180
+								src, con
  181
+							tell_user "Ignoring key #{key.inspect}."
  182
+						end
  183
+					else
  184
+						al.push_ref key
  185
+					end
  186
+				else
  187
+					maruku_error 'Could not read key or reference.'
  188
+				end
  189
+			end # case
  190
+		end # while true
  191
+		al
  192
+	end
  193
+	
  194
+	
  195
+	# We need a helper
  196
+	def is_ial(e); e.kind_of? MDElement and e.node_type == :ial end
  197
+
  198
+	def merge_ial(elements, src, con)	
  199
+
  200
+		# Apply each IAL to the element before
  201
+		elements.each_with_index do |e, i| 
  202
+		if is_ial(e) && i>= 1 then
  203
+			before = elements[i-1]
  204
+			after = elements[i+1]
  205
+			if before.kind_of? MDElement
  206
+				before.al = e.ial
  207
+			elsif after.kind_of? MDElement
  208
+				after.al = e.ial
  209
+			else
  210
+				maruku_error "It is not clear to me what element this IAL {:#{e.ial.to_md}} \n"+
  211
+				"is referring to. The element before is a #{before.class.to_s}, \n"+
  212
+				"the element after is a #{after.class.to_s}.\n"+
  213
+				"\n before: #{before.inspect}"+
  214
+				"\n after: #{after.inspect}",
  215
+				src, con
  216
+				# xxx dire se c'è empty vicino
  217
+			end
  218
+		end 
  219
+		end
  220
+		
  221
+		if not Globals[:debug_keep_ials]
  222
+			elements.delete_if {|x| is_ial(x) unless x == elements.first} 
  223
+		end
  224
+	end
  225
+		
  226
+end end end end 
  227
+#module MaRuKu; module In; module Markdown; module SpanLevelParser
70  vendor/maruku/maruku/defaults.rb
... ...
@@ -0,0 +1,70 @@
  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
+Globals = {
  25
+	:unsafe_features => false,
  26
+	:on_error => :warning,
  27
+	
  28
+	
  29
+	:use_numbered_headers => false,
  30
+	
  31
+	:maruku_signature => false,
  32
+	:code_background_color => '#fef',
  33
+	:code_show_spaces => false,
  34
+	
  35
+	:filter_html => false,
  36
+	
  37
+	:html_math_output_mathml => true, # also set :html_math_engine
  38
+	:html_math_engine => 'none', #ritex, itex2mml
  39
+	
  40
+	:html_math_output_png => false, 	
  41
+	:html_png_engine => 'none',
  42
+	:html_png_dir => 'pngs',
  43
+	:html_png_url => 'pngs/',
  44
+	:html_png_resolution => 200,
  45
+	
  46
+	:html_use_syntax => false,
  47
+	
  48
+	:latex_use_listings => false,
  49
+	:latex_cjk => false,
  50
+	
  51
+	:debug_keep_ials => false,
  52
+	:doc_prefix => ''
  53
+}
  54
+
  55
+class MDElement
  56
+	def get_setting(sym)
  57
+		if self.attributes.has_key?(sym) then
  58
+			return self.attributes[sym]
  59
+		elsif self.doc && self.doc.attributes.has_key?(sym) then
  60
+			return self.doc.attributes[sym]
  61
+		elsif MaRuKu::Globals.has_key?(sym)
  62
+			return MaRuKu::Globals[sym]
  63
+		else
  64
+			$stderr.puts "Bug: no default for #{sym.inspect}"
  65
+			nil
  66
+		end
  67
+	end
  68
+end
  69
+
  70
+end
92  vendor/maruku/maruku/errors_management.rb
... ...
@@ -0,0 +1,92 @@
  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
+#m  Any method that detects formatting error calls the
  24
+#m  maruku_error() method. 
  25
+#m  if @meta[:on_error] == 
  26
+#m
  27
+#m  - :warning   write on the standard err (or @error_stream if defined), 
  28
+#m              then do your best.
  29
+#m  - :ignore    be shy and try to continue
  30
+#m  - :raise     raises a MarukuException
  31
+#m
  32
+#m  default is :raise
  33
+
  34
+module MaRuKu
  35
+	
  36
+	class Exception < RuntimeError
  37
+	end
  38
+	
  39
+module Errors
  40
+	
  41
+	def maruku_error(s,src=nil,con=nil)
  42
+		policy = get_setting(:on_error)
  43
+		
  44
+		case policy
  45
+		when :ignore 
  46
+		when :raise
  47
+			raise_error create_frame(describe_error(s,src,con))
  48
+		when :warning
  49
+			tell_user create_frame(describe_error(s,src,con))
  50
+		else
  51
+			raise "BugBug: policy = #{policy.inspect}"
  52
+		end
  53
+	end
  54
+	
  55
+	def maruku_recover(s,src=nil,con=nil)
  56
+		tell_user create_frame(describe_error(s,src,con))
  57
+	end
  58
+	
  59
+	alias error maruku_error
  60
+
  61
+	def raise_error(s)
  62
+		raise MaRuKu::Exception, s, caller
  63
+	end
  64
+
  65
+	def tell_user(s)
  66
+		error_stream = self.attributes[:error_stream] || $stderr
  67
+		error_stream << s 
  68
+	end
  69
+	
  70
+	def create_frame(s)
  71
+		n = 75
  72
+		"\n" +
  73
+		" "+"_"*n + "\n"+
  74
+		"| Maruku tells you:\n" +
  75
+		"+" + ("-"*n) +"\n"+
  76
+		add_tabs(s,1,'| ') + "\n" +
  77
+		"+" + ("-"*n) + "\n" +
  78
+		add_tabs(caller[0, 5].join("\n"),1,'!') + "\n" +
  79
+		"\\" + ("_"*n) + "\n"
  80
+	end
  81
+
  82
+	def describe_error(s,src,con)
  83
+		t = s
  84
+		src && (t += "\n#{src.describe}\n")
  85
+		con && (t += "\n#{con.describe}\n")
  86
+		t
  87
+	end
  88
+	
  89
+end # Errors
  90
+end # MaRuKu
  91
+
  92
+
100  vendor/maruku/maruku/ext/div.rb
... ...
@@ -0,0 +1,100 @@
  1
+
  2
+
  3
+OpenDiv = /^[ ]{0,3}\+\-\-+\s*([^\s-]*)\s*\-*\s*$/
  4
+CloseDiv = /^[ ]{0,3}\=\-\-+\s*([^\s-]*)\s*\-*\s*$/
  5
+StartPipe = /^[ ]{0,3}\|(.*)$/ # $1 is rest of line
  6
+DecorativeClosing = OpenDiv
  7
+
  8
+MaRuKu::In::Markdown::register_block_extension(
  9
+	:regexp  => OpenDiv,
  10
+	:handler => lambda { |doc, src, context|
  11
+		# return false if not doc.is_math_enabled?
  12
+		first = src.shift_line
  13
+		first =~ OpenDiv
  14
+		ial_at_beginning = $1
  15
+		ial_at_end = nil
  16
+		
  17
+		lines = []
  18
+		# if second line starts with "|"
  19
+		if src.cur_line =~ StartPipe
  20
+			# then we read until no more "|"
  21
+			while src.cur_line && (src.cur_line =~ StartPipe)
  22
+				content = $1
  23
+				lines.push content
  24
+				src.shift_line
  25
+			end
  26
+			if src.cur_line =~ DecorativeClosing
  27
+				ial_at_end = $1
  28
+				src.shift_line 
  29
+			end
  30
+		else
  31
+			# else we read until CloseDiv
  32
+			divs_open = 1
  33
+			while src.cur_line && (divs_open>0)
  34
+				if src.cur_line =~ CloseDiv
  35
+					divs_open -= 1
  36
+					if divs_open == 0
  37
+						ial_at_end = $1
  38
+						src.shift_line
  39
+						break
  40
+					else
  41
+						lines.push src.shift_line
  42
+					end
  43
+				else
  44
+					if src.cur_line =~ OpenDiv
  45
+						divs_open += 1 
  46
+					end
  47
+					lines.push src.shift_line
  48
+				end
  49
+			end
  50
+			
  51
+			if divs_open > 0
  52
+				e = "At end of input, I still have #{divs_open} DIVs open."
  53
+				doc.maruku_error(e, src, context)
  54
+				return true
  55
+			end
  56
+		end
  57
+		
  58
+		ial_at_beginning = nil unless 
  59
+			(ial_at_beginning&&ial_at_beginning.size > 0)
  60
+		ial_at_end = nil unless (ial_at_end && ial_at_end.size > 0)
  61
+
  62
+		if ial_at_beginning && ial_at_end
  63
+			e = "Found two conflicting IALs: #{ial_at_beginning.inspect} and #{ial_at_end.inspect}"
  64
+			doc.maruku_error(e, src, context)
  65
+		end
  66
+		
  67
+		al_string = ial_at_beginning || ial_at_end
  68
+		al = nil
  69
+		
  70
+		if al_string =~ /^\{(.*)\}$/
  71
+			inside = $1
  72
+		cs = MaRuKu::In::Markdown::SpanLevelParser::CharSource
  73
+		al = al_string &&
  74
+			doc.read_attribute_list(cs.new(inside), its_context=nil, break_on=[nil])
  75
+		end
  76
+		
  77
+		src = MaRuKu::In::Markdown::BlockLevelParser::LineSource.new(lines)
  78
+		children = doc.parse_blocks(src)
  79
+
  80
+		context.push doc.md_div(children, al)
  81
+		true
  82
+	})