yap-rawline 0.6.3 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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)