--- config.def.h | 1 + main.c | 114 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ vis.c | 6 +++- vis.h | 1 + 4 files changed, 121 insertions(+), 1 deletion(-) diff --git a/config.def.h b/config.def.h index 8699136..9e52102 100644 --- a/config.def.h +++ b/config.def.h _AT_@ -319,6 +319,7 @@ static const KeyBinding bindings_insert[] = { { "<C-d>", ALIAS("<Escape><<i") }, { "<C-i>", ALIAS("<Tab>") }, { "<C-j>", ALIAS("<Enter>") }, + { "<C-n>", ACTION(FILE_TEXT_AUTOCOMPLETE) }, { "<C-m>", ALIAS("<Enter>") }, { "<C-o>", ACTION(MODE_OPERATOR_PENDING) }, { "<C-r>", ACTION(INSERT_REGISTER) }, diff --git a/main.c b/main.c index d0c0c73..9e224d1 100644 --- a/main.c +++ b/main.c _AT_@ -125,6 +125,12 @@ static const char *percent(Vis*, const char *keys, const Arg *arg); static const char *number_increment_decrement(Vis*, const char *keys, const Arg *arg); /* open a filename under cursor in same (!arg->b) or new (arg->b) window */ static const char *open_file_under_cursor(Vis*, const char *keys, const Arg *arg); +/* Insert text chosen in external file dialog at cursor position(s) */ +static void insert_dialog_selection(Vis*, const char *cmdline, ...); +/* Get output of external command */ +static char *get_output_of_external_command(Vis*, const char *argv[]); +/* Autocomplete input text at cursor based on the words in the current file */ +static const char *autocomplete_file_text(Vis*, const char *keys, const Arg *arg); enum { VIS_ACTION_EDITOR_SUSPEND, _AT_@ -307,6 +313,7 @@ enum { VIS_ACTION_NUMBER_DECREMENT, VIS_ACTION_OPEN_FILE_UNDER_CURSOR, VIS_ACTION_OPEN_FILE_UNDER_CURSOR_NEW_WINDOW, + VIS_ACTION_FILE_TEXT_AUTOCOMPLETE, VIS_ACTION_NOP, }; _AT_@ -1211,6 +1218,11 @@ static const KeyAction vis_action[] = { "Open file under the cursor in a new window", open_file_under_cursor, { .b = true } }, + [VIS_ACTION_FILE_TEXT_AUTOCOMPLETE] = { + "autocomplete-file-text", + "Autocomplete text in file", + autocomplete_file_text, + }, [VIS_ACTION_NOP] = { "nop", "Ignore key, do nothing", _AT_@ -2093,6 +2105,108 @@ static const char *open_file_under_cursor(Vis *vis, const char *keys, const Arg return keys; } +ssize_t read_buffer(void *context, char *data, size_t len) { + buffer_append(context, data, len); + return len; +} + +static char *get_output_of_external_command(Vis *vis, const char *argv[]) { + char *out = NULL; + Buffer bufout, buferr; + buffer_init(&bufout); + buffer_init(&buferr); + + Filerange empty = text_range_empty(); + int status = vis_pipe(vis, &empty, argv, &bufout, read_buffer, + &buferr, read_buffer); + + if (status != 0) { + vis_info_show(vis, "Command failed %s", buffer_content0(&buferr)); + } else { + out = malloc(bufout.len); + strncpy(out, buffer_content0(&bufout), buffer_length0(&bufout)); + out[buffer_length0(&bufout)] = '\0'; + } + + buffer_release(&bufout); + buffer_release(&buferr); + return out; +} + +// Caller has to free the allocated memory for the prefix +static char *get_prefix_for_autocomplete(Vis *vis) { + View *view = vis_view(vis); + Cursor *c = view_cursors(view); + Text *txt = vis_text(vis); + + Filerange r = text_object_word(txt, view_cursors_pos(c)-1); + if (!text_range_valid(&r)) + return NULL; + + char *prefix = text_bytes_alloc0(txt, r.start, text_range_size(&r)); + char *check; + for (check = prefix; *check; check++) { + if (!isspace(*check)) + break; + } + if (*check == '\0') { + vis_info_show(vis, "Autocompletion without prefix input is not valid."); + free(prefix); + return NULL; + } + + return prefix; +} + +static const char *autocomplete_file_text(Vis *vis, const char *keys, const Arg *arg) { + Win *win = vis_window(vis); + const char *fn = vis_window_filename(win); + + char *prefix = get_prefix_for_autocomplete(vis); + if (!prefix) + return keys; + + // TODO: get menu/dialog program to use from config? + insert_dialog_selection(vis, "cat '%s' | tr \" ;:$<>#?{}()[],.'\" '\n' | grep \"^%s\" | sort | uniq | dmenu | tr -d '\n' | sed \"s/%s//\"", fn, prefix, prefix); + + free(prefix); + return keys; +} + +static void insert_dialog_selection(Vis *vis, const char *cmdline, ...) { + View *view = vis_view(vis); + Cursor *c = view_cursors(view); + + va_list ap; + va_start(ap, cmdline); + + char* cmd = malloc(4096); + size_t ret = vsnprintf(cmd, 4096, cmdline, ap); + if (ret == 4096) { + vis_info_show(vis, "Command line too long."); + return; + } + va_end(ap); + + char *outtext = get_output_of_external_command(vis, (const char*[]){cmd, NULL}); + if (outtext == NULL) { + vis_info_show(vis, "Autocompletion command failed. Command was %s", cmd); + free(cmd); + return; + } + size_t len = strlen(outtext); + + for (; c; c = view_cursors_next(c)) { + size_t pos = view_cursors_pos(c); + vis_insert(vis, pos, outtext, len); + view_cursors_scroll_to(c, pos + len); + } + + free(outtext); + free(cmd); + return; +} + static Vis *vis; static void signal_handler(int signum, siginfo_t *siginfo, void *context) { diff --git a/vis.c b/vis.c index 3f04c87..09f854f 100644 --- a/vis.c +++ b/vis.c _AT_@ -1360,4 +1360,8 @@ Win *vis_window(Vis *vis) { bool vis_get_autoindent(const Vis *vis) { return vis->autoindent; -} \ No newline at end of file +} + +const char *vis_window_filename(Win *win) { + return win->file->name; +} diff --git a/vis.h b/vis.h index ab7eade..2878f09 100644 --- a/vis.h +++ b/vis.h _AT_@ -447,6 +447,7 @@ Regex *vis_regex(Vis*, const char *pattern); Text *vis_text(Vis*); View *vis_view(Vis*); Win *vis_window(Vis*); +const char *vis_window_filename(Win*); bool vis_theme_load(Vis*, const char *name); -- 2.8.2Received on Mon May 16 2016 - 13:00:58 CEST
This archive was generated by hypermail 2.3.0 : Mon May 16 2016 - 13:12:16 CEST