diff --git a/app/sheet.c b/app/sheet.c index b284c286..ea2d57fc 100644 --- a/app/sheet.c +++ b/app/sheet.c @@ -355,10 +355,13 @@ static zsvsheet_status zsvsheet_find(struct zsvsheet_builtin_proc_state *state, struct zsvsheet_opts zsvsheet_opts = {0}; int prompt_footer_row = (int)(di->dimensions->rows - di->dimensions->footer_span); + if (!current_ui_buffer->filename) + goto out; + if (!next) { get_subcommand("Find", prompt_buffer, sizeof(prompt_buffer), prompt_footer_row); if (*prompt_buffer == '\0') { - goto no_input; + goto out; } else { free(state->find); state->find = strdup(prompt_buffer); @@ -370,7 +373,7 @@ static zsvsheet_status zsvsheet_find(struct zsvsheet_builtin_proc_state *state, &di->update_buffer, state->custom_prop_handler, state->opts_used); } -no_input: +out: return zsvsheet_status_ok; } @@ -413,9 +416,12 @@ static zsvsheet_status zsvsheet_filter_handler(struct zsvsheet_proc_context *ctx int prompt_footer_row = (int)(di->dimensions->rows - di->dimensions->footer_span); int err; + if (!current_ui_buffer->filename) + goto out; + get_subcommand("Filter", prompt_buffer, sizeof(prompt_buffer), prompt_footer_row); if (*prompt_buffer == '\0') - goto no_input; + goto out; const char *data_filename = zsvsheet_buffer_data_filename(current_ui_buffer); char is_filtered_file = !(data_filename == current_ui_buffer->filename); @@ -444,10 +450,85 @@ static zsvsheet_status zsvsheet_filter_handler(struct zsvsheet_proc_context *ctx free(new_ui_buffer->filename); new_ui_buffer->filename = strdup(current_ui_buffer->filename); } -no_input: +out: return zsvsheet_status_ok; } +static zsvsheet_status zsvsheet_help_handler(struct zsvsheet_proc_context *ctx) { + struct zsvsheet_builtin_proc_state *state = (struct zsvsheet_builtin_proc_state *)ctx->subcommand_context; + struct zsvsheet_display_info *di = &state->display_info; + struct zsvsheet_screen_buffer_opts bopts = { + .no_rownum_column = 1, + .cell_buff_len = 64, + .max_cell_len = 0, + .rows = 256, + }; + struct zsvsheet_ui_buffer_opts uibopts = { + .buff_opts = &bopts, + .filename = NULL, + .no_rownum_col_offset = 1, + }; + struct zsvsheet_ui_buffer *uib; + zsvsheet_screen_buffer_t buffer; + enum zsvsheet_priv_status pstat; + enum zsvsheet_status stat = zsvsheet_status_error; + const size_t cols = 3; + + buffer = zsvsheet_screen_buffer_new(cols, &bopts, &pstat); + if (pstat != zsvsheet_priv_status_ok) + goto free_buffer; + + uib = zsvsheet_ui_buffer_new(buffer, &uibopts); + if (!uib) + goto free_buffer; + + const char *head[3] = {"Key(s)", "Action", "Description"}; + for (size_t j = 0; j < sizeof(head) / sizeof(head[0]); j++) { + pstat = zsvsheet_screen_buffer_write_cell(buffer, 0, j, (const unsigned char *)head[j]); + if (pstat != zsvsheet_priv_status_ok) + goto free_buffer; + } + + size_t row = 1; + for (size_t i = 0; zsvsheet_get_key_binding(i) != NULL; i++) { + struct zsvsheet_key_binding *kb = zsvsheet_get_key_binding(i); + struct zsvsheet_procedure *proc = zsvsheet_find_procedure(kb->proc_id); + + if (proc == NULL || kb->hidden) + continue; + + const char *desc[3] = { + zsvsheet_key_binding_ch_name(kb), + proc->name, + proc->description, + }; + + for (size_t j = 0; j < cols; j++) { + pstat = zsvsheet_screen_buffer_write_cell(buffer, row, j, (const unsigned char *)desc[j]); + if (pstat != zsvsheet_priv_status_ok) + goto free_buffer; + } + + row++; + } + + uib->dimensions.col_count = cols; + uib->dimensions.row_count = row; + uib->buff_used_rows = row; + + if (asprintf(&uib->status, " to exit help ") == -1) + goto free_buffer; + + zsvsheet_ui_buffer_push(di->ui_buffers.base, di->ui_buffers.current, uib); + stat = zsvsheet_status_ok; + goto out; + +free_buffer: + zsvsheet_screen_buffer_delete(buffer); +out: + return stat; +} + /* We do most procedures in one handler. More complex procedures can be * separated into their own handlers. */ @@ -510,32 +591,34 @@ zsvsheet_status zsvsheet_builtin_proc_handler(struct zsvsheet_proc_context *ctx) struct builtin_proc_desc { int proc_id; const char *name; + const char *description; zsvsheet_proc_fn handler; } builtin_procedures[] = { - { zsvsheet_builtin_proc_quit, "quit", zsvsheet_builtin_proc_handler }, - { zsvsheet_builtin_proc_escape, NULL, zsvsheet_builtin_proc_handler }, - { zsvsheet_builtin_proc_move_bottom, NULL, zsvsheet_builtin_proc_handler }, - { zsvsheet_builtin_proc_move_top, NULL, zsvsheet_builtin_proc_handler }, - { zsvsheet_builtin_proc_move_first_col, NULL, zsvsheet_builtin_proc_handler }, - { zsvsheet_builtin_proc_pg_down, "pageup", zsvsheet_builtin_proc_handler }, - { zsvsheet_builtin_proc_pg_up, "pagedn", zsvsheet_builtin_proc_handler }, - { zsvsheet_builtin_proc_move_last_col, NULL, zsvsheet_builtin_proc_handler }, - { zsvsheet_builtin_proc_move_up, NULL, zsvsheet_builtin_proc_handler }, - { zsvsheet_builtin_proc_move_down, NULL, zsvsheet_builtin_proc_handler }, - { zsvsheet_builtin_proc_move_left, NULL, zsvsheet_builtin_proc_handler }, - { zsvsheet_builtin_proc_move_right, NULL, zsvsheet_builtin_proc_handler }, - { zsvsheet_builtin_proc_find, NULL, zsvsheet_builtin_proc_handler }, - { zsvsheet_builtin_proc_find_next, NULL, zsvsheet_builtin_proc_handler }, - { zsvsheet_builtin_proc_resize, NULL, zsvsheet_builtin_proc_handler }, - { zsvsheet_builtin_proc_open_file, "open", zsvsheet_open_file_handler }, - { zsvsheet_builtin_proc_filter, "filter", zsvsheet_filter_handler }, + { zsvsheet_builtin_proc_quit, "quit", "Exit the application", zsvsheet_builtin_proc_handler }, + { zsvsheet_builtin_proc_escape, "escape", "Leave the current view or cancel a subcommand", zsvsheet_builtin_proc_handler }, + { zsvsheet_builtin_proc_move_bottom, "bottom", "Jump to the last row", zsvsheet_builtin_proc_handler }, + { zsvsheet_builtin_proc_move_top, "top", "Jump to the first row", zsvsheet_builtin_proc_handler }, + { zsvsheet_builtin_proc_move_first_col, "first", "Jump to the first column", zsvsheet_builtin_proc_handler }, + { zsvsheet_builtin_proc_pg_down, "pagedown", "Move down one page", zsvsheet_builtin_proc_handler }, + { zsvsheet_builtin_proc_pg_up, "pageup", "Move up one page", zsvsheet_builtin_proc_handler }, + { zsvsheet_builtin_proc_move_last_col, "last", "Jump to the last column", zsvsheet_builtin_proc_handler }, + { zsvsheet_builtin_proc_move_up, "up", "Move up one row", zsvsheet_builtin_proc_handler }, + { zsvsheet_builtin_proc_move_down, "down", "Move down one row", zsvsheet_builtin_proc_handler }, + { zsvsheet_builtin_proc_move_left, "left", "Move left one column", zsvsheet_builtin_proc_handler }, + { zsvsheet_builtin_proc_move_right, "right", "Move right one column", zsvsheet_builtin_proc_handler }, + { zsvsheet_builtin_proc_find, "find", "Set a search term and jump to the first result after the cursor", zsvsheet_builtin_proc_handler }, + { zsvsheet_builtin_proc_find_next, "next", "Jump to the next search result", zsvsheet_builtin_proc_handler }, + { zsvsheet_builtin_proc_resize, "resize", "Resize the layout to fit new terminal dimensions", zsvsheet_builtin_proc_handler }, + { zsvsheet_builtin_proc_open_file, "open", "Open a another CSV file", zsvsheet_open_file_handler }, + { zsvsheet_builtin_proc_filter, "filter", "Hide rows that do not contain the specified text", zsvsheet_filter_handler }, + { zsvsheet_builtin_proc_help, "help", "Display a list of actions and key-bindings", zsvsheet_help_handler }, { -1, NULL, NULL } }; /* clang-format on */ void zsvsheet_register_builtin_procedures(void) { for (struct builtin_proc_desc *desc = builtin_procedures; desc->proc_id != -1; ++desc) { - if (zsvsheet_register_builtin_proc(desc->proc_id, desc->name, desc->handler) < 0) { + if (zsvsheet_register_builtin_proc(desc->proc_id, desc->name, desc->description, desc->handler) < 0) { fprintf(stderr, "failed to register builtin procedure\n"); } } @@ -598,6 +681,7 @@ int ZSV_MAIN_FUNC(ZSV_COMMAND)(int argc, const char *argv[], struct zsv_opts *op noecho(); keypad(stdscr, TRUE); cbreak(); + set_escdelay(30); struct zsvsheet_display_dimensions display_dims = get_display_dimensions(1, 1); display_buffer_subtable(current_ui_buffer, header_span, &display_dims); diff --git a/app/sheet/key-bindings.c b/app/sheet/key-bindings.c index 71171963..dc92242f 100644 --- a/app/sheet/key-bindings.c +++ b/app/sheet/key-bindings.c @@ -1,7 +1,9 @@ #include "procedure.h" #include "key-bindings.h" +#include #include +#include #ifndef ZSVSHEET_CTRL /* clang-format off */ @@ -51,6 +53,16 @@ int zsvsheet_register_proc_key_binding(char ch, zsvsheet_proc_id_t proc_id) { return zsvsheet_register_key_binding(&binding); } +struct zsvsheet_key_binding *zsvsheet_get_key_binding(size_t i) { + if (i >= MAX_KEY_BINDINGS) + return NULL; + + if (key_bindings[i].ch != -1) + return &key_bindings[i]; + + return NULL; +} + struct zsvsheet_key_binding *zsvsheet_find_key_binding(int ch) { for (int i = 0; i < MAX_KEY_BINDINGS; ++i) { if (key_bindings[i].ch == ch) @@ -111,8 +123,11 @@ struct zsvsheet_key_binding zsvsheet_vim_key_bindings[] = { { .ch = 'q', .proc_id = zsvsheet_builtin_proc_quit, }, { .ch = 27, .proc_id = zsvsheet_builtin_proc_escape, }, - { .ch = KEY_RESIZE, .proc_id = zsvsheet_builtin_proc_resize, }, - + { + .ch = KEY_RESIZE, + .proc_id = zsvsheet_builtin_proc_resize, + .hidden = 1, + }, { .ch = '^', .proc_id = zsvsheet_builtin_proc_move_first_col,}, { .ch = '$', .proc_id = zsvsheet_builtin_proc_move_last_col, }, { .ch = KEY_SLEFT, .proc_id = zsvsheet_builtin_proc_move_first_col,}, @@ -132,7 +147,12 @@ struct zsvsheet_key_binding zsvsheet_vim_key_bindings[] = { { .ch = KEY_NPAGE, .proc_id = zsvsheet_builtin_proc_pg_down, }, { .ch = KEY_PPAGE, .proc_id = zsvsheet_builtin_proc_pg_up, }, - { .ch = 'g', .handler = zsvsheet_vim_g_key_binding_dmux_handler }, + { + .ch = 'g', + .ch_name = "g g", + .proc_id = zsvsheet_builtin_proc_move_top, + .handler = zsvsheet_vim_g_key_binding_dmux_handler + }, { .ch = 'G', .proc_id = zsvsheet_builtin_proc_move_bottom, }, /* Shift up/down also move you to the top/bottom of the page but have a * slightly different behaviour in terms of what ends up in the view but @@ -150,6 +170,7 @@ struct zsvsheet_key_binding zsvsheet_vim_key_bindings[] = { /* Open is a subcommand only in vim. Keeping the binding for now */ { .ch = 'e', .proc_id = zsvsheet_builtin_proc_open_file, }, { .ch = 'f', .proc_id = zsvsheet_builtin_proc_filter, }, + { .ch = '?', .proc_id = zsvsheet_builtin_proc_help, }, { .ch = -1 } }; @@ -183,8 +204,11 @@ zsvsheet_status zsvsheet_emacs_Cs_key_binding_dmux_handler(struct zsvsheet_key_b /* clang-format off */ struct zsvsheet_key_binding zsvsheet_emacs_key_bindings[] = { { .ch = 27, .proc_id = zsvsheet_builtin_proc_escape, }, - { .ch = KEY_RESIZE, .proc_id = zsvsheet_builtin_proc_resize, }, - + { + .ch = KEY_RESIZE, + .proc_id = zsvsheet_builtin_proc_resize, + .hidden = 1, + }, { .ch = ZSVSHEET_CTRL('a'), .proc_id = zsvsheet_builtin_proc_move_first_col,}, { .ch = ZSVSHEET_CTRL('e'), .proc_id = zsvsheet_builtin_proc_move_last_col,}, @@ -206,6 +230,8 @@ struct zsvsheet_key_binding zsvsheet_emacs_key_bindings[] = { { .ch = ZSVSHEET_CTRL('f'), .handler = zsvsheet_emacs_Cf_key_binding_dmux_handler, }, /* No such thing in emacs, find a more suitable binding */ { .ch = 'f', .proc_id = zsvsheet_builtin_proc_filter, }, + { .ch = ZSVSHEET_CTRL('h'), .proc_id = zsvsheet_builtin_proc_help, }, + { .ch = -1 } }; @@ -214,3 +240,43 @@ struct zsvsheet_key_binding zsvsheet_emacs_key_bindings[] = { void zsvsheet_register_emacs_key_bindings(void) { zsvsheet_register_builtin_key_bindings(zsvsheet_emacs_key_bindings); } + +struct zsvsheet_ch_name { + int ch; + const char *name; +}; + +/* clang-format off */ +struct zsvsheet_ch_name zsvsheet_common_ch_names[] = { + { .ch = KEY_RESIZE, .name = "" }, + { .ch = 27, .name = "" }, + { .ch = KEY_SLEFT, .name = "" }, + { .ch = KEY_SRIGHT, .name = "" }, + { .ch = KEY_UP, .name = "" }, + { .ch = KEY_DOWN, .name = "" }, + { .ch = KEY_LEFT, .name = "" }, + { .ch = KEY_RIGHT, .name = "" }, + { .ch = ZSVSHEET_CTRL('a'), .name = "a" }, + { .ch = ZSVSHEET_CTRL('d'), .name = "d" }, + { .ch = ZSVSHEET_CTRL('e'), .name = "e" }, + { .ch = ZSVSHEET_CTRL('f'), .name = "f" }, + { .ch = ZSVSHEET_CTRL('h'), .name = "f" }, + { .ch = ZSVSHEET_CTRL('u'), .name = "u" }, + { .ch = ZSVSHEET_CTRL('v'), .name = "v" }, + { .ch = KEY_NPAGE, .name = "" }, + { .ch = KEY_PPAGE, .name = "" }, + { .ch = -1 }, +}; +/* clang-format on */ + +const char *zsvsheet_key_binding_ch_name(struct zsvsheet_key_binding *binding) { + if (binding->ch_name) + return binding->ch_name; + + for (int i = 0; zsvsheet_common_ch_names[i].ch != -1; i++) { + if (zsvsheet_common_ch_names[i].ch == binding->ch) + return zsvsheet_common_ch_names[i].name; + } + + return keyname(binding->ch); +} diff --git a/app/sheet/key-bindings.h b/app/sheet/key-bindings.h index 764454d2..913afc65 100644 --- a/app/sheet/key-bindings.h +++ b/app/sheet/key-bindings.h @@ -20,7 +20,8 @@ enum zsvsheet_key { zsvsheet_key_find, zsvsheet_key_find_next, zsvsheet_key_open_file, - zsvsheet_key_resize + zsvsheet_key_resize, + zsvsheet_key_question, }; struct zsvsheet_key_binding_context; @@ -32,6 +33,8 @@ typedef zsvsheet_status (*zsvsheet_key_binding_fn)(struct zsvsheet_key_binding_c * be implemented if need be by defining a custom handler */ struct zsvsheet_key_binding { int ch; + const char *ch_name; + char hidden; zsvsheet_key_binding_fn handler; zsvsheet_proc_id_t proc_id; }; @@ -51,6 +54,7 @@ int zsvsheet_register_key_binding(struct zsvsheet_key_binding *binding); zsvsheet_status zsvsheet_key_press(int ch, void *subcommand_context); +struct zsvsheet_key_binding *zsvsheet_get_key_binding(size_t i); struct zsvsheet_key_binding *zsvsheet_find_key_binding(int ch); /* @@ -61,4 +65,6 @@ void zsvsheet_register_vim_key_bindings(void); void zsvsheet_register_emacs_key_bindings(void); +const char *zsvsheet_key_binding_ch_name(struct zsvsheet_key_binding *binding); + #endif diff --git a/app/sheet/procedure.c b/app/sheet/procedure.c index 7b4e5361..091ebee3 100644 --- a/app/sheet/procedure.c +++ b/app/sheet/procedure.c @@ -15,6 +15,7 @@ struct zsvsheet_procedure { zsvsheet_proc_id_t id; const char *name; + const char *description; zsvsheet_proc_fn handler; }; @@ -75,12 +76,14 @@ static zsvsheet_proc_id_t zsvsheet_do_register_proc(struct zsvsheet_procedure *p return proc->id; } -zsvsheet_proc_id_t zsvsheet_register_builtin_proc(zsvsheet_proc_id_t id, const char *name, zsvsheet_proc_fn handler) { - struct zsvsheet_procedure procedure = {.id = id, .name = name, .handler = handler}; +zsvsheet_proc_id_t zsvsheet_register_builtin_proc(zsvsheet_proc_id_t id, const char *name, const char *description, + zsvsheet_proc_fn handler) { + struct zsvsheet_procedure procedure = {.id = id, .name = name, .description = description, .handler = handler}; return zsvsheet_do_register_proc(&procedure); } -zsvsheet_proc_id_t zsvsheet_register_proc(const char *name, zsvsheet_proc_fn handler) { - struct zsvsheet_procedure procedure = {.id = zsvsheet_generate_proc_id(), .name = name, .handler = handler}; +zsvsheet_proc_id_t zsvsheet_register_proc(const char *name, const char *description, zsvsheet_proc_fn handler) { + struct zsvsheet_procedure procedure = { + .id = zsvsheet_generate_proc_id(), .name = name, .description = description, .handler = handler}; return zsvsheet_do_register_proc(&procedure); } diff --git a/app/sheet/procedure.h b/app/sheet/procedure.h index d1023bd1..bca5fa91 100644 --- a/app/sheet/procedure.h +++ b/app/sheet/procedure.h @@ -30,6 +30,8 @@ enum { zsvsheet_builtin_proc_open_file, zsvsheet_builtin_proc_resize, zsvsheet_builtin_proc_prompt, + zsvsheet_builtin_proc_help, + zsvsheet_builtin_proc_vim_g_key_binding_dmux, }; #define ZSVSHEET_PROC_INVALID 0 @@ -73,9 +75,10 @@ zsvsheet_status zsvsheet_proc_invoke_from_keypress(zsvsheet_proc_id_t proc_id, i zsvsheet_status zsvsheet_proc_invoke(zsvsheet_proc_id_t proc_id, struct zsvsheet_proc_context *ctx); /* Register builtin procedure with fixed id */ -zsvsheet_proc_id_t zsvsheet_register_builtin_proc(zsvsheet_proc_id_t id, const char *name, zsvsheet_proc_fn handler); +zsvsheet_proc_id_t zsvsheet_register_builtin_proc(zsvsheet_proc_id_t id, const char *name, const char *description, + zsvsheet_proc_fn handler); /* Dynamically register a procedure, returns a positive id or negative error */ -zsvsheet_proc_id_t zsvsheet_register_proc(const char *name, zsvsheet_proc_fn handler); +zsvsheet_proc_id_t zsvsheet_register_proc(const char *name, const char *description, zsvsheet_proc_fn handler); #endif /* ZSVSHEET_PROCEDURE_H */ diff --git a/app/test/Makefile b/app/test/Makefile index 65501fe5..d2bb844e 100644 --- a/app/test/Makefile +++ b/app/test/Makefile @@ -63,9 +63,9 @@ COLOR_RED=\033[1;31m COLOR_BLUE=\033[1;34m COLOR_PINK=\033[1;35m -TEST_PASS=echo "${COLOR_BLUE}$@: ${COLOR_GREEN}Passed${COLOR_NONE}" -TEST_FAIL=(echo "${COLOR_BLUE}$@: ${COLOR_RED}Failed!${COLOR_NONE}" && exit 1) -TEST_INIT=mkdir -p ${TMP_DIR} && echo "${COLOR_PINK}$@: ${COLOR_NONE}" +TEST_PASS=echo -e "${COLOR_BLUE}$@: ${COLOR_GREEN}Passed${COLOR_NONE}" +TEST_FAIL=(echo -e "${COLOR_BLUE}$@: ${COLOR_RED}Failed!${COLOR_NONE}" && exit 1) +TEST_INIT=mkdir -p ${TMP_DIR} && echo -e "${COLOR_PINK}$@: ${COLOR_NONE}" ARGS-sql='select [Loan Number] from data' @@ -587,7 +587,7 @@ test-sheet: test-%: ${BUILD_DIR}/bin/zsv_%${EXE} worldcitiespop_mil.csv test-she test-sheet-cleanup: @rm -f tmux-*.log -test-sheet-all: test-sheet-1 test-sheet-2 test-sheet-3 test-sheet-4 test-sheet-5 test-sheet-6 test-sheet-7 test-sheet-8 test-sheet-9 test-sheet-10 test-sheet-11 test-sheet-12 +test-sheet-all: test-sheet-1 test-sheet-2 test-sheet-3 test-sheet-4 test-sheet-5 test-sheet-6 test-sheet-7 test-sheet-8 test-sheet-9 test-sheet-10 test-sheet-11 test-sheet-12 test-sheet-13 test-sheet-14 @(for SESSION in $^; do ! tmux kill-session -t "$$SESSION" 2>/dev/null; done && ${TEST_PASS} || ${TEST_FAIL}) TMUX_TERM=xterm-256color @@ -722,3 +722,27 @@ test-sheet-12: ${BUILD_DIR}/bin/zsv_sheet${EXE} worldcitiespop_mil.csv tmux capture-pane -t $@ -p ${REDIRECT1} ${TMP_DIR}/$@.out && \ tmux send-keys -t $@ "q" && \ ${CMP} ${TMP_DIR}/$@.out expected/$@.out && ${TEST_PASS} || (echo 'Incorrect output:' && cat ${TMP_DIR}/$@.out && ${TEST_FAIL})) + +test-sheet-13: ${BUILD_DIR}/bin/zsv_sheet${EXE} worldcitiespop_mil.csv + @${TEST_INIT} + @echo 'set-option default-terminal "${TMUX_TERM}"' > ~/.tmux.conf + @(tmux new-session -x 80 -y 25 -d -s $@ "${PREFIX} $< worldcitiespop_mil.csv" && \ + sleep 0.5 && \ + tmux send-keys -t $@ "?" "/" "f" && \ + sleep 0.5 && \ + tmux capture-pane -t $@ -p ${REDIRECT1} ${TMP_DIR}/$@.out && \ + tmux send-keys -t $@ "q" && \ + ${CMP} ${TMP_DIR}/$@.out expected/$@.out && ${TEST_PASS} || (echo 'Incorrect output:' && cat ${TMP_DIR}/$@.out && ${TEST_FAIL})) + +test-sheet-14: ${BUILD_DIR}/bin/zsv_sheet${EXE} worldcitiespop_mil.csv + @${TEST_INIT} + @echo 'set-option default-terminal "${TMUX_TERM}"' > ~/.tmux.conf + @(tmux new-session -x 80 -y 25 -d -s $@ "${PREFIX} $< worldcitiespop_mil.csv" && \ + sleep 0.5 && \ + tmux send-keys -t $@ "?" && \ + sleep 0.5 && \ + tmux send-keys -t $@ "Escape" && \ + sleep 1 && \ + tmux capture-pane -t $@ -p ${REDIRECT1} ${TMP_DIR}/$@.out && \ + tmux send-keys -t $@ "q" && \ + ${CMP} ${TMP_DIR}/$@.out expected/$@.out && ${TEST_PASS} || (echo 'Incorrect output:' && cat ${TMP_DIR}/$@.out && ${TEST_FAIL})) diff --git a/app/test/expected/test-sheet-13.out b/app/test/expected/test-sheet-13.out new file mode 100644 index 00000000..56fc3c48 --- /dev/null +++ b/app/test/expected/test-sheet-13.out @@ -0,0 +1,25 @@ +Key(s) Action Description +q quit Exit the application + escape Leave the current view or +^ first Jump to the first column +$ last Jump to the last column + first Jump to the first column + last Jump to the last column +k up Move up one row +j down Move down one row +h left Move left one column +l right Move right one column + up Move up one row + down Move down one row + left Move left one column + right Move right one column +d pagedown Move down one page +u pageup Move up one page + pagedown Move down one page + pageup Move up one page +g g top Jump to the first row +G bottom Jump to the last row +/ find Set a search term and jum +n next Jump to the next search r +e open Open a another CSV file + to exit help Key(s) diff --git a/app/test/expected/test-sheet-14.out b/app/test/expected/test-sheet-14.out new file mode 100644 index 00000000..610fb180 --- /dev/null +++ b/app/test/expected/test-sheet-14.out @@ -0,0 +1,25 @@ +Row # Country City AccentCit Region Populatio Latitude Longitude +1 ir sarmaj-e Sarmaj-e 13 34.3578 47.5207 +2 ad aixirival Aixirival 06 42.466666 1.5 +3 mm mokho-atw Mokho-atw 09 18.033333 96.75 +4 id selingon Selingon 17 -8.8374 116.4914 +5 ir berimvand Berimvand 13 34.2953 47.1096 +6 pl chomiaza Chomiaza 73 52.7508 17.841793 +7 mg itona Itona 02 -23.86666 47.216666 +8 ad andorra-v Andorra-V 07 42.5 1.5166667 +9 mx la tortug La Tortug 25 25.75 -108.3333 +10 us asaph Asaph PA 41.770833 -77.40527 +11 ad andorre-v Andorre-V 07 42.5 1.5166667 +12 ru dolmatova Dolmatova 71 57.436791 63.279522 +13 ro escu Escu 13 47.133333 23.533333 +14 us la presa La Presa CA 35119 32.708055 -116.9963 +15 pk makam khu Makam Khu 04 33.650863 72.551536 +16 ad aubinya Aubinyà 06 42.45 1.5 +17 mm kiosong Kiosöng 11 22.583333 97.05 +18 tr donencay Dönençay 28 40.266667 38.583333 +19 it roncaglia Roncaglia 05 45.05 9.8 +20 ml kourmouss Kourmouss 04 14.75 -5.033333 +21 id lamogo Lamogo 38 -4.3945 119.9028 +22 ad casas vil Casas Vil 03 42.533333 1.5666667 +23 ru otdeleniy Otdeleniy 86 51.726473 39.714345 +? for help 1