commit d68d0f39ab7675745e7d177f6774736f7ec58783
parent 02141177f8055a2d0edace481d631adbcb4b4c47
Author: Louis Burda <quent.burda@gmail.com>
Date: Wed, 1 Dec 2021 00:31:01 +0100
Replace readline with own implementation for integration
Diffstat:
M | .gitignore | | | 1 | + |
M | Makefile | | | 7 | +++++-- |
A | history.c | | | 184 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | history.h | | | 40 | ++++++++++++++++++++++++++++++++++++++++ |
A | link.c | | | 54 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | link.h | | | 23 | +++++++++++++++++++++++ |
D | main | | | 0 | |
M | main.c | | | 319 | +++++++++++++++++++++++++++++++++---------------------------------------------- |
A | util.c | | | 43 | +++++++++++++++++++++++++++++++++++++++++++ |
A | util.h | | | 12 | ++++++++++++ |
10 files changed, 495 insertions(+), 188 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -1,2 +1,3 @@
vgcore*
main
+*.o
diff --git a/Makefile b/Makefile
@@ -8,5 +8,8 @@ all: main
clean:
rm main
-main: main.c
- $(CC) -o $@ $< $(CFLAGS) $(LDLIBS)
+%.o: %.c %.h
+ $(CC) -c -o $@ $< $(CFLAGS) $(LDLIBS)
+
+main: main.c util.o history.o link.o
+ $(CC) -o $@ $^ $(CFLAGS) $(LDLIBS)
diff --git a/history.c b/history.c
@@ -0,0 +1,184 @@
+#include "history.h"
+#include "link.h"
+#include "util.h"
+
+#include <string.h>
+#include <wchar.h>
+
+
+void
+history_init(struct history *history)
+{
+ history->list = LIST_HEAD;
+ history->query = inputln_init();
+ history->cmd = history->query;
+}
+
+void
+history_submit(struct history *history)
+{
+ /* if chose from history free query */
+ if (history->cmd != history->query) {
+ history_pop(history->query);
+ inputln_free(history->query);
+ }
+
+ /* pop first in case already in history */
+ history_pop(history->cmd);
+ history_add(history, history->cmd);
+
+ /* create new input buf and add to hist */
+ history->query = inputln_init();
+ history->cmd = history->query;
+ history_add(history, history->cmd);
+}
+
+void
+history_free(struct history *history)
+{
+ struct link *iter, *next;
+ struct inputln *ln;
+
+ for (iter = &history->list; iter; iter = next) {
+ next = iter->next;
+ ln = UPCAST(iter, struct inputln);
+ free(ln);
+ }
+ free(history->cmd);
+}
+
+struct inputln *
+history_list_prev(struct inputln *cur, const wchar_t *search)
+{
+ struct link *iter;
+ struct inputln *ln;
+
+ for (iter = cur->link.prev; iter && iter->prev; iter = iter->prev) {
+ ln = UPCAST(iter, struct inputln);
+ if (!search || !*search || wcsstr(ln->buf, search))
+ return ln;
+ }
+
+ return NULL;
+}
+
+struct inputln *
+history_list_next(struct inputln *cur, const wchar_t *search)
+{
+ struct link *iter;
+ struct inputln *ln;
+
+ for (iter = cur->link.next; iter; iter = iter->next) {
+ ln = UPCAST(iter, struct inputln);
+ if (!search || !*search || wcsstr(ln->buf, search))
+ return ln;
+ }
+
+ return cur;
+}
+
+void
+history_prev(struct history *history)
+{
+ history->cmd = history_list_prev(history->cmd, history->query->buf);
+ if (!history->cmd) history->cmd = history->query;
+}
+
+void
+history_next(struct history *history)
+{
+ history->cmd = history_list_next(history->cmd, history->query->buf);
+}
+
+void
+history_pop(struct inputln *line)
+{
+ link_pop(&line->link);
+}
+
+void
+history_add(struct history *history, struct inputln *line)
+{
+ struct inputln *ln;
+ struct link *back;
+
+ if (list_len(&history->list) == HISTORY_MAX) {
+ back = list_back(&history->list);
+ back->prev->next = NULL;
+ ln = UPCAST(back, struct inputln);
+ inputln_free(ln);
+ }
+
+ link_append(&history->list, &line->link);
+}
+
+struct inputln *
+inputln_init(void)
+{
+ struct inputln *ln;
+
+ ln = malloc(sizeof(struct inputln));
+ ASSERT(ln != NULL);
+ ln->cap = 128;
+ ln->buf = malloc(ln->cap * sizeof(wchar_t));
+ ASSERT(ln->buf != NULL);
+ ln->len = 0;
+ ln->buf[ln->len] = '\0';
+ ln->cur = 0;
+ ln->link.next = NULL;
+ ln->link.prev = NULL;
+
+ return ln;
+}
+
+void
+inputln_free(struct inputln *ln)
+{
+ free(ln->buf);
+ free(ln);
+}
+
+void
+inputln_left(struct inputln *cmd)
+{
+ cmd->cur = MAX(0, cmd->cur-1);
+}
+
+void
+inputln_right(struct inputln *cmd)
+{
+ cmd->cur = MIN(cmd->len, cmd->cur+1);
+}
+
+void
+inputln_addch(struct inputln *line, wchar_t c)
+{
+ int i;
+
+ if (line->len + 1 == line->cap) {
+ line->cap *= 2;
+ line->buf = realloc(line->buf, line->cap);
+ }
+
+ for (i = line->len; i > line->cur; i--)
+ line->buf[i] = line->buf[i-1];
+ line->buf[line->cur] = c;
+ line->len++;
+ line->cur++;
+}
+
+void
+inputln_del(struct inputln *line, int n)
+{
+ int i;
+
+ if (!line->cur) return;
+
+ n = MIN(n, line->cur);
+ for (i = line->cur; i <= line->len; i++)
+ line->buf[i-n] = line->buf[i];
+ for (i = line->len - n; i <= line->len; i++)
+ line->buf[i] = 0;
+ line->len -= n;
+ line->cur -= n;
+}
diff --git a/history.h b/history.h
@@ -0,0 +1,40 @@
+#pragma once
+
+#include "link.h"
+
+#include "wchar.h"
+
+#define HISTORY_MAX 100
+
+struct inputln {
+ wchar_t *buf;
+ int len, cap;
+ int cur;
+
+ struct link link;
+};
+
+struct history {
+ struct link list;
+ struct inputln *cmd, *query;
+};
+
+void history_init(struct history *history);
+void history_free(struct history *history);
+
+void history_submit(struct history *history);
+
+void history_prev(struct history *history);
+void history_next(struct history *history);
+
+void history_add(struct history *history, struct inputln *line);
+void history_pop(struct inputln *line);
+
+struct inputln *inputln_init(void);
+void inputln_free(struct inputln *ln);
+
+void inputln_left(struct inputln *line);
+void inputln_right(struct inputln *line);
+
+void inputln_addch(struct inputln *line, wchar_t c);
+void inputln_del(struct inputln *line, int n);
diff --git a/link.c b/link.c
@@ -0,0 +1,54 @@
+#include "link.h"
+
+int
+list_len(struct link *list)
+{
+ struct link *iter;
+ int len;
+
+ len = 0;
+ for (iter = list; iter; iter = iter->next)
+ len += 1;
+
+ return len;
+}
+
+struct link *
+list_back(struct link *link)
+{
+ for (; link->next; link = link->next);
+ return link;
+}
+
+void
+link_prepend(struct link *list, struct link *link)
+{
+ link->prev = list->prev;
+ link->next = list;
+
+ if (link->prev)
+ link->prev->next = link;
+ if (link->next)
+ link->next->prev = link;
+}
+
+void
+link_append(struct link *list, struct link *link)
+{
+ link->prev = list;
+ link->next = list->next;
+
+ if (link->prev)
+ link->prev->next = link;
+ if (link->next)
+ link->next->prev = link;
+}
+
+void
+link_pop(struct link *link)
+{
+ if (link->prev)
+ link->prev->next = link->next;
+ if (link->next)
+ link->next->prev = link->prev;
+}
diff --git a/link.h b/link.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include <stdlib.h>
+
+#define OFFSET(type, attr) ((size_t) &((type *)0)->attr)
+#define UPCAST(ptr, type) ({ \
+ const typeof( ((type *)0)->link ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - OFFSET(type, link) ); })
+
+#define LIST_HEAD ((struct link) { .prev = NULL, .next = NULL })
+
+struct link {
+ struct link *prev, *next;
+};
+
+int list_len(struct link *list);
+struct link* list_back(struct link *list);
+
+void link_push_back(struct link *list, struct link *link);
+
+void link_prepend(struct link *list, struct link *link);
+void link_append(struct link *list, struct link *link);
+void link_pop(struct link *link);
diff --git a/main b/main
Binary files differ.
diff --git a/main.c b/main.c
@@ -1,27 +1,19 @@
-#define _XOPEN_SOURCE 500
+#define _XOPEN_SOURCE 600
+
+#include "util.h"
+#include "link.h"
+#include "history.h"
+
+#include "curses.h"
-#include <stdlib.h>
#include <stdio.h>
-#include <signal.h>
+#include <stdlib.h>
#include <string.h>
-
-#include <unistd.h>
#include <time.h>
+#include <unistd.h>
#include <wchar.h>
#include <wctype.h>
-#include "ncurses.h"
-#include "readline/readline.h"
-#include "readline/history.h"
-
-#define MAX(a, b) ((a) > (b) ? (a) : (b))
-#define MIN(a, b) ((a) > (b) ? (b) : (a))
-#define ARRLEN(x) (sizeof(x)/sizeof((x)[0]))
-
-#define OFFSET(parent, attr) ((size_t) &((type *)0)->attr)
-#define UPCAST(ptr, type) ({ \
- const typeof( ((type *)0)->link ) *__mptr = (ptr); \
- (type *)( (char *)__mptr - OFFSET(type, link) ); })
#define ATTR_ON(win, attr) wattr_on((win), (attr), NULL)
#define ATTR_OFF(win, attr) wattr_off((win), (attr), NULL)
@@ -30,16 +22,27 @@
#define KEY_ESC '\x1b'
#define KEY_TAB '\t'
+#define KEY_CTRL(c) ((c) & ~0x60)
-struct pane;
-typedef int (*pane_handler)(int c);
-typedef void (*pane_updater)(struct pane *pane, int sel);
+enum {
+ STYLE_DEFAULT,
+ STYLE_TITLE,
+ STYLE_PANE_SEP
+};
-struct link {
- struct link *prev, *next;
+enum {
+ EXECUTE,
+ SEARCH
};
+struct pane;
+
+typedef int (*pane_handler)(wint_t c);
+typedef void (*pane_updater)(struct pane *pane, int sel);
+typedef char *(*completion_generator)(const char *text, int state);
+typedef int (*cmd_handler)(const char *args);
+
struct pane {
WINDOW *win;
int sx, sy, ex, ey;
@@ -71,45 +74,38 @@ struct tag {
struct link link;
};
-enum {
- STYLE_DEFAULT,
- STYLE_TITLE,
- STYLE_PANE_SEP
+struct cmd {
+ const char *name;
+ cmd_handler func;
};
-enum {
- EXECUTE,
- SEARCH
-};
int scrw, scrh;
int quit;
-struct pane pane_left, pane_right, pane_bot;
-struct pane *pane_sel;
float win_ratio;
+struct pane *pane_sel;
+struct pane pane_left, pane_right, pane_bot;
+struct pane *const panes[] = {
+ &pane_left,
+ &pane_right,
+ &pane_bot
+};
struct track *track_sel;
struct track *tracks;
-struct track_ref *playlist;
+struct link playlist;
int track_paused;
-int cmdshow, cmdcur;
-int cmdmode;
+completion_generator completion;
+struct history search_history, command_history;
+struct history *history;
+int cmd_show, cmd_mode;
-int rl_input, rl_input_avail;
-
-struct pane *const panes[] = {
- &pane_left,
- &pane_right,
- &pane_bot
-};
void init(void);
void cleanup(void);
-void cancel(int sig);
-
void resize(void);
void pane_resize(struct pane *pane,
int sx, int sy, int ex, int ey);
@@ -117,56 +113,28 @@ void pane_resize(struct pane *pane,
void pane_init(struct pane *p, pane_handler handle, pane_updater update);
void pane_title(struct pane *pane, const char *title, int highlight);
-char **track_name_completion(const char *text, int start, int end);
+char *command_name_generator(const char *text, int state);
char *track_name_generator(const char *text, int state);
-int readline_getc(FILE *f);
-int readline_input_avail(void);
-void readline_consume(int c);
-int exit_cmdmode(int a, int b);
-
-int strnwidth(const char *str, int len);
-
-void evalcmd(char *cmd);
-
-int track_input(int c);
+int track_input(wint_t c);
void track_vis(struct pane *pane, int sel);
-int tag_input(int c);
+int tag_input(wint_t c);
void tag_vis(struct pane *pane, int sel);
-int cmd_input(int c);
+int cmd_input(wint_t c);
void cmd_vis(struct pane *pane, int sel);
-void main_input(int c);
+void main_input(wint_t c);
void main_vis(void);
+
void
init(void)
{
quit = 0;
win_ratio = 0.3f;
- cmdshow = 0;
- cmdcur = 0;
-
- /* readline */
- rl_initialize();
- rl_input = 0;
- rl_input_avail = 0;
-
- rl_catch_signals = 0;
- rl_catch_sigwinch = 0;
- rl_deprep_term_function = NULL;
- rl_prep_term_function = NULL;
- rl_change_environment = 0;
-
- rl_getc_function = readline_getc;
- rl_input_available_hook = readline_input_avail;
- rl_callback_handler_install("", evalcmd);
- rl_attempted_completion_function = track_name_completion;
- rl_bind_key('\x07', exit_cmdmode);
-
- /* curses */
+
initscr();
cbreak();
noecho();
@@ -176,7 +144,9 @@ init(void)
curs_set(0);
ESCDELAY = 0;
- signal(SIGINT, cancel);
+ history = &command_history;
+ history_init(&search_history);
+ history_init(&command_history);
init_pair(STYLE_TITLE, COLOR_WHITE, COLOR_BLUE);
init_pair(STYLE_PANE_SEP, COLOR_BLUE, COLOR_BLACK);
@@ -193,7 +163,9 @@ init(void)
void
cleanup(void)
{
- rl_callback_handler_remove();
+ history_free(&search_history);
+ history_free(&command_history);
+
delwin(pane_left.win);
delwin(pane_right.win);
delwin(pane_bot.win);
@@ -201,16 +173,6 @@ cleanup(void)
}
void
-cancel(int sig)
-{
- if (pane_sel == &pane_bot) {
- pane_sel = NULL;
- } else {
- quit = 1;
- }
-}
-
-void
resize(void)
{
int i;
@@ -240,7 +202,6 @@ pane_resize(struct pane *pane, int sx, int sy, int ex, int ey)
pane->h = pane->ey - pane->sy;
pane->active = (pane->w > 0 && pane->h > 0);
-
if (pane->active) {
wresize(pane->win, pane->h, pane->w);
mvwin(pane->win, pane->sy, pane->sx);
@@ -252,6 +213,7 @@ void
pane_init(struct pane *pane, pane_handler handle, pane_updater update)
{
pane->win = newwin(1, 1, 0, 0);
+ ASSERT(pane->win != NULL);
pane->handle = handle;
pane->update = update;
}
@@ -272,28 +234,28 @@ pane_title(struct pane *pane, const char *title, int highlight)
STYLE_OFF(pane->win, TITLE);
}
-char **
-track_name_completion(const char *text, int start, int end)
+char *
+command_name_generator(const char *text, int reset)
{
- rl_attempted_completion_over = 1;
- return rl_completion_matches(text, track_name_generator);
+ return NULL;
}
char *
-track_name_generator(const char *text, int state)
+track_name_generator(const char *text, int reset)
{
- static int list_index, len;
+ static int index, len;
char *name;
- if (cmdmode != SEARCH) return NULL;
+ if (cmd_mode != SEARCH) return NULL;
- if (!state) {
- list_index = 0;
+ if (reset) {
+ index = 0;
len = strlen(text);
}
- if (list_index++ == 0 && !strncmp("hello", text, len))
+ if (index++ == 0 && !strncmp("hello", text, len))
return strdup("hello");
+
// TODO: iter over playlist
// while ((name = track_names[list_index++])) {
// if (strncmp(name, text, len) == 0) {
@@ -304,88 +266,33 @@ track_name_generator(const char *text, int state)
return NULL;
}
-int
-readline_input_avail(void)
-{
- return rl_input_avail;
-}
-
-int
-readline_getc(FILE *dummy)
-{
- rl_input_avail = false;
- return rl_input;
-}
-
void
-readline_consume(int c)
+tag_init(void)
{
- rl_input = c;
- rl_input_avail = true;
- rl_callback_read_char();
+ /* TODO: load tags as directory names (excluding unsorted) */
}
int
-exit_cmdmode(int a, int b)
+tag_input(wint_t c)
{
- pane_sel = NULL;
- move(5, 5);
- wprintw(stdscr, "HIT %i %i", a, b);
return 0;
}
-int
-strnwidth(const char *s, int n)
-{
- mbstate_t shift_state;
- wchar_t wc;
- size_t wc_len;
- size_t width = 0;
-
- memset(&shift_state, '\0', sizeof shift_state);
-
- for (size_t i = 0; i < n; i += wc_len) {
- wc_len = mbrtowc(&wc, s + i, MB_CUR_MAX, &shift_state);
- if (!wc_len) {
- break;
- } else if (wc_len >= (size_t)-2) {
- width += MIN(n - 1, strlen(s + i));
- break;
- } else {
- width += iswcntrl(wc) ? 2 : MAX(0, wcwidth(wc));
- }
- }
-
-done:
- return width;
-}
-
void
-evalcmd(char *line)
-{
- if (!line || !*line) {
- pane_sel = NULL;
- return;
- }
-
- if (*line) add_history(line);
-}
-
-int
-tag_input(int c)
+tag_vis(struct pane *pane, int sel)
{
- return 0;
+ werase(pane->win);
+ pane_title(pane, "Tags", sel);
}
void
-tag_vis(struct pane *pane, int sel)
+track_init(void)
{
- werase(pane->win);
- pane_title(pane, "Tags", sel);
+ /* TODO: for each tag director load track names */
}
int
-track_input(int c)
+track_input(wint_t c)
{
return 0;
}
@@ -398,16 +305,59 @@ track_vis(struct pane *pane, int sel)
}
int
-cmd_input(int c)
+cmd_input(wint_t c)
{
- readline_consume(c);
+ if (cmd_mode == EXECUTE) {
+ history = &command_history;
+ completion = command_name_generator;
+ } else {
+ history = &search_history;
+ completion = track_name_generator;
+ }
+
+ switch (c) {
+ case KEY_ESC:
+ if (history->cmd == history->query)
+ return 0;
+ history->cmd = history->query;
+ break;
+ case KEY_LEFT:
+ inputln_left(history->cmd);
+ break;
+ case KEY_RIGHT:
+ inputln_right(history->cmd);
+ break;
+ case KEY_CTRL('w'):
+ inputln_del(history->cmd, history->cmd->cur);
+ break;
+ case KEY_UP:
+ // TODO: show visually that no more matches
+ history_next(history);
+ break;
+ case KEY_DOWN:
+ history_prev(history);
+ break;
+ case '\n':
+ case KEY_ENTER:
+ history_submit(history);
+ break;
+ case KEY_TAB:
+ break;
+ case KEY_BACKSPACE:
+ inputln_del(history->cmd, 1);
+ break;
+ default:
+ if (!iswprint(c)) return 0;
+ inputln_addch(history->cmd, c);
+ break;
+ }
return 1;
}
void
cmd_vis(struct pane *pane, int sel)
{
- int in_range;
+ struct inputln *cmd;
werase(pane->win);
@@ -416,23 +366,23 @@ cmd_vis(struct pane *pane, int sel)
else
pane_title(pane, "", 0);
- if (sel || cmdshow) {
+ if (sel || cmd_show) {
wmove(pane->win, 2, 0);
- waddch(pane->win, cmdmode == SEARCH ? '/' : ':');
- wprintw(pane->win, "%-*.*s", pane->w - 1, pane->w - 1, rl_line_buffer);
+ waddch(pane->win, cmd_mode == SEARCH ? '/' : ':');
+ cmd = history->cmd;
+ wprintw(pane->win, "%-*.*ls", pane->w - 1, pane->w - 1, cmd->buf);
+ // TODO: if query != cmd, highlight query substr
if (sel) {
- cmdcur = strnwidth(rl_line_buffer, rl_point);
ATTR_ON(pane->win, A_REVERSE);
- wmove(pane->win, 2, 1 + cmdcur);
- in_range = cmdcur < strlen(rl_line_buffer);
- waddch(pane->win, in_range ? rl_line_buffer[cmdcur] : ' ');
+ wmove(pane->win, 2, 1 + cmd->cur);
+ waddch(pane->win, cmd->cur < cmd->len ? cmd->buf[cmd->cur] : ' ');
ATTR_OFF(pane->win, A_REVERSE);
}
}
}
void
-main_input(int c)
+main_input(wint_t c)
{
switch (c) {
case KEY_TAB:
@@ -450,16 +400,12 @@ main_input(int c)
case KEY_RIGHT:
pane_sel = &pane_right;
break;
- case CTRL('c'):
- quit = 1;
- break;
case ':':
- cmdmode = EXECUTE;
+ cmd_mode = EXECUTE;
pane_sel = &pane_bot;
break;
- case '?':
case '/':
- cmdmode = SEARCH;
+ cmd_mode = SEARCH;
pane_sel = &pane_bot;
break;
}
@@ -486,7 +432,8 @@ main_vis(void)
int
main(int argc, const char **argv)
{
- int i, c, handled;
+ int i, handled;
+ wint_t c;
init();
@@ -511,9 +458,9 @@ main(int argc, const char **argv)
main_vis();
doupdate();
- keypad(stdscr, pane_sel != &pane_bot);
- c = getch();
+ get_wch(&c);
} while (!quit);
cleanup();
}
+
diff --git a/util.c b/util.c
@@ -0,0 +1,43 @@
+#define _XOPEN_SOURCE 600
+
+#include "util.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#include <wctype.h>
+
+int
+strnwidth(const char *s, int n)
+{
+ mbstate_t shift_state;
+ wchar_t wc;
+ size_t wc_len;
+ size_t width = 0;
+
+ memset(&shift_state, '\0', sizeof shift_state);
+
+ for (size_t i = 0; i < n; i += wc_len) {
+ wc_len = mbrtowc(&wc, s + i, MB_CUR_MAX, &shift_state);
+ if (!wc_len) {
+ break;
+ } else if (wc_len >= (size_t)-2) {
+ width += MIN(n - 1, strlen(s + i));
+ break;
+ } else {
+ width += iswcntrl(wc) ? 2 : MAX(0, wcwidth(wc));
+ }
+ }
+
+done:
+ return width;
+}
+
+void
+assert(int cond, const char *file, int line, const char *condstr)
+{
+ if (cond) return;
+
+ fprintf(stderr, "Assertion failed %s:%i (%s)", file, line, condstr);
+ exit(1);
+}
diff --git a/util.h b/util.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#include <stdio.h>
+
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define MIN(a, b) ((a) > (b) ? (b) : (a))
+#define ARRLEN(x) (sizeof(x)/sizeof((x)[0]))
+
+#define ASSERT(x) assert((x), __FILE__, __LINE__, #x)
+
+int strnwidth(const char *s, int n);
+void assert(int cond, const char *file, int line, const char *condstr);