inform-runtime 1.3.2 → 1.5.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ff0b544812291d9f9c2a5169ada547843704aa26f0a5ad0fdd82b1e914acba15
4
- data.tar.gz: e3d011e11b4e3531054d3b4a83d199343541e44d57a770708d219a73ca40f668
3
+ metadata.gz: 1f6e015e55549341a59f8e1556996c1aa58073a0fd59eefd773aa1258a126668
4
+ data.tar.gz: 3841f6b206db14c64d5f7da3535992998932e5e80af56b819ce132e539e930f1
5
5
  SHA512:
6
- metadata.gz: 0c92163b72057f043cadce2abbcf7e79a20be080a51e9384289f44f62f5528738bc3c947e66758312b637c2042e480b092a342c2176bfbc0706389d49be636c2
7
- data.tar.gz: 0e4355ed014b0e499e8e730d70ecf9b22ebaac3af317a5d6fc900ef528f819004526ee3004478e112e5c82221329fb4cb426b681e77c7b62aa1bb345269af17a
6
+ metadata.gz: 1cece70cd39f757e3c455e2dfc5712fb7d3f0aa3b21b0c66aaef53743ad375061a06529aed3cd44ed9e43341d6662377345ff347c0f9500b08fba65b9467d510
7
+ data.tar.gz: 4887504a753634d4247e792823a9389eda28fafc7b48acfd5603fa7a2586d5606977209a9efa64f6b5f2f4a4ed80cad5f21bcf7c96ac5fe78901d48f0428528c
@@ -23,11 +23,17 @@
23
23
  module StoryTeller
24
24
  # The IO module
25
25
  module IO
26
+ class << self
27
+ attr_accessor :default_output
28
+ end
29
+
26
30
  def session
27
31
  return @session if defined?(@session) && !@session.nil?
28
32
  return nil unless defined?(StoryTeller::IO::Session)
29
33
 
30
- StoryTeller::IO::Session.of(self)
34
+ StoryTeller::IO::Session.of(self).tap do |session|
35
+ log.trace "self: #{self}, session: #{session.inspect}"
36
+ end
31
37
  end
32
38
 
33
39
  def noop; end
@@ -40,13 +46,13 @@ module StoryTeller
40
46
  subscribed_connections
41
47
  end
42
48
 
43
- def read
44
- $stdin.gets(chomp: true)
45
- end
46
-
47
49
  def out(s)
48
- if defined?(Curses)
49
- Curses.addstr s
50
+ current_session = session
51
+
52
+ if current_session
53
+ current_session.write_output(s)
54
+ elsif StoryTeller::IO.default_output
55
+ StoryTeller::IO.default_output.write(s)
50
56
  else
51
57
  $stdout.write(s)
52
58
  end
@@ -64,13 +70,9 @@ module StoryTeller
64
70
  alias update inform
65
71
 
66
72
  def flush_io
67
- $stdout.flush
68
- end
73
+ return if session
69
74
 
70
- def tidy_output(s, columns = 76)
71
- s.gsub!(/^\n+/, "\n")
72
- return s if s.length < columns || columns == 0
73
- s.gsub(/(.{#{columns}}\S+)( +|$\n?)/, "\\1\n")
75
+ $stdout.flush
74
76
  end
75
77
 
76
78
  def new_line
@@ -128,59 +130,6 @@ module StoryTeller
128
130
  DefaultLineLength
129
131
  end
130
132
 
131
- # def many_per_line(items)
132
- # max = 0
133
-
134
- # lines = []
135
- # prefs = @player.linkto :preferences
136
- # line_length = preferred_line_length
137
- # buffer = ''
138
-
139
- # items.each do |item|
140
- # max = [item.length, max].max
141
- # end
142
- # max += MinimumColumnPadding
143
- # width = (line_length - MinimumCellPadding) / max
144
- # height = (items.length / width).ceil - 1
145
-
146
- # return if width == 0
147
-
148
- # columns = []
149
- # i = 0
150
- # loop do
151
- # # Build a list of columns with nil padding
152
- # column = items[i, height]
153
- # columns << column + Array.new(height - column.length)
154
- # i += height
155
- # break if (i + height) >= (items.length + height)
156
- # end
157
-
158
- # m = []
159
- # loop do
160
- # m << []
161
- # for column in columns
162
- # m.last << column.shift
163
- # end
164
- # break if columns.last.empty?
165
- # end
166
-
167
- # m.each do |row|
168
- # buffer = "\e[51m"
169
- # row.each do |item|
170
- # next unless item
171
- # n = [max - item.length, MinimumColumnPadding].max
172
- # padding = " " * n unless item == row.last
173
- # buffer += format(ItemPaddingString, item: item, padding: padding)
174
- # end
175
- # buffer += "\e[56m"
176
- # lines << buffer.strip
177
- # buffer = ''
178
- # end
179
-
180
- # lines
181
- # end
182
- # # def many_per_line
183
-
184
133
  # rubocop: disable Metrics/AbcSize
185
134
  # rubocop: disable Metrics/CyclomaticComplexity
186
135
  # rubocop: disable Metrics/MethodLength
@@ -65,7 +65,6 @@ module StoryTeller
65
65
  library_methods.merge(mod.instance_methods(false))
66
66
  end
67
67
  @methods_index.concat(library_methods.to_a).sort
68
- log.trace "Loaded library methods: #{@methods_index}"
69
68
  end
70
69
  # rubocop: enable Metrics/AbcSize
71
70
  # rubocop: enable Metrics/MethodLength
@@ -144,6 +144,33 @@ end
144
144
  module StoryTeller
145
145
  # module IO
146
146
  module IO
147
+ # The OutputBuffer class
148
+ class OutputBuffer
149
+ def initialize
150
+ @content = String.new # rubocop: disable Style/EmptyLiteral
151
+ @mutex = Mutex.new
152
+ end
153
+
154
+ def write(value)
155
+ return if value.nil?
156
+
157
+ @mutex.synchronize { @content << value.to_s }
158
+ end
159
+
160
+ def drain
161
+ @mutex.synchronize do
162
+ content = @content.dup
163
+ @content.clear
164
+ content.strip.length.positive? ? content : ''
165
+ end
166
+ end
167
+
168
+ def reset
169
+ @mutex.synchronize { @content.clear }
170
+ end
171
+ end
172
+ # class OutputBuffer
173
+
147
174
  # The Session class
148
175
  # TODO: Refactor method implementations into modules
149
176
  # rubocop: disable Metrics/ClassLength
@@ -154,7 +181,7 @@ class Session
154
181
  include SessionManagementMethods
155
182
  end
156
183
  attr_accessor :channel, :machine, :player, :state, :status
157
- attr_reader :last_good_state, :last_activity, :previous, :inbound, :outbound, :settings
184
+ attr_reader :last_good_state, :last_activity, :previous, :inbound, :outbound, :settings, :output
158
185
 
159
186
  # These states accept any input, including no input
160
187
  Promiscuous = Set.new
@@ -170,6 +197,7 @@ class Session
170
197
  @settings = settings
171
198
  @status = nil
172
199
  @last_activity = Time.now
200
+ self.output = OutputBuffer.new
173
201
 
174
202
  self.class.register(channel, self) unless channel.nil?
175
203
  control(player) unless player.nil?
@@ -178,11 +206,78 @@ class Session
178
206
 
179
207
  alias preferences settings
180
208
 
209
+ def output=(sink)
210
+ @output = sink
211
+ @outbound = sink
212
+ end
213
+
214
+ def write_output(value)
215
+ @output.write(value)
216
+ end
217
+
218
+ def drain_output
219
+ tidy_output(@outbound.drain)
220
+ end
221
+
222
+ def tidy_output(value, columns = word_wrap_column)
223
+ output = value.to_s
224
+ return '' if output.empty?
225
+
226
+ columns = columns.to_i
227
+ return output if columns <= 0
228
+
229
+ output.each_line.map do |line|
230
+ newline = line.end_with?("\n") ? "\n" : ''
231
+ wrap_line(line.chomp, columns) + newline
232
+ end.join
233
+ end
234
+
235
+ # rubocop: disable Metrics/AbcSize
236
+ # rubocop: disable Metrics/MethodLength
237
+ def wrap_line(line, columns)
238
+ return line if line.length <= columns
239
+
240
+ indentation = line[/\A[ \t]*/].to_s
241
+ words = line.strip.split(/[ \t]+/)
242
+ return line if words.empty?
243
+
244
+ lines = []
245
+ current = indentation.dup
246
+
247
+ words.each do |word|
248
+ candidate = current == indentation ? "#{current}#{word}" : "#{current} #{word}"
249
+
250
+ if candidate.length <= columns || current == indentation
251
+ current = candidate
252
+ else
253
+ lines << current.rstrip
254
+ current = "#{indentation}#{word}"
255
+ end
256
+ end
257
+
258
+ lines << current.rstrip
259
+ lines.join("\n")
260
+ end
261
+ # rubocop: enable Metrics/AbcSize
262
+ # rubocop: enable Metrics/MethodLength
263
+
264
+ def word_wrap_column
265
+ value = preferences[:word_wrap]
266
+ return 76 if value.nil?
267
+
268
+ value.to_i
269
+ end
270
+
271
+ def reset_output
272
+ @outbound.reset
273
+ end
274
+
181
275
  def init_channel_session(channel)
182
276
  Session[channel] = self
183
277
  @channel = channel
184
- @inbound = Inbound.new(self)
185
- @outbound = Outbound.new(self)
278
+ @inbound = Inbound.new(self) if defined?(Inbound)
279
+ @output = OutputBuffer.new
280
+ @outbound = @output
186
281
  @last_activity = Time.now
187
282
  end
188
283
 
@@ -230,7 +325,7 @@ class Session
230
325
  end
231
326
 
232
327
  def process(message, machine = @machine)
233
- raise ArgumentError, 'session machine is required' if machine.nil?
328
+ raise ArgumentError, 'Session machine is required' if machine.nil?
234
329
 
235
330
  @machine = machine
236
331
  @buffer = sanitize(message)
@@ -239,10 +334,11 @@ class Session
239
334
 
240
335
  expose_to(machine)
241
336
  update(machine)
337
+ drain_output
242
338
  end
243
339
 
244
340
  def respond
245
- flush_io
341
+ drain_output
246
342
  end
247
343
 
248
344
  def safely_progress(machine = self)
@@ -49,7 +49,7 @@ module StdLib
49
49
  end
50
50
 
51
51
  def inversion
52
- print Inform::VERSION
52
+ print Inform::VERSION.dup
53
53
  end
54
54
 
55
55
  def standard_interpreter
@@ -23,7 +23,7 @@
23
23
  module StoryTeller
24
24
  # module Engine
25
25
  module Engine
26
- VERSION = '1.3.2'.freeze
26
+ VERSION = '1.5.0'.freeze
27
27
  end
28
28
  end
29
29
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: inform-runtime
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.2
4
+ version: 1.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nels Nelson