// match.c: functions for highlighting matches
#include <assert.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include "nvim/ascii_defs.h"
#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/drawscreen.h"
#include "nvim/eval/funcs.h"
#include "nvim/eval/typval.h"
#include "nvim/eval/window.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
#include "nvim/fold.h"
#include "nvim/func_attr.h"
#include "nvim/gettext.h"
#include "nvim/globals.h"
#include "nvim/highlight.h"
#include "nvim/highlight_group.h"
#include "nvim/macros_defs.h"
#include "nvim/match.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/option_vars.h"
#include "nvim/pos_defs.h"
#include "nvim/profile.h"
#include "nvim/regexp.h"
#include "nvim/strings.h"
#include "nvim/types_defs.h"
#include "nvim/vim_defs.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "match.c.generated.h"
#endif
static const char *e_invalwindow = N_("E957: Invalid window number");
#define SEARCH_HL_PRIORITY 0
/// Add match to the match list of window "wp".
/// If "pat" is not NULL the pattern will be highlighted with the group "grp"
/// with priority "prio".
/// If "pos_list" is not NULL the list of posisions defines the highlights.
/// Optionally, a desired ID "id" can be specified (greater than or equal to 1).
/// If no particular ID is desired, -1 must be specified for "id".
///
/// @param[in] conceal_char pointer to conceal replacement char
/// @return ID of added match, -1 on failure.
static int match_add(win_T *wp, const char *const grp, const char *const pat, int prio, int id,
list_T *pos_list, const char *const conceal_char)
FUNC_ATTR_NONNULL_ARG(1, 2)
{
int hlg_id;
regprog_T *regprog = NULL;
int rtype = UPD_SOME_VALID;
if (*grp == NUL || (pat != NULL && *pat == NUL)) {
return -1;
}
if (id < -1 || id == 0) {
semsg(_("E799: Invalid ID: %" PRId64
" (must be greater than or equal to 1)"),
(int64_t)id);
return -1;
}
if (id == -1) {
// use the next available match ID
id = wp->w_next_match_id++;
} else {
// check the given ID is not already in use
for (matchitem_T *cur = wp->w_match_head; cur != NULL; cur = cur->mit_next) {
if (cur->mit_id == id) {
semsg(_("E801: ID already taken: %" PRId64), (int64_t)id);
return -1;
}
}
// Make sure the next match ID is always higher than the highest
// manually selected ID. Add some extra in case a few more IDs are
// added soon.
if (wp->w_next_match_id < id + 100) {
wp->w_next_match_id = id + 100;
}
}
if ((hlg_id = syn_check_group(grp, strlen(grp))) == 0) {
return -1;
}
if (pat != NULL && (regprog = vim_regcomp(pat, RE_MAGIC)) == NULL) {
semsg(_(e_invarg2), pat);
return -1;
}
// Build new match.
matchitem_T *m = xcalloc(1, sizeof(matchitem_T));
if (pos_list != NULL) {
m->mit_pos_array = xcalloc((size_t)tv_list_len(pos_list), sizeof(llpos_T));
m->mit_pos_count = tv_list_len(pos_list);
}
m->mit_id = id;
m->mit_priority = prio;
m->mit_pattern = pat == NULL ? NULL : xstrdup(pat);
m->mit_hlg_id = hlg_id;
m->mit_match.regprog = regprog;
m->mit_match.rmm_ic = false;
m->mit_match.rmm_maxcol = 0;
m->mit_conceal_char = 0;
if (conceal_char != NULL) {
m->mit_conceal_char = utf_ptr2char(conceal_char);
}
// Set up position matches
if (pos_list != NULL) {
linenr_T toplnum = 0;
linenr_T botlnum = 0;
int i = 0;
TV_LIST_ITER(pos_list, li, {
linenr_T lnum = 0;
colnr_T col = 0;
int len = 1;
bool error = false;
if (TV_LIST_ITEM_TV(li)->v_type == VAR_LIST) {
const list_T *const subl = TV_LIST_ITEM_TV(li)->vval.v_list;
const listitem_T *subli = tv_list_first(subl);
if (subli == NULL) {
semsg(_("E5030: Empty list at position %d"),
(int)tv_list_idx_of_item(pos_list, li));
goto fail;
}
lnum = (linenr_T)tv_get_number_chk(TV_LIST_ITEM_TV(subli), &error);
if (error) {
goto fail;
}
if (lnum <= 0) {
continue;
}
m->mit_pos_array[i].lnum = lnum;
subli = TV_LIST_ITEM_NEXT(subl, subli);
if (subli != NULL) {
col = (colnr_T)tv_get_number_chk(TV_LIST_ITEM_TV(subli), &error);
if (error) {
goto fail;
}
if (col < 0) {
continue;
}
subli = TV_LIST_ITEM_NEXT(subl, subli);
if (subli != NULL) {
len =
|