[hackers][vis][RFC][PATCH 1/2 v2] Add autocompletion for current file contents

From: Silvan Jegen <s.jegen_AT_gmail.com>
Date: Tue, 17 May 2016 21:30:43 +0200

We add some infrastructure in order to run a shell command and get its
output which we then can insert into vis.

This infrastructure we use to execute a shell command which sends all
unique words of the current file to dmenu. The word selected in dmenu
is then inserted into vis at all cursor positions.
---
v2 changes:
	- use vis-menu
	- pipe current file content instead of content of file saved to disk
	- use primary cursor (obtained with view_cursors_primary_get)
 config.def.h |   1 +
 main.c       | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vis.c        |   6 +++-
 vis.h        |   1 +
 4 files changed, 122 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..34cd81d 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*, Filerange range, const char *cmdline, ...);
+/* Get output of external command */
+static char *get_output_of_external_command(Vis*, Filerange range, 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,109 @@ 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, Filerange range, const char *argv[]) {
+	char *out = NULL;
+	Buffer bufout, buferr;
+	buffer_init(&bufout);
+	buffer_init(&buferr);
+
+	if (!text_range_valid(&range))
+		range = text_range_empty();
+
+	int status = vis_pipe(vis, &range, 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) {
+	Text *txt = vis_text(vis);
+	size_t txtsize = text_size(txt);
+
+	char *prefix = get_prefix_for_autocomplete(vis);
+	if (!prefix)
+		return keys;
+
+	insert_dialog_selection(vis, text_range_new(0, txtsize), "tr \" ;:$<>#?{}()[],.'\" '\n' | grep \"^%s\" | sort | uniq | vis-menu | tr -d '\n' | sed \"s/%s//\"", prefix, prefix);
+
+	free(prefix);
+	return keys;
+}
+
+static void insert_dialog_selection(Vis *vis, Filerange range, const char *cmdline, ...) {
+	View *view = vis_view(vis);
+	Cursor *c = view_cursors_primary_get(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, range, (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.2
Received on Tue May 17 2016 - 21:30:43 CEST

This archive was generated by hypermail 2.3.0 : Tue May 17 2016 - 21:36:14 CEST