wallyqs-org-ruby 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (123) hide show
  1. data/.bnsignore +18 -0
  2. data/.gitignore +2 -0
  3. data/Gemfile +7 -0
  4. data/Gemfile.lock +36 -0
  5. data/History.txt +112 -0
  6. data/README.rdoc +67 -0
  7. data/Rakefile +33 -0
  8. data/TAGS +167 -0
  9. data/announcement.txt +24 -0
  10. data/bin/org-ruby +45 -0
  11. data/lib/org-ruby.rb +55 -0
  12. data/lib/org-ruby/headline.rb +110 -0
  13. data/lib/org-ruby/html_output_buffer.rb +273 -0
  14. data/lib/org-ruby/html_symbol_replace.rb +345 -0
  15. data/lib/org-ruby/line.rb +244 -0
  16. data/lib/org-ruby/output_buffer.rb +237 -0
  17. data/lib/org-ruby/parser.rb +366 -0
  18. data/lib/org-ruby/regexp_helper.rb +192 -0
  19. data/lib/org-ruby/textile_output_buffer.rb +102 -0
  20. data/lib/org-ruby/textile_symbol_replace.rb +346 -0
  21. data/lib/org-ruby/tilt.rb +29 -0
  22. data/org-ruby.gemspec +40 -0
  23. data/spec/data/freeform-example.org +113 -0
  24. data/spec/data/freeform.org +111 -0
  25. data/spec/data/hyp-planning.org +335 -0
  26. data/spec/data/remember.org +53 -0
  27. data/spec/headline_spec.rb +65 -0
  28. data/spec/html_examples/advanced-code.html +81 -0
  29. data/spec/html_examples/advanced-code.org +106 -0
  30. data/spec/html_examples/advanced-lists.html +31 -0
  31. data/spec/html_examples/advanced-lists.org +31 -0
  32. data/spec/html_examples/block_code.html +28 -0
  33. data/spec/html_examples/block_code.org +35 -0
  34. data/spec/html_examples/blockcomment.html +3 -0
  35. data/spec/html_examples/blockcomment.org +15 -0
  36. data/spec/html_examples/blockquote.html +7 -0
  37. data/spec/html_examples/blockquote.org +13 -0
  38. data/spec/html_examples/center.html +6 -0
  39. data/spec/html_examples/center.org +7 -0
  40. data/spec/html_examples/code-comment.html +18 -0
  41. data/spec/html_examples/code-comment.org +22 -0
  42. data/spec/html_examples/code-syntax.html +98 -0
  43. data/spec/html_examples/code-syntax.org +99 -0
  44. data/spec/html_examples/comment-trees.html +4 -0
  45. data/spec/html_examples/comment-trees.org +13 -0
  46. data/spec/html_examples/custom-seq-todo.html +15 -0
  47. data/spec/html_examples/custom-seq-todo.org +24 -0
  48. data/spec/html_examples/custom-todo.html +15 -0
  49. data/spec/html_examples/custom-todo.org +24 -0
  50. data/spec/html_examples/custom-typ-todo.html +15 -0
  51. data/spec/html_examples/custom-typ-todo.org +24 -0
  52. data/spec/html_examples/deflist.html +6 -0
  53. data/spec/html_examples/deflist.org +6 -0
  54. data/spec/html_examples/entities.html +4 -0
  55. data/spec/html_examples/entities.org +11 -0
  56. data/spec/html_examples/escape-pre.html +6 -0
  57. data/spec/html_examples/escape-pre.org +6 -0
  58. data/spec/html_examples/export-exclude-only.html +13 -0
  59. data/spec/html_examples/export-exclude-only.org +81 -0
  60. data/spec/html_examples/export-keywords.html +4 -0
  61. data/spec/html_examples/export-keywords.org +18 -0
  62. data/spec/html_examples/export-tags.html +8 -0
  63. data/spec/html_examples/export-tags.org +82 -0
  64. data/spec/html_examples/export-title.html +2 -0
  65. data/spec/html_examples/export-title.org +4 -0
  66. data/spec/html_examples/footnotes.html +10 -0
  67. data/spec/html_examples/footnotes.org +7 -0
  68. data/spec/html_examples/html-literal.html +2 -0
  69. data/spec/html_examples/html-literal.org +6 -0
  70. data/spec/html_examples/inline-formatting.html +20 -0
  71. data/spec/html_examples/inline-formatting.org +33 -0
  72. data/spec/html_examples/inline-images.html +10 -0
  73. data/spec/html_examples/inline-images.org +15 -0
  74. data/spec/html_examples/link-features.html +8 -0
  75. data/spec/html_examples/link-features.org +19 -0
  76. data/spec/html_examples/lists.html +23 -0
  77. data/spec/html_examples/lists.org +47 -0
  78. data/spec/html_examples/metadata-comment.html +27 -0
  79. data/spec/html_examples/metadata-comment.org +30 -0
  80. data/spec/html_examples/only-list.html +5 -0
  81. data/spec/html_examples/only-list.org +3 -0
  82. data/spec/html_examples/only-table.html +6 -0
  83. data/spec/html_examples/only-table.org +5 -0
  84. data/spec/html_examples/skip-header.html +3 -0
  85. data/spec/html_examples/skip-header.org +28 -0
  86. data/spec/html_examples/skip-table.html +4 -0
  87. data/spec/html_examples/skip-table.org +19 -0
  88. data/spec/html_examples/subsupscript-nil.html +3 -0
  89. data/spec/html_examples/subsupscript-nil.org +6 -0
  90. data/spec/html_examples/subsupscript.html +3 -0
  91. data/spec/html_examples/subsupscript.org +5 -0
  92. data/spec/html_examples/tables.html +35 -0
  93. data/spec/html_examples/tables.org +50 -0
  94. data/spec/html_examples/text.html +2 -0
  95. data/spec/html_examples/text.org +16 -0
  96. data/spec/line_spec.rb +155 -0
  97. data/spec/output_buffer_spec.rb +19 -0
  98. data/spec/parser_spec.rb +152 -0
  99. data/spec/regexp_helper_spec.rb +57 -0
  100. data/spec/spec_helper.rb +20 -0
  101. data/spec/textile_examples/block_code.org +35 -0
  102. data/spec/textile_examples/block_code.textile +29 -0
  103. data/spec/textile_examples/blockquote.org +13 -0
  104. data/spec/textile_examples/blockquote.textile +11 -0
  105. data/spec/textile_examples/center.org +7 -0
  106. data/spec/textile_examples/center.textile +6 -0
  107. data/spec/textile_examples/footnotes.org +7 -0
  108. data/spec/textile_examples/footnotes.textile +8 -0
  109. data/spec/textile_examples/keywords.org +13 -0
  110. data/spec/textile_examples/keywords.textile +11 -0
  111. data/spec/textile_examples/links.org +11 -0
  112. data/spec/textile_examples/links.textile +10 -0
  113. data/spec/textile_examples/lists.org +36 -0
  114. data/spec/textile_examples/lists.textile +20 -0
  115. data/spec/textile_examples/single-space-plain-list.org +13 -0
  116. data/spec/textile_examples/single-space-plain-list.textile +10 -0
  117. data/spec/textile_examples/tables.org +50 -0
  118. data/spec/textile_examples/tables.textile +40 -0
  119. data/spec/textile_output_buffer_spec.rb +21 -0
  120. data/tasks/test_case.rake +49 -0
  121. data/test/test_orgmode_parser.rb +0 -0
  122. data/util/gen-special-replace.el +37 -0
  123. metadata +244 -0
@@ -0,0 +1,237 @@
1
+ require 'logger'
2
+
3
+ module Orgmode
4
+
5
+ # The OutputBuffer is used to accumulate multiple lines of orgmode
6
+ # text, and then emit them to the output all in one go. The class
7
+ # will do the final textile substitution for inline formatting and
8
+ # add a newline character prior emitting the output.
9
+ class OutputBuffer
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
+ # This is the overall output buffer
24
+ attr_reader :output
25
+
26
+ # This is the current type of output being accumulated.
27
+ attr_accessor :output_type
28
+
29
+ # This stack is used to do proper outline numbering of headlines.
30
+ attr_accessor :headline_number_stack
31
+
32
+ # Creates a new OutputBuffer object that is bound to an output object.
33
+ # The output will get flushed to =output=.
34
+ def initialize(output)
35
+ @output = output
36
+ @buffer = ""
37
+ @buffered_lines = []
38
+ @buffer_mode = nil
39
+ @output_type = :start
40
+ @list_indent_stack = []
41
+ @paragraph_modifier = nil
42
+ @cancel_modifier = false
43
+ @mode_stack = []
44
+ @headline_number_stack = []
45
+
46
+ @logger = Logger.new(STDERR)
47
+ if ENV['DEBUG'] or $DEBUG
48
+ @logger.level = Logger::DEBUG
49
+ else
50
+ @logger.level = Logger::WARN
51
+ end
52
+
53
+ @re_help = RegexpHelper.new
54
+ push_mode(:normal)
55
+ end
56
+
57
+ Modes = [:normal, :ordered_list, :unordered_list, :definition_list, :blockquote, :src, :example, :table, :inline_example, :center, :property_drawer]
58
+
59
+ def current_mode
60
+ @mode_stack.last
61
+ end
62
+
63
+ def current_mode_list?
64
+ (current_mode == :ordered_list) or (current_mode == :unordered_list)
65
+ end
66
+
67
+ def push_mode(mode)
68
+ raise "Not a recognized mode: #{mode}" unless Modes.include?(mode)
69
+ @mode_stack.push(mode)
70
+ end
71
+
72
+ def pop_mode(mode = nil)
73
+ m = @mode_stack.pop
74
+ @logger.warn "Modes don't match. Expected to pop #{mode}, but popped #{m}" if mode && mode != m
75
+ m
76
+ end
77
+
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)
81
+ @logger.debug "Looking at #{line.paragraph_type}: #{line.to_s}"
82
+ if not should_accumulate_output?(line) then
83
+ @block_lang = line.block_lang if line.begin_block? and line.code_block_type?
84
+ flush!
85
+ maintain_list_indent_stack(line)
86
+ @output_type = line.paragraph_type
87
+ end
88
+ push_mode(:inline_example) if line.inline_example? and current_mode != :inline_example and not line.property_drawer?
89
+ pop_mode(:inline_example) if current_mode == :inline_example and !line.inline_example?
90
+ push_mode(:property_drawer) if line.property_drawer? and current_mode != :property_drawer
91
+ pop_mode(:property_drawer) if current_mode == :property_drawer and line.property_drawer_end_block?
92
+ push_mode(:table) if enter_table?
93
+ pop_mode(:table) if exit_table?
94
+ @buffered_lines.push(line)
95
+ end
96
+
97
+ # Flushes everything currently in the accumulation buffer into the
98
+ # output buffer. Derived classes must override this to actually move
99
+ # content into the output buffer with the appropriate markup. This
100
+ # method just does common bookkeeping cleanup.
101
+ def clear_accumulation_buffer!
102
+ @buffer = ""
103
+ @buffer_mode = nil
104
+ @buffered_lines = []
105
+ end
106
+
107
+ # Gets the next headline number for a given level. The intent is
108
+ # this function is called sequentially for each headline that
109
+ # needs to get numbered. It does standard outline numbering.
110
+ def get_next_headline_number(level)
111
+ raise "Headline level not valid: #{level}" if level <= 0
112
+ while level > @headline_number_stack.length do
113
+ @headline_number_stack.push 0
114
+ end
115
+ while level < @headline_number_stack.length do
116
+ @headline_number_stack.pop
117
+ end
118
+ raise "Oops, shouldn't happen" unless level == @headline_number_stack.length
119
+ @headline_number_stack[@headline_number_stack.length - 1] += 1
120
+ @headline_number_stack.join(".")
121
+ end
122
+
123
+ # Tests if we are entering a table mode.
124
+ def enter_table?
125
+ ((@output_type == :table_row) || (@output_type == :table_header) || (@output_type == :table_separator)) &&
126
+ (current_mode != :table)
127
+ end
128
+
129
+ # Tests if we are existing a table mode.
130
+ def exit_table?
131
+ ((@output_type != :table_row) && (@output_type != :table_header) && (@output_type != :table_separator)) &&
132
+ (current_mode == :table)
133
+ end
134
+
135
+ # Accumulate the string @str@.
136
+ def << (str)
137
+ if @buffer_mode && @buffer_mode != current_mode then
138
+ raise "Accumulation buffer is mixing modes: @buffer_mode == #{@buffer_mode}, current_mode == #{current_mode}"
139
+ else
140
+ @buffer_mode = current_mode
141
+ end
142
+ @buffer << str
143
+ end
144
+
145
+ # Gets the current list indent level.
146
+ def list_indent_level
147
+ @list_indent_stack.length
148
+ end
149
+
150
+ # Test if we're in an output mode in which whitespace is significant.
151
+ def preserve_whitespace?
152
+ mode_is_code current_mode
153
+ end
154
+
155
+ ######################################################################
156
+ private
157
+
158
+ def mode_is_code(mode)
159
+ case mode
160
+ when :src, :inline_example, :example
161
+ true
162
+ else
163
+ false
164
+ end
165
+ end
166
+
167
+ def maintain_list_indent_stack(line)
168
+ if (line.plain_list?) then
169
+ while (not @list_indent_stack.empty? \
170
+ and (@list_indent_stack.last > line.indent))
171
+ @list_indent_stack.pop
172
+ pop_mode
173
+ end
174
+ if (@list_indent_stack.empty? \
175
+ or @list_indent_stack.last < line.indent)
176
+ @list_indent_stack.push(line.indent)
177
+ push_mode line.paragraph_type
178
+ end
179
+ elsif line.blank? then
180
+
181
+ # Nothing
182
+
183
+ elsif ((line.paragraph_type == :paragraph) and
184
+ (not @list_indent_stack.empty? and
185
+ line.indent > @list_indent_stack.last))
186
+
187
+ # Nothing -- output this paragraph inside
188
+ # the list block (ul/ol)
189
+
190
+ else
191
+ @list_indent_stack = []
192
+ while ((current_mode == :ordered_list) or
193
+ (current_mode == :definition_list) or
194
+ (current_mode == :unordered_list))
195
+ pop_mode
196
+ end
197
+ end
198
+ end
199
+
200
+ def output_footnotes!
201
+ return false
202
+ end
203
+
204
+ # Tests if the current line should be accumulated in the current
205
+ # output buffer. (Extraneous line breaks in the orgmode buffer
206
+ # are removed by accumulating lines in the output buffer without
207
+ # line breaks.)
208
+ def should_accumulate_output?(line)
209
+
210
+ # Special case: Preserve line breaks in block code mode.
211
+ return false if preserve_whitespace?
212
+
213
+ # Special case: Multiple blank lines get accumulated.
214
+ return true if line.paragraph_type == :blank and @output_type == :blank
215
+
216
+ # Currently only "paragraphs" get accumulated with previous output.
217
+ return false unless line.paragraph_type == :paragraph
218
+ if ((@output_type == :ordered_list) or
219
+ (@output_type == :definition_list) or
220
+ (@output_type == :unordered_list)) then
221
+
222
+ # If the previous output type was a list item, then we only put a paragraph in it
223
+ # if its indent level is greater than the list indent level.
224
+
225
+ return false unless line.indent > @list_indent_stack.last
226
+ end
227
+
228
+ # Only accumulate paragraphs with lists & paragraphs.
229
+ return false unless
230
+ ((@output_type == :paragraph) or
231
+ (@output_type == :ordered_list) or
232
+ (@output_type == :definition_list) or
233
+ (@output_type == :unordered_list))
234
+ true
235
+ end
236
+ end # class OutputBuffer
237
+ end # module Orgmode
@@ -0,0 +1,366 @@
1
+ require 'rubygems'
2
+ require 'rubypants'
3
+
4
+ module Orgmode
5
+
6
+ ##
7
+ ## Simple routines for loading / saving an ORG file.
8
+ ##
9
+
10
+ class Parser
11
+
12
+ # All of the lines of the orgmode file
13
+ attr_reader :lines
14
+
15
+ # All of the headlines in the org file
16
+ attr_reader :headlines
17
+
18
+ # These are any lines before the first headline
19
+ attr_reader :header_lines
20
+
21
+ # This contains any in-buffer settings from the org-mode file.
22
+ # See http://orgmode.org/manual/In_002dbuffer-settings.html#In_002dbuffer-settings
23
+ attr_reader :in_buffer_settings
24
+
25
+ # This contains in-buffer options; a special case of in-buffer settings.
26
+ attr_reader :options
27
+
28
+ # Array of custom keywords.
29
+ attr_reader :custom_keywords
30
+
31
+ # Regexp that recognizes words in custom_keywords.
32
+ def custom_keyword_regexp
33
+ return nil if @custom_keywords.empty?
34
+ Regexp.new("^(#{@custom_keywords.join('|')})\$")
35
+ end
36
+
37
+ # A set of tags that, if present on any headlines in the org-file, means
38
+ # only those headings will get exported.
39
+ def export_select_tags
40
+ return Array.new unless @in_buffer_settings["EXPORT_SELECT_TAGS"]
41
+ @in_buffer_settings["EXPORT_SELECT_TAGS"].split
42
+ end
43
+
44
+ # A set of tags that, if present on any headlines in the org-file, means
45
+ # that subtree will not get exported.
46
+ def export_exclude_tags
47
+ return Array.new unless @in_buffer_settings["EXPORT_EXCLUDE_TAGS"]
48
+ @in_buffer_settings["EXPORT_EXCLUDE_TAGS"].split
49
+ end
50
+
51
+ # Returns true if we are to export todo keywords on headings.
52
+ def export_todo?
53
+ "t" == @options["todo"]
54
+ end
55
+
56
+ # Returns true if we are to export footnotes
57
+ def export_footnotes?
58
+ "t" == @options["f"]
59
+ end
60
+
61
+ # Returns true if we are to export heading numbers.
62
+ def export_heading_number?
63
+ "t" == @options["num"]
64
+ end
65
+
66
+ # Should we skip exporting text before the first heading?
67
+ def skip_header_lines?
68
+ "t" == @options["skip"]
69
+ end
70
+
71
+ # Should we export tables? Defaults to true, must be overridden
72
+ # with an explicit "nil"
73
+ def export_tables?
74
+ "nil" != @options["|"]
75
+ end
76
+
77
+ # Should we export sub/superscripts? (_{foo}/^{foo})
78
+ # only {} mode is currently supported.
79
+ def use_sub_superscripts?
80
+ @options["^"] != "nil"
81
+ end
82
+
83
+ # I can construct a parser object either with an array of lines
84
+ # or with a single string that I will split along \n boundaries.
85
+ def initialize(lines, offset=0)
86
+ if lines.is_a? Array then
87
+ @lines = lines
88
+ elsif lines.is_a? String then
89
+ @lines = lines.split("\n")
90
+ else
91
+ raise "Unsupported type for +lines+: #{lines.class}"
92
+ end
93
+
94
+ @custom_keywords = []
95
+ @headlines = Array.new
96
+ @current_headline = nil
97
+ @header_lines = []
98
+ @in_buffer_settings = { }
99
+ @options = { }
100
+ mode = :normal
101
+ previous_line = nil
102
+ table_header_set = false
103
+ @lines.each do |line|
104
+ case mode
105
+ when :normal
106
+
107
+ if (Headline.headline? line) then
108
+ @current_headline = Headline.new line, self, offset
109
+ @headlines << @current_headline
110
+ else
111
+ line = Line.new line, self
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
121
+ end
122
+ table_header_set = false if !line.table?
123
+ mode = :code if line.begin_block? and line.block_type == "EXAMPLE"
124
+ mode = :block_comment if line.begin_block? and line.block_type == "COMMENT"
125
+ mode = :property_drawer if line.property_drawer_begin_block?
126
+ if (@current_headline) then
127
+ @current_headline.body_lines << line
128
+ else
129
+ @header_lines << line
130
+ end
131
+ end
132
+
133
+ when :block_comment
134
+ line = Line.new line, self
135
+ if line.end_block? and line.block_type == "COMMENT"
136
+ mode = :normal
137
+ else
138
+ line.assigned_paragraph_type = :comment
139
+ end
140
+
141
+ when :code
142
+
143
+ # As long as we stay in code mode, force lines to be either blank or paragraphs.
144
+ # Don't try to interpret structural items, like headings and tables.
145
+ line = Line.new line, self
146
+ if line.end_block? and line.block_type == "EXAMPLE"
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 :property_drawer
158
+
159
+ line = Line.new line, self
160
+ if line.property_drawer_end_block?
161
+ mode = :normal
162
+ else
163
+ line.assigned_paragraph_type = :property_drawer unless line.blank?
164
+ end
165
+ if (@current_headline) then
166
+ @current_headline.body_lines << line
167
+ else
168
+ @header_lines << line
169
+ end
170
+ end # case
171
+ previous_line = line
172
+ end # @lines.each
173
+ end # initialize
174
+
175
+ # Creates a new parser from the data in a given file
176
+ def self.load(fname)
177
+ lines = IO.readlines(fname)
178
+ return self.new(lines)
179
+ end
180
+
181
+ # Saves the loaded orgmode file as a textile file.
182
+ def to_textile
183
+ output = ""
184
+ output << Line.to_textile(@header_lines)
185
+ @headlines.each do |headline|
186
+ output << headline.to_textile
187
+ end
188
+ output
189
+ end
190
+
191
+ # Converts the loaded org-mode file to HTML.
192
+ def to_html
193
+ mark_trees_for_export
194
+ export_options = {
195
+ :decorate_title => true,
196
+ :export_heading_number => export_heading_number?,
197
+ :export_todo => export_todo?,
198
+ :use_sub_superscripts => use_sub_superscripts?,
199
+ :export_footnotes => export_footnotes?
200
+ }
201
+ export_options[:skip_tables] = true if not export_tables?
202
+ output = ""
203
+ output_buffer = HtmlOutputBuffer.new(output, export_options)
204
+
205
+ if @in_buffer_settings["TITLE"] then
206
+
207
+ # If we're given a new title, then just create a new line
208
+ # for that title.
209
+ title = Line.new(@in_buffer_settings["TITLE"], self)
210
+ Parser.translate([title], output_buffer)
211
+ end
212
+ Parser.translate(@header_lines, output_buffer) unless skip_header_lines?
213
+
214
+ # If we've output anything at all, remove the :decorate_title option.
215
+ export_options.delete(:decorate_title) if (output.length > 0)
216
+ @headlines.each do |headline|
217
+ next if headline.export_state == :exclude
218
+ case headline.export_state
219
+ when :exclude
220
+ # NOTHING
221
+ when :headline_only
222
+ Parser.translate(headline.body_lines[0, 1], output_buffer)
223
+ when :all
224
+ Parser.translate(headline.body_lines, output_buffer)
225
+ end
226
+ end
227
+ rp = RubyPants.new(output)
228
+ rp.to_html
229
+ end
230
+
231
+ ######################################################################
232
+ private
233
+
234
+ # Converts an array of lines to the appropriate format.
235
+ # Writes the output to +output_buffer+.
236
+ def self.translate(lines, output_buffer)
237
+ lines.each do |line|
238
+
239
+ # See if we're carrying paragraph payload, and output
240
+ # it if we're about to switch to some other output type.
241
+ output_buffer.prepare(line)
242
+ case line.paragraph_type
243
+ when :metadata, :table_separator, :blank, :comment, :property_drawer_item, :property_drawer_begin_block, :property_drawer_end_block
244
+
245
+ output_buffer << line.line if output_buffer.preserve_whitespace?
246
+
247
+ when :begin_block
248
+
249
+ output_buffer.push_mode(:blockquote) if line.block_type.casecmp("QUOTE") == 0
250
+ output_buffer.push_mode(:src) if line.block_type.casecmp("SRC") == 0
251
+ output_buffer.push_mode(:example) if line.block_type.casecmp("EXAMPLE") == 0
252
+ output_buffer.push_mode(:center) if line.block_type.casecmp("CENTER") == 0
253
+
254
+ when :end_block
255
+
256
+ output_buffer.pop_mode(:blockquote) if line.block_type.casecmp("QUOTE") == 0
257
+ output_buffer.pop_mode(:src) if line.block_type.casecmp("SRC") == 0
258
+ output_buffer.pop_mode(:example) if line.block_type.casecmp("EXAMPLE") == 0
259
+ output_buffer.pop_mode(:center) if line.block_type.casecmp("CENTER") == 0
260
+
261
+ when :table_row, :table_header
262
+
263
+ output_buffer << line.line.lstrip
264
+
265
+ when :unordered_list, :ordered_list, :definition_list
266
+
267
+ output_buffer << line.output_text << " "
268
+
269
+ when :inline_example
270
+
271
+ output_buffer << line.output_text
272
+
273
+ else
274
+
275
+ if output_buffer.preserve_whitespace? then
276
+ output_buffer << line.output_text
277
+ else
278
+ output_buffer << line.output_text.strip << " "
279
+ end
280
+ end
281
+ end
282
+ output_buffer.flush!
283
+ output_buffer.pop_mode until output_buffer.current_mode == :normal
284
+ output_buffer.output_footnotes!
285
+ output_buffer.output
286
+ end
287
+
288
+ # Uses export_select_tags and export_exclude_tags to determine
289
+ # which parts of the org-file to export.
290
+ def mark_trees_for_export
291
+ marked_any = false
292
+ # cache the tags
293
+ select = export_select_tags
294
+ exclude = export_exclude_tags
295
+ inherit_export_level = nil
296
+ ancestor_stack = []
297
+
298
+ # First pass: See if any headlines are explicitly selected
299
+ @headlines.each do |headline|
300
+ ancestor_stack.pop while not ancestor_stack.empty? and headline.level <= ancestor_stack.last.level
301
+ if inherit_export_level and headline.level > inherit_export_level
302
+ headline.export_state = :all
303
+ else
304
+ inherit_export_level = nil
305
+ headline.tags.each do |tag|
306
+ if (select.include? tag) then
307
+ marked_any = true
308
+ headline.export_state = :all
309
+ ancestor_stack.each { |a| a.export_state = :headline_only unless a.export_state == :all }
310
+ inherit_export_level = headline.level
311
+ end
312
+ end
313
+ end
314
+ ancestor_stack.push headline
315
+ end
316
+
317
+ # If nothing was selected, then EVERYTHING is selected.
318
+ @headlines.each { |h| h.export_state = :all } unless marked_any
319
+
320
+ # Second pass. Look for things that should be excluded, and get rid of them.
321
+ @headlines.each do |headline|
322
+ if inherit_export_level and headline.level > inherit_export_level
323
+ headline.export_state = :exclude
324
+ else
325
+ inherit_export_level = nil
326
+ headline.tags.each do |tag|
327
+ if (exclude.include? tag) then
328
+ headline.export_state = :exclude
329
+ inherit_export_level = headline.level
330
+ end
331
+ end
332
+ if headline.comment_headline?
333
+ headline.export_state = :exclude
334
+ inherit_export_level = headline.level
335
+ end
336
+ end
337
+ end
338
+ end
339
+
340
+ # Stores an in-buffer setting.
341
+ def store_in_buffer_setting(key, value)
342
+ if key == "OPTIONS" then
343
+
344
+ # Options are stored in a hash. Special-case.
345
+
346
+ value.split.each do |opt|
347
+ if opt =~ /^(.*):(.*?)$/ then
348
+ @options[$1] = $2
349
+ else
350
+ raise "Unexpected option: #{opt}"
351
+ end
352
+ end
353
+ elsif key =~ /^(TODO|SEQ_TODO|TYP_TODO)$/ then
354
+ # Handle todo keywords specially.
355
+ value.split.each do |keyword|
356
+ keyword.gsub!(/\(.*\)/, "") # Get rid of any parenthetical notes
357
+ keyword = Regexp.escape(keyword)
358
+ next if keyword == "\\|" # Special character in the todo format, not really a keyword
359
+ @custom_keywords << keyword
360
+ end
361
+ else
362
+ @in_buffer_settings[key] = value
363
+ end
364
+ end
365
+ end # class Parser
366
+ end # module Orgmode