doing 2.0.20 → 2.0.24

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +5 -0
  3. data/Gemfile.lock +1 -1
  4. data/README.md +1 -1
  5. data/doing.rdoc +1 -1
  6. data/lib/doing/log_adapter.rb +3 -1
  7. data/lib/doing/version.rb +1 -1
  8. data/lib/doing/wwid.rb +19 -8
  9. data/lib/helpers/fzf/.goreleaser.yml +119 -0
  10. data/lib/helpers/fzf/.rubocop.yml +28 -0
  11. data/lib/helpers/fzf/ADVANCED.md +565 -0
  12. data/lib/helpers/fzf/BUILD.md +49 -0
  13. data/lib/helpers/fzf/CHANGELOG.md +1193 -0
  14. data/lib/helpers/fzf/Dockerfile +11 -0
  15. data/lib/helpers/fzf/LICENSE +21 -0
  16. data/lib/helpers/fzf/Makefile +166 -0
  17. data/lib/helpers/fzf/README-VIM.md +486 -0
  18. data/lib/helpers/fzf/README.md +712 -0
  19. data/lib/helpers/fzf/bin/fzf-tmux +233 -0
  20. data/lib/helpers/fzf/doc/fzf.txt +512 -0
  21. data/lib/helpers/fzf/go.mod +17 -0
  22. data/lib/helpers/fzf/go.sum +31 -0
  23. data/lib/helpers/fzf/install +382 -0
  24. data/lib/helpers/fzf/install.ps1 +65 -0
  25. data/lib/helpers/fzf/main.go +14 -0
  26. data/lib/helpers/fzf/man/man1/fzf-tmux.1 +68 -0
  27. data/lib/helpers/fzf/man/man1/fzf.1 +1001 -0
  28. data/lib/helpers/fzf/plugin/fzf.vim +1048 -0
  29. data/lib/helpers/fzf/shell/completion.bash +381 -0
  30. data/lib/helpers/fzf/shell/completion.zsh +329 -0
  31. data/lib/helpers/fzf/shell/key-bindings.bash +96 -0
  32. data/lib/helpers/fzf/shell/key-bindings.fish +172 -0
  33. data/lib/helpers/fzf/shell/key-bindings.zsh +114 -0
  34. data/lib/helpers/fzf/src/LICENSE +21 -0
  35. data/lib/helpers/fzf/src/algo/algo.go +884 -0
  36. data/lib/helpers/fzf/src/algo/algo_test.go +197 -0
  37. data/lib/helpers/fzf/src/algo/normalize.go +492 -0
  38. data/lib/helpers/fzf/src/ansi.go +409 -0
  39. data/lib/helpers/fzf/src/ansi_test.go +427 -0
  40. data/lib/helpers/fzf/src/cache.go +81 -0
  41. data/lib/helpers/fzf/src/cache_test.go +39 -0
  42. data/lib/helpers/fzf/src/chunklist.go +89 -0
  43. data/lib/helpers/fzf/src/chunklist_test.go +80 -0
  44. data/lib/helpers/fzf/src/constants.go +85 -0
  45. data/lib/helpers/fzf/src/core.go +351 -0
  46. data/lib/helpers/fzf/src/history.go +96 -0
  47. data/lib/helpers/fzf/src/history_test.go +68 -0
  48. data/lib/helpers/fzf/src/item.go +44 -0
  49. data/lib/helpers/fzf/src/item_test.go +23 -0
  50. data/lib/helpers/fzf/src/matcher.go +235 -0
  51. data/lib/helpers/fzf/src/merger.go +120 -0
  52. data/lib/helpers/fzf/src/merger_test.go +88 -0
  53. data/lib/helpers/fzf/src/options.go +1691 -0
  54. data/lib/helpers/fzf/src/options_test.go +457 -0
  55. data/lib/helpers/fzf/src/pattern.go +425 -0
  56. data/lib/helpers/fzf/src/pattern_test.go +209 -0
  57. data/lib/helpers/fzf/src/protector/protector.go +8 -0
  58. data/lib/helpers/fzf/src/protector/protector_openbsd.go +10 -0
  59. data/lib/helpers/fzf/src/reader.go +201 -0
  60. data/lib/helpers/fzf/src/reader_test.go +63 -0
  61. data/lib/helpers/fzf/src/result.go +243 -0
  62. data/lib/helpers/fzf/src/result_others.go +16 -0
  63. data/lib/helpers/fzf/src/result_test.go +159 -0
  64. data/lib/helpers/fzf/src/result_x86.go +16 -0
  65. data/lib/helpers/fzf/src/terminal.go +2832 -0
  66. data/lib/helpers/fzf/src/terminal_test.go +638 -0
  67. data/lib/helpers/fzf/src/terminal_unix.go +26 -0
  68. data/lib/helpers/fzf/src/terminal_windows.go +45 -0
  69. data/lib/helpers/fzf/src/tokenizer.go +253 -0
  70. data/lib/helpers/fzf/src/tokenizer_test.go +112 -0
  71. data/lib/helpers/fzf/src/tui/dummy.go +46 -0
  72. data/lib/helpers/fzf/src/tui/light.go +987 -0
  73. data/lib/helpers/fzf/src/tui/light_unix.go +110 -0
  74. data/lib/helpers/fzf/src/tui/light_windows.go +145 -0
  75. data/lib/helpers/fzf/src/tui/tcell.go +721 -0
  76. data/lib/helpers/fzf/src/tui/tcell_test.go +392 -0
  77. data/lib/helpers/fzf/src/tui/ttyname_unix.go +47 -0
  78. data/lib/helpers/fzf/src/tui/ttyname_windows.go +14 -0
  79. data/lib/helpers/fzf/src/tui/tui.go +625 -0
  80. data/lib/helpers/fzf/src/tui/tui_test.go +20 -0
  81. data/lib/helpers/fzf/src/util/atomicbool.go +34 -0
  82. data/lib/helpers/fzf/src/util/atomicbool_test.go +17 -0
  83. data/lib/helpers/fzf/src/util/chars.go +198 -0
  84. data/lib/helpers/fzf/src/util/chars_test.go +46 -0
  85. data/lib/helpers/fzf/src/util/eventbox.go +96 -0
  86. data/lib/helpers/fzf/src/util/eventbox_test.go +61 -0
  87. data/lib/helpers/fzf/src/util/slab.go +12 -0
  88. data/lib/helpers/fzf/src/util/util.go +138 -0
  89. data/lib/helpers/fzf/src/util/util_test.go +40 -0
  90. data/lib/helpers/fzf/src/util/util_unix.go +47 -0
  91. data/lib/helpers/fzf/src/util/util_windows.go +83 -0
  92. data/lib/helpers/fzf/test/fzf.vader +175 -0
  93. data/lib/helpers/fzf/test/test_go.rb +2626 -0
  94. data/lib/helpers/fzf/uninstall +117 -0
  95. metadata +88 -2
@@ -0,0 +1,2626 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'minitest/autorun'
5
+ require 'fileutils'
6
+ require 'English'
7
+ require 'shellwords'
8
+ require 'erb'
9
+ require 'tempfile'
10
+
11
+ TEMPLATE = DATA.read
12
+ UNSETS = %w[
13
+ FZF_DEFAULT_COMMAND FZF_DEFAULT_OPTS
14
+ FZF_TMUX FZF_TMUX_OPTS
15
+ FZF_CTRL_T_COMMAND FZF_CTRL_T_OPTS
16
+ FZF_ALT_C_COMMAND
17
+ FZF_ALT_C_OPTS FZF_CTRL_R_OPTS
18
+ fish_history
19
+ ].freeze
20
+ DEFAULT_TIMEOUT = 10
21
+
22
+ FILE = File.expand_path(__FILE__)
23
+ BASE = File.expand_path('..', __dir__)
24
+ Dir.chdir(BASE)
25
+ FZF = "FZF_DEFAULT_OPTS= FZF_DEFAULT_COMMAND= #{BASE}/bin/fzf"
26
+
27
+ def wait
28
+ since = Time.now
29
+ begin
30
+ yield or raise Minitest::Assertion, 'Assertion failure'
31
+ rescue Minitest::Assertion
32
+ raise if Time.now - since > DEFAULT_TIMEOUT
33
+
34
+ sleep(0.05)
35
+ retry
36
+ end
37
+ end
38
+
39
+ class Shell
40
+ class << self
41
+ def bash
42
+ @bash ||=
43
+ begin
44
+ bashrc = '/tmp/fzf.bash'
45
+ File.open(bashrc, 'w') do |f|
46
+ f.puts ERB.new(TEMPLATE).result(binding)
47
+ end
48
+
49
+ "bash --rcfile #{bashrc}"
50
+ end
51
+ end
52
+
53
+ def zsh
54
+ @zsh ||=
55
+ begin
56
+ zdotdir = '/tmp/fzf-zsh'
57
+ FileUtils.rm_rf(zdotdir)
58
+ FileUtils.mkdir_p(zdotdir)
59
+ File.open("#{zdotdir}/.zshrc", 'w') do |f|
60
+ f.puts ERB.new(TEMPLATE).result(binding)
61
+ end
62
+ "ZDOTDIR=#{zdotdir} zsh"
63
+ end
64
+ end
65
+
66
+ def fish
67
+ UNSETS.map { |v| v + '= ' }.join + 'fish'
68
+ end
69
+ end
70
+ end
71
+
72
+ class Tmux
73
+ attr_reader :win
74
+
75
+ def initialize(shell = :bash)
76
+ @win = go(%W[new-window -d -P -F #I #{Shell.send(shell)}]).first
77
+ go(%W[set-window-option -t #{@win} pane-base-index 0])
78
+ return unless shell == :fish
79
+
80
+ send_keys 'function fish_prompt; end; clear', :Enter
81
+ self.until(&:empty?)
82
+ end
83
+
84
+ def kill
85
+ go(%W[kill-window -t #{win}])
86
+ end
87
+
88
+ def focus
89
+ go(%W[select-window -t #{win}])
90
+ end
91
+
92
+ def send_keys(*args)
93
+ go(%W[send-keys -t #{win}] + args.map(&:to_s))
94
+ end
95
+
96
+ def paste(str)
97
+ system('tmux', 'setb', str, ';', 'pasteb', '-t', win, ';', 'send-keys', '-t', win, 'Enter')
98
+ end
99
+
100
+ def capture
101
+ go(%W[capture-pane -p -J -t #{win}]).map(&:rstrip).reverse.drop_while(&:empty?).reverse
102
+ end
103
+
104
+ def until(refresh = false)
105
+ lines = nil
106
+ begin
107
+ wait do
108
+ lines = capture
109
+ class << lines
110
+ def counts
111
+ lazy
112
+ .map { |l| l.scan(%r{^. ([0-9]+)/([0-9]+)( \(([0-9]+)\))?}) }
113
+ .reject(&:empty?)
114
+ .first&.first&.map(&:to_i)&.values_at(0, 1, 3) || [0, 0, 0]
115
+ end
116
+
117
+ def match_count
118
+ counts[0]
119
+ end
120
+
121
+ def item_count
122
+ counts[1]
123
+ end
124
+
125
+ def select_count
126
+ counts[2]
127
+ end
128
+
129
+ def any_include?(val)
130
+ method = val.is_a?(Regexp) ? :match : :include?
131
+ find { |line| line.send(method, val) }
132
+ end
133
+ end
134
+ yield(lines).tap do |ok|
135
+ send_keys 'C-l' if refresh && !ok
136
+ end
137
+ end
138
+ rescue Minitest::Assertion
139
+ puts $ERROR_INFO.backtrace
140
+ puts '>' * 80
141
+ puts lines
142
+ puts '<' * 80
143
+ raise
144
+ end
145
+ lines
146
+ end
147
+
148
+ def prepare
149
+ tries = 0
150
+ begin
151
+ self.until(true) do |lines|
152
+ message = "Prepare[#{tries}]"
153
+ send_keys ' ', 'C-u', :Enter, message, :Left, :Right
154
+ lines[-1] == message
155
+ end
156
+ rescue Minitest::Assertion
157
+ (tries += 1) < 5 ? retry : raise
158
+ end
159
+ send_keys 'C-u', 'C-l'
160
+ end
161
+
162
+ private
163
+
164
+ def go(args)
165
+ IO.popen(%w[tmux] + args) { |io| io.readlines(chomp: true) }
166
+ end
167
+ end
168
+
169
+ class TestBase < Minitest::Test
170
+ TEMPNAME = '/tmp/output'
171
+
172
+ attr_reader :tmux
173
+
174
+ def tempname
175
+ @temp_suffix ||= 0
176
+ [TEMPNAME,
177
+ caller_locations.map(&:label).find { |l| l.start_with?('test_') },
178
+ @temp_suffix].join('-')
179
+ end
180
+
181
+ def writelines(path, lines)
182
+ File.unlink(path) while File.exist?(path)
183
+ File.open(path, 'w') { |f| f.puts lines }
184
+ end
185
+
186
+ def readonce
187
+ wait { assert_path_exists tempname }
188
+ File.read(tempname)
189
+ ensure
190
+ File.unlink(tempname) while File.exist?(tempname)
191
+ @temp_suffix += 1
192
+ tmux.prepare
193
+ end
194
+
195
+ def fzf(*opts)
196
+ fzf!(*opts) + " > #{tempname}.tmp; mv #{tempname}.tmp #{tempname}"
197
+ end
198
+
199
+ def fzf!(*opts)
200
+ opts = opts.map do |o|
201
+ case o
202
+ when Symbol
203
+ o = o.to_s
204
+ o.length > 1 ? "--#{o.tr('_', '-')}" : "-#{o}"
205
+ when String, Numeric
206
+ o.to_s
207
+ end
208
+ end.compact
209
+ "#{FZF} #{opts.join(' ')}"
210
+ end
211
+ end
212
+
213
+ class TestGoFZF < TestBase
214
+ def setup
215
+ super
216
+ @tmux = Tmux.new
217
+ end
218
+
219
+ def teardown
220
+ @tmux.kill
221
+ end
222
+
223
+ def test_vanilla
224
+ tmux.send_keys "seq 1 100000 | #{fzf}", :Enter
225
+ tmux.until do |lines|
226
+ assert_equal '>', lines.last
227
+ assert_equal ' 100000/100000', lines[-2]
228
+ end
229
+ lines = tmux.capture
230
+ assert_equal ' 2', lines[-4]
231
+ assert_equal '> 1', lines[-3]
232
+ assert_equal ' 100000/100000', lines[-2]
233
+ assert_equal '>', lines[-1]
234
+
235
+ # Testing basic key bindings
236
+ tmux.send_keys '99', 'C-a', '1', 'C-f', '3', 'C-b', 'C-h', 'C-u', 'C-e', 'C-y', 'C-k', 'Tab', 'BTab'
237
+ tmux.until do |lines|
238
+ assert_equal '> 3910', lines[-4]
239
+ assert_equal ' 391', lines[-3]
240
+ assert_equal ' 856/100000', lines[-2]
241
+ assert_equal '> 391', lines[-1]
242
+ end
243
+
244
+ tmux.send_keys :Enter
245
+ assert_equal '3910', readonce.chomp
246
+ end
247
+
248
+ def test_fzf_default_command
249
+ tmux.send_keys fzf.sub('FZF_DEFAULT_COMMAND=', "FZF_DEFAULT_COMMAND='echo hello'"), :Enter
250
+ tmux.until { |lines| assert_equal '> hello', lines[-3] }
251
+
252
+ tmux.send_keys :Enter
253
+ assert_equal 'hello', readonce.chomp
254
+ end
255
+
256
+ def test_fzf_default_command_failure
257
+ tmux.send_keys fzf.sub('FZF_DEFAULT_COMMAND=', 'FZF_DEFAULT_COMMAND=false'), :Enter
258
+ tmux.until { |lines| assert_equal ' [Command failed: false]', lines[-2] }
259
+ tmux.send_keys :Enter
260
+ end
261
+
262
+ def test_key_bindings
263
+ tmux.send_keys "#{FZF} -q 'foo bar foo-bar'", :Enter
264
+ tmux.until { |lines| assert_equal '> foo bar foo-bar', lines.last }
265
+
266
+ # CTRL-A
267
+ tmux.send_keys 'C-A', '('
268
+ tmux.until { |lines| assert_equal '> (foo bar foo-bar', lines.last }
269
+
270
+ # META-F
271
+ tmux.send_keys :Escape, :f, ')'
272
+ tmux.until { |lines| assert_equal '> (foo) bar foo-bar', lines.last }
273
+
274
+ # CTRL-B
275
+ tmux.send_keys 'C-B', 'var'
276
+ tmux.until { |lines| assert_equal '> (foovar) bar foo-bar', lines.last }
277
+
278
+ # Left, CTRL-D
279
+ tmux.send_keys :Left, :Left, 'C-D'
280
+ tmux.until { |lines| assert_equal '> (foovr) bar foo-bar', lines.last }
281
+
282
+ # META-BS
283
+ tmux.send_keys :Escape, :BSpace
284
+ tmux.until { |lines| assert_equal '> (r) bar foo-bar', lines.last }
285
+
286
+ # CTRL-Y
287
+ tmux.send_keys 'C-Y', 'C-Y'
288
+ tmux.until { |lines| assert_equal '> (foovfoovr) bar foo-bar', lines.last }
289
+
290
+ # META-B
291
+ tmux.send_keys :Escape, :b, :Space, :Space
292
+ tmux.until { |lines| assert_equal '> ( foovfoovr) bar foo-bar', lines.last }
293
+
294
+ # CTRL-F / Right
295
+ tmux.send_keys 'C-F', :Right, '/'
296
+ tmux.until { |lines| assert_equal '> ( fo/ovfoovr) bar foo-bar', lines.last }
297
+
298
+ # CTRL-H / BS
299
+ tmux.send_keys 'C-H', :BSpace
300
+ tmux.until { |lines| assert_equal '> ( fovfoovr) bar foo-bar', lines.last }
301
+
302
+ # CTRL-E
303
+ tmux.send_keys 'C-E', 'baz'
304
+ tmux.until { |lines| assert_equal '> ( fovfoovr) bar foo-barbaz', lines.last }
305
+
306
+ # CTRL-U
307
+ tmux.send_keys 'C-U'
308
+ tmux.until { |lines| assert_equal '>', lines.last }
309
+
310
+ # CTRL-Y
311
+ tmux.send_keys 'C-Y'
312
+ tmux.until { |lines| assert_equal '> ( fovfoovr) bar foo-barbaz', lines.last }
313
+
314
+ # CTRL-W
315
+ tmux.send_keys 'C-W', 'bar-foo'
316
+ tmux.until { |lines| assert_equal '> ( fovfoovr) bar bar-foo', lines.last }
317
+
318
+ # META-D
319
+ tmux.send_keys :Escape, :b, :Escape, :b, :Escape, :d, 'C-A', 'C-Y'
320
+ tmux.until { |lines| assert_equal '> bar( fovfoovr) bar -foo', lines.last }
321
+
322
+ # CTRL-M
323
+ tmux.send_keys 'C-M'
324
+ tmux.until { |lines| refute_equal '>', lines.last }
325
+ end
326
+
327
+ def test_file_word
328
+ tmux.send_keys "#{FZF} -q '--/foo bar/foo-bar/baz' --filepath-word", :Enter
329
+ tmux.until { |lines| assert_equal '> --/foo bar/foo-bar/baz', lines.last }
330
+
331
+ tmux.send_keys :Escape, :b
332
+ tmux.send_keys :Escape, :b
333
+ tmux.send_keys :Escape, :b
334
+ tmux.send_keys :Escape, :d
335
+ tmux.send_keys :Escape, :f
336
+ tmux.send_keys :Escape, :BSpace
337
+ tmux.until { |lines| assert_equal '> --///baz', lines.last }
338
+ end
339
+
340
+ def test_multi_order
341
+ tmux.send_keys "seq 1 10 | #{fzf(:multi)}", :Enter
342
+ tmux.until { |lines| assert_equal '>', lines.last }
343
+
344
+ tmux.send_keys :Tab, :Up, :Up, :Tab, :Tab, :Tab, # 3, 2
345
+ 'C-K', 'C-K', 'C-K', 'C-K', :BTab, :BTab, # 5, 6
346
+ :PgUp, 'C-J', :Down, :Tab, :Tab # 8, 7
347
+ tmux.until { |lines| assert_equal ' 10/10 (6)', lines[-2] }
348
+ tmux.send_keys 'C-M'
349
+ assert_equal %w[3 2 5 6 8 7], readonce.lines(chomp: true)
350
+ end
351
+
352
+ def test_multi_max
353
+ tmux.send_keys "seq 1 10 | #{FZF} -m 3 --bind A:select-all,T:toggle-all --preview 'echo [{+}]/{}'", :Enter
354
+
355
+ tmux.until { |lines| assert_equal 10, lines.item_count }
356
+
357
+ tmux.send_keys '1'
358
+ tmux.until do |lines|
359
+ assert_includes lines[1], ' [1]/1 '
360
+ assert lines[-2]&.start_with?(' 2/10 ')
361
+ end
362
+
363
+ tmux.send_keys 'A'
364
+ tmux.until do |lines|
365
+ assert_includes lines[1], ' [1 10]/1 '
366
+ assert lines[-2]&.start_with?(' 2/10 (2/3)')
367
+ end
368
+
369
+ tmux.send_keys :BSpace
370
+ tmux.until { |lines| assert lines[-2]&.start_with?(' 10/10 (2/3)') }
371
+
372
+ tmux.send_keys 'T'
373
+ tmux.until do |lines|
374
+ assert_includes lines[1], ' [2 3 4]/1 '
375
+ assert lines[-2]&.start_with?(' 10/10 (3/3)')
376
+ end
377
+
378
+ %w[T A].each do |key|
379
+ tmux.send_keys key
380
+ tmux.until do |lines|
381
+ assert_includes lines[1], ' [1 5 6]/1 '
382
+ assert lines[-2]&.start_with?(' 10/10 (3/3)')
383
+ end
384
+ end
385
+
386
+ tmux.send_keys :BTab
387
+ tmux.until do |lines|
388
+ assert_includes lines[1], ' [5 6]/2 '
389
+ assert lines[-2]&.start_with?(' 10/10 (2/3)')
390
+ end
391
+
392
+ [:BTab, :BTab, 'A'].each do |key|
393
+ tmux.send_keys key
394
+ tmux.until do |lines|
395
+ assert_includes lines[1], ' [5 6 2]/3 '
396
+ assert lines[-2]&.start_with?(' 10/10 (3/3)')
397
+ end
398
+ end
399
+
400
+ tmux.send_keys '2'
401
+ tmux.until { |lines| assert lines[-2]&.start_with?(' 1/10 (3/3)') }
402
+
403
+ tmux.send_keys 'T'
404
+ tmux.until do |lines|
405
+ assert_includes lines[1], ' [5 6]/2 '
406
+ assert lines[-2]&.start_with?(' 1/10 (2/3)')
407
+ end
408
+
409
+ tmux.send_keys :BSpace
410
+ tmux.until { |lines| assert lines[-2]&.start_with?(' 10/10 (2/3)') }
411
+
412
+ tmux.send_keys 'A'
413
+ tmux.until do |lines|
414
+ assert_includes lines[1], ' [5 6 1]/1 '
415
+ assert lines[-2]&.start_with?(' 10/10 (3/3)')
416
+ end
417
+ end
418
+
419
+ def test_with_nth
420
+ [true, false].each do |multi|
421
+ tmux.send_keys "(echo ' 1st 2nd 3rd/';
422
+ echo ' first second third/') |
423
+ #{fzf(multi && :multi, :x, :nth, 2, :with_nth, '2,-1,1')}",
424
+ :Enter
425
+ tmux.until { |lines| assert_equal multi ? ' 2/2 (0)' : ' 2/2', lines[-2] }
426
+
427
+ # Transformed list
428
+ lines = tmux.capture
429
+ assert_equal ' second third/first', lines[-4]
430
+ assert_equal '> 2nd 3rd/1st', lines[-3]
431
+
432
+ # However, the output must not be transformed
433
+ if multi
434
+ tmux.send_keys :BTab, :BTab
435
+ tmux.until { |lines| assert_equal ' 2/2 (2)', lines[-2] }
436
+ tmux.send_keys :Enter
437
+ assert_equal [' 1st 2nd 3rd/', ' first second third/'], readonce.lines(chomp: true)
438
+ else
439
+ tmux.send_keys '^', '3'
440
+ tmux.until { |lines| assert_equal ' 1/2', lines[-2] }
441
+ tmux.send_keys :Enter
442
+ assert_equal [' 1st 2nd 3rd/'], readonce.lines(chomp: true)
443
+ end
444
+ end
445
+ end
446
+
447
+ def test_scroll
448
+ [true, false].each do |rev|
449
+ tmux.send_keys "seq 1 100 | #{fzf(rev && :reverse)}", :Enter
450
+ tmux.until { |lines| assert_includes lines, ' 100/100' }
451
+ tmux.send_keys(*Array.new(110) { rev ? :Down : :Up })
452
+ tmux.until { |lines| assert_includes lines, '> 100' }
453
+ tmux.send_keys :Enter
454
+ assert_equal '100', readonce.chomp
455
+ end
456
+ end
457
+
458
+ def test_select_1
459
+ tmux.send_keys "seq 1 100 | #{fzf(:with_nth, '..,..', :print_query, :q, 5555, :'1')}", :Enter
460
+ assert_equal %w[5555 55], readonce.lines(chomp: true)
461
+ end
462
+
463
+ def test_exit_0
464
+ tmux.send_keys "seq 1 100 | #{fzf(:with_nth, '..,..', :print_query, :q, 555_555, :'0')}", :Enter
465
+ assert_equal %w[555555], readonce.lines(chomp: true)
466
+ end
467
+
468
+ def test_select_1_exit_0_fail
469
+ [:'0', :'1', %i[1 0]].each do |opt|
470
+ tmux.send_keys "seq 1 100 | #{fzf(:print_query, :multi, :q, 5, *opt)}", :Enter
471
+ tmux.until { |lines| assert_equal '> 5', lines.last }
472
+ tmux.send_keys :BTab, :BTab, :BTab
473
+ tmux.until { |lines| assert_equal ' 19/100 (3)', lines[-2] }
474
+ tmux.send_keys :Enter
475
+ assert_equal %w[5 5 50 51], readonce.lines(chomp: true)
476
+ end
477
+ end
478
+
479
+ def test_query_unicode
480
+ tmux.paste "(echo abc; echo $'\\352\\260\\200\\353\\202\\230\\353\\213\\244') | #{fzf(:query, "$'\\352\\260\\200\\353\\213\\244'")}"
481
+ tmux.until { |lines| assert_equal ' 1/2', lines[-2] }
482
+ tmux.send_keys :Enter
483
+ assert_equal %w[가나다], readonce.lines(chomp: true)
484
+ end
485
+
486
+ def test_sync
487
+ tmux.send_keys "seq 1 100 | #{fzf!(:multi)} | awk '{print $1 $1}' | #{fzf(:sync)}", :Enter
488
+ tmux.until { |lines| assert_equal '>', lines[-1] }
489
+ tmux.send_keys 9
490
+ tmux.until { |lines| assert_equal ' 19/100 (0)', lines[-2] }
491
+ tmux.send_keys :BTab, :BTab, :BTab
492
+ tmux.until { |lines| assert_equal ' 19/100 (3)', lines[-2] }
493
+ tmux.send_keys :Enter
494
+ tmux.until { |lines| assert_equal '>', lines[-1] }
495
+ tmux.send_keys 'C-K', :Enter
496
+ assert_equal %w[9090], readonce.lines(chomp: true)
497
+ end
498
+
499
+ def test_tac
500
+ tmux.send_keys "seq 1 1000 | #{fzf(:tac, :multi)}", :Enter
501
+ tmux.until { |lines| assert_equal ' 1000/1000 (0)', lines[-2] }
502
+ tmux.send_keys :BTab, :BTab, :BTab
503
+ tmux.until { |lines| assert_equal ' 1000/1000 (3)', lines[-2] }
504
+ tmux.send_keys :Enter
505
+ assert_equal %w[1000 999 998], readonce.lines(chomp: true)
506
+ end
507
+
508
+ def test_tac_sort
509
+ tmux.send_keys "seq 1 1000 | #{fzf(:tac, :multi)}", :Enter
510
+ tmux.until { |lines| assert_equal ' 1000/1000 (0)', lines[-2] }
511
+ tmux.send_keys '99'
512
+ tmux.until { |lines| assert_equal ' 28/1000 (0)', lines[-2] }
513
+ tmux.send_keys :BTab, :BTab, :BTab
514
+ tmux.until { |lines| assert_equal ' 28/1000 (3)', lines[-2] }
515
+ tmux.send_keys :Enter
516
+ assert_equal %w[99 999 998], readonce.lines(chomp: true)
517
+ end
518
+
519
+ def test_tac_nosort
520
+ tmux.send_keys "seq 1 1000 | #{fzf(:tac, :no_sort, :multi)}", :Enter
521
+ tmux.until { |lines| assert_equal ' 1000/1000 (0)', lines[-2] }
522
+ tmux.send_keys '00'
523
+ tmux.until { |lines| assert_equal ' 10/1000 (0)', lines[-2] }
524
+ tmux.send_keys :BTab, :BTab, :BTab
525
+ tmux.until { |lines| assert_equal ' 10/1000 (3)', lines[-2] }
526
+ tmux.send_keys :Enter
527
+ assert_equal %w[1000 900 800], readonce.lines(chomp: true)
528
+ end
529
+
530
+ def test_expect
531
+ test = lambda do |key, feed, expected = key|
532
+ tmux.send_keys "seq 1 100 | #{fzf(:expect, key)}", :Enter
533
+ tmux.until { |lines| assert_equal ' 100/100', lines[-2] }
534
+ tmux.send_keys '55'
535
+ tmux.until { |lines| assert_equal ' 1/100', lines[-2] }
536
+ tmux.send_keys(*feed)
537
+ tmux.prepare
538
+ assert_equal [expected, '55'], readonce.lines(chomp: true)
539
+ end
540
+ test.call('ctrl-t', 'C-T')
541
+ test.call('ctrl-t', 'Enter', '')
542
+ test.call('alt-c', %i[Escape c])
543
+ test.call('f1', 'f1')
544
+ test.call('f2', 'f2')
545
+ test.call('f3', 'f3')
546
+ test.call('f2,f4', 'f2', 'f2')
547
+ test.call('f2,f4', 'f4', 'f4')
548
+ test.call('alt-/', %i[Escape /])
549
+ %w[f5 f6 f7 f8 f9 f10].each do |key|
550
+ test.call('f5,f6,f7,f8,f9,f10', key, key)
551
+ end
552
+ test.call('@', '@')
553
+ end
554
+
555
+ def test_expect_print_query
556
+ tmux.send_keys "seq 1 100 | #{fzf('--expect=alt-z', :print_query)}", :Enter
557
+ tmux.until { |lines| assert_equal ' 100/100', lines[-2] }
558
+ tmux.send_keys '55'
559
+ tmux.until { |lines| assert_equal ' 1/100', lines[-2] }
560
+ tmux.send_keys :Escape, :z
561
+ assert_equal %w[55 alt-z 55], readonce.lines(chomp: true)
562
+ end
563
+
564
+ def test_expect_printable_character_print_query
565
+ tmux.send_keys "seq 1 100 | #{fzf('--expect=z --print-query')}", :Enter
566
+ tmux.until { |lines| assert_equal ' 100/100', lines[-2] }
567
+ tmux.send_keys '55'
568
+ tmux.until { |lines| assert_equal ' 1/100', lines[-2] }
569
+ tmux.send_keys 'z'
570
+ assert_equal %w[55 z 55], readonce.lines(chomp: true)
571
+ end
572
+
573
+ def test_expect_print_query_select_1
574
+ tmux.send_keys "seq 1 100 | #{fzf('-q55 -1 --expect=alt-z --print-query')}", :Enter
575
+ assert_equal ['55', '', '55'], readonce.lines(chomp: true)
576
+ end
577
+
578
+ def test_toggle_sort
579
+ ['--toggle-sort=ctrl-r', '--bind=ctrl-r:toggle-sort'].each do |opt|
580
+ tmux.send_keys "seq 1 111 | #{fzf("-m +s --tac #{opt} -q11")}", :Enter
581
+ tmux.until { |lines| assert_equal '> 111', lines[-3] }
582
+ tmux.send_keys :Tab
583
+ tmux.until { |lines| assert_equal ' 4/111 -S (1)', lines[-2] }
584
+ tmux.send_keys 'C-R'
585
+ tmux.until { |lines| assert_equal '> 11', lines[-3] }
586
+ tmux.send_keys :Tab
587
+ tmux.until { |lines| assert_equal ' 4/111 +S (2)', lines[-2] }
588
+ tmux.send_keys :Enter
589
+ assert_equal %w[111 11], readonce.lines(chomp: true)
590
+ end
591
+ end
592
+
593
+ def test_unicode_case
594
+ writelines(tempname, %w[строКА1 СТРОКА2 строка3 Строка4])
595
+ assert_equal %w[СТРОКА2 Строка4], `#{FZF} -fС < #{tempname}`.lines(chomp: true)
596
+ assert_equal %w[строКА1 СТРОКА2 строка3 Строка4], `#{FZF} -fс < #{tempname}`.lines(chomp: true)
597
+ end
598
+
599
+ def test_tiebreak
600
+ input = %w[
601
+ --foobar--------
602
+ -----foobar---
603
+ ----foobar--
604
+ -------foobar-
605
+ ]
606
+ writelines(tempname, input)
607
+
608
+ assert_equal input, `#{FZF} -ffoobar --tiebreak=index < #{tempname}`.lines(chomp: true)
609
+
610
+ by_length = %w[
611
+ ----foobar--
612
+ -----foobar---
613
+ -------foobar-
614
+ --foobar--------
615
+ ]
616
+ assert_equal by_length, `#{FZF} -ffoobar < #{tempname}`.lines(chomp: true)
617
+ assert_equal by_length, `#{FZF} -ffoobar --tiebreak=length < #{tempname}`.lines(chomp: true)
618
+
619
+ by_begin = %w[
620
+ --foobar--------
621
+ ----foobar--
622
+ -----foobar---
623
+ -------foobar-
624
+ ]
625
+ assert_equal by_begin, `#{FZF} -ffoobar --tiebreak=begin < #{tempname}`.lines(chomp: true)
626
+ assert_equal by_begin, `#{FZF} -f"!z foobar" -x --tiebreak begin < #{tempname}`.lines(chomp: true)
627
+
628
+ assert_equal %w[
629
+ -------foobar-
630
+ ----foobar--
631
+ -----foobar---
632
+ --foobar--------
633
+ ], `#{FZF} -ffoobar --tiebreak end < #{tempname}`.lines(chomp: true)
634
+
635
+ assert_equal input, `#{FZF} -f"!z" -x --tiebreak end < #{tempname}`.lines(chomp: true)
636
+ end
637
+
638
+ def test_tiebreak_index_begin
639
+ writelines(tempname, [
640
+ 'xoxxxxxoxx',
641
+ 'xoxxxxxox',
642
+ 'xxoxxxoxx',
643
+ 'xxxoxoxxx',
644
+ 'xxxxoxox',
645
+ ' xxoxoxxx'
646
+ ])
647
+
648
+ assert_equal [
649
+ 'xxxxoxox',
650
+ ' xxoxoxxx',
651
+ 'xxxoxoxxx',
652
+ 'xxoxxxoxx',
653
+ 'xoxxxxxox',
654
+ 'xoxxxxxoxx'
655
+ ], `#{FZF} -foo < #{tempname}`.lines(chomp: true)
656
+
657
+ assert_equal [
658
+ 'xxxoxoxxx',
659
+ 'xxxxoxox',
660
+ ' xxoxoxxx',
661
+ 'xxoxxxoxx',
662
+ 'xoxxxxxoxx',
663
+ 'xoxxxxxox'
664
+ ], `#{FZF} -foo --tiebreak=index < #{tempname}`.lines(chomp: true)
665
+
666
+ # Note that --tiebreak=begin is now based on the first occurrence of the
667
+ # first character on the pattern
668
+ assert_equal [
669
+ ' xxoxoxxx',
670
+ 'xxxoxoxxx',
671
+ 'xxxxoxox',
672
+ 'xxoxxxoxx',
673
+ 'xoxxxxxoxx',
674
+ 'xoxxxxxox'
675
+ ], `#{FZF} -foo --tiebreak=begin < #{tempname}`.lines(chomp: true)
676
+
677
+ assert_equal [
678
+ ' xxoxoxxx',
679
+ 'xxxoxoxxx',
680
+ 'xxxxoxox',
681
+ 'xxoxxxoxx',
682
+ 'xoxxxxxox',
683
+ 'xoxxxxxoxx'
684
+ ], `#{FZF} -foo --tiebreak=begin,length < #{tempname}`.lines(chomp: true)
685
+ end
686
+
687
+ def test_tiebreak_begin_algo_v2
688
+ writelines(tempname, [
689
+ 'baz foo bar',
690
+ 'foo bar baz'
691
+ ])
692
+ assert_equal [
693
+ 'foo bar baz',
694
+ 'baz foo bar'
695
+ ], `#{FZF} -fbar --tiebreak=begin --algo=v2 < #{tempname}`.lines(chomp: true)
696
+ end
697
+
698
+ def test_tiebreak_end
699
+ writelines(tempname, [
700
+ 'xoxxxxxxxx',
701
+ 'xxoxxxxxxx',
702
+ 'xxxoxxxxxx',
703
+ 'xxxxoxxxx',
704
+ 'xxxxxoxxx',
705
+ ' xxxxoxxx'
706
+ ])
707
+
708
+ assert_equal [
709
+ ' xxxxoxxx',
710
+ 'xxxxoxxxx',
711
+ 'xxxxxoxxx',
712
+ 'xoxxxxxxxx',
713
+ 'xxoxxxxxxx',
714
+ 'xxxoxxxxxx'
715
+ ], `#{FZF} -fo < #{tempname}`.lines(chomp: true)
716
+
717
+ assert_equal [
718
+ 'xxxxxoxxx',
719
+ ' xxxxoxxx',
720
+ 'xxxxoxxxx',
721
+ 'xxxoxxxxxx',
722
+ 'xxoxxxxxxx',
723
+ 'xoxxxxxxxx'
724
+ ], `#{FZF} -fo --tiebreak=end < #{tempname}`.lines(chomp: true)
725
+
726
+ assert_equal [
727
+ 'xxxxxoxxx',
728
+ ' xxxxoxxx',
729
+ 'xxxxoxxxx',
730
+ 'xxxoxxxxxx',
731
+ 'xxoxxxxxxx',
732
+ 'xoxxxxxxxx'
733
+ ], `#{FZF} -fo --tiebreak=end,length,begin < #{tempname}`.lines(chomp: true)
734
+ end
735
+
736
+ def test_tiebreak_length_with_nth
737
+ input = %w[
738
+ 1:hell
739
+ 123:hello
740
+ 12345:he
741
+ 1234567:h
742
+ ]
743
+ writelines(tempname, input)
744
+
745
+ output = %w[
746
+ 1:hell
747
+ 12345:he
748
+ 123:hello
749
+ 1234567:h
750
+ ]
751
+ assert_equal output, `#{FZF} -fh < #{tempname}`.lines(chomp: true)
752
+
753
+ # Since 0.16.8, --nth doesn't affect --tiebreak
754
+ assert_equal output, `#{FZF} -fh -n2 -d: < #{tempname}`.lines(chomp: true)
755
+ end
756
+
757
+ def test_invalid_cache
758
+ tmux.send_keys "(echo d; echo D; echo x) | #{fzf('-q d')}", :Enter
759
+ tmux.until { |lines| assert_equal ' 2/3', lines[-2] }
760
+ tmux.send_keys :BSpace
761
+ tmux.until { |lines| assert_equal ' 3/3', lines[-2] }
762
+ tmux.send_keys :D
763
+ tmux.until { |lines| assert_equal ' 1/3', lines[-2] }
764
+ tmux.send_keys :Enter
765
+ end
766
+
767
+ def test_invalid_cache_query_type
768
+ command = %[(echo 'foo$bar'; echo 'barfoo'; echo 'foo^bar'; echo "foo'1-2"; seq 100) | #{fzf}]
769
+
770
+ # Suffix match
771
+ tmux.send_keys command, :Enter
772
+ tmux.until { |lines| assert_equal 104, lines.match_count }
773
+ tmux.send_keys 'foo$'
774
+ tmux.until { |lines| assert_equal 1, lines.match_count }
775
+ tmux.send_keys 'bar'
776
+ tmux.until { |lines| assert_equal 1, lines.match_count }
777
+ tmux.send_keys :Enter
778
+
779
+ # Prefix match
780
+ tmux.prepare
781
+ tmux.send_keys command, :Enter
782
+ tmux.until { |lines| assert_equal 104, lines.match_count }
783
+ tmux.send_keys '^bar'
784
+ tmux.until { |lines| assert_equal 1, lines.match_count }
785
+ tmux.send_keys 'C-a', 'foo'
786
+ tmux.until { |lines| assert_equal 1, lines.match_count }
787
+ tmux.send_keys :Enter
788
+
789
+ # Exact match
790
+ tmux.prepare
791
+ tmux.send_keys command, :Enter
792
+ tmux.until { |lines| assert_equal 104, lines.match_count }
793
+ tmux.send_keys "'12"
794
+ tmux.until { |lines| assert_equal 1, lines.match_count }
795
+ tmux.send_keys 'C-a', 'foo'
796
+ tmux.until { |lines| assert_equal 1, lines.match_count }
797
+ end
798
+
799
+ def test_smart_case_for_each_term
800
+ assert_equal 1, `echo Foo bar | #{FZF} -x -f "foo Fbar" | wc -l`.to_i
801
+ end
802
+
803
+ def test_bind
804
+ tmux.send_keys "seq 1 1000 | #{fzf('-m --bind=ctrl-j:accept,u:up,T:toggle-up,t:toggle')}", :Enter
805
+ tmux.until { |lines| assert_equal ' 1000/1000 (0)', lines[-2] }
806
+ tmux.send_keys 'uuu', 'TTT', 'tt', 'uu', 'ttt', 'C-j'
807
+ assert_equal %w[4 5 6 9], readonce.lines(chomp: true)
808
+ end
809
+
810
+ def test_bind_print_query
811
+ tmux.send_keys "seq 1 1000 | #{fzf('-m --bind=ctrl-j:print-query')}", :Enter
812
+ tmux.until { |lines| assert_equal ' 1000/1000 (0)', lines[-2] }
813
+ tmux.send_keys 'print-my-query', 'C-j'
814
+ assert_equal %w[print-my-query], readonce.lines(chomp: true)
815
+ end
816
+
817
+ def test_bind_replace_query
818
+ tmux.send_keys "seq 1 1000 | #{fzf('--print-query --bind=ctrl-j:replace-query')}", :Enter
819
+ tmux.send_keys '1'
820
+ tmux.until { |lines| assert_equal ' 272/1000', lines[-2] }
821
+ tmux.send_keys 'C-k', 'C-j'
822
+ tmux.until { |lines| assert_equal ' 29/1000', lines[-2] }
823
+ tmux.until { |lines| assert_equal '> 10', lines[-1] }
824
+ end
825
+
826
+ def test_long_line
827
+ data = '.' * 256 * 1024
828
+ File.open(tempname, 'w') do |f|
829
+ f << data
830
+ end
831
+ assert_equal data, `#{FZF} -f . < #{tempname}`.chomp
832
+ end
833
+
834
+ def test_read0
835
+ lines = `find .`.lines(chomp: true)
836
+ assert_equal lines.last, `find . | #{FZF} -e -f "^#{lines.last}$"`.chomp
837
+ assert_equal \
838
+ lines.last,
839
+ `find . -print0 | #{FZF} --read0 -e -f "^#{lines.last}$"`.chomp
840
+ end
841
+
842
+ def test_select_all_deselect_all_toggle_all
843
+ tmux.send_keys "seq 100 | #{fzf('--bind ctrl-a:select-all,ctrl-d:deselect-all,ctrl-t:toggle-all --multi')}", :Enter
844
+ tmux.until { |lines| assert_equal ' 100/100 (0)', lines[-2] }
845
+ tmux.send_keys :BTab, :BTab, :BTab
846
+ tmux.until { |lines| assert_equal ' 100/100 (3)', lines[-2] }
847
+ tmux.send_keys 'C-t'
848
+ tmux.until { |lines| assert_equal ' 100/100 (97)', lines[-2] }
849
+ tmux.send_keys 'C-a'
850
+ tmux.until { |lines| assert_equal ' 100/100 (100)', lines[-2] }
851
+ tmux.send_keys :Tab, :Tab
852
+ tmux.until { |lines| assert_equal ' 100/100 (98)', lines[-2] }
853
+ tmux.send_keys '100'
854
+ tmux.until { |lines| assert_equal 1, lines.match_count }
855
+ tmux.send_keys 'C-d'
856
+ tmux.until { |lines| assert_equal ' 1/100 (97)', lines[-2] }
857
+ tmux.send_keys 'C-u'
858
+ tmux.until { |lines| assert_equal 100, lines.match_count }
859
+ tmux.send_keys 'C-d'
860
+ tmux.until { |lines| assert_equal ' 100/100 (0)', lines[-2] }
861
+ tmux.send_keys :BTab, :BTab
862
+ tmux.until { |lines| assert_equal ' 100/100 (2)', lines[-2] }
863
+ tmux.send_keys 0
864
+ tmux.until { |lines| assert_equal ' 10/100 (2)', lines[-2] }
865
+ tmux.send_keys 'C-a'
866
+ tmux.until { |lines| assert_equal ' 10/100 (12)', lines[-2] }
867
+ tmux.send_keys :Enter
868
+ assert_equal %w[1 2 10 20 30 40 50 60 70 80 90 100],
869
+ readonce.lines(chomp: true)
870
+ end
871
+
872
+ def test_history
873
+ history_file = '/tmp/fzf-test-history'
874
+
875
+ # History with limited number of entries
876
+ begin
877
+ File.unlink(history_file)
878
+ rescue StandardError
879
+ nil
880
+ end
881
+ opts = "--history=#{history_file} --history-size=4"
882
+ input = %w[00 11 22 33 44]
883
+ input.each do |keys|
884
+ tmux.prepare
885
+ tmux.send_keys "seq 100 | #{fzf(opts)}", :Enter
886
+ tmux.until { |lines| assert_equal ' 100/100', lines[-2] }
887
+ tmux.send_keys keys
888
+ tmux.until { |lines| assert_equal ' 1/100', lines[-2] }
889
+ tmux.send_keys :Enter
890
+ end
891
+ wait do
892
+ assert_path_exists history_file
893
+ assert_equal input[1..-1], File.readlines(history_file, chomp: true)
894
+ end
895
+
896
+ # Update history entries (not changed on disk)
897
+ tmux.send_keys "seq 100 | #{fzf(opts)}", :Enter
898
+ tmux.until { |lines| assert_equal ' 100/100', lines[-2] }
899
+ tmux.send_keys 'C-p'
900
+ tmux.until { |lines| assert_equal '> 44', lines[-1] }
901
+ tmux.send_keys 'C-p'
902
+ tmux.until { |lines| assert_equal '> 33', lines[-1] }
903
+ tmux.send_keys :BSpace
904
+ tmux.until { |lines| assert_equal '> 3', lines[-1] }
905
+ tmux.send_keys 1
906
+ tmux.until { |lines| assert_equal '> 31', lines[-1] }
907
+ tmux.send_keys 'C-p'
908
+ tmux.until { |lines| assert_equal '> 22', lines[-1] }
909
+ tmux.send_keys 'C-n'
910
+ tmux.until { |lines| assert_equal '> 31', lines[-1] }
911
+ tmux.send_keys 0
912
+ tmux.until { |lines| assert_equal '> 310', lines[-1] }
913
+ tmux.send_keys :Enter
914
+ wait do
915
+ assert_path_exists history_file
916
+ assert_equal %w[22 33 44 310], File.readlines(history_file, chomp: true)
917
+ end
918
+
919
+ # Respect --bind option
920
+ tmux.send_keys "seq 100 | #{fzf(opts + ' --bind ctrl-p:next-history,ctrl-n:previous-history')}", :Enter
921
+ tmux.until { |lines| assert_equal ' 100/100', lines[-2] }
922
+ tmux.send_keys 'C-n', 'C-n', 'C-n', 'C-n', 'C-p'
923
+ tmux.until { |lines| assert_equal '> 33', lines[-1] }
924
+ tmux.send_keys :Enter
925
+ ensure
926
+ File.unlink(history_file)
927
+ end
928
+
929
+ def test_execute
930
+ output = '/tmp/fzf-test-execute'
931
+ opts = %[--bind "alt-a:execute(echo /{}/ >> #{output}),alt-b:execute[echo /{}{}/ >> #{output}],C:execute:echo /{}{}{}/ >> #{output}"]
932
+ writelines(tempname, %w[foo'bar foo"bar foo$bar])
933
+ tmux.send_keys "cat #{tempname} | #{fzf(opts)}", :Enter
934
+ tmux.until { |lines| assert_equal ' 3/3', lines[-2] }
935
+ tmux.send_keys :Escape, :a
936
+ tmux.send_keys :Escape, :a
937
+ tmux.send_keys :Up
938
+ tmux.send_keys :Escape, :b
939
+ tmux.send_keys :Escape, :b
940
+ tmux.send_keys :Up
941
+ tmux.send_keys :C
942
+ tmux.send_keys 'barfoo'
943
+ tmux.until { |lines| assert_equal ' 0/3', lines[-2] }
944
+ tmux.send_keys :Escape, :a
945
+ tmux.send_keys :Escape, :b
946
+ wait do
947
+ assert_path_exists output
948
+ assert_equal %w[
949
+ /foo'bar/ /foo'bar/
950
+ /foo"barfoo"bar/ /foo"barfoo"bar/
951
+ /foo$barfoo$barfoo$bar/
952
+ ], File.readlines(output, chomp: true)
953
+ end
954
+ ensure
955
+ begin
956
+ File.unlink(output)
957
+ rescue StandardError
958
+ nil
959
+ end
960
+ end
961
+
962
+ def test_execute_multi
963
+ output = '/tmp/fzf-test-execute-multi'
964
+ opts = %[--multi --bind "alt-a:execute-multi(echo {}/{+} >> #{output})"]
965
+ writelines(tempname, %w[foo'bar foo"bar foo$bar foobar])
966
+ tmux.send_keys "cat #{tempname} | #{fzf(opts)}", :Enter
967
+ tmux.until { |lines| assert_equal ' 4/4 (0)', lines[-2] }
968
+ tmux.send_keys :Escape, :a
969
+ tmux.send_keys :BTab, :BTab, :BTab
970
+ tmux.until { |lines| assert_equal ' 4/4 (3)', lines[-2] }
971
+ tmux.send_keys :Escape, :a
972
+ tmux.send_keys :Tab, :Tab
973
+ tmux.until { |lines| assert_equal ' 4/4 (3)', lines[-2] }
974
+ tmux.send_keys :Escape, :a
975
+ wait do
976
+ assert_path_exists output
977
+ assert_equal [
978
+ %(foo'bar/foo'bar),
979
+ %(foo'bar foo"bar foo$bar/foo'bar foo"bar foo$bar),
980
+ %(foo'bar foo"bar foobar/foo'bar foo"bar foobar)
981
+ ], File.readlines(output, chomp: true)
982
+ end
983
+ ensure
984
+ begin
985
+ File.unlink(output)
986
+ rescue StandardError
987
+ nil
988
+ end
989
+ end
990
+
991
+ def test_execute_plus_flag
992
+ output = tempname + '.tmp'
993
+ begin
994
+ File.unlink(output)
995
+ rescue StandardError
996
+ nil
997
+ end
998
+ writelines(tempname, ['foo bar', '123 456'])
999
+
1000
+ tmux.send_keys "cat #{tempname} | #{FZF} --multi --bind 'x:execute-silent(echo {+}/{}/{+2}/{2} >> #{output})'", :Enter
1001
+
1002
+ tmux.until { |lines| assert_equal ' 2/2 (0)', lines[-2] }
1003
+ tmux.send_keys 'xy'
1004
+ tmux.until { |lines| assert_equal ' 0/2 (0)', lines[-2] }
1005
+ tmux.send_keys :BSpace
1006
+ tmux.until { |lines| assert_equal ' 2/2 (0)', lines[-2] }
1007
+
1008
+ tmux.send_keys :Up
1009
+ tmux.send_keys :Tab
1010
+ tmux.send_keys 'xy'
1011
+ tmux.until { |lines| assert_equal ' 0/2 (1)', lines[-2] }
1012
+ tmux.send_keys :BSpace
1013
+ tmux.until { |lines| assert_equal ' 2/2 (1)', lines[-2] }
1014
+
1015
+ tmux.send_keys :Tab
1016
+ tmux.send_keys 'xy'
1017
+ tmux.until { |lines| assert_equal ' 0/2 (2)', lines[-2] }
1018
+ tmux.send_keys :BSpace
1019
+ tmux.until { |lines| assert_equal ' 2/2 (2)', lines[-2] }
1020
+
1021
+ wait do
1022
+ assert_path_exists output
1023
+ assert_equal [
1024
+ %(foo bar/foo bar/bar/bar),
1025
+ %(123 456/foo bar/456/bar),
1026
+ %(123 456 foo bar/foo bar/456 bar/bar)
1027
+ ], File.readlines(output, chomp: true)
1028
+ end
1029
+ rescue StandardError
1030
+ begin
1031
+ File.unlink(output)
1032
+ rescue StandardError
1033
+ nil
1034
+ end
1035
+ end
1036
+
1037
+ def test_execute_shell
1038
+ # Custom script to use as $SHELL
1039
+ output = tempname + '.out'
1040
+ begin
1041
+ File.unlink(output)
1042
+ rescue StandardError
1043
+ nil
1044
+ end
1045
+ writelines(tempname,
1046
+ ['#!/usr/bin/env bash', "echo $1 / $2 > #{output}"])
1047
+ system("chmod +x #{tempname}")
1048
+
1049
+ tmux.send_keys "echo foo | SHELL=#{tempname} fzf --bind 'enter:execute:{}bar'", :Enter
1050
+ tmux.until { |lines| assert_equal ' 1/1', lines[-2] }
1051
+ tmux.send_keys :Enter
1052
+ tmux.until { |lines| assert_equal ' 1/1', lines[-2] }
1053
+ wait do
1054
+ assert_path_exists output
1055
+ assert_equal ["-c / 'foo'bar"], File.readlines(output, chomp: true)
1056
+ end
1057
+ ensure
1058
+ begin
1059
+ File.unlink(output)
1060
+ rescue StandardError
1061
+ nil
1062
+ end
1063
+ end
1064
+
1065
+ def test_cycle
1066
+ tmux.send_keys "seq 8 | #{fzf(:cycle)}", :Enter
1067
+ tmux.until { |lines| assert_equal ' 8/8', lines[-2] }
1068
+ tmux.send_keys :Down
1069
+ tmux.until { |lines| assert_equal '> 8', lines[-10] }
1070
+ tmux.send_keys :Down
1071
+ tmux.until { |lines| assert_equal '> 7', lines[-9] }
1072
+ tmux.send_keys :Up
1073
+ tmux.until { |lines| assert_equal '> 8', lines[-10] }
1074
+ tmux.send_keys :PgUp
1075
+ tmux.until { |lines| assert_equal '> 8', lines[-10] }
1076
+ tmux.send_keys :Up
1077
+ tmux.until { |lines| assert_equal '> 1', lines[-3] }
1078
+ tmux.send_keys :PgDn
1079
+ tmux.until { |lines| assert_equal '> 1', lines[-3] }
1080
+ tmux.send_keys :Down
1081
+ tmux.until { |lines| assert_equal '> 8', lines[-10] }
1082
+ end
1083
+
1084
+ def test_header_lines
1085
+ tmux.send_keys "seq 100 | #{fzf('--header-lines=10 -q 5')}", :Enter
1086
+ 2.times do
1087
+ tmux.until do |lines|
1088
+ assert_equal ' 18/90', lines[-2]
1089
+ assert_equal ' 1', lines[-3]
1090
+ assert_equal ' 2', lines[-4]
1091
+ assert_equal '> 50', lines[-13]
1092
+ end
1093
+ tmux.send_keys :Down
1094
+ end
1095
+ tmux.send_keys :Enter
1096
+ assert_equal '50', readonce.chomp
1097
+ end
1098
+
1099
+ def test_header_lines_reverse
1100
+ tmux.send_keys "seq 100 | #{fzf('--header-lines=10 -q 5 --reverse')}", :Enter
1101
+ 2.times do
1102
+ tmux.until do |lines|
1103
+ assert_equal ' 18/90', lines[1]
1104
+ assert_equal ' 1', lines[2]
1105
+ assert_equal ' 2', lines[3]
1106
+ assert_equal '> 50', lines[12]
1107
+ end
1108
+ tmux.send_keys :Up
1109
+ end
1110
+ tmux.send_keys :Enter
1111
+ assert_equal '50', readonce.chomp
1112
+ end
1113
+
1114
+ def test_header_lines_reverse_list
1115
+ tmux.send_keys "seq 100 | #{fzf('--header-lines=10 -q 5 --layout=reverse-list')}", :Enter
1116
+ 2.times do
1117
+ tmux.until do |lines|
1118
+ assert_equal '> 50', lines[0]
1119
+ assert_equal ' 2', lines[-4]
1120
+ assert_equal ' 1', lines[-3]
1121
+ assert_equal ' 18/90', lines[-2]
1122
+ end
1123
+ tmux.send_keys :Up
1124
+ end
1125
+ tmux.send_keys :Enter
1126
+ assert_equal '50', readonce.chomp
1127
+ end
1128
+
1129
+ def test_header_lines_overflow
1130
+ tmux.send_keys "seq 100 | #{fzf('--header-lines=200')}", :Enter
1131
+ tmux.until do |lines|
1132
+ assert_equal ' 0/0', lines[-2]
1133
+ assert_equal ' 1', lines[-3]
1134
+ end
1135
+ tmux.send_keys :Enter
1136
+ assert_equal '', readonce.chomp
1137
+ end
1138
+
1139
+ def test_header_lines_with_nth
1140
+ tmux.send_keys "seq 100 | #{fzf('--header-lines 5 --with-nth 1,1,1,1,1')}", :Enter
1141
+ tmux.until do |lines|
1142
+ assert_equal ' 95/95', lines[-2]
1143
+ assert_equal ' 11111', lines[-3]
1144
+ assert_equal ' 55555', lines[-7]
1145
+ assert_equal '> 66666', lines[-8]
1146
+ end
1147
+ tmux.send_keys :Enter
1148
+ assert_equal '6', readonce.chomp
1149
+ end
1150
+
1151
+ def test_header
1152
+ tmux.send_keys "seq 100 | #{fzf("--header \"$(head -5 #{FILE})\"")}", :Enter
1153
+ header = File.readlines(FILE, chomp: true).take(5)
1154
+ tmux.until do |lines|
1155
+ assert_equal ' 100/100', lines[-2]
1156
+ assert_equal header.map { |line| " #{line}".rstrip }, lines[-7..-3]
1157
+ assert_equal '> 1', lines[-8]
1158
+ end
1159
+ end
1160
+
1161
+ def test_header_reverse
1162
+ tmux.send_keys "seq 100 | #{fzf("--header \"$(head -5 #{FILE})\" --reverse")}", :Enter
1163
+ header = File.readlines(FILE, chomp: true).take(5)
1164
+ tmux.until do |lines|
1165
+ assert_equal ' 100/100', lines[1]
1166
+ assert_equal header.map { |line| " #{line}".rstrip }, lines[2..6]
1167
+ assert_equal '> 1', lines[7]
1168
+ end
1169
+ end
1170
+
1171
+ def test_header_reverse_list
1172
+ tmux.send_keys "seq 100 | #{fzf("--header \"$(head -5 #{FILE})\" --layout=reverse-list")}", :Enter
1173
+ header = File.readlines(FILE, chomp: true).take(5)
1174
+ tmux.until do |lines|
1175
+ assert_equal ' 100/100', lines[-2]
1176
+ assert_equal header.map { |line| " #{line}".rstrip }, lines[-7..-3]
1177
+ assert_equal '> 1', lines[0]
1178
+ end
1179
+ end
1180
+
1181
+ def test_header_and_header_lines
1182
+ tmux.send_keys "seq 100 | #{fzf("--header-lines 10 --header \"$(head -5 #{FILE})\"")}", :Enter
1183
+ header = File.readlines(FILE, chomp: true).take(5)
1184
+ tmux.until do |lines|
1185
+ assert_equal ' 90/90', lines[-2]
1186
+ assert_equal header.map { |line| " #{line}".rstrip }, lines[-7...-2]
1187
+ assert_equal (' 1'..' 10').to_a.reverse, lines[-17...-7]
1188
+ end
1189
+ end
1190
+
1191
+ def test_header_and_header_lines_reverse
1192
+ tmux.send_keys "seq 100 | #{fzf("--reverse --header-lines 10 --header \"$(head -5 #{FILE})\"")}", :Enter
1193
+ header = File.readlines(FILE, chomp: true).take(5)
1194
+ tmux.until do |lines|
1195
+ assert_equal ' 90/90', lines[1]
1196
+ assert_equal header.map { |line| " #{line}".rstrip }, lines[2...7]
1197
+ assert_equal (' 1'..' 10').to_a, lines[7...17]
1198
+ end
1199
+ end
1200
+
1201
+ def test_header_and_header_lines_reverse_list
1202
+ tmux.send_keys "seq 100 | #{fzf("--layout=reverse-list --header-lines 10 --header \"$(head -5 #{FILE})\"")}", :Enter
1203
+ header = File.readlines(FILE, chomp: true).take(5)
1204
+ tmux.until do |lines|
1205
+ assert_equal ' 90/90', lines[-2]
1206
+ assert_equal header.map { |line| " #{line}".rstrip }, lines[-7...-2]
1207
+ assert_equal (' 1'..' 10').to_a.reverse, lines[-17...-7]
1208
+ end
1209
+ end
1210
+
1211
+ def test_cancel
1212
+ tmux.send_keys "seq 10 | #{fzf('--bind 2:cancel')}", :Enter
1213
+ tmux.until { |lines| assert_equal ' 10/10', lines[-2] }
1214
+ tmux.send_keys '123'
1215
+ tmux.until do |lines|
1216
+ assert_equal '> 3', lines[-1]
1217
+ assert_equal ' 1/10', lines[-2]
1218
+ end
1219
+ tmux.send_keys 'C-y', 'C-y'
1220
+ tmux.until { |lines| assert_equal '> 311', lines[-1] }
1221
+ tmux.send_keys 2
1222
+ tmux.until { |lines| assert_equal '>', lines[-1] }
1223
+ tmux.send_keys 2
1224
+ tmux.prepare
1225
+ end
1226
+
1227
+ def test_margin
1228
+ tmux.send_keys "yes | head -1000 | #{fzf('--margin 5,3')}", :Enter
1229
+ tmux.until do |lines|
1230
+ assert_equal '', lines[4]
1231
+ assert_equal ' y', lines[5]
1232
+ end
1233
+ tmux.send_keys :Enter
1234
+ end
1235
+
1236
+ def test_margin_reverse
1237
+ tmux.send_keys "seq 1000 | #{fzf('--margin 7,5 --reverse')}", :Enter
1238
+ tmux.until { |lines| assert_equal ' 1000/1000', lines[1 + 7] }
1239
+ tmux.send_keys :Enter
1240
+ end
1241
+
1242
+ def test_margin_reverse_list
1243
+ tmux.send_keys "yes | head -1000 | #{fzf('--margin 5,3 --layout=reverse-list')}", :Enter
1244
+ tmux.until do |lines|
1245
+ assert_equal '', lines[4]
1246
+ assert_equal ' > y', lines[5]
1247
+ end
1248
+ tmux.send_keys :Enter
1249
+ end
1250
+
1251
+ def test_tabstop
1252
+ writelines(tempname, %W[f\too\tba\tr\tbaz\tbarfooq\tux])
1253
+ {
1254
+ 1 => '> f oo ba r baz barfooq ux',
1255
+ 2 => '> f oo ba r baz barfooq ux',
1256
+ 3 => '> f oo ba r baz barfooq ux',
1257
+ 4 => '> f oo ba r baz barfooq ux',
1258
+ 5 => '> f oo ba r baz barfooq ux',
1259
+ 6 => '> f oo ba r baz barfooq ux',
1260
+ 7 => '> f oo ba r baz barfooq ux',
1261
+ 8 => '> f oo ba r baz barfooq ux',
1262
+ 9 => '> f oo ba r baz barfooq ux'
1263
+ }.each do |ts, exp|
1264
+ tmux.prepare
1265
+ tmux.send_keys %(cat #{tempname} | fzf --tabstop=#{ts}), :Enter
1266
+ tmux.until(true) do |lines|
1267
+ assert_equal exp, lines[-3]
1268
+ end
1269
+ tmux.send_keys :Enter
1270
+ end
1271
+ end
1272
+
1273
+ def test_with_nth_basic
1274
+ writelines(tempname, ['hello world ', 'byebye'])
1275
+ assert_equal \
1276
+ 'hello world ',
1277
+ `#{FZF} -f"^he hehe" -x -n 2.. --with-nth 2,1,1 < #{tempname}`.chomp
1278
+ end
1279
+
1280
+ def test_with_nth_ansi
1281
+ writelines(tempname, ["\x1b[33mhello \x1b[34;1mworld\x1b[m ", 'byebye'])
1282
+ assert_equal \
1283
+ 'hello world ',
1284
+ `#{FZF} -f"^he hehe" -x -n 2.. --with-nth 2,1,1 --ansi < #{tempname}`.chomp
1285
+ end
1286
+
1287
+ def test_with_nth_no_ansi
1288
+ src = "\x1b[33mhello \x1b[34;1mworld\x1b[m "
1289
+ writelines(tempname, [src, 'byebye'])
1290
+ assert_equal \
1291
+ src,
1292
+ `#{FZF} -fhehe -x -n 2.. --with-nth 2,1,1 --no-ansi < #{tempname}`.chomp
1293
+ end
1294
+
1295
+ def test_exit_0_exit_code
1296
+ `echo foo | #{FZF} -q bar -0`
1297
+ assert_equal 1, $CHILD_STATUS.exitstatus
1298
+ end
1299
+
1300
+ def test_invalid_option
1301
+ lines = `#{FZF} --foobar 2>&1`
1302
+ assert_equal 2, $CHILD_STATUS.exitstatus
1303
+ assert_includes lines, 'unknown option: --foobar'
1304
+ end
1305
+
1306
+ def test_filter_exitstatus
1307
+ # filter / streaming filter
1308
+ ['', '--no-sort'].each do |opts|
1309
+ assert_includes `echo foo | #{FZF} -f foo #{opts}`, 'foo'
1310
+ assert_equal 0, $CHILD_STATUS.exitstatus
1311
+
1312
+ assert_empty `echo foo | #{FZF} -f bar #{opts}`
1313
+ assert_equal 1, $CHILD_STATUS.exitstatus
1314
+ end
1315
+ end
1316
+
1317
+ def test_exitstatus_empty
1318
+ { '99' => '0', '999' => '1' }.each do |query, status|
1319
+ tmux.send_keys "seq 100 | #{FZF} -q #{query}; echo --$?--", :Enter
1320
+ tmux.until { |lines| assert_match %r{ [10]/100}, lines[-2] }
1321
+ tmux.send_keys :Enter
1322
+ tmux.until { |lines| assert_equal "--#{status}--", lines.last }
1323
+ end
1324
+ end
1325
+
1326
+ def test_default_extended
1327
+ assert_equal '100', `seq 100 | #{FZF} -f "1 00$"`.chomp
1328
+ assert_equal '', `seq 100 | #{FZF} -f "1 00$" +x`.chomp
1329
+ end
1330
+
1331
+ def test_exact
1332
+ assert_equal 4, `seq 123 | #{FZF} -f 13`.lines.length
1333
+ assert_equal 2, `seq 123 | #{FZF} -f 13 -e`.lines.length
1334
+ assert_equal 4, `seq 123 | #{FZF} -f 13 +e`.lines.length
1335
+ end
1336
+
1337
+ def test_or_operator
1338
+ assert_equal %w[1 5 10], `seq 10 | #{FZF} -f "1 | 5"`.lines(chomp: true)
1339
+ assert_equal %w[1 10 2 3 4 5 6 7 8 9],
1340
+ `seq 10 | #{FZF} -f '1 | !1'`.lines(chomp: true)
1341
+ end
1342
+
1343
+ def test_hscroll_off
1344
+ writelines(tempname, ['=' * 10_000 + '0123456789'])
1345
+ [0, 3, 6].each do |off|
1346
+ tmux.prepare
1347
+ tmux.send_keys "#{FZF} --hscroll-off=#{off} -q 0 < #{tempname}", :Enter
1348
+ tmux.until { |lines| assert lines[-3]&.end_with?((0..off).to_a.join + '..') }
1349
+ tmux.send_keys '9'
1350
+ tmux.until { |lines| assert lines[-3]&.end_with?('789') }
1351
+ tmux.send_keys :Enter
1352
+ end
1353
+ end
1354
+
1355
+ def test_partial_caching
1356
+ tmux.send_keys 'seq 1000 | fzf -e', :Enter
1357
+ tmux.until { |lines| assert_equal ' 1000/1000', lines[-2] }
1358
+ tmux.send_keys 11
1359
+ tmux.until { |lines| assert_equal ' 19/1000', lines[-2] }
1360
+ tmux.send_keys 'C-a', "'"
1361
+ tmux.until { |lines| assert_equal ' 28/1000', lines[-2] }
1362
+ tmux.send_keys :Enter
1363
+ end
1364
+
1365
+ def test_jump
1366
+ tmux.send_keys "seq 1000 | #{fzf("--multi --jump-labels 12345 --bind 'ctrl-j:jump'")}", :Enter
1367
+ tmux.until { |lines| assert_equal ' 1000/1000 (0)', lines[-2] }
1368
+ tmux.send_keys 'C-j'
1369
+ tmux.until { |lines| assert_equal '5 5', lines[-7] }
1370
+ tmux.until { |lines| assert_equal ' 6', lines[-8] }
1371
+ tmux.send_keys '5'
1372
+ tmux.until { |lines| assert_equal '> 5', lines[-7] }
1373
+ tmux.send_keys :Tab
1374
+ tmux.until { |lines| assert_equal ' >5', lines[-7] }
1375
+ tmux.send_keys 'C-j'
1376
+ tmux.until { |lines| assert_equal '5>5', lines[-7] }
1377
+ tmux.send_keys '2'
1378
+ tmux.until { |lines| assert_equal '> 2', lines[-4] }
1379
+ tmux.send_keys :Tab
1380
+ tmux.until { |lines| assert_equal ' >2', lines[-4] }
1381
+ tmux.send_keys 'C-j'
1382
+ tmux.until { |lines| assert_equal '5>5', lines[-7] }
1383
+
1384
+ # Press any key other than jump labels to cancel jump
1385
+ tmux.send_keys '6'
1386
+ tmux.until { |lines| assert_equal '> 1', lines[-3] }
1387
+ tmux.send_keys :Tab
1388
+ tmux.until { |lines| assert_equal '>>1', lines[-3] }
1389
+ tmux.send_keys :Enter
1390
+ assert_equal %w[5 2 1], readonce.lines(chomp: true)
1391
+ end
1392
+
1393
+ def test_jump_accept
1394
+ tmux.send_keys "seq 1000 | #{fzf("--multi --jump-labels 12345 --bind 'ctrl-j:jump-accept'")}", :Enter
1395
+ tmux.until { |lines| assert_equal ' 1000/1000 (0)', lines[-2] }
1396
+ tmux.send_keys 'C-j'
1397
+ tmux.until { |lines| assert_equal '5 5', lines[-7] }
1398
+ tmux.send_keys '3'
1399
+ assert_equal '3', readonce.chomp
1400
+ end
1401
+
1402
+ def test_pointer
1403
+ tmux.send_keys "seq 10 | #{fzf("--pointer '>>'")}", :Enter
1404
+ # Assert that specified pointer is displayed
1405
+ tmux.until { |lines| assert_equal '>> 1', lines[-3] }
1406
+ end
1407
+
1408
+ def test_pointer_with_jump
1409
+ tmux.send_keys "seq 10 | #{fzf("--multi --jump-labels 12345 --bind 'ctrl-j:jump' --pointer '>>'")}", :Enter
1410
+ tmux.until { |lines| assert_equal ' 10/10 (0)', lines[-2] }
1411
+ tmux.send_keys 'C-j'
1412
+ # Correctly padded jump label should appear
1413
+ tmux.until { |lines| assert_equal '5 5', lines[-7] }
1414
+ tmux.until { |lines| assert_equal ' 6', lines[-8] }
1415
+ tmux.send_keys '5'
1416
+ # Assert that specified pointer is displayed
1417
+ tmux.until { |lines| assert_equal '>> 5', lines[-7] }
1418
+ end
1419
+
1420
+ def test_marker
1421
+ tmux.send_keys "seq 10 | #{fzf("--multi --marker '>>'")}", :Enter
1422
+ tmux.until { |lines| assert_equal ' 10/10 (0)', lines[-2] }
1423
+ tmux.send_keys :BTab
1424
+ # Assert that specified marker is displayed
1425
+ tmux.until { |lines| assert_equal ' >>1', lines[-3] }
1426
+ end
1427
+
1428
+ def test_preview
1429
+ tmux.send_keys %(seq 1000 | sed s/^2$// | #{FZF} -m --preview 'sleep 0.2; echo {{}-{+}}' --bind ?:toggle-preview), :Enter
1430
+ tmux.until { |lines| assert_includes lines[1], ' {1-1} ' }
1431
+ tmux.send_keys :Up
1432
+ tmux.until { |lines| assert_includes lines[1], ' {-} ' }
1433
+ tmux.send_keys '555'
1434
+ tmux.until { |lines| assert_includes lines[1], ' {555-555} ' }
1435
+ tmux.send_keys '?'
1436
+ tmux.until { |lines| refute_includes lines[1], ' {555-555} ' }
1437
+ tmux.send_keys '?'
1438
+ tmux.until { |lines| assert_includes lines[1], ' {555-555} ' }
1439
+ tmux.send_keys :BSpace
1440
+ tmux.until { |lines| assert lines[-2]&.start_with?(' 28/1000 ') }
1441
+ tmux.send_keys 'foobar'
1442
+ tmux.until { |lines| refute_includes lines[1], ' {55-55} ' }
1443
+ tmux.send_keys 'C-u'
1444
+ tmux.until { |lines| assert_equal 1000, lines.match_count }
1445
+ tmux.until { |lines| assert_includes lines[1], ' {1-1} ' }
1446
+ tmux.send_keys :BTab
1447
+ tmux.until { |lines| assert_includes lines[1], ' {-1} ' }
1448
+ tmux.send_keys :BTab
1449
+ tmux.until { |lines| assert_includes lines[1], ' {3-1 } ' }
1450
+ tmux.send_keys :BTab
1451
+ tmux.until { |lines| assert_includes lines[1], ' {4-1 3} ' }
1452
+ tmux.send_keys :BTab
1453
+ tmux.until { |lines| assert_includes lines[1], ' {5-1 3 4} ' }
1454
+ end
1455
+
1456
+ def test_preview_hidden
1457
+ tmux.send_keys %(seq 1000 | #{FZF} --preview 'echo {{}-{}-$FZF_PREVIEW_LINES-$FZF_PREVIEW_COLUMNS}' --preview-window down:1:hidden --bind ?:toggle-preview), :Enter
1458
+ tmux.until { |lines| assert_equal '>', lines[-1] }
1459
+ tmux.send_keys '?'
1460
+ tmux.until { |lines| assert_match(/ {1-1-1-[0-9]+}/, lines[-2]) }
1461
+ tmux.send_keys '555'
1462
+ tmux.until { |lines| assert_match(/ {555-555-1-[0-9]+}/, lines[-2]) }
1463
+ tmux.send_keys '?'
1464
+ tmux.until { |lines| assert_equal '> 555', lines[-1] }
1465
+ end
1466
+
1467
+ def test_preview_size_0
1468
+ begin
1469
+ File.unlink(tempname)
1470
+ rescue StandardError
1471
+ nil
1472
+ end
1473
+ tmux.send_keys %(seq 100 | #{FZF} --reverse --preview 'echo {} >> #{tempname}; echo ' --preview-window 0), :Enter
1474
+ tmux.until do |lines|
1475
+ assert_equal 100, lines.item_count
1476
+ assert_equal ' 100/100', lines[1]
1477
+ assert_equal '> 1', lines[2]
1478
+ end
1479
+ wait do
1480
+ assert_path_exists tempname
1481
+ assert_equal %w[1], File.readlines(tempname, chomp: true)
1482
+ end
1483
+ tmux.send_keys :Down
1484
+ tmux.until { |lines| assert_equal '> 2', lines[3] }
1485
+ wait do
1486
+ assert_path_exists tempname
1487
+ assert_equal %w[1 2], File.readlines(tempname, chomp: true)
1488
+ end
1489
+ tmux.send_keys :Down
1490
+ tmux.until { |lines| assert_equal '> 3', lines[4] }
1491
+ wait do
1492
+ assert_path_exists tempname
1493
+ assert_equal %w[1 2 3], File.readlines(tempname, chomp: true)
1494
+ end
1495
+ end
1496
+
1497
+ def test_preview_flags
1498
+ tmux.send_keys %(seq 10 | sed 's/^/:: /; s/$/ /' |
1499
+ #{FZF} --multi --preview 'echo {{2}/{s2}/{+2}/{+s2}/{q}/{n}/{+n}}'), :Enter
1500
+ tmux.until { |lines| assert_includes lines[1], ' {1/1 /1/1 //0/0} ' }
1501
+ tmux.send_keys '123'
1502
+ tmux.until { |lines| assert_includes lines[1], ' {////123//} ' }
1503
+ tmux.send_keys 'C-u', '1'
1504
+ tmux.until { |lines| assert_equal 2, lines.match_count }
1505
+ tmux.until { |lines| assert_includes lines[1], ' {1/1 /1/1 /1/0/0} ' }
1506
+ tmux.send_keys :BTab
1507
+ tmux.until { |lines| assert_includes lines[1], ' {10/10 /1/1 /1/9/0} ' }
1508
+ tmux.send_keys :BTab
1509
+ tmux.until { |lines| assert_includes lines[1], ' {10/10 /1 10/1 10 /1/9/0 9} ' }
1510
+ tmux.send_keys '2'
1511
+ tmux.until { |lines| assert_includes lines[1], ' {//1 10/1 10 /12//0 9} ' }
1512
+ tmux.send_keys '3'
1513
+ tmux.until { |lines| assert_includes lines[1], ' {//1 10/1 10 /123//0 9} ' }
1514
+ end
1515
+
1516
+ def test_preview_file
1517
+ tmux.send_keys %[(echo foo bar; echo bar foo) | #{FZF} --multi --preview 'cat {+f} {+f2} {+nf} {+fn}' --print0], :Enter
1518
+ tmux.until { |lines| assert_includes lines[1], ' foo barbar00 ' }
1519
+ tmux.send_keys :BTab
1520
+ tmux.until { |lines| assert_includes lines[1], ' foo barbar00 ' }
1521
+ tmux.send_keys :BTab
1522
+ tmux.until { |lines| assert_includes lines[1], ' foo barbar foobarfoo0101 ' }
1523
+ end
1524
+
1525
+ def test_preview_q_no_match
1526
+ tmux.send_keys %(: | #{FZF} --preview 'echo foo {q}'), :Enter
1527
+ tmux.until { |lines| assert_equal 0, lines.match_count }
1528
+ tmux.until { |lines| refute_includes lines[1], ' foo ' }
1529
+ tmux.send_keys 'bar'
1530
+ tmux.until { |lines| assert_includes lines[1], ' foo bar ' }
1531
+ tmux.send_keys 'C-u'
1532
+ tmux.until { |lines| refute_includes lines[1], ' foo ' }
1533
+ end
1534
+
1535
+ def test_preview_q_no_match_with_initial_query
1536
+ tmux.send_keys %(: | #{FZF} --preview 'echo foo {q}{q}' --query foo), :Enter
1537
+ tmux.until { |lines| assert_equal 0, lines.match_count }
1538
+ tmux.until { |lines| assert_includes lines[1], ' foofoo ' }
1539
+ end
1540
+
1541
+ def test_no_clear
1542
+ tmux.send_keys "seq 10 | fzf --no-clear --inline-info --height 5 > #{tempname}", :Enter
1543
+ prompt = '> < 10/10'
1544
+ tmux.until { |lines| assert_equal prompt, lines[-1] }
1545
+ tmux.send_keys :Enter
1546
+ wait do
1547
+ assert_path_exists tempname
1548
+ assert_equal %w[1], File.readlines(tempname, chomp: true)
1549
+ end
1550
+ tmux.until { |lines| assert_equal prompt, lines[-1] }
1551
+ end
1552
+
1553
+ def test_info_hidden
1554
+ tmux.send_keys 'seq 10 | fzf --info=hidden', :Enter
1555
+ tmux.until { |lines| assert_equal '> 1', lines[-2] }
1556
+ end
1557
+
1558
+ def test_change_first_last
1559
+ tmux.send_keys %(seq 1000 | #{FZF} --bind change:first,alt-Z:last), :Enter
1560
+ tmux.until { |lines| assert_equal 1000, lines.match_count }
1561
+ tmux.send_keys :Up
1562
+ tmux.until { |lines| assert_equal '> 2', lines[-4] }
1563
+ tmux.send_keys 1
1564
+ tmux.until { |lines| assert_equal '> 1', lines[-3] }
1565
+ tmux.send_keys :Up
1566
+ tmux.until { |lines| assert_equal '> 10', lines[-4] }
1567
+ tmux.send_keys 1
1568
+ tmux.until { |lines| assert_equal '> 11', lines[-3] }
1569
+ tmux.send_keys 'C-u'
1570
+ tmux.until { |lines| assert_equal '> 1', lines[-3] }
1571
+ tmux.send_keys :Escape, 'Z'
1572
+ tmux.until { |lines| assert_equal '> 1000', lines[0] }
1573
+ tmux.send_keys :Enter
1574
+ end
1575
+
1576
+ def test_accept_non_empty
1577
+ tmux.send_keys %(seq 1000 | #{fzf('--print-query --bind enter:accept-non-empty')}), :Enter
1578
+ tmux.until { |lines| assert_equal 1000, lines.match_count }
1579
+ tmux.send_keys 'foo'
1580
+ tmux.until { |lines| assert_equal ' 0/1000', lines[-2] }
1581
+ # fzf doesn't exit since there's no selection
1582
+ tmux.send_keys :Enter
1583
+ tmux.until { |lines| assert_equal ' 0/1000', lines[-2] }
1584
+ tmux.send_keys 'C-u'
1585
+ tmux.until { |lines| assert_equal ' 1000/1000', lines[-2] }
1586
+ tmux.send_keys '999'
1587
+ tmux.until { |lines| assert_equal ' 1/1000', lines[-2] }
1588
+ tmux.send_keys :Enter
1589
+ assert_equal %w[999 999], readonce.lines(chomp: true)
1590
+ end
1591
+
1592
+ def test_accept_non_empty_with_multi_selection
1593
+ tmux.send_keys %(seq 1000 | #{fzf('-m --print-query --bind enter:accept-non-empty')}), :Enter
1594
+ tmux.until { |lines| assert_equal 1000, lines.match_count }
1595
+ tmux.send_keys :Tab
1596
+ tmux.until { |lines| assert_equal ' 1000/1000 (1)', lines[-2] }
1597
+ tmux.send_keys 'foo'
1598
+ tmux.until { |lines| assert_equal ' 0/1000 (1)', lines[-2] }
1599
+ # fzf will exit in this case even though there's no match for the current query
1600
+ tmux.send_keys :Enter
1601
+ assert_equal %w[foo 1], readonce.lines(chomp: true)
1602
+ end
1603
+
1604
+ def test_accept_non_empty_with_empty_list
1605
+ tmux.send_keys %(: | #{fzf('-q foo --print-query --bind enter:accept-non-empty')}), :Enter
1606
+ tmux.until { |lines| assert_equal ' 0/0', lines[-2] }
1607
+ tmux.send_keys :Enter
1608
+ # fzf will exit anyway since input list is empty
1609
+ assert_equal %w[foo], readonce.lines(chomp: true)
1610
+ end
1611
+
1612
+ def test_preview_update_on_select
1613
+ tmux.send_keys %(seq 10 | fzf -m --preview 'echo {+}' --bind a:toggle-all),
1614
+ :Enter
1615
+ tmux.until { |lines| assert_equal 10, lines.item_count }
1616
+ tmux.send_keys 'a'
1617
+ tmux.until { |lines| assert(lines.any? { |line| line.include?(' 1 2 3 4 5 ') }) }
1618
+ tmux.send_keys 'a'
1619
+ tmux.until { |lines| lines.each { |line| refute_includes line, ' 1 2 3 4 5 ' } }
1620
+ end
1621
+
1622
+ def test_escaped_meta_characters
1623
+ input = [
1624
+ 'foo^bar',
1625
+ 'foo$bar',
1626
+ 'foo!bar',
1627
+ "foo'bar",
1628
+ 'foo bar',
1629
+ 'bar foo'
1630
+ ]
1631
+ writelines(tempname, input)
1632
+
1633
+ assert_equal input.length, `#{FZF} -f'foo bar' < #{tempname}`.lines.length
1634
+ assert_equal input.length - 1, `#{FZF} -f'^foo bar$' < #{tempname}`.lines.length
1635
+ assert_equal ['foo bar'], `#{FZF} -f'foo\\ bar' < #{tempname}`.lines(chomp: true)
1636
+ assert_equal ['foo bar'], `#{FZF} -f'^foo\\ bar$' < #{tempname}`.lines(chomp: true)
1637
+ assert_equal input.length - 1, `#{FZF} -f'!^foo\\ bar$' < #{tempname}`.lines.length
1638
+ end
1639
+
1640
+ def test_inverse_only_search_should_not_sort_the_result
1641
+ # Filter
1642
+ assert_equal %w[aaaaa b ccc],
1643
+ `printf '%s\n' aaaaa b ccc BAD | #{FZF} -f '!bad'`.lines(chomp: true)
1644
+
1645
+ # Interactive
1646
+ tmux.send_keys %(printf '%s\n' aaaaa b ccc BAD | #{FZF} -q '!bad'), :Enter
1647
+ tmux.until do |lines|
1648
+ assert_equal 4, lines.item_count
1649
+ assert_equal 3, lines.match_count
1650
+ end
1651
+ tmux.until { |lines| assert_equal '> aaaaa', lines[-3] }
1652
+ tmux.until { |lines| assert_equal ' b', lines[-4] }
1653
+ tmux.until { |lines| assert_equal ' ccc', lines[-5] }
1654
+ end
1655
+
1656
+ def test_preview_correct_tab_width_after_ansi_reset_code
1657
+ writelines(tempname, ["\x1b[31m+\x1b[m\t\x1b[32mgreen"])
1658
+ tmux.send_keys "#{FZF} --preview 'cat #{tempname}'", :Enter
1659
+ tmux.until { |lines| assert_includes lines[1], ' + green ' }
1660
+ end
1661
+
1662
+ def test_disabled
1663
+ tmux.send_keys %(seq 1000 | #{FZF} --query 333 --disabled --bind a:enable-search,b:disable-search,c:toggle-search --preview 'echo {} {q}'), :Enter
1664
+ tmux.until { |lines| assert_equal 1000, lines.match_count }
1665
+ tmux.until { |lines| assert_includes lines[1], ' 1 333 ' }
1666
+ tmux.send_keys 'foo'
1667
+ tmux.until { |lines| assert_equal 1000, lines.match_count }
1668
+ tmux.until { |lines| assert_includes lines[1], ' 1 333foo ' }
1669
+
1670
+ # Already disabled, no change
1671
+ tmux.send_keys 'b'
1672
+ tmux.until { |lines| assert_equal 1000, lines.match_count }
1673
+
1674
+ # Enable search
1675
+ tmux.send_keys 'a'
1676
+ tmux.until { |lines| assert_equal 0, lines.match_count }
1677
+ tmux.send_keys :BSpace, :BSpace, :BSpace
1678
+ tmux.until { |lines| assert_equal 1, lines.match_count }
1679
+ tmux.until { |lines| assert_includes lines[1], ' 333 333 ' }
1680
+
1681
+ # Toggle search -> disabled again, but retains the previous result
1682
+ tmux.send_keys 'c'
1683
+ tmux.send_keys 'foo'
1684
+ tmux.until { |lines| assert_includes lines[1], ' 333 333foo ' }
1685
+ tmux.until { |lines| assert_equal 1, lines.match_count }
1686
+
1687
+ # Enabled, no match
1688
+ tmux.send_keys 'c'
1689
+ tmux.until { |lines| assert_equal 0, lines.match_count }
1690
+ tmux.until { |lines| assert_includes lines[1], ' 333foo ' }
1691
+ end
1692
+
1693
+ def test_reload
1694
+ tmux.send_keys %(seq 1000 | #{FZF} --bind 'change:reload(seq {q}),a:reload(seq 100),b:reload:seq 200' --header-lines 2 --multi 2), :Enter
1695
+ tmux.until { |lines| assert_equal 998, lines.match_count }
1696
+ tmux.send_keys 'a'
1697
+ tmux.until do |lines|
1698
+ assert_equal 98, lines.item_count
1699
+ assert_equal 98, lines.match_count
1700
+ end
1701
+ tmux.send_keys 'b'
1702
+ tmux.until do |lines|
1703
+ assert_equal 198, lines.item_count
1704
+ assert_equal 198, lines.match_count
1705
+ end
1706
+ tmux.send_keys :Tab
1707
+ tmux.until { |lines| assert_equal ' 198/198 (1/2)', lines[-2] }
1708
+ tmux.send_keys '555'
1709
+ tmux.until { |lines| assert_equal ' 1/553 (0/2)', lines[-2] }
1710
+ end
1711
+
1712
+ def test_reload_even_when_theres_no_match
1713
+ tmux.send_keys %(: | #{FZF} --bind 'space:reload(seq 10)'), :Enter
1714
+ tmux.until { |lines| assert_equal 0, lines.item_count }
1715
+ tmux.send_keys :Space
1716
+ tmux.until { |lines| assert_equal 10, lines.item_count }
1717
+ end
1718
+
1719
+ def test_clear_list_when_header_lines_changed_due_to_reload
1720
+ tmux.send_keys %(seq 10 | #{FZF} --header 0 --header-lines 3 --bind 'space:reload(seq 1)'), :Enter
1721
+ tmux.until { |lines| assert_includes lines, ' 9' }
1722
+ tmux.send_keys :Space
1723
+ tmux.until { |lines| refute_includes lines, ' 9' }
1724
+ end
1725
+
1726
+ def test_clear_query
1727
+ tmux.send_keys %(: | #{FZF} --query foo --bind space:clear-query), :Enter
1728
+ tmux.until { |lines| assert_equal 0, lines.item_count }
1729
+ tmux.until { |lines| assert_equal '> foo', lines.last }
1730
+ tmux.send_keys 'C-a', 'bar'
1731
+ tmux.until { |lines| assert_equal '> barfoo', lines.last }
1732
+ tmux.send_keys :Space
1733
+ tmux.until { |lines| assert_equal '>', lines.last }
1734
+ end
1735
+
1736
+ def test_clear_selection
1737
+ tmux.send_keys %(seq 100 | #{FZF} --multi --bind space:clear-selection), :Enter
1738
+ tmux.until { |lines| assert_equal 100, lines.match_count }
1739
+ tmux.send_keys :Tab
1740
+ tmux.until { |lines| assert_equal ' 100/100 (1)', lines[-2] }
1741
+ tmux.send_keys 'foo'
1742
+ tmux.until { |lines| assert_equal ' 0/100 (1)', lines[-2] }
1743
+ tmux.send_keys :Space
1744
+ tmux.until { |lines| assert_equal ' 0/100 (0)', lines[-2] }
1745
+ end
1746
+
1747
+ def test_backward_delete_char_eof
1748
+ tmux.send_keys "seq 1000 | #{fzf("--bind 'bs:backward-delete-char/eof'")}", :Enter
1749
+ tmux.until { |lines| assert_equal ' 1000/1000', lines[-2] }
1750
+ tmux.send_keys '11'
1751
+ tmux.until { |lines| assert_equal '> 11', lines[-1] }
1752
+ tmux.send_keys :BSpace
1753
+ tmux.until { |lines| assert_equal '> 1', lines[-1] }
1754
+ tmux.send_keys :BSpace
1755
+ tmux.until { |lines| assert_equal '>', lines[-1] }
1756
+ tmux.send_keys :BSpace
1757
+ tmux.prepare
1758
+ end
1759
+
1760
+ def test_strip_xterm_osc_sequence
1761
+ %W[\x07 \x1b\\].each do |esc|
1762
+ writelines(tempname, [%(printf $1"\e]4;3;rgb:aa/bb/cc#{esc} "$2)])
1763
+ File.chmod(0o755, tempname)
1764
+ tmux.prepare
1765
+ tmux.send_keys \
1766
+ %(echo foo bar | #{FZF} --preview '#{tempname} {2} {1}'), :Enter
1767
+
1768
+ tmux.until { |lines| assert lines.any_include?('bar foo') }
1769
+ tmux.send_keys :Enter
1770
+ end
1771
+ end
1772
+
1773
+ def test_keep_right
1774
+ tmux.send_keys "seq 10000 | #{FZF} --read0 --keep-right", :Enter
1775
+ tmux.until { |lines| assert lines.any_include?('9999 10000') }
1776
+ end
1777
+
1778
+ def test_backward_eof
1779
+ tmux.send_keys "echo foo | #{FZF} --bind 'backward-eof:reload(seq 100)'", :Enter
1780
+ tmux.until { |lines| lines.item_count == 1 && lines.match_count == 1 }
1781
+ tmux.send_keys 'x'
1782
+ tmux.until { |lines| lines.item_count == 1 && lines.match_count == 0 }
1783
+ tmux.send_keys :BSpace
1784
+ tmux.until { |lines| lines.item_count == 1 && lines.match_count == 1 }
1785
+ tmux.send_keys :BSpace
1786
+ tmux.until { |lines| lines.item_count == 100 && lines.match_count == 100 }
1787
+ end
1788
+
1789
+ def test_preview_bindings_with_default_preview
1790
+ tmux.send_keys "seq 10 | #{FZF} --preview 'echo [{}]' --bind 'a:preview(echo [{}{}]),b:preview(echo [{}{}{}]),c:refresh-preview'", :Enter
1791
+ tmux.until { |lines| lines.item_count == 10 }
1792
+ tmux.until { |lines| assert_includes lines[1], '[1]' }
1793
+ tmux.send_keys 'a'
1794
+ tmux.until { |lines| assert_includes lines[1], '[11]' }
1795
+ tmux.send_keys 'c'
1796
+ tmux.until { |lines| assert_includes lines[1], '[1]' }
1797
+ tmux.send_keys 'b'
1798
+ tmux.until { |lines| assert_includes lines[1], '[111]' }
1799
+ tmux.send_keys :Up
1800
+ tmux.until { |lines| assert_includes lines[1], '[2]' }
1801
+ end
1802
+
1803
+ def test_preview_bindings_without_default_preview
1804
+ tmux.send_keys "seq 10 | #{FZF} --bind 'a:preview(echo [{}{}]),b:preview(echo [{}{}{}]),c:refresh-preview'", :Enter
1805
+ tmux.until { |lines| lines.item_count == 10 }
1806
+ tmux.until { |lines| refute_includes lines[1], '1' }
1807
+ tmux.send_keys 'a'
1808
+ tmux.until { |lines| assert_includes lines[1], '[11]' }
1809
+ tmux.send_keys 'c' # does nothing
1810
+ tmux.until { |lines| assert_includes lines[1], '[11]' }
1811
+ tmux.send_keys 'b'
1812
+ tmux.until { |lines| assert_includes lines[1], '[111]' }
1813
+ tmux.send_keys 9
1814
+ tmux.until { |lines| lines.match_count == 1 }
1815
+ tmux.until { |lines| refute_includes lines[1], '2' }
1816
+ tmux.until { |lines| assert_includes lines[1], '[111]' }
1817
+ end
1818
+
1819
+ def test_preview_scroll_begin_constant
1820
+ tmux.send_keys "echo foo 123 321 | #{FZF} --preview 'seq 1000' --preview-window left:+123", :Enter
1821
+ tmux.until { |lines| assert_match %r{1/1}, lines[-2] }
1822
+ tmux.until { |lines| assert_match %r{123.*123/1000}, lines[1] }
1823
+ end
1824
+
1825
+ def test_preview_scroll_begin_expr
1826
+ tmux.send_keys "echo foo 123 321 | #{FZF} --preview 'seq 1000' --preview-window left:+{3}", :Enter
1827
+ tmux.until { |lines| assert_match %r{1/1}, lines[-2] }
1828
+ tmux.until { |lines| assert_match %r{321.*321/1000}, lines[1] }
1829
+ end
1830
+
1831
+ def test_preview_scroll_begin_and_offset
1832
+ ['echo foo 123 321', 'echo foo :123: 321'].each do |input|
1833
+ tmux.send_keys "#{input} | #{FZF} --preview 'seq 1000' --preview-window left:+{2}-2", :Enter
1834
+ tmux.until { |lines| assert_match %r{1/1}, lines[-2] }
1835
+ tmux.until { |lines| assert_match %r{121.*121/1000}, lines[1] }
1836
+ tmux.send_keys 'C-c'
1837
+ end
1838
+ end
1839
+
1840
+ def test_normalized_match
1841
+ echoes = '(echo a; echo á; echo A; echo Á;)'
1842
+ assert_equal %w[a á A Á], `#{echoes} | #{FZF} -f a`.lines.map(&:chomp)
1843
+ assert_equal %w[á Á], `#{echoes} | #{FZF} -f á`.lines.map(&:chomp)
1844
+ assert_equal %w[A Á], `#{echoes} | #{FZF} -f A`.lines.map(&:chomp)
1845
+ assert_equal %w[Á], `#{echoes} | #{FZF} -f Á`.lines.map(&:chomp)
1846
+ end
1847
+
1848
+ def test_preview_clear_screen
1849
+ tmux.send_keys %{seq 100 | #{FZF} --preview 'for i in $(seq 300); do (( i % 200 == 0 )) && printf "\\033[2J"; echo "[$i]"; sleep 0.001; done'}, :Enter
1850
+ tmux.until { |lines| lines.item_count == 100 }
1851
+ tmux.until { |lines| lines[1]&.include?('[200]') }
1852
+ end
1853
+
1854
+ def test_change_prompt
1855
+ tmux.send_keys "#{FZF} --bind 'a:change-prompt(a> ),b:change-prompt:b> ' --query foo", :Enter
1856
+ tmux.until { |lines| assert_equal '> foo', lines[-1] }
1857
+ tmux.send_keys 'a'
1858
+ tmux.until { |lines| assert_equal 'a> foo', lines[-1] }
1859
+ tmux.send_keys 'b'
1860
+ tmux.until { |lines| assert_equal 'b> foo', lines[-1] }
1861
+ end
1862
+
1863
+ def test_preview_window_follow
1864
+ tmux.send_keys "#{FZF} --preview 'seq 1000 | nl' --preview-window down:noborder:follow", :Enter
1865
+ tmux.until { |lines| assert_equal '1000 1000', lines[-1].strip }
1866
+ end
1867
+
1868
+ def test_toggle_preview_wrap
1869
+ tmux.send_keys "#{FZF} --preview 'for i in $(seq $FZF_PREVIEW_COLUMNS); do echo -n .; done; echo wrapped; echo 2nd line' --bind ctrl-w:toggle-preview-wrap", :Enter
1870
+ 2.times do
1871
+ tmux.until { |lines| assert_includes lines[2], '2nd line' }
1872
+ tmux.send_keys 'C-w'
1873
+ tmux.until do |lines|
1874
+ assert_includes lines[2], 'wrapped'
1875
+ assert_includes lines[3], '2nd line'
1876
+ end
1877
+ tmux.send_keys 'C-w'
1878
+ end
1879
+ end
1880
+
1881
+ def test_close
1882
+ tmux.send_keys "seq 100 | #{FZF} --preview 'echo foo' --bind ctrl-c:close", :Enter
1883
+ tmux.until { |lines| assert_equal 100, lines.match_count }
1884
+ tmux.until { |lines| assert_includes lines[1], 'foo' }
1885
+ tmux.send_keys 'C-c'
1886
+ tmux.until { |lines| refute_includes lines[1], 'foo' }
1887
+ tmux.send_keys '10'
1888
+ tmux.until { |lines| assert_equal 2, lines.match_count }
1889
+ tmux.send_keys 'C-c'
1890
+ tmux.send_keys 'C-l', 'closed'
1891
+ tmux.until { |lines| assert_includes lines[0], 'closed' }
1892
+ end
1893
+
1894
+ def test_select_deselect
1895
+ tmux.send_keys "seq 3 | #{FZF} --multi --bind up:deselect+up,down:select+down", :Enter
1896
+ tmux.until { |lines| assert_equal 3, lines.match_count }
1897
+ tmux.send_keys :Tab
1898
+ tmux.until { |lines| assert_equal 1, lines.select_count }
1899
+ tmux.send_keys :Up
1900
+ tmux.until { |lines| assert_equal 0, lines.select_count }
1901
+ tmux.send_keys :Down, :Down
1902
+ tmux.until { |lines| assert_equal 2, lines.select_count }
1903
+ tmux.send_keys :Tab
1904
+ tmux.until { |lines| assert_equal 1, lines.select_count }
1905
+ tmux.send_keys :Down, :Down
1906
+ tmux.until { |lines| assert_equal 2, lines.select_count }
1907
+ tmux.send_keys :Up
1908
+ tmux.until { |lines| assert_equal 1, lines.select_count }
1909
+ tmux.send_keys :Down
1910
+ tmux.until { |lines| assert_equal 1, lines.select_count }
1911
+ tmux.send_keys :Down
1912
+ tmux.until { |lines| assert_equal 2, lines.select_count }
1913
+ end
1914
+
1915
+ def test_interrupt_execute
1916
+ tmux.send_keys "seq 100 | #{FZF} --bind 'ctrl-l:execute:echo executing {}; sleep 100'", :Enter
1917
+ tmux.until { |lines| assert_equal 100, lines.item_count }
1918
+ tmux.send_keys 'C-l'
1919
+ tmux.until { |lines| assert lines.any_include?('executing 1') }
1920
+ tmux.send_keys 'C-c'
1921
+ tmux.until { |lines| assert_equal 100, lines.item_count }
1922
+ tmux.send_keys 99
1923
+ tmux.until { |lines| assert_equal 1, lines.match_count }
1924
+ end
1925
+
1926
+ def test_kill_default_command_on_abort
1927
+ script = tempname + '.sh'
1928
+ writelines(script,
1929
+ ['#!/usr/bin/env bash',
1930
+ "echo 'Started'",
1931
+ 'while :; do sleep 1; done'])
1932
+ system("chmod +x #{script}")
1933
+
1934
+ tmux.send_keys fzf.sub('FZF_DEFAULT_COMMAND=', "FZF_DEFAULT_COMMAND=#{script}"), :Enter
1935
+ tmux.until { |lines| assert_equal 1, lines.item_count }
1936
+ tmux.send_keys 'C-c'
1937
+ tmux.send_keys 'C-l', 'closed'
1938
+ tmux.until { |lines| assert_includes lines[0], 'closed' }
1939
+ wait { refute system("pgrep -f #{script}") }
1940
+ ensure
1941
+ system("pkill -9 -f #{script}")
1942
+ begin
1943
+ File.unlink(script)
1944
+ rescue StandardError
1945
+ nil
1946
+ end
1947
+ end
1948
+
1949
+ def test_kill_default_command_on_accept
1950
+ script = tempname + '.sh'
1951
+ writelines(script,
1952
+ ['#!/usr/bin/env bash',
1953
+ "echo 'Started'",
1954
+ 'while :; do sleep 1; done'])
1955
+ system("chmod +x #{script}")
1956
+
1957
+ tmux.send_keys fzf.sub('FZF_DEFAULT_COMMAND=', "FZF_DEFAULT_COMMAND=#{script}"), :Enter
1958
+ tmux.until { |lines| assert_equal 1, lines.item_count }
1959
+ tmux.send_keys :Enter
1960
+ assert_equal 'Started', readonce.chomp
1961
+ wait { refute system("pgrep -f #{script}") }
1962
+ ensure
1963
+ system("pkill -9 -f #{script}")
1964
+ begin
1965
+ File.unlink(script)
1966
+ rescue StandardError
1967
+ nil
1968
+ end
1969
+ end
1970
+
1971
+ def test_kill_reload_command_on_abort
1972
+ script = tempname + '.sh'
1973
+ writelines(script,
1974
+ ['#!/usr/bin/env bash',
1975
+ "echo 'Started'",
1976
+ 'while :; do sleep 1; done'])
1977
+ system("chmod +x #{script}")
1978
+
1979
+ tmux.send_keys "seq 1 3 | #{fzf("--bind 'ctrl-r:reload(#{script})'")}", :Enter
1980
+ tmux.until { |lines| assert_equal 3, lines.item_count }
1981
+ tmux.send_keys 'C-r'
1982
+ tmux.until { |lines| assert_equal 1, lines.item_count }
1983
+ tmux.send_keys 'C-c'
1984
+ tmux.send_keys 'C-l', 'closed'
1985
+ tmux.until { |lines| assert_includes lines[0], 'closed' }
1986
+ wait { refute system("pgrep -f #{script}") }
1987
+ ensure
1988
+ system("pkill -9 -f #{script}")
1989
+ begin
1990
+ File.unlink(script)
1991
+ rescue StandardError
1992
+ nil
1993
+ end
1994
+ end
1995
+
1996
+ def test_kill_reload_command_on_accept
1997
+ script = tempname + '.sh'
1998
+ writelines(script,
1999
+ ['#!/usr/bin/env bash',
2000
+ "echo 'Started'",
2001
+ 'while :; do sleep 1; done'])
2002
+ system("chmod +x #{script}")
2003
+
2004
+ tmux.send_keys "seq 1 3 | #{fzf("--bind 'ctrl-r:reload(#{script})'")}", :Enter
2005
+ tmux.until { |lines| assert_equal 3, lines.item_count }
2006
+ tmux.send_keys 'C-r'
2007
+ tmux.until { |lines| assert_equal 1, lines.item_count }
2008
+ tmux.send_keys :Enter
2009
+ assert_equal 'Started', readonce.chomp
2010
+ wait { refute system("pgrep -f #{script}") }
2011
+ ensure
2012
+ system("pkill -9 -f #{script}")
2013
+ begin
2014
+ File.unlink(script)
2015
+ rescue StandardError
2016
+ nil
2017
+ end
2018
+ end
2019
+
2020
+ def test_preview_header
2021
+ tmux.send_keys "seq 100 | #{FZF} --bind ctrl-k:preview-up+preview-up,ctrl-j:preview-down+preview-down+preview-down --preview 'seq 1000' --preview-window 'top:+{1}:~3'", :Enter
2022
+ tmux.until { |lines| assert_equal 100, lines.item_count }
2023
+ top5 = ->(lines) { lines.drop(1).take(5).map { |s| s[/[0-9]+/] } }
2024
+ tmux.until do |lines|
2025
+ assert_includes lines[1], '4/1000'
2026
+ assert_equal(%w[1 2 3 4 5], top5[lines])
2027
+ end
2028
+ tmux.send_keys '55'
2029
+ tmux.until do |lines|
2030
+ assert_equal 1, lines.match_count
2031
+ assert_equal(%w[1 2 3 55 56], top5[lines])
2032
+ end
2033
+ tmux.send_keys 'C-J'
2034
+ tmux.until do |lines|
2035
+ assert_equal(%w[1 2 3 58 59], top5[lines])
2036
+ end
2037
+ tmux.send_keys :BSpace
2038
+ tmux.until do |lines|
2039
+ assert_equal 19, lines.match_count
2040
+ assert_equal(%w[1 2 3 5 6], top5[lines])
2041
+ end
2042
+ tmux.send_keys 'C-K'
2043
+ tmux.until { |lines| assert_equal(%w[1 2 3 4 5], top5[lines]) }
2044
+ end
2045
+
2046
+ def test_unbind
2047
+ tmux.send_keys "seq 100 | #{FZF} --bind 'c:clear-query,d:unbind(c,d)'", :Enter
2048
+ tmux.until { |lines| assert_equal 100, lines.item_count }
2049
+ tmux.send_keys 'ab'
2050
+ tmux.until { |lines| assert_equal '> ab', lines[-1] }
2051
+ tmux.send_keys 'c'
2052
+ tmux.until { |lines| assert_equal '>', lines[-1] }
2053
+ tmux.send_keys 'dabcd'
2054
+ tmux.until { |lines| assert_equal '> abcd', lines[-1] }
2055
+ end
2056
+
2057
+ def test_item_index_reset_on_reload
2058
+ tmux.send_keys "seq 10 | #{FZF} --preview 'echo [[{n}]]' --bind 'up:last,down:first,space:reload:seq 100'", :Enter
2059
+ tmux.until { |lines| assert_includes lines[1], '[[0]]' }
2060
+ tmux.send_keys :Up
2061
+ tmux.until { |lines| assert_includes lines[1], '[[9]]' }
2062
+ tmux.send_keys :Down
2063
+ tmux.until { |lines| assert_includes lines[1], '[[0]]' }
2064
+ tmux.send_keys :Space
2065
+ tmux.until do |lines|
2066
+ assert_equal 100, lines.item_count
2067
+ assert_includes lines[1], '[[0]]'
2068
+ end
2069
+ tmux.send_keys :Up
2070
+ tmux.until { |lines| assert_includes lines[1], '[[99]]' }
2071
+ end
2072
+
2073
+ def test_reload_should_update_preview
2074
+ tmux.send_keys "seq 3 | #{FZF} --bind 'ctrl-t:reload:echo 4' --preview 'echo {}' --preview-window 'nohidden'", :Enter
2075
+ tmux.until { |lines| assert_includes lines[1], '1' }
2076
+ tmux.send_keys 'C-t'
2077
+ tmux.until { |lines| assert_includes lines[1], '4' }
2078
+ end
2079
+
2080
+ def test_scroll_off
2081
+ tmux.send_keys "seq 1000 | #{FZF} --scroll-off=3 --bind l:last", :Enter
2082
+ tmux.until { |lines| assert_equal 1000, lines.item_count }
2083
+ height = tmux.until { |lines| lines }.first.to_i
2084
+ tmux.send_keys :PgUp
2085
+ tmux.until do |lines|
2086
+ assert_equal height + 3, lines.first.to_i
2087
+ assert_equal "> #{height}", lines[3].strip
2088
+ end
2089
+ tmux.send_keys :Up
2090
+ tmux.until { |lines| assert_equal "> #{height + 1}", lines[3].strip }
2091
+ tmux.send_keys 'l'
2092
+ tmux.until { |lines| assert_equal '> 1000', lines.first.strip }
2093
+ tmux.send_keys :PgDn
2094
+ tmux.until { |lines| assert_equal "> #{1000 - height + 1}", lines.reverse[5].strip }
2095
+ tmux.send_keys :Down
2096
+ tmux.until { |lines| assert_equal "> #{1000 - height}", lines.reverse[5].strip }
2097
+ end
2098
+
2099
+ def test_scroll_off_large
2100
+ tmux.send_keys "seq 1000 | #{FZF} --scroll-off=9999", :Enter
2101
+ tmux.until { |lines| assert_equal 1000, lines.item_count }
2102
+ height = tmux.until { |lines| lines }.first.to_i
2103
+ tmux.send_keys :PgUp
2104
+ tmux.until { |lines| assert_equal "> #{height}", lines[height / 2].strip }
2105
+ tmux.send_keys :Up
2106
+ tmux.until { |lines| assert_equal "> #{height + 1}", lines[height / 2].strip }
2107
+ tmux.send_keys :Up
2108
+ tmux.until { |lines| assert_equal "> #{height + 2}", lines[height / 2].strip }
2109
+ tmux.send_keys :Down
2110
+ tmux.until { |lines| assert_equal "> #{height + 1}", lines[height / 2].strip }
2111
+ end
2112
+
2113
+ def test_header_first
2114
+ tmux.send_keys "seq 1000 | #{FZF} --header foobar --header-lines 3 --header-first", :Enter
2115
+ tmux.until do |lines|
2116
+ expected = <<~OUTPUT
2117
+ > 4
2118
+ 997/997
2119
+ >
2120
+ 3
2121
+ 2
2122
+ 1
2123
+ foobar
2124
+ OUTPUT
2125
+
2126
+ assert_equal expected.chomp, lines.reverse.take(7).reverse.join("\n")
2127
+ end
2128
+ end
2129
+
2130
+ def test_header_first_reverse
2131
+ tmux.send_keys "seq 1000 | #{FZF} --header foobar --header-lines 3 --header-first --reverse --inline-info", :Enter
2132
+ tmux.until do |lines|
2133
+ expected = <<~OUTPUT
2134
+ foobar
2135
+ 1
2136
+ 2
2137
+ 3
2138
+ > < 997/997
2139
+ > 4
2140
+ OUTPUT
2141
+
2142
+ assert_equal expected.chomp, lines.take(6).join("\n")
2143
+ end
2144
+ end
2145
+ end
2146
+
2147
+ module TestShell
2148
+ def setup
2149
+ @tmux = Tmux.new(shell)
2150
+ tmux.prepare
2151
+ end
2152
+
2153
+ def teardown
2154
+ @tmux.kill
2155
+ end
2156
+
2157
+ def set_var(name, val)
2158
+ tmux.prepare
2159
+ tmux.send_keys "export #{name}='#{val}'", :Enter
2160
+ tmux.prepare
2161
+ end
2162
+
2163
+ def unset_var(name)
2164
+ tmux.prepare
2165
+ tmux.send_keys "unset #{name}", :Enter
2166
+ tmux.prepare
2167
+ end
2168
+
2169
+ def test_ctrl_t
2170
+ set_var('FZF_CTRL_T_COMMAND', 'seq 100')
2171
+
2172
+ tmux.prepare
2173
+ tmux.send_keys 'C-t'
2174
+ tmux.until { |lines| assert_equal 100, lines.item_count }
2175
+ tmux.send_keys :Tab, :Tab, :Tab
2176
+ tmux.until { |lines| assert lines.any_include?(' (3)') }
2177
+ tmux.send_keys :Enter
2178
+ tmux.until { |lines| assert lines.any_include?('1 2 3') }
2179
+ tmux.send_keys 'C-c'
2180
+ end
2181
+
2182
+ def test_ctrl_t_unicode
2183
+ writelines(tempname, ['fzf-unicode 테스트1', 'fzf-unicode 테스트2'])
2184
+ set_var('FZF_CTRL_T_COMMAND', "cat #{tempname}")
2185
+
2186
+ tmux.prepare
2187
+ tmux.send_keys 'echo ', 'C-t'
2188
+ tmux.until { |lines| assert_equal 2, lines.item_count }
2189
+ tmux.send_keys 'fzf-unicode'
2190
+ tmux.until { |lines| assert_equal 2, lines.match_count }
2191
+
2192
+ tmux.send_keys '1'
2193
+ tmux.until { |lines| assert_equal 1, lines.match_count }
2194
+ tmux.send_keys :Tab
2195
+ tmux.until { |lines| assert_equal 1, lines.select_count }
2196
+
2197
+ tmux.send_keys :BSpace
2198
+ tmux.until { |lines| assert_equal 2, lines.match_count }
2199
+
2200
+ tmux.send_keys '2'
2201
+ tmux.until { |lines| assert_equal 1, lines.match_count }
2202
+ tmux.send_keys :Tab
2203
+ tmux.until { |lines| assert_equal 2, lines.select_count }
2204
+
2205
+ tmux.send_keys :Enter
2206
+ tmux.until { |lines| assert_match(/echo .*fzf-unicode.*1.* .*fzf-unicode.*2/, lines.join) }
2207
+ tmux.send_keys :Enter
2208
+ tmux.until { |lines| assert_equal 'fzf-unicode 테스트1 fzf-unicode 테스트2', lines[-1] }
2209
+ end
2210
+
2211
+ def test_alt_c
2212
+ tmux.prepare
2213
+ tmux.send_keys :Escape, :c
2214
+ lines = tmux.until { |lines| assert_operator lines.match_count, :>, 0 }
2215
+ expected = lines.reverse.find { |l| l.start_with?('> ') }[2..-1]
2216
+ tmux.send_keys :Enter
2217
+ tmux.prepare
2218
+ tmux.send_keys :pwd, :Enter
2219
+ tmux.until { |lines| assert lines[-1]&.end_with?(expected) }
2220
+ end
2221
+
2222
+ def test_alt_c_command
2223
+ set_var('FZF_ALT_C_COMMAND', 'echo /tmp')
2224
+
2225
+ tmux.prepare
2226
+ tmux.send_keys 'cd /', :Enter
2227
+
2228
+ tmux.prepare
2229
+ tmux.send_keys :Escape, :c
2230
+ tmux.until { |lines| assert_equal 1, lines.item_count }
2231
+ tmux.send_keys :Enter
2232
+
2233
+ tmux.prepare
2234
+ tmux.send_keys :pwd, :Enter
2235
+ tmux.until { |lines| assert_equal '/tmp', lines[-1] }
2236
+ end
2237
+
2238
+ def test_ctrl_r
2239
+ tmux.prepare
2240
+ tmux.send_keys 'echo 1st', :Enter
2241
+ tmux.prepare
2242
+ tmux.send_keys 'echo 2nd', :Enter
2243
+ tmux.prepare
2244
+ tmux.send_keys 'echo 3d', :Enter
2245
+ tmux.prepare
2246
+ 3.times do
2247
+ tmux.send_keys 'echo 3rd', :Enter
2248
+ tmux.prepare
2249
+ end
2250
+ tmux.send_keys 'echo 4th', :Enter
2251
+ tmux.prepare
2252
+ tmux.send_keys 'C-r'
2253
+ tmux.until { |lines| assert_operator lines.match_count, :>, 0 }
2254
+ tmux.send_keys 'e3d'
2255
+ # Duplicates removed: 3d (1) + 3rd (1) => 2 matches
2256
+ tmux.until { |lines| assert_equal 2, lines.match_count }
2257
+ tmux.until { |lines| assert lines[-3]&.end_with?(' echo 3d') }
2258
+ tmux.send_keys 'C-r'
2259
+ tmux.until { |lines| assert lines[-3]&.end_with?(' echo 3rd') }
2260
+ tmux.send_keys :Enter
2261
+ tmux.until { |lines| assert_equal 'echo 3rd', lines[-1] }
2262
+ tmux.send_keys :Enter
2263
+ tmux.until { |lines| assert_equal '3rd', lines[-1] }
2264
+ end
2265
+
2266
+ def test_ctrl_r_multiline
2267
+ tmux.send_keys 'echo "foo', :Enter, 'bar"', :Enter
2268
+ tmux.until { |lines| assert_equal %w[foo bar], lines[-2..-1] }
2269
+ tmux.prepare
2270
+ tmux.send_keys 'C-r'
2271
+ tmux.until { |lines| assert_equal '>', lines[-1] }
2272
+ tmux.send_keys 'foo bar'
2273
+ tmux.until { |lines| assert lines[-3]&.end_with?('bar"') }
2274
+ tmux.send_keys :Enter
2275
+ tmux.until { |lines| assert lines[-1]&.end_with?('bar"') }
2276
+ tmux.send_keys :Enter
2277
+ tmux.until { |lines| assert_equal %w[foo bar], lines[-2..-1] }
2278
+ end
2279
+
2280
+ def test_ctrl_r_abort
2281
+ skip("doesn't restore the original line when search is aborted pre Bash 4") if shell == :bash && `#{Shell.bash} --version`[/(?<= version )\d+/].to_i < 4
2282
+ %w[foo ' "].each do |query|
2283
+ tmux.prepare
2284
+ tmux.send_keys :Enter, query
2285
+ tmux.until { |lines| assert lines[-1]&.start_with?(query) }
2286
+ tmux.send_keys 'C-r'
2287
+ tmux.until { |lines| assert_equal "> #{query}", lines[-1] }
2288
+ tmux.send_keys 'C-g'
2289
+ tmux.until { |lines| assert lines[-1]&.start_with?(query) }
2290
+ end
2291
+ end
2292
+ end
2293
+
2294
+ module CompletionTest
2295
+ def test_file_completion
2296
+ FileUtils.mkdir_p('/tmp/fzf-test')
2297
+ FileUtils.mkdir_p('/tmp/fzf test')
2298
+ (1..100).each { |i| FileUtils.touch("/tmp/fzf-test/#{i}") }
2299
+ ['no~such~user', '/tmp/fzf test/foobar'].each do |f|
2300
+ FileUtils.touch(File.expand_path(f))
2301
+ end
2302
+ tmux.prepare
2303
+ tmux.send_keys 'cat /tmp/fzf-test/10**', :Tab
2304
+ tmux.until { |lines| assert_operator lines.match_count, :>, 0 }
2305
+ tmux.send_keys ' !d'
2306
+ tmux.until { |lines| assert_equal 2, lines.match_count }
2307
+ tmux.send_keys :Tab, :Tab
2308
+ tmux.until { |lines| assert_equal 2, lines.select_count }
2309
+ tmux.send_keys :Enter
2310
+ tmux.until(true) do |lines|
2311
+ assert_equal 'cat /tmp/fzf-test/10 /tmp/fzf-test/100', lines[-1]
2312
+ end
2313
+
2314
+ # ~USERNAME**<TAB>
2315
+ user = `whoami`.chomp
2316
+ tmux.send_keys 'C-u'
2317
+ tmux.send_keys "cat ~#{user}**", :Tab
2318
+ tmux.until { |lines| assert_operator lines.match_count, :>, 0 }
2319
+ tmux.send_keys "/#{user}"
2320
+ tmux.until { |lines| assert(lines.any? { |l| l.end_with?("/#{user}") }) }
2321
+ tmux.send_keys :Enter
2322
+ tmux.until(true) do |lines|
2323
+ assert_match %r{cat .*/#{user}}, lines[-1]
2324
+ end
2325
+
2326
+ # ~INVALID_USERNAME**<TAB>
2327
+ tmux.send_keys 'C-u'
2328
+ tmux.send_keys 'cat ~such**', :Tab
2329
+ tmux.until(true) { |lines| assert lines.any_include?('no~such~user') }
2330
+ tmux.send_keys :Enter
2331
+ tmux.until(true) { |lines| assert_equal 'cat no~such~user', lines[-1] }
2332
+
2333
+ # /tmp/fzf\ test**<TAB>
2334
+ tmux.send_keys 'C-u'
2335
+ tmux.send_keys 'cat /tmp/fzf\ test/**', :Tab
2336
+ tmux.until { |lines| assert_operator lines.match_count, :>, 0 }
2337
+ tmux.send_keys 'foobar$'
2338
+ tmux.until { |lines| assert_equal 1, lines.match_count }
2339
+ tmux.send_keys :Enter
2340
+ tmux.until(true) { |lines| assert_equal 'cat /tmp/fzf\ test/foobar', lines[-1] }
2341
+
2342
+ # Should include hidden files
2343
+ (1..100).each { |i| FileUtils.touch("/tmp/fzf-test/.hidden-#{i}") }
2344
+ tmux.send_keys 'C-u'
2345
+ tmux.send_keys 'cat /tmp/fzf-test/hidden**', :Tab
2346
+ tmux.until(true) do |lines|
2347
+ assert_equal 100, lines.match_count
2348
+ assert lines.any_include?('/tmp/fzf-test/.hidden-')
2349
+ end
2350
+ tmux.send_keys :Enter
2351
+ ensure
2352
+ ['/tmp/fzf-test', '/tmp/fzf test', '~/.fzf-home', 'no~such~user'].each do |f|
2353
+ FileUtils.rm_rf(File.expand_path(f))
2354
+ end
2355
+ end
2356
+
2357
+ def test_file_completion_root
2358
+ tmux.send_keys 'ls /**', :Tab
2359
+ tmux.until { |lines| assert_operator lines.match_count, :>, 0 }
2360
+ tmux.send_keys :Enter
2361
+ end
2362
+
2363
+ def test_dir_completion
2364
+ (1..100).each do |idx|
2365
+ FileUtils.mkdir_p("/tmp/fzf-test/d#{idx}")
2366
+ end
2367
+ FileUtils.touch('/tmp/fzf-test/d55/xxx')
2368
+ tmux.prepare
2369
+ tmux.send_keys 'cd /tmp/fzf-test/**', :Tab
2370
+ tmux.until { |lines| assert_operator lines.match_count, :>, 0 }
2371
+ tmux.send_keys :Tab, :Tab # Tab does not work here
2372
+ tmux.send_keys 55
2373
+ tmux.until { |lines| assert_equal 1, lines.match_count }
2374
+ tmux.send_keys :Enter
2375
+ tmux.until(true) { |lines| assert_equal 'cd /tmp/fzf-test/d55/', lines[-1] }
2376
+ tmux.send_keys :xx
2377
+ tmux.until { |lines| assert_equal 'cd /tmp/fzf-test/d55/xx', lines[-1] }
2378
+
2379
+ # Should not match regular files (bash-only)
2380
+ if instance_of?(TestBash)
2381
+ tmux.send_keys :Tab
2382
+ tmux.until { |lines| assert_equal 'cd /tmp/fzf-test/d55/xx', lines[-1] }
2383
+ end
2384
+
2385
+ # Fail back to plusdirs
2386
+ tmux.send_keys :BSpace, :BSpace, :BSpace
2387
+ tmux.until { |lines| assert_equal 'cd /tmp/fzf-test/d55', lines[-1] }
2388
+ tmux.send_keys :Tab
2389
+ tmux.until { |lines| assert_equal 'cd /tmp/fzf-test/d55/', lines[-1] }
2390
+ end
2391
+
2392
+ def test_process_completion
2393
+ tmux.send_keys 'sleep 12345 &', :Enter
2394
+ lines = tmux.until { |lines| assert lines[-1]&.start_with?('[1] ') }
2395
+ pid = lines[-1]&.split&.last
2396
+ tmux.prepare
2397
+ tmux.send_keys 'C-L'
2398
+ tmux.send_keys 'kill ', :Tab
2399
+ tmux.until { |lines| assert_operator lines.match_count, :>, 0 }
2400
+ tmux.send_keys 'sleep12345'
2401
+ tmux.until { |lines| assert lines.any_include?('sleep 12345') }
2402
+ tmux.send_keys :Enter
2403
+ tmux.until(true) { |lines| assert_equal "kill #{pid}", lines[-1] }
2404
+ ensure
2405
+ if pid
2406
+ begin
2407
+ Process.kill('KILL', pid.to_i)
2408
+ rescue StandardError
2409
+ nil
2410
+ end
2411
+ end
2412
+ end
2413
+
2414
+ def test_custom_completion
2415
+ tmux.send_keys '_fzf_compgen_path() { echo "$1"; seq 10; }', :Enter
2416
+ tmux.prepare
2417
+ tmux.send_keys 'ls /tmp/**', :Tab
2418
+ tmux.until { |lines| assert_equal 11, lines.match_count }
2419
+ tmux.send_keys :Tab, :Tab, :Tab
2420
+ tmux.until { |lines| assert_equal 3, lines.select_count }
2421
+ tmux.send_keys :Enter
2422
+ tmux.until(true) { |lines| assert_equal 'ls /tmp 1 2', lines[-1] }
2423
+ end
2424
+
2425
+ def test_unset_completion
2426
+ tmux.send_keys 'export FZFFOOBAR=BAZ', :Enter
2427
+ tmux.prepare
2428
+
2429
+ # Using tmux
2430
+ tmux.send_keys 'unset FZFFOOBR**', :Tab
2431
+ tmux.until { |lines| assert_equal 1, lines.match_count }
2432
+ tmux.send_keys :Enter
2433
+ tmux.until { |lines| assert_equal 'unset FZFFOOBAR', lines[-1] }
2434
+ tmux.send_keys 'C-c'
2435
+
2436
+ # FZF_TMUX=1
2437
+ new_shell
2438
+ tmux.focus
2439
+ tmux.send_keys 'unset FZFFOOBR**', :Tab
2440
+ tmux.until { |lines| assert_equal 1, lines.match_count }
2441
+ tmux.send_keys :Enter
2442
+ tmux.until { |lines| assert_equal 'unset FZFFOOBAR', lines[-1] }
2443
+ end
2444
+
2445
+ def test_file_completion_unicode
2446
+ FileUtils.mkdir_p('/tmp/fzf-test')
2447
+ tmux.paste "cd /tmp/fzf-test; echo test3 > $'fzf-unicode \\355\\205\\214\\354\\212\\244\\355\\212\\2701'; echo test4 > $'fzf-unicode \\355\\205\\214\\354\\212\\244\\355\\212\\2702'"
2448
+ tmux.prepare
2449
+ tmux.send_keys 'cat fzf-unicode**', :Tab
2450
+ tmux.until { |lines| assert_equal 2, lines.match_count }
2451
+
2452
+ tmux.send_keys '1'
2453
+ tmux.until { |lines| assert_equal 1, lines.match_count }
2454
+ tmux.send_keys :Tab
2455
+ tmux.until { |lines| assert_equal 1, lines.select_count }
2456
+
2457
+ tmux.send_keys :BSpace
2458
+ tmux.until { |lines| assert_equal 2, lines.match_count }
2459
+
2460
+ tmux.send_keys '2'
2461
+ tmux.until { |lines| assert_equal 1, lines.select_count }
2462
+ tmux.send_keys :Tab
2463
+ tmux.until { |lines| assert_equal 2, lines.select_count }
2464
+
2465
+ tmux.send_keys :Enter
2466
+ tmux.until(true) { |lines| assert_match(/cat .*fzf-unicode.*1.* .*fzf-unicode.*2/, lines[-1]) }
2467
+ tmux.send_keys :Enter
2468
+ tmux.until { |lines| assert_equal %w[test3 test4], lines[-2..-1] }
2469
+ end
2470
+
2471
+ def test_custom_completion_api
2472
+ tmux.send_keys 'eval "_fzf$(declare -f _comprun)"', :Enter
2473
+ %w[f g].each do |command|
2474
+ tmux.prepare
2475
+ tmux.send_keys "#{command} b**", :Tab
2476
+ tmux.until do |lines|
2477
+ assert_equal 2, lines.item_count
2478
+ assert_equal 1, lines.match_count
2479
+ assert lines.any_include?("prompt-#{command}")
2480
+ assert lines.any_include?("preview-#{command}-bar")
2481
+ end
2482
+ tmux.send_keys :Enter
2483
+ tmux.until { |lines| assert_equal "#{command} #{command}barbar", lines[-1] }
2484
+ tmux.send_keys 'C-u'
2485
+ end
2486
+ ensure
2487
+ tmux.prepare
2488
+ tmux.send_keys 'unset -f _fzf_comprun', :Enter
2489
+ end
2490
+ end
2491
+
2492
+ class TestBash < TestBase
2493
+ include TestShell
2494
+ include CompletionTest
2495
+
2496
+ def shell
2497
+ :bash
2498
+ end
2499
+
2500
+ def new_shell
2501
+ tmux.prepare
2502
+ tmux.send_keys "FZF_TMUX=1 #{Shell.bash}", :Enter
2503
+ tmux.prepare
2504
+ end
2505
+
2506
+ def test_dynamic_completion_loader
2507
+ tmux.paste 'touch /tmp/foo; _fzf_completion_loader=1'
2508
+ tmux.paste '_completion_loader() { complete -o default fake; }'
2509
+ tmux.paste 'complete -F _fzf_path_completion -o default -o bashdefault fake'
2510
+ tmux.send_keys 'fake /tmp/foo**', :Tab
2511
+ tmux.until { |lines| assert_operator lines.match_count, :>, 0 }
2512
+ tmux.send_keys 'C-c'
2513
+
2514
+ tmux.prepare
2515
+ tmux.send_keys 'fake /tmp/foo'
2516
+ tmux.send_keys :Tab, 'C-u'
2517
+
2518
+ tmux.prepare
2519
+ tmux.send_keys 'fake /tmp/foo**', :Tab
2520
+ tmux.until { |lines| assert_operator lines.match_count, :>, 0 }
2521
+ end
2522
+ end
2523
+
2524
+ class TestZsh < TestBase
2525
+ include TestShell
2526
+ include CompletionTest
2527
+
2528
+ def shell
2529
+ :zsh
2530
+ end
2531
+
2532
+ def new_shell
2533
+ tmux.send_keys "FZF_TMUX=1 #{Shell.zsh}", :Enter
2534
+ tmux.prepare
2535
+ end
2536
+
2537
+ def test_complete_quoted_command
2538
+ tmux.send_keys 'export FZFFOOBAR=BAZ', :Enter
2539
+ ['unset', '\unset', "'unset'"].each do |command|
2540
+ tmux.prepare
2541
+ tmux.send_keys "#{command} FZFFOOBR**", :Tab
2542
+ tmux.until { |lines| assert_equal 1, lines.match_count }
2543
+ tmux.send_keys :Enter
2544
+ tmux.until { |lines| assert_equal "#{command} FZFFOOBAR", lines[-1] }
2545
+ tmux.send_keys 'C-c'
2546
+ end
2547
+ end
2548
+ end
2549
+
2550
+ class TestFish < TestBase
2551
+ include TestShell
2552
+
2553
+ def shell
2554
+ :fish
2555
+ end
2556
+
2557
+ def new_shell
2558
+ tmux.send_keys 'env FZF_TMUX=1 fish', :Enter
2559
+ tmux.send_keys 'function fish_prompt; end; clear', :Enter
2560
+ tmux.until { |lines| assert_empty lines }
2561
+ end
2562
+
2563
+ def set_var(name, val)
2564
+ tmux.prepare
2565
+ tmux.send_keys "set -g #{name} '#{val}'", :Enter
2566
+ tmux.prepare
2567
+ end
2568
+ end
2569
+
2570
+ __END__
2571
+ PS1= PROMPT_COMMAND= HISTFILE= HISTSIZE=100
2572
+ unset <%= UNSETS.join(' ') %>
2573
+ unset $(env | sed -n /^_fzf_orig/s/=.*//p)
2574
+ unset $(declare -F | sed -n "/_fzf/s/.*-f //p")
2575
+
2576
+ # Setup fzf
2577
+ # ---------
2578
+ if [[ ! "$PATH" == *<%= BASE %>/bin* ]]; then
2579
+ export PATH="${PATH:+${PATH}:}<%= BASE %>/bin"
2580
+ fi
2581
+
2582
+ # Auto-completion
2583
+ # ---------------
2584
+ [[ $- == *i* ]] && source "<%= BASE %>/shell/completion.<%= __method__ %>" 2> /dev/null
2585
+
2586
+ # Key bindings
2587
+ # ------------
2588
+ source "<%= BASE %>/shell/key-bindings.<%= __method__ %>"
2589
+
2590
+ # Old API
2591
+ _fzf_complete_f() {
2592
+ _fzf_complete "+m --multi --prompt \"prompt-f> \"" "$@" < <(
2593
+ echo foo
2594
+ echo bar
2595
+ )
2596
+ }
2597
+
2598
+ # New API
2599
+ _fzf_complete_g() {
2600
+ _fzf_complete +m --multi --prompt "prompt-g> " -- "$@" < <(
2601
+ echo foo
2602
+ echo bar
2603
+ )
2604
+ }
2605
+
2606
+ _fzf_complete_f_post() {
2607
+ awk '{print "f" $0 $0}'
2608
+ }
2609
+
2610
+ _fzf_complete_g_post() {
2611
+ awk '{print "g" $0 $0}'
2612
+ }
2613
+
2614
+ [ -n "$BASH" ] && complete -F _fzf_complete_f -o default -o bashdefault f
2615
+ [ -n "$BASH" ] && complete -F _fzf_complete_g -o default -o bashdefault g
2616
+
2617
+ _comprun() {
2618
+ local command=$1
2619
+ shift
2620
+
2621
+ case "$command" in
2622
+ f) fzf "$@" --preview 'echo preview-f-{}' ;;
2623
+ g) fzf "$@" --preview 'echo preview-g-{}' ;;
2624
+ *) fzf "$@" ;;
2625
+ esac
2626
+ }