org-parse 0.1.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 (68) hide show
  1. data/.document +5 -0
  2. data/.gitignore +23 -0
  3. data/ChangeLog +4 -0
  4. data/LICENSE +20 -0
  5. data/README.rdoc +68 -0
  6. data/Rakefile +54 -0
  7. data/VERSION.yml +5 -0
  8. data/bin/org-parse +51 -0
  9. data/bin/org-test +74 -0
  10. data/doc/images/org-parse-struct_1ffae50f0c5eb867f9418df6800f40a5cc3d1751.png +0 -0
  11. data/doc/org-parse.html +203 -0
  12. data/doc/org-parse.org +71 -0
  13. data/doc/struct.dot +10 -0
  14. data/doc/struct.png +0 -0
  15. data/examples/body-only.html.erb +1 -0
  16. data/examples/dot.org-parse-rc +21 -0
  17. data/lib/org-parse/inline-parser.output +945 -0
  18. data/lib/org-parse/inline-parser.rb +219 -0
  19. data/lib/org-parse/inline-parser.ry +77 -0
  20. data/lib/org-parse/inline-parser.tab.rb +411 -0
  21. data/lib/org-parse/node.rb +329 -0
  22. data/lib/org-parse/struct-parser.output +1019 -0
  23. data/lib/org-parse/struct-parser.rb +78 -0
  24. data/lib/org-parse/struct-parser.ry +125 -0
  25. data/lib/org-parse/struct-parser.tab.rb +608 -0
  26. data/lib/org-parse/struct-scanner.rb +272 -0
  27. data/lib/org-parse/templates/single.html.erb +118 -0
  28. data/lib/org-parse/textile-visitor.rb +296 -0
  29. data/lib/org-parse/utils.rb +15 -0
  30. data/lib/org-parse/visitor.rb +542 -0
  31. data/lib/org-parse.rb +46 -0
  32. data/org-parse.gemspec +113 -0
  33. data/rakelib/racc.rake +16 -0
  34. data/test/data/blocks.org +67 -0
  35. data/test/data/emphasis.org +7 -0
  36. data/test/data/footnote.html +136 -0
  37. data/test/data/footnote.org +8 -0
  38. data/test/data/html-export.html +1062 -0
  39. data/test/data/html-export.org +342 -0
  40. data/test/data/images.html +179 -0
  41. data/test/data/images.org +30 -0
  42. data/test/data/index.org +242 -0
  43. data/test/data/lily20100228.jpg +0 -0
  44. data/test/data/lily20100228t.jpg +0 -0
  45. data/test/data/link.org +7 -0
  46. data/test/data/list_before_1st_headline.html +119 -0
  47. data/test/data/list_before_1st_headline.org +7 -0
  48. data/test/data/lists.html +284 -0
  49. data/test/data/lists.org +78 -0
  50. data/test/data/no-headline.org +6 -0
  51. data/test/data/one-headline.org +2 -0
  52. data/test/data/paragraph.org +13 -0
  53. data/test/data/quote.org +15 -0
  54. data/test/data/sections.html +173 -0
  55. data/test/data/sections.org +9 -0
  56. data/test/data/simple-list.org +6 -0
  57. data/test/data/skip_t.org +3 -0
  58. data/test/data/structure.org +53 -0
  59. data/test/data/table.org +14 -0
  60. data/test/data/test-list.org +12 -0
  61. data/test/data/text-bef-hl.org +5 -0
  62. data/test/data/text.org +6 -0
  63. data/test/data/title.html +88 -0
  64. data/test/data/title.org +6 -0
  65. data/test/data/verse.org +48 -0
  66. data/test/helper.rb +31 -0
  67. data/test/test_org-parse.rb +148 -0
  68. metadata +134 -0
@@ -0,0 +1,272 @@
1
+ # -*- coding: utf-8 -*-
2
+ require ::File.join(OrgParse::LIBPATH , 'org-parse', 'inline-parser.rb')
3
+ require ::File.join(OrgParse::LIBPATH , 'org-parse', 'utils.rb')
4
+ require 'yaml'
5
+
6
+
7
+ module OrgParse
8
+
9
+ # Org-modeの文字列を、構造レベルのトークンに分解する
10
+ #
11
+ # 処理は、行単位で行う
12
+ #
13
+ class StructScanner
14
+ include Utils
15
+ include InlineUtils
16
+
17
+ GENERATOR = "OrgParse #{VERSION} Powered by Ruby #{RUBY_VERSION}"
18
+
19
+ ListSymbols = {
20
+ :UL_START => :UL_END, :OL_START => :OL_END,
21
+ :DL_START => :DL_END,
22
+ }
23
+
24
+ # コンストラクタ
25
+ # [_src_] ソース文字列(または、文字列の配列)
26
+ # ソース文字列は、1行単位の配列として @srcに保存する
27
+ # [_title_] skip:t の場合にタイトルとして使われる
28
+ def initialize(src, title)
29
+ @src = (src.is_a? Array) ? src : src.to_a
30
+ @line_idx = 0
31
+ @outline_level = 0
32
+ @nest_stack = []
33
+ @token_que = []
34
+ @section_stack = []
35
+ @options = { :H => 3, :skip => false, :toc => true, :num => true,
36
+ :author => nil,
37
+ :url => nil,
38
+ :email => nil,
39
+ :creator => GENERATOR, :timestamp => true,
40
+ :title => nil, :text => [], :language => 'ja', :charset => 'utf-8',
41
+ :default_title => title, :style => '', :uv => true,
42
+ }
43
+ read_options
44
+ opt = YAML.dump @options
45
+ File.open('dot.rc2', "w") {|f|
46
+ f.write opt
47
+ }
48
+ # p @options
49
+ end
50
+
51
+ # http://orgmode.org/manual/Export-options.html#Export-options
52
+ def read_options
53
+ @src.reject! do |line|
54
+ m = true
55
+ case line
56
+ when /^\s*#\+OPTIONS:\s/i
57
+ case $'
58
+ when /H:([0-9]+)/
59
+ @options[:H] = $1.to_i
60
+ when /skip:(\w)/
61
+ @options[:skip] = $1 != 'nil'
62
+ when /num:(\w+)/
63
+ @options[:num] = $1 != 'nil'
64
+ when /toc:(\w+)/
65
+ @options[:toc] = $1 != 'nil'
66
+ when /author:(\w+)/
67
+ @options[:author] = $1 != 'nil'
68
+ when /creator:(\w+)/
69
+ @options[:creator] = $1 != 'nil'
70
+ when /timestamp:(\w+)/
71
+ @options[:timestamp] = $1 != 'nil'
72
+ when /uv:(\w+)/
73
+ @options[:uv] = $1 != 'nil'
74
+ else
75
+ m = false
76
+ end
77
+ when /^\s*#\+TITLE:\s+(.*)$/i
78
+ @options[:title] = $1
79
+ when /^\s*#\+TEXT:\s+(.*)$/i
80
+ @options[:text] += line_parse($1 + "\n")
81
+ when /^\s*#\+LANGUAGE:\s+(.*)/i
82
+ @options[:language] = $1
83
+ when /^\s*#\+AUTHOR:\s*(.*)/i
84
+ @options[:author] = $1
85
+ when /^\s*#\+EMAIL:\s*(.*)/i
86
+ @options[:email] = $1
87
+ when /^\s*#\+STYLE:\s*(.*)$/i
88
+ @options[:style] += $1
89
+ else
90
+ m = false
91
+ end
92
+ m
93
+ end
94
+ end
95
+
96
+ def next_token
97
+ return [[false, false],[]] if @token_que.empty?
98
+ # p @token_que[0]
99
+ token = @token_que.shift
100
+ vars = []
101
+
102
+ while token[0] == :VARIABLELINE and (token[1][0] == "ATTR_HTML" or token[1][0] == "CAPTION")
103
+ if token[1][0] == "CAPTION"
104
+ vars << "CAPTION:" + token[1][1]
105
+ else
106
+ vars << token[1][1]
107
+ end
108
+ return [[false, false], []] if @token_que.empty?
109
+ token = @token_que.shift
110
+ end
111
+ [token, vars]
112
+ end
113
+
114
+ # リスト開始ラインのネストをチェックする
115
+ # @nest_stack が、空なら新規リストの開始
116
+ # @nest_stack.last のインデントよりも、インデントが深ければ、
117
+ # ネストしたリストの開始
118
+ # @nest_stack.last のインデントとインデントが等しければ
119
+ # 新規のリストアイテムの開始
120
+ # @nest_stack.last のインデントよりインデントが浅ければ、
121
+ # @nest_stack.last のリストの終了後、新規リストを開始
122
+ def check_nest(kind, indent, string, dt = '')
123
+ last = @nest_stack.last
124
+ if @nest_stack.empty?
125
+ # puts "0: line: #{line.chomp} indent: #{indent} last: #{last.inspect}"
126
+ @nest_stack << [kind, indent]
127
+ @token_que << [kind, string]
128
+ @token_que << [:LI_START, [kind, string, dt]]
129
+ elsif last[1] < indent
130
+ # puts "1: line: #{line.chomp} indent: #{indent} last: #{last.inspect}"
131
+ @nest_stack << [kind, indent]
132
+ # @token_que << [:LI_END, line]
133
+ @token_que << [kind, string]
134
+ @token_que << [:LI_START, [kind, string, dt]]
135
+ elsif last[1] > indent
136
+ # puts "2: line: #{line.chomp} indent: #{indent} last: #{last.inspect}"
137
+ nest = @nest_stack.pop
138
+ @token_que << [:LI_END, string]
139
+ @token_que << [ListSymbols[nest[0]], ListSymbols[nest[0]]]
140
+ check_nest kind, indent, string
141
+ else
142
+ # puts "3: line: #{line.chomp} indent: #{indent} last: #{last.inspect}"
143
+ @token_que << [:LI_END, string]
144
+ @token_que << [:LI_START, [kind, string, dt]]
145
+ end
146
+ end
147
+
148
+ # if in list then exit list.
149
+ def exit_nests(line)
150
+ indent = get_indent line
151
+ while @nest_stack.last and @nest_stack.last[1] >= indent
152
+ nest = @nest_stack.pop
153
+ @token_que << [:LI_END, line]
154
+ @token_que << [ListSymbols[nest[0]], line]
155
+ end
156
+ end
157
+
158
+ def exit_section(level)
159
+ cnt = 0
160
+ while @section_stack.last and @section_stack.last >= level
161
+ @token_que << [:SEC_END, level]
162
+ @section_stack.pop
163
+ end
164
+ end
165
+
166
+ def skip_to_1st_headline
167
+ @line_idx += 1 while @src[@line_idx] !~ /^\*\s+[^\s]/
168
+ end
169
+
170
+ # scan before 1st headline
171
+ def scan_before_1st_headline
172
+ title = nil
173
+ if @options[:title]
174
+ title =@options[:title]
175
+ skip_to_1st_headline if @options[:skip]
176
+ elsif @options[:skip]
177
+ skip_to_1st_headline
178
+ title = @options[:default_title]
179
+ else
180
+ @line_idx += 1 while @src[@line_idx] =~ /^\s*$/
181
+ title = @src[@line_idx].chomp.sub(/^\s*/, '')
182
+ if title =~ /^\*+\s*/
183
+ title = @options[:default_title]
184
+ else
185
+ @line_idx += 1
186
+ end
187
+ end
188
+ @options[:title] = line_parse title
189
+ end
190
+
191
+ # split to tokens from @src array and set to @token_que
192
+ def scan
193
+ @line_idx = 0
194
+ scan_before_1st_headline
195
+ example_flag = false
196
+ @token_que << [:DOCUMENT_START, @options]
197
+ while @line_idx < @src.size
198
+ line = @src[@line_idx]
199
+ if example_flag && line !~ /^\s*#\+end_(example|html|src)/i
200
+ @token_que << [:TEXTLINE, [line, get_indent(line)]]
201
+ @line_idx += 1
202
+ next
203
+ end
204
+
205
+ case line
206
+ when /^\s*$/ # WHITELINE
207
+ @token_que << [:WHITELINE, line]
208
+ when /^(\*+)(\s+)COMMENT(\s)/
209
+ @line_idx += 1
210
+ @line_idx += 1 while @src[@line_idx] and @src[@line_idx] !~ /^\*+\s/
211
+ @line_idx -= 1 if @src[@line_idx]
212
+ when /^(\*+)(\s+)/
213
+ rest = $'
214
+ level = $1
215
+ exit_nests('')
216
+ exit_section(level.size)
217
+ @section_stack << level.size
218
+ @token_que << [:HEADLINE, [level, rest]]
219
+ when /^(\s*)-\s(.+)\s::\s+/
220
+ # Definition LIST
221
+ rest = $'
222
+ check_nest :DL_START, get_indent($1), rest, $2
223
+ when /^(\s*)[-+*]\s/
224
+ # Unordered LIST
225
+ rest = $'
226
+ check_nest :UL_START, get_indent($1), rest
227
+ when /^(\s*)[0-9]+(\.|\))\s+/
228
+ # Ordered LIST
229
+ rest = $'
230
+ check_nest :OL_START, get_indent($1), rest
231
+ when /^\s*#\+HTML:/
232
+ # #+HTML
233
+ rest = $'
234
+ @token_que << [:QUOTE, $']
235
+ when /^\s*#\+([^ :]+):\s*(.+)$/
236
+ @token_que << [:VARIABLELINE, [$1.upcase, $2.chomp]]
237
+ when /^(\s*):\s(.*)$/
238
+ @token_que << [:EXAMPLE, [$2+"\n", get_indent($1)]]
239
+ when /^\s*#\+BEGIN_([A-Z0-9_]+)/i # BLOCK
240
+ block_name = $1.upcase
241
+ exit_nests line
242
+ @token_que << [:BLOCK_START, [block_name, line, get_indent(line)]]
243
+ example_flag = true if ['EXAMPLE', 'HTML', 'SRC'].include? block_name.upcase
244
+ when /^\s*#\+END_([A-Z0-9_]+)/i # BLOCK
245
+ block_name = $1
246
+ exit_nests line
247
+ @token_que << [:BLOCK_END, [block_name, line]]
248
+ example_flag = false
249
+ when /^\s*\|[-\|\+]*\s*$/ # table separator
250
+ # an org-mode table separator has the first non-whitespace
251
+ # character as a | (pipe), then consists of nothing else other
252
+ # than pipes, hyphens, and pluses.
253
+ exit_nests line
254
+ @token_que << [:TABLE_SEP, line]
255
+ when /^\s*\|/ # table_row
256
+ # the first non-whitespace character is a | (pipe).
257
+ exit_nests line
258
+ @token_que << [:TABLE_ROW, line]
259
+ when /^\s*\[fn:([^\]]+)\]\s+(.+)$/
260
+ exit_nests line
261
+ @token_que << [:FOOTNOTE, [$1, $2]]
262
+ else
263
+ exit_nests line
264
+ @token_que << [:TEXTLINE, [line, get_indent(line)]]
265
+ end
266
+ @line_idx += 1
267
+ end
268
+ exit_nests ''
269
+ exit_section(0)
270
+ end
271
+ end
272
+ end
@@ -0,0 +1,118 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
3
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
4
+ <html xmlns="http://www.w3.org/1999/xhtml"
5
+ lang="<%= @language %>" xml:lang="<%= @language %>">
6
+ <head>
7
+ <title><%= @title %></title>
8
+ <meta http-equiv="Content-Type" content="text/html;charset=<%= @charset %>"/>
9
+ <meta name="generator" content="Org-parse"/>
10
+ <meta name="generated" content="<%= Time.now.to_s %>"/>
11
+ <% if @options[:author] %>
12
+ <meta name="author" content="<%= @options[:author] %>"/>
13
+ <% end %>
14
+ <meta name="description" content=""/>
15
+ <meta name="keywords" content=""/>
16
+ <style type="text/css">
17
+ <!--/*--><![CDATA[/*><!--*/
18
+ html { font-family: "MS Pゴシック",Osaka,sans-serif,serif,monospace,cursive,fantasy; font-size: 12pt; }
19
+ .title { text-align: center; }
20
+ .todo { color: red; }
21
+ .done { color: green; }
22
+ .tag { background-color: #add8e6; font-weight:normal }
23
+ .target { }
24
+ .timestamp { color: #bebebe; }
25
+ .timestamp-kwd { color: #5f9ea0; }
26
+ p.verse { margin-left: 3% }
27
+ pre {
28
+ border: 1pt solid #AEBDCC;
29
+ background-color: #F3F5F7;
30
+ padding: 5pt;
31
+ font-family: courier, monospace;
32
+ font-size: 90%;
33
+ overflow:auto;
34
+ }
35
+ blockquote {
36
+ border: 1pt solid #AEBDCC;
37
+ background-color: #F3F5F7;
38
+ padding: 5pt;
39
+ font-family: courier, monospace;
40
+ font-size: 90%;
41
+ /* overflow:auto; */
42
+ }
43
+ table { border-collapse: collapse; }
44
+ td, th { vertical-align: top; }
45
+ dt { font-weight: bold; }
46
+ div.figure { padding: 0.5em; }
47
+ div.figure p { text-align: center; }
48
+ .linenr { font-size:smaller }
49
+ .code-highlighted {background-color:#ffff00;}
50
+ .org-info-js_info-navigation { border-style:none; }
51
+ #org-info-js_console-label { font-size:10px; font-weight:bold;
52
+ white-space:nowrap; }
53
+ .org-info-js_search-highlight {background-color:#ffff00; color:#000000;
54
+ font-weight:bold; }
55
+ /*]]>*/-->
56
+ </style>
57
+ <%= @add_to_head %>
58
+ <script type="text/javascript">
59
+ <!--/*--><![CDATA[/*><!--*/
60
+ function CodeHighlightOn(elem, id)
61
+ {
62
+ var target = document.getElementById(id);
63
+ if(null != target) {
64
+ elem.cacheClassElem = elem.className;
65
+ elem.cacheClassTarget = target.className;
66
+ target.className = "code-highlighted";
67
+ elem.className = "code-highlighted";
68
+ }
69
+ }
70
+ function CodeHighlightOff(elem, id)
71
+ {
72
+ var target = document.getElementById(id);
73
+ if(elem.cacheClassElem)
74
+ elem.className = elem.cacheClassElem;
75
+ if(elem.cacheClassTarget)
76
+ target.className = elem.cacheClassTarget;
77
+ }
78
+ /*]]>*///-->
79
+ </script>
80
+ </head>
81
+ <body>
82
+ <div id="content">
83
+
84
+ <h1 class="title"><%= @title %></h1>
85
+
86
+ <%= @before_text %>
87
+
88
+ <%= table_of_contents %>
89
+
90
+ <%= @body %>
91
+
92
+ <% unless @footnotes.empty? %>
93
+ <div id="footnotes">
94
+ <h2 class="footnotes">Footnotes: </h2>
95
+ <div id="text-footnotes">
96
+ <%= footnotes %>
97
+ </div>
98
+ </div>
99
+ <% end %>
100
+
101
+ <div id="postamble">
102
+ <% if @options[:author] %>
103
+ <p class="author"> Author: <%= @options[:author] %>
104
+ <% if @options[:email] %>
105
+ <a href="mailto:<%= @options[:email] %>">&lt;<%= @options[:email] %>&gt;</a>
106
+ <% end %>
107
+ </p>
108
+ <% end %>
109
+ <% if @options[:timestamp] %>
110
+ <p class="date"> Date: <%= Time.now %> </p>
111
+ <% end %>
112
+ <% if @options[:creator] %>
113
+ <p class="creator">HTML generated by <%= @options[:creator] %></p>
114
+ <% end %>
115
+ </div>
116
+ </div>
117
+ </body>
118
+ </html>
@@ -0,0 +1,296 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'rubygems'
3
+ require ::File.join(OrgParse::LIBPATH , 'org-parse', 'visitor.rb')
4
+
5
+ module OrgParse
6
+
7
+ # todo:
8
+ # \\- &shy; \\- 
9
+ # -- &ndash; --
10
+ # --- &mdash; ---
11
+ # ... &hellip; \ldots
12
+
13
+ # output HTML format
14
+ class TextileVisitor < OrgVisitor
15
+ include VisitorUtils
16
+
17
+ def initialize(root, cm_opts = {})
18
+ @body = @title = @add_to_head = ''
19
+ @root = root
20
+ @ul_stack = []
21
+ @list_level = 0
22
+ @body = ''
23
+ @options = cm_opts
24
+ super()
25
+ end
26
+
27
+ # build the textile
28
+ def build
29
+ @options = @root.options
30
+ @before_text = exec_list @options[:text]
31
+ title = exec_list @root.options[:title]
32
+ body = "h1. " + title + "\n\n" + @before_text
33
+ @footnotes = []
34
+ @footnote_idxs = []
35
+ start_flag = true
36
+ @root.children.each do |node|
37
+ if start_flag and node.kind != :SECTION
38
+ @before_text += execute(node)
39
+ else
40
+ start_flag = false
41
+ body += execute(node)
42
+ end
43
+ end
44
+ body += footnotes
45
+ end
46
+
47
+ def headline(node)
48
+ level = node.level+1
49
+ %Q|\nh#{level}. #{exec_children(node).chomp}\n\n|
50
+ end
51
+
52
+ # paragraph
53
+ # if @p_tag_flag == false then we don't output <p></p> tags.
54
+ def textblock(node)
55
+ if node.verse? or node.example? or node.html? or node.src?
56
+ exec_children node
57
+ elsif @p_tag_flag
58
+ "p. #{exec_children(node).sub(/^\s*/,'').chomp}\n"
59
+ else
60
+ str = exec_children node
61
+ @p_tag_flag = true # next paragraph has <p>
62
+ str
63
+ end
64
+ end
65
+
66
+ def textline(node)
67
+ str = exec_children(node)
68
+ str = h str unless node.verse? or node.example? or node.html? or node.src?
69
+ str
70
+ end
71
+
72
+ def example(node)
73
+ body = exec_children(node)
74
+ %Q|<pre>\n#{ body.chomp }\n</pre>\n|
75
+ end
76
+
77
+ def src(node)
78
+ if @options[:redmine]
79
+ text = exec_children(node)
80
+ syntax = node.syntax
81
+ %Q|<pre><code class="#{syntax}">\n#{ body.chomp }\n</code></pre>\n|
82
+ else
83
+ example node
84
+ end
85
+ end
86
+
87
+ def blockquote(node)
88
+ body = exec_children(node)
89
+ body.sub(/\n/, '<br/>')
90
+ %Q|\nbq.#{body.sub(/^\s*/,'').chomp}\n\n|
91
+ end
92
+
93
+ def blocks(node)
94
+ case node.block_name
95
+ when 'VERSE'
96
+ example node
97
+ when 'EXAMPLE'
98
+ example node
99
+ when 'QUOTE'
100
+ blockquote node
101
+ when 'HTML'
102
+ textblock node
103
+ when 'COMMENT'
104
+ ''
105
+ when 'SRC'
106
+ src node
107
+ else
108
+ puts "not implimented block=>[#{node.block_name}](#{node.inspect})"
109
+ exec_children(node)
110
+ end
111
+ end
112
+
113
+ def link(node)
114
+ desc = exec_children node
115
+ if desc.empty?
116
+ if node.uri =~ @image_file_reg
117
+ " !#{node.uri.sub(/^file:/, '')}! "
118
+ else
119
+ " #{node.uri.sub(/^file:/, '')} "
120
+ end
121
+ else
122
+ %Q| "#{desc}":#{node.uri.sub(/^file:/, '')} |
123
+ end
124
+ end
125
+
126
+ def table(node)
127
+ ret = ''
128
+ ret += exec_list node.hrows
129
+ ret += exec_children node
130
+ end
131
+
132
+ def table_row(node)
133
+ ret = ''
134
+ s = node.is_head? ? '|_.' : '|'
135
+ node.children.each {|cols|
136
+ cols.each{|col|
137
+ ret += s + execute(col)
138
+ }
139
+ }
140
+ ret += "|\n"
141
+ end
142
+
143
+ def variable(node)
144
+ fvs = ['CAPTION', 'ATTR_HTML', 'TEXT']
145
+ if fvs.include? node.name
146
+ @flash_vars[node.name] = node.value
147
+ else
148
+ @options[node.name] = node.value
149
+ end
150
+ ''
151
+ end
152
+
153
+ def clear_vars
154
+ @flash_vars.clear
155
+ end
156
+
157
+ def lists(node)
158
+ tags = { :UNORDERED_LIST => ['<ul>', '</ul>'],
159
+ :ORDERED_LIST => ['<ol>', '</ol>'],
160
+ :DEFINITION_LIST => ['<dl>', '</dl>'],
161
+ }
162
+ @list_level += 1
163
+ body = exec_children node
164
+ @list_level -= 1
165
+ if node.kind == :DEFINITION_LIST
166
+ "#{tags[node.kind][0]}\n#{body.chomp}\n#{tags[node.kind][1]}\n"
167
+ else
168
+ body
169
+ end
170
+ end
171
+
172
+ # list_item
173
+ def list_item(node)
174
+ marks = { :UL_START => '*', :OL_START => '#' }
175
+ if node.children.empty?
176
+ if node.type == :DL_START
177
+ " <dt>#{exec_list(node.dt)}</dt>\n <dd>#{exec_list(node.value).chomp}</dd>\n"
178
+ else
179
+ mark = marks[node.type] * @list_level
180
+ "#{mark} #{exec_list(node.value).chomp}\n"
181
+ end
182
+ else
183
+ @list_level += 1
184
+ @p_tag_flag = false
185
+ str = exec_list(node.value)
186
+ str += exec_children node
187
+ str.sub(/\n/, '<br/>')
188
+ if node.type == :DL_START
189
+ str = " <dt>#{exec_list(node.dt)}</dt>\n <dd>\n#{str.chomp}</dd>\n"
190
+ else
191
+ mark = marks[node.type] * @list_level
192
+ str = "#{mark} #{str.chomp}\n"
193
+ end
194
+ @p_tag_flag = true
195
+ @list_level -= 1
196
+ str
197
+ end
198
+ end
199
+
200
+ def footnote_link(node)
201
+ idx = @footnote_idxs.index node.value
202
+ unless idx
203
+ idx = @footnote_idxs.size
204
+ @footnote_idxs << node.value
205
+ end
206
+ idx += 1
207
+ "[#{idx}]"
208
+ end
209
+
210
+ def footnote_define(node)
211
+ idx = @footnote_idxs.index node.value
212
+ unless idx
213
+ idx = @footnote_idxs.size
214
+ @footnote_idxs << node.value
215
+ end
216
+ @footnotes[idx] = exec_children(node)
217
+ ''
218
+ end
219
+
220
+ def footnotes
221
+ ret = ''
222
+ @footnotes.each_index{|idx|
223
+ n = idx+1
224
+ val = @footnotes[idx]
225
+ ret += "fn#{n}. #{val}\n"
226
+ }
227
+ ret
228
+ end
229
+
230
+ def execute(node)
231
+ # puts "node:#{node.kind} [#{node.value}]\n"
232
+ # STDERR.puts node.inspect
233
+ return '' if node.done?
234
+ case node.kind
235
+ when :SECTION
236
+ str = execute node.headline
237
+ str += exec_children node # section node
238
+ when :HEADLINE
239
+ headline node
240
+ when :TEXTBLOCK
241
+ textblock node
242
+ when :WHITELINE, :WHITELINES
243
+ "\n" * node.value
244
+ when :STRING
245
+ node.value
246
+ when :QUOTE
247
+ if node.value == "\n"
248
+ '<br/>'
249
+ else
250
+ node.value
251
+ end
252
+ when :TEXTLINE
253
+ textline node
254
+ when :EMPHASIS
255
+ " *#{ exec_children node }* "
256
+ when :ITALIC
257
+ " _#{ exec_children node }_ "
258
+ when :UNDER_LINE
259
+ %Q| +#{exec_children node}+ |
260
+ when :STRIKE_THROUGH
261
+ " -#{ exec_children node}- "
262
+ when :CODE, :VERBATIM
263
+ " @#{ exec_children node }@ "
264
+ when :VARIABLE
265
+ variable node
266
+ when :UNORDERED_LIST, :ORDERED_LIST, :DEFINITION_LIST
267
+ lists node
268
+ when :LIST_ITEM
269
+ list_item node
270
+ when :LINK
271
+ link node
272
+ when :BLOCK
273
+ blocks node
274
+ when :TABLE
275
+ table node
276
+ when :TABLE_ROW
277
+ table_row node
278
+ when :FN_LINK
279
+ footnote_link node
280
+ when :FN_DEFINE
281
+ footnote_define node
282
+ else
283
+ puts "not implimented=>[#{node.kind}](#{node.inspect})"
284
+ ''
285
+ end
286
+ end
287
+
288
+ # HTML escape
289
+ def html_escape(s)
290
+ s.to_s.gsub(/&/, "&amp;").gsub(/>/, "&gt;").gsub(/</, "&lt;")
291
+ end
292
+ alias h html_escape
293
+
294
+ end
295
+
296
+ end
@@ -0,0 +1,15 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module OrgParse
4
+
5
+ module Utils
6
+ # とりあえず
7
+ def get_indent(str)
8
+ return 0 if str.empty?
9
+ str =~ /^(\s*)/
10
+ $1.gsub(/([^\t]{8})|([^\t]*)\t/n){ [$+].pack("A8") }.size
11
+ end
12
+ end
13
+
14
+ end
15
+