wallyqs-org-ruby 0.6.1

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.
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