Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add help buffer #289

Merged
merged 3 commits into from
Nov 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 106 additions & 22 deletions app/sheet.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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, "<esc> 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.
*/
Expand Down Expand Up @@ -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");
}
}
Expand Down Expand Up @@ -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);

Expand Down
76 changes: 71 additions & 5 deletions app/sheet/key-bindings.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#include "procedure.h"
#include "key-bindings.h"

#include <errno.h>
#include <stdio.h>
#include <assert.h>

#ifndef ZSVSHEET_CTRL
/* clang-format off */
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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,},
Expand All @@ -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
Expand All @@ -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 }
};
Expand Down Expand Up @@ -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,},

Expand All @@ -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 }
};
Expand All @@ -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 = "<resize>" },
{ .ch = 27, .name = "<esc>" },
{ .ch = KEY_SLEFT, .name = "<shift><left>" },
{ .ch = KEY_SRIGHT, .name = "<shift><right>" },
{ .ch = KEY_UP, .name = "<up>" },
{ .ch = KEY_DOWN, .name = "<down>" },
{ .ch = KEY_LEFT, .name = "<left>" },
{ .ch = KEY_RIGHT, .name = "<right>" },
{ .ch = ZSVSHEET_CTRL('a'), .name = "<ctrl>a" },
{ .ch = ZSVSHEET_CTRL('d'), .name = "<ctrl>d" },
{ .ch = ZSVSHEET_CTRL('e'), .name = "<ctrl>e" },
{ .ch = ZSVSHEET_CTRL('f'), .name = "<ctrl>f" },
{ .ch = ZSVSHEET_CTRL('h'), .name = "<ctrl>f" },
{ .ch = ZSVSHEET_CTRL('u'), .name = "<ctrl>u" },
{ .ch = ZSVSHEET_CTRL('v'), .name = "<ctrl>v" },
{ .ch = KEY_NPAGE, .name = "<page up>" },
{ .ch = KEY_PPAGE, .name = "<page down>" },
{ .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);
}
8 changes: 7 additions & 1 deletion app/sheet/key-bindings.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
};
Expand All @@ -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);

/*
Expand All @@ -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
11 changes: 7 additions & 4 deletions app/sheet/procedure.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
struct zsvsheet_procedure {
zsvsheet_proc_id_t id;
const char *name;
const char *description;
zsvsheet_proc_fn handler;
};

Expand Down Expand Up @@ -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);
}
7 changes: 5 additions & 2 deletions app/sheet/procedure.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 */
Loading