summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--README.md38
-rw-r--r--src/choices.c2
-rw-r--r--src/fzy.c11
-rw-r--r--src/tty.c12
-rw-r--r--src/tty.h3
-rw-r--r--src/tty_interface.c69
-rw-r--r--test/acceptance/acceptance_test.rb191
8 files changed, 256 insertions, 72 deletions
diff --git a/Makefile b/Makefile
index 294a044..85a8993 100644
--- a/Makefile
+++ b/Makefile
@@ -22,7 +22,7 @@ test/fzytest: $(TESTOBJECTS)
$(CC) $(CFLAGS) $(CCFLAGS) -Isrc -o $@ $(TESTOBJECTS) $(LIBS)
acceptance: fzy
- cd test/acceptance && bundle && bundle exec ruby acceptance_test.rb
+ cd test/acceptance && bundle --quiet && bundle exec ruby acceptance_test.rb
test: check
check: test/fzytest
diff --git a/README.md b/README.md
index b2dd47f..e6b3db0 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
**fzy** is a fast, simple fuzzy text selector for the terminal with an advanced [scoring
algorithm](#sorting).
-![](http://i.hawth.ca/u/fzy2.gif)
+![](http://i.hawth.ca/u/fzy_animated_demo.svg)
<blockquote>
It's been kind of life-changing.
@@ -41,30 +41,19 @@ Rather than clearing the screen, fzy displays its interface directly below the c
The `PREFIX` environment variable can be used to specify the install location,
the default is `/usr/local`.
-### MacOS
-
-Using Homebrew
-
- brew install fzy
-
-Using MacPorts
-
- sudo port install fzy
+### Arch Linux/MSYS2
-### Ubuntu/Debian 64-bit
+fzy is available in the `community` repo.
- wget https://github.com/jhawthorn/fzy/releases/download/0.9/fzy_0.9-1_amd64.deb
- sudo dpkg -i fzy_0.9-1_amd64.deb
+ sudo pacman -S fzy
### Fedora/Redhat/CentOS
sudo yum install https://github.com/jhawthorn/fzy/releases/download/0.9/fzy-0.9-1.x86_64.rpm
-### Arch Linux/MSYS2
-
-fzy is available in the `community` repo.
+### FreeBSD
- sudo pacman -S fzy
+ pkg install fzy
### Gentoo Linux
@@ -72,6 +61,21 @@ fzy is available in the main repo.
emerge -av app-shells/fzy
+### macOS
+
+Using Homebrew
+
+ brew install fzy
+
+Using MacPorts
+
+ sudo port install fzy
+
+### Ubuntu/Debian 64-bit
+
+ wget https://github.com/jhawthorn/fzy/releases/download/0.9/fzy_0.9-1_amd64.deb
+ sudo dpkg -i fzy_0.9-1_amd64.deb
+
### pkgsrc (NetBSD and others)
sudo pkgin install fzy
diff --git a/src/choices.c b/src/choices.c
index 64f9a8b..a8c24b6 100644
--- a/src/choices.c
+++ b/src/choices.c
@@ -21,7 +21,7 @@ static int cmpchoice(const void *_idx1, const void *_idx2) {
if (a->score == b->score) {
/* To ensure a stable sort, we must also sort by the string
- * pointers. We can do this since we know all the stings are
+ * pointers. We can do this since we know all the strings are
* from a contiguous memory segment (buffer in choices_t).
*/
if (a->str < b->str) {
diff --git a/src/fzy.c b/src/fzy.c
index 461f022..6b9aa5a 100644
--- a/src/fzy.c
+++ b/src/fzy.c
@@ -3,6 +3,7 @@
#include <stdlib.h>
#include <ctype.h>
#include <limits.h>
+#include <unistd.h>
#include "match.h"
#include "tty.h"
@@ -20,16 +21,17 @@ int main(int argc, char *argv[]) {
choices_t choices;
choices_init(&choices, &options);
- choices_fread(&choices, stdin);
if (options.benchmark) {
if (!options.filter) {
fprintf(stderr, "Must specify -e/--show-matches with --benchmark\n");
exit(EXIT_FAILURE);
}
+ choices_fread(&choices, stdin);
for (int i = 0; i < options.benchmark; i++)
choices_search(&choices, options.filter);
} else if (options.filter) {
+ choices_fread(&choices, stdin);
choices_search(&choices, options.filter);
for (size_t i = 0; i < choices_available(&choices); i++) {
if (options.show_scores)
@@ -38,9 +40,16 @@ int main(int argc, char *argv[]) {
}
} else {
/* interactive */
+
+ if (isatty(STDIN_FILENO))
+ choices_fread(&choices, stdin);
+
tty_t tty;
tty_init(&tty, options.tty_filename);
+ if (!isatty(STDIN_FILENO))
+ choices_fread(&choices, stdin);
+
if (options.num_lines > choices.size)
options.num_lines = choices.size;
diff --git a/src/tty.c b/src/tty.c
index 357b068..b5b942c 100644
--- a/src/tty.c
+++ b/src/tty.c
@@ -111,11 +111,23 @@ void tty_setinvert(tty_t *tty) {
tty_sgr(tty, 7);
}
+void tty_setunderline(tty_t *tty) {
+ tty_sgr(tty, 4);
+}
+
void tty_setnormal(tty_t *tty) {
tty_sgr(tty, 0);
tty->fgcolor = 9;
}
+void tty_setnowrap(tty_t *tty) {
+ tty_printf(tty, "%c%c?7l", 0x1b, '[');
+}
+
+void tty_setwrap(tty_t *tty) {
+ tty_printf(tty, "%c%c?7h", 0x1b, '[');
+}
+
void tty_newline(tty_t *tty) {
tty_printf(tty, "%c%cK\n", 0x1b, '[');
}
diff --git a/src/tty.h b/src/tty.h
index 3172499..aca40d9 100644
--- a/src/tty.h
+++ b/src/tty.h
@@ -21,7 +21,10 @@ int tty_input_ready(tty_t *tty, int pending);
void tty_setfg(tty_t *tty, int fg);
void tty_setinvert(tty_t *tty);
+void tty_setunderline(tty_t *tty);
void tty_setnormal(tty_t *tty);
+void tty_setnowrap(tty_t *tty);
+void tty_setwrap(tty_t *tty);
#define TTY_COLOR_BLACK 0
#define TTY_COLOR_RED 1
diff --git a/src/tty_interface.c b/src/tty_interface.c
index 8081f44..f6aeef0 100644
--- a/src/tty_interface.c
+++ b/src/tty_interface.c
@@ -7,6 +7,14 @@
#include "tty_interface.h"
#include "../config.h"
+static int isprint_unicode(char c) {
+ return isprint(c) || c & (1 << 7);
+}
+
+static int is_boundary(char c) {
+ return ~c & (1 << 7) || c & (1 << 6);
+}
+
static void clear(tty_interface_t *state) {
tty_t *tty = state->tty;
@@ -34,34 +42,32 @@ static void draw_match(tty_interface_t *state, const char *choice, int selected)
score_t score = match_positions(search, choice, &positions[0]);
- size_t maxwidth = tty_getwidth(tty);
-
- if (options->show_scores && maxwidth >= 9) {
+ if (options->show_scores) {
if (score == SCORE_MIN) {
tty_printf(tty, "( ) ");
} else {
tty_printf(tty, "(%5.2f) ", score);
}
- maxwidth -= 8;
}
if (selected)
+#ifdef TTY_SELECTION_UNDERLINE
+ tty_setunderline(tty);
+#else
tty_setinvert(tty);
+#endif
+ tty_setnowrap(tty);
for (size_t i = 0, p = 0; choice[i] != '\0'; i++) {
- if (i + 1 < maxwidth) {
- if (positions[p] == i) {
- tty_setfg(tty, TTY_COLOR_HIGHLIGHT);
- p++;
- } else {
- tty_setfg(tty, TTY_COLOR_NORMAL);
- }
- tty_printf(tty, "%c", choice[i]);
+ if (positions[p] == i) {
+ tty_setfg(tty, TTY_COLOR_HIGHLIGHT);
+ p++;
} else {
- tty_printf(tty, "$");
- break;
+ tty_setfg(tty, TTY_COLOR_NORMAL);
}
+ tty_printf(tty, "%c", choice[i]);
}
+ tty_setwrap(tty);
tty_setnormal(tty);
}
@@ -95,7 +101,10 @@ static void draw(tty_interface_t *state) {
tty_moveup(tty, num_lines);
}
- tty_setcol(tty, strlen(options->prompt) + state->cursor);
+ tty_setcol(tty, 0);
+ fputs(options->prompt, tty->fout);
+ for (size_t i = 0; i < state->cursor; i++)
+ fputc(state->search[i], tty->fout);
tty_flush(tty);
}
@@ -135,12 +144,16 @@ static void action_emit(tty_interface_t *state) {
static void action_del_char(tty_interface_t *state) {
if (*state->search) {
size_t length = strlen(state->search);
- if(state->cursor == 0) {
+ if (state->cursor == 0) {
return;
}
+ size_t original_cursor = state->cursor;
state->cursor--;
- memmove(&state->search[state->cursor], &state->search[state->cursor + 1], length - state->cursor);
+ while (!is_boundary(state->search[state->cursor]) && state->cursor)
+ state->cursor--;
+
+ memmove(&state->search[state->cursor], &state->search[original_cursor], length - original_cursor + 1);
}
}
@@ -169,7 +182,7 @@ static void action_prev(tty_interface_t *state) {
}
static void action_ignore(tty_interface_t *state) {
- (void) state;
+ (void)state;
}
static void action_next(tty_interface_t *state) {
@@ -178,13 +191,19 @@ static void action_next(tty_interface_t *state) {
}
static void action_left(tty_interface_t *state) {
- if (state->cursor > 0)
+ if (state->cursor > 0) {
state->cursor--;
+ while (!is_boundary(state->search[state->cursor]) && state->cursor)
+ state->cursor--;
+ }
}
static void action_right(tty_interface_t *state) {
- if (state->cursor < strlen(state->search))
+ if (state->cursor < strlen(state->search)) {
state->cursor++;
+ while (!is_boundary(state->search[state->cursor]))
+ state->cursor++;
+ }
}
static void action_beginning(tty_interface_t *state) {
@@ -197,13 +216,13 @@ static void action_end(tty_interface_t *state) {
static void action_pageup(tty_interface_t *state) {
update_state(state);
- for(size_t i = 0; i < state->options->num_lines && state->choices->selection > 0; i++)
+ for (size_t i = 0; i < state->options->num_lines && state->choices->selection > 0; i++)
choices_prev(state->choices);
}
static void action_pagedown(tty_interface_t *state) {
update_state(state);
- for(size_t i = 0; i < state->options->num_lines && state->choices->selection < state->choices->available-1; i++)
+ for (size_t i = 0; i < state->options->num_lines && state->choices->selection < state->choices->available - 1; i++)
choices_next(state->choices);
}
@@ -273,8 +292,8 @@ static const keybinding_t keybindings[] = {{"\x1b", action_exit}, /* ESC *
{KEY_CTRL('M'), action_emit}, /* CR */
{KEY_CTRL('P'), action_prev}, /* C-P */
{KEY_CTRL('N'), action_next}, /* C-N */
- {KEY_CTRL('K'), action_prev}, /* C-J */
- {KEY_CTRL('J'), action_next}, /* C-K */
+ {KEY_CTRL('K'), action_prev}, /* C-K */
+ {KEY_CTRL('J'), action_next}, /* C-J */
{KEY_CTRL('A'), action_beginning}, /* C-A */
{KEY_CTRL('E'), action_end}, /* C-E */
@@ -335,7 +354,7 @@ static void handle_input(tty_interface_t *state, const char *s, int handle_ambig
/* No matching keybinding, add to search */
for (int i = 0; input[i]; i++)
- if (isprint(input[i]))
+ if (isprint_unicode(input[i]))
append_search(state, input[i]);
/* We have processed the input, so clear it */
diff --git a/test/acceptance/acceptance_test.rb b/test/acceptance/acceptance_test.rb
index 4a11cac..52c6af0 100644
--- a/test/acceptance/acceptance_test.rb
+++ b/test/acceptance/acceptance_test.rb
@@ -5,8 +5,11 @@ require 'ttytest'
class FzyTest < Minitest::Test
FZY_PATH = File.expand_path('../../../fzy', __FILE__)
+ LEFT = "\e[D"
+ RIGHT = "\e[C"
+
def test_empty_list
- @tty = TTYtest.new_terminal(%{echo placeholder;echo -n "" | #{FZY_PATH}})
+ @tty = interactive_fzy(input: %w[], before: "placeholder")
@tty.assert_cursor_position(y: 1, x: 2)
@tty.assert_matches <<~TTY
placeholder
@@ -36,7 +39,7 @@ class FzyTest < Minitest::Test
end
def test_one_item
- @tty = TTYtest.new_terminal(%{echo placeholder;echo -n "test" | #{FZY_PATH}})
+ @tty = interactive_fzy(input: %w[test], before: "placeholder")
@tty.assert_matches <<~TTY
placeholder
>
@@ -68,7 +71,7 @@ class FzyTest < Minitest::Test
end
def test_two_items
- @tty = TTYtest.new_terminal(%{echo placeholder;echo -n "test\nfoo" | #{FZY_PATH}})
+ @tty = interactive_fzy(input: %w[test foo], before: "placeholder")
@tty.assert_cursor_position(y: 1, x: 2)
@tty.assert_matches <<~TTY
placeholder
@@ -105,7 +108,7 @@ class FzyTest < Minitest::Test
end
def test_editing
- @tty = TTYtest.new_terminal(%{echo placeholder;echo -n "test\nfoo" | #{FZY_PATH}})
+ @tty = interactive_fzy(input: %w[test foo], before: "placeholder")
@tty.assert_cursor_position(y: 1, x: 2)
@tty.assert_matches <<~TTY
placeholder
@@ -146,7 +149,7 @@ class FzyTest < Minitest::Test
end
def test_ctrl_d
- @tty = TTYtest.new_terminal(%{echo -n "foo\nbar" | #{FZY_PATH}})
+ @tty = interactive_fzy(input: %w[foo bar])
@tty.assert_matches ">\nfoo\nbar"
@tty.send_keys('foo')
@@ -158,7 +161,7 @@ class FzyTest < Minitest::Test
end
def test_ctrl_c
- @tty = TTYtest.new_terminal(%{echo -n "foo\nbar" | #{FZY_PATH}})
+ @tty = interactive_fzy(input: %w[foo bar])
@tty.assert_matches ">\nfoo\nbar"
@tty.send_keys('foo')
@@ -170,25 +173,25 @@ class FzyTest < Minitest::Test
end
def test_down_arrow
- @tty = TTYtest.new_terminal(%{echo -n "foo\nbar" | #{FZY_PATH}})
+ @tty = interactive_fzy(input: %w[foo bar])
@tty.assert_matches ">\nfoo\nbar"
@tty.send_keys("\e[A\r")
@tty.assert_matches "bar"
- @tty = TTYtest.new_terminal(%{echo -n "foo\nbar" | #{FZY_PATH}})
+ @tty = interactive_fzy(input: %w[foo bar])
@tty.assert_matches ">\nfoo\nbar"
@tty.send_keys("\eOA\r")
@tty.assert_matches "bar"
end
def test_up_arrow
- @tty = TTYtest.new_terminal(%{echo -n "foo\nbar" | #{FZY_PATH}})
+ @tty = interactive_fzy(input: %w[foo bar])
@tty.assert_matches ">\nfoo\nbar"
@tty.send_keys("\e[A") # first down
@tty.send_keys("\e[B\r") # and back up
@tty.assert_matches "foo"
- @tty = TTYtest.new_terminal(%{echo -n "foo\nbar" | #{FZY_PATH}})
+ @tty = interactive_fzy(input: %w[foo bar])
@tty.assert_matches ">\nfoo\nbar"
@tty.send_keys("\eOA") # first down
@tty.send_keys("\e[B\r") # and back up
@@ -196,45 +199,48 @@ class FzyTest < Minitest::Test
end
def test_lines
- @tty = TTYtest.new_terminal(%{seq 10 | #{FZY_PATH}})
+ input10 = (1..10).map(&:to_s)
+ input20 = (1..20).map(&:to_s)
+
+ @tty = interactive_fzy(input: input10)
@tty.assert_matches ">\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10"
- @tty = TTYtest.new_terminal(%{seq 20 | #{FZY_PATH}})
+ @tty = interactive_fzy(input: input20)
@tty.assert_matches ">\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10"
- @tty = TTYtest.new_terminal(%{seq 10 | #{FZY_PATH} -l 5})
+ @tty = interactive_fzy(input: input10, args: "-l 5")
@tty.assert_matches ">\n1\n2\n3\n4\n5"
- @tty = TTYtest.new_terminal(%{seq 10 | #{FZY_PATH} --lines=5})
+ @tty = interactive_fzy(input: input10, args: "--lines=5")
@tty.assert_matches ">\n1\n2\n3\n4\n5"
end
def test_prompt
- @tty = TTYtest.new_terminal(%{echo -n "" | #{FZY_PATH}})
+ @tty = interactive_fzy
@tty.send_keys("foo")
@tty.assert_matches '> foo'
- @tty = TTYtest.new_terminal(%{echo -n "" | #{FZY_PATH} -p 'C:\\'})
+ @tty = interactive_fzy(args: "-p 'C:\\'")
@tty.send_keys("foo")
@tty.assert_matches 'C:\foo'
- @tty = TTYtest.new_terminal(%{echo -n "" | #{FZY_PATH} --prompt="foo bar "})
+ @tty = interactive_fzy(args: "--prompt=\"foo bar \"")
@tty.send_keys("baz")
@tty.assert_matches "foo bar baz"
end
def test_show_scores
expected_score = '( inf)'
- @tty = TTYtest.new_terminal(%{echo -n "foo\nbar" | #{FZY_PATH} -s})
+ @tty = interactive_fzy(input: %w[foo bar], args: "-s")
@tty.send_keys('foo')
@tty.assert_matches "> foo\n#{expected_score} foo"
- @tty = TTYtest.new_terminal(%{echo -n "foo\nbar" | #{FZY_PATH} --show-scores})
+ @tty = interactive_fzy(input: %w[foo bar], args: "--show-scores")
@tty.send_keys('foo')
@tty.assert_matches "> foo\n#{expected_score} foo"
expected_score = '( 0.89)'
- @tty = TTYtest.new_terminal(%{echo -n "foo\nbar" | #{FZY_PATH} -s})
+ @tty = interactive_fzy(input: %w[foo bar], args: "-s")
@tty.send_keys('f')
@tty.assert_matches "> f\n#{expected_score} foo"
end
@@ -252,7 +258,7 @@ class FzyTest < Minitest::Test
end
def test_worker_count
- @tty = TTYtest.new_terminal(%{echo -n "foo\nbar" | #{FZY_PATH} -j1})
+ @tty = interactive_fzy(input: %w[foo bar], args: "-j1")
@tty.send_keys('foo')
@tty.assert_matches "> foo\nfoo"
@@ -266,29 +272,29 @@ class FzyTest < Minitest::Test
end
def test_initial_query
- @tty = TTYtest.new_terminal(%{echo -n "foo\nbar" | #{FZY_PATH} -q fo})
+ @tty = interactive_fzy(input: %w[foo bar], args: "-q fo")
@tty.assert_matches "> fo\nfoo"
@tty.send_keys("o")
@tty.assert_matches "> foo\nfoo"
@tty.send_keys("o")
@tty.assert_matches "> fooo"
- @tty = TTYtest.new_terminal(%{echo -n "foo\nbar" | #{FZY_PATH} -q asdf})
+ @tty = interactive_fzy(input: %w[foo bar], args: "-q asdf")
@tty.assert_matches "> asdf"
end
def test_non_interactive
- @tty = TTYtest.new_terminal(%{echo before; echo -n "foo\nbar" | #{FZY_PATH} -e foo; echo after})
+ @tty = interactive_fzy(input: %w[foo bar], args: "-e foo", before: "before", after: "after")
@tty.assert_matches "before\nfoo\nafter"
end
def test_moving_text_cursor
- @tty = TTYtest.new_terminal(%{echo -n "foo\nbar" | #{FZY_PATH}; echo after})
+ @tty = interactive_fzy(input: %w[foo bar])
@tty.send_keys("br")
@tty.assert_matches "> br\nbar"
@tty.assert_cursor_position(y: 0, x: 4)
- @tty.send_keys("\e[D") # left
+ @tty.send_keys(LEFT)
@tty.assert_cursor_position(y: 0, x: 3)
@tty.assert_matches "> br\nbar"
@tty.send_keys("a")
@@ -314,12 +320,132 @@ class FzyTest < Minitest::Test
# https://github.com/jhawthorn/fzy/issues/42
# https://cirw.in/blog/bracketed-paste
def test_bracketed_paste_characters
- @tty = TTYtest.new_terminal(%{echo -n "foo\nbar" | #{FZY_PATH}})
+ @tty = interactive_fzy(input: %w[foo bar])
@tty.assert_matches ">\nfoo\nbar"
@tty.send_keys("\e[200~foo\e[201~")
@tty.assert_matches "> foo\nfoo"
end
+ # https://github.com/jhawthorn/fzy/issues/81
+ def test_slow_stdin_fast_user
+ @tty = TTYtest.new_terminal(%{(sleep 0.5; echo aa; echo bc; echo bd) | #{FZY_PATH}})
+
+ # Before input has all come in, but wait for fzy to at least start
+ sleep 0.1
+
+ @tty.send_keys("b\r")
+ @tty.assert_matches "bc"
+ end
+
+ def test_unicode
+ @tty = interactive_fzy(input: %w[English Français 日本語])
+ @tty.assert_matches <<~TTY
+ >
+ English
+ Français
+ 日本語
+ TTY
+ @tty.assert_cursor_position(y: 0, x: 2)
+
+ @tty.send_keys("ç")
+ @tty.assert_matches <<~TTY
+ > ç
+ Français
+ TTY
+ @tty.assert_cursor_position(y: 0, x: 3)
+
+ @tty.send_keys("\r")
+ @tty.assert_matches "Français"
+ end
+
+ def test_unicode_backspace
+ @tty = interactive_fzy
+ @tty.send_keys "Français"
+ @tty.assert_matches "> Français"
+ @tty.assert_cursor_position(y: 0, x: 10)
+
+ @tty.send_keys(ctrl('H') * 3)
+ @tty.assert_matches "> Franç"
+ @tty.assert_cursor_position(y: 0, x: 7)
+
+ @tty.send_keys(ctrl('H'))
+ @tty.assert_matches "> Fran"
+ @tty.assert_cursor_position(y: 0, x: 6)
+
+ @tty.send_keys('ce')
+ @tty.assert_matches "> France"
+
+ @tty = interactive_fzy
+ @tty.send_keys "日本語"
+ @tty.assert_matches "> 日本語"
+ @tty.send_keys(ctrl('H'))
+ @tty.assert_matches "> 日本"
+ @tty.send_keys(ctrl('H'))
+ @tty.assert_matches "> 日"
+ @tty.send_keys(ctrl('H'))
+ @tty.assert_matches "> "
+ @tty.assert_cursor_position(y: 0, x: 2)
+ end
+
+ def test_unicode_delete_word
+ @tty = interactive_fzy
+ @tty.send_keys "Je parle Français"
+ @tty.assert_matches "> Je parle Français"
+ @tty.assert_cursor_position(y: 0, x: 19)
+
+ @tty.send_keys(ctrl('W'))
+ @tty.assert_matches "> Je parle"
+ @tty.assert_cursor_position(y: 0, x: 11)
+
+ @tty = interactive_fzy
+ @tty.send_keys "日本語"
+ @tty.assert_matches "> 日本語"
+ @tty.send_keys(ctrl('W'))
+ @tty.assert_matches "> "
+ @tty.assert_cursor_position(y: 0, x: 2)
+ end
+
+ def test_unicode_cursor_movement
+ @tty = interactive_fzy
+ @tty.send_keys "Français"
+ @tty.assert_cursor_position(y: 0, x: 10)
+
+ @tty.send_keys(LEFT*5)
+ @tty.assert_cursor_position(y: 0, x: 5)
+
+ @tty.send_keys(RIGHT*3)
+ @tty.assert_cursor_position(y: 0, x: 8)
+
+ @tty = interactive_fzy
+ @tty.send_keys "日本語"
+ @tty.assert_matches "> 日本語"
+ @tty.assert_cursor_position(y: 0, x: 8)
+ @tty.send_keys(LEFT)
+ @tty.assert_cursor_position(y: 0, x: 6)
+ @tty.send_keys(LEFT)
+ @tty.assert_cursor_position(y: 0, x: 4)
+ @tty.send_keys(LEFT)
+ @tty.assert_cursor_position(y: 0, x: 2)
+ @tty.send_keys(LEFT)
+ @tty.assert_cursor_position(y: 0, x: 2)
+ @tty.send_keys(RIGHT*3)
+ @tty.assert_cursor_position(y: 0, x: 8)
+ @tty.send_keys(RIGHT)
+ @tty.assert_cursor_position(y: 0, x: 8)
+ end
+
+ def test_long_strings
+ ascii = "LongStringOfText" * 6
+ unicode = "LongStringOfText" * 3
+
+ @tty = interactive_fzy(input: [ascii, unicode])
+ @tty.assert_matches <<~TTY
+ >
+ LongStringOfTextLongStringOfTextLongStringOfTextLongStringOfTextLongStringOfText
+ LongStringOfTextLongStringOfTextLongStri
+ TTY
+ end
+
def test_help
@tty = TTYtest.new_terminal(%{#{FZY_PATH} --help})
@tty.assert_matches <<TTY
@@ -335,4 +461,15 @@ Usage: fzy [OPTION]...
-v, --version Output version information and exit
TTY
end
+
+ private
+
+ def interactive_fzy(input: [], before: nil, after: nil, args: "")
+ cmd = []
+ cmd << %{echo "#{before}"} if before
+ cmd << %{printf "#{input.join("\\n")}" | #{FZY_PATH} #{args}}
+ cmd << %{echo "#{after}"} if after
+ cmd = cmd.join("; ")
+ TTYtest.new_terminal(cmd)
+ end
end