tAdd very basic search functionality and start working on commands - ledit - Te… | |
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 e181351df92df2596bd48578667ce195f4cf34d0 | |
parent b8f2762e5d7b778ba858d790f40c85aeab2f57ab | |
Author: lumidify <[email protected]> | |
Date: Wed, 26 May 2021 21:39:22 +0200 | |
Add very basic search functionality and start working on commands | |
Diffstat: | |
M Makefile | 4 ++-- | |
M buffer.c | 31 ++++++++++++++++++++++++++---… | |
A commands.c | 68 +++++++++++++++++++++++++++++… | |
M common.h | 6 +++++- | |
M ledit.c | 188 ++++++++++++++++++++++++++++-… | |
A search.c | 171 +++++++++++++++++++++++++++++… | |
A search.h | 13 +++++++++++++ | |
7 files changed, 459 insertions(+), 22 deletions(-) | |
--- | |
diff --git a/Makefile b/Makefile | |
t@@ -9,8 +9,8 @@ MANPREFIX = ${PREFIX}/man | |
BIN = ${NAME} | |
MAN1 = ${BIN:=.1} | |
-OBJ = ${BIN:=.o} cache.o buffer.o memory.o util.o | |
-HDR = cache.h buffer.h memory.h common.h util.h | |
+OBJ = ${BIN:=.o} cache.o buffer.o memory.o util.o search.o | |
+HDR = cache.h buffer.h memory.h common.h util.h search.h | |
CFLAGS_LEDIT = -g -Wall -Wextra -D_POSIX_C_SOURCE=200809L `pkg-config --cflags… | |
LDFLAGS_LEDIT = ${LDFLAGS} `pkg-config --libs x11 xkbfile pangoxft xext` -lm | |
diff --git a/buffer.c b/buffer.c | |
t@@ -105,14 +105,16 @@ ledit_insert_text(ledit_buffer *buffer, int line_index, … | |
ledit_line *line = &buffer->lines[line_index]; | |
if (len == -1) | |
len = strlen(text); | |
- if (line->len + len > line->cap || line->text == NULL) { | |
+ /* \0 is not included in line->len */ | |
+ if (line->len + len + 1 > line->cap || line->text == NULL) { | |
/* FIXME: read up on what the best values are here */ | |
- line->cap = line->cap * 2 > line->len + len ? line->cap * 2 : … | |
+ line->cap = line->cap * 2 > line->len + len + 1 ? line->cap * … | |
line->text = ledit_realloc(line->text, line->cap); | |
} | |
memmove(line->text + index + len, line->text + index, line->len - inde… | |
memcpy(line->text + index, text, len); | |
line->len += len; | |
+ line->text[line->len] = '\0'; | |
pango_layout_set_text(line->layout, line->text, line->len); | |
/*recalc_single_line_size(buffer, line_index);*/ | |
line->dirty = 1; | |
t@@ -120,6 +122,16 @@ ledit_insert_text(ledit_buffer *buffer, int line_index, i… | |
static void append_line_impl(ledit_buffer *buffer, int line_index, int text_in… | |
+/* FIXME: this isn't optimized like the standard version, but whatever */ | |
+static char * | |
+strchr_len(char *text, char c, int len) { | |
+ for (int i = 0; i < len; i++) { | |
+ if (text[i] == c) | |
+ return text + i; | |
+ } | |
+ return NULL; | |
+} | |
+ | |
void | |
ledit_insert_text_with_newlines( | |
ledit_buffer *buffer, | |
t@@ -128,16 +140,18 @@ ledit_insert_text_with_newlines( | |
int *end_line_ret, int *end_char_ret) { | |
if (len == -1) | |
len = strlen(text); | |
+ int rem_len = len; | |
char *cur, *last = text; | |
int cur_line = line_index; | |
int cur_index = index; | |
- while ((cur = strchr(last, '\n'))) { | |
+ while ((cur = strchr_len(last, '\n', rem_len)) != NULL) { | |
ledit_insert_text(buffer, cur_line, cur_index, last, cur - las… | |
/* FIXME: inefficient because there's no gap buffer yet */ | |
append_line_impl(buffer, cur_line, -1); | |
cur_index = 0; | |
cur_line++; | |
last = cur + 1; | |
+ rem_len -= cur - last + 1; | |
} | |
/* FIXME: check how legal this casting between pointers and ints is */ | |
ledit_insert_text(buffer, cur_line, cur_index, last, text + len - last… | |
t@@ -193,8 +207,10 @@ init_line(ledit_buffer *buffer, ledit_line *line) { | |
pango_layout_set_font_description(line->layout, buffer->state->font); | |
pango_layout_set_wrap(line->layout, PANGO_WRAP_WORD_CHAR); | |
pango_layout_set_attributes(line->layout, basic_attrs); | |
- line->text = NULL; | |
- line->cap = line->len = 0; | |
+ line->cap = 2; /* arbitrary */ | |
+ line->text = ledit_malloc(line->cap); | |
+ line->text[0] = '\0'; | |
+ line->len = 0; | |
line->cache_index = -1; | |
line->dirty = 1; | |
/* FIXME: does this set line height reasonably when no text yet? */ | |
t@@ -223,13 +239,16 @@ append_line_impl(ledit_buffer *buffer, int line_index, i… | |
init_line(buffer, new_l); | |
buffer->lines_num++; | |
if (text_index != -1) { | |
+ /* FIXME: use ledit_insert... here */ | |
ledit_line *l = &buffer->lines[line_index]; | |
int len = l->len - text_index; | |
new_l->len = len; | |
new_l->cap = len + 10; | |
new_l->text = ledit_malloc(new_l->cap); | |
memcpy(new_l->text, l->text + text_index, len); | |
+ new_l->text[new_l->len] = '\0'; | |
l->len = text_index; | |
+ l->text[l->len] = '\0'; | |
pango_layout_set_text(new_l->layout, new_l->text, new_l->len); | |
pango_layout_set_text(l->layout, l->text, l->len); | |
/* FIXME: set height here */ | |
t@@ -333,6 +352,7 @@ ledit_delete_unicode_char(ledit_buffer *buffer, int line_i… | |
memmove(l->text + byte_index, l->text + i, l->len - i); | |
l->len -= i - byte_index; | |
} | |
+ l->text[l->len] = '\0'; | |
pango_layout_set_text(l->layout, l->text, l->len); | |
recalc_single_line_size(buffer, line_index); | |
return new_index; | |
t@@ -343,6 +363,7 @@ delete_line_section(ledit_buffer *buffer, int line, int st… | |
ledit_line *l = &buffer->lines[line]; | |
memmove(l->text + start, l->text + start + length, l->len - start - le… | |
l->len -= length; | |
+ l->text[l->len] = '\0'; | |
pango_layout_set_text(l->layout, l->text, l->len); | |
recalc_single_line_size(buffer, line); | |
l->dirty = 1; | |
diff --git a/commands.c b/commands.c | |
t@@ -0,0 +1,68 @@ | |
+/* FIXME: Parse commands properly and allow combinations of commands */ | |
+ | |
+static int | |
+handle_write_quit(ledit_buffer *buffer, char *cmd, int l1, int l2) { | |
+ return 0; | |
+} | |
+ | |
+static int | |
+handle_substitute(ledit_buffer *buffer, char *cmd, int l1, int l2) { | |
+ return 0; | |
+} | |
+ | |
+enum cmd_type { | |
+ CMD_NORMAL, | |
+ CMD_OPTIONAL_RANGE | |
+}; | |
+ | |
+static const struct { | |
+ char *cmd; | |
+ enum cmd_type type; | |
+ int (*handler)(ledit_buffer *buffer, char *cmd, int l1, int l2); | |
+} cmds[] = { | |
+ {"w", CMD_NORMAL, &handle_write_quit}, | |
+ {"q", CMD_NORMAL, &handle_write_quit}, | |
+ {"s", CMD_OPTIONAL_RANGE, &handle_substitute} | |
+}; | |
+ | |
+#define LENGTH(X) (sizeof(X) / sizeof(X[0])) | |
+ | |
+int | |
+ledit_handle_cmd(ledit_buffer *buffer, char *cmd, int len) { | |
+ if (len < 0) | |
+ len = strlen(cmd); | |
+ if (len < 1) | |
+ return; | |
+ char *cur_pos = cmd; | |
+ int l1 = buffer->cur_line; | |
+ int l2 = buffer->cur_line; | |
+ int range_given = 0; | |
+ switch (cur_pos[0]) { | |
+ case '%': | |
+ l1 = 0; | |
+ l2 = buffer->lines_num - 1; | |
+ range_given = 1; | |
+ cur_pos++; | |
+ break; | |
+ case '&' | |
+ l1 = buffer->sel.line1; | |
+ l2 = buffer->sel.line2; | |
+ if (l1 < 0 || l2 < 0) | |
+ return 1; | |
+ if (l1 < l2) { | |
+ int tmp = l1; | |
+ l1 = l2; | |
+ l2 = tmp; | |
+ } | |
+ range_given = 1; | |
+ cur_pos++; | |
+ break | |
+ } | |
+ for (int i = 0; i < LENGTH(cmds); i++) { | |
+ if (!strncmp(cmds[i].cmd, cur_pos, strlen(cmds[i].cmd)) && | |
+ (!range_given || cmds[i].type == CMD_NORMAL)) { | |
+ return cmds[i].handler(buffer, cur_pos, l1, l2); | |
+ } | |
+ } | |
+ return 1; | |
+} | |
diff --git a/common.h b/common.h | |
t@@ -1,7 +1,9 @@ | |
enum ledit_mode { | |
NORMAL = 1, | |
INSERT = 2, | |
- VISUAL = 4 | |
+ VISUAL = 4, | |
+ COMMANDEDIT = 8, | |
+ SEARCHEDIT = 16 | |
}; | |
typedef struct { | |
t@@ -24,6 +26,8 @@ typedef struct { | |
int scroll_dragging; | |
int scroll_grab_handle; | |
int selecting; | |
+ int bottom_text_shown; | |
+ int message_shown; | |
enum ledit_mode mode; | |
XIM xim; | |
XIC xic; | |
diff --git a/ledit.c b/ledit.c | |
t@@ -1,3 +1,4 @@ | |
+/* FIXME: overflow in repeated commands */ | |
/* FIXME: Fix lag when scrolling */ | |
/* FIXME: Fix lag when selecting with mouse */ | |
/* FIXME: Use PANGO_PIXELS() */ | |
t@@ -8,6 +9,7 @@ | |
#include <math.h> | |
#include <stdio.h> | |
#include <errno.h> | |
+#include <assert.h> | |
#include <string.h> | |
#include <stdlib.h> | |
#include <limits.h> | |
t@@ -29,6 +31,7 @@ | |
#include "memory.h" | |
#include "common.h" | |
#include "buffer.h" | |
+#include "search.h" | |
#include "cache.h" | |
#include "util.h" | |
t@@ -52,6 +55,12 @@ struct { | |
PangoLayout *ruler; | |
ledit_draw *ruler_draw; | |
int ruler_w, ruler_h; | |
+ PangoLayout *line; | |
+ ledit_draw *line_draw; | |
+ int line_w, line_h; | |
+ char *line_text; | |
+ int line_alloc, line_len; | |
+ int line_cur_pos; | |
} bottom_bar; | |
struct key { | |
t@@ -142,13 +151,64 @@ static void get_new_line_softline( | |
#define SCROLLBAR_WIDTH 10 | |
#define SCROLL_STEP 10 | |
+/* FIXME: shouldn't state.bottom_text_shown also be true when message_shown? */ | |
static void | |
recalc_text_size(void) { | |
int bar_h = bottom_bar.mode_h; | |
+ if ((state.bottom_text_shown || state.message_shown) && bottom_bar.lin… | |
+ bar_h = bottom_bar.line_h; | |
state.text_w = state.w - SCROLLBAR_WIDTH; | |
state.text_h = state.h - bar_h; | |
} | |
+/* FIXME: allow lines longer than window width to be displayed properly */ | |
+static void | |
+insert_bottom_bar_text(char *text, int len) { | |
+ assert(len >= -1); | |
+ assert(bottom_bar.line_cur_pos <= bottom_bar.line_alloc); | |
+ | |
+ if (len == -1) | |
+ len = strlen(text); | |
+ /* \0 not included in len */ | |
+ if (bottom_bar.line_len + len + 1 > bottom_bar.line_alloc || bottom_ba… | |
+ /* FIXME: read up on what the best values are here */ | |
+ bottom_bar.line_alloc = | |
+ bottom_bar.line_alloc * 2 > bottom_bar.line_len + len + 1 ? | |
+ bottom_bar.line_alloc * 2 : | |
+ bottom_bar.line_len + len + 1; | |
+ bottom_bar.line_text = ledit_realloc(bottom_bar.line_text, bot… | |
+ } | |
+ memmove( | |
+ bottom_bar.line_text + bottom_bar.line_cur_pos + len, | |
+ bottom_bar.line_text + bottom_bar.line_cur_pos, | |
+ bottom_bar.line_len - bottom_bar.line_cur_pos | |
+ ); | |
+ memcpy(bottom_bar.line_text + bottom_bar.line_cur_pos, text, len); | |
+ bottom_bar.line_len += len; | |
+ bottom_bar.line_text[bottom_bar.line_len] = '\0'; | |
+ pango_layout_set_text(bottom_bar.line, bottom_bar.line_text, bottom_ba… | |
+ pango_layout_get_pixel_size(bottom_bar.line, &bottom_bar.line_w, &bott… | |
+ ledit_grow_draw(&state, bottom_bar.line_draw, bottom_bar.line_w, botto… | |
+ XftDrawRect(bottom_bar.line_draw->xftdraw, &state.bg, 0, 0, bottom_bar… | |
+ pango_xft_render_layout(bottom_bar.line_draw->xftdraw, &state.fg, bott… | |
+ recalc_text_size(); | |
+} | |
+ | |
+static void | |
+set_bottom_bar_text(char *text, int len) { | |
+ bottom_bar.line_len = 0; | |
+ bottom_bar.line_cur_pos = 0; | |
+ insert_bottom_bar_text(text, len); | |
+} | |
+ | |
+static void | |
+show_message(char *text, int len) { | |
+ set_bottom_bar_text(text, len); | |
+ /* FIXME: rename these */ | |
+ state.bottom_text_shown = 0; | |
+ state.message_shown = 2; | |
+} | |
+ | |
/* clipboard handling largely stolen from st (simple terminal) */ | |
#define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit))) | |
t@@ -181,7 +241,7 @@ set_mode(enum ledit_mode mode) { | |
ledit_grow_draw(&state, bottom_bar.mode_draw, bottom_bar.mode_w, botto… | |
XftDrawRect(bottom_bar.mode_draw->xftdraw, &state.bg, 0, 0, bottom_bar… | |
pango_xft_render_layout(bottom_bar.mode_draw->xftdraw, &state.fg, bott… | |
- recalc_text_size(); /* probably not necessary, but whatever */ | |
+ recalc_text_size(); | |
} | |
void | |
t@@ -885,6 +945,15 @@ setup(int argc, char *argv[]) { | |
/* FIXME: only create "dummy draw" at first and create with proper siz… | |
bottom_bar.mode_draw = ledit_create_draw(&state, 10, 10); | |
set_mode(INSERT); | |
+ bottom_bar.line = pango_layout_new(state.context); | |
+ pango_layout_set_font_description(bottom_bar.line, state.font); | |
+ bottom_bar.line_draw = ledit_create_draw(&state, 10, 10); | |
+ bottom_bar.line_w = bottom_bar.line_h = 10; | |
+ bottom_bar.line_text = NULL; | |
+ bottom_bar.line_alloc = bottom_bar.line_len = 0; | |
+ bottom_bar.line_cur_pos = 0; | |
+ state.bottom_text_shown = 0; | |
+ state.message_shown = 0; | |
XMapWindow(state.dpy, state.win); | |
t@@ -908,6 +977,7 @@ setup(int argc, char *argv[]) { | |
static void | |
cleanup(void) { | |
/* FIXME: cleanup everything else */ | |
+ ledit_cleanup_search(); | |
ledit_destroy_cache(); | |
ledit_destroy_buffer(buffer); | |
XDestroyWindow(state.dpy, state.win); | |
t@@ -1006,12 +1076,21 @@ redraw(void) { | |
0, state.text_h, | |
state.w, state.h - state.text_h | |
); | |
- XCopyArea( | |
- state.dpy, bottom_bar.mode_draw->pixmap, | |
- state.drawable, state.gc, | |
- 0, 0, bottom_bar.mode_w, bottom_bar.mode_h, | |
- state.w - bottom_bar.mode_w, state.text_h | |
- ); | |
+ if (state.bottom_text_shown || state.message_shown) { | |
+ XCopyArea( | |
+ state.dpy, bottom_bar.line_draw->pixmap, | |
+ state.drawable, state.gc, | |
+ 0, 0, bottom_bar.line_w, bottom_bar.line_h, | |
+ 0, state.text_h | |
+ ); | |
+ } else { | |
+ XCopyArea( | |
+ state.dpy, bottom_bar.mode_draw->pixmap, | |
+ state.drawable, state.gc, | |
+ 0, 0, bottom_bar.mode_w, bottom_bar.mode_h, | |
+ state.w - bottom_bar.mode_w, state.text_h | |
+ ); | |
+ } | |
XdbeSwapInfo swap_info; | |
swap_info.swap_window = state.win; | |
t@@ -1582,6 +1661,71 @@ switch_selection_end(void) { | |
#define XK_ANY_MOD UINT_MAX | |
#define XK_NO_MOD 0 | |
+static void | |
+enter_commandedit(void) { | |
+ set_bottom_bar_text(":", -1); | |
+ bottom_bar.line_cur_pos = 1; | |
+ state.mode = COMMANDEDIT; | |
+ state.bottom_text_shown = 1; | |
+} | |
+ | |
+static void | |
+enter_searchedit_forward(void) { | |
+ set_bottom_bar_text("/", -1); | |
+ bottom_bar.line_cur_pos = 1; | |
+ state.mode = SEARCHEDIT; | |
+ state.bottom_text_shown = 1; | |
+} | |
+ | |
+static void | |
+enter_searchedit_backward(void) { | |
+ set_bottom_bar_text("?", -1); | |
+ bottom_bar.line_cur_pos = 1; | |
+ state.mode = SEARCHEDIT; | |
+ state.bottom_text_shown = 1; | |
+} | |
+ | |
+/* FIXME: support visual mode, i.e. change selection to new place? */ | |
+static void | |
+search_next(void) { | |
+ /* FIXME: avoid this when line doesn't change */ | |
+ ledit_wipe_line_cursor_attrs(buffer, buffer->cur_line); | |
+ enum ledit_search_state ret = ledit_search_next(buffer, &buffer->cur_l… | |
+ ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_inde… | |
+ if (ret != SEARCH_NORMAL) | |
+ show_message(ledit_search_state_to_str(ret), -1); | |
+} | |
+ | |
+static void | |
+search_prev(void) { | |
+ /* FIXME: avoid this when line doesn't change */ | |
+ ledit_wipe_line_cursor_attrs(buffer, buffer->cur_line); | |
+ enum ledit_search_state ret = ledit_search_prev(buffer, &buffer->cur_l… | |
+ ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_inde… | |
+ if (ret != SEARCH_NORMAL) | |
+ show_message(ledit_search_state_to_str(ret), -1); | |
+} | |
+ | |
+static void | |
+end_lineedit(void) { | |
+ enum ledit_mode old_mode = state.mode; | |
+ /* FIXME: go to last mode (visual or normal) */ | |
+ set_mode(NORMAL); | |
+ state.bottom_text_shown = 0; | |
+ | |
+ /* FIXME: forward/backward; check for empty string; | |
+ end edit mode when / or ? is deleted with backspace */ | |
+ if (old_mode == SEARCHEDIT) { | |
+ /* FIXME: this is all so horrible */ | |
+ if (bottom_bar.line_text[0] == '/') | |
+ ledit_set_search_forward(bottom_bar.line_text + 1); | |
+ else | |
+ ledit_set_search_backward(bottom_bar.line_text + 1); | |
+ search_next(); | |
+ } | |
+} | |
+ | |
+/* FIXME: maybe sort these and use binary search */ | |
static struct key keys_en[] = { | |
{NULL, 0, XK_BackSpace, INSERT, KEY_ANY, KEY_ANY, &backspace}, | |
{NULL, 0, XK_Left, VISUAL|INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_lef… | |
t@@ -1612,7 +1756,13 @@ static struct key keys_en[] = { | |
{"v", 0, 0, NORMAL, KEY_ANY, KEY_ANY, &enter_visual}, | |
{"o", 0, 0, VISUAL, KEY_ANY, KEY_ANY, &switch_selection_end}, | |
{"c", ControlMask, 0, INSERT|VISUAL, KEY_ANY, KEY_ANY, &clipcopy}, | |
- {"v", ControlMask, 0, INSERT, KEY_ANY, KEY_ANY, &clippaste} | |
+ {"v", ControlMask, 0, INSERT, KEY_ANY, KEY_ANY, &clippaste}, | |
+ {":", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_ANY, &enter_commandedit}, | |
+ {"?", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_ANY, &enter_searchedit_backwa… | |
+ {"/", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_ANY, &enter_searchedit_forwar… | |
+ {NULL, 0, XK_Return, COMMANDEDIT|SEARCHEDIT, KEY_ANY, KEY_ANY, &end_li… | |
+ {"n", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_ANY, &search_next}, | |
+ {"N", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_ANY, &search_prev} | |
}; | |
static struct key keys_ur[] = { | |
t@@ -1720,7 +1870,9 @@ key_press(XEvent event) { | |
(cur_keys->keys[i].modes & state.mode) && | |
(!e || (e->key & cur_keys->keys[i].prev_keys)) && | |
!strncmp(cur_keys->keys[i].text, buf, n) && | |
- match(cur_keys->keys[i].mods, key_state)) { | |
+ match(cur_keys->keys[i].mods, key_state & ~ShiftM… | |
+ /* FIXME: seems a bit hacky to remove shift, b… | |
+ is needed to make keys that use shift match… | |
cur_keys->keys[i].func(); | |
found = 1; | |
} | |
t@@ -1730,10 +1882,14 @@ key_press(XEvent event) { | |
cur_keys->keys[i].func(); | |
found = 1; | |
} | |
- if (found) | |
- break; | |
} | |
- if (state.mode == INSERT && !found && n > 0) { | |
+ if (found) { | |
+ /* FIXME: only do this when necessary */ | |
+ ensure_cursor_shown(); | |
+ /* FIXME: this is a bit hacky */ | |
+ if (state.message_shown > 0) | |
+ state.message_shown--; | |
+ } else if (state.mode == INSERT && !found && n > 0) { | |
delete_selection(); | |
ledit_insert_text_with_newlines( | |
buffer, | |
t@@ -1741,7 +1897,11 @@ key_press(XEvent event) { | |
buf, n, | |
&buffer->cur_line, &buffer->cur_index | |
); | |
+ ensure_cursor_shown(); | |
+ if (state.message_shown > 0) | |
+ state.message_shown--; | |
+ } else if (!found && (state.mode == COMMANDEDIT || state.mode == SEARC… | |
+ insert_bottom_bar_text(buf, n); | |
+ bottom_bar.line_cur_pos += n; | |
} | |
- /* FIXME: only do this when necessary */ | |
- ensure_cursor_shown(); | |
} | |
diff --git a/search.c b/search.c | |
t@@ -0,0 +1,171 @@ | |
+#include <string.h> | |
+#include <X11/Xlib.h> | |
+#include <X11/Xutil.h> | |
+#include <pango/pangoxft.h> | |
+#include <X11/extensions/Xdbe.h> | |
+ | |
+#include "memory.h" | |
+#include "common.h" | |
+#include "buffer.h" | |
+#include "search.h" | |
+ | |
+/* FIXME: make sure only whole utf8 chars are matched */ | |
+/* FIXME: clean this up */ | |
+char *last_search = NULL; | |
+enum { | |
+ FORWARD, | |
+ BACKWARD | |
+} last_dir = FORWARD; | |
+ | |
+void | |
+ledit_set_search_forward(char *pattern) { | |
+ last_dir = FORWARD; | |
+ free(last_search); | |
+ last_search = ledit_strdup(pattern); | |
+} | |
+ | |
+void | |
+ledit_cleanup_search(void) { | |
+ free(last_search); | |
+} | |
+ | |
+void | |
+ledit_set_search_backward(char *pattern) { | |
+ last_dir = BACKWARD; | |
+ free(last_search); | |
+ last_search = ledit_strdup(pattern); | |
+} | |
+ | |
+static enum ledit_search_state | |
+search_forward(ledit_buffer *buffer, int *line_ret, int *byte_ret) { | |
+ *line_ret = buffer->cur_line; | |
+ *byte_ret = buffer->cur_index; | |
+ if (last_search == NULL) | |
+ return SEARCH_NO_PATTERN; | |
+ int line = buffer->cur_line; | |
+ /* start one byte later so it doesn't get stuck on a match | |
+ note: since the string ends with '\0', this is always valid */ | |
+ int byte = buffer->cur_index + 1; | |
+ char *res; | |
+ ledit_line *lline = ledit_get_line(buffer, line); | |
+ if ((res = strstr(lline->text + byte, last_search)) != NULL) { | |
+ *line_ret = line; | |
+ *byte_ret = (int)(res - lline->text); | |
+ return SEARCH_NORMAL; | |
+ } | |
+ for (int i = line + 1; i < buffer->lines_num; i++) { | |
+ lline = ledit_get_line(buffer, i); | |
+ if ((res = strstr(lline->text, last_search)) != NULL) { | |
+ *line_ret = i; | |
+ *byte_ret = (int)(res - lline->text); | |
+ return SEARCH_NORMAL; | |
+ } | |
+ } | |
+ for (int i = 0; i < line; i++) { | |
+ lline = ledit_get_line(buffer, i); | |
+ if ((res = strstr(lline->text, last_search)) != NULL) { | |
+ *line_ret = i; | |
+ *byte_ret = (int)(res - lline->text); | |
+ return SEARCH_WRAPPED; | |
+ } | |
+ } | |
+ lline = ledit_get_line(buffer, line); | |
+ if ((res = strstr(lline->text, last_search)) != NULL) { | |
+ *line_ret = line; | |
+ *byte_ret = (int)(res - lline->text); | |
+ return SEARCH_WRAPPED; | |
+ } | |
+ return SEARCH_NOT_FOUND; | |
+} | |
+ | |
+/* FIXME: this is insanely inefficient */ | |
+static enum ledit_search_state | |
+search_backward(ledit_buffer *buffer, int *line_ret, int *byte_ret) { | |
+ *line_ret = buffer->cur_line; | |
+ *byte_ret = buffer->cur_index; | |
+ if (last_search == NULL) | |
+ return SEARCH_NO_PATTERN; | |
+ int line = buffer->cur_line; | |
+ int byte = buffer->cur_index; | |
+ ledit_line *lline = ledit_get_line(buffer, line); | |
+ char *last = NULL, *res = lline->text; | |
+ while ((res = strstr(res, last_search)) != NULL && res < lline->text +… | |
+ last = res; | |
+ res++; | |
+ } | |
+ if (last != NULL) { | |
+ *line_ret = line; | |
+ /* FIXME: check if this is safe */ | |
+ *byte_ret = (int)(last - lline->text); | |
+ return SEARCH_NORMAL; | |
+ } | |
+ for (int i = line - 1; i >= 0; i--) { | |
+ lline = ledit_get_line(buffer, i); | |
+ res = lline->text; | |
+ while ((res = strstr(res, last_search)) != NULL) { | |
+ last = res; | |
+ res++; | |
+ } | |
+ if (last != NULL) { | |
+ *line_ret = i; | |
+ *byte_ret = (int)(last - lline->text); | |
+ return SEARCH_NORMAL; | |
+ } | |
+ } | |
+ for (int i = buffer->lines_num - 1; i > line; i--) { | |
+ lline = ledit_get_line(buffer, i); | |
+ res = lline->text; | |
+ while ((res = strstr(res, last_search)) != NULL) { | |
+ last = res; | |
+ res++; | |
+ } | |
+ if (last != NULL) { | |
+ *line_ret = i; | |
+ *byte_ret = (int)(last - lline->text); | |
+ return SEARCH_WRAPPED; | |
+ } | |
+ } | |
+ lline = ledit_get_line(buffer, line); | |
+ res = lline->text + byte; | |
+ while ((res = strstr(res, last_search)) != NULL) { | |
+ last = res; | |
+ res++; | |
+ } | |
+ if (last != NULL) { | |
+ *line_ret = line; | |
+ *byte_ret = (int)(last - lline->text); | |
+ return SEARCH_WRAPPED; | |
+ } | |
+ return SEARCH_NOT_FOUND; | |
+} | |
+ | |
+enum ledit_search_state | |
+ledit_search_next(ledit_buffer *buffer, int *line_ret, int *byte_ret) { | |
+ if (last_dir == FORWARD) | |
+ return search_forward(buffer, line_ret, byte_ret); | |
+ else | |
+ return search_backward(buffer, line_ret, byte_ret); | |
+} | |
+ | |
+enum ledit_search_state | |
+ledit_search_prev(ledit_buffer *buffer, int *line_ret, int *byte_ret) { | |
+ if (last_dir == FORWARD) | |
+ return search_backward(buffer, line_ret, byte_ret); | |
+ else | |
+ return search_forward(buffer, line_ret, byte_ret); | |
+} | |
+ | |
+char * | |
+ledit_search_state_to_str(enum ledit_search_state state) { | |
+ switch (state) { | |
+ case SEARCH_WRAPPED: | |
+ return "Search wrapped"; | |
+ case SEARCH_NOT_FOUND: | |
+ return "Pattern not found"; | |
+ case SEARCH_NO_PATTERN: | |
+ return "No previous search pattern"; | |
+ default: | |
+ return "This message should not be shown. " | |
+ "Please bug lumidify about it."; | |
+ } | |
+} | |
diff --git a/search.h b/search.h | |
t@@ -0,0 +1,13 @@ | |
+enum ledit_search_state { | |
+ SEARCH_NORMAL, | |
+ SEARCH_WRAPPED, | |
+ SEARCH_NOT_FOUND, | |
+ SEARCH_NO_PATTERN | |
+}; | |
+ | |
+void ledit_cleanup_search(void); | |
+void ledit_set_search_forward(char *pattern); | |
+void ledit_set_search_backward(char *pattern); | |
+enum ledit_search_state ledit_search_next(ledit_buffer *buffer, int *line_ret,… | |
+enum ledit_search_state ledit_search_prev(ledit_buffer *buffer, int *line_ret,… | |
+char *ledit_search_state_to_str(enum ledit_search_state state); |