aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarc André Tanner <mat@brain-dump.org>2015-07-10 17:56:28 +0200
committerMarc André Tanner <mat@brain-dump.org>2015-07-10 18:03:24 +0200
commita657e8499ab04bf8b9b4ffcb954acee574d239a1 (patch)
tree7aa7af07677a5b24955230e9da2d708b8abb1ec3
parent29b718e1d2b94f95281911c80ec5460cbd7fc526 (diff)
downloadvis-a657e8499ab04bf8b9b4ffcb954acee574d239a1.tar.gz
vis-a657e8499ab04bf8b9b4ffcb954acee574d239a1.tar.xz
vis: add a file open dialog
For this to work make sure you have vis-open and slmenu or dmenu somewhere in $PATH. For now the file dialog is shown for :open, :split and :vsplit when the argument is either . (a dot) or looks like a file pattern. For example :open *.[ch] will show a listing of all C source and header files in the current directory. Use a fuzzy search to make your choice.
-rw-r--r--Makefile2
-rwxr-xr-xvis-open47
-rw-r--r--vis.c77
3 files changed, 107 insertions, 19 deletions
diff --git a/Makefile b/Makefile
index 31dd931..e2fcf38 100644
--- a/Makefile
+++ b/Makefile
@@ -34,6 +34,8 @@ install: vis
@mkdir -p ${DESTDIR}${PREFIX}/bin
@cp -f vis ${DESTDIR}${PREFIX}/bin
@chmod 755 ${DESTDIR}${PREFIX}/bin/vis
+ @cp -f vis-open ${DESTDIR}${PREFIX}/bin
+ @chmod 755 ${DESTDIR}${PREFIX}/bin/vis-open
@echo installing manual page to ${DESTDIR}${MANPREFIX}/man1
@mkdir -p ${DESTDIR}${MANPREFIX}/man1
@sed "s/VERSION/${VERSION}/g" < vis.1 > ${DESTDIR}${MANPREFIX}/man1/vis.1
diff --git a/vis-open b/vis-open
new file mode 100755
index 0000000..0bcdd5c
--- /dev/null
+++ b/vis-open
@@ -0,0 +1,47 @@
+#!/bin/sh
+
+PATTERN="."
+[ -z "$VIS_MENU" ] && VIS_MENU="slmenu"
+[ -z "$VIS_MENU_ARGS" ] && VIS_MENU_ARGS="-b"
+
+while [ $# -gt 0 ]; do
+ case "$1" in
+ -h|--help)
+ echo "usage: $0 [-h] [-p prompt] [file-pattern]"
+ exit 0;
+ ;;
+ -p)
+ VIS_MENU_ARGS="$VIS_MENU_ARGS -p $2"
+ shift
+ shift
+ ;;
+ *)
+ PATTERN=$*
+ break
+ ;;
+ esac
+done
+
+if ! type "$VIS_MENU" >/dev/null 2>&1; then
+ if [ ! -z "$DISPLAY" ] && type "dmenu" >/dev/null 2>&1; then
+ VIS_MENU="dmenu"
+ else
+ echo "Neither slmenu nor dmenu found"
+ exit 1
+ fi
+fi
+
+DIR=""
+
+while true; do
+ SEL=$({ echo ..; ls -1 $PATTERN; } | $VIS_MENU $VIS_MENU_ARGS)
+ [ -z "$SEL" ] && exit 1
+ [ ! -z "$DIR" ] && SEL="$DIR/$SEL"
+ if [ -d "$SEL" ]; then
+ DIR="$SEL"
+ PATTERN="$DIR"
+ else
+ echo "$SEL"
+ exit 0
+ fi
+done
diff --git a/vis.c b/vis.c
index 753ddf6..275d48e 100644
--- a/vis.c
+++ b/vis.c
@@ -1502,13 +1502,58 @@ static bool cmd_set(Filerange *range, enum CmdOpt cmdopt, const char *argv[]) {
return true;
}
-static bool cmd_open(Filerange *range, enum CmdOpt opt, const char *argv[]) {
- if (!argv[1])
- return vis_window_new(NULL);
- for (const char **file = &argv[1]; *file; file++) {
- if (!vis_window_new(*file)) {
- errno = 0;
- editor_info_show(vis, "Can't open `%s' %s", *file,
+static bool is_file_pattern(const char *pattern) {
+ return strcmp(pattern, ".") == 0 || strchr(pattern, '*') ||
+ strchr(pattern, '[') || strchr(pattern, '{');
+}
+
+static const char *file_open_dialog(const char *pattern) {
+ if (!is_file_pattern(pattern))
+ return pattern;
+ /* this is a bit of a hack, we temporarily replace the text/view of the active
+ * window such that we can use cmd_filter as is */
+ char vis_open[512];
+ static char filename[PATH_MAX];
+ Filerange range = text_range_empty();
+ Win *win = vis->win;
+ File *file = win->file;
+ Text *txt_orig = file->text;
+ View *view_orig = win->view;
+ Text *txt = text_load(NULL);
+ View *view = view_new(txt, NULL);
+ filename[0] = '\0';
+ snprintf(vis_open, sizeof(filename)-1, "vis-open %s", pattern ? pattern : "");
+
+ if (!txt || !view)
+ goto out;
+ win->view = view;
+ file->text = txt;
+
+ if (cmd_filter(&range, CMD_OPT_NONE, (const char *[]){ "open", vis_open, NULL })) {
+ size_t len = text_size(txt);
+ if (len >= sizeof(filename))
+ len = 0;
+ if (len > 0)
+ text_bytes_get(txt, 0, len-1, filename);
+ filename[len] = '\0';
+ }
+
+out:
+ view_free(view);
+ text_free(txt);
+ win->view = view_orig;
+ file->text = txt_orig;
+ return filename[0] ? filename : NULL;
+}
+
+static bool openfiles(const char **files) {
+ for (; *files; files++) {
+ const char *file = file_open_dialog(*files);
+ if (!file)
+ continue;
+ errno = 0;
+ if (!vis_window_new(file)) {
+ editor_info_show(vis, "Could not open `%s' %s", file,
errno ? strerror(errno) : "");
return false;
}
@@ -1516,6 +1561,12 @@ static bool cmd_open(Filerange *range, enum CmdOpt opt, const char *argv[]) {
return true;
}
+static bool cmd_open(Filerange *range, enum CmdOpt opt, const char *argv[]) {
+ if (!argv[1])
+ return vis_window_new(NULL);
+ return openfiles(&argv[1]);
+}
+
static bool is_view_closeable(Win *win) {
if (!text_modified(win->file->text))
return true;
@@ -1620,18 +1671,6 @@ static bool cmd_substitute(Filerange *range, enum CmdOpt opt, const char *argv[]
return cmd_filter(range, opt, (const char*[]){ argv[0], "sed", pattern, NULL});
}
-static bool openfiles(const char **files) {
- for (; *files; files++) {
- errno = 0;
- if (!vis_window_new(*files)) {
- editor_info_show(vis, "Could not open `%s' %s", *files,
- errno ? strerror(errno) : "");
- return false;
- }
- }
- return true;
-}
-
static bool cmd_split(Filerange *range, enum CmdOpt opt, const char *argv[]) {
editor_windows_arrange(vis, UI_LAYOUT_HORIZONTAL);
if (!argv[1])