org-ruby 0.4.2 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|