org-ruby 0.7.2 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -101,85 +101,45 @@ module Orgmode
101
101
  table_header_set = false
102
102
  @lines.each do |text|
103
103
  line = Line.new text, self
104
+ mode = :normal if line.end_block? and mode == line.paragraph_type
105
+ mode = :normal if line.property_drawer_end_block? and mode == :property_drawer
104
106
 
105
107
  case mode
106
- when :normal
107
-
108
- if (Headline.headline? line.line) then
109
- @current_headline = Headline.new line.line, self, offset
110
- @headlines << @current_headline
111
- else
112
- # If there is a setting on this line, remember it.
113
- line.in_buffer_setting? do |key, value|
114
- store_in_buffer_setting key, value
115
- end
116
- if line.table_separator? then
117
- if previous_line and previous_line.paragraph_type == :table_row and !table_header_set
118
- previous_line.assigned_paragraph_type = :table_header
119
- table_header_set = true
120
- end
108
+ when :normal, :quote, :center
109
+ if Headline.headline? line.line
110
+ line = Headline.new line.line, self, offset
111
+ elsif line.table_separator?
112
+ if previous_line and previous_line.paragraph_type == :table_row and !table_header_set
113
+ previous_line.assigned_paragraph_type = :table_header
114
+ table_header_set = true
121
115
  end
122
- table_header_set = false if !line.table?
123
- mode = :code if line.begin_block? and line.block_type.casecmp("EXAMPLE") == 0
124
- mode = :src_code if line.begin_block? and line.block_type.casecmp("SRC") == 0
125
- mode = :block_comment if line.begin_block? and line.block_type == "COMMENT"
126
- mode = :property_drawer if line.property_drawer_begin_block?
127
- if (@current_headline) then
128
- @current_headline.body_lines << line
129
- else
130
- @header_lines << line
131
- end
132
- end
133
-
134
- when :block_comment
135
-
136
- if line.end_block? and line.block_type == "COMMENT"
137
- mode = :normal
138
- else
139
- line.assigned_paragraph_type = :comment
140
116
  end
117
+ table_header_set = false if !line.table?
141
118
 
142
- when :code
143
-
144
- # As long as we stay in code mode, force lines to be either blank or paragraphs.
119
+ when :example, :html, :src
120
+ # As long as we stay in code mode, force lines to be code.
145
121
  # Don't try to interpret structural items, like headings and tables.
146
- if line.end_block? and line.code_block?
147
- mode = :normal
148
- else
149
- line.assigned_paragraph_type = :paragraph unless line.blank?
150
- end
151
- if (@current_headline) then
152
- @current_headline.body_lines << line
153
- else
154
- @header_lines << line
155
- end
156
-
157
- when :src_code
122
+ line.assigned_paragraph_type = :code
123
+ end
158
124
 
159
- if line.end_block? and line.code_block?
160
- mode = :normal
161
- else
162
- line.assigned_paragraph_type = :src
163
- end
164
- if (@current_headline) then
165
- @current_headline.body_lines << line
166
- else
167
- @header_lines << line
125
+ if mode == :normal
126
+ @headlines << @current_headline = line if Headline.headline? line.line
127
+ # If there is a setting on this line, remember it.
128
+ line.in_buffer_setting? do |key, value|
129
+ store_in_buffer_setting key.upcase, value
168
130
  end
169
131
 
170
- when :property_drawer
132
+ mode = line.paragraph_type if line.begin_block?
133
+ mode = :property_drawer if line.property_drawer_begin_block?
134
+ end
171
135
 
172
- if line.property_drawer_end_block?
173
- mode = :normal
174
- else
175
- line.assigned_paragraph_type = :property_drawer unless line.blank?
176
- end
177
- if (@current_headline) then
136
+ unless mode == :comment
137
+ if @current_headline
178
138
  @current_headline.body_lines << line
179
139
  else
180
140
  @header_lines << line
181
141
  end
182
- end # case
142
+ end
183
143
 
184
144
  previous_line = line
185
145
  end # @lines.each
@@ -196,9 +156,9 @@ module Orgmode
196
156
  output = ""
197
157
  output_buffer = TextileOutputBuffer.new(output)
198
158
 
199
- Parser.translate(@header_lines, output_buffer)
159
+ translate(@header_lines, output_buffer)
200
160
  @headlines.each do |headline|
201
- Parser.translate(headline.body_lines, output_buffer)
161
+ translate(headline.body_lines, output_buffer)
202
162
  end
203
163
  output
204
164
  end
@@ -207,7 +167,7 @@ module Orgmode
207
167
  def to_html
208
168
  mark_trees_for_export
209
169
  export_options = {
210
- :decorate_title => true,
170
+ :decorate_title => @in_buffer_settings["TITLE"],
211
171
  :export_heading_number => export_heading_number?,
212
172
  :export_todo => export_todo?,
213
173
  :use_sub_superscripts => use_sub_superscripts?,
@@ -222,9 +182,9 @@ module Orgmode
222
182
  # If we're given a new title, then just create a new line
223
183
  # for that title.
224
184
  title = Line.new(@in_buffer_settings["TITLE"], self)
225
- Parser.translate([title], output_buffer)
185
+ translate([title], output_buffer)
226
186
  end
227
- Parser.translate(@header_lines, output_buffer) unless skip_header_lines?
187
+ translate(@header_lines, output_buffer) unless skip_header_lines?
228
188
 
229
189
  # If we've output anything at all, remove the :decorate_title option.
230
190
  export_options.delete(:decorate_title) if (output.length > 0)
@@ -234,11 +194,13 @@ module Orgmode
234
194
  when :exclude
235
195
  # NOTHING
236
196
  when :headline_only
237
- Parser.translate(headline.body_lines[0, 1], output_buffer)
197
+ translate(headline.body_lines[0, 1], output_buffer)
238
198
  when :all
239
- Parser.translate(headline.body_lines, output_buffer)
199
+ translate(headline.body_lines, output_buffer)
240
200
  end
241
201
  end
202
+ output << "\n"
203
+
242
204
  rp = RubyPants.new(output)
243
205
  rp.to_html
244
206
  end
@@ -248,58 +210,11 @@ module Orgmode
248
210
 
249
211
  # Converts an array of lines to the appropriate format.
250
212
  # Writes the output to +output_buffer+.
251
- def self.translate(lines, output_buffer)
213
+ def translate(lines, output_buffer)
252
214
  output_buffer.output_type = :start
253
- lines.each do |line|
254
-
255
- # See if we're carrying paragraph payload, and output
256
- # it if we're about to switch to some other output type.
257
- output_buffer.prepare(line)
258
- case line.paragraph_type
259
- when :metadata, :table_separator, :blank, :comment, :property_drawer_item, :property_drawer_begin_block, :property_drawer_end_block
260
-
261
- output_buffer << line.line if output_buffer.preserve_whitespace?
262
-
263
- when :begin_block
264
-
265
- output_buffer.push_mode(:blockquote) if line.block_type.casecmp("QUOTE") == 0
266
- output_buffer.push_mode(:src) if line.block_type.casecmp("SRC") == 0
267
- output_buffer.push_mode(:example) if line.block_type.casecmp("EXAMPLE") == 0
268
- output_buffer.push_mode(:center) if line.block_type.casecmp("CENTER") == 0
269
-
270
- when :end_block
271
-
272
- output_buffer.pop_mode(:blockquote) if line.block_type.casecmp("QUOTE") == 0
273
- output_buffer.pop_mode(:src) if line.block_type.casecmp("SRC") == 0
274
- output_buffer.pop_mode(:example) if line.block_type.casecmp("EXAMPLE") == 0
275
- output_buffer.pop_mode(:center) if line.block_type.casecmp("CENTER") == 0
276
-
277
- when :table_row, :table_header
278
-
279
- output_buffer << line.line.lstrip
280
-
281
- when :unordered_list, :ordered_list, :definition_list
282
-
283
- output_buffer << line.output_text << " "
284
-
285
- when :inline_example
286
-
287
- output_buffer << line.output_text
288
-
289
- when :src
290
-
291
- output_buffer << line.output_text << "\n"
292
-
293
- else
294
- if output_buffer.preserve_whitespace? then
295
- output_buffer << line.output_text
296
- else
297
- output_buffer << line.output_text.strip << " "
298
- end
299
- end
300
- end
215
+ lines.each { |line| output_buffer.insert(line) }
301
216
  output_buffer.flush!
302
- output_buffer.pop_mode until output_buffer.current_mode == :normal
217
+ output_buffer.pop_mode while output_buffer.current_mode
303
218
  output_buffer.output_footnotes!
304
219
  output_buffer.output
305
220
  end
@@ -39,24 +39,20 @@ module Orgmode
39
39
  # body-regexp A regexp like \".\" to match a body character. Don't use
40
40
  # non-shy groups here, and don't allow newline here.
41
41
  # newline The maximum number of newlines allowed in an emphasis exp.
42
- #
43
- # I currently don't use +newline+ because I've thrown this information
44
- # away by this point in the code. TODO -- revisit?
45
- attr_reader :pre_emphasis
46
- attr_reader :post_emphasis
47
- attr_reader :border_forbidden
48
- attr_reader :body_regexp
49
- attr_reader :markers
50
42
 
51
- attr_reader :org_emphasis_regexp
43
+ attr_reader :org_image_file_regexp
52
44
 
53
45
  def initialize
54
46
  # Set up the emphasis regular expression.
55
- @pre_emphasis = " \t\\('\"\\{"
56
- @post_emphasis = "- \t\\.,:!\\?;'\"\\)\\}\\\\"
57
- @border_forbidden = " \t\r\n,\"'"
58
- @body_regexp = ".*?"
59
- @markers = "\\*\\/_=~\\+"
47
+ @pre_emphasis = ' \t\(\'"\{'
48
+ @post_emphasis = '- \t\.,:!\?;\'"\)\}\\\\'
49
+ @border_forbidden = '\s,"\''
50
+ @body_regexp = '.*?'
51
+ @max_newlines = 1
52
+ @body_regexp = "#{@body_regexp}" +
53
+ "(?:\\n#{@body_regexp}){0,#{@max_newlines}}" if @max_newlines > 0
54
+ @markers = '\*\/_=~\+'
55
+ @code_snippet_stack = []
60
56
  @logger = Logger.new(STDERR)
61
57
  @logger.level = Logger::WARN
62
58
  build_org_emphasis_regexp
@@ -94,23 +90,32 @@ module Orgmode
94
90
  # replace "*bold*", "/italic/", and "=code=",
95
91
  # respectively. (Clearly this sample string will use HTML-like
96
92
  # syntax, assuming +map+ is defined appropriately.)
97
- def rewrite_emphasis(str)
98
- str.gsub(@org_emphasis_regexp) do |match|
99
- inner = yield $2, $3
93
+ def rewrite_emphasis str
94
+ # escape the percent signs for safe restoring code snippets
95
+ str.gsub!(/%/, "%%")
96
+ format_str = "%s"
97
+ str.gsub! @org_emphasis_regexp do |match|
98
+ # preserve the code snippet from further formatting
99
+ inner = if $2 == "=" or $2 == "~"
100
+ @code_snippet_stack.push $3
101
+ yield $2, format_str
102
+ else
103
+ yield $2, $3
104
+ end
100
105
  "#{$1}#{inner}"
101
106
  end
102
107
  end
103
108
 
104
109
  # rewrite subscript and superscript (_{foo} and ^{bar})
105
- def rewrite_subp(str) # :yields: type ("_" for subscript and "^" for superscript), text
106
- str.gsub(@org_subp_regexp) do |match|
110
+ def rewrite_subp str # :yields: type ("_" for subscript and "^" for superscript), text
111
+ str.gsub! @org_subp_regexp do |match|
107
112
  yield $1, $2
108
113
  end
109
114
  end
110
115
 
111
116
  # rewrite footnotes
112
- def rewrite_footnote(str) # :yields: name, definition or nil
113
- str.gsub(@org_footnote_regexp) do |match|
117
+ def rewrite_footnote str # :yields: name, definition or nil
118
+ str.gsub! @org_footnote_regexp do |match|
114
119
  yield $1, $3
115
120
  end
116
121
  end
@@ -140,48 +145,43 @@ module Orgmode
140
145
  # +http://www.hotmail.com+. In both cases, the block returns an
141
146
  # HTML-style link, and that is how things will get recorded in
142
147
  # +result+.
143
- def rewrite_links(str) # :yields: link, text
144
- str.gsub(@org_link_regexp) do |match|
148
+ def rewrite_links str # :yields: link, text
149
+ str.gsub! @org_link_regexp do |match|
150
+ yield $1, $3
151
+ end
152
+ str.gsub! @org_angle_link_text_regexp do |match|
145
153
  yield $1, nil
146
- end.gsub(@org_link_text_regexp) do |match|
147
- yield $1, $2
148
- end.gsub(@org_angle_link_text_regexp) do |match|
149
- yield "#{$2}:#{$3}", nil
150
154
  end
155
+
156
+ str # for testing
151
157
  end
152
158
 
153
- # Rewrites all of the inline image tags.
154
- def rewrite_images(str) # :yields: image_link
155
- str.gsub(@org_img_regexp) do |match|
156
- yield $1
157
- end
159
+ def restore_code_snippets str
160
+ str = str % @code_snippet_stack
161
+ @code_snippet_stack = []
162
+ str
158
163
  end
159
164
 
160
165
  private
161
166
 
162
167
  def build_org_emphasis_regexp
163
- @org_emphasis_regexp = Regexp.new("([#{@pre_emphasis}]|^)\n" +
164
- "( [#{@markers}] ) (?!\\2)\n" +
165
- "( [^#{@border_forbidden}] | " +
166
- "[^#{@border_forbidden}]#{@body_regexp}[^#{@border_forbidden}] )\n" +
167
- "\\2\n" +
168
- "(?=[#{@post_emphasis}]|$)\n", Regexp::EXTENDED)
168
+ @org_emphasis_regexp = Regexp.new("([#{@pre_emphasis}]|^)" +
169
+ "([#{@markers}])(?!\\2)" +
170
+ "([^#{@border_forbidden}]|" +
171
+ "[^#{@border_forbidden}]#{@body_regexp}" +
172
+ "[^#{@border_forbidden}])\\2" +
173
+ "(?=[#{@post_emphasis}]|$)")
169
174
  @logger.debug "Just created regexp: #{@org_emphasis_regexp}"
170
175
  end
171
176
 
172
177
  def build_org_link_regexp
173
178
  @org_link_regexp = /\[\[
174
- ([^\]]*) # This is the URL
175
- \]\]/x
176
- @org_img_regexp = /\[\[
177
- ([^\]]*\.(jpg|jpeg|gif|png)) # Like a normal URL, but must end with a specified extension
178
- \]\]/xi
179
- @org_link_text_regexp = /\[\[
180
- ([^\]]*) # This is the URL
181
- \]\[
182
- ([^\]]*) # This is the friendly text
183
- \]\]/x
184
- @org_angle_link_text_regexp = /(<|&lt;)(\w+):([^\]\t\n\r<> ][^\]\t\n\r<> ]*)(>|&gt;)/x
179
+ ([^\]\[]+) # This is the URL
180
+ \](\[
181
+ ([^\]\[]+) # This is the friendly text
182
+ \])?\]/x
183
+ @org_angle_link_text_regexp = /<(\w+:[^\]\s<>]+)>/
184
+ @org_image_file_regexp = /\.(gif|jpe?g|p(?:bm|gm|n[gm]|pm)|svg|tiff?|x[bp]m)/i
185
185
  end
186
186
  end # class Emphasis
187
187
  end # module Orgmode
@@ -6,21 +6,28 @@ module Orgmode
6
6
 
7
7
  def initialize(output)
8
8
  super(output)
9
- @add_paragraph = false
9
+ @add_paragraph = true
10
10
  @support_definition_list = true # TODO this should be an option
11
- @footnotes = {}
11
+ @footnotes = []
12
12
  end
13
13
 
14
- def push_mode(mode)
14
+ def push_mode(mode, indent)
15
+ @list_indent_stack.push(indent)
15
16
  super(mode)
16
- @output << "bc.. " if mode_is_code(mode)
17
- @output << "\np=. " if mode == :center
17
+ @output << "bc. " if mode_is_code? mode
18
+ if mode == :center or mode == :quote
19
+ @add_paragraph = false
20
+ @output << "\n"
21
+ end
18
22
  end
19
23
 
20
24
  def pop_mode(mode = nil)
21
25
  m = super(mode)
22
- @add_paragraph = (mode_is_code(m))
23
- @output << "\n" if mode == :center
26
+ @list_indent_stack.pop
27
+ if m == :center or m == :quote
28
+ @add_paragraph = true
29
+ @output << "\n"
30
+ end
24
31
  m
25
32
  end
26
33
 
@@ -36,37 +43,68 @@ module Orgmode
36
43
 
37
44
  # Handles inline formatting for textile.
38
45
  def inline_formatting(input)
39
- input = @re_help.rewrite_emphasis(input) do |marker, body|
46
+ @re_help.rewrite_emphasis input do |marker, body|
40
47
  m = TextileMap[marker]
41
48
  "#{m}#{body}#{m}"
42
49
  end
43
- input = @re_help.rewrite_subp(input) do |type, text|
50
+ @re_help.rewrite_subp input do |type, text|
44
51
  if type == "_" then
45
52
  "~#{text}~"
46
53
  elsif type == "^" then
47
54
  "^#{text}^"
48
55
  end
49
56
  end
50
- input = @re_help.rewrite_links(input) do |link, text|
51
- text ||= link
52
- link = link.gsub(/ /, "%20")
53
- "\"#{text}\":#{link}"
57
+ @re_help.rewrite_links input do |link, defi|
58
+ [link, defi].compact.each do |text|
59
+ # We don't support search links right now. Get rid of it.
60
+ text.sub!(/\A(file:[^\s]+)::[^\s]*?\Z/, "\\1")
61
+ text.sub!(/\A(file:[^\s]+)\.org\Z/i, "\\1.textile")
62
+ text.sub!(/\Afile:(?=[^\s]+\Z)/, "")
63
+ end
64
+
65
+ # We don't add a description for images in links, because its
66
+ # empty value forces the image to be inlined.
67
+ defi ||= link unless link =~ @re_help.org_image_file_regexp
68
+ link = link.gsub(/ /, "%%20")
69
+
70
+ if defi =~ @re_help.org_image_file_regexp
71
+ defi = "!#{defi}(#{defi})!"
72
+ elsif defi
73
+ defi = "\"#{defi}\""
74
+ end
75
+
76
+ if defi
77
+ "#{defi}:#{link}"
78
+ else
79
+ "!#{link}(#{link})!"
80
+ end
54
81
  end
55
- input = @re_help.rewrite_footnote(input) do |name, defi|
56
- # textile only support numerical names! Use hash as a workarround
57
- name = name.hash.to_s unless name.to_i.to_s == name # check if number
58
- @footnotes[name] = defi if defi
59
- "[#{name}]"
82
+ @re_help.rewrite_footnote input do |name, definition|
83
+ # textile only support numerical names, so we need to do some conversion
84
+ # Try to find the footnote and use its index
85
+ footnote = @footnotes.select {|f| f[:name] == name }.first
86
+ if footnote
87
+ # The latest definition overrides other ones
88
+ footnote[:definition] = definition if definition and not footnote[:definition]
89
+ else
90
+ # There is no footnote with the current name so we add it
91
+ footnote = { :name => name, :definition => definition }
92
+ @footnotes << footnote
93
+ end
94
+
95
+ "[#{@footnotes.index(footnote)}]"
60
96
  end
61
97
  Orgmode.special_symbols_to_textile(input)
98
+ input = @re_help.restore_code_snippets input
62
99
  input
63
100
  end
64
101
 
65
102
  def output_footnotes!
66
103
  return false if @footnotes.empty?
67
104
 
68
- @footnotes.each do |name, defi|
69
- @output << "\nfn#{name}. #{defi}\n"
105
+ @footnotes.each do |footnote|
106
+ index = @footnotes.index(footnote)
107
+ @output << "\nfn#{index}. #{footnote[:definition] || 'DEFINITION NOT FOUND' }\n"
70
108
  end
71
109
 
72
110
  return true
@@ -74,35 +112,44 @@ module Orgmode
74
112
 
75
113
  # Flushes the current buffer
76
114
  def flush!
115
+ return false if @buffer.empty? and @output_type != :blank
77
116
  @logger.debug "FLUSH ==========> #{@output_type}"
78
- if (@output_type == :blank) then
117
+ @buffer.gsub!(/\A\n*/, "")
118
+
119
+ case
120
+ when preserve_whitespace?
121
+ @output << @buffer << "\n"
122
+
123
+ when @output_type == :blank
79
124
  @output << "\n"
80
- elsif (@buffer.length > 0) then
81
- if @add_paragraph then
82
- @output << "p. " if @output_type == :paragraph
83
- @add_paragraph = false
84
- end
85
- @output << "bq. " if current_mode == :blockquote
86
- if @output_type == :definition_list and @support_definition_list then
87
- @output << "-" * @list_indent_stack.length << " "
88
- @buffer.sub!("::", ":=")
89
- elsif @output_type == :ordered_list then
90
- @output << "#" * @list_indent_stack.length << " "
91
- elsif @output_type == :unordered_list or \
92
- (@output_type == :definition_list and not @support_definition_list) then
93
- @output << "*" * @list_indent_stack.length << " "
94
- end
95
- if (@buffered_lines[0].kind_of?(Headline)) then
96
- headline = @buffered_lines[0]
97
- raise "Cannot be more than one headline!" if @buffered_lines.length > 1
98
- @output << "h#{headline.level}. #{headline.headline_text}\n"
99
- else
100
- @output << inline_formatting(@buffer) << "\n"
125
+
126
+ else
127
+ case current_mode
128
+ when :paragraph
129
+ @output << "p. " if @add_paragraph
130
+ @output << "p=. " if @mode_stack[0] == :center
131
+ @output << "bq. " if @mode_stack[0] == :quote
132
+
133
+ when :list_item
134
+ if @mode_stack[-2] == :ordered_list
135
+ @output << "#" * @mode_stack.count(:list_item) << " "
136
+ else # corresponds to unordered list
137
+ @output << "*" * @mode_stack.count(:list_item) << " "
138
+ end
139
+
140
+ when :definition_term
141
+ if @support_definition_list
142
+ @output << "-" * @mode_stack.count(:definition_term) << " "
143
+ @buffer.sub!("::", ":=")
144
+ end
101
145
  end
146
+ @output << inline_formatting(@buffer) << "\n"
102
147
  end
103
- clear_accumulation_buffer!
148
+ @buffer = ""
104
149
  end
105
150
 
106
-
151
+ def add_line_attributes headline
152
+ @output << "h#{headline.level}. "
153
+ end
107
154
  end # class TextileOutputBuffer
108
155
  end # module Orgmode