yap-rawline 0.6.3 → 0.7.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
  SHA1:
3
- metadata.gz: 9fc89446c1a7d8c2867d80966385bd4440afd881
4
- data.tar.gz: 035306deacfd89d6e45a6892718bb241e8db8966
3
+ metadata.gz: 92c874a27903902d286dbf30ed5a9650766b09cd
4
+ data.tar.gz: b88b897fd79df0714fe681765296577e617189ad
5
5
  SHA512:
6
- metadata.gz: a6b3bdf5587d883b255aa70875f717ac411cb2189db1b42eb1c815cf29bef09a52679f9293bf0794a8bbe67dba83d05a6ef2332676c3ed9a38c700a259568d92
7
- data.tar.gz: 661bad9b1aa6eb3df3fbadd57ba7f9a4c61087fbdf839019e503269def8ab309a5d2e95674e03da004175e2e97e876e29d9be43d246847c80ca65d641a21cc48
6
+ metadata.gz: 8356ff5e616f22d71c360271c16b54b17ae431d3473f22b5105894ab605d05c88d308bde5cbf2eeffa596f702aa4e8eb62cceda6cba858717a420ea7c64b5e1f
7
+ data.tar.gz: 2d07d64f41788cd96655f2b227e4361f727ce9b393b3473633c06ab51a0e05604bc5920c79fb9060ac233c2af7866fd9974f5835abd94a5532c85a98f700dfe9
@@ -11,11 +11,10 @@ module RawLine
11
11
  @done_proc = done
12
12
  @keys = keys
13
13
 
14
- @completion_matches = HistoryBuffer.new(0) do |h|
15
- h.duplicates = false
16
- h.cycle = true
17
- end
18
- @completion_matches.empty
14
+ @completion_matches = HistoryBuffer.new_with_infinite_size(
15
+ cycle: true,
16
+ duplicates: false
17
+ )
19
18
 
20
19
  @first_time = true
21
20
  @word_start = @line.word[:start]
@@ -30,7 +29,8 @@ module RawLine
30
29
 
31
30
  if @first_time
32
31
  matches = fetch_completions
33
- resize(matches)
32
+ @completion_matches.replace(matches)
33
+ @completion_matches.first!
34
34
 
35
35
  if matches.length == 1
36
36
  handle_one_match
@@ -77,7 +77,7 @@ module RawLine
77
77
 
78
78
  def handle_one_match
79
79
  Treefell['editor'].puts "completer, exactly one possible completion found: #{@completion_matches.inspect}"
80
- @completion_selected_proc.call(@completion_matches.first)
80
+ @completion_selected_proc.call(@completion_matches.get)
81
81
 
82
82
  Treefell['editor'].puts "completer, done"
83
83
  @done_proc.call
@@ -85,9 +85,6 @@ module RawLine
85
85
 
86
86
  def handle_more_than_one_match
87
87
  Treefell['editor'].puts "completer, more than one possible completion"
88
-
89
- # Get first match
90
- @completion_matches.forward
91
88
  match = @completion_matches.get
92
89
 
93
90
  Treefell['editor'].puts "completer: first completion: #{match} possible: #{@completion_matches.inspect}"
@@ -102,13 +99,6 @@ module RawLine
102
99
  @done_proc.call
103
100
  end
104
101
 
105
- def resize(matches)
106
- if matches.any?
107
- @completion_matches.resize(matches.length)
108
- matches.each { |w| @completion_matches << w }
109
- end
110
- end
111
-
112
102
  def select_next
113
103
  @completion_matches.forward
114
104
  match = @completion_matches.get
@@ -213,17 +213,17 @@ module RawLine
213
213
 
214
214
  Signal.trap("SIGWINCH") do
215
215
  Treefell['editor'].puts "terminal-resized trap=SIGWINCH"
216
- @event_loop.add_event name: "terminal-resized", source: self
216
+ @event_loop.add_event name: "terminal-resized"
217
217
  end
218
218
 
219
219
  @event_registry.subscribe("terminal-resized") do
220
220
  width, height = terminal_width, terminal_height
221
221
  Treefell['editor'].puts "terminal-resizing width=#{width} height=#{height}"
222
222
  @renderer.update_dimensions(width: width, height: height)
223
- @event_loop.add_event name: "render", source: self
223
+ @event_loop.add_event name: "render"
224
224
  end
225
225
 
226
- @event_loop.add_event name: "render", source: self
226
+ @event_loop.add_event name: "render"
227
227
  @event_loop.start
228
228
  end
229
229
 
@@ -248,7 +248,7 @@ module RawLine
248
248
  if bytes.any?
249
249
  keyboard_input_processor.read_bytes(bytes)
250
250
  end
251
- @event_loop.add_event name: 'check_for_keyboard_input', source: self
251
+ @event_loop.add_event name: 'check_for_keyboard_input'
252
252
  end
253
253
 
254
254
  def read_bytes(bytes)
@@ -266,20 +266,17 @@ module RawLine
266
266
  if @char == @terminal.keys[:enter] || !@char
267
267
  Treefell['editor'].puts "processing line: #{@line.text.inspect}"
268
268
 
269
- @renderer.pause
270
- process_line
269
+ @renderer.rollup { process_line }
271
270
  else
272
271
  process_character
273
272
  new_position = @line.position
274
273
  end
275
274
  end
276
- ensure
277
- @renderer.unpause
278
275
  end
279
276
  end
280
277
 
281
278
  def process_line
282
- @event_loop.immediately(name: "process_line", source: self) do
279
+ @event_loop.immediately(name: "process_line") do
283
280
  add_to_history
284
281
 
285
282
  @terminal.snapshot_tty_attrs
@@ -288,14 +285,14 @@ module RawLine
288
285
  @terminal.move_to_beginning_of_row
289
286
  @terminal.puts
290
287
  end
291
- @event_loop.immediately(name: "line_read", source: self, payload: { line: @line.text.without_ansi.dup })
292
- @event_loop.immediately(name: "prepare_new_line", source: self) do
288
+ @event_loop.immediately(name: "line_read", payload: { line: @line.text.without_ansi.dup })
289
+ @event_loop.immediately(name: "prepare_new_line") do
293
290
  history.clear_position
294
291
  reset_line
295
292
  move_to_beginning_of_input
296
293
  end
297
- @event_loop.immediately(name: "restore_tty_attrs", source: self) { @terminal.restore_tty_attrs }
298
- @event_loop.immediately(name: "render", source: self, payload: { reset: true })
294
+ @event_loop.immediately(name: "restore_tty_attrs") { @terminal.restore_tty_attrs }
295
+ @event_loop.immediately(name: "render", payload: { reset: true })
299
296
  end
300
297
 
301
298
  #
@@ -833,7 +830,7 @@ module RawLine
833
830
 
834
831
  @dom.on(:content_changed) do |*args|
835
832
  Treefell['editor'].puts 'DOM content changed, re-rendering'
836
- @event_loop.add_event name: "render"
833
+ @event_loop.immediately name: "render"
837
834
  end
838
835
 
839
836
  @dom.on(:child_changed) do |*args|
@@ -843,7 +840,7 @@ module RawLine
843
840
 
844
841
  @dom.on :position_changed do |*args|
845
842
  Treefell['editor'].puts 'DOM position changed, rendering cursor'
846
- @renderer.render
843
+ @renderer.render_cursor
847
844
  end
848
845
 
849
846
  @dom.on :focus_changed do |*args|
@@ -10,7 +10,6 @@ module RawLine
10
10
 
11
11
  # event looks like:
12
12
  # * name
13
- # * source
14
13
  # * target
15
14
  # * payload
16
15
  def add_event(**event, &blk)
@@ -18,6 +17,10 @@ module RawLine
18
17
  event[:_event_callback] = blk if blk
19
18
  end
20
19
 
20
+ unless event.has_key?(:_event_caller)
21
+ event[:_event_caller] = caller[0..5]
22
+ end
23
+
21
24
  unless event.has_key?(:_event_id)
22
25
  @counter += 1
23
26
  event[:_event_id] = @counter
@@ -39,7 +42,7 @@ module RawLine
39
42
  end
40
43
 
41
44
  def immediately(**event_kwargs, &blk)
42
- dispatch_event(event_kwargs.merge(_event_callback: blk))
45
+ dispatch_event(event_kwargs.merge(_event_callback: blk, _event_caller: caller[0..5]))
43
46
  end
44
47
 
45
48
  def reset
@@ -100,7 +103,7 @@ module RawLine
100
103
  end
101
104
 
102
105
  def default_event
103
- { name: 'default', source: self, _event_id: -1 }
106
+ { name: 'default', _event_id: -1 }
104
107
  end
105
108
 
106
109
  def recur_at(interval_in_ms)
@@ -16,124 +16,171 @@ module RawLine
16
16
  # The HistoryBuffer class is used to hold the editor and line histories, as well
17
17
  # as word completion matches.
18
18
  #
19
- class HistoryBuffer < Array
19
+ class HistoryBuffer
20
+ DefaultSize = 1024
21
+ Infinity = 1.0 / 0
20
22
 
21
- attr_reader :position, :size
22
- attr_accessor :duplicates, :exclude, :cycle
23
+ # +size+ is used to determine how many history items should be kept.
24
+ attr_reader :size
25
+
26
+ # +duplicates+ is used to determine if the history can house duplicate \
27
+ # items, consecutively
28
+ attr_accessor :duplicates
29
+
30
+ # +exclude+ can be set to a lambda/proc filter to determine
31
+ # if an item should be excluded from the history
32
+ attr_accessor :exclude
33
+
34
+ # +cycle+ is used to determine if the the buffer is cyclic
35
+ attr_accessor :cycle
36
+
37
+ # +position+ can be used to set or get the current location of the history
38
+ attr_accessor :position
39
+
40
+ def self.new_with_infinite_size(**kwargs)
41
+ new(Infinity, **kwargs)
42
+ end
23
43
 
24
44
  #
25
45
  # Create an instance of RawLine::HistoryBuffer.
26
46
  # This method takes an optional block used to override the
27
- # following instance attributes:
28
- # * <tt>@duplicates</tt> - whether or not duplicate items will be stored in the buffer.
29
- # * <tt>@exclude</tt> - a Proc object defining exclusion rules to prevent items from being added to the buffer.
30
- # * <tt>@cycle</tt> - Whether or not the buffer is cyclic.
31
- #
32
- def initialize(size)
33
- @duplicates = true
34
- @exclude = lambda{|a|}
35
- @cycle = false
36
- yield self if block_given?
37
- @size = size
38
- @position = nil
39
- end
40
-
47
+ # following properties:
41
48
  #
42
- # Clears the current position on the history object. Useful when deciding
43
- # to cancel/reset history navigation.
49
+ # * <tt>duplicates</tt> - whether or not duplicate items will be stored in the buffer.
50
+ # * <tt>exclude</tt> - a Proc object defining exclusion rules to prevent items from being added to the buffer.
51
+ # * <tt>cycle</tt> - Whether or not the buffer is cyclic.
44
52
  #
45
- def clear_position
53
+ def initialize(size=DefaultSize, cycle: false, duplicates: true, exclude: nil)
54
+ @history = []
55
+ @duplicates = duplicates
56
+ @exclude = exclude || -> (item){ }
57
+ @cycle = cycle
58
+ yield self if block_given?
59
+ @size = size && size > 0 ? size : DefaultSize
46
60
  @position = nil
47
61
  end
48
62
 
49
- #
50
- # Resize the buffer, resetting <tt>@position</tt> to nil.
51
- #
52
- def resize(new_size)
53
- if new_size < @size
54
- @size-new_size.times { pop }
55
- end
56
- @size = new_size
57
- @position = nil
63
+ def [](index)
64
+ @history[index]
58
65
  end
59
66
 
60
- def find_match_backward(text)
61
- regex = to_regex(text)
62
- offset = @position ? length - position : 0
63
- reverse[offset..-1].detect.with_index do |item, index|
64
- if item.match(regex)
65
- @position = length - index - (offset + 1)
66
- end
67
- end
67
+ def any?(&blk)
68
+ @history.any?(&blk)
68
69
  end
69
70
 
70
- def find_match_forward(text)
71
- regex = to_regex(text)
72
- offset = @position ? @position + 1 : 0
73
- self[offset..-1].detect.with_index do |item, index|
74
- if item.match(regex)
75
- @position = index + offset
76
- end
71
+ #
72
+ # Decrement <tt>@position</tt>. By default the history will become
73
+ # positioned at the previous item.
74
+ #
75
+ # If <tt>@cycle</tt> is set to true then the history will cycle to the end
76
+ # when it finds itself at the beginning. If false calling this when
77
+ # at the beginning will result in the position not changing.
78
+ #
79
+ # If a search strategy is assigned then the method <tt>search_backward</tt> will be
80
+ # called on the search strategy to determine the position. This method is
81
+ # given any passed in <tt>options</tt> as well as a <tt>:history</tt> option. The
82
+ # <tt>:history</tt> option will be a reference to self.
83
+ #
84
+ def back(options={})
85
+ return nil unless length > 0
86
+
87
+ case @position
88
+ when nil then
89
+ @position = length - 1
90
+ when 0 then
91
+ @position = length - 1 if @cycle
92
+ else
93
+ @position -= 1
77
94
  end
78
95
  end
79
96
 
80
97
  #
81
- # Clear the content of the buffer and reset <tt>@position</tt> to nil.
98
+ # Clear the content of the buffer and resets <tt>position</tt> to nil.
82
99
  #
83
- def empty
100
+ def clear
84
101
  @position = nil
85
- clear
102
+ @history.clear
86
103
  end
87
104
 
88
105
  #
89
- # Retrieve a copy of the element at <tt>@position</tt>.
106
+ # Clears the current position on the history object. Useful when deciding
107
+ # to cancel/reset history navigation.
90
108
  #
91
- def get
92
- return nil unless length > 0
93
- @position = length-1 unless @position
94
- at(@position).dup
109
+ def clear_position
110
+ @position = nil
111
+ end
112
+
113
+ def detect(&blk)
114
+ @history.detect(&blk)
115
+ end
116
+
117
+ def each(&blk)
118
+ @history.each(&blk)
119
+ end
120
+
121
+ # Return true if the history is empty, otherwise false.
122
+ def empty?
123
+ @history.empty?
95
124
  end
96
125
 
97
126
  #
98
127
  # Return true if <tt>@position</tt> is at the end of the buffer.
99
128
  #
100
129
  def end?
101
- @position == length-1
130
+ @position == length - 1
102
131
  end
103
132
 
104
133
  #
105
- # Return true if <tt>@position</tt> is at the start of the buffer.
134
+ # Override equals check. Will be the same as another HistoryBuffer
135
+ # object with the same history items. Ignores position, exclusion filter,
136
+ # and so on.
106
137
  #
107
- def start?
108
- @position == 0
138
+ def ==(other)
139
+ other.is_a?(self.class) &&
140
+ other.instance_variable_get(:@history) == @history
109
141
  end
110
142
 
111
143
  #
112
- # Decrement <tt>@position</tt>. By default the history will become
113
- # positioned at the previous item.
144
+ # Finds and returns the first item matching the given text, starting
145
+ # from the current <tt>position</tt>, moving backwards. The given text
146
+ # will be successfully matched if it matches any part of a history item.
114
147
  #
115
- # If <tt>@cycle</tt> is set to true then the history will cycle to the end
116
- # when it finds itself at the beginning. If false calling this when
117
- # at the beginning will result in the position not changing.
148
+ def find_match_backward(text)
149
+ regex = to_regex(text)
150
+ @position = nil if @position == 0 && @cycle
151
+ offset = @position ? length - position : 0
152
+ @history.reverse[offset..-1].detect.with_index do |item, index|
153
+ if item.match(regex)
154
+ @position = length - index - (offset + 1)
155
+ end
156
+ end
157
+ end
158
+
118
159
  #
119
- # If a search strategy is assigned then the method <tt>search_backward</tt> will be
120
- # called on the search strategy to determine the position. This method is
121
- # given any passed in <tt>options</tt> as well as a <tt>:history</tt> option. The
122
- # <tt>:history</tt> option will be a reference to self.
160
+ # Finds and returns the first item matching the given text, starting
161
+ # from the current <tt>position</tt>, moving forwards. The given text
162
+ # will be successfully matched if it matches any part of a history item.
123
163
  #
124
- def back(options={})
125
- return nil unless length > 0
126
-
127
- case @position
128
- when nil then
129
- @position = length-1
130
- when 0 then
131
- @position = length-1 if @cycle
132
- else
133
- @position -= 1
164
+ def find_match_forward(text)
165
+ regex = to_regex(text)
166
+ @position = -1 if @position == length - 1 && @cycle
167
+ offset = @position ? @position + 1 : 0
168
+ @history[offset..-1].detect.with_index do |item, index|
169
+ if item.match(regex)
170
+ @position = index + offset
171
+ end
134
172
  end
135
173
  end
136
174
 
175
+ def first
176
+ @history.first
177
+ end
178
+
179
+ def first!
180
+ @position = 0
181
+ get
182
+ end
183
+
137
184
  #
138
185
  # Increment <tt>@position</tt>. By default the history will become
139
186
  # positioned at the next item.
@@ -152,7 +199,7 @@ module RawLine
152
199
 
153
200
  case @position
154
201
  when nil then
155
- @position = 0
202
+ @position = length - 1
156
203
  when length-1 then
157
204
  @position = 0 if @cycle
158
205
  else
@@ -160,29 +207,110 @@ module RawLine
160
207
  end
161
208
  end
162
209
 
210
+ #
211
+ # Retrieve a copy of the history item at the current <tt>position</tt>.
212
+ #
213
+ def get
214
+ return nil unless length > 0
215
+ @position = length - 1 unless @position
216
+ @history.at(@position).dup
217
+ end
218
+
219
+ #
220
+ # Returns the index of the given item if it exists in the history,
221
+ # otherwise nil.
222
+ #
223
+ def index(item)
224
+ @history.index(item)
225
+ end
226
+
227
+ #
228
+ # Retrieve a copy of the last history item
229
+ #
230
+ def last
231
+ return nil unless @history.last
232
+ @history.last.dup
233
+ end
234
+
235
+ #
236
+ # Returns the number of items in the HistoryBuffer.
237
+ #
238
+ def length
239
+ @history.length
240
+ end
241
+
242
+ def map(&blk)
243
+ @history.map(&blk)
244
+ end
245
+
163
246
  #
164
247
  # Add a new item to the buffer.
165
248
  #
166
249
  def push(item)
167
- if !@duplicates && include?(item)
250
+ if !@duplicates && last == item
168
251
  # skip adding this line
169
- return
252
+ return self
170
253
  end
171
254
 
172
255
  unless @exclude.call(item)
173
256
  # Remove the oldest element if size is exceeded
174
257
  if @size <= length
175
- reverse!.pop
176
- reverse!
258
+ @history.reverse!.pop
259
+ @history.reverse!
177
260
  end
178
261
  # Add the new item and reset the position
179
- super(item)
262
+ @history.push item
180
263
  @position = nil
181
264
  end
265
+ self
182
266
  end
183
-
184
267
  alias << push
185
268
 
269
+ #
270
+ # Replaces the current history with the given <tt>new_history</tt>. The
271
+ # new_history can be given as an array of history items or a HistoryBuffer
272
+ # object.
273
+ def replace(new_history)
274
+ if new_history.is_a?(HistoryBuffer)
275
+ new_history = new_history.instance_variable_get(:@history)
276
+ end
277
+ @history.replace(new_history)
278
+ end
279
+
280
+ #
281
+ # Resize the buffer, resetting <tt>position</tt> to nil.
282
+ #
283
+ def resize(new_size)
284
+ if new_size < length
285
+ (length - new_size).times { @history.shift }
286
+ end
287
+ @size = new_size
288
+ @position = nil
289
+ self
290
+ end
291
+
292
+ def reverse
293
+ self.class.new(size).tap do |new_history|
294
+ @history.reverse.each do |item|
295
+ new_history << item
296
+ end
297
+ end
298
+ end
299
+
300
+ #
301
+ # Return true if <tt>position</tt> is at the start of the buffer.
302
+ #
303
+ def beginning?
304
+ @position == 0
305
+ end
306
+
307
+ #
308
+ # Returns a copy of the HistoryBuffer as an array.
309
+ #
310
+ def to_a
311
+ @history.dup
312
+ end
313
+
186
314
  private
187
315
 
188
316
  def to_regex(text)