gamefic 1.4.1 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/gamefic.rb +1 -2
- data/lib/gamefic/character.rb +31 -45
- data/lib/gamefic/director/delegate.rb +46 -24
- data/lib/gamefic/director/order.rb +7 -0
- data/lib/gamefic/director/parser.rb +11 -11
- data/lib/gamefic/engine/base.rb +2 -3
- data/lib/gamefic/entity.rb +8 -26
- data/lib/gamefic/plot.rb +38 -242
- data/lib/gamefic/plot/callbacks.rb +101 -0
- data/lib/gamefic/plot/command_mount.rb +70 -40
- data/lib/gamefic/plot/entities.rb +82 -0
- data/lib/gamefic/plot/host.rb +46 -0
- data/lib/gamefic/plot/playbook.rb +192 -0
- data/lib/gamefic/plot/players.rb +15 -0
- data/lib/gamefic/plot/scene_mount.rb +69 -31
- data/lib/gamefic/plot/snapshot.rb +20 -5
- data/lib/gamefic/scene/active.rb +8 -1
- data/lib/gamefic/scene/base.rb +4 -26
- data/lib/gamefic/scene/custom.rb +53 -3
- data/lib/gamefic/scene/multiple_choice.rb +1 -0
- data/lib/gamefic/scene/yes_or_no.rb +1 -1
- data/lib/gamefic/scene_data/multiple_scene.rb +0 -4
- data/lib/gamefic/shell.rb +0 -1
- data/lib/gamefic/source/file.rb +1 -1
- data/lib/gamefic/stage.rb +10 -2
- data/lib/gamefic/subplot.rb +70 -53
- data/lib/gamefic/syntax.rb +9 -2
- data/lib/gamefic/tester.rb +1 -1
- data/lib/gamefic/text.rb +8 -0
- data/lib/gamefic/{ansi.rb → text/ansi.rb} +12 -15
- data/lib/gamefic/text/html.rb +68 -0
- data/lib/gamefic/text/html/conversions.rb +250 -0
- data/lib/gamefic/text/html/entities.rb +9 -0
- data/lib/gamefic/tty.rb +2 -0
- data/lib/gamefic/user/tty.rb +2 -4
- data/lib/gamefic/version.rb +1 -1
- metadata +12 -8
- data/lib/gamefic/direction.rb +0 -46
- data/lib/gamefic/html.rb +0 -68
- data/lib/gamefic/html_to_ansi.rb +0 -185
- data/lib/gamefic/plot/entity_mount.rb +0 -45
- data/lib/gamefic/rule.rb +0 -18
@@ -15,7 +15,7 @@ module Gamefic
|
|
15
15
|
if data.yes? or data.no?
|
16
16
|
this_scene = actor.scene
|
17
17
|
do_finish_block actor, data
|
18
|
-
actor.cue :active if (actor.scene == this_scene and actor.next_scene.nil?)
|
18
|
+
#actor.cue :active if (actor.scene == this_scene and actor.next_scene.nil?)
|
19
19
|
else
|
20
20
|
actor.tell data.invalid_message
|
21
21
|
end
|
@@ -1,8 +1,6 @@
|
|
1
1
|
module Gamefic
|
2
2
|
|
3
3
|
class SceneData::MultipleScene < SceneData::MultipleChoice
|
4
|
-
attr_accessor :selection
|
5
|
-
attr_accessor :number
|
6
4
|
def options
|
7
5
|
scene_map.keys
|
8
6
|
end
|
@@ -15,8 +13,6 @@ module Gamefic
|
|
15
13
|
scene_map[choice]
|
16
14
|
end
|
17
15
|
|
18
|
-
private
|
19
|
-
|
20
16
|
def scene_map
|
21
17
|
@scene_map ||= {}
|
22
18
|
end
|
data/lib/gamefic/shell.rb
CHANGED
data/lib/gamefic/source/file.rb
CHANGED
data/lib/gamefic/stage.rb
CHANGED
@@ -3,16 +3,17 @@ module Gamefic
|
|
3
3
|
module Stage
|
4
4
|
def stage *args, &block
|
5
5
|
s = generate_stage
|
6
|
-
|
7
6
|
if block.nil?
|
8
7
|
s.module_eval(*args)
|
9
8
|
else
|
10
9
|
s.module_exec(*args, &block)
|
11
10
|
end
|
12
11
|
end
|
12
|
+
|
13
13
|
private
|
14
|
+
|
14
15
|
def generate_stage
|
15
|
-
return @stage
|
16
|
+
return @stage unless @stage.nil?
|
16
17
|
|
17
18
|
exposed = self.class.exposed_methods.keys
|
18
19
|
mounted = self.class.mounted_modules.keys
|
@@ -44,6 +45,7 @@ module Gamefic
|
|
44
45
|
|
45
46
|
return @stage
|
46
47
|
end
|
48
|
+
|
47
49
|
module ClassMethods
|
48
50
|
def mount *args
|
49
51
|
args.each { |a|
|
@@ -51,22 +53,28 @@ module Gamefic
|
|
51
53
|
mounted_modules[a] = nil
|
52
54
|
}
|
53
55
|
end
|
56
|
+
|
54
57
|
def expose *args
|
55
58
|
args.each { |a|
|
56
59
|
exposed_methods[a] = nil
|
57
60
|
}
|
58
61
|
end
|
62
|
+
|
59
63
|
def exposed_methods
|
60
64
|
@@exposed_methods ||= from_superclass(:exposed_methods, {}).dup
|
61
65
|
end
|
66
|
+
|
62
67
|
def mounted_modules
|
63
68
|
@@mounted_modules ||= from_superclass(:mounted_modules, {}).dup
|
64
69
|
end
|
70
|
+
|
65
71
|
private
|
72
|
+
|
66
73
|
def from_superclass(m, default = nil)
|
67
74
|
superclass.respond_to?(m) ? superclass.send(m) : default
|
68
75
|
end
|
69
76
|
end
|
77
|
+
|
70
78
|
def self.included(base)
|
71
79
|
base.extend(ClassMethods)
|
72
80
|
end
|
data/lib/gamefic/subplot.rb
CHANGED
@@ -1,82 +1,99 @@
|
|
1
|
+
require 'gamefic/plot'
|
2
|
+
|
1
3
|
module Gamefic
|
2
4
|
|
3
5
|
class Subplot
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
#
|
19
|
-
# @return [Array<Subplot>]
|
20
|
-
def subplots
|
21
|
-
p_subplots.clone
|
22
|
-
end
|
23
|
-
|
24
|
-
# Start a new subplot based on the provided class.
|
25
|
-
#
|
26
|
-
# @param [Class] The class of the subplot to be created (Subplot by default)
|
27
|
-
# @return [Subplot]
|
28
|
-
def branch subplot_class, feature:nil, &block
|
29
|
-
subplot = subplot_class.new(self, feature: feature, &block)
|
30
|
-
p_subplots.push subplot
|
31
|
-
subplot
|
6
|
+
include Stage
|
7
|
+
|
8
|
+
attr_reader :plot
|
9
|
+
attr_writer :denied_message
|
10
|
+
|
11
|
+
mount Plot::Entities
|
12
|
+
mount Plot::CommandMount
|
13
|
+
mount Plot::Callbacks
|
14
|
+
mount Plot::SceneMount
|
15
|
+
expose :plot, :conclude
|
16
|
+
|
17
|
+
class << self
|
18
|
+
def start_proc
|
19
|
+
@start_proc
|
32
20
|
end
|
33
|
-
|
21
|
+
|
34
22
|
private
|
35
|
-
|
36
|
-
|
23
|
+
|
24
|
+
def on_start &block
|
25
|
+
@start_proc = block
|
37
26
|
end
|
38
27
|
end
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
def initialize plot, feature:nil
|
28
|
+
|
29
|
+
def initialize plot, introduce: nil
|
43
30
|
@plot = plot
|
44
|
-
@entities = []
|
45
|
-
@players = []
|
46
31
|
@concluded = false
|
47
|
-
|
48
|
-
|
49
|
-
introduce
|
32
|
+
stage &self.class.start_proc unless self.class.start_proc.nil?
|
33
|
+
playbook.freeze
|
34
|
+
self.introduce introduce unless introduce.nil?
|
35
|
+
end
|
36
|
+
|
37
|
+
def default_scene
|
38
|
+
plot.default_scene
|
39
|
+
end
|
40
|
+
|
41
|
+
def default_conclusion
|
42
|
+
plot.default_conclusion
|
43
|
+
end
|
44
|
+
|
45
|
+
def playbook
|
46
|
+
@playbook ||= plot.playbook.dup
|
50
47
|
end
|
51
|
-
|
48
|
+
|
49
|
+
# HACK: Always assume subplots are running for the sake of entity destruction
|
50
|
+
def running?
|
51
|
+
true
|
52
52
|
end
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
e.instance_variable_set(:@subplot, self)
|
57
|
-
entities.push e
|
58
|
-
e
|
53
|
+
|
54
|
+
def denied_message
|
55
|
+
@denied_message ||= 'You are already involved in another subplot.'
|
59
56
|
end
|
57
|
+
|
60
58
|
def introduce player
|
61
|
-
|
62
|
-
|
59
|
+
if plot.subbed?(player)
|
60
|
+
player.tell denied_message
|
61
|
+
else
|
62
|
+
super
|
63
|
+
end
|
63
64
|
end
|
65
|
+
|
64
66
|
def exeunt player
|
65
|
-
player.
|
66
|
-
|
67
|
+
player.playbook = plot.playbook
|
68
|
+
p_players.delete player
|
67
69
|
end
|
70
|
+
|
68
71
|
def conclude
|
69
|
-
concluded = true
|
72
|
+
@concluded = true
|
70
73
|
entities.each { |e|
|
71
|
-
e
|
74
|
+
destroy e
|
72
75
|
}
|
73
76
|
players.each { |p|
|
74
77
|
exeunt p
|
75
78
|
}
|
76
79
|
end
|
80
|
+
|
77
81
|
def concluded?
|
78
82
|
@concluded
|
79
83
|
end
|
84
|
+
|
85
|
+
def ready
|
86
|
+
conclude if players.empty?
|
87
|
+
return if concluded?
|
88
|
+
playbook.freeze
|
89
|
+
call_ready
|
90
|
+
call_player_ready
|
91
|
+
end
|
92
|
+
|
93
|
+
def update
|
94
|
+
call_player_update
|
95
|
+
call_update
|
96
|
+
end
|
80
97
|
end
|
81
98
|
|
82
99
|
end
|
data/lib/gamefic/syntax.rb
CHANGED
@@ -60,7 +60,10 @@ module Gamefic
|
|
60
60
|
m = text.match(@regexp)
|
61
61
|
return nil if m.nil?
|
62
62
|
arguments = []
|
63
|
-
|
63
|
+
# HACK: Skip the first word if the verb is not nil.
|
64
|
+
# This is ugly.
|
65
|
+
b = @verb.nil? ? 0 : 1
|
66
|
+
@replace.to_s.split_words[b..-1].each { |r|
|
64
67
|
if r.match(/^\{\$[0-9]+\}$/)
|
65
68
|
arguments.push m[r[2..-2].to_i]
|
66
69
|
else
|
@@ -70,6 +73,10 @@ module Gamefic
|
|
70
73
|
Command.new @verb, arguments
|
71
74
|
end
|
72
75
|
|
76
|
+
def accept? text
|
77
|
+
!text.match(@regexp).nil?
|
78
|
+
end
|
79
|
+
|
73
80
|
# Get a signature that identifies the form of the Syntax.
|
74
81
|
# Signatures are used to compare Syntaxes to each other.
|
75
82
|
#
|
@@ -106,7 +113,7 @@ module Gamefic
|
|
106
113
|
}
|
107
114
|
cb <=> ca
|
108
115
|
}
|
109
|
-
matches
|
116
|
+
matches
|
110
117
|
end
|
111
118
|
end
|
112
119
|
|
data/lib/gamefic/tester.rb
CHANGED
@@ -9,7 +9,7 @@ module Gamefic
|
|
9
9
|
end
|
10
10
|
def run_test name, actor
|
11
11
|
queue = []
|
12
|
-
|
12
|
+
stage actor, queue, &test_procs[name]
|
13
13
|
actor.queue.push *queue
|
14
14
|
actor[:test_queue_length] = queue.length
|
15
15
|
actor[:test_queue_scene] = actor.scene
|
data/lib/gamefic/text.rb
ADDED
@@ -1,11 +1,8 @@
|
|
1
1
|
module Gamefic
|
2
2
|
|
3
|
-
# Constants for ANSI codes, plus
|
4
|
-
module Ansi
|
3
|
+
# Constants for ANSI codes, plus ExtraCodes for custom formatting.
|
4
|
+
module Text::Ansi
|
5
5
|
module Code
|
6
|
-
class Nonstandard < String
|
7
|
-
|
8
|
-
end
|
9
6
|
module Attribute
|
10
7
|
NORMAL = 0
|
11
8
|
BOLD = 1
|
@@ -35,19 +32,19 @@ module Gamefic
|
|
35
32
|
WHITE = 47
|
36
33
|
end
|
37
34
|
module Extra
|
38
|
-
BLOCK =
|
39
|
-
PRE =
|
40
|
-
HREF =
|
41
|
-
IMAGE =
|
42
|
-
SRC =
|
43
|
-
UPPERCASE =
|
44
|
-
COMMAND =
|
45
|
-
IGNORED =
|
46
|
-
LINE =
|
35
|
+
BLOCK = :block
|
36
|
+
PRE = :pre
|
37
|
+
HREF = :href
|
38
|
+
IMAGE = :image
|
39
|
+
SRC = :src
|
40
|
+
UPPERCASE = :uppercase
|
41
|
+
COMMAND = :command
|
42
|
+
IGNORED = :ignored
|
43
|
+
LINE = :line
|
47
44
|
end
|
48
45
|
end
|
49
46
|
def self.graphics_mode(*settings)
|
50
|
-
ansi = settings.flatten.
|
47
|
+
ansi = settings.flatten.that_are(Fixnum)
|
51
48
|
return '' if ansi.length == 0
|
52
49
|
"\e[#{ansi.join(';')}m"
|
53
50
|
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'rexml/document'
|
2
|
+
require 'gamefic/text/html/entities'
|
3
|
+
|
4
|
+
module Gamefic
|
5
|
+
module Text
|
6
|
+
module Html
|
7
|
+
autoload :Conversions, 'gamefic/text/html/conversions'
|
8
|
+
|
9
|
+
# Convert ampersands to &
|
10
|
+
#
|
11
|
+
# @param text [String]
|
12
|
+
# @return [String]
|
13
|
+
def self.fix_ampersands(text)
|
14
|
+
codes = []
|
15
|
+
ENTITIES.keys.each { |e|
|
16
|
+
codes.push e[1..-1]
|
17
|
+
}
|
18
|
+
piped = codes.join('|')
|
19
|
+
re = Regexp.new("&(?!(#{piped}))")
|
20
|
+
text.gsub(re, '&\1')
|
21
|
+
end
|
22
|
+
|
23
|
+
# Encode a String with HTML entities
|
24
|
+
#
|
25
|
+
# @param text [String]
|
26
|
+
# @return [String]
|
27
|
+
def self.encode(text)
|
28
|
+
encoded = text
|
29
|
+
ENTITIES.each { |k, v|
|
30
|
+
encoded = encoded.gsub(v, k)
|
31
|
+
}
|
32
|
+
encoded
|
33
|
+
end
|
34
|
+
|
35
|
+
# Decode a String's HTML entities
|
36
|
+
#
|
37
|
+
# @param text [String]
|
38
|
+
# @return [String]
|
39
|
+
def self.decode(text)
|
40
|
+
ENTITIES.each { |k, v|
|
41
|
+
text = text.gsub(k, v)
|
42
|
+
}
|
43
|
+
text
|
44
|
+
end
|
45
|
+
|
46
|
+
# Parse a String into an XML document
|
47
|
+
#
|
48
|
+
# @param code [String]
|
49
|
+
# @return [REXML::Document]
|
50
|
+
def self.parse(code)
|
51
|
+
code = fix_ampersands(code).strip
|
52
|
+
last = nil
|
53
|
+
begin
|
54
|
+
doc = REXML::Document.new code
|
55
|
+
rescue REXML::ParseException => e
|
56
|
+
# Convert invalid < characters to <
|
57
|
+
if e.source.buffer != last and e.source.buffer[0,1] == '<'
|
58
|
+
code = code[0,(code.length - e.source.buffer.length)] + '<' + e.source.buffer[1..-1]
|
59
|
+
last = e.source.buffer
|
60
|
+
retry
|
61
|
+
end
|
62
|
+
raise e
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
@@ -0,0 +1,250 @@
|
|
1
|
+
require 'gamefic/text/ansi'
|
2
|
+
require 'gamefic/text/html'
|
3
|
+
require 'io/console'
|
4
|
+
|
5
|
+
module Gamefic
|
6
|
+
|
7
|
+
module Text::Html::Conversions
|
8
|
+
include Gamefic::Text
|
9
|
+
|
10
|
+
def self.html_to_ansi text, wrap: true, width: nil
|
11
|
+
return '' if text.strip == ''
|
12
|
+
output = ''
|
13
|
+
begin
|
14
|
+
doc = Html.parse("<body>#{text.gsub(/\r/, '').strip}</body>")
|
15
|
+
output = AnsiFormatter.new.format(doc) + Ansi.graphics_mode(Ansi::Code::Attribute::NORMAL)
|
16
|
+
output = Html.decode(output)
|
17
|
+
rescue REXML::ParseException => e
|
18
|
+
output = text.strip
|
19
|
+
end
|
20
|
+
calc_width = width || size[0]
|
21
|
+
if calc_width.nil? or !wrap
|
22
|
+
output
|
23
|
+
else
|
24
|
+
terminalize(output, calc_width - 1)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.html_to_text text, wrap: true, width: nil
|
29
|
+
text = html_to_ansi text, wrap: wrap, width: width
|
30
|
+
text.gsub(/\e\[([;\d]+)?m/, '').gsub(/\n +\n/, "\n\n")
|
31
|
+
end
|
32
|
+
|
33
|
+
class AnsiNode
|
34
|
+
include Gamefic::Text::Ansi::Code
|
35
|
+
attr_accessor :parent
|
36
|
+
|
37
|
+
def render
|
38
|
+
end
|
39
|
+
|
40
|
+
def in_block?
|
41
|
+
p = parent
|
42
|
+
while !p.nil?
|
43
|
+
return true if p.kind_of?(BlockNode)
|
44
|
+
p = p.parent
|
45
|
+
end
|
46
|
+
false
|
47
|
+
end
|
48
|
+
end
|
49
|
+
private_constant :AnsiNode
|
50
|
+
|
51
|
+
class TextNode < AnsiNode
|
52
|
+
@@prev_format = nil
|
53
|
+
attr_reader :text, :format
|
54
|
+
def initialize text, format
|
55
|
+
@text = text
|
56
|
+
@format = format
|
57
|
+
end
|
58
|
+
def render
|
59
|
+
return @text if format.include?(Extra::PRE)
|
60
|
+
index = parent.children.index(self)
|
61
|
+
if index > 0
|
62
|
+
prev = parent.children[index - 1]
|
63
|
+
if prev.kind_of?(TextNode) and prev.format == format
|
64
|
+
if prev.text.match(/ $/)
|
65
|
+
return @text.lstrip
|
66
|
+
else
|
67
|
+
return @text
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
if @@prev_format == format
|
72
|
+
@text
|
73
|
+
else
|
74
|
+
@prev_format = format
|
75
|
+
Gamefic::Text::Ansi.graphics_mode(*format) + @text
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
private_constant :TextNode
|
80
|
+
|
81
|
+
class ElementNode < AnsiNode
|
82
|
+
def children
|
83
|
+
@children ||= []
|
84
|
+
end
|
85
|
+
def append child
|
86
|
+
children.push child
|
87
|
+
child.parent = self
|
88
|
+
end
|
89
|
+
end
|
90
|
+
private_constant :ElementNode
|
91
|
+
|
92
|
+
class BlockNode < ElementNode
|
93
|
+
def render
|
94
|
+
output = ''
|
95
|
+
children.each { |c|
|
96
|
+
output += c.render
|
97
|
+
}
|
98
|
+
output = "\n" + output.strip unless in_block?
|
99
|
+
output
|
100
|
+
end
|
101
|
+
end
|
102
|
+
private_constant :BlockNode
|
103
|
+
|
104
|
+
class InlineNode < ElementNode
|
105
|
+
def render
|
106
|
+
output = ''
|
107
|
+
children.each { |c|
|
108
|
+
output += c.render
|
109
|
+
output += "\n" if c.kind_of?(BlockNode)
|
110
|
+
}
|
111
|
+
output
|
112
|
+
end
|
113
|
+
end
|
114
|
+
private_constant :InlineNode
|
115
|
+
|
116
|
+
class BreakNode < ElementNode
|
117
|
+
def render
|
118
|
+
output = ''
|
119
|
+
children.each { |c|
|
120
|
+
output += c.render
|
121
|
+
}
|
122
|
+
output + "\n"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
private_constant :BreakNode
|
126
|
+
|
127
|
+
class AnsiFormatter
|
128
|
+
include Gamefic::Text::Ansi::Code
|
129
|
+
def format document
|
130
|
+
@document = document
|
131
|
+
@ansi_root = InlineNode.new
|
132
|
+
@list_index = []
|
133
|
+
format_recursively @document.root, @ansi_root, [Attribute::NORMAL]
|
134
|
+
output = @ansi_root.render
|
135
|
+
output += (@ansi_root.children.last.kind_of?(BlockNode) ? "\n" : "")
|
136
|
+
end
|
137
|
+
|
138
|
+
def format_recursively element, ansi_node, stack
|
139
|
+
if element.is_a?(REXML::Text)
|
140
|
+
append_text element, ansi_node, stack
|
141
|
+
else
|
142
|
+
current = []
|
143
|
+
case element.name
|
144
|
+
when 'b', 'strong', 'em'
|
145
|
+
current.push Attribute::BOLD
|
146
|
+
format_children element, ansi_node, stack + current
|
147
|
+
when 'i', 'u'
|
148
|
+
current.push Attribute::UNDERSCORE
|
149
|
+
format_children element, ansi_node, stack + current
|
150
|
+
when 'h1', 'h2', 'h3', 'h4', 'h5'
|
151
|
+
current.push Attribute::BOLD, Extra::UPPERCASE
|
152
|
+
format_paragraph element, ansi_node, stack + current
|
153
|
+
when 'p'
|
154
|
+
format_paragraph element, ansi_node, stack
|
155
|
+
when 'ol', 'ul'
|
156
|
+
@list_index.push 0
|
157
|
+
format_paragraph element, ansi_node, stack
|
158
|
+
@list_index.pop
|
159
|
+
when 'li'
|
160
|
+
format_list_item element, ansi_node, stack
|
161
|
+
when 'pre'
|
162
|
+
current.push Extra::PRE
|
163
|
+
format_children element, ansi_node, stack + current
|
164
|
+
when 'br'
|
165
|
+
ansi_node.append TextNode.new("\n", stack + [Extra::PRE])
|
166
|
+
else
|
167
|
+
format_children element, ansi_node, stack
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def append_text element, ansi_node, stack
|
173
|
+
text = element.to_s
|
174
|
+
text.gsub!(/[\s]+/, ' ') unless stack.include?(Extra::PRE)
|
175
|
+
text.upcase! if stack.include?(Extra::UPPERCASE)
|
176
|
+
ansi_node.append TextNode.new(text, stack)
|
177
|
+
end
|
178
|
+
|
179
|
+
def format_children element, node, stack
|
180
|
+
element.each { |e|
|
181
|
+
format_recursively e, node, stack
|
182
|
+
}
|
183
|
+
end
|
184
|
+
|
185
|
+
def format_paragraph element, node, stack
|
186
|
+
paragraph = BlockNode.new
|
187
|
+
node.append paragraph
|
188
|
+
format_children element, paragraph, stack
|
189
|
+
end
|
190
|
+
|
191
|
+
def format_list_item element, node, stack
|
192
|
+
i = 0
|
193
|
+
unless @list_index.empty?
|
194
|
+
@list_index[-1] = @list_index[-1] + 1
|
195
|
+
i = @list_index[-1]
|
196
|
+
end
|
197
|
+
b = BreakNode.new
|
198
|
+
node.append b
|
199
|
+
if element.parent.name == 'ol'
|
200
|
+
b.append TextNode.new("#{i}. ", stack)
|
201
|
+
else
|
202
|
+
b.append TextNode.new("* ", stack)
|
203
|
+
end
|
204
|
+
format_children element, b, stack
|
205
|
+
end
|
206
|
+
end
|
207
|
+
private_constant :AnsiFormatter
|
208
|
+
|
209
|
+
def self.terminalize string, max_length
|
210
|
+
i = 0
|
211
|
+
output = ''
|
212
|
+
line_length = 0
|
213
|
+
while i < string.length
|
214
|
+
line_length += 1
|
215
|
+
char = string[i,1]
|
216
|
+
if char == "\e"
|
217
|
+
# Right now, graphics modes are the only supported ANSI sequences.
|
218
|
+
end_of_seq = string.index("m", i)
|
219
|
+
output += string[i..end_of_seq]
|
220
|
+
i = end_of_seq + 1
|
221
|
+
elsif char == " "
|
222
|
+
next_space = string.index(/[\s]/, i + 1)
|
223
|
+
if !next_space.nil? and line_length + (next_space - i) > max_length
|
224
|
+
output += "\n"
|
225
|
+
line_length = 0
|
226
|
+
else
|
227
|
+
output += char
|
228
|
+
end
|
229
|
+
i += 1
|
230
|
+
else
|
231
|
+
if char == "\n"
|
232
|
+
line_length = 0
|
233
|
+
end
|
234
|
+
output += char
|
235
|
+
i += 1
|
236
|
+
end
|
237
|
+
end
|
238
|
+
output
|
239
|
+
end
|
240
|
+
|
241
|
+
def self.size
|
242
|
+
begin
|
243
|
+
return STDOUT.winsize.reverse
|
244
|
+
rescue
|
245
|
+
return [nil,nil]
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
end
|