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.
data/lib/org-ruby/line.rb CHANGED
@@ -14,6 +14,12 @@ module Orgmode
14
14
  # Backpointer to the parser that owns this line.
15
15
  attr_reader :parser
16
16
 
17
+ # Paragraph type determined for the line.
18
+ attr_reader :paragraph_type
19
+
20
+ # Major modes associate paragraphs with a table, list and so on.
21
+ attr_reader :major_mode
22
+
17
23
  # A line can have its type assigned instead of inferred from its
18
24
  # content. For example, something that parses as a "table" on its
19
25
  # own ("| one | two|\n") may just be a paragraph if it's inside
@@ -26,6 +32,8 @@ module Orgmode
26
32
  @line = line
27
33
  @indent = 0
28
34
  @line =~ /\s*/
35
+ determine_paragraph_type
36
+ determine_major_mode
29
37
  @assigned_paragraph_type = nil
30
38
  @indent = $&.length unless blank?
31
39
  end
@@ -88,7 +96,7 @@ module Orgmode
88
96
  @line.sub(UnorderedListRegexp, "")
89
97
  end
90
98
 
91
- DefinitionListRegexp = /^\s*(-|\+|\s+[*])\s*(.*?)::/
99
+ DefinitionListRegexp = /^\s*(-|\+|\s+[*])\s+(.*\s+|)::($|\s+)/
92
100
 
93
101
  def definition_list?
94
102
  check_assignment_or_regexp(:definition_list, DefinitionListRegexp)
@@ -116,6 +124,7 @@ module Orgmode
116
124
  return strip_ordered_list_tag if ordered_list?
117
125
  return strip_unordered_list_tag if unordered_list?
118
126
  return @line.sub(InlineExampleRegexp, "") if inline_example?
127
+ return strip_raw_text_tag if raw_text?
119
128
  return line
120
129
  end
121
130
 
@@ -168,10 +177,6 @@ module Orgmode
168
177
  block_type =~ /^(EXAMPLE|SRC)$/i
169
178
  end
170
179
 
171
- def code_block_line?
172
- @assigned_paragraph_type == :src
173
- end
174
-
175
180
  InlineExampleRegexp = /^\s*:\s/
176
181
 
177
182
  # Test if the line matches the "inline example" case:
@@ -180,6 +185,21 @@ module Orgmode
180
185
  check_assignment_or_regexp(:inline_example, InlineExampleRegexp)
181
186
  end
182
187
 
188
+ RawTextRegexp = /^(\s*)#\+(\w+):\s*/
189
+
190
+ # Checks if this line is raw text.
191
+ def raw_text?
192
+ check_assignment_or_regexp(:raw_text, RawTextRegexp)
193
+ end
194
+
195
+ def raw_text_tag
196
+ $2.upcase if @line =~ RawTextRegexp
197
+ end
198
+
199
+ def strip_raw_text_tag
200
+ @line.sub(RawTextRegexp) { |match| $1 }
201
+ end
202
+
183
203
  InBufferSettingRegexp = /^#\+(\w+):\s*(.*)$/
184
204
 
185
205
  # call-seq:
@@ -202,25 +222,60 @@ module Orgmode
202
222
  end
203
223
 
204
224
  # Determines the paragraph type of the current line.
205
- def paragraph_type
206
- return :blank if blank?
207
- return :src if code_block_line? # Do not try to guess the type of this line if it is accumulating source code
208
- return :definition_list if definition_list? # order is important! A definition_list is also an unordered_list!
209
- return :ordered_list if ordered_list?
210
- return :unordered_list if unordered_list?
211
- return :property_drawer_begin_block if property_drawer_begin_block?
212
- return :property_drawer_end_block if property_drawer_end_block?
213
- return :property_drawer_item if property_drawer_item?
214
- return :metadata if metadata?
215
- return :begin_block if begin_block?
216
- return :end_block if end_block?
217
- return :comment if comment?
218
- return :table_separator if table_separator?
219
- return :table_row if table_row?
220
- return :table_header if table_header?
221
- return :inline_example if inline_example?
222
- return :horizontal_rule if horizontal_rule?
223
- return :paragraph
225
+ def determine_paragraph_type
226
+ @paragraph_type = \
227
+ case
228
+ when blank?
229
+ :blank
230
+ when definition_list? # order is important! A definition_list is also an unordered_list!
231
+ :definition_term
232
+ when (ordered_list? or unordered_list?)
233
+ :list_item
234
+ when property_drawer_begin_block?
235
+ :property_drawer_begin_block
236
+ when property_drawer_end_block?
237
+ :property_drawer_end_block
238
+ when property_drawer_item?
239
+ :property_drawer_item
240
+ when metadata?
241
+ :metadata
242
+ when block_type
243
+ case block_type.downcase.to_sym
244
+ when :center, :comment, :example, :html, :quote, :src
245
+ block_type.downcase.to_sym
246
+ else
247
+ :comment
248
+ end
249
+ when raw_text? # order is important! Raw text can be also a comment
250
+ :raw_text
251
+ when comment?
252
+ :comment
253
+ when table_separator?
254
+ :table_separator
255
+ when table_row?
256
+ :table_row
257
+ when table_header?
258
+ :table_header
259
+ when inline_example?
260
+ :inline_example
261
+ when horizontal_rule?
262
+ :horizontal_rule
263
+ else :paragraph
264
+ end
265
+ end
266
+
267
+ def determine_major_mode
268
+ @major_mode = \
269
+ case
270
+ when definition_list? # order is important! A definition_list is also an unordered_list!
271
+ :definition_list
272
+ when ordered_list?
273
+ :ordered_list
274
+ when unordered_list?
275
+ :unordered_list
276
+ when table?
277
+ :table
278
+ end
224
279
  end
225
280
 
226
281
  ######################################################################
@@ -8,40 +8,29 @@ module Orgmode
8
8
  # add a newline character prior emitting the output.
9
9
  class OutputBuffer
10
10
 
11
- # This is the accumulation buffer. It's a holding pen so
12
- # consecutive lines of the right type can get stuck together
13
- # without intervening newlines.
14
- attr_reader :buffer
15
-
16
- # These are the Line objects that are currently in the accumulation
17
- # buffer.
18
- attr_reader :buffered_lines
19
-
20
- # This is the output mode of the accumulation buffer.
21
- attr_reader :buffer_mode
22
-
23
11
  # This is the overall output buffer
24
12
  attr_reader :output
25
13
 
26
14
  # This is the current type of output being accumulated.
27
15
  attr_accessor :output_type
28
16
 
29
- # This stack is used to do proper outline numbering of headlines.
30
- attr_accessor :headline_number_stack
31
-
32
17
  # Creates a new OutputBuffer object that is bound to an output object.
33
18
  # The output will get flushed to =output=.
34
19
  def initialize(output)
35
- @output = output
20
+ # This is the accumulation buffer. It's a holding pen so
21
+ # consecutive lines of the right type can get stuck together
22
+ # without intervening newlines.
36
23
  @buffer = ""
37
- @buffered_lines = []
38
- @buffer_mode = nil
24
+
25
+ # This stack is used to do proper outline numbering of
26
+ # headlines.
27
+ @headline_number_stack = []
28
+
29
+ @output = output
39
30
  @output_type = :start
40
31
  @list_indent_stack = []
41
- @paragraph_modifier = nil
42
- @cancel_modifier = false
43
32
  @mode_stack = []
44
- @headline_number_stack = []
33
+ @code_block_indent = nil
45
34
 
46
35
  @logger = Logger.new(STDERR)
47
36
  if ENV['DEBUG'] or $DEBUG
@@ -51,21 +40,13 @@ module Orgmode
51
40
  end
52
41
 
53
42
  @re_help = RegexpHelper.new
54
- push_mode(:normal)
55
43
  end
56
44
 
57
- Modes = [:normal, :ordered_list, :unordered_list, :definition_list, :blockquote, :src, :example, :table, :inline_example, :center, :property_drawer]
58
-
59
45
  def current_mode
60
46
  @mode_stack.last
61
47
  end
62
48
 
63
- def current_mode_list?
64
- (current_mode == :ordered_list) or (current_mode == :unordered_list)
65
- end
66
-
67
49
  def push_mode(mode)
68
- raise "Not a recognized mode: #{mode}" unless Modes.include?(mode)
69
50
  @mode_stack.push(mode)
70
51
  end
71
52
 
@@ -75,40 +56,49 @@ module Orgmode
75
56
  m
76
57
  end
77
58
 
78
- # Prepares the output buffer to receive content from a line.
79
- # As a side effect, this may flush the current accumulated text.
80
- def prepare(line)
59
+ def insert(line)
60
+ # Prepares the output buffer to receive content from a line.
61
+ # As a side effect, this may flush the current accumulated text.
81
62
  @logger.debug "Looking at #{line.paragraph_type}(#{current_mode}) : #{line.to_s}"
82
- if line.begin_block? and line.code_block?
83
- flush!
84
- # We try to get the lang from #+BEGIN_SRC blocks
85
- @block_lang = line.block_lang
86
- @output_type = line.paragraph_type
87
- elsif current_mode == :example and line.end_block?
63
+ # We try to get the lang from #+BEGIN_SRC blocks
64
+ @block_lang = line.block_lang if line.begin_block?
65
+ unless should_accumulate_output?(line)
88
66
  flush!
89
- @output_type = line.paragraph_type
90
- elsif not should_accumulate_output?(line)
91
- flush!
92
- maintain_list_indent_stack(line)
93
- @output_type = line.paragraph_type
67
+ maintain_mode_stack(line)
94
68
  end
95
- push_mode(:inline_example) if line.inline_example? and current_mode != :inline_example and not line.property_drawer?
96
- pop_mode(:inline_example) if current_mode == :inline_example and !line.inline_example?
97
- push_mode(:property_drawer) if line.property_drawer? and current_mode != :property_drawer
98
- pop_mode(:property_drawer) if current_mode == :property_drawer and line.property_drawer_end_block?
99
- push_mode(:table) if enter_table?
100
- pop_mode(:table) if exit_table?
101
- @buffered_lines.push(line)
102
- end
103
-
104
- # Flushes everything currently in the accumulation buffer into the
105
- # output buffer. Derived classes must override this to actually move
106
- # content into the output buffer with the appropriate markup. This
107
- # method just does common bookkeeping cleanup.
108
- def clear_accumulation_buffer!
109
- @buffer = ""
110
- @buffer_mode = nil
111
- @buffered_lines = []
69
+
70
+ # Adds the current line to the output buffer
71
+ case
72
+ when line.raw_text?
73
+ @buffer << "\n" << line.output_text if line.raw_text_tag == @buffer_tag
74
+ when preserve_whitespace?
75
+ @buffer << "\n" << line.output_text unless line.block_type
76
+ when line.assigned_paragraph_type == :code
77
+ # If the line is contained within a code block but we should
78
+ # not preserve whitespaces, then we do nothing.
79
+ when (line.kind_of? Headline)
80
+ add_line_attributes line
81
+ @buffer << "\n" << line.output_text.strip
82
+ when ([:definition_term, :list_item, :table_row, :table_header,
83
+ :horizontal_rule].include? line.paragraph_type)
84
+ @buffer << "\n" << line.output_text.strip
85
+ when line.paragraph_type == :paragraph
86
+ @buffer << "\n"
87
+ buffer_indentation
88
+ @buffer << line.output_text.strip
89
+ end
90
+
91
+ if mode_is_code? current_mode and not line.block_type
92
+ # Determines the amount of whitespaces to be stripped at the
93
+ # beginning of each line in code block.
94
+ if @code_block_indent
95
+ @code_block_indent = [@code_block_indent, line.indent].min
96
+ else
97
+ @code_block_indent = line.indent
98
+ end
99
+ end
100
+
101
+ @output_type = line.assigned_paragraph_type || line.paragraph_type
112
102
  end
113
103
 
114
104
  # Gets the next headline number for a given level. The intent is
@@ -127,28 +117,6 @@ module Orgmode
127
117
  @headline_number_stack.join(".")
128
118
  end
129
119
 
130
- # Tests if we are entering a table mode.
131
- def enter_table?
132
- ((@output_type == :table_row) || (@output_type == :table_header) || (@output_type == :table_separator)) &&
133
- (current_mode != :table)
134
- end
135
-
136
- # Tests if we are existing a table mode.
137
- def exit_table?
138
- ((@output_type != :table_row) && (@output_type != :table_header) && (@output_type != :table_separator)) &&
139
- (current_mode == :table)
140
- end
141
-
142
- # Accumulate the string @str@.
143
- def << (str)
144
- if @buffer_mode && @buffer_mode != current_mode then
145
- raise "Accumulation buffer is mixing modes: @buffer_mode == #{@buffer_mode}, current_mode == #{current_mode}"
146
- else
147
- @buffer_mode = current_mode
148
- end
149
- @buffer << str
150
- end
151
-
152
120
  # Gets the current list indent level.
153
121
  def list_indent_level
154
122
  @list_indent_stack.length
@@ -156,50 +124,76 @@ module Orgmode
156
124
 
157
125
  # Test if we're in an output mode in which whitespace is significant.
158
126
  def preserve_whitespace?
159
- mode_is_code current_mode
127
+ [:example, :inline_example, :raw_text, :src].include? current_mode
160
128
  end
161
129
 
162
130
  ######################################################################
163
131
  private
164
132
 
165
- def mode_is_code(mode)
166
- case mode
167
- when :src, :inline_example, :example
168
- true
169
- else
170
- false
171
- end
133
+ def mode_is_heading?(mode)
134
+ [:heading1, :heading2, :heading3,
135
+ :heading4, :heading5, :heading6].include? mode
172
136
  end
173
137
 
174
- def maintain_list_indent_stack(line)
175
- if (line.plain_list?) then
176
- while (not @list_indent_stack.empty? \
177
- and (@list_indent_stack.last > line.indent))
178
- @list_indent_stack.pop
179
- pop_mode
180
- end
181
- if (@list_indent_stack.empty? \
182
- or @list_indent_stack.last < line.indent)
183
- @list_indent_stack.push(line.indent)
184
- push_mode line.paragraph_type
185
- end
186
- elsif line.blank? then
138
+ def mode_is_block?(mode)
139
+ [:quote, :center, :example, :src].include? mode
140
+ end
187
141
 
188
- # Nothing
142
+ def mode_is_code?(mode)
143
+ [:example, :src].include? mode
144
+ end
189
145
 
190
- elsif ((not line.plain_list?) and
191
- (not @list_indent_stack.empty?) and
192
- (line.indent > @list_indent_stack.last))
146
+ def boundary_of_block?(line)
147
+ # Boundary of inline example
148
+ return true if ((line.paragraph_type == :inline_example) ^
149
+ (@output_type == :inline_example))
150
+ # Boundary of begin...end block
151
+ return true if mode_is_block? @output_type
152
+ end
193
153
 
194
- # Nothing -- output this paragraph inside
195
- # the list block (ul/ol)
154
+ def maintain_mode_stack(line)
155
+ # Always close the following lines
156
+ pop_mode if (mode_is_heading? current_mode or
157
+ current_mode == :paragraph or
158
+ current_mode == :horizontal_rule or
159
+ current_mode == :inline_example or
160
+ current_mode == :raw_text)
196
161
 
197
- else
198
- @list_indent_stack = []
199
- while ((current_mode == :ordered_list) or
200
- (current_mode == :definition_list) or
201
- (current_mode == :unordered_list))
202
- pop_mode
162
+ # End-block line closes every mode within block
163
+ if line.end_block? and @mode_stack.include? line.paragraph_type
164
+ pop_mode until current_mode == line.paragraph_type
165
+ end
166
+
167
+ if ((not line.paragraph_type == :blank) or
168
+ @output_type == :blank)
169
+ # Close previous tags on demand. Two blank lines close all tags.
170
+ while ((not @list_indent_stack.empty?) and
171
+ @list_indent_stack.last >= line.indent and
172
+ # Don't allow an arbitrary line to close block
173
+ (not mode_is_block? current_mode))
174
+ # Item can't close its major mode
175
+ if (@list_indent_stack.last == line.indent and
176
+ line.major_mode == current_mode)
177
+ break
178
+ else
179
+ pop_mode
180
+ end
181
+ end
182
+ end
183
+
184
+ # Special case: Only end-block line closes block
185
+ pop_mode if line.end_block? and line.paragraph_type == current_mode
186
+
187
+ unless line.paragraph_type == :blank
188
+ if (@list_indent_stack.empty? or
189
+ @list_indent_stack.last <= line.indent or
190
+ mode_is_block? current_mode)
191
+ # Opens the major mode of line if it exists
192
+ if @list_indent_stack.last != line.indent or mode_is_block? current_mode
193
+ push_mode(line.major_mode, line.indent) if line.major_mode
194
+ end
195
+ # Opens tag that precedes text immediately
196
+ push_mode(line.paragraph_type, line.indent) unless line.end_block?
203
197
  end
204
198
  end
205
199
  end
@@ -209,39 +203,43 @@ module Orgmode
209
203
  end
210
204
 
211
205
  # Tests if the current line should be accumulated in the current
212
- # output buffer. (Extraneous line breaks in the orgmode buffer
213
- # are removed by accumulating lines in the output buffer without
214
- # line breaks.)
215
- def should_accumulate_output?(line)
216
-
217
- # Special case: We are accumulating source code block content for colorizing
218
- return true if line.paragraph_type == :src and @output_type == :src
219
-
220
- # Special case: Preserve line breaks in block code mode.
221
- return false if preserve_whitespace?
222
-
223
- # Special case: Multiple blank lines get accumulated.
224
- return true if line.paragraph_type == :blank and @output_type == :blank
225
-
226
- # Currently only "paragraphs" get accumulated with previous output.
227
- return false unless line.paragraph_type == :paragraph
228
- if ((@output_type == :ordered_list) or
229
- (@output_type == :definition_list) or
230
- (@output_type == :unordered_list)) then
231
-
232
- # If the previous output type was a list item, then we only put a paragraph in it
233
- # if its indent level is greater than the list indent level.
234
-
235
- return false unless line.indent > @list_indent_stack.last
206
+ # output buffer.
207
+ def should_accumulate_output? line
208
+ # Special case: Assign mode if not yet done.
209
+ return false unless current_mode
210
+
211
+ # Special case: Handles accumulating block content and example lines
212
+ if mode_is_code? current_mode
213
+ return true unless (line.end_block? and
214
+ line.paragraph_type == current_mode)
215
+ end
216
+ return false if boundary_of_block? line
217
+ return true if current_mode == :inline_example
218
+
219
+ # Special case: Don't accumulate the following lines.
220
+ return false if (mode_is_heading? @output_type or
221
+ @output_type == :comment or
222
+ @output_type == :horizontal_rule or
223
+ @output_type == :raw_text)
224
+
225
+ # Special case: Blank line at least splits paragraphs
226
+ return false if @output_type == :blank
227
+
228
+ if line.paragraph_type == :paragraph
229
+ # Paragraph gets accumulated only if its indent level is
230
+ # greater than the indent level of the previous mode.
231
+ if @mode_stack[-2] and not mode_is_block? @mode_stack[-2]
232
+ return false if line.indent <= @list_indent_stack[-2]
233
+ end
234
+ # Special case: Multiple "paragraphs" get accumulated.
235
+ return true
236
236
  end
237
237
 
238
- # Only accumulate paragraphs with lists & paragraphs.
239
- return false unless
240
- ((@output_type == :paragraph) or
241
- (@output_type == :ordered_list) or
242
- (@output_type == :definition_list) or
243
- (@output_type == :unordered_list))
244
- true
238
+ false
239
+ end
240
+
241
+ def buffer_indentation
242
+ return false
245
243
  end
246
244
  end # class OutputBuffer
247
245
  end # module Orgmode