org-ruby 0.2.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/.bnsignore +18 -0
- data/History.txt +24 -0
- data/README.txt +66 -0
- data/Rakefile +22 -0
- data/TAGS +128 -0
- data/bin/org-ruby +40 -0
- data/lib/org-ruby.rb +48 -0
- data/lib/org-ruby/headline.rb +75 -0
- data/lib/org-ruby/html_output_buffer.rb +80 -0
- data/lib/org-ruby/line.rb +172 -0
- data/lib/org-ruby/output_buffer.rb +154 -0
- data/lib/org-ruby/parser.rb +72 -0
- data/lib/org-ruby/regexp_helper.rb +156 -0
- data/lib/org-ruby/textile_output_buffer.rb +67 -0
- data/spec/data/freeform.org +111 -0
- data/spec/data/hyp-planning.org +335 -0
- data/spec/data/remember.org +53 -0
- data/spec/headline_spec.rb +55 -0
- data/spec/html_examples/block_code.html +29 -0
- data/spec/html_examples/block_code.org +35 -0
- data/spec/html_examples/blockquote.html +7 -0
- data/spec/html_examples/blockquote.org +13 -0
- data/spec/html_examples/inline-formatting.html +10 -0
- data/spec/html_examples/inline-formatting.org +17 -0
- data/spec/html_examples/lists.html +19 -0
- data/spec/html_examples/lists.org +36 -0
- data/spec/html_examples/tables.html +20 -0
- data/spec/html_examples/tables.org +26 -0
- data/spec/html_examples/text.html +2 -0
- data/spec/html_examples/text.org +16 -0
- data/spec/line_spec.rb +89 -0
- data/spec/parser_spec.rb +86 -0
- data/spec/regexp_helper_spec.rb +57 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/textile_examples/block_code.org +35 -0
- data/spec/textile_examples/block_code.textile +29 -0
- data/spec/textile_examples/blockquote.org +13 -0
- data/spec/textile_examples/blockquote.textile +11 -0
- data/spec/textile_examples/keywords.org +13 -0
- data/spec/textile_examples/keywords.textile +11 -0
- data/spec/textile_examples/links.org +11 -0
- data/spec/textile_examples/links.textile +10 -0
- data/spec/textile_examples/lists.org +36 -0
- data/spec/textile_examples/lists.textile +20 -0
- data/spec/textile_examples/single-space-plain-list.org +13 -0
- data/spec/textile_examples/single-space-plain-list.textile +10 -0
- data/spec/textile_examples/tables.org +26 -0
- data/spec/textile_examples/tables.textile +23 -0
- data/spec/textile_output_buffer_spec.rb +21 -0
- data/test/test_orgmode_parser.rb +0 -0
- metadata +120 -0
@@ -0,0 +1,172 @@
|
|
1
|
+
module Orgmode
|
2
|
+
|
3
|
+
# Represents a single line of an orgmode file.
|
4
|
+
class Line
|
5
|
+
|
6
|
+
# This is the line itself.
|
7
|
+
attr_reader :line
|
8
|
+
|
9
|
+
# The indent level of this line. this is important to properly translate
|
10
|
+
# nested lists from orgmode to textile.
|
11
|
+
# TODO 2009-12-20 bdewey: Handle tabs
|
12
|
+
attr_reader :indent
|
13
|
+
|
14
|
+
def initialize(line)
|
15
|
+
@line = line
|
16
|
+
@indent = 0
|
17
|
+
@line =~ /\s*/
|
18
|
+
@indent = $&.length unless blank?
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
return @line
|
23
|
+
end
|
24
|
+
|
25
|
+
# Tests if a line is a comment.
|
26
|
+
def comment?
|
27
|
+
@line =~ /^\s*#/
|
28
|
+
end
|
29
|
+
|
30
|
+
# Tests if a line contains metadata instead of actual content.
|
31
|
+
def metadata?
|
32
|
+
@line =~ /^\s*(CLOCK|DEADLINE|START|CLOSED|SCHEDULED):/
|
33
|
+
end
|
34
|
+
|
35
|
+
def nonprinting?
|
36
|
+
comment? || metadata?
|
37
|
+
end
|
38
|
+
|
39
|
+
def blank?
|
40
|
+
@line =~ /^\s*$/
|
41
|
+
end
|
42
|
+
|
43
|
+
def plain_list?
|
44
|
+
ordered_list? or unordered_list?
|
45
|
+
end
|
46
|
+
|
47
|
+
UnorderedListRegexp = /^\s*(-|\+)\s*/
|
48
|
+
|
49
|
+
def unordered_list?
|
50
|
+
@line =~ UnorderedListRegexp
|
51
|
+
end
|
52
|
+
|
53
|
+
def strip_unordered_list_tag
|
54
|
+
@line.sub(UnorderedListRegexp, "")
|
55
|
+
end
|
56
|
+
|
57
|
+
OrderedListRegexp = /^\s*\d+(\.|\))\s*/
|
58
|
+
|
59
|
+
def ordered_list?
|
60
|
+
@line =~ OrderedListRegexp
|
61
|
+
end
|
62
|
+
|
63
|
+
def strip_ordered_list_tag
|
64
|
+
@line.sub(OrderedListRegexp, "")
|
65
|
+
end
|
66
|
+
|
67
|
+
def plain_text?
|
68
|
+
not metadata? and not blank? and not plain_list?
|
69
|
+
end
|
70
|
+
|
71
|
+
def table_row?
|
72
|
+
# for an org-mode table, the first non-whitespace character is a
|
73
|
+
# | (pipe).
|
74
|
+
@line =~ /^\s*\|/
|
75
|
+
end
|
76
|
+
|
77
|
+
def table_separator?
|
78
|
+
# an org-mode table separator has the first non-whitespace
|
79
|
+
# character as a | (pipe), then consists of nothing else other
|
80
|
+
# than pipes, hyphens, and pluses.
|
81
|
+
|
82
|
+
@line =~ /^\s*\|[-\|\+]*\s*$/
|
83
|
+
end
|
84
|
+
|
85
|
+
def table?
|
86
|
+
table_row? or table_separator?
|
87
|
+
end
|
88
|
+
|
89
|
+
BlockRegexp = /^\s*#\+(BEGIN|END)_(\w*)/
|
90
|
+
|
91
|
+
def begin_block?
|
92
|
+
@line =~ BlockRegexp && $1 == "BEGIN"
|
93
|
+
end
|
94
|
+
|
95
|
+
def end_block?
|
96
|
+
@line =~ BlockRegexp && $1 == "END"
|
97
|
+
end
|
98
|
+
|
99
|
+
def block_type
|
100
|
+
$2 if @line =~ BlockRegexp
|
101
|
+
end
|
102
|
+
|
103
|
+
# Determines the paragraph type of the current line.
|
104
|
+
def paragraph_type
|
105
|
+
return :blank if blank?
|
106
|
+
return :ordered_list if ordered_list?
|
107
|
+
return :unordered_list if unordered_list?
|
108
|
+
return :metadata if metadata?
|
109
|
+
return :comment if comment?
|
110
|
+
return :table_separator if table_separator?
|
111
|
+
return :table_row if table_row?
|
112
|
+
return :paragraph
|
113
|
+
end
|
114
|
+
|
115
|
+
def self.to_textile(lines)
|
116
|
+
output = ""
|
117
|
+
output_buffer = TextileOutputBuffer.new(output)
|
118
|
+
translate(lines, output_buffer)
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.to_html(lines)
|
122
|
+
output = ""
|
123
|
+
output_buffer = HtmlOutputBuffer.new(output)
|
124
|
+
translate(lines, output_buffer)
|
125
|
+
end
|
126
|
+
|
127
|
+
# Converts an array of lines to textile format.
|
128
|
+
def self.translate(lines, output_buffer)
|
129
|
+
lines.each do |line|
|
130
|
+
|
131
|
+
# See if we're carrying paragraph payload, and output
|
132
|
+
# it if we're about to switch to some other output type.
|
133
|
+
output_buffer.prepare(line)
|
134
|
+
|
135
|
+
case line.paragraph_type
|
136
|
+
when :metadata, :table_separator, :blank
|
137
|
+
|
138
|
+
# IGNORE
|
139
|
+
|
140
|
+
when :comment
|
141
|
+
|
142
|
+
output_buffer.push_mode(:blockquote) if line.begin_block? and line.block_type == "QUOTE"
|
143
|
+
output_buffer.push_mode(:code) if line.begin_block? and line.block_type == "EXAMPLE"
|
144
|
+
output_buffer.pop_mode(:blockquote) if line.end_block? and line.block_type == "QUOTE"
|
145
|
+
output_buffer.pop_mode(:code) if line.end_block? and line.block_type == "EXAMPLE"
|
146
|
+
|
147
|
+
when :table_row
|
148
|
+
|
149
|
+
output_buffer << line.line.lstrip
|
150
|
+
|
151
|
+
when :ordered_list
|
152
|
+
|
153
|
+
output_buffer << line.strip_ordered_list_tag << " "
|
154
|
+
|
155
|
+
when :unordered_list
|
156
|
+
|
157
|
+
output_buffer << line.strip_unordered_list_tag << " "
|
158
|
+
|
159
|
+
when :paragraph
|
160
|
+
|
161
|
+
if output_buffer.preserve_whitespace? then
|
162
|
+
output_buffer << line.line
|
163
|
+
else
|
164
|
+
output_buffer << line.line.strip << " "
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
output_buffer.flush!
|
169
|
+
output_buffer.output
|
170
|
+
end
|
171
|
+
end # class Line
|
172
|
+
end # module Orgmode
|
@@ -0,0 +1,154 @@
|
|
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 temporary buffer that we accumulate into.
|
12
|
+
attr_reader :buffer
|
13
|
+
|
14
|
+
# This is the overall output buffer
|
15
|
+
attr_reader :output
|
16
|
+
|
17
|
+
# This is the current type of output being accumulated.
|
18
|
+
attr_accessor :output_type
|
19
|
+
|
20
|
+
# Creates a new OutputBuffer object that is bound to an output object.
|
21
|
+
# The output will get flushed to =output=.
|
22
|
+
def initialize(output)
|
23
|
+
@output = output
|
24
|
+
@buffer = ""
|
25
|
+
@output_type = :start
|
26
|
+
@list_indent_stack = []
|
27
|
+
@paragraph_modifier = nil
|
28
|
+
@cancel_modifier = false
|
29
|
+
@mode_stack = []
|
30
|
+
push_mode(:normal)
|
31
|
+
|
32
|
+
@logger = Logger.new(STDERR)
|
33
|
+
@logger.level = Logger::WARN
|
34
|
+
|
35
|
+
@re_help = RegexpHelper.new
|
36
|
+
end
|
37
|
+
|
38
|
+
Modes = [:normal, :ordered_list, :unordered_list, :blockquote, :code, :table]
|
39
|
+
|
40
|
+
def current_mode
|
41
|
+
@mode_stack.last
|
42
|
+
end
|
43
|
+
|
44
|
+
def current_mode_list?
|
45
|
+
(current_mode == :ordered_list) or (current_mode == :unordered_list)
|
46
|
+
end
|
47
|
+
|
48
|
+
def push_mode(mode)
|
49
|
+
raise "Not a recognized mode: #{mode}" unless Modes.include?(mode)
|
50
|
+
@mode_stack.push(mode)
|
51
|
+
end
|
52
|
+
|
53
|
+
def pop_mode(mode = nil)
|
54
|
+
m = @mode_stack.pop
|
55
|
+
@logger.warn "Modes don't match. Expected to pop #{mode}, but popped #{m}" if mode && mode != m
|
56
|
+
m
|
57
|
+
end
|
58
|
+
|
59
|
+
# Prepares the output buffer to receive content from a line.
|
60
|
+
# As a side effect, this may flush the current accumulated text.
|
61
|
+
def prepare(line)
|
62
|
+
@logger.debug "Looking at #{line.paragraph_type}: #{line.to_s}"
|
63
|
+
if not should_accumulate_output?(line) then
|
64
|
+
flush!
|
65
|
+
maintain_list_indent_stack(line)
|
66
|
+
@output_type = line.paragraph_type
|
67
|
+
end
|
68
|
+
push_mode(:table) if enter_table?
|
69
|
+
pop_mode(:table) if exit_table?
|
70
|
+
end
|
71
|
+
|
72
|
+
# Tests if we are entering a table mode.
|
73
|
+
def enter_table?
|
74
|
+
((@output_type == :table_row) || (@output_type == :table_separator)) &&
|
75
|
+
(current_mode != :table)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Tests if we are existing a table mode.
|
79
|
+
def exit_table?
|
80
|
+
((@output_type != :table_row) && (@output_type != :table_separator)) &&
|
81
|
+
(current_mode == :table)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Accumulate the string @str@.
|
85
|
+
def << (str)
|
86
|
+
@buffer << str
|
87
|
+
end
|
88
|
+
|
89
|
+
# Gets the current list indent level.
|
90
|
+
def list_indent_level
|
91
|
+
@list_indent_stack.length
|
92
|
+
end
|
93
|
+
|
94
|
+
# Test if we're in an output mode in which whitespace is significant.
|
95
|
+
def preserve_whitespace?
|
96
|
+
return current_mode == :code
|
97
|
+
end
|
98
|
+
|
99
|
+
######################################################################
|
100
|
+
private
|
101
|
+
|
102
|
+
def maintain_list_indent_stack(line)
|
103
|
+
if (line.plain_list?) then
|
104
|
+
while (not @list_indent_stack.empty? \
|
105
|
+
and (@list_indent_stack.last > line.indent))
|
106
|
+
@list_indent_stack.pop
|
107
|
+
pop_mode
|
108
|
+
end
|
109
|
+
if (@list_indent_stack.empty? \
|
110
|
+
or @list_indent_stack.last < line.indent)
|
111
|
+
@list_indent_stack.push(line.indent)
|
112
|
+
push_mode line.paragraph_type
|
113
|
+
end
|
114
|
+
else
|
115
|
+
@list_indent_stack = []
|
116
|
+
while ((current_mode == :ordered_list) or
|
117
|
+
(current_mode == :unordered_list))
|
118
|
+
pop_mode
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# Tests if the current line should be accumulated in the current
|
124
|
+
# output buffer. (Extraneous line breaks in the orgmode buffer
|
125
|
+
# are removed by accumulating lines in the output buffer without
|
126
|
+
# line breaks.)
|
127
|
+
def should_accumulate_output?(line)
|
128
|
+
|
129
|
+
# Special case: Preserve line breaks in block code mode.
|
130
|
+
return false if preserve_whitespace?
|
131
|
+
|
132
|
+
# Special case: Multiple blank lines get accumulated.
|
133
|
+
return true if line.paragraph_type == :blank and @output_type == :blank
|
134
|
+
|
135
|
+
# Currently only "paragraphs" get accumulated with previous output.
|
136
|
+
return false unless line.paragraph_type == :paragraph
|
137
|
+
if ((@output_type == :ordered_list) or
|
138
|
+
(@output_type == :unordered_list)) then
|
139
|
+
|
140
|
+
# If the previous output type was a list item, then we only put a paragraph in it
|
141
|
+
# if its indent level is greater than the list indent level.
|
142
|
+
|
143
|
+
return false unless line.indent > @list_indent_stack.last
|
144
|
+
end
|
145
|
+
|
146
|
+
# Only accumulate paragraphs with lists & paragraphs.
|
147
|
+
return false unless
|
148
|
+
((@output_type == :paragraph) or
|
149
|
+
(@output_type == :ordered_list) or
|
150
|
+
(@output_type == :unordered_list))
|
151
|
+
true
|
152
|
+
end
|
153
|
+
end # class OutputBuffer
|
154
|
+
end # module Orgmode
|
@@ -0,0 +1,72 @@
|
|
1
|
+
##
|
2
|
+
## Simple routines for loading / saving an ORG file.
|
3
|
+
##
|
4
|
+
|
5
|
+
module Orgmode
|
6
|
+
|
7
|
+
class Parser
|
8
|
+
|
9
|
+
# All of the lines of the orgmode file
|
10
|
+
attr_reader :lines
|
11
|
+
|
12
|
+
# All of the headlines in the org file
|
13
|
+
attr_reader :headlines
|
14
|
+
|
15
|
+
# These are any lines before the first headline
|
16
|
+
attr_reader :header_lines
|
17
|
+
|
18
|
+
# I can construct a parser object either with an array of lines
|
19
|
+
# or with a single string that I will split along \n boundaries.
|
20
|
+
def initialize(lines)
|
21
|
+
if lines.is_a? Array then
|
22
|
+
@lines = lines
|
23
|
+
elsif lines.is_a? String then
|
24
|
+
@lines = lines.split("\n")
|
25
|
+
else
|
26
|
+
raise "Unsupported type for +lines+: #{lines.class}"
|
27
|
+
end
|
28
|
+
|
29
|
+
@headlines = Array.new
|
30
|
+
@current_headline = nil
|
31
|
+
@header_lines = []
|
32
|
+
@lines.each do |line|
|
33
|
+
if (Headline.headline? line) then
|
34
|
+
@current_headline = Headline.new line
|
35
|
+
@headlines << @current_headline
|
36
|
+
else
|
37
|
+
line = Line.new line
|
38
|
+
if (@current_headline) then
|
39
|
+
@current_headline.body_lines << line
|
40
|
+
else
|
41
|
+
@header_lines << line
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end # initialize
|
46
|
+
|
47
|
+
# Creates a new parser from the data in a given file
|
48
|
+
def self.load(fname)
|
49
|
+
lines = IO.readlines(fname)
|
50
|
+
return self.new(lines)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Saves the loaded orgmode file as a textile file.
|
54
|
+
def to_textile
|
55
|
+
output = ""
|
56
|
+
output << Line.to_textile(@header_lines)
|
57
|
+
@headlines.each do |headline|
|
58
|
+
output << headline.to_textile
|
59
|
+
end
|
60
|
+
output
|
61
|
+
end
|
62
|
+
|
63
|
+
def to_html
|
64
|
+
output = ""
|
65
|
+
output << Line.to_html(@header_lines)
|
66
|
+
@headlines.each do |headline|
|
67
|
+
output << headline.to_html
|
68
|
+
end
|
69
|
+
output
|
70
|
+
end
|
71
|
+
end # class Parser
|
72
|
+
end # module Orgmode
|
@@ -0,0 +1,156 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module Orgmode
|
4
|
+
|
5
|
+
# = Summary
|
6
|
+
#
|
7
|
+
# This class contains helper routines to deal with the Regexp "black
|
8
|
+
# magic" you need to properly parse org-mode files.
|
9
|
+
#
|
10
|
+
# = Key methods
|
11
|
+
#
|
12
|
+
# * Use +rewrite_emphasis+ to replace org-mode emphasis strings (e.g.,
|
13
|
+
# \/italic/) with the suitable markup for the output.
|
14
|
+
#
|
15
|
+
# * Use +rewrite_links+ to get a chance to rewrite all org-mode
|
16
|
+
# links with suitable markup for the output.
|
17
|
+
class RegexpHelper
|
18
|
+
|
19
|
+
######################################################################
|
20
|
+
# EMPHASIS
|
21
|
+
#
|
22
|
+
# I figure it's best to stick as closely to the elisp implementation
|
23
|
+
# as possible for emphasis. org.el defines the regular expression that
|
24
|
+
# is used to apply "emphasis" (in my terminology, inline formatting
|
25
|
+
# instead of block formatting). Here's the documentation from org.el.
|
26
|
+
#
|
27
|
+
# Terminology: In an emphasis string like " *strong word* ", we
|
28
|
+
# call the initial space PREMATCH, the final space POSTMATCH, the
|
29
|
+
# stars MARKERS, "s" and "d" are BORDER characters and "trong wor"
|
30
|
+
# is the body. The different components in this variable specify
|
31
|
+
# what is allowed/forbidden in each part:
|
32
|
+
#
|
33
|
+
# pre Chars allowed as prematch. Line beginning allowed, too.
|
34
|
+
# post Chars allowed as postmatch. Line end will be allowed too.
|
35
|
+
# border The chars *forbidden* as border characters.
|
36
|
+
# body-regexp A regexp like \".\" to match a body character. Don't use
|
37
|
+
# non-shy groups here, and don't allow newline here.
|
38
|
+
# newline The maximum number of newlines allowed in an emphasis exp.
|
39
|
+
#
|
40
|
+
# I currently don't use +newline+ because I've thrown this information
|
41
|
+
# away by this point in the code. TODO -- revisit?
|
42
|
+
attr_reader :pre_emphasis
|
43
|
+
attr_reader :post_emphasis
|
44
|
+
attr_reader :border_forbidden
|
45
|
+
attr_reader :body_regexp
|
46
|
+
attr_reader :markers
|
47
|
+
|
48
|
+
attr_reader :org_emphasis_regexp
|
49
|
+
|
50
|
+
def initialize
|
51
|
+
# Set up the emphasis regular expression.
|
52
|
+
@pre_emphasis = " \t\\('\""
|
53
|
+
@post_emphasis = "- \t.,:!?;'\"\\)"
|
54
|
+
@border_forbidden = " \t\r\n,\"'"
|
55
|
+
@body_regexp = ".*?"
|
56
|
+
@markers = "*/_=~+"
|
57
|
+
@logger = Logger.new(STDERR)
|
58
|
+
@logger.level = Logger::WARN
|
59
|
+
build_org_emphasis_regexp
|
60
|
+
build_org_link_regexp
|
61
|
+
end
|
62
|
+
|
63
|
+
# Finds all emphasis matches in a string.
|
64
|
+
# Supply a block that will get the marker and body as parameters.
|
65
|
+
def match_all(str)
|
66
|
+
str.scan(@org_emphasis_regexp) do |match|
|
67
|
+
yield $2, $3
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Compute replacements for all matching emphasized phrases.
|
72
|
+
# Supply a block that will get the marker and body as parameters;
|
73
|
+
# return the replacement string from your block.
|
74
|
+
#
|
75
|
+
# = Example
|
76
|
+
#
|
77
|
+
# re = RegexpHelper.new
|
78
|
+
# result = re.rewrite_emphasis("*bold*, /italic/, =code=") do |marker, body|
|
79
|
+
# "<#{map[marker]}>#{body}</#{map[marker]}>"
|
80
|
+
# end
|
81
|
+
#
|
82
|
+
# In this example, the block body will get called three times:
|
83
|
+
#
|
84
|
+
# 1. Marker: "*", body: "bold"
|
85
|
+
# 2. Marker: "/", body: "italic"
|
86
|
+
# 3. Marker: "=", body: "code"
|
87
|
+
#
|
88
|
+
# The return from this block is a string that will be used to
|
89
|
+
# replace "*bold*", "/italic/", and "=code=",
|
90
|
+
# respectively. (Clearly this sample string will use HTML-like
|
91
|
+
# syntax, assuming +map+ is defined appropriately.)
|
92
|
+
def rewrite_emphasis(str)
|
93
|
+
str.gsub(@org_emphasis_regexp) do |match|
|
94
|
+
inner = yield $2, $3
|
95
|
+
"#{$1}#{inner}#{$4}"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# = Summary
|
100
|
+
#
|
101
|
+
# Rewrite org-mode links in a string to markup suitable to the
|
102
|
+
# output format.
|
103
|
+
#
|
104
|
+
# = Usage
|
105
|
+
#
|
106
|
+
# Give this a block that expect the link and optional friendly
|
107
|
+
# text. Return how that link should get formatted.
|
108
|
+
#
|
109
|
+
# = Example
|
110
|
+
#
|
111
|
+
# re = RegexpHelper.new
|
112
|
+
# result = re.rewrite_links("[[http://www.bing.com]] and [[http://www.hotmail.com][Hotmail]]") do |link, text}
|
113
|
+
# text ||= link
|
114
|
+
# "<a href=\"#{link}\">#{text}</a>"
|
115
|
+
# end
|
116
|
+
#
|
117
|
+
# In this example, the block body will get called two times. In the
|
118
|
+
# first instance, +text+ will be nil (the org-mode markup gives no
|
119
|
+
# friendly text for the link +http://www.bing.com+. In the second
|
120
|
+
# instance, the block will get text of *Hotmail* and the link
|
121
|
+
# +http://www.hotmail.com+. In both cases, the block returns an
|
122
|
+
# HTML-style link, and that is how things will get recorded in
|
123
|
+
# +result+.
|
124
|
+
def rewrite_links(str)
|
125
|
+
i = str.gsub(@org_link_regexp) do |match|
|
126
|
+
yield $1, nil
|
127
|
+
end
|
128
|
+
i.gsub(@org_link_text_regexp) do |match|
|
129
|
+
yield $1, $2
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
private
|
134
|
+
|
135
|
+
def build_org_emphasis_regexp
|
136
|
+
@org_emphasis_regexp = Regexp.new("([#{@pre_emphasis}]|^)\n" +
|
137
|
+
"( [#{@markers}] )\n" +
|
138
|
+
"( [^#{@border_forbidden}] | " +
|
139
|
+
" [^#{@border_forbidden}]#{@body_regexp}[^#{@border_forbidden}] )\n" +
|
140
|
+
"\\2\n" +
|
141
|
+
"([#{@post_emphasis}]|$)\n", Regexp::EXTENDED)
|
142
|
+
@logger.debug "Just created regexp: #{@org_emphasis_regexp}"
|
143
|
+
end
|
144
|
+
|
145
|
+
def build_org_link_regexp
|
146
|
+
@org_link_regexp = /\[\[
|
147
|
+
([^\]]*) # This is the URL
|
148
|
+
\]\]/x
|
149
|
+
@org_link_text_regexp = /\[\[
|
150
|
+
([^\]]*) # This is the URL
|
151
|
+
\]\[
|
152
|
+
([^\]]*) # This is the friendly text
|
153
|
+
\]\]/x
|
154
|
+
end
|
155
|
+
end # class Emphasis
|
156
|
+
end # module Orgmode
|