// This is an open source non-commercial project. Dear PVS-Studio, please check
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
// match.c: functions for highlighting matches
#include <stdbool.h>
#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/drawscreen.h"
#include "nvim/eval.h"
#include "nvim/eval/funcs.h"
#include "nvim/ex_docmd.h"
#include "nvim/fold.h"
#include "nvim/highlight.h"
#include "nvim/highlight_group.h"
#include "nvim/match.h"
#include "nvim/memline.h"
#include "nvim/profile.h"
#include "nvim/regexp.h"
#include "nvim/runtime.h"
#include "nvim/vim.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "match.c.generated.h"
#endif
static char *e_invalwindow = N_("E957: Invalid window number");
#define SEARCH_HL_PRIORITY 0
/// Add match to the match list of window 'wp'. The pattern 'pat' will be
/// highlighted with the group 'grp' with priority 'prio'.
/// Optionally, a desired ID 'id' can be specified (greater than or equal to 1).
///
/// @param[in] id a desired ID 'id' can be specified
/// (greater than or equal to 1). -1 must be specified if no
/// particular ID is desired
/// @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)
{
matchitem_T *cur;
matchitem_T *prev;
matchitem_T *m;
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) {
cur = wp->w_match_head;
while (cur != NULL) {
if (cur->id == id) {
semsg(_("E801: ID already taken: %" PRId64), (int64_t)id);
return -1;
}
cur = cur->next;
}
}
if ((hlg_id = syn_check_group(grp, strlen(grp))) == 0) {
return -1;
}
if (pat != NULL && (regprog = vim_regcomp((char *)pat, RE_MAGIC)) == NULL) {
semsg(_(e_invarg2), pat);
return -1;
}
// Find available match ID.
while (id == -1) {
cur = wp->w_match_head;
while (cur != NULL && cur->id != wp->w_next_match_id) {
cur = cur->next;
}
if (cur == NULL) {
id = wp->w_next_match_id;
}
wp->w_next_match_id++;
}
// Build new match.
m = xcalloc(1, sizeof(matchitem_T));
m->id = id;
m->priority = prio;
m->pattern = pat == NULL ? NULL: xstrdup(pat);
m->hlg_id = hlg_id;
m->match.regprog = regprog;
m->match.rmm_ic = false;
m->match.rmm_maxcol = 0;
m->conceal_char = 0;
if (conceal_char != NULL) {
m->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->pos.pos[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 = (colnr_T)tv_get_number_chk(TV_LIST_ITEM_TV(subli), &error);
if (len < 0) {
continue;
}
if (error) {
goto fail;
}
}
}
m->pos.pos[i].col = col;
m->pos.pos[i].len = len;
} else if (TV_LIST_ITEM_TV(li)->v_type == VAR_NUMBER) {
if (TV_LIST_ITEM_TV(li)->vval.v_number <= 0) {
continue;
}
m->pos.pos[i].lnum = (linenr_T)TV_LIST_ITEM_TV(li)->vval.v_number;
m->pos.pos[i].col = 0;
m->
|