evil-qemacs/search.c
Charlie Gordon a3fac89268 Add standard bindings, improve consistency
- `C-g` invokes keyboard-quit
- bind `ESC ESC ESC` to keyboard-quit
- prevent ESC key combination in `combine_keys()` if not first
- rename `abort` as `keyboard-quit` and do_break() as do_keyboard_quit()
- rename do_combine_char() as do_combine_accent()
- rename do_word_right() as do_word_left_right()
- pass numeric argument to do_scroll_left_right()
- pass numeric argument to do_minibuffer_history()
2020-11-08 00:47:24 +01:00

956 lines
29 KiB
C

/*
* QEmacs, tiny but powerful multimode editor
*
* Copyright (c) 2000-2002 Fabrice Bellard.
* Copyright (c) 2000-2020 Charlie Gordon.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "qe.h"
#include "variables.h"
/* Search stuff */
#define SEARCH_FLAG_SMARTCASE 0x0001 /* case fold unless upper case present */
#define SEARCH_FLAG_IGNORECASE 0x0002
#define SEARCH_FLAG_WORD 0x0004
#define SEARCH_FLAG_WRAPPED 0x0008
#define SEARCH_FLAG_HEX 0x0010
#define SEARCH_FLAG_UNIHEX 0x0020
#define SEARCH_FLAG_REGEX 0x0040
/* should separate search string length and number of match positions */
#define SEARCH_LENGTH 256
#define FOUND_TAG 0x80000000
#define FOUND_REV 0x40000000
struct ISearchState {
EditState *s;
int saved_mark;
int start_offset;
int start_dir;
int quoting;
int dir;
int pos; /* position in search_u32_flags */
int search_u32_len;
int search_flags;
int found_offset, found_end;
unsigned int search_u32_flags[SEARCH_LENGTH];
unsigned int search_u32[SEARCH_LENGTH];
};
/* XXX: should store to screen */
ISearchState isearch_state;
/* last searched string */
/* XXX: should store in a buffer as a list */
static unsigned int last_search_u32[SEARCH_LENGTH];
static int last_search_u32_len = 0;
static int last_search_u32_flags = 0;
static int eb_search(EditBuffer *b, int dir, int flags,
int start_offset, int end_offset,
const unsigned int *buf, int len,
CSSAbortFunc *abort_func, void *abort_opaque,
int *found_offset, int *found_end)
{
int total_size = b->total_size;
int c, c2, offset = start_offset, offset1, offset2, offset3, pos;
if (len == 0)
return 0;
if (end_offset > total_size)
end_offset = total_size;
*found_offset = -1;
*found_end = -1;
/* analyze buffer if smart case */
if (flags & SEARCH_FLAG_SMARTCASE) {
int upper_count = 0;
int lower_count = 0;
for (pos = 0; pos < len; pos++) {
lower_count += qe_islower(buf[pos]);
upper_count += qe_isupper(buf[pos]);
}
if (lower_count > 0 && upper_count == 0)
flags |= SEARCH_FLAG_IGNORECASE;
}
if (flags & SEARCH_FLAG_HEX) {
/* handle buffer as single bytes */
/* XXX: should handle ucs2 and ucs4 as words */
if (dir >= 0)
offset--;
for (;;) {
if (dir < 0) {
if (offset == 0)
return 0;
offset--;
} else {
offset++;
if (offset >= end_offset)
return 0;
}
if ((offset & 0xfffff) == 0) {
/* check for search abort every megabyte */
if (abort_func && abort_func(abort_opaque))
return -1;
}
pos = 0;
for (offset2 = offset; offset2 < total_size;) {
/* CG: Should bufferize a bit ? */
c = eb_read_one_byte(b, offset2++);
c2 = buf[pos++];
if (c != c2)
break;
if (pos >= len) {
if (dir >= 0 || offset2 <= start_offset) {
*found_offset = offset;
*found_end = offset2;
return 1;
}
}
}
}
}
for (offset1 = offset;;) {
if (dir < 0) {
if (offset == 0)
return 0;
offset = eb_prev(b, offset);
} else {
offset = offset1;
if (offset >= end_offset)
return 0;
}
if ((offset & 0xfffff) == 0) {
/* check for search abort every megabyte */
if (abort_func && abort_func(abort_opaque))
return -1;
}
/* CG: XXX: Should use buffer specific accelerator */
/* Get first char separately to compute offset1 */
c = eb_nextc(b, offset, &offset1);
pos = 0;
for (offset2 = offset1;;) {
c2 = buf[pos++];
if (flags & SEARCH_FLAG_IGNORECASE) {
if (qe_toupper(c) != qe_toupper(c2))
break;
} else {
if (c != c2)
break;
}
if (pos >= len) {
if (flags & SEARCH_FLAG_WORD) {
/* check for word boundaries */
if (qe_isword(eb_prevc(b, offset, &offset3))
|| qe_isword(eb_nextc(b, offset2, &offset3)))
break;
}
if (dir >= 0 || offset2 <= start_offset) {
*found_offset = offset;
*found_end = offset2;
return 1;
}
}
if (offset2 >= total_size)
break;
c = eb_nextc(b, offset2, &offset2);
}
}
}
static int search_abort_func(qe__unused__ void *opaque)
{
return is_user_input_pending();
}
static void buf_encode_search_u32(buf_t *out, const unsigned int *str, int len)
{
int i;
for (i = 0; i < len; i++) {
unsigned int v = str[i];
if (v < 32 || v == 127) {
buf_printf(out, "^%c", (v + '@') & 127);
} else {
buf_putc_utf8(out, v);
}
if (buf_avail(out) <= 0)
break;
}
}
static void buf_encode_search_str(buf_t *out, const char *str)
{
while (*str) {
int c = utf8_decode(&str);
if (c < 32 || c == 127) {
buf_printf(out, "^%c", (c + '@') & 127);
} else {
buf_putc_utf8(out, c);
}
if (buf_avail(out) <= 0)
break;
}
}
static void buf_disp_search_flags(buf_t *out, int search_flags) {
if (search_flags & SEARCH_FLAG_UNIHEX)
buf_puts(out, "Unihex ");
if (search_flags & SEARCH_FLAG_HEX)
buf_puts(out, "Hex ");
if (search_flags & SEARCH_FLAG_IGNORECASE)
buf_puts(out, "Folding ");
else
if (!(search_flags & SEARCH_FLAG_SMARTCASE))
buf_puts(out, "Exact ");
if (search_flags & SEARCH_FLAG_REGEX)
buf_puts(out, "Regex ");
if (search_flags & SEARCH_FLAG_WORD)
buf_puts(out, "Word ");
}
static void isearch_run(ISearchState *is)
{
EditState *s = is->s;
char ubuf[256];
buf_t outbuf, *out;
int c, i, len, hex_nibble, max_nibble, h, hc;
unsigned int v;
int search_offset, flags, dir = is->start_dir;
int start_time, elapsed_time;
start_time = get_clock_ms();
/* prepare the search bytes */
len = 0;
search_offset = is->start_offset;
max_nibble = hex_nibble = hc = 0;
flags = is->search_flags;
if (flags & SEARCH_FLAG_UNIHEX)
max_nibble = 6;
if (flags & SEARCH_FLAG_HEX)
max_nibble = 2;
for (i = 0; i < is->pos; i++) {
v = is->search_u32_flags[i];
if (v & FOUND_TAG) {
dir = (v & FOUND_REV) ? -1 : 1;
search_offset = v & ~(FOUND_TAG | FOUND_REV);
continue;
}
c = v;
if (len < countof(is->search_u32)) {
if (max_nibble) {
h = qe_digit_value(c);
if (h < 16) {
hc = (hc << 4) | h;
if (++hex_nibble == max_nibble) {
is->search_u32[len++] = hc;
hex_nibble = hc = 0;
}
} else {
if (c == ' ' && hex_nibble) {
is->search_u32[len++] = hc;
hex_nibble = hc = 0;
}
}
} else {
is->search_u32[len++] = c;
}
}
}
if (hex_nibble >= 2 && len < countof(is->search_u32)) {
is->search_u32[len++] = hc;
hex_nibble = hc = 0;
}
is->search_u32_len = len;
is->dir = dir;
if (len == 0) {
s->b->mark = is->saved_mark;
s->offset = is->start_offset;
s->region_style = 0;
is->found_offset = -1;
} else {
if (eb_search(s->b, is->dir, flags,
search_offset, s->b->total_size,
is->search_u32, is->search_u32_len,
search_abort_func, NULL,
&is->found_offset, &is->found_end) > 0) {
s->region_style = QE_STYLE_SEARCH_MATCH;
if (is->dir >= 0) {
s->b->mark = is->found_offset;
s->offset = is->found_end;
} else {
s->b->mark = is->found_end;
s->offset = is->found_offset;
}
}
}
/* display search string */
out = buf_init(&outbuf, ubuf, sizeof(ubuf));
if (is->found_offset < 0 && len > 0)
buf_puts(out, "Failing ");
else
if (is->search_flags & SEARCH_FLAG_WRAPPED) {
buf_puts(out, "Wrapped ");
is->search_flags &= ~SEARCH_FLAG_WRAPPED;
}
buf_disp_search_flags(out, is->search_flags);
buf_puts(out, "I-search");
if (is->dir < 0)
buf_puts(out, " backward");
buf_puts(out, ": ");
buf_encode_search_u32(out, is->search_u32, is->search_u32_len);
if (is->quoting)
buf_puts(out, "^Q-");
/* display text */
do_center_cursor(s, 0);
edit_display(s->qe_state);
put_status(NULL, "%s", out->buf); /* XXX: why NULL? */
elapsed_time = get_clock_ms() - start_time;
if (elapsed_time >= 100)
put_status(s, "|isearch_run: %dms", elapsed_time);
dpy_flush(s->screen);
}
static int isearch_grab(ISearchState *is, EditBuffer *b, int from, int to)
{
int offset, c, last = is->pos;
if (b) {
if (to < 0 || to > b->total_size)
to = b->total_size;
for (offset = from; is->pos < SEARCH_LENGTH && offset < to;) {
c = eb_nextc(b, offset, &offset);
is->search_u32_flags[is->pos++] = c;
}
}
return is->pos - last;
}
static void isearch_key(void *opaque, int ch)
{
ISearchState *is = opaque;
EditState *s = is->s;
QEmacsState *qs = &qe_state;
int offset0, offset1, curdir = is->dir;
int emacs_behaviour = !qs->emulation_flags;
if (is->quoting) {
is->quoting = 0;
if (!KEY_IS_SPECIAL(ch))
goto addch;
}
/* XXX: all these should be isearch-mode bindings */
switch (ch) {
case KEY_DEL:
case KEY_BS:
/* cancel last input item from search string */
if (is->pos > 0)
is->pos--;
break;
case KEY_CTRL('g'):
/* XXX: when search has failed should cancel input back to what has been
* found successfully.
* when search is successful aborts and moves point to starting point.
*/
s->b->mark = is->saved_mark;
s->offset = is->start_offset;
s->region_style = 0;
s->isearch_state = NULL;
put_status(s, "Quit");
the_end:
/* save current searched string */
if (is->search_u32_len > 0) {
memcpy(last_search_u32, is->search_u32,
is->search_u32_len * sizeof(*is->search_u32));
last_search_u32_len = is->search_u32_len;
last_search_u32_flags = is->search_flags;
}
qe_ungrab_keys();
edit_display(s->qe_state);
dpy_flush(s->screen);
return;
case KEY_CTRL('s'): /* next match */
is->dir = 1;
goto addpos;
case KEY_CTRL('r'): /* previous match */
is->dir = -1;
addpos:
/* use last searched string if no input */
if (is->search_u32_len == 0 && is->dir == curdir) {
int len = min(last_search_u32_len, SEARCH_LENGTH - is->pos);
memcpy(is->search_u32_flags + is->pos, last_search_u32,
len * sizeof(*is->search_u32_flags));
is->pos += len;
is->search_flags = last_search_u32_flags;
} else
if (is->pos < SEARCH_LENGTH) {
/* add the match position, if any */
unsigned long v = (is->dir >= 0) ? FOUND_TAG : FOUND_TAG | FOUND_REV;
if (is->found_offset < 0 && is->search_u32_len > 0) {
is->search_flags |= SEARCH_FLAG_WRAPPED;
if (is->dir < 0)
v |= s->b->total_size;
} else {
v |= s->offset;
}
is->search_u32_flags[is->pos++] = v;
}
break;
case KEY_CTRL('q'):
is->quoting = 1;
break;
case KEY_META('w'):
emacs_behaviour ^= 1;
/* fall thru */
case KEY_CTRL('w'):
if (emacs_behaviour) {
/* grab word at cursor */
offset0 = s->offset;
do_word_left_right(s, 1);
offset1 = s->offset;
s->offset = offset0;
isearch_grab(is, s->b, offset0, offset1);
} else {
/* toggle word match */
is->search_flags ^= SEARCH_FLAG_WORD;
}
break;
case KEY_META('y'):
emacs_behaviour ^= 1;
/* fall thru */
case KEY_CTRL('y'):
if (emacs_behaviour) {
/* grab line at cursor */
offset0 = s->offset;
if (eb_nextc(s->b, offset0, &offset1) == '\n')
offset0 = offset1;
do_eol(s);
offset1 = s->offset;
s->offset = offset0;
isearch_grab(is, s->b, offset0, offset1);
} else {
/* yank into search string */
isearch_grab(is, qs->yank_buffers[qs->yank_current], 0, -1);
}
break;
case KEY_META(KEY_CTRL('b')):
/* cycle unihex, hex, normal search */
if (is->search_flags & SEARCH_FLAG_UNIHEX)
is->search_flags ^= SEARCH_FLAG_HEX | SEARCH_FLAG_UNIHEX;
else
if (is->search_flags & SEARCH_FLAG_HEX)
is->search_flags ^= SEARCH_FLAG_HEX;
else
is->search_flags ^= SEARCH_FLAG_UNIHEX;
break;
case KEY_META('c'):
case KEY_CTRL('c'):
/* toggle case sensitivity */
if (is->search_flags & (SEARCH_FLAG_IGNORECASE | SEARCH_FLAG_SMARTCASE)) {
is->search_flags &= ~SEARCH_FLAG_IGNORECASE;
} else {
is->search_flags |= SEARCH_FLAG_IGNORECASE;
}
is->search_flags &= ~SEARCH_FLAG_SMARTCASE;
break;
case KEY_META('r'):
case KEY_CTRL('t'):
is->search_flags ^= ~SEARCH_FLAG_REGEX;
break;
case KEY_CTRL('l'):
do_center_cursor(s, 1);
break;
default:
if ((KEY_IS_SPECIAL(ch) || KEY_IS_CONTROL(ch)) &&
ch != '\t' && ch != KEY_CTRL('j')) {
/* exit search mode */
#if 0
// FIXME: behaviour from qemacs-0.3pre13
if (is->found_offset >= 0) {
s->b->mark = is->found_offset;
} else {
s->b->mark = is->start_offset;
}
put_status(s, "Marked");
#endif
s->b->mark = is->start_offset;
s->region_style = 0;
put_status(s, "Mark saved where search started");
/* repost key */
/* do not keep search matches lingering */
s->isearch_state = NULL;
if (ch != KEY_RET) {
unget_key(ch);
}
goto the_end;
} else {
addch:
if (is->pos < SEARCH_LENGTH) {
is->search_u32_flags[is->pos++] = ch;
}
}
break;
}
isearch_run(is);
}
/* XXX: handle busy */
void do_isearch(EditState *s, int dir, int argval)
{
ISearchState *is = &isearch_state;
EditState *e;
int flags = SEARCH_FLAG_SMARTCASE;
/* prevent search from minibuffer */
if (s->flags & WF_MINIBUF)
return;
/* stop displaying search matches on last window */
e = check_window(&is->s);
if (e) {
e->isearch_state = NULL;
}
memset(is, 0, sizeof(isearch_state));
s->isearch_state = is;
is->s = s;
is->saved_mark = s->b->mark;
is->start_offset = s->offset;
is->start_dir = is->dir = dir;
if (s->hex_mode) {
if (s->unihex_mode)
flags |= SEARCH_FLAG_UNIHEX;
else
flags |= SEARCH_FLAG_HEX;
}
if (argval != NO_ARG)
flags |= SEARCH_FLAG_REGEX;
is->search_flags = flags;
qe_grab_keys(isearch_key, is);
isearch_run(is);
}
void isearch_colorize_matches(EditState *s, unsigned int *buf, int len,
QETermStyle *sbuf, int offset_start)
{
ISearchState *is = s->isearch_state;
EditBuffer *b = s->b;
int offset, char_offset, found_offset, found_end, offset_end;
if (!is || is->search_u32_len <= 0)
return;
char_offset = eb_get_char_offset(b, offset_start);
offset_end = eb_goto_char(b, char_offset + len);
offset = 0;
if (char_offset > is->search_u32_len + 1)
offset = eb_goto_char(b, char_offset - is->search_u32_len - 1);
while (eb_search(b, 1, is->search_flags, offset, offset_end,
is->search_u32, is->search_u32_len, NULL, NULL,
&found_offset, &found_end) > 0) {
int line, start, stop, i;
if (found_offset >= offset_end)
break;
if (found_end > offset_start) {
/* Compute character positions */
start = 0;
if (found_offset > offset_start)
eb_get_pos(b, &line, &start, found_offset);
stop = len;
if (found_end < offset_end) {
eb_get_pos(b, &line, &stop, found_end);
if (stop > len)
stop = len;
}
for (i = start; i < stop; i++) {
sbuf[i] = QE_STYLE_SEARCH_HILITE;
}
}
offset = found_end;
}
}
static int search_to_u32(unsigned int *buf, int size,
const char *str, int flags)
{
if (flags & (SEARCH_FLAG_HEX | SEARCH_FLAG_UNIHEX)) {
/* CG: XXX: Should mix utf8 and hex syntax in hex modes */
const char *s;
int c, hex_nibble, max_nibble, h, hc, len;
max_nibble = (flags & SEARCH_FLAG_UNIHEX) ? 6 : 2;
s = str;
hex_nibble = hc = 0;
for (len = 0; len < size;) {
c = *s++;
if (c == '\0') {
if (hex_nibble >= 2) {
buf[len++] = hc;
hex_nibble = hc = 0;
}
break;
}
h = qe_digit_value(c);
if (h < 16) {
hc = (hc << 4) | h;
if (++hex_nibble == max_nibble) {
buf[len++] = hc;
hex_nibble = hc = 0;
}
} else {
if (c == ' ' && hex_nibble) {
buf[len++] = hc;
hex_nibble = hc = 0;
}
}
}
return len;
} else {
return utf8_to_unicode(buf, size, str);
}
}
typedef struct QueryReplaceState {
EditState *s;
int start_offset;
int search_flags;
int replace_all;
int nb_reps;
int search_u32_len, replace_u32_len;
int found_offset, found_end;
int last_offset;
char search_str[SEARCH_LENGTH]; /* may be in hex */
char replace_str[SEARCH_LENGTH]; /* may be in hex */
unsigned int search_u32[SEARCH_LENGTH]; /* code points */
unsigned int replace_u32[SEARCH_LENGTH]; /* code points */
} QueryReplaceState;
static void query_replace_abort(QueryReplaceState *is)
{
EditState *s = is->s;
qe_ungrab_keys();
s->b->mark = is->start_offset;
s->region_style = 0;
put_status(NULL, "Replaced %d occurrences", is->nb_reps);
qe_free(&is);
edit_display(s->qe_state);
dpy_flush(s->screen);
}
static void query_replace_replace(QueryReplaceState *is)
{
EditState *s = is->s;
/* XXX: handle smart case replacement */
is->nb_reps++;
eb_delete_range(s->b, is->found_offset, is->found_end);
is->found_offset += eb_insert_u32_buf(s->b, is->found_offset,
is->replace_u32, is->replace_u32_len);
}
static void query_replace_display(QueryReplaceState *is)
{
EditState *s = is->s;
char ubuf[256];
buf_t outbuf, *out;
is->last_offset = is->found_offset;
is->search_u32_len = search_to_u32(is->search_u32,
countof(is->search_u32),
is->search_str, is->search_flags);
is->replace_u32_len = search_to_u32(is->replace_u32,
countof(is->replace_u32),
is->replace_str, is->search_flags);
for (;;) {
if (eb_search(s->b, 1, is->search_flags,
is->found_offset, s->b->total_size,
is->search_u32, is->search_u32_len,
NULL, NULL, &is->found_offset, &is->found_end) <= 0) {
query_replace_abort(is);
return;
}
if (is->replace_all) {
query_replace_replace(is);
continue;
}
break;
}
/* display prompt string */
out = buf_init(&outbuf, ubuf, sizeof(ubuf));
buf_disp_search_flags(out, is->search_flags);
buf_puts(out, "Query replace ");
buf_encode_search_str(out, is->search_str);
buf_puts(out, " with ");
buf_encode_search_str(out, is->replace_str);
buf_puts(out, ": ");
s->offset = is->found_end;
s->b->mark = is->found_offset;
s->region_style = QE_STYLE_SEARCH_MATCH;
do_center_cursor(s, 0);
edit_display(s->qe_state);
put_status(NULL, "%s", out->buf);
dpy_flush(s->screen);
}
static void query_replace_key(void *opaque, int ch)
{
QueryReplaceState *is = opaque;
EditState *s = is->s;
QEmacsState *qs = &qe_state;
switch (ch) {
case 'Y':
case 'y':
case KEY_SPC:
query_replace_replace(is);
s->offset = is->found_offset;
break;
case '!':
is->replace_all = 1;
break;
case 'N':
case 'n':
case KEY_DELETE:
is->found_offset = is->found_end;
break;
case KEY_META('w'):
case KEY_CTRL('w'):
/* toggle word match */
is->search_flags ^= SEARCH_FLAG_WORD;
is->found_offset = is->last_offset;
break;
case KEY_META('b'):
case KEY_CTRL('b'):
/* cycle unihex, hex, normal search */
if (is->search_flags & SEARCH_FLAG_UNIHEX)
is->search_flags ^= SEARCH_FLAG_HEX | SEARCH_FLAG_UNIHEX;
else
if (is->search_flags & SEARCH_FLAG_HEX)
is->search_flags ^= SEARCH_FLAG_HEX;
else
is->search_flags ^= SEARCH_FLAG_UNIHEX;
is->found_offset = is->last_offset;
break;
case KEY_META('c'):
case KEY_CTRL('c'):
/* toggle case sensitivity */
if (is->search_flags & (SEARCH_FLAG_IGNORECASE | SEARCH_FLAG_SMARTCASE)) {
is->search_flags &= ~SEARCH_FLAG_IGNORECASE;
} else {
is->search_flags |= SEARCH_FLAG_IGNORECASE;
}
is->search_flags &= ~SEARCH_FLAG_SMARTCASE;
is->found_offset = is->last_offset;
break;
case KEY_CTRL('g'):
/* abort */
if (qs->emulation_flags) {
/* restore point to original location */
s->offset = is->start_offset;
}
query_replace_abort(is);
return;
case KEY_CTRL('l'):
do_center_cursor(s, 1);
break;
case '.':
query_replace_replace(is);
s->offset = is->found_offset;
/* FALL THRU */
default:
query_replace_abort(is);
return;
}
query_replace_display(is);
}
static void query_replace(EditState *s, const char *search_str,
const char *replace_str, int all, int flags)
{
QueryReplaceState *is;
/* prevent replace from minibuffer */
if (s->flags & WF_MINIBUF)
return;
if (s->b->flags & BF_READONLY)
return;
is = qe_mallocz(QueryReplaceState);
if (!is)
return;
is->s = s;
pstrcpy(is->search_str, sizeof(is->search_str), search_str);
pstrcpy(is->replace_str, sizeof(is->replace_str), replace_str);
if (s->hex_mode) {
if (s->unihex_mode)
flags = SEARCH_FLAG_UNIHEX;
else
flags = SEARCH_FLAG_HEX;
}
is->search_flags = flags;
is->replace_all = all;
is->start_offset = is->last_offset = s->offset;
is->found_offset = is->found_end = s->offset;
qe_grab_keys(query_replace_key, is);
query_replace_display(is);
}
void do_query_replace(EditState *s, const char *search_str,
const char *replace_str)
{
int flags = SEARCH_FLAG_SMARTCASE;
query_replace(s, search_str, replace_str, 0, flags);
}
void do_replace_string(EditState *s, const char *search_str,
const char *replace_str, int argval)
{
int flags = SEARCH_FLAG_SMARTCASE;
if (argval != NO_ARG)
flags |= SEARCH_FLAG_WORD;
query_replace(s, search_str, replace_str, 1, flags);
}
/* dir = 0, -1, 1, 2 -> count matches, reverse, forward, delete-matching-lines */
void do_search_string(EditState *s, const char *search_str, int dir)
{
unsigned int search_u32[SEARCH_LENGTH];
int search_u32_len;
int found_offset, found_end;
int flags = SEARCH_FLAG_SMARTCASE;
int offset, count = 0;
if (s->hex_mode) {
if (s->unihex_mode)
flags |= SEARCH_FLAG_UNIHEX;
else
flags |= SEARCH_FLAG_HEX;
}
search_u32_len = search_to_u32(search_u32, countof(search_u32),
search_str, flags);
/* empty string matches */
if (search_u32_len <= 0)
return;
for (offset = s->offset;;) {
if (eb_search(s->b, dir, flags,
offset, s->b->total_size,
search_u32, search_u32_len,
NULL, NULL, &found_offset, &found_end) > 0) {
count++;
if (dir == 0) {
offset = found_end;
continue;
} else
if (dir == 2) {
offset = eb_goto_bol(s->b, found_offset);
eb_delete_range(s->b, offset,
eb_next_line(s->b, found_offset));
continue;
} else {
s->offset = (dir < 0) ? found_offset : found_end;
do_center_cursor(s, 0);
return;
}
} else {
if (dir == 0) {
put_status(s, "%d matches", count);
} else
if (dir == 2) {
put_status(s, "deleted %d lines", count);
} else {
put_status(s, "Search failed: \"%s\"", search_str);
}
return;
}
}
}
static CmdDef search_commands[] = {
/*---------------- Search and replace ----------------*/
/* M-C-s should be bound to isearch-forward-regex */
/* mg binds search-forward to M-s */
CMD3( KEY_META('S'), KEY_NONE,
"search-forward", do_search_string, ESsi, 1,
"s{Search forward: }|search|"
"v", "")
/* M-C-r should be bound to isearch-backward-regex */
/* mg binds search-forward to M-r */
CMD3( KEY_META('R'), KEY_NONE,
"search-backward", do_search_string, ESsi, -1,
"s{Search backward: }|search|"
"v", "")
CMD3( KEY_META('C'), KEY_NONE,
"count-matches", do_search_string, ESsi, 0,
"s{Count Matches: }|search|"
"v", "")
CMD3( KEY_NONE, KEY_NONE,
"delete-matching-lines", do_search_string, ESsi, 2,
"*" "s{Delete lines containing: }|search|"
"v", "")
/* passing argument should switch to regex incremental search */
CMD3( KEY_CTRL('r'), KEY_NONE,
"isearch-backward", do_isearch, ESii, -1, "vp" , "")
CMD3( KEY_CTRL('s'), KEY_NONE,
"isearch-forward", do_isearch, ESii, 1, "vp" , "")
CMD2( KEY_META('%'), KEY_NONE,
"query-replace", do_query_replace, ESss,
"*" "s{Query replace: }|search|"
"s{With: }|replace|", "")
/* passing argument restricts replace to word matches */
/* XXX: non standard binding */
CMD2( KEY_META('r'), KEY_NONE,
"replace-string", do_replace_string, ESssi,
"*" "s{Replace String: }|search|"
"s{With: }|replace|"
"p", "")
CMD_DEF_END,
};
static int search_init(void) {
qe_register_cmd_table(search_commands, NULL);
return 0;
}
qe_module_init(search_init);