org-ruby 0.4.2 → 0.5.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.
- data/History.txt +18 -0
- data/lib/org-ruby.rb +1 -1
- data/lib/org-ruby/headline.rb +49 -9
- data/lib/org-ruby/html_output_buffer.rb +37 -7
- data/lib/org-ruby/line.rb +34 -3
- data/lib/org-ruby/output_buffer.rb +2 -2
- data/lib/org-ruby/parser.rb +180 -10
- data/spec/data/freeform-example.org +113 -0
- data/spec/html_examples/custom-seq-todo.html +15 -0
- data/spec/html_examples/custom-seq-todo.org +24 -0
- data/spec/html_examples/custom-todo.html +15 -0
- data/spec/html_examples/custom-todo.org +24 -0
- data/spec/html_examples/custom-typ-todo.html +15 -0
- data/spec/html_examples/custom-typ-todo.org +24 -0
- data/spec/html_examples/entities.html +1 -0
- data/spec/html_examples/entities.org +3 -0
- data/spec/html_examples/export-exclude-only.html +13 -0
- data/spec/html_examples/export-exclude-only.org +81 -0
- data/spec/html_examples/export-keywords.html +4 -0
- data/spec/html_examples/export-keywords.org +18 -0
- data/spec/html_examples/export-tags.html +8 -0
- data/spec/html_examples/export-tags.org +82 -0
- data/spec/html_examples/export-title.html +2 -0
- data/spec/html_examples/export-title.org +4 -0
- data/spec/html_examples/link-features.html +8 -0
- data/spec/html_examples/link-features.org +19 -0
- data/spec/html_examples/only-table.html +1 -1
- data/spec/html_examples/skip-header.html +3 -0
- data/spec/html_examples/skip-header.org +28 -0
- data/spec/html_examples/skip-table.html +4 -0
- data/spec/html_examples/skip-table.org +19 -0
- data/spec/html_examples/tables.html +1 -1
- data/spec/line_spec.rb +35 -0
- data/spec/parser_spec.rb +94 -14
- data/spec/spec_helper.rb +1 -0
- metadata +23 -2
data/History.txt
CHANGED
@@ -1,3 +1,21 @@
|
|
1
|
+
== 0.5.0 / 2009-12-29
|
2
|
+
|
3
|
+
* Parse (but not necessarily *use*) in-buffer settings. The following
|
4
|
+
in-buffer settings *are* used:
|
5
|
+
* Understand the #+TITLE: directive.
|
6
|
+
* Exporting todo keywords (option todo:t)
|
7
|
+
* Numbering headlines (option num:t)
|
8
|
+
* Skipping text before the first headline (option skip:t)
|
9
|
+
* Skipping tables (option |:nil)
|
10
|
+
* Custom todo keywords
|
11
|
+
* EXPORT_SELECT_TAGS and EXPORT_EXLUDE_TAGS for controlling parts of
|
12
|
+
the tree to export
|
13
|
+
* Rewrite "file:(blah).org" links to "http:(blah).html" links. This
|
14
|
+
makes the inter-links to other org-mode files work.
|
15
|
+
* Uses <th> tags inside table rows that precede table separators.
|
16
|
+
* Bugfixes:
|
17
|
+
* Headings now have HTML escaped.
|
18
|
+
|
1
19
|
== 0.4.2 / 2009-12-29
|
2
20
|
|
3
21
|
* Got rid of the extraneous newline at the start of code blocks.
|
data/lib/org-ruby.rb
CHANGED
data/lib/org-ruby/headline.rb
CHANGED
@@ -21,6 +21,15 @@ module Orgmode
|
|
21
21
|
# Optional keyword found at the beginning of the headline.
|
22
22
|
attr_reader :keyword
|
23
23
|
|
24
|
+
# Valid states for partial export.
|
25
|
+
# exclude:: The entire subtree from this heading should be excluded.
|
26
|
+
# headline_only:: The headline should be exported, but not the body.
|
27
|
+
# all:: Everything should be exported, headline/body/children.
|
28
|
+
ValidExportStates = [:exclude, :headline_only, :all]
|
29
|
+
|
30
|
+
# The export state of this headline. See +ValidExportStates+.
|
31
|
+
attr_accessor :export_state
|
32
|
+
|
24
33
|
# This is the regex that matches a line
|
25
34
|
LineRegexp = /^\*+\s+/
|
26
35
|
|
@@ -30,12 +39,13 @@ module Orgmode
|
|
30
39
|
# Special keywords allowed at the start of a line.
|
31
40
|
Keywords = %w[TODO DONE]
|
32
41
|
|
33
|
-
KeywordsRegexp = Regexp.new("
|
42
|
+
KeywordsRegexp = Regexp.new("^(#{Keywords.join('|')})\$")
|
34
43
|
|
35
|
-
def initialize(line)
|
36
|
-
super(line)
|
44
|
+
def initialize(line, parser = nil)
|
45
|
+
super(line, parser)
|
37
46
|
@body_lines = []
|
38
47
|
@tags = []
|
48
|
+
@export_state = :exclude
|
39
49
|
if (@line =~ LineRegexp) then
|
40
50
|
@level = $&.strip.length
|
41
51
|
@headline_text = $'.strip
|
@@ -44,10 +54,8 @@ module Orgmode
|
|
44
54
|
@tags.delete_at(0) # the first item will be empty; discard
|
45
55
|
@headline_text.gsub!(TagsRegexp, "") # Removes the tags from the headline
|
46
56
|
end
|
47
|
-
|
48
|
-
|
49
|
-
@keyword = $1
|
50
|
-
end
|
57
|
+
@keyword = nil
|
58
|
+
parse_keywords
|
51
59
|
else
|
52
60
|
raise "'#{line}' is not a valid headline"
|
53
61
|
end
|
@@ -67,14 +75,46 @@ module Orgmode
|
|
67
75
|
end
|
68
76
|
|
69
77
|
def to_html(opts = {})
|
78
|
+
return "" if @export_state == :exclude
|
70
79
|
if opts[:decorate_title]
|
71
80
|
decoration = " class=\"title\""
|
81
|
+
opts.delete(:decorate_title)
|
72
82
|
else
|
73
83
|
decoration = ""
|
74
84
|
end
|
75
|
-
output = "<h#{@level}#{decoration}
|
76
|
-
|
85
|
+
output = "<h#{@level}#{decoration}>"
|
86
|
+
if @parser and @parser.export_heading_number? then
|
87
|
+
heading_number = @parser.get_next_headline_number(@level)
|
88
|
+
output << "<span class=\"heading-number heading-number-#{@level}\">#{heading_number} </span>"
|
89
|
+
end
|
90
|
+
if @parser and @parser.export_todo? and @keyword then
|
91
|
+
output << "<span class=\"todo-keyword #{@keyword}\">#{@keyword} </span>"
|
92
|
+
end
|
93
|
+
output << "#{escape(@headline_text)}</h#{@level}>\n"
|
94
|
+
return output if @export_state == :headline_only
|
95
|
+
output << Line.to_html(@body_lines, opts)
|
77
96
|
output
|
78
97
|
end
|
98
|
+
|
99
|
+
######################################################################
|
100
|
+
private
|
101
|
+
|
102
|
+
# TODO 2009-12-29 This duplicates escape_buffer! in html_output_buffer. DRY.
|
103
|
+
def escape(str)
|
104
|
+
str = str.gsub(/&/, "&")
|
105
|
+
str = str.gsub(/</, "<")
|
106
|
+
str = str.gsub(/>/, ">")
|
107
|
+
str
|
108
|
+
end
|
109
|
+
|
110
|
+
def parse_keywords
|
111
|
+
re = @parser.custom_keyword_regexp if @parser
|
112
|
+
re ||= KeywordsRegexp
|
113
|
+
words = @headline_text.split
|
114
|
+
if words.length > 0 && words[0] =~ re then
|
115
|
+
@keyword = words[0]
|
116
|
+
@headline_text.sub!(Regexp.new("^#{@keyword}\s*"), "")
|
117
|
+
end
|
118
|
+
end
|
79
119
|
end # class Headline
|
80
120
|
end # class Orgmode
|
@@ -8,7 +8,8 @@ module Orgmode
|
|
8
8
|
:paragraph => "p",
|
9
9
|
:ordered_list => "li",
|
10
10
|
:unordered_list => "li",
|
11
|
-
:table_row => "tr"
|
11
|
+
:table_row => "tr",
|
12
|
+
:table_header => "tr"
|
12
13
|
}
|
13
14
|
|
14
15
|
ModeTag = {
|
@@ -19,6 +20,8 @@ module Orgmode
|
|
19
20
|
:code => "pre"
|
20
21
|
}
|
21
22
|
|
23
|
+
attr_reader :options
|
24
|
+
|
22
25
|
def initialize(output, opts = {})
|
23
26
|
super(output)
|
24
27
|
if opts[:decorate_title] then
|
@@ -26,13 +29,15 @@ module Orgmode
|
|
26
29
|
else
|
27
30
|
@title_decoration = ""
|
28
31
|
end
|
32
|
+
@options = opts
|
33
|
+
@logger.debug "HTML export options: #{@options.inspect}"
|
29
34
|
end
|
30
35
|
|
31
36
|
def push_mode(mode)
|
32
37
|
if ModeTag[mode] then
|
33
38
|
output_indentation
|
34
39
|
@logger.debug "<#{ModeTag[mode]}>\n"
|
35
|
-
@output << "<#{ModeTag[mode]}>\n"
|
40
|
+
@output << "<#{ModeTag[mode]}>\n" unless mode == :table and skip_tables?
|
36
41
|
# Entering a new mode obliterates the title decoration
|
37
42
|
@title_decoration = ""
|
38
43
|
end
|
@@ -44,7 +49,7 @@ module Orgmode
|
|
44
49
|
if ModeTag[m] then
|
45
50
|
output_indentation
|
46
51
|
@logger.debug "</#{ModeTag[m]}>\n"
|
47
|
-
@output << "</#{ModeTag[m]}>\n"
|
52
|
+
@output << "</#{ModeTag[m]}>\n" unless mode == :table and skip_tables?
|
48
53
|
end
|
49
54
|
end
|
50
55
|
|
@@ -60,12 +65,16 @@ module Orgmode
|
|
60
65
|
@output << @buffer << "\n"
|
61
66
|
else
|
62
67
|
if (@buffer.length > 0) then
|
63
|
-
|
64
|
-
|
65
|
-
|
68
|
+
unless buffer_mode_is_table? and skip_tables?
|
69
|
+
@logger.debug "FLUSH ==========> #{@buffer_mode}"
|
70
|
+
output_indentation
|
71
|
+
@output << "<#{HtmlBlockTag[@output_type]}#{@title_decoration}>" \
|
66
72
|
<< inline_formatting(@buffer) \
|
67
73
|
<< "</#{HtmlBlockTag[@output_type]}>\n"
|
68
|
-
|
74
|
+
@title_decoration = ""
|
75
|
+
else
|
76
|
+
@logger.debug "SKIP ==========> #{@buffer_mode}"
|
77
|
+
end
|
69
78
|
end
|
70
79
|
end
|
71
80
|
@buffer = ""
|
@@ -75,6 +84,14 @@ module Orgmode
|
|
75
84
|
######################################################################
|
76
85
|
private
|
77
86
|
|
87
|
+
def skip_tables?
|
88
|
+
@options[:skip_tables]
|
89
|
+
end
|
90
|
+
|
91
|
+
def buffer_mode_is_table?
|
92
|
+
@buffer_mode == :table
|
93
|
+
end
|
94
|
+
|
78
95
|
# Escapes any HTML content in the output accumulation buffer @buffer.
|
79
96
|
def escape_buffer!
|
80
97
|
@buffer.gsub!(/&/, "&")
|
@@ -105,6 +122,14 @@ module Orgmode
|
|
105
122
|
end
|
106
123
|
str = @re_help.rewrite_links(str) do |link, text|
|
107
124
|
text ||= link
|
125
|
+
link = link.sub(/^file:(.*)::(.*?)$/) do
|
126
|
+
|
127
|
+
# We don't support search links right now. Get rid of it.
|
128
|
+
|
129
|
+
"file:#{$1}"
|
130
|
+
end
|
131
|
+
link = link.sub(/^file:/i, "") # will default to HTTP
|
132
|
+
link = link.sub(/\.org$/i, ".html")
|
108
133
|
"<a href=\"#{link}\">#{text}</a>"
|
109
134
|
end
|
110
135
|
if (@output_type == :table_row) then
|
@@ -112,6 +137,11 @@ module Orgmode
|
|
112
137
|
str.gsub!(/\s*\|$/, "</td>")
|
113
138
|
str.gsub!(/\s*\|\s*/, "</td><td>")
|
114
139
|
end
|
140
|
+
if (@output_type == :table_header) then
|
141
|
+
str.gsub!(/^\|\s*/, "<th>")
|
142
|
+
str.gsub!(/\s*\|$/, "</th>")
|
143
|
+
str.gsub!(/\s*\|\s*/, "</th><th>")
|
144
|
+
end
|
115
145
|
str
|
116
146
|
end
|
117
147
|
|
data/lib/org-ruby/line.rb
CHANGED
@@ -11,6 +11,9 @@ module Orgmode
|
|
11
11
|
# TODO 2009-12-20 bdewey: Handle tabs
|
12
12
|
attr_reader :indent
|
13
13
|
|
14
|
+
# Backpointer to the parser that owns this line.
|
15
|
+
attr_reader :parser
|
16
|
+
|
14
17
|
# A line can have its type assigned instead of inferred from its
|
15
18
|
# content. For example, something that parses as a "table" on its
|
16
19
|
# own ("| one | two|\n") may just be a paragraph if it's inside
|
@@ -18,7 +21,8 @@ module Orgmode
|
|
18
21
|
# type. This will then affect the value of +paragraph_type+.
|
19
22
|
attr_accessor :assigned_paragraph_type
|
20
23
|
|
21
|
-
def initialize(line)
|
24
|
+
def initialize(line, parser = nil)
|
25
|
+
@parser = parser
|
22
26
|
@line = line
|
23
27
|
@indent = 0
|
24
28
|
@line =~ /\s*/
|
@@ -90,8 +94,13 @@ module Orgmode
|
|
90
94
|
check_assignment_or_regexp(:table_separator, /^\s*\|[-\|\+]*\s*$/)
|
91
95
|
end
|
92
96
|
|
97
|
+
# Checks if this line is a table header.
|
98
|
+
def table_header?
|
99
|
+
@assigned_paragraph_type == :table_header
|
100
|
+
end
|
101
|
+
|
93
102
|
def table?
|
94
|
-
table_row? or table_separator?
|
103
|
+
table_row? or table_separator? or table_header?
|
95
104
|
end
|
96
105
|
|
97
106
|
BlockRegexp = /^\s*#\+(BEGIN|END)_(\w*)/
|
@@ -108,6 +117,27 @@ module Orgmode
|
|
108
117
|
$2 if @line =~ BlockRegexp
|
109
118
|
end
|
110
119
|
|
120
|
+
InBufferSettingRegexp = /^#\+(\w+):\s*(.*)$/
|
121
|
+
|
122
|
+
# call-seq:
|
123
|
+
# line.in_buffer_setting? => boolean
|
124
|
+
# line.in_buffer_setting? { |key, value| ... }
|
125
|
+
#
|
126
|
+
# Called without a block, this method determines if the line
|
127
|
+
# contains an in-buffer setting. Called with a block, the block
|
128
|
+
# will get called if the line contains an in-buffer setting with
|
129
|
+
# the key and value for the setting.
|
130
|
+
def in_buffer_setting?
|
131
|
+
return false if @assigned_paragraph_type && @assigned_paragraph_type != :comment
|
132
|
+
if block_given? then
|
133
|
+
if @line =~ InBufferSettingRegexp
|
134
|
+
yield $1, $2
|
135
|
+
end
|
136
|
+
else
|
137
|
+
@line =~ InBufferSettingRegexp
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
111
141
|
# Determines the paragraph type of the current line.
|
112
142
|
def paragraph_type
|
113
143
|
return :blank if blank?
|
@@ -117,6 +147,7 @@ module Orgmode
|
|
117
147
|
return :comment if comment?
|
118
148
|
return :table_separator if table_separator?
|
119
149
|
return :table_row if table_row?
|
150
|
+
return :table_header if table_header?
|
120
151
|
return :paragraph
|
121
152
|
end
|
122
153
|
|
@@ -157,7 +188,7 @@ module Orgmode
|
|
157
188
|
output_buffer << line.line if output_buffer.preserve_whitespace?
|
158
189
|
end
|
159
190
|
|
160
|
-
when :table_row
|
191
|
+
when :table_row, :table_header
|
161
192
|
|
162
193
|
output_buffer << line.line.lstrip
|
163
194
|
|
@@ -81,13 +81,13 @@ module Orgmode
|
|
81
81
|
|
82
82
|
# Tests if we are entering a table mode.
|
83
83
|
def enter_table?
|
84
|
-
((@output_type == :table_row) || (@output_type == :table_separator)) &&
|
84
|
+
((@output_type == :table_row) || (@output_type == :table_header) || (@output_type == :table_separator)) &&
|
85
85
|
(current_mode != :table)
|
86
86
|
end
|
87
87
|
|
88
88
|
# Tests if we are existing a table mode.
|
89
89
|
def exit_table?
|
90
|
-
((@output_type != :table_row) && (@output_type != :table_separator)) &&
|
90
|
+
((@output_type != :table_row) && (@output_type != :table_header) && (@output_type != :table_separator)) &&
|
91
91
|
(current_mode == :table)
|
92
92
|
end
|
93
93
|
|
data/lib/org-ruby/parser.rb
CHANGED
@@ -17,7 +17,77 @@ module Orgmode
|
|
17
17
|
|
18
18
|
# These are any lines before the first headline
|
19
19
|
attr_reader :header_lines
|
20
|
-
|
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
|
+
# This stack is used to do proper outline numbering of headlines.
|
57
|
+
attr_accessor :headline_number_stack
|
58
|
+
|
59
|
+
# Returns true if we are to export heading numbers.
|
60
|
+
def export_heading_number?
|
61
|
+
"t" == @options["num"]
|
62
|
+
end
|
63
|
+
|
64
|
+
# Should we skip exporting text before the first heading?
|
65
|
+
def skip_header_lines?
|
66
|
+
"t" == @options["skip"]
|
67
|
+
end
|
68
|
+
|
69
|
+
# Should we export tables? Defaults to true, must be overridden
|
70
|
+
# with an explicit "nil"
|
71
|
+
def export_tables?
|
72
|
+
"nil" != @options["|"]
|
73
|
+
end
|
74
|
+
|
75
|
+
# Gets the next headline number for a given level. The intent is
|
76
|
+
# this function is called sequentially for each headline that
|
77
|
+
# needs to get numbered. It does standard outline numbering.
|
78
|
+
def get_next_headline_number(level)
|
79
|
+
raise "Headline level not valid: #{level}" if level <= 0
|
80
|
+
while level > @headline_number_stack.length do
|
81
|
+
@headline_number_stack.push 0
|
82
|
+
end
|
83
|
+
while level < @headline_number_stack.length do
|
84
|
+
@headline_number_stack.pop
|
85
|
+
end
|
86
|
+
raise "Oops, shouldn't happen" unless level == @headline_number_stack.length
|
87
|
+
@headline_number_stack[@headline_number_stack.length - 1] += 1
|
88
|
+
@headline_number_stack.join(".")
|
89
|
+
end
|
90
|
+
|
21
91
|
# I can construct a parser object either with an array of lines
|
22
92
|
# or with a single string that I will split along \n boundaries.
|
23
93
|
def initialize(lines)
|
@@ -28,20 +98,32 @@ module Orgmode
|
|
28
98
|
else
|
29
99
|
raise "Unsupported type for +lines+: #{lines.class}"
|
30
100
|
end
|
31
|
-
|
101
|
+
|
102
|
+
@custom_keywords = []
|
32
103
|
@headlines = Array.new
|
33
104
|
@current_headline = nil
|
34
105
|
@header_lines = []
|
106
|
+
@in_buffer_settings = { }
|
107
|
+
@headline_number_stack = []
|
108
|
+
@options = { }
|
35
109
|
mode = :normal
|
110
|
+
previous_line = nil
|
36
111
|
@lines.each do |line|
|
37
112
|
case mode
|
38
113
|
when :normal
|
39
114
|
|
40
115
|
if (Headline.headline? line) then
|
41
|
-
@current_headline = Headline.new line
|
116
|
+
@current_headline = Headline.new line, self
|
42
117
|
@headlines << @current_headline
|
43
118
|
else
|
44
|
-
line = Line.new line
|
119
|
+
line = Line.new line, self
|
120
|
+
# If there is a setting on this line, remember it.
|
121
|
+
line.in_buffer_setting? do |key, value|
|
122
|
+
store_in_buffer_setting key, value
|
123
|
+
end
|
124
|
+
if line.table_separator? then
|
125
|
+
previous_line.assigned_paragraph_type = :table_header if previous_line and previous_line.paragraph_type == :table_row
|
126
|
+
end
|
45
127
|
mode = :code if line.begin_block? and line.block_type == "EXAMPLE"
|
46
128
|
if (@current_headline) then
|
47
129
|
@current_headline.body_lines << line
|
@@ -66,7 +148,8 @@ module Orgmode
|
|
66
148
|
@header_lines << line
|
67
149
|
end
|
68
150
|
end # case
|
69
|
-
|
151
|
+
previous_line = line
|
152
|
+
end # @lines.each
|
70
153
|
end # initialize
|
71
154
|
|
72
155
|
# Creates a new parser from the data in a given file
|
@@ -87,16 +170,103 @@ module Orgmode
|
|
87
170
|
|
88
171
|
# Converts the loaded org-mode file to HTML.
|
89
172
|
def to_html
|
173
|
+
mark_trees_for_export
|
174
|
+
@headline_number_stack = []
|
175
|
+
export_options = { }
|
176
|
+
export_options[:skip_tables] = true if not export_tables?
|
90
177
|
output = ""
|
91
|
-
|
92
|
-
|
93
|
-
|
178
|
+
if @in_buffer_settings["TITLE"] then
|
179
|
+
output << "<p class=\"title\">#{@in_buffer_settings["TITLE"]}</p>\n"
|
180
|
+
else
|
181
|
+
export_options[:decorate_title] = true
|
182
|
+
end
|
183
|
+
output << Line.to_html(@header_lines, export_options) unless skip_header_lines?
|
184
|
+
|
185
|
+
# If we've output anything at all, remove the :decorate_title option.
|
186
|
+
export_options.delete(:decorate_title) if (output.length > 0)
|
94
187
|
@headlines.each do |headline|
|
95
|
-
output << headline.to_html(
|
96
|
-
|
188
|
+
output << headline.to_html(export_options)
|
189
|
+
export_options.delete(:decorate_title) if (output.length > 0)
|
97
190
|
end
|
98
191
|
rp = RubyPants.new(output)
|
99
192
|
rp.to_html
|
100
193
|
end
|
194
|
+
|
195
|
+
######################################################################
|
196
|
+
private
|
197
|
+
|
198
|
+
# Uses export_select_tags and export_exclude_tags to determine
|
199
|
+
# which parts of the org-file to export.
|
200
|
+
def mark_trees_for_export
|
201
|
+
marked_any = false
|
202
|
+
# cache the tags
|
203
|
+
select = export_select_tags
|
204
|
+
exclude = export_exclude_tags
|
205
|
+
inherit_export_level = nil
|
206
|
+
ancestor_stack = []
|
207
|
+
|
208
|
+
# First pass: See if any headlines are explicitly selected
|
209
|
+
@headlines.each do |headline|
|
210
|
+
ancestor_stack.pop while not ancestor_stack.empty? and headline.level <= ancestor_stack.last.level
|
211
|
+
if inherit_export_level and headline.level > inherit_export_level
|
212
|
+
headline.export_state = :all
|
213
|
+
else
|
214
|
+
inherit_export_level = nil
|
215
|
+
headline.tags.each do |tag|
|
216
|
+
if (select.include? tag) then
|
217
|
+
marked_any = true
|
218
|
+
headline.export_state = :all
|
219
|
+
ancestor_stack.each { |a| a.export_state = :headline_only unless a.export_state == :all }
|
220
|
+
inherit_export_level = headline.level
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
ancestor_stack.push headline
|
225
|
+
end
|
226
|
+
|
227
|
+
# If nothing was selected, then EVERYTHING is selected.
|
228
|
+
@headlines.each { |h| h.export_state = :all } unless marked_any
|
229
|
+
|
230
|
+
# Second pass. Look for things that should be excluded, and get rid of them.
|
231
|
+
@headlines.each do |headline|
|
232
|
+
if inherit_export_level and headline.level > inherit_export_level
|
233
|
+
headline.export_state = :exclude
|
234
|
+
else
|
235
|
+
inherit_export_level = nil
|
236
|
+
headline.tags.each do |tag|
|
237
|
+
if (exclude.include? tag) then
|
238
|
+
headline.export_state = :exclude
|
239
|
+
inherit_export_level = headline.level
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
# Stores an in-buffer setting.
|
247
|
+
def store_in_buffer_setting(key, value)
|
248
|
+
if key == "OPTIONS" then
|
249
|
+
|
250
|
+
# Options are stored in a hash. Special-case.
|
251
|
+
|
252
|
+
value.split.each do |opt|
|
253
|
+
if opt =~ /^(.*):(.*?)$/ then
|
254
|
+
@options[$1] = $2
|
255
|
+
else
|
256
|
+
raise "Unexpected option: #{opt}"
|
257
|
+
end
|
258
|
+
end
|
259
|
+
elsif key =~ /^(TODO|SEQ_TODO|TYP_TODO)$/ then
|
260
|
+
# Handle todo keywords specially.
|
261
|
+
value.split.each do |keyword|
|
262
|
+
keyword.gsub!(/\(.*\)/, "") # Get rid of any parenthetical notes
|
263
|
+
keyword = Regexp.escape(keyword)
|
264
|
+
next if keyword == "\\|" # Special character in the todo format, not really a keyword
|
265
|
+
@custom_keywords << keyword
|
266
|
+
end
|
267
|
+
else
|
268
|
+
@in_buffer_settings[key] = value
|
269
|
+
end
|
270
|
+
end
|
101
271
|
end # class Parser
|
102
272
|
end # module Orgmode
|