diff options
Diffstat (limited to 'search.c')
-rw-r--r-- | search.c | 359 |
1 files changed, 274 insertions, 85 deletions
@@ -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 |