ledit

Text editor (WIP)
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 <nobody@lumidify.org>
Date:   Wed, 26 May 2021 21:39:22 +0200

Add very basic search functionality and start working on commands

Diffstat:
MMakefile | 4++--
Mbuffer.c | 31++++++++++++++++++++++++++-----
Acommands.c | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcommon.h | 6+++++-
Mledit.c | 188+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Asearch.c | 171+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asearch.h | 13+++++++++++++
7 files changed, 459 insertions(+), 22 deletions(-)

diff --git a/Makefile b/Makefile @@ -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 x11 xkbfile pangoxft xext` LDFLAGS_LEDIT = ${LDFLAGS} `pkg-config --libs x11 xkbfile pangoxft xext` -lm diff --git a/buffer.c b/buffer.c @@ -105,14 +105,16 @@ ledit_insert_text(ledit_buffer *buffer, int line_index, int index, char *text, i 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->len + len; + line->cap = line->cap * 2 > line->len + len + 1 ? line->cap * 2 : line->len + len + 1; line->text = ledit_realloc(line->text, line->cap); } memmove(line->text + index + len, line->text + index, line->len - index); 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; @@ -120,6 +122,16 @@ ledit_insert_text(ledit_buffer *buffer, int line_index, int index, char *text, i static void append_line_impl(ledit_buffer *buffer, int line_index, int text_index); +/* 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, @@ -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 - last); /* 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); @@ -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? */ @@ -223,13 +239,16 @@ append_line_impl(ledit_buffer *buffer, int line_index, int text_index) { 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 */ @@ -333,6 +352,7 @@ ledit_delete_unicode_char(ledit_buffer *buffer, int line_index, int byte_index, 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; @@ -343,6 +363,7 @@ delete_line_section(ledit_buffer *buffer, int line, int start, int length) { ledit_line *l = &buffer->lines[line]; memmove(l->text + start, l->text + start + length, l->len - start - length); 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 @@ -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 @@ -1,7 +1,9 @@ enum ledit_mode { NORMAL = 1, INSERT = 2, - VISUAL = 4 + VISUAL = 4, + COMMANDEDIT = 8, + SEARCHEDIT = 16 }; typedef struct { @@ -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 @@ -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() */ @@ -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> @@ -29,6 +31,7 @@ #include "memory.h" #include "common.h" #include "buffer.h" +#include "search.h" #include "cache.h" #include "util.h" @@ -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 { @@ -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.line_h > bar_h) + 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_bar.line_text == NULL) { + /* 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, bottom_bar.line_alloc); + } + 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_bar.line_len); + pango_layout_get_pixel_size(bottom_bar.line, &bottom_bar.line_w, &bottom_bar.line_h); + ledit_grow_draw(&state, bottom_bar.line_draw, bottom_bar.line_w, bottom_bar.line_h); + XftDrawRect(bottom_bar.line_draw->xftdraw, &state.bg, 0, 0, bottom_bar.line_w, bottom_bar.line_h); + pango_xft_render_layout(bottom_bar.line_draw->xftdraw, &state.fg, bottom_bar.line, 0, 0); + 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))) @@ -181,7 +241,7 @@ set_mode(enum ledit_mode mode) { ledit_grow_draw(&state, bottom_bar.mode_draw, bottom_bar.mode_w, bottom_bar.mode_h); XftDrawRect(bottom_bar.mode_draw->xftdraw, &state.bg, 0, 0, bottom_bar.mode_w, bottom_bar.mode_h); pango_xft_render_layout(bottom_bar.mode_draw->xftdraw, &state.fg, bottom_bar.mode, 0, 0); - recalc_text_size(); /* probably not necessary, but whatever */ + recalc_text_size(); } void @@ -885,6 +945,15 @@ setup(int argc, char *argv[]) { /* FIXME: only create "dummy draw" at first and create with proper size when needed */ 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); @@ -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); @@ -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; @@ -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_line, &buffer->cur_index); + ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index); + 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_line, &buffer->cur_index); + ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index); + 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_left}, @@ -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_backward}, + {"/", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_ANY, &enter_searchedit_forward}, + {NULL, 0, XK_Return, COMMANDEDIT|SEARCHEDIT, KEY_ANY, KEY_ANY, &end_lineedit}, + {"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[] = { @@ -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 & ~ShiftMask)) { + /* FIXME: seems a bit hacky to remove shift, but it + is needed to make keys that use shift match */ cur_keys->keys[i].func(); found = 1; } @@ -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, @@ -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 == SEARCHEDIT) && n > 0) { + 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 @@ -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 + byte) { + 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 @@ -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, int *byte_ret); +enum ledit_search_state ledit_search_prev(ledit_buffer *buffer, int *line_ret, int *byte_ret); +char *ledit_search_state_to_str(enum ledit_search_state state);