doing 2.0.20 → 2.0.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/Gemfile.lock +1 -1
  4. data/README.md +1 -1
  5. data/doing.rdoc +1 -1
  6. data/lib/doing/version.rb +1 -1
  7. data/lib/helpers/fzf/.goreleaser.yml +119 -0
  8. data/lib/helpers/fzf/.rubocop.yml +28 -0
  9. data/lib/helpers/fzf/ADVANCED.md +565 -0
  10. data/lib/helpers/fzf/BUILD.md +49 -0
  11. data/lib/helpers/fzf/CHANGELOG.md +1193 -0
  12. data/lib/helpers/fzf/Dockerfile +11 -0
  13. data/lib/helpers/fzf/LICENSE +21 -0
  14. data/lib/helpers/fzf/Makefile +166 -0
  15. data/lib/helpers/fzf/README-VIM.md +486 -0
  16. data/lib/helpers/fzf/README.md +712 -0
  17. data/lib/helpers/fzf/bin/fzf-tmux +233 -0
  18. data/lib/helpers/fzf/doc/fzf.txt +512 -0
  19. data/lib/helpers/fzf/go.mod +17 -0
  20. data/lib/helpers/fzf/go.sum +31 -0
  21. data/lib/helpers/fzf/install +382 -0
  22. data/lib/helpers/fzf/install.ps1 +65 -0
  23. data/lib/helpers/fzf/main.go +14 -0
  24. data/lib/helpers/fzf/man/man1/fzf-tmux.1 +68 -0
  25. data/lib/helpers/fzf/man/man1/fzf.1 +1001 -0
  26. data/lib/helpers/fzf/plugin/fzf.vim +1048 -0
  27. data/lib/helpers/fzf/shell/completion.bash +381 -0
  28. data/lib/helpers/fzf/shell/completion.zsh +329 -0
  29. data/lib/helpers/fzf/shell/key-bindings.bash +96 -0
  30. data/lib/helpers/fzf/shell/key-bindings.fish +172 -0
  31. data/lib/helpers/fzf/shell/key-bindings.zsh +114 -0
  32. data/lib/helpers/fzf/src/LICENSE +21 -0
  33. data/lib/helpers/fzf/src/algo/algo.go +884 -0
  34. data/lib/helpers/fzf/src/algo/algo_test.go +197 -0
  35. data/lib/helpers/fzf/src/algo/normalize.go +492 -0
  36. data/lib/helpers/fzf/src/ansi.go +409 -0
  37. data/lib/helpers/fzf/src/ansi_test.go +427 -0
  38. data/lib/helpers/fzf/src/cache.go +81 -0
  39. data/lib/helpers/fzf/src/cache_test.go +39 -0
  40. data/lib/helpers/fzf/src/chunklist.go +89 -0
  41. data/lib/helpers/fzf/src/chunklist_test.go +80 -0
  42. data/lib/helpers/fzf/src/constants.go +85 -0
  43. data/lib/helpers/fzf/src/core.go +351 -0
  44. data/lib/helpers/fzf/src/history.go +96 -0
  45. data/lib/helpers/fzf/src/history_test.go +68 -0
  46. data/lib/helpers/fzf/src/item.go +44 -0
  47. data/lib/helpers/fzf/src/item_test.go +23 -0
  48. data/lib/helpers/fzf/src/matcher.go +235 -0
  49. data/lib/helpers/fzf/src/merger.go +120 -0
  50. data/lib/helpers/fzf/src/merger_test.go +88 -0
  51. data/lib/helpers/fzf/src/options.go +1691 -0
  52. data/lib/helpers/fzf/src/options_test.go +457 -0
  53. data/lib/helpers/fzf/src/pattern.go +425 -0
  54. data/lib/helpers/fzf/src/pattern_test.go +209 -0
  55. data/lib/helpers/fzf/src/protector/protector.go +8 -0
  56. data/lib/helpers/fzf/src/protector/protector_openbsd.go +10 -0
  57. data/lib/helpers/fzf/src/reader.go +201 -0
  58. data/lib/helpers/fzf/src/reader_test.go +63 -0
  59. data/lib/helpers/fzf/src/result.go +243 -0
  60. data/lib/helpers/fzf/src/result_others.go +16 -0
  61. data/lib/helpers/fzf/src/result_test.go +159 -0
  62. data/lib/helpers/fzf/src/result_x86.go +16 -0
  63. data/lib/helpers/fzf/src/terminal.go +2832 -0
  64. data/lib/helpers/fzf/src/terminal_test.go +638 -0
  65. data/lib/helpers/fzf/src/terminal_unix.go +26 -0
  66. data/lib/helpers/fzf/src/terminal_windows.go +45 -0
  67. data/lib/helpers/fzf/src/tokenizer.go +253 -0
  68. data/lib/helpers/fzf/src/tokenizer_test.go +112 -0
  69. data/lib/helpers/fzf/src/tui/dummy.go +46 -0
  70. data/lib/helpers/fzf/src/tui/light.go +987 -0
  71. data/lib/helpers/fzf/src/tui/light_unix.go +110 -0
  72. data/lib/helpers/fzf/src/tui/light_windows.go +145 -0
  73. data/lib/helpers/fzf/src/tui/tcell.go +721 -0
  74. data/lib/helpers/fzf/src/tui/tcell_test.go +392 -0
  75. data/lib/helpers/fzf/src/tui/ttyname_unix.go +47 -0
  76. data/lib/helpers/fzf/src/tui/ttyname_windows.go +14 -0
  77. data/lib/helpers/fzf/src/tui/tui.go +625 -0
  78. data/lib/helpers/fzf/src/tui/tui_test.go +20 -0
  79. data/lib/helpers/fzf/src/util/atomicbool.go +34 -0
  80. data/lib/helpers/fzf/src/util/atomicbool_test.go +17 -0
  81. data/lib/helpers/fzf/src/util/chars.go +198 -0
  82. data/lib/helpers/fzf/src/util/chars_test.go +46 -0
  83. data/lib/helpers/fzf/src/util/eventbox.go +96 -0
  84. data/lib/helpers/fzf/src/util/eventbox_test.go +61 -0
  85. data/lib/helpers/fzf/src/util/slab.go +12 -0
  86. data/lib/helpers/fzf/src/util/util.go +138 -0
  87. data/lib/helpers/fzf/src/util/util_test.go +40 -0
  88. data/lib/helpers/fzf/src/util/util_unix.go +47 -0
  89. data/lib/helpers/fzf/src/util/util_windows.go +83 -0
  90. data/lib/helpers/fzf/test/fzf.vader +175 -0
  91. data/lib/helpers/fzf/test/test_go.rb +2626 -0
  92. data/lib/helpers/fzf/uninstall +117 -0
  93. metadata +87 -1
@@ -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
+ }