/* vi:set ts=8 sts=4 sw=4 noet:
*
* VIM - Vi IMproved by Bram Moolenaar
*
* Do ":help uganda" in Vim to read copying and usage conditions.
* Do ":help credits" in Vim to see a list of people who contributed.
* See README.txt for an overview of the Vim source code.
*/
/*
* Text properties implementation.
*
* Text properties are attached to the text. They move with the text when
* text is inserted/deleted.
*
* Text properties have a user specified ID number, which can be unique.
* Text properties have a type, which can be used to specify highlighting.
*
* TODO:
* - Adjust text property column and length when text is inserted/deleted.
* -> a :substitute with a multi-line match
* -> search for changed_bytes() from ex_cmds.c
* - Perhaps we only need TP_FLAG_CONT_NEXT and can drop TP_FLAG_CONT_PREV?
* - Add an arrray for global_proptypes, to quickly lookup a prop type by ID
* - Add an arrray for b_proptypes, to quickly lookup a prop type by ID
* - Checking the text length to detect text properties is slow. Use a flag in
* the index, like DB_MARKED?
* - Also test line2byte() with many lines, so that ml_updatechunk() is taken
* into account.
* - add mechanism to keep track of changed lines.
*/
#include "vim.h"
#if defined(FEAT_TEXT_PROP) || defined(PROTO)
/*
* In a hashtable item "hi_key" points to "pt_name" in a proptype_T.
* This avoids adding a pointer to the hashtable item.
* PT2HIKEY() converts a proptype pointer to a hashitem key pointer.
* HIKEY2PT() converts a hashitem key pointer to a proptype pointer.
* HI2PT() converts a hashitem pointer to a proptype pointer.
*/
#define PT2HIKEY(p) ((p)->pt_name)
#define HIKEY2PT(p) ((proptype_T *)((p) - offsetof(proptype_T, pt_name)))
#define HI2PT(hi) HIKEY2PT((hi)->hi_key)
// The global text property types.
static hashtab_T *global_proptypes = NULL;
// The last used text property type ID.
static int proptype_id = 0;
static char_u e_type_not_exist[] = N_("E971: Property type %s does not exist");
static char_u e_invalid_col[] = N_("E964: Invalid column number: %ld");
static char_u e_invalid_lnum[] = N_("E966: Invalid line number: %ld");
/*
* Find a property type by name, return the hashitem.
* Returns NULL if the item can't be found.
*/
static hashitem_T *
find_prop_hi(char_u *name, buf_T *buf)
{
hashtab_T *ht;
hashitem_T *hi;
if (*name == NUL)
return NULL;
if (buf == NULL)
ht = global_proptypes;
else
ht = buf->b_proptypes;
if (ht == NULL)
return NULL;
hi = hash_find(ht, name);
if (HASHITEM_EMPTY(hi))
return NULL;
return hi;
}
/*
* Like find_prop_hi() but return the property type.
*/
static proptype_T *
find_prop(char_u *name, buf_T *buf)
{
hashitem_T *hi = find_prop_hi(name, buf);
if (hi == NULL)
return NULL;
return HI2PT(hi);
}
/*
* Lookup a property type by name. First in "buf" and when not found in the
* global types.
* When not found gives an error message and returns NULL.
*/
static proptype_T *
lookup_prop_type(char_u *name, buf_T *buf)
{
proptype_T *type = find_prop(name, buf);
if (type == NULL)
type = find_prop(name, NULL);
if (type == NULL)
EMSG2(_(e_type_not_exist), name);
return type;
}
/*
* Get an optional "bufnr" item from the dict in "arg".
* When the argument is not used or "bufnr" is not present then "buf" is
* unchanged.
* If "bufnr" is valid or not present return OK.
* When "arg" is not a dict or "bufnr" is invalide return FAIL.
*/
static int
get_bufnr_from_arg(typval_T *arg, buf_T **buf)
{
dictitem_T *di;
if (arg->v_type != VAR_DICT)
{
EMSG(_(e_dictreq));
return FAIL;
}
if (arg->vval.v_dict == NULL)
return OK; // NULL dict is like an empty dict
di = dict_find(arg->vval.v_dict, (char_u *)"bufnr", -1);
if (di != NULL)
{
*buf = tv_get_buf(&di->di_tv, FALSE);
if (*buf == NULL)
return FAIL;
}
return OK;
}
/*
* prop_add({lnum}, {col}, {props})
*/
void
f_prop_add(typval_T *argvars, typval_T *rettv UNUSED)
{
linenr_T lnum;
linenr_T start_lnum;
linenr_T end_lnum;
colnr_T start_col;
colnr_T end_col;
dict_T *dict;
char_u *type_name;
proptype_T *type;
buf_T *buf = curbuf;
int id = 0;
char_u *newtext;
int proplen;
size_t textlen;
char_u *props;
char_u *newprops;
textprop_T tmp_prop;
int i;
start_lnum = tv_get_number(&argvars[0]);
start_col = tv_get_number(&argvars[1]);
if (start_col < 1