aboutsummaryrefslogtreecommitdiff
path: root/search.c
diff options
context:
space:
mode:
Diffstat (limited to 'search.c')
-rw-r--r--search.c359
1 files changed, 274 insertions, 85 deletions
diff --git a/search.c b/search.c
index 7650ab379508..f619fbebeae8 100644
--- a/search.c
+++ b/search.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 1984-2020 Mark Nudelman
+ * Copyright (C) 1984-2021 Mark Nudelman
*
* You may distribute under the terms of either the GNU General Public
* License or the Less License, as specified in the README file.
@@ -16,8 +16,8 @@
#include "position.h"
#include "charset.h"
-#define MINPOS(a,b) (((a) < (b)) ? (a) : (b))
-#define MAXPOS(a,b) (((a) > (b)) ? (a) : (b))
+#define MINPOS(a,b) (((a) < (b)) ? (a) : (b))
+#define MAXPOS(a,b) (((a) > (b)) ? (a) : (b))
extern int sigs;
extern int how_search;
@@ -33,6 +33,10 @@ extern POSITION start_attnpos;
extern POSITION end_attnpos;
extern int utf_mode;
extern int screen_trashed;
+extern int sc_width;
+extern int sc_height;
+extern int chopline;
+extern int hshift;
#if HILITE_SEARCH
extern int hilite_search;
extern int size_linebuf;
@@ -41,8 +45,7 @@ extern int can_goto_line;
static int hide_hilite;
static POSITION prep_startpos;
static POSITION prep_endpos;
-static int is_caseless;
-static int is_ucase_pattern;
+extern POSITION xxpos;
/*
* Structures for maintaining a set of ranges for hilites and filtered-out
@@ -94,6 +97,7 @@ struct hilite_tree
static struct hilite_tree hilite_anchor = HILITE_INITIALIZER();
static struct hilite_tree filter_anchor = HILITE_INITIALIZER();
+static struct pattern_info *filter_infos = NULL;
#endif
@@ -105,6 +109,7 @@ struct pattern_info {
PATTERN_TYPE compiled;
char* text;
int search_type;
+ struct pattern_info *next;
};
#if NO_REGEX
@@ -114,7 +119,8 @@ struct pattern_info {
#endif
static struct pattern_info search_info;
-static struct pattern_info filter_info;
+static int is_ucase_pattern;
+static int is_caseless;
/*
* Are there any uppercase letters in this string?
@@ -136,18 +142,34 @@ is_ucase(str)
}
/*
+ * Discard a saved pattern.
+ */
+ static void
+clear_pattern(info)
+ struct pattern_info *info;
+{
+ if (info->text != NULL)
+ free(info->text);
+ info->text = NULL;
+#if !NO_REGEX
+ uncompile_pattern(&info->compiled);
+#endif
+}
+
+/*
* Compile and save a search pattern.
*/
static int
-set_pattern(info, pattern, search_type)
+set_pattern(info, pattern, search_type, show_error)
struct pattern_info *info;
char *pattern;
int search_type;
+ int show_error;
{
#if !NO_REGEX
if (pattern == NULL)
- CLEAR_PATTERN(info->compiled);
- else if (compile_pattern(pattern, search_type, &info->compiled) < 0)
+ SET_NULL_PATTERN(info->compiled);
+ else if (compile_pattern(pattern, search_type, show_error, &info->compiled) < 0)
return -1;
#endif
/* Pattern compiled successfully; save the text too. */
@@ -174,30 +196,16 @@ set_pattern(info, pattern, search_type)
}
/*
- * Discard a saved pattern.
- */
- static void
-clear_pattern(info)
- struct pattern_info *info;
-{
- if (info->text != NULL)
- free(info->text);
- info->text = NULL;
-#if !NO_REGEX
- uncompile_pattern(&info->compiled);
-#endif
-}
-
-/*
* Initialize saved pattern to nothing.
*/
static void
init_pattern(info)
struct pattern_info *info;
{
- CLEAR_PATTERN(info->compiled);
+ SET_NULL_PATTERN(info->compiled);
info->text = NULL;
info->search_type = 0;
+ info->next = NULL;
}
/*
@@ -207,7 +215,6 @@ init_pattern(info)
init_search(VOID_PARAM)
{
init_pattern(&search_info);
- init_pattern(&filter_info);
}
/*
@@ -217,18 +224,13 @@ init_search(VOID_PARAM)
get_cvt_ops(VOID_PARAM)
{
int ops = 0;
- if (is_caseless || bs_mode == BS_SPECIAL)
- {
- if (is_caseless)
- ops |= CVT_TO_LC;
- if (bs_mode == BS_SPECIAL)
- ops |= CVT_BS;
- if (bs_mode != BS_CONTROL)
- ops |= CVT_CRLF;
- } else if (bs_mode != BS_CONTROL)
- {
+
+ if (is_caseless)
+ ops |= CVT_TO_LC;
+ if (bs_mode == BS_SPECIAL)
+ ops |= CVT_BS;
+ if (bs_mode != BS_CONTROL)
ops |= CVT_CRLF;
- }
if (ctldisp == OPT_ONPLUS)
ops |= CVT_ANSI;
return (ops);
@@ -292,6 +294,7 @@ repaint_hilite(on)
lower_left();
hide_hilite = save_hide_hilite;
}
+#endif
/*
* Clear the attn hilite.
@@ -299,6 +302,7 @@ repaint_hilite(on)
public void
clear_attn(VOID_PARAM)
{
+#if HILITE_SEARCH
int sindex;
POSITION old_start_attnpos;
POSITION old_end_attnpos;
@@ -337,27 +341,30 @@ clear_attn(VOID_PARAM)
}
if (moved)
lower_left();
-}
#endif
+}
/*
- * Hide search string highlighting.
+ * Toggle or clear search string highlighting.
*/
public void
-undo_search(VOID_PARAM)
+undo_search(clear)
+ int clear;
{
- if (!prev_pattern(&search_info))
+ clear_pattern(&search_info);
+#if HILITE_SEARCH
+ if (clear)
+ {
+ clr_hilite();
+ } else
{
if (hilite_anchor.first == NULL)
{
error("No previous regular expression", NULL_PARG);
return;
}
- clr_hilite(); /* Next time, hilite_anchor.first will be NULL. */
+ hide_hilite = !hide_hilite;
}
- clear_pattern(&search_info);
-#if HILITE_SEARCH
- hide_hilite = !hide_hilite;
repaint_hilite(1);
#endif
}
@@ -601,7 +608,7 @@ prev_unfiltered(pos)
* If nohide is nonzero, don't consider hide_hilite.
*/
public int
-is_hilited(pos, epos, nohide, p_matches)
+is_hilited_attr(pos, epos, nohide, p_matches)
POSITION pos;
POSITION epos;
int nohide;
@@ -614,12 +621,12 @@ is_hilited(pos, epos, nohide, p_matches)
if (!status_col &&
start_attnpos != NULL_POSITION &&
- pos < end_attnpos &&
- (epos == NULL_POSITION || epos > start_attnpos))
+ pos <= end_attnpos &&
+ (epos == NULL_POSITION || epos >= start_attnpos))
/*
* The attn line overlaps this range.
*/
- return (1);
+ return (AT_HILITE|AT_COLOR_ATTN);
match = is_hilited_range(pos, epos);
if (!match)
@@ -631,7 +638,7 @@ is_hilited(pos, epos, nohide, p_matches)
* hilite in status column. In this case we want to return
* hilite status even if hiliting is disabled or hidden.
*/
- return (1);
+ return (AT_HILITE|AT_COLOR_SEARCH);
/*
* Report matches, even if we're hiding highlights.
@@ -650,7 +657,7 @@ is_hilited(pos, epos, nohide, p_matches)
*/
return (0);
- return (1);
+ return (AT_HILITE|AT_COLOR_SEARCH);
}
/*
@@ -1156,10 +1163,91 @@ search_pos(search_type)
}
/*
+ * Check to see if the line matches the filter pattern.
+ * If so, add an entry to the filter list.
+ */
+#if HILITE_SEARCH
+ static int
+matches_filters(pos, cline, line_len, chpos, linepos, sp, ep)
+ POSITION pos;
+ char *cline;
+ int line_len;
+ int *chpos;
+ POSITION linepos;
+ char **sp;
+ char **ep;
+{
+ struct pattern_info *filter;
+
+ for (filter = filter_infos; filter != NULL; filter = filter->next)
+ {
+ int line_filter = match_pattern(info_compiled(filter), filter->text,
+ cline, line_len, sp, ep, 0, filter->search_type);
+ if (line_filter)
+ {
+ struct hilite hl;
+ hl.hl_startpos = linepos;
+ hl.hl_endpos = pos;
+ add_hilite(&filter_anchor, &hl);
+ free(cline);
+ free(chpos);
+ return (1);
+ }
+ }
+ return (0);
+}
+#endif
+
+/*
+ * Get the position of the first char in the screen line which
+ * puts tpos on screen.
+ */
+ static POSITION
+get_lastlinepos(pos, tpos, sheight)
+ POSITION pos;
+ POSITION tpos;
+ int sheight;
+{
+ int nlines;
+
+ for (nlines = 0;; nlines++)
+ {
+ POSITION npos = forw_line(pos);
+ if (npos > tpos)
+ {
+ if (nlines < sheight)
+ return NULL_POSITION;
+ return pos;
+ }
+ pos = npos;
+ }
+}
+
+/*
+ * Get the segment index of tpos in the line starting at pos.
+ * A segment is a string of printable chars that fills the screen width.
+ */
+ static int
+get_seg(pos, tpos)
+ POSITION pos;
+ POSITION tpos;
+{
+ int seg;
+
+ for (seg = 0;; seg++)
+ {
+ POSITION npos = forw_line_seg(pos, TRUE);
+ if (npos > tpos)
+ return seg;
+ pos = npos;
+ }
+}
+
+/*
* Search a subset of the file, specified by start/end position.
*/
static int
-search_range(pos, endpos, search_type, matches, maxlines, plinepos, pendpos)
+search_range(pos, endpos, search_type, matches, maxlines, plinepos, pendpos, plastlinepos)
POSITION pos;
POSITION endpos;
int search_type;
@@ -1167,6 +1255,7 @@ search_range(pos, endpos, search_type, matches, maxlines, plinepos, pendpos)
int maxlines;
POSITION *plinepos;
POSITION *pendpos;
+ POSITION *plastlinepos;
{
char *line;
char *cline;
@@ -1178,9 +1267,14 @@ search_range(pos, endpos, search_type, matches, maxlines, plinepos, pendpos)
int cvt_len;
int *chpos;
POSITION linepos, oldpos;
+ int swidth = sc_width - line_pfx_width();
+ int sheight = sc_height - sindex_from_sline(jump_sline);
linenum = find_linenum(pos);
oldpos = pos;
+ /* When the search wraps around, end at starting position. */
+ if ((search_type & SRCH_WRAP) && endpos == NULL_POSITION)
+ endpos = pos;
for (;;)
{
/*
@@ -1196,7 +1290,9 @@ search_range(pos, endpos, search_type, matches, maxlines, plinepos, pendpos)
return (-1);
}
- if ((endpos != NULL_POSITION && pos >= endpos) || maxlines == 0)
+ if ((endpos != NULL_POSITION && !(search_type & SRCH_WRAP) &&
+ (((search_type & SRCH_FORW) && pos >= endpos) ||
+ ((search_type & SRCH_BACK) && pos <= endpos))) || maxlines == 0)
{
/*
* Reached end position without a match.
@@ -1235,6 +1331,35 @@ search_range(pos, endpos, search_type, matches, maxlines, plinepos, pendpos)
/*
* Reached EOF/BOF without a match.
*/
+ if (search_type & SRCH_WRAP)
+ {
+ /*
+ * The search wraps around the current file, so
+ * try to continue at BOF/EOF.
+ */
+ if (search_type & SRCH_FORW)
+ {
+ pos = ch_zero();
+ } else
+ {
+ pos = ch_length();
+ if (pos == NULL_POSITION)
+ {
+ (void) ch_end_seek();
+ pos = ch_length();
+ }
+ }
+ if (pos != NULL_POSITION) {
+ /*
+ * Wrap-around was successful. Clear
+ * the flag so we don't wrap again, and
+ * continue the search at new pos.
+ */
+ search_type &= ~SRCH_WRAP;
+ linenum = find_linenum(pos);
+ continue;
+ }
+ }
if (pendpos != NULL)
*pendpos = oldpos;
return (matches);
@@ -1252,8 +1377,10 @@ search_range(pos, endpos, search_type, matches, maxlines, plinepos, pendpos)
add_lnum(linenum, pos);
oldpos = pos;
+#if HILITE_SEARCH
if (is_filtered(linepos))
continue;
+#endif
/*
* If it's a caseless search, convert the line to lowercase.
@@ -1267,25 +1394,14 @@ search_range(pos, endpos, search_type, matches, maxlines, plinepos, pendpos)
#if HILITE_SEARCH
/*
- * Check to see if the line matches the filter pattern.
- * If so, add an entry to the filter list.
+ * If any filters are in effect, ignore non-matching lines.
*/
- if (((search_type & SRCH_FIND_ALL) ||
+ if (filter_infos != NULL &&
+ ((search_type & SRCH_FIND_ALL) ||
prep_startpos == NULL_POSITION ||
- linepos < prep_startpos || linepos >= prep_endpos) &&
- prev_pattern(&filter_info)) {
- int line_filter = match_pattern(info_compiled(&filter_info), filter_info.text,
- cline, line_len, &sp, &ep, 0, filter_info.search_type);
- if (line_filter)
- {
- struct hilite hl;
- hl.hl_startpos = linepos;
- hl.hl_endpos = pos;
- add_hilite(&filter_anchor, &hl);
- free(cline);
- free(chpos);
+ linepos < prep_startpos || linepos >= prep_endpos)) {
+ if (matches_filters(pos, cline, line_len, chpos, linepos, &sp, &ep))
continue;
- }
}
#endif
@@ -1330,6 +1446,50 @@ search_range(pos, endpos, search_type, matches, maxlines, plinepos, pendpos)
hilite_line(linepos, cline, line_len, chpos, sp, ep, cvt_ops);
}
#endif
+ if (chopline)
+ {
+ /*
+ * If necessary, shift horizontally to make sure
+ * search match is fully visible.
+ */
+ if (sp != NULL && ep != NULL)
+ {
+ int start_off = sp - cline;
+ int end_off = ep - cline;
+ int save_hshift = hshift;
+ int sshift;
+ int eshift;
+ hshift = 0; /* make get_seg count screen lines */
+ chopline = FALSE;
+ sshift = swidth * get_seg(linepos, linepos + chpos[start_off]);
+ eshift = swidth * get_seg(linepos, linepos + chpos[end_off]);
+ chopline = TRUE;
+ if (sshift >= save_hshift && eshift <= save_hshift)
+ {
+ hshift = save_hshift;
+ } else
+ {
+ hshift = sshift;
+ screen_trashed = 1;
+ }
+ }
+ } else if (plastlinepos != NULL)
+ {
+ /*
+ * If the line is so long that the highlighted match
+ * won't be seen when the line is displayed normally
+ * (starting at the first char) because it fills the whole
+ * screen and more, scroll forward until the last char
+ * of the match appears in the last line on the screen.
+ * lastlinepos is the position of the first char of that last line.
+ */
+ if (ep != NULL)
+ {
+ int end_off = ep - cline;
+ if (end_off >= swidth * sheight / 4) /* heuristic */
+ *plastlinepos = get_lastlinepos(linepos, linepos + chpos[end_off], sheight);
+ }
+ }
free(cline);
free(chpos);
if (plinepos != NULL)
@@ -1358,8 +1518,8 @@ hist_pattern(search_type)
if (pattern == NULL)
return (0);
- if (set_pattern(&search_info, pattern, search_type) < 0)
- return (0);
+ if (set_pattern(&search_info, pattern, search_type, 1) < 0)
+ return (-1);
#if HILITE_SEARCH
if (hilite_search == OPT_ONPLUS && !hide_hilite)
@@ -1392,7 +1552,7 @@ chg_caseless(VOID_PARAM)
* Regenerate the pattern using the new state.
*/
clear_pattern(&search_info);
- hist_pattern(search_info.search_type);
+ (void) hist_pattern(search_info.search_type);
}
}
@@ -1412,6 +1572,8 @@ search(search_type, pattern, n)
int n;
{
POSITION pos;
+ POSITION opos;
+ POSITION lastlinepos = NULL_POSITION;
if (pattern == NULL || *pattern == '\0')
{
@@ -1419,10 +1581,13 @@ search(search_type, pattern, n)
* A null pattern means use the previously compiled pattern.
*/
search_type |= SRCH_AFTER_TARGET;
- if (!prev_pattern(&search_info) && !hist_pattern(search_type))
+ if (!prev_pattern(&search_info))
{
- error("No previous regular expression", NULL_PARG);
- return (-1);
+ int r = hist_pattern(search_type);
+ if (r == 0)
+ error("No previous regular expression", NULL_PARG);
+ if (r <= 0)
+ return (-1);
}
if ((search_type & SRCH_NO_REGEX) !=
(search_info.search_type & SRCH_NO_REGEX))
@@ -1455,7 +1620,8 @@ search(search_type, pattern, n)
/*
* Compile the pattern.
*/
- if (set_pattern(&search_info, pattern, search_type) < 0)
+ int show_error = !(search_type & SRCH_INCR);
+ if (set_pattern(&search_info, pattern, search_type, show_error) < 0)
return (-1);
#if HILITE_SEARCH
if (hilite_search || status_col)
@@ -1483,6 +1649,7 @@ search(search_type, pattern, n)
* Figure out where to start the search.
*/
pos = search_pos(search_type);
+ opos = position(sindex_from_sline(jump_sline));
if (pos == NULL_POSITION)
{
/*
@@ -1490,14 +1657,16 @@ search(search_type, pattern, n)
*/
if (search_type & SRCH_PAST_EOF)
return (n);
+#if HILITE_SEARCH
if (hilite_search == OPT_ON || status_col)
repaint_hilite(1);
+#endif
error("Nothing to search", NULL_PARG);
return (-1);
}
n = search_range(pos, NULL_POSITION, search_type, n, -1,
- &pos, (POSITION*)NULL);
+ &pos, (POSITION*)NULL, &lastlinepos);
if (n != 0)
{
/*
@@ -1518,7 +1687,10 @@ search(search_type, pattern, n)
/*
* Go to the matching line.
*/
- jump_loc(pos, jump_sline);
+ if (lastlinepos != NULL_POSITION)
+ jump_loc(lastlinepos, BOTTOM);
+ else if (pos != opos)
+ jump_loc(pos, jump_sline);
}
#if HILITE_SEARCH
@@ -1531,7 +1703,6 @@ search(search_type, pattern, n)
return (0);
}
-
#if HILITE_SEARCH
/*
* Prepare hilites in a given range of the file.
@@ -1560,7 +1731,7 @@ prep_hilite(spos, epos, maxlines)
* Search beyond where we're asked to search, so the prep region covers
* more than we need. Do one big search instead of a bunch of small ones.
*/
-#define SEARCH_MORE (3*size_linebuf)
+#define SEARCH_MORE (3*size_linebuf)
if (!prev_pattern(&search_info) && !is_filtering())
return;
@@ -1665,7 +1836,7 @@ prep_hilite(spos, epos, maxlines)
search_type |= (search_info.search_type & SRCH_NO_REGEX);
for (;;)
{
- result = search_range(spos, epos, search_type, 0, maxlines, (POSITION*)NULL, &new_epos);
+ result = search_range(spos, epos, search_type, 0, maxlines, (POSITION*)NULL, &new_epos, (POSITION*)NULL);
if (result < 0)
return;
if (prep_endpos == NULL_POSITION || new_epos > prep_endpos)
@@ -1718,11 +1889,29 @@ set_filter_pattern(pattern, search_type)
char *pattern;
int search_type;
{
+ struct pattern_info *filter;
+
clr_filter();
if (pattern == NULL || *pattern == '\0')
- clear_pattern(&filter_info);
- else
- set_pattern(&filter_info, pattern, search_type);
+ {
+ /* Clear and free all filters. */
+ for (filter = filter_infos; filter != NULL; )
+ {
+ struct pattern_info *next_filter = filter->next;
+ clear_pattern(filter);
+ free(filter);
+ filter = next_filter;
+ }
+ filter_infos = NULL;
+ } else
+ {
+ /* Create a new filter and add it to the filter_infos list. */
+ filter = ecalloc(1, sizeof(struct pattern_info));
+ init_pattern(filter);
+ set_pattern(filter, pattern, search_type, 1);
+ filter->next = filter_infos;
+ filter_infos = filter;
+ }
screen_trashed = 1;
}
@@ -1734,7 +1923,7 @@ is_filtering(VOID_PARAM)
{
if (ch_getflags() & CH_HELPFILE)
return (0);
- return prev_pattern(&filter_info);
+ return (filter_infos != NULL);
}
#endif