Commit Diff


commit - af29d739553d6efe6ff97d2cba50406122a99ff6
commit + b1e1e41a7e4757b4b9e028031431db0e0a304f25
blob - b7f36fd2312f9fcc2535cd7cfc24023380fa91a9
blob + e1a1f074b8ab728c5cb9320c8938c8195a08f358
--- Makefile.am
+++ Makefile.am
@@ -5,6 +5,8 @@ telescope_SOURCES =	cmd.c		\
 			cmd.h		\
 			compat.h	\
 			compat/*.[ch]	\
+			compl.c		\
+			compl.h		\
 			defaults.c	\
 			defaults.h	\
 			fs.c		\
blob - 42c6cf2f7c3255420553a008549209927297e980
blob + 3c700286dfa06f282179020891c070f993c8960e
--- cmd.c
+++ cmd.c
@@ -18,6 +18,7 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include "compl.h"
 #include "defaults.h"
 #include "minibuffer.h"
 #include "telescope.h"
@@ -347,7 +348,7 @@ cmd_execute_extended_command(struct buffer *buffer)
 	}
 
 	enter_minibuffer(eecmd_self_insert, eecmd_select, exit_minibuffer,
-	    &eecmd_history, NULL, NULL);
+	    &eecmd_history, compl_eecmd, NULL);
 
 	len = sizeof(ministate.prompt);
 	strlcpy(ministate.prompt, "", len);
@@ -583,6 +584,8 @@ cmd_mini_delete_char(struct buffer *buffer)
 	n = utf8_next_cp(c);
 
 	memmove(c, n, strlen(n)+1);
+
+	recompute_completions(0);
 }
 
 void
@@ -605,6 +608,8 @@ cmd_mini_delete_backward_char(struct buffer *buffer)
 
 	memmove(p, c, strlen(c)+1);
 	buffer->cpoff--;
+
+	recompute_completions(0);
 }
 
 void
@@ -620,6 +625,8 @@ cmd_mini_kill_line(struct buffer *buffer)
 	minibuffer_taint_hist();
 	c = utf8_nth(buffer->current_line->line, buffer->cpoff);
 	*c = '\0';
+
+	recompute_completions(0);
 }
 
 void
blob - /dev/null
blob + 8d01e57a79a67f027ffbb5055e4b6d1189314455 (mode 644)
--- /dev/null
+++ compl.c
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdlib.h>
+
+#include "compl.h"
+#include "telescope.h"
+
+const char *
+compl_eecmd(void **data)
+{
+	struct cmd	**state = (struct cmd **)data;
+
+	/* first time: init the state */
+	if (*state == NULL)
+		*state = cmds;
+
+	if ((*state)->cmd == NULL)
+		return NULL;
+
+	(*state)++;
+	return (*state)->cmd;
+}
blob - /dev/null
blob + 8bf04bcb4c4660854bfa2ed924c52f23eb9c1c0c (mode 644)
--- /dev/null
+++ compl.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef COMPL_H
+#define COMPL_H
+
+const char	*compl_eecmd(void **);
+
+#endif
blob - 5354c5fd3f762db6e1f280b5d2f04c8d41708314
blob + 00e7a2d4e24e1f49fbd241b3dacfecb83efeb8f1
--- minibuffer.c
+++ minibuffer.c
@@ -42,6 +42,30 @@ struct histhead eecmd_history,
 
 struct ministate ministate;
 
+struct buffer minibufferwin;
+
+/*
+ * Recompute the visible completions.  If add is 1, don't consider the
+ * ones already hidden.
+ */
+void
+recompute_completions(int add)
+{
+	struct line	*l;
+
+	if (in_minibuffer != MB_COMPREAD)
+		return;
+
+	TAILQ_FOREACH(l, &ministate.compl.buffer.page.head, lines) {
+		if (add && l->flags & L_HIDDEN)
+			continue;
+		if (strstr(l->line, ministate.buf) != NULL)
+			l->flags &= ~L_HIDDEN;
+		else
+			l->flags |= L_HIDDEN;
+	}
+}
+
 static void
 minibuffer_hist_save_entry(void)
 {
@@ -97,6 +121,8 @@ minibuffer_self_insert(void)
 	memmove(c + len, c, strlen(c)+1);
 	memcpy(c, tmp, len);
 	ministate.buffer.cpoff++;
+
+	recompute_completions(1);
 }
 
 void
@@ -226,6 +252,51 @@ read_select(void)
         exit_minibuffer();
 	minibuffer_hist_save_entry();
 	read_cb(ministate.buf, read_data);
+}
+
+/*
+ * Just like erase_buffer, but don't call free(l->line);
+ */
+static inline void
+erase_compl_buffer(struct buffer *buffer)
+{
+	struct line *l, *lt;
+
+	empty_vlist(buffer);
+
+	TAILQ_FOREACH_SAFE(l, &buffer->page.head, lines, lt) {
+		TAILQ_REMOVE(&buffer->page.head, l, lines);
+		/* don't free l->line! */
+		free(l);
+	}
+}
+
+/*
+ * TODO: we should collect this asynchronously...
+ */
+static inline void
+populate_compl_buffer(complfn *fn, void *data)
+{
+	const char	*s;
+	struct line	*l;
+	struct buffer	*b;
+	struct parser	*p;
+
+	b = &ministate.compl.buffer;
+	p = &b->page;
+
+	while ((s = fn(&data)) != NULL) {
+		if ((l = calloc(1, sizeof(*l))) == NULL)
+			abort();
+
+		l->type = LINE_TEXT;
+		l->line = s;
+
+		if (TAILQ_EMPTY(&p->head))
+			TAILQ_INSERT_HEAD(&p->head, l, lines);
+		else
+			TAILQ_INSERT_TAIL(&p->head, l, lines);
+	}
 }
 
 void
@@ -233,7 +304,17 @@ enter_minibuffer(void (*self_insert_fn)(void), void (*
     void (*abortfn)(void), struct histhead *hist,
     complfn *complfn, void *compldata)
 {
-	in_minibuffer = 1;
+	in_minibuffer = complfn == NULL ? MB_READ : MB_COMPREAD;
+	if (in_minibuffer == MB_COMPREAD) {
+		ui_schedule_redraw();
+
+		erase_compl_buffer(&ministate.compl.buffer);
+
+		ministate.compl.fn = complfn;
+		ministate.compl.data = compldata;
+		populate_compl_buffer(complfn, compldata);
+	}
+
 	base_map = &minibuffer_map;
 	current_map = &minibuffer_map;
 
@@ -255,6 +336,9 @@ enter_minibuffer(void (*self_insert_fn)(void), void (*
 void
 exit_minibuffer(void)
 {
+	if (in_minibuffer == MB_COMPREAD)
+		ui_schedule_redraw();
+
 	in_minibuffer = 0;
 	base_map = &global_map;
 	current_map = &global_map;
@@ -286,7 +370,7 @@ yornp(const char *prompt, void (*fn)(int, struct tab*)
  */
 void
 completing_read(const char *prompt, void (*fn)(const char *, struct tab *),
-    struct tab *data, complfn *complfn, void *compldata)
+    struct tab *data)
 {
 	size_t len;
 
@@ -296,7 +380,7 @@ completing_read(const char *prompt, void (*fn)(const c
 	read_cb = fn;
 	read_data = data;
 	enter_minibuffer(read_self_insert, read_select, read_abort,
-	    &read_history, complfn, compldata);
+	    &read_history, NULL, NULL);
 
 	len = sizeof(ministate.prompt);
 	strlcpy(ministate.prompt, prompt, len);
blob - e6577cbd7191c8338c24499cb418c43757f399fd
blob + 73e4f9f746397c6856c0ce65b15083e15bd7380c
--- minibuffer.h
+++ minibuffer.h
@@ -23,8 +23,44 @@
 #define MB_READ		1
 #define MB_COMPREAD	2
 
-typedef char *(complfn)(void *);
+/*
+ * Completion provider function.  These functions are called
+ * asynchronously.  The function should compute the next completion
+ * using the given parameter `state' and modify it eventually.  To
+ * signal the end of the completions, complfn should return NULL: the
+ * value of state will then be discarded and the function never called
+ * again.
+ */
+typedef const char *(complfn)(void **);
 
+struct ministate {
+	char		*curmesg;
+
+	char		 prompt[64];
+	void		 (*donefn)(void);
+	void		 (*abortfn)(void);
+
+	char		 buf[1025];
+	struct line	 line;
+	struct vline	 vline;
+	struct buffer	 buffer;
+
+	struct histhead	*history;
+	struct hist	*hist_cur;
+	size_t		 hist_off;
+
+	struct {
+		struct buffer	 buffer;
+		complfn		*fn;
+		void		*data;
+	} compl;
+};
+extern struct ministate ministate;
+
+extern struct buffer minibufferwin;
+
+void	 recompute_completions(int);
+
 void	 enter_minibuffer(void(*)(void), void(*)(void), void(*)(void),
     struct histhead *,
     complfn *, void *);
@@ -36,12 +72,9 @@ void	 yornp(const char *, void (*)(int, struct tab *),
  * completing_read asks the user for something using the minibuffer.
  * The first argument is the string prompt.  The second and third are
  * the callback to call when done and the data; the callback function
- * can't be NULL.  The last two arguments are the completion function
- * and its data; if not given, no completion will be shown.  The
- * function providing the completion will be called asynchronously.
+ * can't be NULL.
  */
 void	 completing_read(const char *,
-    void (*)(const char *, struct tab *), struct tab *,
-    complfn *, void *);
+    void (*)(const char *, struct tab *), struct tab *);
 
 #endif
blob - f06eeb66d293166c1ef048fc6445686a3dcaa295
blob + 540de79098f2017095d3d728b08ede7f01a69e79
--- ui.c
+++ ui.c
@@ -66,6 +66,7 @@ static void		 redraw_modeline(struct tab*);
 static void		 redraw_minibuffer(void);
 static void		 do_redraw_echoarea(void);
 static void		 do_redraw_minibuffer(void);
+static void		 do_redraw_minibuffer_compl(void);
 static void		 place_cursor(int);
 static void		 redraw_tab(struct tab*);
 static void		 emit_help_item(char*, void*);
@@ -331,6 +332,14 @@ handle_resize_nodelay(int s, short ev, void *d)
 
 	/* move and resize the windows, in reverse order! */
 
+	if (in_minibuffer == MB_COMPREAD) {
+		mvwin(minibuffer, lines-10, 0);
+		wresize(minibuffer, 10, COLS);
+		lines -= 10;
+
+		wrap_page(&ministate.compl.buffer, COLS);
+	}
+
 	mvwin(echoarea, --lines, 0);
 	wresize(echoarea, 1, COLS);
 
@@ -597,6 +606,9 @@ adjust_line(struct vline *vl, struct buffer *buffer)
 {
 	struct vline *t;
 
+	if (vl == NULL)
+		return NULL;
+
 	if (!(vl->parent->flags & L_HIDDEN))
 		return vl;
 
@@ -641,6 +653,9 @@ again:
 	if (TAILQ_EMPTY(&buffer->head))
 		goto end;
 
+	if (buffer->top_line == NULL)
+		buffer->top_line = TAILQ_FIRST(&buffer->head);
+
 	buffer->top_line = adjust_line(buffer->top_line, buffer);
 	if (buffer->top_line == NULL)
 		goto end;
@@ -773,6 +788,9 @@ redraw_minibuffer(void)
 	else
 		do_redraw_echoarea();
 
+	if (in_minibuffer == MB_COMPREAD)
+		do_redraw_minibuffer_compl();
+
 	wattr_off(echoarea, minibuffer_face.background, NULL);
 }
 
@@ -826,6 +844,13 @@ do_redraw_minibuffer(void)
 		wprintw(echoarea, " [%s]", ministate.curmesg);
 
 	wmove(echoarea, 0, off_x + utf8_swidth_between(start, c));
+}
+
+static void
+do_redraw_minibuffer_compl(void)
+{
+	redraw_window(minibuffer, 10, body_cols,
+	    &ministate.compl.buffer);
 }
 
 /*
@@ -871,6 +896,9 @@ redraw_tab(struct tab *tab)
 	wnoutrefresh(tabline);
 	wnoutrefresh(modeline);
 
+	if (in_minibuffer == MB_COMPREAD)
+		wnoutrefresh(minibuffer);
+
 	place_cursor(1);
 
 	doupdate();
@@ -1196,7 +1224,13 @@ ui_toggle_side_window(void)
 void
 ui_schedule_redraw(void)
 {
-	handle_resize_nodelay(0, 0, NULL);
+	struct timeval tv = {0, 0};
+
+	if (event_pending(&resizeev, EV_TIMEOUT, NULL))
+		event_del(&resizeev);
+
+	evtimer_set(&resizeev, handle_resize_nodelay, NULL);
+	evtimer_add(&resizeev, &tv);
 }
 
 void
@@ -1224,7 +1258,7 @@ void
 ui_read(const char *prompt, void (*fn)(const char*, struct tab *),
     struct tab *data)
 {
-	completing_read(prompt, fn, data, NULL, NULL);
+	completing_read(prompt, fn, data);
 	redraw_tab(current_tab());
 }
 
blob - f88c1530477be74f4bd8856b0c55dd1381795c9d
blob + 909ba81917a545d6a5e0a29aaad82c3b3ee8f1f8
--- ui.h
+++ ui.h
@@ -89,24 +89,6 @@ extern struct histhead eecmd_history,
 	lu_history,
 	read_history;
 
-struct ministate {
-	char		*curmesg;
-
-	char		 prompt[64];
-	void		 (*donefn)(void);
-	void		 (*abortfn)(void);
-
-	char		 buf[1025];
-	struct line	 line;
-	struct vline	 vline;
-	struct buffer	 buffer;
-
-	struct histhead	*history;
-	struct hist	*hist_cur;
-	size_t		 hist_off;
-};
-extern struct ministate ministate;
-
 void		 save_excursion(struct excursion *, struct buffer *);
 void		 restore_excursion(struct excursion *, struct buffer *);
 void		 global_key_unbound(void);