livetext 0.9.52 → 0.9.56
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.
- checksums.yaml +4 -4
- data/imports/bookish.rb +3 -3
- data/lib/livetext/ast/show_ast_clean.rb +10 -0
- data/lib/livetext/ast/show_ast_result.rb +60 -0
- data/lib/livetext/ast/show_raw_arrays.rb +13 -0
- data/lib/livetext/ast.rb +464 -0
- data/lib/livetext/ast_to_html.rb +32 -0
- data/lib/livetext/core.rb +110 -53
- data/lib/livetext/errors.rb +1 -0
- data/lib/livetext/expansion.rb +21 -21
- data/lib/livetext/formatter.rb +70 -200
- data/lib/livetext/formatter_component.rb +189 -0
- data/lib/livetext/function_registry.rb +163 -0
- data/lib/livetext/functions.rb +26 -0
- data/lib/livetext/handler/mixin.rb +53 -0
- data/lib/livetext/helpers.rb +33 -16
- data/lib/livetext/reopen.rb +2 -0
- data/lib/livetext/skeleton.rb +0 -3
- data/lib/livetext/standard.rb +120 -72
- data/lib/livetext/userapi.rb +20 -1
- data/lib/livetext/variable_manager.rb +78 -0
- data/lib/livetext/variables.rb +9 -1
- data/lib/livetext/version.rb +1 -1
- data/lib/livetext.rb +9 -3
- data/plugin/booktool.rb +14 -14
- data/plugin/lt3scriptor.rb +914 -0
- data/plugin/mixin_functions_class.rb +33 -0
- data/test/snapshots/complex_body/expected-error.txt +0 -0
- data/test/snapshots/complex_body/expected-output.txt +8 -0
- data/test/snapshots/complex_body/source.lt3 +19 -0
- data/test/snapshots/debug_command/expected-error.txt +0 -0
- data/test/snapshots/debug_command/expected-output.txt +1 -0
- data/test/snapshots/debug_command/source.lt3 +3 -0
- data/test/snapshots/def_parameters/expected-error.txt +0 -0
- data/test/snapshots/def_parameters/expected-output.txt +21 -0
- data/test/snapshots/def_parameters/source.lt3 +44 -0
- data/test/snapshots/error_missing_end/match-error.txt +1 -1
- data/test/snapshots/functions_reflection/expected-error.txt +0 -0
- data/test/snapshots/functions_reflection/expected-output.txt +27 -0
- data/test/snapshots/functions_reflection/source.lt3 +5 -0
- data/test/snapshots/mixin_functions_class/expected-error.txt +0 -0
- data/test/snapshots/mixin_functions_class/expected-output.txt +20 -0
- data/test/snapshots/mixin_functions_class/mixin_functions_class.rb +33 -0
- data/test/snapshots/mixin_functions_class/source.lt3 +17 -0
- data/test/snapshots/multiple_functions/expected-error.txt +0 -0
- data/test/snapshots/multiple_functions/expected-output.txt +5 -0
- data/test/snapshots/multiple_functions/source.lt3 +16 -0
- data/test/snapshots/nested_includes/expected-error.txt +0 -0
- data/test/snapshots/nested_includes/expected-output.txt +68 -0
- data/test/snapshots/nested_includes/level2.inc +34 -0
- data/test/snapshots/nested_includes/level3.inc +20 -0
- data/test/snapshots/nested_includes/source.lt3 +49 -0
- data/test/snapshots/parameter_handling/expected-error.txt +0 -0
- data/test/snapshots/parameter_handling/expected-output.txt +7 -0
- data/test/snapshots/parameter_handling/source.lt3 +10 -0
- data/test/snapshots/subset.txt +1 -0
- data/test/snapshots/system_info/expected-error.txt +0 -0
- data/test/snapshots/system_info/match-output.txt +18 -0
- data/test/snapshots/system_info/source.lt3 +16 -0
- data/test/unit/all.rb +7 -0
- data/test/unit/ast.rb +90 -0
- data/test/unit/ast_directives.rb +104 -0
- data/test/unit/ast_variables.rb +71 -0
- data/test/unit/core_methods.rb +317 -0
- data/test/unit/formatter.rb +84 -0
- data/test/unit/formatter_component.rb +84 -0
- data/test/unit/function_registry.rb +132 -0
- data/test/unit/mixin_functions_class.rb +131 -0
- data/test/unit/stringparser.rb +14 -32
- data/test/unit/variable_manager.rb +71 -0
- metadata +51 -5
- data/imports/markdown.rb +0 -44
- data/lib/livetext/processor.rb +0 -88
- data/plugin/markdown.rb +0 -43
data/lib/livetext/core.rb
CHANGED
@@ -8,7 +8,23 @@ class Livetext
|
|
8
8
|
|
9
9
|
TTY = ::File.open("/dev/tty", "w")
|
10
10
|
|
11
|
-
|
11
|
+
Disallowed =
|
12
|
+
%i[ __binding__ __id__ __send__ class
|
13
|
+
clone display dup enum_for
|
14
|
+
eql? equal? extend freeze
|
15
|
+
frozen? hash inspect instance_eval
|
16
|
+
instance_exec instance_of? is_a? kind_of?
|
17
|
+
method methods nil? object_id
|
18
|
+
pretty_inspect private_methods protected_methods public_method
|
19
|
+
public_methods public_send respond_to? send
|
20
|
+
singleton_class singleton_method singleton_methods taint
|
21
|
+
tainted? tap to_enum to_s
|
22
|
+
trust untaint untrust untrusted?
|
23
|
+
define_singleton_method instance_variable_defined?
|
24
|
+
instance_variable_get instance_variable_set
|
25
|
+
remove_instance_variable instance_variables ]
|
26
|
+
|
27
|
+
attr_reader :sources, :function_registry, :variables, :formatter
|
12
28
|
attr_accessor :nopass, :nopara
|
13
29
|
attr_accessor :body, :indentation
|
14
30
|
|
@@ -17,7 +33,7 @@ class Livetext
|
|
17
33
|
end
|
18
34
|
|
19
35
|
def vars
|
20
|
-
@
|
36
|
+
@variables
|
21
37
|
end
|
22
38
|
|
23
39
|
def self.interpolate(str)
|
@@ -27,16 +43,10 @@ class Livetext
|
|
27
43
|
str3
|
28
44
|
end
|
29
45
|
|
30
|
-
def peek_nextline
|
31
|
-
@main.peek_nextline # delegate
|
32
|
-
end
|
33
46
|
|
34
|
-
def nextline
|
35
|
-
@main.nextline # delegate
|
36
|
-
end
|
37
47
|
|
38
48
|
def sources
|
39
|
-
@
|
49
|
+
@sources # delegate
|
40
50
|
end
|
41
51
|
|
42
52
|
def save_location
|
@@ -48,18 +58,15 @@ class Livetext
|
|
48
58
|
end
|
49
59
|
|
50
60
|
def initialize(output = ::STDOUT) # Livetext
|
51
|
-
@source = nil
|
52
|
-
@_mixins = []
|
53
|
-
@_imports = []
|
54
|
-
@_outdir = "."
|
55
|
-
@no_puts = output.nil?
|
56
61
|
@body = ""
|
57
|
-
@main = Processor.new(self, output) # nil = make @main its own parent??
|
58
|
-
@parent = @main
|
59
62
|
@indentation = [0]
|
60
|
-
@_vars = Livetext::Vars
|
61
63
|
@api = UserAPI.new(self)
|
62
|
-
|
64
|
+
@output = ::Livetext.output = output
|
65
|
+
@html = Livetext::HTML.new(@api)
|
66
|
+
@sources = []
|
67
|
+
@function_registry = Livetext::FunctionRegistry.new
|
68
|
+
@variables = Livetext::VariableManager.new(self)
|
69
|
+
@formatter = Livetext::Formatter.new(self)
|
63
70
|
# puts "------ init: self = "
|
64
71
|
# p self
|
65
72
|
end
|
@@ -71,8 +78,10 @@ class Livetext
|
|
71
78
|
mix.each do |lib|
|
72
79
|
obj.invoke_dotcmd(:mixin, lib.dup)
|
73
80
|
end
|
74
|
-
call.each {|cmd| obj.
|
81
|
+
call.each {|cmd| obj.handle_dotcmd(cmd) } # Use handle_dotcmd for proper command parsing
|
75
82
|
obj.api.setvars(vars)
|
83
|
+
# Also set variables in global Livetext::Vars for backward compatibility
|
84
|
+
vars.each {|var, val| Vars[var.to_sym] = val.to_s }
|
76
85
|
obj
|
77
86
|
end
|
78
87
|
|
@@ -80,22 +89,19 @@ class Livetext
|
|
80
89
|
mix = Array(mix)
|
81
90
|
call = Array(call)
|
82
91
|
mix.each {|lib| mixin(lib) }
|
83
|
-
call.each {|cmd|
|
92
|
+
call.each {|cmd| handle_dotcmd(cmd) } # Use handle_dotcmd for proper command parsing
|
84
93
|
# vars.each_pair {|var, val| @api.set(var, val.to_s) }
|
85
94
|
api.setvars(vars)
|
95
|
+
# Also set variables in global Livetext::Vars for backward compatibility
|
96
|
+
vars.each {|var, val| Vars[var.to_sym] = val.to_s }
|
86
97
|
self
|
87
98
|
end
|
88
99
|
|
89
|
-
|
100
|
+
def inspect
|
90
101
|
api_abbr = @api ? "(non-nil)" : "(not shown)"
|
91
|
-
main_abbr = @main ? "(non-nil)" : "(not shown)"
|
92
102
|
"Livetext:\n" +
|
93
|
-
" source = #{@source.inspect}\n" +
|
94
|
-
" mixins = #{@_mixins.inspect}\n" +
|
95
|
-
" import = #{@_mixins.inspect}\n" +
|
96
|
-
" main = #{main_abbr}\n" +
|
97
103
|
" indent = #{@indentation.inspect}\n" +
|
98
|
-
" vars = #{@
|
104
|
+
" vars = #{@variables.inspect}\n" +
|
99
105
|
" api = #{api_abbr}\n" +
|
100
106
|
" body = (#{@body.size} bytes)"
|
101
107
|
end
|
@@ -104,23 +110,81 @@ class Livetext
|
|
104
110
|
@api
|
105
111
|
end
|
106
112
|
|
113
|
+
def error(*args)
|
114
|
+
::STDERR.puts *args
|
115
|
+
end
|
116
|
+
|
117
|
+
def disallowed?(name)
|
118
|
+
flag = Disallowed.include?(name.to_sym)
|
119
|
+
flag
|
120
|
+
end
|
121
|
+
|
122
|
+
def output=(io)
|
123
|
+
@output = io
|
124
|
+
end
|
125
|
+
|
126
|
+
def html
|
127
|
+
@html
|
128
|
+
end
|
129
|
+
|
130
|
+
def source(enum, file, line)
|
131
|
+
@sources.push([enum, file, line])
|
132
|
+
end
|
133
|
+
|
134
|
+
def peek_nextline
|
135
|
+
return nil if @sources.empty?
|
136
|
+
source = @sources.last
|
137
|
+
line = source[0].peek
|
138
|
+
line
|
139
|
+
rescue StopIteration
|
140
|
+
@sources.pop
|
141
|
+
nil
|
142
|
+
rescue => err
|
143
|
+
TTY.puts "#{__method__}: RESCUE err = #{err.inspect}"
|
144
|
+
nil
|
145
|
+
end
|
146
|
+
|
147
|
+
def nextline
|
148
|
+
return nil if @sources.empty?
|
149
|
+
line = @sources.last[0].next
|
150
|
+
@sources.last[2] += 1
|
151
|
+
line
|
152
|
+
rescue StopIteration
|
153
|
+
@sources.pop
|
154
|
+
nil
|
155
|
+
end
|
156
|
+
|
107
157
|
def api=(obj)
|
108
158
|
@api = obj
|
109
159
|
end
|
110
160
|
|
111
|
-
def
|
112
|
-
#
|
113
|
-
@
|
114
|
-
|
161
|
+
def process(text: nil, file: nil, vars: {})
|
162
|
+
# Set variables first
|
163
|
+
@variables.set_multiple(vars) unless vars.empty?
|
164
|
+
|
165
|
+
# Process based on input type
|
166
|
+
case
|
167
|
+
when file && text.nil?
|
168
|
+
process_file(file)
|
169
|
+
when file.nil? && text
|
170
|
+
transform_text(text)
|
171
|
+
when file.nil? && text.nil?
|
172
|
+
raise "Must specify file or text"
|
173
|
+
when file && text
|
174
|
+
raise "Cannot specify file and text"
|
175
|
+
end
|
176
|
+
|
177
|
+
[self.body, @variables.to_h]
|
115
178
|
end
|
116
179
|
|
117
|
-
|
180
|
+
# Keep transform for backward compatibility, but make it private
|
181
|
+
private def transform_text(text)
|
118
182
|
setfile!("(string)")
|
119
183
|
enum = text.each_line
|
120
184
|
front = text.match(/.*?\n/).to_a.first.chomp rescue ""
|
121
|
-
|
185
|
+
source(enum, "STDIN: '#{front}...'", 0)
|
122
186
|
loop do
|
123
|
-
line =
|
187
|
+
line = nextline
|
124
188
|
break if line.nil?
|
125
189
|
process_line(line)
|
126
190
|
end
|
@@ -129,29 +193,22 @@ class Livetext
|
|
129
193
|
result
|
130
194
|
end
|
131
195
|
|
132
|
-
#
|
196
|
+
# Keep for backward compatibility
|
197
|
+
def transform(text)
|
198
|
+
transform_text(text)
|
199
|
+
end
|
200
|
+
|
201
|
+
# Keep for backward compatibility
|
133
202
|
def xform(*args, file: nil, text: nil, vars: {})
|
134
|
-
|
135
|
-
|
136
|
-
xform_file(file)
|
137
|
-
when file.nil? && text
|
138
|
-
transform(text)
|
139
|
-
when file.nil? && text.nil?
|
140
|
-
raise "Must specify file or text"
|
141
|
-
when file && text
|
142
|
-
raise "Cannot specify file and text"
|
143
|
-
end
|
144
|
-
self.process_file(file)
|
145
|
-
self.body
|
203
|
+
body, _vars = process(file: file, text: text, vars: vars)
|
204
|
+
body
|
146
205
|
end
|
147
206
|
|
207
|
+
# Keep for backward compatibility
|
148
208
|
def xform_file(file, vars: nil)
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
self.process_file(file)
|
153
|
-
# checkpoint! "...returned"
|
154
|
-
self.body
|
209
|
+
vars_hash = vars.nil? ? {} : vars
|
210
|
+
body, _vars = process(file: file, vars: vars_hash)
|
211
|
+
body
|
155
212
|
end
|
156
213
|
|
157
214
|
end
|
data/lib/livetext/errors.rb
CHANGED
@@ -67,6 +67,7 @@ end
|
|
67
67
|
make_exception(:EndWithoutOpening, "Error: found .end with no opening command")
|
68
68
|
make_exception(:UnknownMethod, "Error: name '%1' is unknown")
|
69
69
|
make_exception(:NoSuchFile, "Error: can't find file '%1' (method '%2')")
|
70
|
+
make_exception(:ExpectedEnd, "Error: expected .end but found end of file")
|
70
71
|
|
71
72
|
# Move others here? DisallowedName, etc.
|
72
73
|
|
data/lib/livetext/expansion.rb
CHANGED
@@ -8,8 +8,6 @@ class Livetext::Expansion
|
|
8
8
|
Lbrack = "\\["
|
9
9
|
Colon = ":"
|
10
10
|
|
11
|
-
Formatter = ::Livetext::Formatter
|
12
|
-
|
13
11
|
def initialize(instance) # Livetext::Expansion
|
14
12
|
@live = instance
|
15
13
|
end
|
@@ -18,7 +16,7 @@ class Livetext::Expansion
|
|
18
16
|
return "" if line == "\n" || line.nil?
|
19
17
|
with_vars = expand_variables(line)
|
20
18
|
with_func = expand_function_calls(with_vars)
|
21
|
-
formatted =
|
19
|
+
formatted = @live.formatter.format(with_func)
|
22
20
|
end
|
23
21
|
|
24
22
|
def expand_variables(str)
|
@@ -53,28 +51,30 @@ class Livetext::Expansion
|
|
53
51
|
end
|
54
52
|
|
55
53
|
def funcall(name, param)
|
56
|
-
|
54
|
+
# Use the unified function registry
|
57
55
|
name = name.gsub(/\./, "__")
|
56
|
+
result = @live.function_registry.call(name, param)
|
58
57
|
|
59
|
-
#
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
58
|
+
# If not found in registry, fall back to old system for backward compatibility
|
59
|
+
if result.start_with?("[Error evaluating $$#{name}(")
|
60
|
+
# Try old Livetext::Functions system
|
61
|
+
fobj = ::Livetext::Functions.new
|
62
|
+
old_result = fobj.send(name, param) rescue nil
|
63
|
+
return old_result.to_s if old_result
|
64
|
+
|
65
|
+
# Try Livetext instance (for mixin functions)
|
66
|
+
if @live.respond_to?(name)
|
67
|
+
method = @live.method(name)
|
68
|
+
if method.parameters.empty?
|
69
|
+
old_result = @live.send(name) rescue nil
|
70
|
+
else
|
71
|
+
old_result = @live.send(name, param) rescue nil
|
72
|
+
end
|
73
|
+
return old_result.to_s if old_result
|
72
74
|
end
|
73
|
-
return result.to_s
|
74
75
|
end
|
75
76
|
|
76
|
-
|
77
|
-
err
|
77
|
+
result
|
78
78
|
end
|
79
79
|
|
80
80
|
def expand_function_calls(str)
|
@@ -85,7 +85,7 @@ class Livetext::Expansion
|
|
85
85
|
rbrack = "\\]"
|
86
86
|
space_eol = "( |$)"
|
87
87
|
prx1 = "(?<param>[^ ]+)"
|
88
|
-
prx2 = "(?<param
|
88
|
+
prx2 = "(?<param>.*)"
|
89
89
|
pat2 = "(?<full_param>#{colon}#{prx1})"
|
90
90
|
pat3 = "(?<full_param>#{lbrack}#{prx2}#{rbrack})"
|
91
91
|
rx = Regexp.compile("#{pat1}(#{pat2}|#{pat3})?")
|
data/lib/livetext/formatter.rb
CHANGED
@@ -1,35 +1,84 @@
|
|
1
|
-
|
1
|
+
# Formatter - Centralized text formatting for Livetext
|
2
|
+
class Livetext::Formatter
|
3
|
+
def initialize(parent)
|
4
|
+
@parent = parent
|
5
|
+
end
|
2
6
|
|
3
|
-
def
|
4
|
-
|
7
|
+
def format(text)
|
8
|
+
return "" if text.nil? || text.empty?
|
9
|
+
|
10
|
+
text = text.chomp
|
5
11
|
# First, mark escaped characters so they won't be processed as formatting
|
6
|
-
|
12
|
+
text = mark_escaped_characters(text)
|
7
13
|
|
8
|
-
# Process all marker types in sequence
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
14
|
+
# Process all marker types in sequence
|
15
|
+
text = handle_double_markers(text)
|
16
|
+
text = handle_bracketed_markers(text)
|
17
|
+
text = handle_single_markers(text)
|
18
|
+
text = handle_underscore_markers(text)
|
19
|
+
text = handle_backtick_markers(text)
|
20
|
+
text = handle_tilde_markers(text)
|
15
21
|
|
16
|
-
|
17
|
-
|
22
|
+
text = unmark_escaped_characters(text)
|
23
|
+
text
|
24
|
+
end
|
25
|
+
|
26
|
+
def format_line(line)
|
27
|
+
return "" if line.nil?
|
28
|
+
format(line.chomp)
|
29
|
+
end
|
30
|
+
|
31
|
+
def format_multiple(lines)
|
32
|
+
lines.map { |line| format_line(line) }
|
33
|
+
end
|
34
|
+
|
35
|
+
# Convenience methods for common formatting patterns
|
36
|
+
def bold(text)
|
37
|
+
"<b>#{text}</b>"
|
38
|
+
end
|
39
|
+
|
40
|
+
def italic(text)
|
41
|
+
"<i>#{text}</i>"
|
42
|
+
end
|
43
|
+
|
44
|
+
def code(text)
|
45
|
+
"<tt>#{text}</tt>"
|
46
|
+
end
|
47
|
+
|
48
|
+
def strike(text)
|
49
|
+
"<strike>#{text}</strike>"
|
50
|
+
end
|
51
|
+
|
52
|
+
def link(text, url)
|
53
|
+
"<a href='#{url}'>#{text}</a>"
|
54
|
+
end
|
55
|
+
|
56
|
+
def escape_html(text)
|
57
|
+
text.gsub(/[&<>"']/) do |char|
|
58
|
+
case char
|
59
|
+
when '&' then '&'
|
60
|
+
when '<' then '<'
|
61
|
+
when '>' then '>'
|
62
|
+
when '"' then '"'
|
63
|
+
when "'" then '''
|
64
|
+
else char
|
65
|
+
end
|
66
|
+
end
|
18
67
|
end
|
19
68
|
|
20
69
|
private
|
21
70
|
|
22
|
-
def
|
71
|
+
def mark_escaped_characters(str)
|
23
72
|
# Replace escaped characters with a null byte marker (safe for internal use)
|
24
73
|
str.gsub(/\\([*_`~])/, "\u0000\\1")
|
25
74
|
end
|
26
75
|
|
27
|
-
def
|
76
|
+
def unmark_escaped_characters(str)
|
28
77
|
# Restore escaped characters
|
29
78
|
str.gsub(/\u0000([*_`~])/, '\1')
|
30
79
|
end
|
31
80
|
|
32
|
-
def
|
81
|
+
def handle_double_markers(str)
|
33
82
|
# **word -> <b>word</b> (terminated by space, comma, period)
|
34
83
|
# But ignore standalone ** or ** surrounded by spaces
|
35
84
|
str.gsub(/(?<=\s|^)\*\*([^\s,.]*)/) do |match|
|
@@ -41,7 +90,7 @@ module Livetext::Formatter
|
|
41
90
|
end
|
42
91
|
end
|
43
92
|
|
44
|
-
def
|
93
|
+
def handle_bracketed_markers(str)
|
45
94
|
# Handle all bracketed markers: *[content], _[content], `[content]
|
46
95
|
# *[content] -> <b>content</b>
|
47
96
|
# _[content] -> <i>content</i>
|
@@ -85,7 +134,7 @@ module Livetext::Formatter
|
|
85
134
|
str
|
86
135
|
end
|
87
136
|
|
88
|
-
def
|
137
|
+
def handle_single_markers(str)
|
89
138
|
# *word -> <b>word</b> (only at start of word or after space)
|
90
139
|
# But ignore standalone * or * surrounded by spaces
|
91
140
|
# Also ignore * that are part of ** patterns (already processed)
|
@@ -102,7 +151,7 @@ module Livetext::Formatter
|
|
102
151
|
end
|
103
152
|
end
|
104
153
|
|
105
|
-
def
|
154
|
+
def handle_underscore_markers(str)
|
106
155
|
# _word -> <i>word</i> (only at start of word or after space)
|
107
156
|
# But ignore standalone _ or _ surrounded by spaces
|
108
157
|
str.gsub(/(?<=\s|^)_([^\s]*)/) do |match|
|
@@ -114,7 +163,7 @@ module Livetext::Formatter
|
|
114
163
|
end
|
115
164
|
end
|
116
165
|
|
117
|
-
def
|
166
|
+
def handle_backtick_markers(str)
|
118
167
|
# `word -> <tt>word</tt> (only at start of word or after space)
|
119
168
|
# But ignore standalone ` or ` surrounded by spaces
|
120
169
|
str.gsub(/(?<=\s|^)`([^\s]*)/) do |match|
|
@@ -126,7 +175,7 @@ module Livetext::Formatter
|
|
126
175
|
end
|
127
176
|
end
|
128
177
|
|
129
|
-
def
|
178
|
+
def handle_tilde_markers(str)
|
130
179
|
# ~word -> <strike>word</strike> (only at start of word or after space)
|
131
180
|
# But ignore standalone ~ or ~ surrounded by spaces
|
132
181
|
str.gsub(/(?<=\s|^)~([^\s]*)/) do |match|
|
@@ -137,183 +186,4 @@ module Livetext::Formatter
|
|
137
186
|
end
|
138
187
|
end
|
139
188
|
end
|
140
|
-
|
141
189
|
end
|
142
|
-
|
143
|
-
# Legacy classes - kept for compatibility but not used
|
144
|
-
class Livetext::Formatter::Delimited
|
145
|
-
def initialize(str, marker, tag)
|
146
|
-
@str, @marker, @tag = str.dup, marker, tag
|
147
|
-
@buffer = ""
|
148
|
-
@cdata = ""
|
149
|
-
@state = :INITIAL
|
150
|
-
end
|
151
|
-
|
152
|
-
def status(where)
|
153
|
-
if $debug
|
154
|
-
STDERR.printf "%-11s %-7s #{@marker.inspect} \n #{' '*11} state = %-8s str = %-20s buffer = %-20s cdata = %-20s\n",
|
155
|
-
where, self.class, @state, @str.inspect, @buffer.inspect, @cdata.inspect
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
def front
|
160
|
-
@str[0]
|
161
|
-
end
|
162
|
-
|
163
|
-
def grab(n=1)
|
164
|
-
char = @str.slice!(0..(n-1))
|
165
|
-
char
|
166
|
-
end
|
167
|
-
|
168
|
-
def grab_terminator
|
169
|
-
@state = :LOOPING
|
170
|
-
end
|
171
|
-
|
172
|
-
def eol?
|
173
|
-
@str.empty?
|
174
|
-
end
|
175
|
-
|
176
|
-
def space?
|
177
|
-
front == " "
|
178
|
-
end
|
179
|
-
|
180
|
-
def escape?
|
181
|
-
front == "\\"
|
182
|
-
end
|
183
|
-
|
184
|
-
def terminated?
|
185
|
-
space?
|
186
|
-
end
|
187
|
-
|
188
|
-
def marker?
|
189
|
-
@str.start_with?(@marker)
|
190
|
-
end
|
191
|
-
|
192
|
-
def space_marker?
|
193
|
-
@str.start_with?(" " + @marker)
|
194
|
-
end
|
195
|
-
|
196
|
-
def wrap(text)
|
197
|
-
if text.empty?
|
198
|
-
result = @marker
|
199
|
-
result = "" if @marker[1] == "["
|
200
|
-
return result
|
201
|
-
end
|
202
|
-
"<#{@tag}>#{text}</#{@tag}>"
|
203
|
-
end
|
204
|
-
|
205
|
-
def initial
|
206
|
-
n = @marker.length
|
207
|
-
case
|
208
|
-
when escape?
|
209
|
-
grab
|
210
|
-
@buffer << grab
|
211
|
-
when space_marker?
|
212
|
-
@buffer << grab
|
213
|
-
grab(n)
|
214
|
-
@state = :CDATA
|
215
|
-
when marker?
|
216
|
-
grab(n)
|
217
|
-
@state = :CDATA
|
218
|
-
when eol?
|
219
|
-
@state = :FINAL
|
220
|
-
else
|
221
|
-
@state = :BUFFER
|
222
|
-
end
|
223
|
-
end
|
224
|
-
|
225
|
-
def buffer
|
226
|
-
@buffer << grab
|
227
|
-
@state = :LOOPING
|
228
|
-
end
|
229
|
-
|
230
|
-
def cdata
|
231
|
-
case
|
232
|
-
when eol?
|
233
|
-
if @cdata.empty?
|
234
|
-
@buffer << @marker unless @marker[1] == "["
|
235
|
-
else
|
236
|
-
@buffer << wrap(@cdata)
|
237
|
-
end
|
238
|
-
@state = :FINAL
|
239
|
-
when terminated?
|
240
|
-
@buffer << wrap(@cdata)
|
241
|
-
grab_terminator
|
242
|
-
@cdata = ""
|
243
|
-
@state = :LOOPING
|
244
|
-
else
|
245
|
-
@cdata << grab
|
246
|
-
@state = :CDATA
|
247
|
-
end
|
248
|
-
end
|
249
|
-
|
250
|
-
def looping
|
251
|
-
n = @marker.length
|
252
|
-
case
|
253
|
-
when escape?
|
254
|
-
grab
|
255
|
-
@buffer << grab
|
256
|
-
when space_marker?
|
257
|
-
@buffer << grab
|
258
|
-
grab(n)
|
259
|
-
@state = :CDATA
|
260
|
-
when eol?
|
261
|
-
@state = :FINAL
|
262
|
-
else
|
263
|
-
@buffer << grab
|
264
|
-
end
|
265
|
-
end
|
266
|
-
|
267
|
-
def handle
|
268
|
-
loop do
|
269
|
-
break if @state == :FINAL
|
270
|
-
meth = @state.downcase
|
271
|
-
send(meth)
|
272
|
-
end
|
273
|
-
return @buffer
|
274
|
-
end
|
275
|
-
|
276
|
-
def self.process(str)
|
277
|
-
bold = self.new(str, "*", "b")
|
278
|
-
sb = bold.handle
|
279
|
-
ital = self.new(sb, "_", "i")
|
280
|
-
si = ital.handle
|
281
|
-
code = self.new(si, "`", "tt")
|
282
|
-
sc = code.handle
|
283
|
-
stri = self.new(sc, "~", "strike")
|
284
|
-
si = stri.handle
|
285
|
-
si
|
286
|
-
end
|
287
|
-
end
|
288
|
-
|
289
|
-
class Livetext::Formatter::Single < Livetext::Formatter::Delimited
|
290
|
-
end
|
291
|
-
|
292
|
-
class Livetext::Formatter::Double < Livetext::Formatter::Delimited
|
293
|
-
def initialize(str, sigil, tag)
|
294
|
-
super
|
295
|
-
@marker = sigil + sigil
|
296
|
-
end
|
297
|
-
|
298
|
-
def terminated?
|
299
|
-
terms = [" ", ".", ","]
|
300
|
-
terms.include?(front)
|
301
|
-
end
|
302
|
-
end
|
303
|
-
|
304
|
-
class Livetext::Formatter::Bracketed < Livetext::Formatter::Delimited
|
305
|
-
def initialize(str, sigil, tag)
|
306
|
-
super
|
307
|
-
@marker = sigil + "["
|
308
|
-
end
|
309
|
-
|
310
|
-
def terminated?
|
311
|
-
front == "]" || eol?
|
312
|
-
end
|
313
|
-
|
314
|
-
def grab_terminator
|
315
|
-
@state = :LOOPING
|
316
|
-
grab
|
317
|
-
end
|
318
|
-
end
|
319
|
-
|