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

Pivot builtin #336

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
16 changes: 15 additions & 1 deletion app/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -198,12 +198,14 @@ ZSV=$(BINDIR)/zsv${EXE}
SOURCES=echo paste count count-pull select select-pull 2tsv 2json serialize flatten pretty stack desc sql 2db compare prop rm mv jq overwrite
CLI_SOURCES=echo select desc count paste 2tsv pretty sql flatten 2json serialize stack 2db compare prop rm mv jq overwrite

SHEET_INCLUDES=
ifeq ($(ZSVSHEET_BUILD),1)
SOURCES+=sheet
CLI_SOURCES+=sheet
CFLAGS+=-DZSVSHEET_BUILD
CFLAGS+=${CFLAGS_NCURSES}
LDFLAGS+=${LDFLAGS_NCURSES}
SHEET_INCLUDES=$(wildcard sheet/*.c sheet/*.h)
endif

CFLAGS+= -DUSE_JQ
Expand Down Expand Up @@ -285,6 +287,8 @@ SQLITE_SRC=${THIS_MAKEFILE_DIR}/external/sqlite3/sqlite3*.c
SQLITE_EXT=${BUILD_DIR}-external/sqlite3/sqlite3_and_csv_vtab.o
SQLITE_EXT_INCLUDE=-I${THIS_MAKEFILE_DIR}/external/sqlite3

SQL_INTERNAL_OBJECT=${CLI_OBJ_PFX}sql_internal.o

# everything uses prop, which in turn uses yajl and jq and json and sqlite3
OBJECTS+= ${YAJL_OBJ} ${YAJL_HELPER_OBJ} ${BUILD_DIR}/objs/utils/json.o ${SQLITE_EXT}
MORE_SOURCE+= ${YAJL_INCLUDE} ${YAJL_HELPER_INCLUDE} -I${JQ_INCLUDE_DIR} ${SQLITE_EXT_INCLUDE}
Expand Down Expand Up @@ -398,12 +402,18 @@ ${INIH_OBJECT}: ${INIH_SRC}/ini.c
@mkdir -p `dirname "$@"`
${CC} ${CFLAGS} -I${INIH_INCLUDE} -DINI_HANDLER_LINENO=1 -DINI_CALL_HANDLER_ON_NEW_SECTION=1 -c $< -o $@

${SQL_INTERNAL_OBJECT}: ${CLI_OBJ_PFX}%.o: %.c %.h
@mkdir -p `dirname "$@"`
${CC} ${CFLAGS} -I${INCLUDE_DIR} -c -o $@ $<

${CLI_OBJ_PFX}sheet.o: ${SHEET_INCLUDES}

${CLI_APP_OBJECT} : cli_ini.c builtin/*.c ${JQ_LIB}
${CLI_APP_OBJECT} ${CLI_OBJECTS}: ${CLI_OBJ_PFX}%.o: %.c ${UTF8PROC_SRC}/utf8proc.c # ${MORE_OBJECTS}
@mkdir -p `dirname "$@"`
${CC} ${CFLAGS} -DVERSION=\"${VERSION}\" -DZSV_CLI ${CLI_INCLUDE} -I${THIS_MAKEFILE_DIR}/external/sglib -I${INCLUDE_DIR} -I${UTF8PROC_INCLUDE} -c $< -o $@ ${MORE_SOURCE}

${CLI}: cli_internal.c.in cli_internal.h cli_internal.h.in ${CLI_APP_OBJECT} ${CLI_OBJECTS} ${OBJECTS} ${UTF8PROC_OBJECT} cli_ini.c ${INIH_OBJECT} ${LIBZSV_INSTALL} ${MORE_OBJECTS}
${CLI}: cli_internal.c.in cli_internal.h cli_internal.h.in ${CLI_APP_OBJECT} ${CLI_OBJECTS} ${OBJECTS} ${UTF8PROC_OBJECT} cli_ini.c ${INIH_OBJECT} ${LIBZSV_INSTALL} ${MORE_OBJECTS} ${SQL_INTERNAL_OBJECT}
@mkdir -p `dirname "$@"`
${CC} ${CFLAGS} ${CFLAGS_EXE} -I${INCLUDE_DIR} -o $@ ${CLI_APP_OBJECT} ${CLI_OBJECTS} ${OBJECTS} ${UTF8PROC_OBJECT} ${INIH_OBJECT} -L${LIBDIR} ${LIBZSV_L} ${LDFLAGS} ${LDFLAGS_OPT} ${MORE_OBJECTS} ${MORE_SOURCE} ${MORE_LIBS} ${STATIC_LIB_FLAGS}
@echo Built $@
Expand Down Expand Up @@ -468,6 +478,10 @@ ${CLI} ${STANDALONE_PFX}2json${EXE}: MORE_OBJECTS+= ${BUILD_DIR}/objs/utils/db.o
# pretty uses termcap
${CLI} ${STANDALONE_PFX}pretty${EXE}: MORE_LIBS+=${LDFLAGS_TERMCAP}

${STANDALONE_PFX}sheet${EXE}: ${SHEET_INCLUDES}

${CLI} ${STANDALONE_PFX}sheet${EXE} ${STANDALONE_PFX}sql${EXE}: ${SQL_INTERNAL_OBJECT}
${CLI} ${STANDALONE_PFX}sheet${EXE} ${STANDALONE_PFX}sql${EXE}: MORE_OBJECTS+=${SQL_INTERNAL_OBJECT}

${STANDALONE_PFX}%${EXE}: %.c ${OBJECTS} ${MORE_OBJECTS} ${LIBZSV_INSTALL} ${UTF8PROC_OBJECT}
@mkdir -p `dirname "$@"`
Expand Down
1 change: 1 addition & 0 deletions app/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,7 @@ static struct zsv_ext_callbacks *zsv_ext_callbacks_init(struct zsv_ext_callbacks
e->ext_sheet_buffer_filename = zsvsheet_buffer_filename;
e->ext_sheet_buffer_data_filename = zsvsheet_buffer_data_filename;
e->ext_sheet_open_file = zsvsheet_open_file;
e->ext_sheet_open_file_opts = zsvsheet_ext_open_file_opts;
e->ext_sheet_register_proc = zsvsheet_register_proc;
e->ext_sheet_register_proc_key_binding = zsvsheet_register_proc_key_binding;
e->ext_sheet_push_transformation = zsvsheet_push_transformation;
Expand Down
18 changes: 9 additions & 9 deletions app/ext_example/mysheet_extension.c
Original file line number Diff line number Diff line change
Expand Up @@ -189,9 +189,12 @@ static zsvsheet_status zsv_sqlite3_to_csv(zsvsheet_proc_context_t pctx, struct z
if (writer_opts.stream)
fclose(writer_opts.stream);

if (tmp_fn && zsv_file_exists(tmp_fn))
zst = zsv_cb.ext_sheet_open_file(pctx, tmp_fn, NULL);
else {
if (tmp_fn && zsv_file_exists(tmp_fn)) {
struct zsvsheet_open_file_opts ofopts = {0};
ofopts.data_filename = tmp_fn;
ofopts.no_auto_row_num = 1;
zst = zsv_cb.ext_sheet_open_file_opts(pctx, &ofopts);
} else {
if (zst == zsvsheet_status_ok) {
zst = zsvsheet_status_error; // to do: make this more specific
if (!err_msg && zdb && zdb->rc != SQLITE_OK)
Expand Down Expand Up @@ -229,12 +232,9 @@ zsvsheet_status pivot_drill_down(zsvsheet_proc_context_t ctx) {
if (!zdb || !(sql_str = sqlite3_str_new(zdb->db)))
zst = zsvsheet_status_memory;
else if (zdb->rc == SQLITE_OK && zsv_cb.ext_sqlite3_add_csv(zdb, pd->data_filename, NULL, NULL) == SQLITE_OK) {
if (zsv_cb.ext_sheet_buffer_info(buff).has_row_num)
sqlite3_str_appendf(sql_str, "select *");
else
sqlite3_str_appendf(sql_str, "select rowid as [Row #], *");
sqlite3_str_appendf(sql_str, "select rowid as [Row #], *");
sqlite3_str_appendf(sql_str, " from data where %s = %Q", pd->value_sql, pr->value);
fprintf(stderr, "SQL: %s\n", sqlite3_str_value(sql_str));
// fprintf(stderr, "SQL: %s\n", sqlite3_str_value(sql_str));
zst = zsv_sqlite3_to_csv(ctx, zdb, sqlite3_str_value(sql_str), NULL, NULL, NULL);
}
if (sql_str)
Expand Down Expand Up @@ -327,7 +327,7 @@ enum zsv_ext_status zsv_ext_init(struct zsv_ext_callbacks *cb, zsv_execution_con
int proc_id = zsv_cb.ext_sheet_register_proc("my-sheet-pivot", "my sheet pivot", my_pivot_table_command_handler);
if (proc_id < 0)
return zsv_ext_status_error;
zsv_cb.ext_sheet_register_proc_key_binding('v', proc_id);
zsv_cb.ext_sheet_register_proc_key_binding('s', proc_id);
return zsv_ext_status_ok;
}

Expand Down
51 changes: 34 additions & 17 deletions app/sheet.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ struct zsvsheet_opts {
#define ZSVSHEET_CELL_DISPLAY_MIN_WIDTH 10
static size_t zsvsheet_cell_display_width(struct zsvsheet_ui_buffer *ui_buffer,
struct zsvsheet_display_dimensions *ddims) {
size_t width = ddims->columns /
(ui_buffer->dimensions.col_count + (ui_buffer->rownum_col_offset && !ui_buffer->has_row_num ? 1 : 0));
size_t width = ddims->columns / (ui_buffer->dimensions.col_count +
(ui_buffer->rownum_col_offset && !ui_buffer->no_add_row_num ? 1 : 0));
return width < ZSVSHEET_CELL_DISPLAY_MIN_WIDTH ? ZSVSHEET_CELL_DISPLAY_MIN_WIDTH : width;
}

Expand Down Expand Up @@ -166,7 +166,8 @@ size_t display_data_rowcount(struct zsvsheet_display_dimensions *dims) {
return dims->rows - dims->footer_span - dims->header_span;
}

char zsvsheet_status_text[256] = {0};
static int zsvsheet_status_priority;
static char zsvsheet_status_text[256];
static void zsvsheet_display_status_text(const struct zsvsheet_display_dimensions *ddims) {
// clear the entire line
mvprintw(ddims->rows - ddims->footer_span, 0, "%-*s", (int)sizeof(zsvsheet_status_text), "");
Expand All @@ -177,15 +178,24 @@ static void zsvsheet_display_status_text(const struct zsvsheet_display_dimension
attroff(A_REVERSE);
}

static void zsvsheet_priv_set_status(const struct zsvsheet_display_dimensions *ddims, int overwrite, const char *fmt,
#define ZSVSHEET_STATUS_LOW_PRIO 1
#define ZSVSHEET_STATUS_HIGH_PRIO 10

static void zsvsheet_priv_set_status(const struct zsvsheet_display_dimensions *ddims, int priority, const char *fmt,
...) {
if (overwrite || !*zsvsheet_status_text) {
if (priority > zsvsheet_status_priority || !*zsvsheet_status_text) {
va_list argv;
va_start(argv, fmt);
vsnprintf(zsvsheet_status_text, sizeof(zsvsheet_status_text), fmt, argv);
va_end(argv);
// note: if (n < (int)sizeof(zsvsheet_status_text)), then we just ignore
zsvsheet_status_priority = priority;
}

// The priority decays with each call which is at least n times per second as set by halfdelay
if (zsvsheet_status_priority > 0)
zsvsheet_status_priority--;

zsvsheet_display_status_text(ddims);
}

Expand Down Expand Up @@ -331,7 +341,7 @@ char zsvsheet_handle_find_next(struct zsvsheet_ui_buffer *uib, const char *needl
*update_buffer = zsvsheet_goto_input_raw_row(uib, zsvsheet_opts->found_rownum, header_span, ddims, (size_t)-1);
return 1;
}
zsvsheet_priv_set_status(ddims, 1, "Not found");
zsvsheet_priv_set_status(ddims, ZSVSHEET_STATUS_HIGH_PRIO, "Not found");
return 0;
}

Expand Down Expand Up @@ -394,11 +404,11 @@ static zsvsheet_status zsvsheet_open_file_handler(struct zsvsheet_proc_context *
if ((err = zsvsheet_ui_buffer_open_file(filename, NULL, state->custom_prop_handler, di->ui_buffers.base,
di->ui_buffers.current))) {
if (err > 0)
zsvsheet_priv_set_status(di->dimensions, 1, "%s: %s", filename, strerror(err));
zsvsheet_priv_set_status(di->dimensions, ZSVSHEET_STATUS_HIGH_PRIO, "%s: %s", filename, strerror(err));
else if (err < 0)
zsvsheet_priv_set_status(di->dimensions, 1, "Unexpected error");
zsvsheet_priv_set_status(di->dimensions, ZSVSHEET_STATUS_HIGH_PRIO, "Unexpected error");
else
zsvsheet_priv_set_status(di->dimensions, 1, "Not found: %s", filename);
zsvsheet_priv_set_status(di->dimensions, ZSVSHEET_STATUS_HIGH_PRIO, "Not found: %s", filename);
return zsvsheet_status_ignore;
}
no_input:
Expand Down Expand Up @@ -462,16 +472,17 @@ static zsvsheet_status zsvsheet_help_handler(struct zsvsheet_proc_context *ctx)
struct zsvsheet_sheet_context *state = (struct zsvsheet_sheet_context *)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_opts zsvsheet_opts = {0};
zsvsheet_opts.hide_row_nums = 1;
struct zsvsheet_ui_buffer_opts uibopts = {
.zsvsheet_opts = &zsvsheet_opts,
.buff_opts = &bopts,
.filename = NULL,
.data_filename = NULL,
.no_rownum_col_offset = 1,
.write_after_open = 0,
};
struct zsvsheet_ui_buffer *uib = NULL;
Expand Down Expand Up @@ -538,6 +549,7 @@ static zsvsheet_status zsvsheet_help_handler(struct zsvsheet_proc_context *ctx)
return stat;
}

#include "sheet/pivot.c"
#include "sheet/newline_handler.c"

/* We do most procedures in one handler. More complex procedures can be
Expand Down Expand Up @@ -624,7 +636,9 @@ struct builtin_proc_desc {
{ zsvsheet_builtin_proc_filter, "filter", "Hide rows that do not contain the specified text", zsvsheet_filter_handler },
{ zsvsheet_builtin_proc_subcommand, "subcommand", "Editor subcommand", zsvsheet_subcommand_handler },
{ zsvsheet_builtin_proc_help, "help", "Display a list of actions and key-bindings", zsvsheet_help_handler },
{ zsvsheet_builtin_proc_newline, "<Enter>","Follow hyperlink (if any)", zsvsheet_newline_handler },
{ zsvsheet_builtin_proc_newline, "<Enter>","Follow hyperlink (if any) or drill down", zsvsheet_newline_handler },
{ zsvsheet_builtin_proc_pivot_cur_col, "pivotcur","Group rows by the column under the cursor", zsvsheet_pivot_handler },
{ zsvsheet_builtin_proc_pivot_expr, "pivotexpr","Group rows with group-by SQL expression", zsvsheet_pivot_handler },
{ -1, NULL, NULL, NULL }
};
/* clang-format on */
Expand All @@ -642,7 +656,7 @@ static void zsvsheet_check_buffer_worker_updates(struct zsvsheet_ui_buffer *ub,
struct zsvsheet_sheet_context *handler_state) {
pthread_mutex_lock(&ub->mutex);
if (ub->status)
zsvsheet_priv_set_status(display_dims, 1, ub->status);
zsvsheet_priv_set_status(display_dims, ZSVSHEET_STATUS_LOW_PRIO, ub->status);
if (ub->index_ready && ub->dimensions.row_count != ub->index->row_count + 1) {
ub->dimensions.row_count = ub->index->row_count + 1;
handler_state->display_info.update_buffer = true;
Expand Down Expand Up @@ -738,7 +752,7 @@ int ZSV_MAIN_FUNC(ZSV_COMMAND)(int argc, const char *argv[], struct zsv_opts *op
ch = getch();

handler_state.display_info.update_buffer = false;
zsvsheet_priv_set_status(&display_dims, 1, "");
zsvsheet_priv_set_status(&display_dims, ZSVSHEET_STATUS_LOW_PRIO, "");

if (ch != ERR) {
status = zsvsheet_key_press(ch, &handler_state);
Expand All @@ -753,9 +767,12 @@ int ZSV_MAIN_FUNC(ZSV_COMMAND)(int argc, const char *argv[], struct zsv_opts *op

if (handler_state.display_info.update_buffer && zsvsheet_buffer_data_filename(ub)) {
struct zsvsheet_opts zsvsheet_opts = {0};
if (read_data(&ub, NULL, current_ui_buffer->input_offset.row, current_ui_buffer->input_offset.col, header_span,
&zsvsheet_opts, custom_prop_handler)) {
zsvsheet_priv_set_status(&display_dims, 1, "Unexpected error!"); // to do: better error message
struct zsvsheet_ui_buffer_opts uibopts = {0};
uibopts.zsvsheet_opts = &zsvsheet_opts;
if (read_data(&ub, &uibopts, current_ui_buffer->input_offset.row, current_ui_buffer->input_offset.col,
header_span, custom_prop_handler)) {
zsvsheet_priv_set_status(&display_dims, ZSVSHEET_STATUS_HIGH_PRIO,
"Unexpected error!"); // to do: better error message
continue;
}
}
Expand Down
21 changes: 20 additions & 1 deletion app/sheet/file.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#include <zsv/ext/sheet.h>

int zsvsheet_ui_buffer_open_file_opts(struct zsvsheet_ui_buffer_opts *uibopts,
struct zsv_prop_handler *custom_prop_handler,
struct zsvsheet_ui_buffer **ui_buffer_stack_bottom,
Expand All @@ -6,11 +8,14 @@ int zsvsheet_ui_buffer_open_file_opts(struct zsvsheet_ui_buffer_opts *uibopts,
struct zsvsheet_screen_buffer_opts bopts = {0};
struct zsvsheet_ui_buffer *tmp_ui_buffer = NULL;

if (!uibopts->zsvsheet_opts)
uibopts->zsvsheet_opts = &zsvsheet_opts;

if (!uibopts->buff_opts)
uibopts->buff_opts = &bopts;

int err = 0;
if ((err = read_data(&tmp_ui_buffer, uibopts, 0, 0, 0, &zsvsheet_opts, custom_prop_handler)) != 0 || !tmp_ui_buffer ||
if ((err = read_data(&tmp_ui_buffer, uibopts, 0, 0, 0, custom_prop_handler)) != 0 || !tmp_ui_buffer ||
!tmp_ui_buffer->buff_used_rows) {
zsvsheet_ui_buffer_delete(tmp_ui_buffer);
if (err)
Expand Down Expand Up @@ -62,3 +67,17 @@ zsvsheet_status zsvsheet_open_file_opts(struct zsvsheet_proc_context *ctx, struc
return zsvsheet_status_error;
return zsvsheet_status_ok;
}

zsvsheet_status zsvsheet_ext_open_file_opts(struct zsvsheet_proc_context *ctx, struct zsvsheet_open_file_opts *opts) {
struct zsvsheet_ui_buffer_opts uibopts = {0};
struct zsvsheet_opts zsvsheet_opts = {0};

zsvsheet_opts.hide_row_nums = opts->no_auto_row_num;
uibopts.zsvsheet_opts = &zsvsheet_opts;
uibopts.filename = opts->filename;
uibopts.data_filename = opts->data_filename;
if (opts->zsv_opts)
uibopts.zsv_opts = *opts->zsv_opts;

return zsvsheet_open_file_opts(ctx, &uibopts);
}
1 change: 1 addition & 0 deletions app/sheet/file.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ int zsvsheet_ui_buffer_open_file(const char *filename, const struct zsv_opts *zs
struct zsvsheet_ui_buffer **ui_buffer_stack_top);

zsvsheet_status zsvsheet_open_file_opts(struct zsvsheet_proc_context *ctx, struct zsvsheet_ui_buffer_opts *opts);
zsvsheet_status zsvsheet_open_file(struct zsvsheet_proc_context *ctx, const char *filepath, struct zsv_opts *zopts);

#endif
5 changes: 3 additions & 2 deletions app/sheet/handlers.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ zsvsheet_status zsvsheet_set_status(struct zsvsheet_proc_context *ctx, const cha
va_end(argv);
// note: if (n < (int)sizeof(zsvsheet_status_text)), then we just ignore
zsvsheet_display_status_text(state->display_info.dimensions);
zsvsheet_status_priority = ZSVSHEET_STATUS_HIGH_PRIO;
return zsvsheet_status_ok;
}

Expand Down Expand Up @@ -112,7 +113,7 @@ zsvsheet_status zsvsheet_buffer_get_selected_cell(zsvsheet_buffer_t h, struct zs
if (!uib)
return zsvsheet_status_error;
rc->row = uib->cursor_row + uib->input_offset.row + uib->buff_offset.row;
rc->col = uib->cursor_col + uib->input_offset.col + uib->buff_offset.row;
rc->col = uib->cursor_col + uib->input_offset.col + uib->buff_offset.col;
return zsvsheet_status_ok;
}

Expand Down Expand Up @@ -191,7 +192,7 @@ struct zsvsheet_buffer_data zsvsheet_buffer_info(zsvsheet_buffer_t h) {
struct zsvsheet_buffer_data d = {0};
struct zsvsheet_ui_buffer *b = h;
if (b) {
d.has_row_num = b->has_row_num;
d.has_row_num = b->no_add_row_num;
}
return d;
}
Expand Down
7 changes: 7 additions & 0 deletions app/sheet/handlers_internal.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#ifndef ZSVSHEET_HANDLER_INTERNAL_H
#define ZSVSHEET_HANDLER_INTERNAL_H

#include <zsv/ext.h>

struct zsvsheet_context {
const char *subcommand_value; // e.g. "/path/to/myfile.csv"
int ch; // key press value from getch()
Expand Down Expand Up @@ -34,6 +36,11 @@ zsvsheet_status zsvsheet_open_file(struct zsvsheet_proc_context *ctx, const char

/** extension support **/

/**
* Open a tabular file with external facing options
*/
zsvsheet_status zsvsheet_ext_open_file_opts(struct zsvsheet_proc_context *ctx, struct zsvsheet_open_file_opts *opts);

/**
* Set the subcommand prompt
*/
Expand Down
2 changes: 2 additions & 0 deletions app/sheet/key-bindings.c
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@ struct zsvsheet_key_binding zsvsheet_vim_key_bindings[] = {
{ .ch = '?', .proc_id = zsvsheet_builtin_proc_help, },
{ .ch = '\n', .proc_id = zsvsheet_builtin_proc_newline, },
{ .ch = '\r', .proc_id = zsvsheet_builtin_proc_newline, },
{ .ch = 'v', .proc_id = zsvsheet_builtin_proc_pivot_cur_col, },
{ .ch = 'V', .proc_id = zsvsheet_builtin_proc_pivot_expr, },

{ .ch = -1 }
};
Expand Down
Loading
Loading