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:
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
@@ -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);