📄 search.c
字号:
int case_sensitive = get_opt_bool("document.browse.search.case"); int wraparound = get_opt_bool("document.browse.search.wraparound"); int textlen = strlen(text); assert(textlen && direction && offset); /* The link interval in which we are currently searching */ /* Set up the range of links that should be search in first attempt */ if (direction > 0) { upper_link = document->nlinks; lower_link = i - 1; } else { upper_link = i + 1; lower_link = -1; } for (; i > lower_link && i < upper_link; i += direction) { struct link *link = &document->links[i]; int match_offset = match_link_text(link, text, textlen, case_sensitive); if (match_offset >= 0) { *offset = match_offset; return i; } if (!wraparound) continue; /* Check if we are at the end of the first range. * Only wrap around one time. Initialize @i with * {+= direction} in mind. */ if (direction > 0) { if (i == upper_link - 1) { upper_link = current_link + 1; lower_link = -1; i = lower_link; wraparound = 0; } } else { if (i == lower_link + 1) { upper_link = document->nlinks; lower_link = current_link - 1; i = upper_link; wraparound = 0; } } } return -1;}/* The typeahead input line takes up one of the viewed lines so we * might have to scroll if the link is under the input line. */static inline voidfixup_typeahead_match(struct session *ses, struct document_view *doc_view){ int current_link = doc_view->vs->current_link; struct link *link = &doc_view->document->links[current_link]; doc_view->box.height -= 1; set_pos_x(doc_view, link); set_pos_y(doc_view, link); doc_view->box.height += 1;}static inline unsigned charget_document_char(struct document *document, int x, int y){ return (document->height > y && document->data[y].length > x) ? document->data[y].chars[x].data : 0;}static voiddraw_typeahead_match(struct terminal *term, struct document_view *doc_view, int chars, int offset){ struct color_pair *color = get_bfu_color(term, "searched"); int xoffset = doc_view->box.x - doc_view->vs->x; int yoffset = doc_view->box.y - doc_view->vs->y; struct link *link = get_current_link(doc_view); unsigned char *text = get_link_typeahead_text(link); int end = offset + chars; int i, j; for (i = 0, j = 0; text[j] && i < end; i++, j++) { int x = link->points[i].x; int y = link->points[i].y; unsigned char data = get_document_char(doc_view->document, x, y); /* Text wrapping might remove space chars from the link * position array so try to align the matched typeahead text * with what is actually on the screen by shifting the link * position variables if the canvas data do not match. */ if (data != text[j]) { i--; end--; offset--; } else if (i >= offset) { /* TODO: We should take in account original colors and * combine them with defined color. */ draw_char_color(term, xoffset + x, yoffset + y, color); } }}static enum typeahead_codedo_typeahead(struct session *ses, struct document_view *doc_view, unsigned char *text, int action, int *offset){ int current = int_max(doc_view->vs->current_link, 0); int direction, match, i = current; struct document *document = doc_view->document; switch (action) { case ACT_EDIT_PREVIOUS_ITEM: case ACT_EDIT_UP: direction = -1; i--; if (i >= 0) break; if (!get_opt_bool("document.browse.search.wraparound")) {search_hit_boundary: if (match_link_text(&document->links[current], text, strlen(text), get_opt_bool("document" ".browse" ".search" ".case")) >= 0) { return TYPEAHEAD_ERROR_NO_FURTHER; } return TYPEAHEAD_ERROR; } i = doc_view->document->nlinks - 1; break; case ACT_EDIT_NEXT_ITEM: case ACT_EDIT_DOWN: direction = 1; i++; if (i < doc_view->document->nlinks) break; if (!get_opt_bool("document.browse.search.wraparound")) goto search_hit_boundary; i = 0; break; case ACT_EDIT_ENTER: goto_current_link(ses, doc_view, 0); return TYPEAHEAD_CANCEL; default: direction = 1; } match = search_link_text(document, current, i, text, direction, offset); if (match == current && i != current) return TYPEAHEAD_ERROR_NO_FURTHER; if (match < 0) { if (i != current) return TYPEAHEAD_ERROR_NO_FURTHER; return TYPEAHEAD_ERROR; } assert(match >= 0 && match < doc_view->document->nlinks); doc_view->vs->current_link = match; return TYPEAHEAD_MATCHED;}/* Typeahead */static enum input_line_codetext_typeahead_handler(struct input_line *line, int action){ struct session *ses = line->ses; unsigned char *buffer = line->buffer; struct document_view *doc_view = current_frame(ses); int direction = ((unsigned char *) line->data)[0] == '/' ? 1 : -1; int report_errors = action == -1; enum find_error error; assertm(doc_view, "document not formatted"); if_assert_failed return INPUT_LINE_CANCEL; switch (action) { case ACT_EDIT_REDRAW: return INPUT_LINE_PROCEED; case ACT_EDIT_ENTER: if (!*buffer) { /* This ensures that search-typeahead-text * followed immediately with enter * clears the last search. */ search_for_do(ses, buffer, direction, 0); } goto_current_link(ses, doc_view, 0); return INPUT_LINE_CANCEL; case ACT_EDIT_PREVIOUS_ITEM: find_next(ses, doc_view, -1); break; case ACT_EDIT_NEXT_ITEM: find_next(ses, doc_view, 1); break; case ACT_EDIT_SEARCH_TOGGLE_REGEX: { struct option *opt = get_opt_rec(config_options, "document.browse.search.regex"); opt->value.number = (opt->value.number + 1) % (opt->max + 1); opt->flags |= OPT_TOUCHED; } /* Fall thru */ default: error = search_for_do(ses, buffer, direction, 0); if (error == FIND_ERROR_REGEX) break; if (report_errors) print_find_error(ses, error); /* We need to check |*buffer| here because * the input-line code will call this handler * even after it handles a back-space press. */ if (error != FIND_ERROR_HIT_TOP && error != FIND_ERROR_HIT_BOTTOM && error != FIND_ERROR_NONE && *buffer) return INPUT_LINE_REWIND; } draw_formatted(ses, 0); return INPUT_LINE_PROCEED;}static enum input_line_codelink_typeahead_handler(struct input_line *line, int action){ struct session *ses = line->ses; unsigned char *buffer = line->buffer; struct document_view *doc_view = current_frame(ses); int offset = 0; assertm(doc_view, "document not formatted"); if_assert_failed return INPUT_LINE_CANCEL; /* If there is nothing to match with don't start searching */ if (!*buffer) { /* If something already were typed we need to redraw * in order to remove the coloring of the link text. */ if (line->data) draw_formatted(ses, 0); return INPUT_LINE_PROCEED; } if (action == ACT_EDIT_REDRAW) { int current = doc_view->vs->current_link; int offset, bufferlen; if (current < 0) return INPUT_LINE_PROCEED; bufferlen = strlen(buffer); offset = match_link_text(&doc_view->document->links[current], buffer, bufferlen, get_opt_bool("document.browse" ".search.case")); if (offset >= 0) { draw_typeahead_match(ses->tab->term, doc_view, bufferlen, offset); } return INPUT_LINE_PROCEED; } /* Hack time .. should we change mode? */ if (!line->data) { enum main_action action = ACT_MAIN_NONE; switch (*buffer) { case '#': action = ACT_MAIN_SEARCH_TYPEAHEAD_LINK; break; case '?': action = ACT_MAIN_SEARCH_TYPEAHEAD_TEXT_BACK; break; case '/': action = ACT_MAIN_SEARCH_TYPEAHEAD_TEXT; break; default: break; } /* Should we reboot the input line .. (inefficient but easy) */ if (action != ACT_MAIN_NONE) { search_typeahead(ses, doc_view, action); return INPUT_LINE_CANCEL; } line->data = "#"; } switch (do_typeahead(ses, doc_view, buffer, action, &offset)) { case TYPEAHEAD_MATCHED: fixup_typeahead_match(ses, doc_view); draw_formatted(ses, 0); draw_typeahead_match(ses->tab->term, doc_view, strlen(buffer), offset); return INPUT_LINE_PROCEED; case TYPEAHEAD_ERROR_NO_FURTHER: typeahead_error(ses, buffer, 1); draw_typeahead_match(ses->tab->term, doc_view, strlen(buffer), offset); return INPUT_LINE_PROCEED; case TYPEAHEAD_ERROR: typeahead_error(ses, buffer, 0); return INPUT_LINE_REWIND; case TYPEAHEAD_CANCEL: default: return INPUT_LINE_CANCEL; }}enum frame_event_statussearch_typeahead(struct session *ses, struct document_view *doc_view, int action){ unsigned char *prompt = "#"; unsigned char *data = NULL; input_line_handler handler = text_typeahead_handler; struct input_history *history = &search_history; switch (action) { case ACT_MAIN_SEARCH_TYPEAHEAD_TEXT: prompt = data = "/"; break; case ACT_MAIN_SEARCH_TYPEAHEAD_TEXT_BACK: prompt = data = "?"; break; case ACT_MAIN_SEARCH_TYPEAHEAD_LINK: data = "#"; /* Falling forward .. good punk rock */ case ACT_MAIN_SEARCH_TYPEAHEAD: default: if (doc_view->document->nlinks) { handler = link_typeahead_handler; break; } info_box(ses->tab->term, MSGBOX_FREE_TEXT, N_("Typeahead"), ALIGN_CENTER, msg_text(ses->tab->term, N_("No links in current document"))); return FRAME_EVENT_OK; } input_field_line(ses, prompt, data, history, handler); return FRAME_EVENT_OK;}/* The dialog functions are clones of input_field() ones. Gross code * duplication. *//* TODO: This is just hacked input_field(), containing a lot of generic crap * etc. The useless cruft should be blasted out. And it's quite ugly anyway, * a nice cleanup target ;-). --pasky */enum search_option { SEARCH_OPT_REGEX, SEARCH_OPT_CASE, SEARCH_OPTIONS,};static struct option_resolver resolvers[] = { { SEARCH_OPT_REGEX, "document.browse.search.regex" }, { SEARCH_OPT_CASE, "document.browse.search.case" },};struct search_dlg_hop { void *data; union option_value values[SEARCH_OPTIONS];};static t_handler_event_statussearch_dlg_cancel(struct dialog_data *dlg_data, struct widget_data *widget_data){ void (*fn)(void *) = widget_data->widget->data; struct search_dlg_hop *hop = dlg_data->dlg->udata2; void *data = hop->data; if (fn) fn(data); return cancel_dialog(dlg_data, widget_data);}static t_handler_event_statussearch_dlg_ok(struct dialog_data *dlg_data, struct widget_data *widget_data){ void (*fn)(void *, unsigned char *) = widget_data->widget->data; struct search_dlg_hop *hop = dlg_data->dlg->udata2; void *data = hop->data; unsigned char *text = dlg_data->widgets_data->cdata; update_dialog_data(dlg_data); commit_option_values(resolvers, config_options, hop->values, SEARCH_OPTIONS); if (check_dialog(dlg_data)) return EVENT_NOT_PROCESSED; add_to_input_history(dlg_data->dlg->widgets->info.field.history, text, 1); if (fn) fn(data, text); return cancel_dialog(dlg_data, widget_data);}/* XXX: @data is ignored. */static voidsearch_dlg_do(struct terminal *term, struct memory_list *ml, unsigned char *title, void *data, struct input_history *history, void (*fn)(void *, unsigned char *)){ struct dialog *dlg; unsigned char *field; struct search_dlg_hop *hop; unsigned char *text = _("Search for text", term); hop = mem_calloc(1, sizeof(*hop)); if (!hop) return; checkout_option_values(resolvers, config_options, hop->values, SEARCH_OPTIONS); hop->data = data;#define SEARCH_WIDGETS_COUNT 8 dlg = calloc_dialog(SEARCH_WIDGETS_COUNT, MAX_STR_LEN); if (!dlg) { mem_free(hop); return; } dlg->title = _(title, term); dlg->layouter = generic_dialog_layouter; dlg->layout.fit_datalen = 1; dlg->layout.float_groups = 1; dlg->udata = text; dlg->udata2 = hop; add_to_ml(&ml, hop, NULL); /* @field is automatically cleared by calloc() */ field = get_dialog_offset(dlg, SEARCH_WIDGETS_COUNT); add_dlg_field(dlg, text, 0, 0, NULL, MAX_STR_LEN, field, history); add_dlg_radio(dlg, _("Normal search", term), 1, 0, hop->values[SEARCH_OPT_REGEX].number); add_dlg_radio(dlg, _("Regexp search", term), 1, 1, hop->values[SEARCH_OPT_REGEX].number); add_dlg_radio(dlg, _("Extended regexp search", term), 1, 2, hop->values[SEARCH_OPT_REGEX].number); add_dlg_radio(dlg, _("Case sensitive", term), 2, 1, hop->values[SEARCH_OPT_CASE].number); add_dlg_radio(dlg, _("Case insensitive", term), 2, 0, hop->values[SEARCH_OPT_CASE].number); add_dlg_button(dlg, _("~OK", term), B_ENTER, search_dlg_ok, fn); add_dlg_button(dlg, _("~Cancel", term), B_ESC, search_dlg_cancel, NULL); add_dlg_end(dlg, SEARCH_WIDGETS_COUNT); add_to_ml(&ml, dlg, NULL); do_dialog(term, dlg, ml);}enum frame_event_statussearch_dlg(struct session *ses, struct document_view *doc_view, int direction){ unsigned char *title; void *search_function; assert(direction); if_assert_failed return FRAME_EVENT_OK; if (direction > 0) { title = N_("Search"); search_function = search_for; } else { title = N_("Search backward"); search_function = search_for_back; } search_dlg_do(ses->tab->term, NULL, title, ses, &search_history, search_function); return FRAME_EVENT_OK;}static enum evhook_statussearch_history_write_hook(va_list ap, void *data){ save_input_history(&search_history, SEARCH_HISTORY_FILENAME); return EVENT_HOOK_STATUS_NEXT;}struct event_hook_info search_history_hooks[] = { { "periodic-saving", search_history_write_hook, NULL }, NULL_EVENT_HOOK_INFO,};voidinit_search_history(void){ load_input_history(&search_history, SEARCH_HISTORY_FILENAME); register_event_hooks(search_history_hooks);}voiddone_search_history(void){ unregister_event_hooks(search_history_hooks); save_input_history(&search_history, SEARCH_HISTORY_FILENAME); free_list(search_history.entries);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -