org-ruby 0.7.2 → 0.8.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.
@@ -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