Introduction
Introduction Statistics Contact Development Disclaimer Help
tImprove range parsing; add search and command history - ledit - Text editor (W…
git clone git://lumidify.org/ledit.git (fast, but not encrypted)
git clone https://lumidify.org/git/ledit.git (encrypted, but very slow)
Log
Files
Refs
README
LICENSE
---
commit 7501cea8774e0b58705da53357947723f213cbf5
parent 0498ed82f507017d8a5c6d83da08c66b2520bf95
Author: lumidify <[email protected]>
Date: Thu, 9 Dec 2021 20:10:59 +0100
Improve range parsing; add search and command history
Diffstat:
M keys_basic.c | 5 +++--
M keys_command.c | 233 +++++++++++++++++++++++++++--…
M keys_command.h | 1 +
M keys_command_config.h | 10 ++++++++++
M ledit.c | 1 +
M search.c | 5 +++--
M undo.c | 2 +-
M util.c | 7 +++++++
M util.h | 2 ++
M window.c | 14 ++++++++++++++
M window.h | 14 +++++++++++++-
11 files changed, 262 insertions(+), 32 deletions(-)
---
diff --git a/keys_basic.c b/keys_basic.c
t@@ -1839,9 +1839,10 @@ static struct action
enter_commandedit(ledit_view *view, char *text, size_t len) {
(void)text;
(void)len;
- window_set_bottom_bar_text(view->window, ":", -1);
+ char *str = view->sel_valid ? ":'<,'>" : ":";
+ window_set_bottom_bar_text(view->window, str, -1);
+ window_set_bottom_bar_cursor(view->window, strlen(str));
window_set_bottom_bar_min_pos(view->window, 1);
- window_set_bottom_bar_cursor(view->window, 1);
view->cur_command_type = CMD_EDIT;
window_set_bottom_bar_text_shown(view->window, 1);
discard_repetition_stack();
diff --git a/keys_command.c b/keys_command.c
t@@ -22,6 +22,7 @@
#include "view.h"
#include "search.h"
#include "cleanup.h"
+#include "util.h"
#include "keys.h"
#include "keys_command.h"
t@@ -42,6 +43,53 @@ static struct {
int start_group; /* only set for the first replacement */
} sub_state = {NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
+typedef struct {
+ size_t len, cur, cap;
+ char **cmds;
+} history;
+
+history cmdhistory = {0, 0, 0, NULL};
+
+history searchhistory = {0, 0, 0, NULL};
+
+static void
+push_history(history *hist, char *cmd, size_t len) {
+ if (hist->len >= hist->cap) {
+ size_t cap = hist->cap * 2 > hist->cap + 2 ? hist->cap * 2 : h…
+ if (cap <= hist->len)
+ exit(1); /* FIXME: overflow */
+ hist->cmds = ledit_reallocarray(hist->cmds, cap, sizeof(char *…
+ hist->cap = cap;
+ }
+ hist->cmds[hist->len] = ledit_strndup(cmd, len);
+ hist->len++;
+ hist->cur = hist->len;
+}
+
+static void
+push_cmdhistory(char *cmd, size_t len) {
+ push_history(&cmdhistory, cmd, len);
+}
+
+static void
+push_searchhistory(char *search, size_t len) {
+ push_history(&searchhistory, search, len);
+}
+
+void
+command_key_cleanup(void) {
+ free(sub_state.search);
+ free(sub_state.replace);
+ for (size_t i = 0; i < cmdhistory.len; i++) {
+ free(cmdhistory.cmds[i]);
+ }
+ for (size_t i = 0; i < searchhistory.len; i++) {
+ free(searchhistory.cmds[i]);
+ }
+ free(cmdhistory.cmds);
+ free(searchhistory.cmds);
+}
+
static int
view_locked_error(ledit_view *view) {
window_show_message(view->window, view->lock_text, -1);
t@@ -231,18 +279,20 @@ handle_substitute(ledit_view *view, char *cmd, size_t l1…
CHECK_VIEW_LOCKED(view);
cmd++; /* remove 's' at beginning */
size_t len = strlen(cmd);
+ char *sep = NULL;
if (len == 0) goto error;
- /* FIXME: utf8 */
- char sep = cmd[0];
- cmd++;
- char *next = strchr(cmd, sep);
+ char *sepend = next_utf8(cmd + 1);
+ size_t seplen = sepend - cmd;
+ sep = ledit_strndup(cmd, seplen);
+ cmd += seplen;
+ char *next = strstr(cmd, sep);
if (next == NULL) goto error;
*next = '\0';
- next++;
- char *last = strchr(next, sep);
+ next += seplen;
+ char *last = strstr(next, sep);
if (last == NULL) goto error;
*last = '\0';
- last++;
+ last += seplen;
int confirm = 0, global = 0;
char *c = last;
while (*c != '\0') {
t@@ -280,9 +330,11 @@ handle_substitute(ledit_view *view, char *cmd, size_t l1,…
} else {
substitute_all_remaining(view);
}
+ free(sep);
return 0;
error:
window_show_message(view->window, "Invalid command", -1);
+ free(sep);
return 0;
}
t@@ -305,13 +357,14 @@ static const struct {
};
/*
-. current line - FIXME: implement
+. current line
$ last line
% all lines
*/
/* FIXME: ACTUALLY USE LEN!!! */
-/* FIXME: allow using marks and selection range here */
+/* NOTE: Marks are only recognized here if they are one unicode character! */
+/* NOTE: Only the line range of the selection is used at the moment. */
static int
parse_range(ledit_view *view, char *cmd, size_t len, char **cmd_ret, size_t *l…
(void)len;
t@@ -325,7 +378,7 @@ parse_range(ledit_view *view, char *cmd, size_t len, char …
*l1_valid = 0;
*l2_valid = 0;
char *c = cmd;
- for (; *c != '\0'; c++) {
+ while (*c != '\0') {
if (isdigit(*c)) {
/* FIXME: integer overflow */
if (s & IN_LINENO) {
t@@ -338,21 +391,43 @@ parse_range(ledit_view *view, char *cmd, size_t len, cha…
} else if ((s & START_LINENO) && (s & START_RANGE)) {
l1 = *c - '0';
*l1_valid = 1;
- s &= ~START_RANGE;
- s &= ~START_LINENO;
- s |= IN_RANGE | IN_LINENO;
+ s = IN_RANGE | IN_LINENO;
} else if ((s & START_LINENO)) {
l2 = *c - '0';
*l2_valid = 1;
- s &= ~START_LINENO;
- s |= IN_LINENO;
+ s = IN_LINENO;
+ }
+ } else if (*c == '\'' && (s & START_LINENO)) {
+ if (c[1] == '\0' || c[2] == '\0')
+ return 1;
+ char *aftermark = next_utf8(c + 2);
+ size_t marklen = aftermark - (c + 1);
+ size_t l, b;
+ if (!strncmp(c + 1, "<", strlen("<")) && view->sel_val…
+ l = view->sel.line1 < view->sel.line2 ? view->…
+ } else if (!strncmp(c + 1, ">", strlen(">")) && view->…
+ l = view->sel.line1 > view->sel.line2 ? view->…
+ } else {
+ if (buffer_get_mark(view->buffer, c + 1, markl…
+ /* FIXME: show better error message */
+ return 1;
+ }
+ }
+ if (!*l1_valid) {
+ l1 = l + 1;
+ *l1_valid = 1;
+ } else {
+ l2 = l + 1;
+ *l2_valid = 1;
}
+ c = aftermark;
+ s = 0;
+ continue;
} else if (*c == ',' && !(s & START_RANGE)) {
if (*l1_valid && *l2_valid) {
return 1;
} else {
- s |= START_LINENO;
- s &= ~IN_LINENO;
+ s = START_LINENO;
}
} else if (*c == '%') {
if (s & START_RANGE) {
t@@ -367,20 +442,33 @@ parse_range(ledit_view *view, char *cmd, size_t len, cha…
} else if (*c == '$') {
if (s & START_LINENO) {
if (!*l1_valid) {
+ l1 = view->cur_line + 1;
+ *l1_valid = 1;
+ } else {
+ l2 = view->cur_line + 1;
+ *l2_valid = 1;
+ }
+ s = 0;
+ } else {
+ return 1;
+ }
+ } else if (*c == '$') {
+ if (s & START_LINENO) {
+ if (!*l1_valid) {
l1 = view->lines_num;
*l1_valid = 1;
} else {
l2 = view->lines_num;
*l2_valid = 1;
}
- s &= ~START_LINENO;
- s &= ~IN_LINENO;
+ s = 0;
} else {
return 1;
}
} else {
break;
}
+ c++;
}
if ((!*l1_valid || !*l2_valid) && !(s & START_RANGE))
return 1;
t@@ -397,12 +485,14 @@ static int
handle_cmd(ledit_view *view, char *cmd, size_t len) {
if (len < 1)
return 0;
+ push_cmdhistory(cmd, len);
char *c;
size_t l1, l2;
int l1_valid, l2_valid;
- /* FIXME: show error msg here */
- if (parse_range(view, cmd, len, &c, &l1, &l2, &l1_valid, &l2_valid))
+ if (parse_range(view, cmd, len, &c, &l1, &l2, &l1_valid, &l2_valid)) {
+ window_show_message(view->window, "Error parsing command", -1);
return 0;
+ }
int range_given = l1_valid && l2_valid;
if (!range_given) {
l1 = l2 = view->cur_line;
t@@ -413,6 +503,7 @@ handle_cmd(ledit_view *view, char *cmd, size_t len) {
return cmds[i].handler(view, c, l1, l2);
}
}
+ window_show_message(view->window, "Invalid command", -1);
return 0;
}
t@@ -531,8 +622,72 @@ edit_submit(ledit_view *view, char *key_text, size_t len)…
(void)key_text;
(void)len;
window_set_bottom_bar_text_shown(view->window, 0);
+ char *text = window_get_bottom_bar_text(view->window);
+ int min_pos = window_get_bottom_bar_min_pos(view->window);
+ int textlen = strlen(text);
+ /* this should never happen */
+ if (min_pos > textlen) {
+ textlen = 0;
+ } else {
+ textlen -= min_pos;
+ text += min_pos;
+ }
/* FIXME: this is hacky */
- return handle_cmd(view, window_get_bottom_bar_text(view->window) + 1, …
+ return handle_cmd(view, text, (size_t)textlen);
+}
+
+static int
+edit_prevcommand(ledit_view *view, char *key_text, size_t len) {
+ (void)key_text;
+ (void)len;
+ if (cmdhistory.cur > 0) {
+ cmdhistory.cur--;
+ window_set_bottom_bar_realtext(view->window, cmdhistory.cmds[c…
+ window_bottom_bar_cursor_to_end(view->window);
+ }
+ return 1;
+}
+
+static int
+edit_nextcommand(ledit_view *view, char *key_text, size_t len) {
+ (void)key_text;
+ (void)len;
+ if (cmdhistory.len > 0 && cmdhistory.cur < cmdhistory.len - 1) {
+ cmdhistory.cur++;
+ window_set_bottom_bar_realtext(view->window, cmdhistory.cmds[c…
+ } else {
+ cmdhistory.cur = cmdhistory.len;
+ window_set_bottom_bar_realtext(view->window, "", -1);
+ }
+ window_bottom_bar_cursor_to_end(view->window);
+ return 1;
+}
+
+static int
+edit_prevsearch(ledit_view *view, char *key_text, size_t len) {
+ (void)key_text;
+ (void)len;
+ if (searchhistory.cur > 0) {
+ searchhistory.cur--;
+ window_set_bottom_bar_realtext(view->window, searchhistory.cmd…
+ window_bottom_bar_cursor_to_end(view->window);
+ }
+ return 1;
+}
+
+static int
+edit_nextsearch(ledit_view *view, char *key_text, size_t len) {
+ (void)key_text;
+ (void)len;
+ if (searchhistory.len > 0 && searchhistory.cur < searchhistory.len - 1…
+ searchhistory.cur++;
+ window_set_bottom_bar_realtext(view->window, searchhistory.cmd…
+ } else {
+ searchhistory.cur = searchhistory.len;
+ window_set_bottom_bar_realtext(view->window, "", -1);
+ }
+ window_bottom_bar_cursor_to_end(view->window);
+ return 1;
}
/* FIXME: support visual mode, i.e. change selection to new place? */
t@@ -561,8 +716,21 @@ editsearch_submit(ledit_view *view, char *key_text, size_…
(void)key_text;
(void)len;
window_set_bottom_bar_text_shown(view->window, 0);
- set_search_forward(window_get_bottom_bar_text(view->window) + 1);
- search_next(view);
+ char *text = window_get_bottom_bar_text(view->window);
+ int min_pos = window_get_bottom_bar_min_pos(view->window);
+ int textlen = strlen(text);
+ /* this should always be the case */
+ if (min_pos <= textlen) {
+ if (min_pos < textlen)
+ push_searchhistory(text + min_pos, textlen - min_pos);
+ set_search_forward(text + min_pos);
+ search_next(view);
+ } else {
+ window_show_message(
+ view->window,
+ "Error in program. Tell lumidify about it.", -1
+ );
+ }
return 0;
}
t@@ -571,8 +739,21 @@ editsearchb_submit(ledit_view *view, char *key_text, size…
(void)key_text;
(void)len;
window_set_bottom_bar_text_shown(view->window, 0);
- set_search_backward(window_get_bottom_bar_text(view->window) + 1);
- search_next(view);
+ char *text = window_get_bottom_bar_text(view->window);
+ int min_pos = window_get_bottom_bar_min_pos(view->window);
+ int textlen = strlen(text);
+ /* this should always be the case */
+ if (min_pos <= textlen) {
+ if (min_pos < textlen)
+ push_searchhistory(text + min_pos, textlen - min_pos);
+ set_search_backward(text + min_pos);
+ search_next(view);
+ } else {
+ window_show_message(
+ view->window,
+ "Error in program. Tell lumidify about it.", -1
+ );
+ }
return 0;
}
diff --git a/keys_command.h b/keys_command.h
t@@ -2,4 +2,5 @@
void search_next(ledit_view *view);
void search_prev(ledit_view *view);
+void command_key_cleanup(void);
struct action command_key_handler(ledit_view *view, XEvent *event, int lang_in…
diff --git a/keys_command_config.h b/keys_command_config.h
t@@ -16,6 +16,10 @@ static int edit_cursor_to_beginning(ledit_view *view, char …
static int edit_backspace(ledit_view *view, char *key_text, size_t len);
static int edit_delete(ledit_view *view, char *key_text, size_t len);
static int edit_submit(ledit_view *view, char *key_text, size_t len);
+static int edit_prevcommand(ledit_view *view, char *key_text, size_t len);
+static int edit_nextcommand(ledit_view *view, char *key_text, size_t len);
+static int edit_prevsearch(ledit_view *view, char *key_text, size_t len);
+static int edit_nextsearch(ledit_view *view, char *key_text, size_t len);
static int editsearch_submit(ledit_view *view, char *key_text, size_t len);
static int editsearchb_submit(ledit_view *view, char *key_text, size_t len);
static int edit_discard(ledit_view *view, char *key_text, size_t len);
t@@ -43,6 +47,12 @@ static struct key keys_en[] = {
{NULL, 0, XK_Right, CMD_EDIT, &edit_cursor_right},
{NULL, 0, XK_Right, CMD_EDITSEARCH, &edit_cursor_right},
{NULL, 0, XK_Right, CMD_EDITSEARCHB, &edit_cursor_right},
+ {NULL, 0, XK_Up, CMD_EDIT, &edit_prevcommand},
+ {NULL, 0, XK_Up, CMD_EDITSEARCH, &edit_prevsearch},
+ {NULL, 0, XK_Up, CMD_EDITSEARCHB, &edit_prevsearch},
+ {NULL, 0, XK_Down, CMD_EDIT, &edit_nextcommand},
+ {NULL, 0, XK_Down, CMD_EDITSEARCH, &edit_nextsearch},
+ {NULL, 0, XK_Down, CMD_EDITSEARCHB, &edit_nextsearch},
{NULL, 0, XK_BackSpace, CMD_EDIT, &edit_backspace},
{NULL, 0, XK_BackSpace, CMD_EDITSEARCH, &edit_backspace},
{NULL, 0, XK_BackSpace, CMD_EDITSEARCHB, &edit_backspace},
diff --git a/ledit.c b/ledit.c
t@@ -259,6 +259,7 @@ ledit_cleanup(void) {
/* FIXME: check for other things to clean up */
search_cleanup();
basic_key_cleanup();
+ command_key_cleanup();
if (buffer)
buffer_destroy(buffer);
if (theme)
diff --git a/search.c b/search.c
t@@ -48,7 +48,8 @@ static enum ledit_search_state
search_forward(ledit_view *view, size_t *line_ret, size_t *byte_ret) {
*line_ret = view->cur_line;
*byte_ret = view->cur_index;
- if (last_search == NULL)
+ /* if last_search is empty, strstr will find the ending '\0' */
+ if (last_search == NULL || last_search[0] == '\0')
return SEARCH_NO_PATTERN;
size_t line = view->cur_line;
/* start one byte later so it doesn't get stuck on a match
t@@ -96,7 +97,7 @@ static enum ledit_search_state
search_backward(ledit_view *view, size_t *line_ret, size_t *byte_ret) {
*line_ret = view->cur_line;
*byte_ret = view->cur_index;
- if (last_search == NULL)
+ if (last_search == NULL || last_search[0] == '\0')
return SEARCH_NO_PATTERN;
size_t line = view->cur_line;
size_t byte = view->cur_index;
diff --git a/undo.c b/undo.c
t@@ -72,7 +72,7 @@ push_undo_elem(undo_stack *undo) {
if (undo->len > undo->cap) {
/* FIXME: wait, why is it size_t here already? */
size_t cap = undo->len * 2;
- undo->stack = ledit_realloc(undo->stack, cap * sizeof(undo_ele…
+ undo->stack = ledit_reallocarray(undo->stack, cap, sizeof(undo…
for (size_t i = undo->cap; i < cap; i++) {
undo->stack[i].text = NULL;
}
diff --git a/util.c b/util.c
t@@ -48,3 +48,10 @@ draw_destroy(ledit_window *window, ledit_draw *draw) {
XftDrawDestroy(draw->xftdraw);
free(draw);
}
+
+char *
+next_utf8(char *str) {
+ while ((*str & 0xC0) == 0x80)
+ str++;
+ return str;
+}
diff --git a/util.h b/util.h
t@@ -24,3 +24,5 @@ void draw_grow(ledit_window *window, ledit_draw *draw, int w…
* Destroy a draw.
*/
void draw_destroy(ledit_window *window, ledit_draw *draw);
+
+char *next_utf8(char *str);
diff --git a/window.c b/window.c
t@@ -165,6 +165,11 @@ window_set_bottom_bar_min_pos(ledit_window *window, int p…
window->bb->min_pos = pos;
}
+int
+window_get_bottom_bar_min_pos(ledit_window *window) {
+ return window->bb->min_pos;
+}
+
void
window_bottom_bar_cursor_to_beginning(ledit_window *window) {
window->bb->line_cur_pos = window->bb->min_pos;
t@@ -254,6 +259,15 @@ window_set_bottom_bar_text(ledit_window *window, char *te…
window->redraw = 1;
}
+void
+window_set_bottom_bar_realtext(ledit_window *window, char *text, int len) {
+ if (window->bb->min_pos <= window->bb->line_len)
+ window->bb->line_len = window->bb->min_pos;
+ window->bb->line_cur_pos = window->bb->line_len;
+ window_insert_bottom_bar_text(window, text, len);
+ window->redraw = 1;
+}
+
char *
window_get_bottom_bar_text(ledit_window *window) {
return window->bb->line_text;
diff --git a/window.h b/window.h
t@@ -116,13 +116,18 @@ void window_bottom_bar_cursor_to_beginning(ledit_window …
void window_bottom_bar_cursor_to_end(ledit_window *window);
/*
- * Set the minimum byte position of the cursor of the editable text to 'pos'.
+ * Set the minimum byte position of the cursor of the editable text to 'pos'.
* This means that the cursor will not be allowed to go further left
* than that position (used e.g. for the ':' in commands).
*/
void window_set_bottom_bar_min_pos(ledit_window *window, int pos);
/*
+ * Get the mininum position (see above).
+ */
+int window_get_bottom_bar_min_pos(ledit_window *window);
+
+/*
* Set whether the editable text is shown.
*/
void window_set_bottom_bar_text_shown(ledit_window *window, int shown);
t@@ -153,6 +158,13 @@ void window_insert_bottom_bar_text(ledit_window *window, …
void window_set_bottom_bar_text(ledit_window *window, char *text, int len);
/*
+ * Set the text after the minimum position.
+ * If the set minimum position is after the current length
+ * of the text in the bar, the text is just appended.
+ */
+void window_set_bottom_bar_realtext(ledit_window *window, char *text, int len);
+
+/*
* Get the text of the editable line.
*/
char *window_get_bottom_bar_text(ledit_window *window);
You are viewing proxied material from lumidify.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.