[hackers][vis][PATCH] Implement a first version of the 'gf' family of commands

From: Silvan Jegen <s.jegen_AT_gmail.com>
Date: Mon, 1 Feb 2016 21:55:32 +0100

There are still a lot of rough edges. We don't change the jumplist for
example, which means that we won't be able to jump back to the old
file. We also don't check the file paths we want to open. If a path
element of a file we open with one of the implemented commands does not
exist before using the command we won't be able to save it to that path.
---
I like these commands but I assume not everybody does. Before spending
more time on this implementation I thought I would gather some feedback.
 config.def.h   |  2 ++
 main.c         | 12 ++++++++++++
 text-motions.c | 13 +++++++++++++
 text-motions.h |  9 +++++++++
 vis.c          | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 vis.h          |  4 ++++
 6 files changed, 93 insertions(+)
diff --git a/config.def.h b/config.def.h
index 47ed326..af4b2b2 100644
--- a/config.def.h
+++ b/config.def.h
_AT_@ -227,6 +227,8 @@ static const KeyBinding bindings_normal[] = {
 	{ "m",                  ACTION(MARK_SET)                            },
 	{ "<F1>",               ALIAS(":help<Enter>")                       },
 	{ "ga",                 ACTION(UNICODE_INFO)                        },
+	{ "gf",                 ACTION(OPEN_FILE_UNDER_CURSOR)              },
+	{ "<C-w>gf",            ACTION(OPEN_FILE_UNDER_CURSOR_NEW_WINDOW)   },
 	{ "p",                  ACTION(PUT_AFTER)                           },
 	{ "P",                  ACTION(PUT_BEFORE)                          },
 	{ "gp",                 ACTION(PUT_AFTER_END)                       },
diff --git a/main.c b/main.c
index 918f4c8..2404522 100644
--- a/main.c
+++ b/main.c
_AT_@ -213,6 +213,8 @@ enum {
 	VIS_ACTION_INSERT_LINE_START,
 	VIS_ACTION_OPEN_LINE_ABOVE,
 	VIS_ACTION_OPEN_LINE_BELOW,
+	VIS_ACTION_OPEN_FILE_UNDER_CURSOR,
+	VIS_ACTION_OPEN_FILE_UNDER_CURSOR_NEW_WINDOW,
 	VIS_ACTION_JOIN_LINE_BELOW,
 	VIS_ACTION_JOIN_LINES,
 	VIS_ACTION_PROMPT_SHOW,
_AT_@ -757,6 +759,16 @@ static KeyAction vis_action[] = {
 		"Focus previous window",
 		call, { .f = vis_window_prev }
 	},
+	[VIS_ACTION_OPEN_FILE_UNDER_CURSOR] = {
+		"open-file-under-cursor",
+		"Open file under the cursor",
+		call, { .f = vis_open_file }
+	},
+	[VIS_ACTION_OPEN_FILE_UNDER_CURSOR_NEW_WINDOW] = {
+		"open-file-under-cursor-new_window",
+		"Open file under the cursor in a new window",
+		call, { .f = vis_open_file_new_window }
+	},
 	[VIS_ACTION_APPEND_CHAR_NEXT] = {
 		"append-char-next",
 		"Append text after the cursor",
diff --git a/text-motions.c b/text-motions.c
index f406da3..2752158 100644
--- a/text-motions.c
+++ b/text-motions.c
_AT_@ -26,6 +26,11 @@ int is_word_boundry(int c) {
 	         ('A' <= c && c <= 'Z') || c == '_');
 }
 
+int is_filename_boundry(int c) {
+	return isspace(c) || (c == ';' ||
+	         c == '"' || c == '\'' );
+}
+
 size_t text_begin(Text *txt, size_t pos) {
 	return 0;
 }
_AT_@ -357,6 +362,14 @@ size_t text_word_start_prev(Text *txt, size_t pos) {
 	return text_customword_start_prev(txt, pos, is_word_boundry);
 }
 
+size_t text_filename_start_prev(Text *txt, size_t pos) {
+	return text_customword_start_prev(txt, pos, is_filename_boundry);
+}
+
+size_t text_filename_end_next(Text *txt, size_t pos) {
+	return text_customword_end_next(txt, pos, is_filename_boundry);
+}
+
 static size_t text_paragraph_sentence_next(Text *txt, size_t pos, bool sentence) {
 	char c;
 	bool content = false, paragraph = false;
diff --git a/text-motions.h b/text-motions.h
index 32e4a1f..67a366b 100644
--- a/text-motions.h
+++ b/text-motions.h
_AT_@ -66,6 +66,15 @@ size_t text_longword_end_next(Text*, size_t pos);
 size_t text_longword_end_prev(Text*, size_t pos);
 size_t text_longword_start_next(Text*, size_t pos);
 size_t text_longword_start_prev(Text*, size_t pos);
+
+/*
+ * Get the beginning and the end of a file name. Every non-punctuation
+ * character except white space is being considered a file name
+ * character. TODO?: handle escaped space characters in filenames.
+ */
+size_t text_filename_start_prev(Text*, size_t pos);
+size_t text_filename_end_next(Text*, size_t pos);
+
 /*
  * A word consists of a sequence of letters, digits and underscores, or a
  * sequence of other non-blank characters, separated with white space.
diff --git a/vis.c b/vis.c
index b65724c..795dd7d 100644
--- a/vis.c
+++ b/vis.c
_AT_@ -335,6 +335,59 @@ err:
 	return NULL;
 }
 
+/* Gets file name under cursor. Freeing the allocated memory is the
+ * caller's responsibility.
+ */
+static char* vis_get_filename_under_cursor(Vis *vis) {
+	Win *win = vis->win;
+	Cursor *c = view_cursors(win->view);
+	size_t pos = view_cursors_pos(c);
+	Text *txt = vis->win->file->text;
+	// We have to put in some pos offsets here because the custom
+	// text motion function will skip over boundaries.
+	size_t startpos = text_filename_start_prev(txt, pos+1);
+	size_t endpos = text_filename_end_next(txt, pos-1);
+	if (startpos == endpos) {
+		vis_info_show(vis, "No filename found under cursor.");
+		return NULL;
+	}
+
+	char *fn = text_bytes_alloc0(txt, startpos, endpos-startpos+1);
+	return fn;
+}
+
+void vis_open_file(Vis *vis) {
+	Win *win = vis->win;
+	if (text_modified(win->file->text)) {
+		vis_info_show(vis, "This window does contains unsaved text so we cannot close it.");
+		return;
+	}
+	Win *oldwin = vis->win;
+
+	char *fn = vis_get_filename_under_cursor(vis);
+	if (!fn)
+		return;
+	if (!vis_window_new(vis, fn)) {
+		vis_info_show(vis, "Could not open `%s' %s", fn,
+									errno ? strerror(errno) : "");
+		free(fn);
+		return;
+	}
+
+	vis_window_close(oldwin);
+	free(fn);
+}
+
+void vis_open_file_new_window(Vis *vis) {
+	char *fn = vis_get_filename_under_cursor(vis);
+	if (!fn)
+		return;
+	if (!vis_window_new(vis, fn))
+		vis_info_show(vis, "Could not open `%s' %s", fn,
+									errno ? strerror(errno) : "");
+	free(fn);
+}
+
 void vis_free(Vis *vis) {
 	if (!vis)
 		return;
diff --git a/vis.h b/vis.h
index 3bcd9c7..d357f0f 100644
--- a/vis.h
+++ b/vis.h
_AT_@ -79,6 +79,10 @@ void vis_window_next(Vis*);
 void vis_window_prev(Vis*);
 /* display a user prompt with a certain title and default text */
 void vis_prompt_show(Vis*, const char *title);
+/* open file under cursor in current window */
+void vis_open_file(Vis *vis);
+/* open file under cursor in new window */
+void vis_open_file_new_window(Vis *vis);
 
 /* display a one line message to the user, will be hidden upon keypress */
 void vis_info_show(Vis*, const char *msg, ...);
-- 
2.7.0
Received on Mon Feb 01 2016 - 21:55:32 CET

This archive was generated by hypermail 2.3.0 : Mon Feb 01 2016 - 22:00:17 CET