198aefe7cSBram Moolenaar /* vi:set ts=8 sts=4 sw=4 noet:
298aefe7cSBram Moolenaar *
398aefe7cSBram Moolenaar * VIM - Vi IMproved by Bram Moolenaar
498aefe7cSBram Moolenaar *
598aefe7cSBram Moolenaar * Do ":help uganda" in Vim to read copying and usage conditions.
698aefe7cSBram Moolenaar * Do ":help credits" in Vim to see a list of people who contributed.
798aefe7cSBram Moolenaar * See README.txt for an overview of the Vim source code.
898aefe7cSBram Moolenaar */
998aefe7cSBram Moolenaar
1098aefe7cSBram Moolenaar /*
1145dd07f1SBram Moolenaar * Text properties implementation. See ":help text-properties".
1298aefe7cSBram Moolenaar *
1398aefe7cSBram Moolenaar * TODO:
14b9c67a51SBram Moolenaar * - Adjust text property column and length when text is inserted/deleted.
154164bb20SBram Moolenaar * -> a :substitute with a multi-line match
16f9e3e09fSBram Moolenaar * -> search for changed_bytes() from misc1.c
1780e737ccSBram Moolenaar * -> search for mark_col_adjust()
18b9c67a51SBram Moolenaar * - Perhaps we only need TP_FLAG_CONT_NEXT and can drop TP_FLAG_CONT_PREV?
1932aa1020SBram Moolenaar * - Add an array for global_proptypes, to quickly lookup a prop type by ID
2032aa1020SBram Moolenaar * - Add an array for b_proptypes, to quickly lookup a prop type by ID
21b56ac049SBram Moolenaar * - Checking the text length to detect text properties is slow. Use a flag in
22b56ac049SBram Moolenaar * the index, like DB_MARKED?
23b413d2e6SBram Moolenaar * - Also test line2byte() with many lines, so that ml_updatechunk() is taken
24b413d2e6SBram Moolenaar * into account.
25c6663883SBram Moolenaar * - Perhaps have a window-local option to disable highlighting from text
26c6663883SBram Moolenaar * properties?
2798aefe7cSBram Moolenaar */
2898aefe7cSBram Moolenaar
2998aefe7cSBram Moolenaar #include "vim.h"
3098aefe7cSBram Moolenaar
3105ad5ff0SBram Moolenaar #if defined(FEAT_PROP_POPUP) || defined(PROTO)
3298aefe7cSBram Moolenaar
3398aefe7cSBram Moolenaar /*
3498aefe7cSBram Moolenaar * In a hashtable item "hi_key" points to "pt_name" in a proptype_T.
3598aefe7cSBram Moolenaar * This avoids adding a pointer to the hashtable item.
3698aefe7cSBram Moolenaar * PT2HIKEY() converts a proptype pointer to a hashitem key pointer.
3798aefe7cSBram Moolenaar * HIKEY2PT() converts a hashitem key pointer to a proptype pointer.
3898aefe7cSBram Moolenaar * HI2PT() converts a hashitem pointer to a proptype pointer.
3998aefe7cSBram Moolenaar */
4098aefe7cSBram Moolenaar #define PT2HIKEY(p) ((p)->pt_name)
4198aefe7cSBram Moolenaar #define HIKEY2PT(p) ((proptype_T *)((p) - offsetof(proptype_T, pt_name)))
4298aefe7cSBram Moolenaar #define HI2PT(hi) HIKEY2PT((hi)->hi_key)
4398aefe7cSBram Moolenaar
4498aefe7cSBram Moolenaar // The global text property types.
4598aefe7cSBram Moolenaar static hashtab_T *global_proptypes = NULL;
4698aefe7cSBram Moolenaar
4798aefe7cSBram Moolenaar // The last used text property type ID.
4898aefe7cSBram Moolenaar static int proptype_id = 0;
4998aefe7cSBram Moolenaar
5098aefe7cSBram Moolenaar static char_u e_type_not_exist[] = N_("E971: Property type %s does not exist");
5198aefe7cSBram Moolenaar static char_u e_invalid_col[] = N_("E964: Invalid column number: %ld");
52e3d31b02SBram Moolenaar static char_u e_invalid_lnum[] = N_("E966: Invalid line number: %ld");
5398aefe7cSBram Moolenaar
5498aefe7cSBram Moolenaar /*
5598aefe7cSBram Moolenaar * Find a property type by name, return the hashitem.
5698aefe7cSBram Moolenaar * Returns NULL if the item can't be found.
5798aefe7cSBram Moolenaar */
5898aefe7cSBram Moolenaar static hashitem_T *
find_prop_hi(char_u * name,buf_T * buf)5998aefe7cSBram Moolenaar find_prop_hi(char_u *name, buf_T *buf)
6098aefe7cSBram Moolenaar {
6198aefe7cSBram Moolenaar hashtab_T *ht;
6298aefe7cSBram Moolenaar hashitem_T *hi;
6398aefe7cSBram Moolenaar
6498aefe7cSBram Moolenaar if (*name == NUL)
6598aefe7cSBram Moolenaar return NULL;
6698aefe7cSBram Moolenaar if (buf == NULL)
6798aefe7cSBram Moolenaar ht = global_proptypes;
6898aefe7cSBram Moolenaar else
6998aefe7cSBram Moolenaar ht = buf->b_proptypes;
7098aefe7cSBram Moolenaar
7198aefe7cSBram Moolenaar if (ht == NULL)
7298aefe7cSBram Moolenaar return NULL;
7398aefe7cSBram Moolenaar hi = hash_find(ht, name);
7498aefe7cSBram Moolenaar if (HASHITEM_EMPTY(hi))
7598aefe7cSBram Moolenaar return NULL;
7698aefe7cSBram Moolenaar return hi;
7798aefe7cSBram Moolenaar }
7898aefe7cSBram Moolenaar
7998aefe7cSBram Moolenaar /*
8098aefe7cSBram Moolenaar * Like find_prop_hi() but return the property type.
8198aefe7cSBram Moolenaar */
8298aefe7cSBram Moolenaar static proptype_T *
find_prop(char_u * name,buf_T * buf)8398aefe7cSBram Moolenaar find_prop(char_u *name, buf_T *buf)
8498aefe7cSBram Moolenaar {
8598aefe7cSBram Moolenaar hashitem_T *hi = find_prop_hi(name, buf);
8698aefe7cSBram Moolenaar
8798aefe7cSBram Moolenaar if (hi == NULL)
8898aefe7cSBram Moolenaar return NULL;
8998aefe7cSBram Moolenaar return HI2PT(hi);
9098aefe7cSBram Moolenaar }
9198aefe7cSBram Moolenaar
9298aefe7cSBram Moolenaar /*
9312034e22SBram Moolenaar * Get the prop type ID of "name".
9412034e22SBram Moolenaar * When not found return zero.
9512034e22SBram Moolenaar */
9612034e22SBram Moolenaar int
find_prop_type_id(char_u * name,buf_T * buf)9712034e22SBram Moolenaar find_prop_type_id(char_u *name, buf_T *buf)
9812034e22SBram Moolenaar {
9912034e22SBram Moolenaar proptype_T *pt = find_prop(name, buf);
10012034e22SBram Moolenaar
10112034e22SBram Moolenaar if (pt == NULL)
10212034e22SBram Moolenaar return 0;
10312034e22SBram Moolenaar return pt->pt_id;
10412034e22SBram Moolenaar }
10512034e22SBram Moolenaar
10612034e22SBram Moolenaar /*
10798aefe7cSBram Moolenaar * Lookup a property type by name. First in "buf" and when not found in the
10898aefe7cSBram Moolenaar * global types.
10998aefe7cSBram Moolenaar * When not found gives an error message and returns NULL.
11098aefe7cSBram Moolenaar */
11198aefe7cSBram Moolenaar static proptype_T *
lookup_prop_type(char_u * name,buf_T * buf)11298aefe7cSBram Moolenaar lookup_prop_type(char_u *name, buf_T *buf)
11398aefe7cSBram Moolenaar {
11498aefe7cSBram Moolenaar proptype_T *type = find_prop(name, buf);
11598aefe7cSBram Moolenaar
11698aefe7cSBram Moolenaar if (type == NULL)
11798aefe7cSBram Moolenaar type = find_prop(name, NULL);
11898aefe7cSBram Moolenaar if (type == NULL)
119f9e3e09fSBram Moolenaar semsg(_(e_type_not_exist), name);
12098aefe7cSBram Moolenaar return type;
12198aefe7cSBram Moolenaar }
12298aefe7cSBram Moolenaar
12398aefe7cSBram Moolenaar /*
12498aefe7cSBram Moolenaar * Get an optional "bufnr" item from the dict in "arg".
12598aefe7cSBram Moolenaar * When the argument is not used or "bufnr" is not present then "buf" is
12698aefe7cSBram Moolenaar * unchanged.
12798aefe7cSBram Moolenaar * If "bufnr" is valid or not present return OK.
12832aa1020SBram Moolenaar * When "arg" is not a dict or "bufnr" is invalid return FAIL.
12998aefe7cSBram Moolenaar */
13098aefe7cSBram Moolenaar static int
get_bufnr_from_arg(typval_T * arg,buf_T ** buf)13198aefe7cSBram Moolenaar get_bufnr_from_arg(typval_T *arg, buf_T **buf)
13298aefe7cSBram Moolenaar {
13398aefe7cSBram Moolenaar dictitem_T *di;
13498aefe7cSBram Moolenaar
13598aefe7cSBram Moolenaar if (arg->v_type != VAR_DICT)
13698aefe7cSBram Moolenaar {
137f9e3e09fSBram Moolenaar emsg(_(e_dictreq));
13898aefe7cSBram Moolenaar return FAIL;
13998aefe7cSBram Moolenaar }
14098aefe7cSBram Moolenaar if (arg->vval.v_dict == NULL)
14198aefe7cSBram Moolenaar return OK; // NULL dict is like an empty dict
14298aefe7cSBram Moolenaar di = dict_find(arg->vval.v_dict, (char_u *)"bufnr", -1);
143e2390c7fSMartin Tournoij if (di != NULL && (di->di_tv.v_type != VAR_NUMBER
144e2390c7fSMartin Tournoij || di->di_tv.vval.v_number != 0))
14598aefe7cSBram Moolenaar {
146f0884c5fSBram Moolenaar *buf = get_buf_arg(&di->di_tv);
14798aefe7cSBram Moolenaar if (*buf == NULL)
14898aefe7cSBram Moolenaar return FAIL;
14998aefe7cSBram Moolenaar }
15098aefe7cSBram Moolenaar return OK;
15198aefe7cSBram Moolenaar }
15298aefe7cSBram Moolenaar
15398aefe7cSBram Moolenaar /*
15498aefe7cSBram Moolenaar * prop_add({lnum}, {col}, {props})
15598aefe7cSBram Moolenaar */
15698aefe7cSBram Moolenaar void
f_prop_add(typval_T * argvars,typval_T * rettv UNUSED)15798aefe7cSBram Moolenaar f_prop_add(typval_T *argvars, typval_T *rettv UNUSED)
15898aefe7cSBram Moolenaar {
159e3d31b02SBram Moolenaar linenr_T start_lnum;
160e3d31b02SBram Moolenaar colnr_T start_col;
16198aefe7cSBram Moolenaar
16283494b4aSYegappan Lakshmanan if (in_vim9script()
16383494b4aSYegappan Lakshmanan && (check_for_number_arg(argvars, 0) == FAIL
16483494b4aSYegappan Lakshmanan || check_for_number_arg(argvars, 1) == FAIL
16583494b4aSYegappan Lakshmanan || check_for_dict_arg(argvars, 2) == FAIL))
16683494b4aSYegappan Lakshmanan return;
16783494b4aSYegappan Lakshmanan
168e3d31b02SBram Moolenaar start_lnum = tv_get_number(&argvars[0]);
169e3d31b02SBram Moolenaar start_col = tv_get_number(&argvars[1]);
170e3d31b02SBram Moolenaar if (start_col < 1)
17198aefe7cSBram Moolenaar {
172f9e3e09fSBram Moolenaar semsg(_(e_invalid_col), (long)start_col);
17398aefe7cSBram Moolenaar return;
17498aefe7cSBram Moolenaar }
17598aefe7cSBram Moolenaar if (argvars[2].v_type != VAR_DICT)
17698aefe7cSBram Moolenaar {
177f9e3e09fSBram Moolenaar emsg(_(e_dictreq));
17898aefe7cSBram Moolenaar return;
17998aefe7cSBram Moolenaar }
1807a8d0278SBram Moolenaar
1817a8d0278SBram Moolenaar prop_add_common(start_lnum, start_col, argvars[2].vval.v_dict,
1827a8d0278SBram Moolenaar curbuf, &argvars[2]);
1837a8d0278SBram Moolenaar }
1847a8d0278SBram Moolenaar
1857a8d0278SBram Moolenaar /*
186*ccfb7c67SYegappan Lakshmanan * Attach a text property 'type_name' to the text starting
187*ccfb7c67SYegappan Lakshmanan * at [start_lnum, start_col] and ending at [end_lnum, end_col] in
188*ccfb7c67SYegappan Lakshmanan * the buffer 'buf' and assign identifier 'id'.
189*ccfb7c67SYegappan Lakshmanan */
190*ccfb7c67SYegappan Lakshmanan static int
prop_add_one(buf_T * buf,char_u * type_name,int id,linenr_T start_lnum,linenr_T end_lnum,colnr_T start_col,colnr_T end_col)191*ccfb7c67SYegappan Lakshmanan prop_add_one(
192*ccfb7c67SYegappan Lakshmanan buf_T *buf,
193*ccfb7c67SYegappan Lakshmanan char_u *type_name,
194*ccfb7c67SYegappan Lakshmanan int id,
195*ccfb7c67SYegappan Lakshmanan linenr_T start_lnum,
196*ccfb7c67SYegappan Lakshmanan linenr_T end_lnum,
197*ccfb7c67SYegappan Lakshmanan colnr_T start_col,
198*ccfb7c67SYegappan Lakshmanan colnr_T end_col)
199*ccfb7c67SYegappan Lakshmanan {
200*ccfb7c67SYegappan Lakshmanan proptype_T *type;
201*ccfb7c67SYegappan Lakshmanan linenr_T lnum;
202*ccfb7c67SYegappan Lakshmanan int proplen;
203*ccfb7c67SYegappan Lakshmanan char_u *props = NULL;
204*ccfb7c67SYegappan Lakshmanan char_u *newprops;
205*ccfb7c67SYegappan Lakshmanan size_t textlen;
206*ccfb7c67SYegappan Lakshmanan char_u *newtext;
207*ccfb7c67SYegappan Lakshmanan int i;
208*ccfb7c67SYegappan Lakshmanan textprop_T tmp_prop;
209*ccfb7c67SYegappan Lakshmanan
210*ccfb7c67SYegappan Lakshmanan type = lookup_prop_type(type_name, buf);
211*ccfb7c67SYegappan Lakshmanan if (type == NULL)
212*ccfb7c67SYegappan Lakshmanan return FAIL;
213*ccfb7c67SYegappan Lakshmanan
214*ccfb7c67SYegappan Lakshmanan if (start_lnum < 1 || start_lnum > buf->b_ml.ml_line_count)
215*ccfb7c67SYegappan Lakshmanan {
216*ccfb7c67SYegappan Lakshmanan semsg(_(e_invalid_lnum), (long)start_lnum);
217*ccfb7c67SYegappan Lakshmanan return FAIL;
218*ccfb7c67SYegappan Lakshmanan }
219*ccfb7c67SYegappan Lakshmanan if (end_lnum < start_lnum || end_lnum > buf->b_ml.ml_line_count)
220*ccfb7c67SYegappan Lakshmanan {
221*ccfb7c67SYegappan Lakshmanan semsg(_(e_invalid_lnum), (long)end_lnum);
222*ccfb7c67SYegappan Lakshmanan return FAIL;
223*ccfb7c67SYegappan Lakshmanan }
224*ccfb7c67SYegappan Lakshmanan
225*ccfb7c67SYegappan Lakshmanan if (buf->b_ml.ml_mfp == NULL)
226*ccfb7c67SYegappan Lakshmanan {
227*ccfb7c67SYegappan Lakshmanan emsg(_("E275: Cannot add text property to unloaded buffer"));
228*ccfb7c67SYegappan Lakshmanan return FAIL;
229*ccfb7c67SYegappan Lakshmanan }
230*ccfb7c67SYegappan Lakshmanan
231*ccfb7c67SYegappan Lakshmanan for (lnum = start_lnum; lnum <= end_lnum; ++lnum)
232*ccfb7c67SYegappan Lakshmanan {
233*ccfb7c67SYegappan Lakshmanan colnr_T col; // start column
234*ccfb7c67SYegappan Lakshmanan long length; // in bytes
235*ccfb7c67SYegappan Lakshmanan
236*ccfb7c67SYegappan Lakshmanan // Fetch the line to get the ml_line_len field updated.
237*ccfb7c67SYegappan Lakshmanan proplen = get_text_props(buf, lnum, &props, TRUE);
238*ccfb7c67SYegappan Lakshmanan textlen = buf->b_ml.ml_line_len - proplen * sizeof(textprop_T);
239*ccfb7c67SYegappan Lakshmanan
240*ccfb7c67SYegappan Lakshmanan if (lnum == start_lnum)
241*ccfb7c67SYegappan Lakshmanan col = start_col;
242*ccfb7c67SYegappan Lakshmanan else
243*ccfb7c67SYegappan Lakshmanan col = 1;
244*ccfb7c67SYegappan Lakshmanan if (col - 1 > (colnr_T)textlen)
245*ccfb7c67SYegappan Lakshmanan {
246*ccfb7c67SYegappan Lakshmanan semsg(_(e_invalid_col), (long)start_col);
247*ccfb7c67SYegappan Lakshmanan return FAIL;
248*ccfb7c67SYegappan Lakshmanan }
249*ccfb7c67SYegappan Lakshmanan
250*ccfb7c67SYegappan Lakshmanan if (lnum == end_lnum)
251*ccfb7c67SYegappan Lakshmanan length = end_col - col;
252*ccfb7c67SYegappan Lakshmanan else
253*ccfb7c67SYegappan Lakshmanan length = (int)textlen - col + 1;
254*ccfb7c67SYegappan Lakshmanan if (length > (long)textlen)
255*ccfb7c67SYegappan Lakshmanan length = (int)textlen; // can include the end-of-line
256*ccfb7c67SYegappan Lakshmanan if (length < 0)
257*ccfb7c67SYegappan Lakshmanan length = 0; // zero-width property
258*ccfb7c67SYegappan Lakshmanan
259*ccfb7c67SYegappan Lakshmanan // Allocate the new line with space for the new property.
260*ccfb7c67SYegappan Lakshmanan newtext = alloc(buf->b_ml.ml_line_len + sizeof(textprop_T));
261*ccfb7c67SYegappan Lakshmanan if (newtext == NULL)
262*ccfb7c67SYegappan Lakshmanan return FAIL;
263*ccfb7c67SYegappan Lakshmanan // Copy the text, including terminating NUL.
264*ccfb7c67SYegappan Lakshmanan mch_memmove(newtext, buf->b_ml.ml_line_ptr, textlen);
265*ccfb7c67SYegappan Lakshmanan
266*ccfb7c67SYegappan Lakshmanan // Find the index where to insert the new property.
267*ccfb7c67SYegappan Lakshmanan // Since the text properties are not aligned properly when stored with
268*ccfb7c67SYegappan Lakshmanan // the text, we need to copy them as bytes before using it as a struct.
269*ccfb7c67SYegappan Lakshmanan for (i = 0; i < proplen; ++i)
270*ccfb7c67SYegappan Lakshmanan {
271*ccfb7c67SYegappan Lakshmanan mch_memmove(&tmp_prop, props + i * sizeof(textprop_T),
272*ccfb7c67SYegappan Lakshmanan sizeof(textprop_T));
273*ccfb7c67SYegappan Lakshmanan if (tmp_prop.tp_col >= col)
274*ccfb7c67SYegappan Lakshmanan break;
275*ccfb7c67SYegappan Lakshmanan }
276*ccfb7c67SYegappan Lakshmanan newprops = newtext + textlen;
277*ccfb7c67SYegappan Lakshmanan if (i > 0)
278*ccfb7c67SYegappan Lakshmanan mch_memmove(newprops, props, sizeof(textprop_T) * i);
279*ccfb7c67SYegappan Lakshmanan
280*ccfb7c67SYegappan Lakshmanan tmp_prop.tp_col = col;
281*ccfb7c67SYegappan Lakshmanan tmp_prop.tp_len = length;
282*ccfb7c67SYegappan Lakshmanan tmp_prop.tp_id = id;
283*ccfb7c67SYegappan Lakshmanan tmp_prop.tp_type = type->pt_id;
284*ccfb7c67SYegappan Lakshmanan tmp_prop.tp_flags = (lnum > start_lnum ? TP_FLAG_CONT_PREV : 0)
285*ccfb7c67SYegappan Lakshmanan | (lnum < end_lnum ? TP_FLAG_CONT_NEXT : 0);
286*ccfb7c67SYegappan Lakshmanan mch_memmove(newprops + i * sizeof(textprop_T), &tmp_prop,
287*ccfb7c67SYegappan Lakshmanan sizeof(textprop_T));
288*ccfb7c67SYegappan Lakshmanan
289*ccfb7c67SYegappan Lakshmanan if (i < proplen)
290*ccfb7c67SYegappan Lakshmanan mch_memmove(newprops + (i + 1) * sizeof(textprop_T),
291*ccfb7c67SYegappan Lakshmanan props + i * sizeof(textprop_T),
292*ccfb7c67SYegappan Lakshmanan sizeof(textprop_T) * (proplen - i));
293*ccfb7c67SYegappan Lakshmanan
294*ccfb7c67SYegappan Lakshmanan if (buf->b_ml.ml_flags & ML_LINE_DIRTY)
295*ccfb7c67SYegappan Lakshmanan vim_free(buf->b_ml.ml_line_ptr);
296*ccfb7c67SYegappan Lakshmanan buf->b_ml.ml_line_ptr = newtext;
297*ccfb7c67SYegappan Lakshmanan buf->b_ml.ml_line_len += sizeof(textprop_T);
298*ccfb7c67SYegappan Lakshmanan buf->b_ml.ml_flags |= ML_LINE_DIRTY;
299*ccfb7c67SYegappan Lakshmanan }
300*ccfb7c67SYegappan Lakshmanan
301*ccfb7c67SYegappan Lakshmanan changed_lines_buf(buf, start_lnum, end_lnum + 1, 0);
302*ccfb7c67SYegappan Lakshmanan return OK;
303*ccfb7c67SYegappan Lakshmanan }
304*ccfb7c67SYegappan Lakshmanan
305*ccfb7c67SYegappan Lakshmanan /*
306*ccfb7c67SYegappan Lakshmanan * prop_add_list()
307*ccfb7c67SYegappan Lakshmanan * First argument specifies the text property:
308*ccfb7c67SYegappan Lakshmanan * {'type': <str>, 'id': <num>, 'bufnr': <num>}
309*ccfb7c67SYegappan Lakshmanan * Second argument is a List where each item is a List with the following
310*ccfb7c67SYegappan Lakshmanan * entries: [lnum, start_col, end_col]
311*ccfb7c67SYegappan Lakshmanan */
312*ccfb7c67SYegappan Lakshmanan void
f_prop_add_list(typval_T * argvars,typval_T * rettv UNUSED)313*ccfb7c67SYegappan Lakshmanan f_prop_add_list(typval_T *argvars, typval_T *rettv UNUSED)
314*ccfb7c67SYegappan Lakshmanan {
315*ccfb7c67SYegappan Lakshmanan dict_T *dict;
316*ccfb7c67SYegappan Lakshmanan char_u *type_name;
317*ccfb7c67SYegappan Lakshmanan buf_T *buf = curbuf;
318*ccfb7c67SYegappan Lakshmanan int id = 0;
319*ccfb7c67SYegappan Lakshmanan listitem_T *li;
320*ccfb7c67SYegappan Lakshmanan list_T *pos_list;
321*ccfb7c67SYegappan Lakshmanan linenr_T start_lnum;
322*ccfb7c67SYegappan Lakshmanan colnr_T start_col;
323*ccfb7c67SYegappan Lakshmanan linenr_T end_lnum;
324*ccfb7c67SYegappan Lakshmanan colnr_T end_col;
325*ccfb7c67SYegappan Lakshmanan int error = FALSE;
326*ccfb7c67SYegappan Lakshmanan
327*ccfb7c67SYegappan Lakshmanan if (check_for_dict_arg(argvars, 0) == FAIL
328*ccfb7c67SYegappan Lakshmanan || check_for_list_arg(argvars, 1) == FAIL)
329*ccfb7c67SYegappan Lakshmanan return;
330*ccfb7c67SYegappan Lakshmanan
331*ccfb7c67SYegappan Lakshmanan if (argvars[1].vval.v_list == NULL)
332*ccfb7c67SYegappan Lakshmanan {
333*ccfb7c67SYegappan Lakshmanan emsg(_(e_listreq));
334*ccfb7c67SYegappan Lakshmanan return;
335*ccfb7c67SYegappan Lakshmanan }
336*ccfb7c67SYegappan Lakshmanan
337*ccfb7c67SYegappan Lakshmanan dict = argvars[0].vval.v_dict;
338*ccfb7c67SYegappan Lakshmanan if (dict == NULL || dict_find(dict, (char_u *)"type", -1) == NULL)
339*ccfb7c67SYegappan Lakshmanan {
340*ccfb7c67SYegappan Lakshmanan emsg(_("E965: missing property type name"));
341*ccfb7c67SYegappan Lakshmanan return;
342*ccfb7c67SYegappan Lakshmanan }
343*ccfb7c67SYegappan Lakshmanan type_name = dict_get_string(dict, (char_u *)"type", FALSE);
344*ccfb7c67SYegappan Lakshmanan
345*ccfb7c67SYegappan Lakshmanan if (dict_find(dict, (char_u *)"id", -1) != NULL)
346*ccfb7c67SYegappan Lakshmanan id = dict_get_number(dict, (char_u *)"id");
347*ccfb7c67SYegappan Lakshmanan
348*ccfb7c67SYegappan Lakshmanan if (get_bufnr_from_arg(&argvars[0], &buf) == FAIL)
349*ccfb7c67SYegappan Lakshmanan return;
350*ccfb7c67SYegappan Lakshmanan
351*ccfb7c67SYegappan Lakshmanan FOR_ALL_LIST_ITEMS(argvars[1].vval.v_list, li)
352*ccfb7c67SYegappan Lakshmanan {
353*ccfb7c67SYegappan Lakshmanan if (li->li_tv.v_type != VAR_LIST || li->li_tv.vval.v_list == NULL)
354*ccfb7c67SYegappan Lakshmanan {
355*ccfb7c67SYegappan Lakshmanan emsg(_(e_listreq));
356*ccfb7c67SYegappan Lakshmanan return;
357*ccfb7c67SYegappan Lakshmanan }
358*ccfb7c67SYegappan Lakshmanan
359*ccfb7c67SYegappan Lakshmanan pos_list = li->li_tv.vval.v_list;
360*ccfb7c67SYegappan Lakshmanan start_lnum = list_find_nr(pos_list, 0L, &error);
361*ccfb7c67SYegappan Lakshmanan start_col = list_find_nr(pos_list, 1L, &error);
362*ccfb7c67SYegappan Lakshmanan end_lnum = list_find_nr(pos_list, 2L, &error);
363*ccfb7c67SYegappan Lakshmanan end_col = list_find_nr(pos_list, 3L, &error);
364*ccfb7c67SYegappan Lakshmanan if (error || start_lnum <= 0 || start_col <= 0
365*ccfb7c67SYegappan Lakshmanan || end_lnum <= 0 || end_col <= 0)
366*ccfb7c67SYegappan Lakshmanan {
367*ccfb7c67SYegappan Lakshmanan emsg(_(e_invarg));
368*ccfb7c67SYegappan Lakshmanan return;
369*ccfb7c67SYegappan Lakshmanan }
370*ccfb7c67SYegappan Lakshmanan if (prop_add_one(buf, type_name, id, start_lnum, end_lnum,
371*ccfb7c67SYegappan Lakshmanan start_col, end_col) == FAIL)
372*ccfb7c67SYegappan Lakshmanan return;
373*ccfb7c67SYegappan Lakshmanan }
374*ccfb7c67SYegappan Lakshmanan
375*ccfb7c67SYegappan Lakshmanan buf->b_has_textprop = TRUE; // this is never reset
376*ccfb7c67SYegappan Lakshmanan redraw_buf_later(buf, VALID);
377*ccfb7c67SYegappan Lakshmanan }
378*ccfb7c67SYegappan Lakshmanan
379*ccfb7c67SYegappan Lakshmanan /*
3807a8d0278SBram Moolenaar * Shared between prop_add() and popup_create().
3817a8d0278SBram Moolenaar * "dict_arg" is the function argument of a dict containing "bufnr".
3827a8d0278SBram Moolenaar * it is NULL for popup_create().
3837a8d0278SBram Moolenaar */
3847a8d0278SBram Moolenaar void
prop_add_common(linenr_T start_lnum,colnr_T start_col,dict_T * dict,buf_T * default_buf,typval_T * dict_arg)3857a8d0278SBram Moolenaar prop_add_common(
3867a8d0278SBram Moolenaar linenr_T start_lnum,
3877a8d0278SBram Moolenaar colnr_T start_col,
3887a8d0278SBram Moolenaar dict_T *dict,
3897a8d0278SBram Moolenaar buf_T *default_buf,
3907a8d0278SBram Moolenaar typval_T *dict_arg)
3917a8d0278SBram Moolenaar {
3927a8d0278SBram Moolenaar linenr_T end_lnum;
3937a8d0278SBram Moolenaar colnr_T end_col;
3947a8d0278SBram Moolenaar char_u *type_name;
3957a8d0278SBram Moolenaar buf_T *buf = default_buf;
3967a8d0278SBram Moolenaar int id = 0;
39798aefe7cSBram Moolenaar
39898aefe7cSBram Moolenaar if (dict == NULL || dict_find(dict, (char_u *)"type", -1) == NULL)
39998aefe7cSBram Moolenaar {
400f9e3e09fSBram Moolenaar emsg(_("E965: missing property type name"));
40198aefe7cSBram Moolenaar return;
40298aefe7cSBram Moolenaar }
4038f66717aSBram Moolenaar type_name = dict_get_string(dict, (char_u *)"type", FALSE);
40498aefe7cSBram Moolenaar
40598aefe7cSBram Moolenaar if (dict_find(dict, (char_u *)"end_lnum", -1) != NULL)
40698aefe7cSBram Moolenaar {
407e3d31b02SBram Moolenaar end_lnum = dict_get_number(dict, (char_u *)"end_lnum");
408e3d31b02SBram Moolenaar if (end_lnum < start_lnum)
409e3d31b02SBram Moolenaar {
410f9e3e09fSBram Moolenaar semsg(_(e_invargval), "end_lnum");
41198aefe7cSBram Moolenaar return;
41298aefe7cSBram Moolenaar }
413e3d31b02SBram Moolenaar }
414e3d31b02SBram Moolenaar else
415e3d31b02SBram Moolenaar end_lnum = start_lnum;
41698aefe7cSBram Moolenaar
41798aefe7cSBram Moolenaar if (dict_find(dict, (char_u *)"length", -1) != NULL)
418e3d31b02SBram Moolenaar {
419e3d31b02SBram Moolenaar long length = dict_get_number(dict, (char_u *)"length");
420e3d31b02SBram Moolenaar
421b9c67a51SBram Moolenaar if (length < 0 || end_lnum > start_lnum)
422e3d31b02SBram Moolenaar {
423f9e3e09fSBram Moolenaar semsg(_(e_invargval), "length");
424e3d31b02SBram Moolenaar return;
425e3d31b02SBram Moolenaar }
426b9c67a51SBram Moolenaar end_col = start_col + length;
427e3d31b02SBram Moolenaar }
42898aefe7cSBram Moolenaar else if (dict_find(dict, (char_u *)"end_col", -1) != NULL)
42998aefe7cSBram Moolenaar {
430e3d31b02SBram Moolenaar end_col = dict_get_number(dict, (char_u *)"end_col");
431e3d31b02SBram Moolenaar if (end_col <= 0)
43298aefe7cSBram Moolenaar {
433f9e3e09fSBram Moolenaar semsg(_(e_invargval), "end_col");
43498aefe7cSBram Moolenaar return;
43598aefe7cSBram Moolenaar }
43698aefe7cSBram Moolenaar }
437e3d31b02SBram Moolenaar else if (start_lnum == end_lnum)
438e3d31b02SBram Moolenaar end_col = start_col;
439e3d31b02SBram Moolenaar else
440e3d31b02SBram Moolenaar end_col = 1;
44198aefe7cSBram Moolenaar
44298aefe7cSBram Moolenaar if (dict_find(dict, (char_u *)"id", -1) != NULL)
4438f66717aSBram Moolenaar id = dict_get_number(dict, (char_u *)"id");
44498aefe7cSBram Moolenaar
4457a8d0278SBram Moolenaar if (dict_arg != NULL && get_bufnr_from_arg(dict_arg, &buf) == FAIL)
44698aefe7cSBram Moolenaar return;
44798aefe7cSBram Moolenaar
448*ccfb7c67SYegappan Lakshmanan prop_add_one(buf, type_name, id, start_lnum, end_lnum, start_col, end_col);
44998aefe7cSBram Moolenaar
450b413d2e6SBram Moolenaar buf->b_has_textprop = TRUE; // this is never reset
4511764faa3SBram Moolenaar redraw_buf_later(buf, VALID);
45298aefe7cSBram Moolenaar }
45398aefe7cSBram Moolenaar
45498aefe7cSBram Moolenaar /*
455fb95e212SBram Moolenaar * Fetch the text properties for line "lnum" in buffer "buf".
45698aefe7cSBram Moolenaar * Returns the number of text properties and, when non-zero, a pointer to the
45798aefe7cSBram Moolenaar * first one in "props" (note that it is not aligned, therefore the char_u
45898aefe7cSBram Moolenaar * pointer).
45998aefe7cSBram Moolenaar */
46098aefe7cSBram Moolenaar int
get_text_props(buf_T * buf,linenr_T lnum,char_u ** props,int will_change)46198aefe7cSBram Moolenaar get_text_props(buf_T *buf, linenr_T lnum, char_u **props, int will_change)
46298aefe7cSBram Moolenaar {
46398aefe7cSBram Moolenaar char_u *text;
46498aefe7cSBram Moolenaar size_t textlen;
46598aefe7cSBram Moolenaar size_t proplen;
46698aefe7cSBram Moolenaar
467b413d2e6SBram Moolenaar // Be quick when no text property types have been defined or the buffer,
468b413d2e6SBram Moolenaar // unless we are adding one.
469d79eef2eSBram Moolenaar if ((!buf->b_has_textprop && !will_change) || buf->b_ml.ml_mfp == NULL)
47098aefe7cSBram Moolenaar return 0;
47198aefe7cSBram Moolenaar
47298aefe7cSBram Moolenaar // Fetch the line to get the ml_line_len field updated.
47398aefe7cSBram Moolenaar text = ml_get_buf(buf, lnum, will_change);
47498aefe7cSBram Moolenaar textlen = STRLEN(text) + 1;
47598aefe7cSBram Moolenaar proplen = buf->b_ml.ml_line_len - textlen;
47698aefe7cSBram Moolenaar if (proplen % sizeof(textprop_T) != 0)
47798aefe7cSBram Moolenaar {
478f9e3e09fSBram Moolenaar iemsg(_("E967: text property info corrupted"));
47998aefe7cSBram Moolenaar return 0;
48098aefe7cSBram Moolenaar }
48198aefe7cSBram Moolenaar if (proplen > 0)
48298aefe7cSBram Moolenaar *props = text + textlen;
4834efe73b4SBram Moolenaar return (int)(proplen / sizeof(textprop_T));
48498aefe7cSBram Moolenaar }
48598aefe7cSBram Moolenaar
48687be9be1SBram Moolenaar /**
48787be9be1SBram Moolenaar * Return the number of text properties on line "lnum" in the current buffer.
48887be9be1SBram Moolenaar * When "only_starting" is true only text properties starting in this line will
48987be9be1SBram Moolenaar * be considered.
49087be9be1SBram Moolenaar */
49187be9be1SBram Moolenaar int
count_props(linenr_T lnum,int only_starting)49287be9be1SBram Moolenaar count_props(linenr_T lnum, int only_starting)
49387be9be1SBram Moolenaar {
49487be9be1SBram Moolenaar char_u *props;
49587be9be1SBram Moolenaar int proplen = get_text_props(curbuf, lnum, &props, 0);
49687be9be1SBram Moolenaar int result = proplen;
49787be9be1SBram Moolenaar int i;
49887be9be1SBram Moolenaar textprop_T prop;
49987be9be1SBram Moolenaar
50087be9be1SBram Moolenaar if (only_starting)
50187be9be1SBram Moolenaar for (i = 0; i < proplen; ++i)
50287be9be1SBram Moolenaar {
50387be9be1SBram Moolenaar mch_memmove(&prop, props + i * sizeof(prop), sizeof(prop));
50487be9be1SBram Moolenaar if (prop.tp_flags & TP_FLAG_CONT_PREV)
50587be9be1SBram Moolenaar --result;
50687be9be1SBram Moolenaar }
50787be9be1SBram Moolenaar return result;
50887be9be1SBram Moolenaar }
50987be9be1SBram Moolenaar
5104164bb20SBram Moolenaar /*
51112034e22SBram Moolenaar * Find text property "type_id" in the visible lines of window "wp".
51212034e22SBram Moolenaar * Match "id" when it is > 0.
51312034e22SBram Moolenaar * Returns FAIL when not found.
51412034e22SBram Moolenaar */
51512034e22SBram Moolenaar int
find_visible_prop(win_T * wp,int type_id,int id,textprop_T * prop,linenr_T * found_lnum)51612034e22SBram Moolenaar find_visible_prop(win_T *wp, int type_id, int id, textprop_T *prop,
51712034e22SBram Moolenaar linenr_T *found_lnum)
51812034e22SBram Moolenaar {
51912034e22SBram Moolenaar linenr_T lnum;
52012034e22SBram Moolenaar char_u *props;
52112034e22SBram Moolenaar int count;
52212034e22SBram Moolenaar int i;
52312034e22SBram Moolenaar
52412034e22SBram Moolenaar // w_botline may not have been updated yet.
52523999d79SBram Moolenaar validate_botline_win(wp);
52612034e22SBram Moolenaar for (lnum = wp->w_topline; lnum < wp->w_botline; ++lnum)
52712034e22SBram Moolenaar {
52812034e22SBram Moolenaar count = get_text_props(wp->w_buffer, lnum, &props, FALSE);
52912034e22SBram Moolenaar for (i = 0; i < count; ++i)
53012034e22SBram Moolenaar {
53112034e22SBram Moolenaar mch_memmove(prop, props + i * sizeof(textprop_T),
53212034e22SBram Moolenaar sizeof(textprop_T));
53312034e22SBram Moolenaar if (prop->tp_type == type_id && (id <= 0 || prop->tp_id == id))
53412034e22SBram Moolenaar {
53512034e22SBram Moolenaar *found_lnum = lnum;
53612034e22SBram Moolenaar return OK;
53712034e22SBram Moolenaar }
53812034e22SBram Moolenaar }
53912034e22SBram Moolenaar }
54012034e22SBram Moolenaar return FAIL;
54112034e22SBram Moolenaar }
54212034e22SBram Moolenaar
54312034e22SBram Moolenaar /*
5444164bb20SBram Moolenaar * Set the text properties for line "lnum" to "props" with length "len".
5454164bb20SBram Moolenaar * If "len" is zero text properties are removed, "props" is not used.
5464164bb20SBram Moolenaar * Any existing text properties are dropped.
5474164bb20SBram Moolenaar * Only works for the current buffer.
5484164bb20SBram Moolenaar */
5494164bb20SBram Moolenaar static void
set_text_props(linenr_T lnum,char_u * props,int len)5504164bb20SBram Moolenaar set_text_props(linenr_T lnum, char_u *props, int len)
5514164bb20SBram Moolenaar {
5524164bb20SBram Moolenaar char_u *text;
5534164bb20SBram Moolenaar char_u *newtext;
5548aef43b6SBram Moolenaar int textlen;
5554164bb20SBram Moolenaar
5564164bb20SBram Moolenaar text = ml_get(lnum);
5578aef43b6SBram Moolenaar textlen = (int)STRLEN(text) + 1;
5584164bb20SBram Moolenaar newtext = alloc(textlen + len);
5594164bb20SBram Moolenaar if (newtext == NULL)
5604164bb20SBram Moolenaar return;
5614164bb20SBram Moolenaar mch_memmove(newtext, text, textlen);
5624164bb20SBram Moolenaar if (len > 0)
5634164bb20SBram Moolenaar mch_memmove(newtext + textlen, props, len);
5644164bb20SBram Moolenaar if (curbuf->b_ml.ml_flags & ML_LINE_DIRTY)
5654164bb20SBram Moolenaar vim_free(curbuf->b_ml.ml_line_ptr);
5664164bb20SBram Moolenaar curbuf->b_ml.ml_line_ptr = newtext;
5674164bb20SBram Moolenaar curbuf->b_ml.ml_line_len = textlen + len;
5684164bb20SBram Moolenaar curbuf->b_ml.ml_flags |= ML_LINE_DIRTY;
5694164bb20SBram Moolenaar }
5704164bb20SBram Moolenaar
57198aefe7cSBram Moolenaar static proptype_T *
find_type_by_id(hashtab_T * ht,int id)57298aefe7cSBram Moolenaar find_type_by_id(hashtab_T *ht, int id)
57398aefe7cSBram Moolenaar {
57498aefe7cSBram Moolenaar long todo;
57598aefe7cSBram Moolenaar hashitem_T *hi;
57698aefe7cSBram Moolenaar
57798aefe7cSBram Moolenaar if (ht == NULL)
57898aefe7cSBram Moolenaar return NULL;
57998aefe7cSBram Moolenaar
58098aefe7cSBram Moolenaar // TODO: Make this faster by keeping a list of types sorted on ID and use
58198aefe7cSBram Moolenaar // a binary search.
58298aefe7cSBram Moolenaar
58398aefe7cSBram Moolenaar todo = (long)ht->ht_used;
58498aefe7cSBram Moolenaar for (hi = ht->ht_array; todo > 0; ++hi)
58598aefe7cSBram Moolenaar {
58698aefe7cSBram Moolenaar if (!HASHITEM_EMPTY(hi))
58798aefe7cSBram Moolenaar {
58898aefe7cSBram Moolenaar proptype_T *prop = HI2PT(hi);
58998aefe7cSBram Moolenaar
59098aefe7cSBram Moolenaar if (prop->pt_id == id)
59198aefe7cSBram Moolenaar return prop;
59298aefe7cSBram Moolenaar --todo;
59398aefe7cSBram Moolenaar }
59498aefe7cSBram Moolenaar }
59598aefe7cSBram Moolenaar return NULL;
59698aefe7cSBram Moolenaar }
59798aefe7cSBram Moolenaar
59898aefe7cSBram Moolenaar /*
599e05a89acSBram Moolenaar * Fill 'dict' with text properties in 'prop'.
600e05a89acSBram Moolenaar */
601e05a89acSBram Moolenaar static void
prop_fill_dict(dict_T * dict,textprop_T * prop,buf_T * buf)602e05a89acSBram Moolenaar prop_fill_dict(dict_T *dict, textprop_T *prop, buf_T *buf)
603e05a89acSBram Moolenaar {
604e05a89acSBram Moolenaar proptype_T *pt;
605e2390c7fSMartin Tournoij int buflocal = TRUE;
606e05a89acSBram Moolenaar
607e05a89acSBram Moolenaar dict_add_number(dict, "col", prop->tp_col);
608e05a89acSBram Moolenaar dict_add_number(dict, "length", prop->tp_len);
609e05a89acSBram Moolenaar dict_add_number(dict, "id", prop->tp_id);
610e05a89acSBram Moolenaar dict_add_number(dict, "start", !(prop->tp_flags & TP_FLAG_CONT_PREV));
611e05a89acSBram Moolenaar dict_add_number(dict, "end", !(prop->tp_flags & TP_FLAG_CONT_NEXT));
612e2390c7fSMartin Tournoij
613e2390c7fSMartin Tournoij pt = find_type_by_id(buf->b_proptypes, prop->tp_type);
614e2390c7fSMartin Tournoij if (pt == NULL)
615e2390c7fSMartin Tournoij {
616e2390c7fSMartin Tournoij pt = find_type_by_id(global_proptypes, prop->tp_type);
617e2390c7fSMartin Tournoij buflocal = FALSE;
618e2390c7fSMartin Tournoij }
619e05a89acSBram Moolenaar if (pt != NULL)
620e05a89acSBram Moolenaar dict_add_string(dict, "type", pt->pt_name);
621e2390c7fSMartin Tournoij
622e2390c7fSMartin Tournoij if (buflocal)
623e2390c7fSMartin Tournoij dict_add_number(dict, "type_bufnr", buf->b_fnum);
624e2390c7fSMartin Tournoij else
625e2390c7fSMartin Tournoij dict_add_number(dict, "type_bufnr", 0);
626e05a89acSBram Moolenaar }
627e05a89acSBram Moolenaar
628e05a89acSBram Moolenaar /*
62998aefe7cSBram Moolenaar * Find a property type by ID in "buf" or globally.
63098aefe7cSBram Moolenaar * Returns NULL if not found.
63198aefe7cSBram Moolenaar */
63298aefe7cSBram Moolenaar proptype_T *
text_prop_type_by_id(buf_T * buf,int id)63398aefe7cSBram Moolenaar text_prop_type_by_id(buf_T *buf, int id)
63498aefe7cSBram Moolenaar {
63598aefe7cSBram Moolenaar proptype_T *type;
63698aefe7cSBram Moolenaar
63798aefe7cSBram Moolenaar type = find_type_by_id(buf->b_proptypes, id);
63898aefe7cSBram Moolenaar if (type == NULL)
63998aefe7cSBram Moolenaar type = find_type_by_id(global_proptypes, id);
64098aefe7cSBram Moolenaar return type;
64198aefe7cSBram Moolenaar }
64298aefe7cSBram Moolenaar
64398aefe7cSBram Moolenaar /*
64498aefe7cSBram Moolenaar * prop_clear({lnum} [, {lnum_end} [, {bufnr}]])
64598aefe7cSBram Moolenaar */
64698aefe7cSBram Moolenaar void
f_prop_clear(typval_T * argvars,typval_T * rettv UNUSED)64798aefe7cSBram Moolenaar f_prop_clear(typval_T *argvars, typval_T *rettv UNUSED)
64898aefe7cSBram Moolenaar {
64983494b4aSYegappan Lakshmanan linenr_T start;
65083494b4aSYegappan Lakshmanan linenr_T end;
65198aefe7cSBram Moolenaar linenr_T lnum;
65298aefe7cSBram Moolenaar buf_T *buf = curbuf;
653da1dbed0SBram Moolenaar int did_clear = FALSE;
65498aefe7cSBram Moolenaar
65583494b4aSYegappan Lakshmanan if (in_vim9script()
65683494b4aSYegappan Lakshmanan && (check_for_number_arg(argvars, 0) == FAIL
65783494b4aSYegappan Lakshmanan || check_for_opt_number_arg(argvars, 1) == FAIL
65883494b4aSYegappan Lakshmanan || (argvars[1].v_type != VAR_UNKNOWN
65983494b4aSYegappan Lakshmanan && check_for_opt_dict_arg(argvars, 2) == FAIL)))
66083494b4aSYegappan Lakshmanan return;
66183494b4aSYegappan Lakshmanan
66283494b4aSYegappan Lakshmanan start = tv_get_number(&argvars[0]);
66383494b4aSYegappan Lakshmanan end = start;
66498aefe7cSBram Moolenaar if (argvars[1].v_type != VAR_UNKNOWN)
66598aefe7cSBram Moolenaar {
666d155d7a8SBram Moolenaar end = tv_get_number(&argvars[1]);
66798aefe7cSBram Moolenaar if (argvars[2].v_type != VAR_UNKNOWN)
66898aefe7cSBram Moolenaar {
66998aefe7cSBram Moolenaar if (get_bufnr_from_arg(&argvars[2], &buf) == FAIL)
67098aefe7cSBram Moolenaar return;
67198aefe7cSBram Moolenaar }
67298aefe7cSBram Moolenaar }
67398aefe7cSBram Moolenaar if (start < 1 || end < 1)
67498aefe7cSBram Moolenaar {
675108010aaSBram Moolenaar emsg(_(e_invalid_range));
67698aefe7cSBram Moolenaar return;
67798aefe7cSBram Moolenaar }
67898aefe7cSBram Moolenaar
67998aefe7cSBram Moolenaar for (lnum = start; lnum <= end; ++lnum)
68098aefe7cSBram Moolenaar {
68198aefe7cSBram Moolenaar char_u *text;
68298aefe7cSBram Moolenaar size_t len;
68398aefe7cSBram Moolenaar
68498aefe7cSBram Moolenaar if (lnum > buf->b_ml.ml_line_count)
68598aefe7cSBram Moolenaar break;
68698aefe7cSBram Moolenaar text = ml_get_buf(buf, lnum, FALSE);
68798aefe7cSBram Moolenaar len = STRLEN(text) + 1;
68898aefe7cSBram Moolenaar if ((size_t)buf->b_ml.ml_line_len > len)
68998aefe7cSBram Moolenaar {
690da1dbed0SBram Moolenaar did_clear = TRUE;
69198aefe7cSBram Moolenaar if (!(buf->b_ml.ml_flags & ML_LINE_DIRTY))
69298aefe7cSBram Moolenaar {
69398aefe7cSBram Moolenaar char_u *newtext = vim_strsave(text);
69498aefe7cSBram Moolenaar
69598aefe7cSBram Moolenaar // need to allocate the line now
69698aefe7cSBram Moolenaar if (newtext == NULL)
69798aefe7cSBram Moolenaar return;
69898aefe7cSBram Moolenaar buf->b_ml.ml_line_ptr = newtext;
69998aefe7cSBram Moolenaar buf->b_ml.ml_flags |= ML_LINE_DIRTY;
70098aefe7cSBram Moolenaar }
7014efe73b4SBram Moolenaar buf->b_ml.ml_line_len = (int)len;
70298aefe7cSBram Moolenaar }
70398aefe7cSBram Moolenaar }
704da1dbed0SBram Moolenaar if (did_clear)
70598aefe7cSBram Moolenaar redraw_buf_later(buf, NOT_VALID);
70698aefe7cSBram Moolenaar }
70798aefe7cSBram Moolenaar
70898aefe7cSBram Moolenaar /*
709e05a89acSBram Moolenaar * prop_find({props} [, {direction}])
710e05a89acSBram Moolenaar */
711e05a89acSBram Moolenaar void
f_prop_find(typval_T * argvars,typval_T * rettv)712e05a89acSBram Moolenaar f_prop_find(typval_T *argvars, typval_T *rettv)
713e05a89acSBram Moolenaar {
714e05a89acSBram Moolenaar pos_T *cursor = &curwin->w_cursor;
715e05a89acSBram Moolenaar dict_T *dict;
716e05a89acSBram Moolenaar buf_T *buf = curbuf;
717e05a89acSBram Moolenaar dictitem_T *di;
718e05a89acSBram Moolenaar int lnum_start;
719e05a89acSBram Moolenaar int start_pos_has_prop = 0;
720e05a89acSBram Moolenaar int seen_end = 0;
7210d4d9ee9SBram Moolenaar int id = 0;
7220d4d9ee9SBram Moolenaar int id_found = FALSE;
723e05a89acSBram Moolenaar int type_id = -1;
724e05a89acSBram Moolenaar int skipstart = 0;
725e05a89acSBram Moolenaar int lnum = -1;
726e05a89acSBram Moolenaar int col = -1;
727e05a89acSBram Moolenaar int dir = 1; // 1 = forward, -1 = backward
72824f21fdfSBram Moolenaar int both;
729e05a89acSBram Moolenaar
7304490ec4eSYegappan Lakshmanan if (in_vim9script()
7314490ec4eSYegappan Lakshmanan && (check_for_dict_arg(argvars, 0) == FAIL
7324490ec4eSYegappan Lakshmanan || check_for_opt_string_arg(argvars, 1) == FAIL))
7334490ec4eSYegappan Lakshmanan return;
7344490ec4eSYegappan Lakshmanan
735e05a89acSBram Moolenaar if (argvars[0].v_type != VAR_DICT || argvars[0].vval.v_dict == NULL)
736e05a89acSBram Moolenaar {
7375b73992dSYegappan Lakshmanan emsg(_(e_dictreq));
738e05a89acSBram Moolenaar return;
739e05a89acSBram Moolenaar }
740e05a89acSBram Moolenaar dict = argvars[0].vval.v_dict;
741e05a89acSBram Moolenaar
742e05a89acSBram Moolenaar if (get_bufnr_from_arg(&argvars[0], &buf) == FAIL)
743e05a89acSBram Moolenaar return;
744e05a89acSBram Moolenaar if (buf->b_ml.ml_mfp == NULL)
745e05a89acSBram Moolenaar return;
746e05a89acSBram Moolenaar
747e05a89acSBram Moolenaar if (argvars[1].v_type != VAR_UNKNOWN)
748e05a89acSBram Moolenaar {
749e05a89acSBram Moolenaar char_u *dir_s = tv_get_string(&argvars[1]);
750e05a89acSBram Moolenaar
751e05a89acSBram Moolenaar if (*dir_s == 'b')
752e05a89acSBram Moolenaar dir = -1;
753e05a89acSBram Moolenaar else if (*dir_s != 'f')
754e05a89acSBram Moolenaar {
755e05a89acSBram Moolenaar emsg(_(e_invarg));
756e05a89acSBram Moolenaar return;
757e05a89acSBram Moolenaar }
758e05a89acSBram Moolenaar }
759e05a89acSBram Moolenaar
760e05a89acSBram Moolenaar di = dict_find(dict, (char_u *)"lnum", -1);
761e05a89acSBram Moolenaar if (di != NULL)
762e05a89acSBram Moolenaar lnum = tv_get_number(&di->di_tv);
763e05a89acSBram Moolenaar
764e05a89acSBram Moolenaar di = dict_find(dict, (char_u *)"col", -1);
765e05a89acSBram Moolenaar if (di != NULL)
766e05a89acSBram Moolenaar col = tv_get_number(&di->di_tv);
767e05a89acSBram Moolenaar
768e05a89acSBram Moolenaar if (lnum == -1)
769e05a89acSBram Moolenaar {
770e05a89acSBram Moolenaar lnum = cursor->lnum;
771e05a89acSBram Moolenaar col = cursor->col + 1;
772e05a89acSBram Moolenaar }
773e05a89acSBram Moolenaar else if (col == -1)
774e05a89acSBram Moolenaar col = 1;
775e05a89acSBram Moolenaar
776e05a89acSBram Moolenaar if (lnum < 1 || lnum > buf->b_ml.ml_line_count)
777e05a89acSBram Moolenaar {
778108010aaSBram Moolenaar emsg(_(e_invalid_range));
779e05a89acSBram Moolenaar return;
780e05a89acSBram Moolenaar }
781e05a89acSBram Moolenaar
782eb24556dSBram Moolenaar skipstart = dict_get_bool(dict, (char_u *)"skipstart", 0);
783e05a89acSBram Moolenaar
784e05a89acSBram Moolenaar if (dict_find(dict, (char_u *)"id", -1) != NULL)
7858e3fc135SBram Moolenaar {
786e05a89acSBram Moolenaar id = dict_get_number(dict, (char_u *)"id");
787e041dde7SBram Moolenaar id_found = TRUE;
7888e3fc135SBram Moolenaar }
789e05a89acSBram Moolenaar if (dict_find(dict, (char_u *)"type", -1))
790e05a89acSBram Moolenaar {
791e05a89acSBram Moolenaar char_u *name = dict_get_string(dict, (char_u *)"type", FALSE);
792e05a89acSBram Moolenaar proptype_T *type = lookup_prop_type(name, buf);
793e05a89acSBram Moolenaar
794e05a89acSBram Moolenaar if (type == NULL)
795e05a89acSBram Moolenaar return;
796e05a89acSBram Moolenaar type_id = type->pt_id;
797e05a89acSBram Moolenaar }
79824f21fdfSBram Moolenaar both = dict_get_bool(dict, (char_u *)"both", FALSE);
7990d4d9ee9SBram Moolenaar if (!id_found && type_id == -1)
800e05a89acSBram Moolenaar {
801e05a89acSBram Moolenaar emsg(_("E968: Need at least one of 'id' or 'type'"));
802e05a89acSBram Moolenaar return;
803e05a89acSBram Moolenaar }
8040d4d9ee9SBram Moolenaar if (both && (!id_found || type_id == -1))
80524f21fdfSBram Moolenaar {
80624f21fdfSBram Moolenaar emsg(_("E860: Need 'id' and 'type' with 'both'"));
80724f21fdfSBram Moolenaar return;
80824f21fdfSBram Moolenaar }
809e05a89acSBram Moolenaar
810e05a89acSBram Moolenaar lnum_start = lnum;
811e05a89acSBram Moolenaar
812e05a89acSBram Moolenaar if (rettv_dict_alloc(rettv) == FAIL)
813e05a89acSBram Moolenaar return;
814e05a89acSBram Moolenaar
815e05a89acSBram Moolenaar while (1)
816e05a89acSBram Moolenaar {
817e05a89acSBram Moolenaar char_u *text = ml_get_buf(buf, lnum, FALSE);
818e05a89acSBram Moolenaar size_t textlen = STRLEN(text) + 1;
819e05a89acSBram Moolenaar int count = (int)((buf->b_ml.ml_line_len - textlen)
820e05a89acSBram Moolenaar / sizeof(textprop_T));
821e05a89acSBram Moolenaar int i;
822e05a89acSBram Moolenaar textprop_T prop;
823e05a89acSBram Moolenaar int prop_start;
824e05a89acSBram Moolenaar int prop_end;
825e05a89acSBram Moolenaar
826e05a89acSBram Moolenaar for (i = 0; i < count; ++i)
827e05a89acSBram Moolenaar {
828e05a89acSBram Moolenaar mch_memmove(&prop, text + textlen + i * sizeof(textprop_T),
829e05a89acSBram Moolenaar sizeof(textprop_T));
830e05a89acSBram Moolenaar
831346f18e5SBram Moolenaar if (lnum == lnum_start)
832965fd8d4SBram Moolenaar {
83366b98854SBram Moolenaar if (dir < 0)
83466b98854SBram Moolenaar {
83566b98854SBram Moolenaar if (col < prop.tp_col)
83666b98854SBram Moolenaar break;
83766b98854SBram Moolenaar }
83866b98854SBram Moolenaar else if (prop.tp_col + prop.tp_len - (prop.tp_len != 0) < col)
83966b98854SBram Moolenaar continue;
840965fd8d4SBram Moolenaar }
84124f21fdfSBram Moolenaar if (both ? prop.tp_id == id && prop.tp_type == type_id
8420d4d9ee9SBram Moolenaar : (id_found && prop.tp_id == id)
8430d4d9ee9SBram Moolenaar || prop.tp_type == type_id)
844e05a89acSBram Moolenaar {
845e05a89acSBram Moolenaar // Check if the starting position has text props.
84666b98854SBram Moolenaar if (lnum_start == lnum
84766b98854SBram Moolenaar && col >= prop.tp_col
84866b98854SBram Moolenaar && (col <= prop.tp_col + prop.tp_len
84966b98854SBram Moolenaar - (prop.tp_len != 0)))
850e05a89acSBram Moolenaar start_pos_has_prop = 1;
851e05a89acSBram Moolenaar
852e05a89acSBram Moolenaar prop_start = !(prop.tp_flags & TP_FLAG_CONT_PREV);
853e05a89acSBram Moolenaar prop_end = !(prop.tp_flags & TP_FLAG_CONT_NEXT);
854e05a89acSBram Moolenaar if (!prop_start && prop_end && dir > 0)
855e05a89acSBram Moolenaar seen_end = 1;
856e05a89acSBram Moolenaar
857e05a89acSBram Moolenaar // Skip lines without the start flag.
858e05a89acSBram Moolenaar if (!prop_start)
859e05a89acSBram Moolenaar {
860e05a89acSBram Moolenaar // Always search backwards for start when search started
861e05a89acSBram Moolenaar // on a prop and we're not skipping.
862e05a89acSBram Moolenaar if (start_pos_has_prop && !skipstart)
863e05a89acSBram Moolenaar dir = -1;
8644da7a259SBram Moolenaar continue;
865e05a89acSBram Moolenaar }
866e05a89acSBram Moolenaar
867e05a89acSBram Moolenaar // If skipstart is true, skip the prop at start pos (even if
868e05a89acSBram Moolenaar // continued from another line).
869e05a89acSBram Moolenaar if (start_pos_has_prop && skipstart && !seen_end)
870e05a89acSBram Moolenaar {
871e05a89acSBram Moolenaar start_pos_has_prop = 0;
8724da7a259SBram Moolenaar continue;
873e05a89acSBram Moolenaar }
874e05a89acSBram Moolenaar
875e05a89acSBram Moolenaar prop_fill_dict(rettv->vval.v_dict, &prop, buf);
876e05a89acSBram Moolenaar dict_add_number(rettv->vval.v_dict, "lnum", lnum);
877e05a89acSBram Moolenaar
878e05a89acSBram Moolenaar return;
879e05a89acSBram Moolenaar }
880e05a89acSBram Moolenaar }
881e05a89acSBram Moolenaar
882e05a89acSBram Moolenaar if (dir > 0)
883e05a89acSBram Moolenaar {
884e05a89acSBram Moolenaar if (lnum >= buf->b_ml.ml_line_count)
885e05a89acSBram Moolenaar break;
886e05a89acSBram Moolenaar lnum++;
887e05a89acSBram Moolenaar }
888e05a89acSBram Moolenaar else
889e05a89acSBram Moolenaar {
890e05a89acSBram Moolenaar if (lnum <= 1)
891e05a89acSBram Moolenaar break;
892e05a89acSBram Moolenaar lnum--;
893e05a89acSBram Moolenaar }
89466b98854SBram Moolenaar // Adjust col to indicate that we're continuing from prev/next line.
89566b98854SBram Moolenaar col = dir < 0 ? buf->b_ml.ml_line_len : 1;
896e05a89acSBram Moolenaar }
897e05a89acSBram Moolenaar }
898e05a89acSBram Moolenaar
899e05a89acSBram Moolenaar /*
90098aefe7cSBram Moolenaar * prop_list({lnum} [, {bufnr}])
90198aefe7cSBram Moolenaar */
90298aefe7cSBram Moolenaar void
f_prop_list(typval_T * argvars,typval_T * rettv)90398aefe7cSBram Moolenaar f_prop_list(typval_T *argvars, typval_T *rettv)
90498aefe7cSBram Moolenaar {
9051a71d31bSYegappan Lakshmanan linenr_T lnum;
90698aefe7cSBram Moolenaar buf_T *buf = curbuf;
90798aefe7cSBram Moolenaar
9081a71d31bSYegappan Lakshmanan if (in_vim9script()
9091a71d31bSYegappan Lakshmanan && (check_for_number_arg(argvars, 0) == FAIL
91083494b4aSYegappan Lakshmanan || check_for_opt_dict_arg(argvars, 1) == FAIL))
9111a71d31bSYegappan Lakshmanan return;
9121a71d31bSYegappan Lakshmanan
9131a71d31bSYegappan Lakshmanan lnum = tv_get_number(&argvars[0]);
91498aefe7cSBram Moolenaar if (argvars[1].v_type != VAR_UNKNOWN)
91598aefe7cSBram Moolenaar {
91698aefe7cSBram Moolenaar if (get_bufnr_from_arg(&argvars[1], &buf) == FAIL)
91798aefe7cSBram Moolenaar return;
91898aefe7cSBram Moolenaar }
91998aefe7cSBram Moolenaar if (lnum < 1 || lnum > buf->b_ml.ml_line_count)
92098aefe7cSBram Moolenaar {
921108010aaSBram Moolenaar emsg(_(e_invalid_range));
92298aefe7cSBram Moolenaar return;
92398aefe7cSBram Moolenaar }
92498aefe7cSBram Moolenaar
92598aefe7cSBram Moolenaar if (rettv_list_alloc(rettv) == OK)
92698aefe7cSBram Moolenaar {
92798aefe7cSBram Moolenaar char_u *text = ml_get_buf(buf, lnum, FALSE);
92898aefe7cSBram Moolenaar size_t textlen = STRLEN(text) + 1;
9294efe73b4SBram Moolenaar int count = (int)((buf->b_ml.ml_line_len - textlen)
9304efe73b4SBram Moolenaar / sizeof(textprop_T));
93198aefe7cSBram Moolenaar int i;
93298aefe7cSBram Moolenaar textprop_T prop;
93398aefe7cSBram Moolenaar
93498aefe7cSBram Moolenaar for (i = 0; i < count; ++i)
93598aefe7cSBram Moolenaar {
93698aefe7cSBram Moolenaar dict_T *d = dict_alloc();
93798aefe7cSBram Moolenaar
93898aefe7cSBram Moolenaar if (d == NULL)
93998aefe7cSBram Moolenaar break;
94098aefe7cSBram Moolenaar mch_memmove(&prop, text + textlen + i * sizeof(textprop_T),
94198aefe7cSBram Moolenaar sizeof(textprop_T));
942e05a89acSBram Moolenaar prop_fill_dict(d, &prop, buf);
94398aefe7cSBram Moolenaar list_append_dict(rettv->vval.v_list, d);
94498aefe7cSBram Moolenaar }
94598aefe7cSBram Moolenaar }
94698aefe7cSBram Moolenaar }
94798aefe7cSBram Moolenaar
94898aefe7cSBram Moolenaar /*
94998aefe7cSBram Moolenaar * prop_remove({props} [, {lnum} [, {lnum_end}]])
95098aefe7cSBram Moolenaar */
95198aefe7cSBram Moolenaar void
f_prop_remove(typval_T * argvars,typval_T * rettv)95298aefe7cSBram Moolenaar f_prop_remove(typval_T *argvars, typval_T *rettv)
95398aefe7cSBram Moolenaar {
95498aefe7cSBram Moolenaar linenr_T start = 1;
95598aefe7cSBram Moolenaar linenr_T end = 0;
95698aefe7cSBram Moolenaar linenr_T lnum;
957965c0448SBram Moolenaar linenr_T first_changed = 0;
958965c0448SBram Moolenaar linenr_T last_changed = 0;
95998aefe7cSBram Moolenaar dict_T *dict;
96098aefe7cSBram Moolenaar buf_T *buf = curbuf;
961a5a40c56SBram Moolenaar int do_all;
96298aefe7cSBram Moolenaar int id = -1;
96398aefe7cSBram Moolenaar int type_id = -1;
964a5a40c56SBram Moolenaar int both;
96598aefe7cSBram Moolenaar
96698aefe7cSBram Moolenaar rettv->vval.v_number = 0;
96783494b4aSYegappan Lakshmanan
96883494b4aSYegappan Lakshmanan if (in_vim9script()
96983494b4aSYegappan Lakshmanan && (check_for_dict_arg(argvars, 0) == FAIL
97083494b4aSYegappan Lakshmanan || check_for_opt_number_arg(argvars, 1) == FAIL
97183494b4aSYegappan Lakshmanan || (argvars[1].v_type != VAR_UNKNOWN
97283494b4aSYegappan Lakshmanan && check_for_opt_number_arg(argvars, 2) == FAIL)))
97383494b4aSYegappan Lakshmanan return;
97483494b4aSYegappan Lakshmanan
97598aefe7cSBram Moolenaar if (argvars[0].v_type != VAR_DICT || argvars[0].vval.v_dict == NULL)
97698aefe7cSBram Moolenaar {
977f9e3e09fSBram Moolenaar emsg(_(e_invarg));
97898aefe7cSBram Moolenaar return;
97998aefe7cSBram Moolenaar }
98098aefe7cSBram Moolenaar
98198aefe7cSBram Moolenaar if (argvars[1].v_type != VAR_UNKNOWN)
98298aefe7cSBram Moolenaar {
983d155d7a8SBram Moolenaar start = tv_get_number(&argvars[1]);
98498aefe7cSBram Moolenaar end = start;
98598aefe7cSBram Moolenaar if (argvars[2].v_type != VAR_UNKNOWN)
986d155d7a8SBram Moolenaar end = tv_get_number(&argvars[2]);
98798aefe7cSBram Moolenaar if (start < 1 || end < 1)
98898aefe7cSBram Moolenaar {
989108010aaSBram Moolenaar emsg(_(e_invalid_range));
99098aefe7cSBram Moolenaar return;
99198aefe7cSBram Moolenaar }
99298aefe7cSBram Moolenaar }
99398aefe7cSBram Moolenaar
99498aefe7cSBram Moolenaar dict = argvars[0].vval.v_dict;
995f0884c5fSBram Moolenaar if (get_bufnr_from_arg(&argvars[0], &buf) == FAIL)
99698aefe7cSBram Moolenaar return;
997f0884c5fSBram Moolenaar if (buf->b_ml.ml_mfp == NULL)
998f0884c5fSBram Moolenaar return;
99998aefe7cSBram Moolenaar
1000a5a40c56SBram Moolenaar do_all = dict_get_bool(dict, (char_u *)"all", FALSE);
100198aefe7cSBram Moolenaar
100298aefe7cSBram Moolenaar if (dict_find(dict, (char_u *)"id", -1) != NULL)
10038f66717aSBram Moolenaar id = dict_get_number(dict, (char_u *)"id");
100498aefe7cSBram Moolenaar if (dict_find(dict, (char_u *)"type", -1))
100598aefe7cSBram Moolenaar {
10068f66717aSBram Moolenaar char_u *name = dict_get_string(dict, (char_u *)"type", FALSE);
100798aefe7cSBram Moolenaar proptype_T *type = lookup_prop_type(name, buf);
100898aefe7cSBram Moolenaar
100998aefe7cSBram Moolenaar if (type == NULL)
101098aefe7cSBram Moolenaar return;
101198aefe7cSBram Moolenaar type_id = type->pt_id;
101298aefe7cSBram Moolenaar }
1013a5a40c56SBram Moolenaar both = dict_get_bool(dict, (char_u *)"both", FALSE);
1014a5a40c56SBram Moolenaar
101598aefe7cSBram Moolenaar if (id == -1 && type_id == -1)
101698aefe7cSBram Moolenaar {
1017f9e3e09fSBram Moolenaar emsg(_("E968: Need at least one of 'id' or 'type'"));
101898aefe7cSBram Moolenaar return;
101998aefe7cSBram Moolenaar }
102049b79bd4SBram Moolenaar if (both && (id == -1 || type_id == -1))
102149b79bd4SBram Moolenaar {
102249b79bd4SBram Moolenaar emsg(_("E860: Need 'id' and 'type' with 'both'"));
102349b79bd4SBram Moolenaar return;
102449b79bd4SBram Moolenaar }
102598aefe7cSBram Moolenaar
102698aefe7cSBram Moolenaar if (end == 0)
102798aefe7cSBram Moolenaar end = buf->b_ml.ml_line_count;
102898aefe7cSBram Moolenaar for (lnum = start; lnum <= end; ++lnum)
102998aefe7cSBram Moolenaar {
103098aefe7cSBram Moolenaar char_u *text;
103198aefe7cSBram Moolenaar size_t len;
103298aefe7cSBram Moolenaar
103398aefe7cSBram Moolenaar if (lnum > buf->b_ml.ml_line_count)
103498aefe7cSBram Moolenaar break;
103598aefe7cSBram Moolenaar text = ml_get_buf(buf, lnum, FALSE);
103698aefe7cSBram Moolenaar len = STRLEN(text) + 1;
103798aefe7cSBram Moolenaar if ((size_t)buf->b_ml.ml_line_len > len)
103898aefe7cSBram Moolenaar {
103998aefe7cSBram Moolenaar static textprop_T textprop; // static because of alignment
104098aefe7cSBram Moolenaar unsigned idx;
104198aefe7cSBram Moolenaar
104298aefe7cSBram Moolenaar for (idx = 0; idx < (buf->b_ml.ml_line_len - len)
104398aefe7cSBram Moolenaar / sizeof(textprop_T); ++idx)
104498aefe7cSBram Moolenaar {
104598aefe7cSBram Moolenaar char_u *cur_prop = buf->b_ml.ml_line_ptr + len
104698aefe7cSBram Moolenaar + idx * sizeof(textprop_T);
104798aefe7cSBram Moolenaar size_t taillen;
104898aefe7cSBram Moolenaar
104998aefe7cSBram Moolenaar mch_memmove(&textprop, cur_prop, sizeof(textprop_T));
105049b79bd4SBram Moolenaar if (both ? textprop.tp_id == id && textprop.tp_type == type_id
105149b79bd4SBram Moolenaar : textprop.tp_id == id || textprop.tp_type == type_id)
105298aefe7cSBram Moolenaar {
105398aefe7cSBram Moolenaar if (!(buf->b_ml.ml_flags & ML_LINE_DIRTY))
105498aefe7cSBram Moolenaar {
105598aefe7cSBram Moolenaar char_u *newptr = alloc(buf->b_ml.ml_line_len);
105698aefe7cSBram Moolenaar
105798aefe7cSBram Moolenaar // need to allocate the line to be able to change it
105898aefe7cSBram Moolenaar if (newptr == NULL)
105998aefe7cSBram Moolenaar return;
106098aefe7cSBram Moolenaar mch_memmove(newptr, buf->b_ml.ml_line_ptr,
106198aefe7cSBram Moolenaar buf->b_ml.ml_line_len);
106298aefe7cSBram Moolenaar buf->b_ml.ml_line_ptr = newptr;
10630a2f578eSBram Moolenaar buf->b_ml.ml_flags |= ML_LINE_DIRTY;
10640a2f578eSBram Moolenaar
10650a2f578eSBram Moolenaar cur_prop = buf->b_ml.ml_line_ptr + len
10660a2f578eSBram Moolenaar + idx * sizeof(textprop_T);
106798aefe7cSBram Moolenaar }
106898aefe7cSBram Moolenaar
106998aefe7cSBram Moolenaar taillen = buf->b_ml.ml_line_len - len
107098aefe7cSBram Moolenaar - (idx + 1) * sizeof(textprop_T);
107198aefe7cSBram Moolenaar if (taillen > 0)
107298aefe7cSBram Moolenaar mch_memmove(cur_prop, cur_prop + sizeof(textprop_T),
107398aefe7cSBram Moolenaar taillen);
107498aefe7cSBram Moolenaar buf->b_ml.ml_line_len -= sizeof(textprop_T);
107598aefe7cSBram Moolenaar --idx;
107698aefe7cSBram Moolenaar
1077965c0448SBram Moolenaar if (first_changed == 0)
1078965c0448SBram Moolenaar first_changed = lnum;
1079965c0448SBram Moolenaar last_changed = lnum;
108098aefe7cSBram Moolenaar ++rettv->vval.v_number;
108198aefe7cSBram Moolenaar if (!do_all)
108298aefe7cSBram Moolenaar break;
108398aefe7cSBram Moolenaar }
108498aefe7cSBram Moolenaar }
108598aefe7cSBram Moolenaar }
108698aefe7cSBram Moolenaar }
1087965c0448SBram Moolenaar if (first_changed > 0)
1088fc643e60SBram Moolenaar {
1089965c0448SBram Moolenaar changed_lines_buf(buf, first_changed, last_changed + 1, 0);
1090fc643e60SBram Moolenaar redraw_buf_later(buf, VALID);
1091fc643e60SBram Moolenaar }
1092fc643e60SBram Moolenaar }
109398aefe7cSBram Moolenaar
109498aefe7cSBram Moolenaar /*
109598aefe7cSBram Moolenaar * Common for f_prop_type_add() and f_prop_type_change().
109698aefe7cSBram Moolenaar */
10975843f5f3SBram Moolenaar static void
prop_type_set(typval_T * argvars,int add)109898aefe7cSBram Moolenaar prop_type_set(typval_T *argvars, int add)
109998aefe7cSBram Moolenaar {
110098aefe7cSBram Moolenaar char_u *name;
110198aefe7cSBram Moolenaar buf_T *buf = NULL;
110298aefe7cSBram Moolenaar dict_T *dict;
110398aefe7cSBram Moolenaar dictitem_T *di;
110498aefe7cSBram Moolenaar proptype_T *prop;
110598aefe7cSBram Moolenaar
11064490ec4eSYegappan Lakshmanan if (in_vim9script()
11074490ec4eSYegappan Lakshmanan && (check_for_string_arg(argvars, 0) == FAIL
11084490ec4eSYegappan Lakshmanan || check_for_dict_arg(argvars, 1) == FAIL))
11094490ec4eSYegappan Lakshmanan return;
11104490ec4eSYegappan Lakshmanan
1111d155d7a8SBram Moolenaar name = tv_get_string(&argvars[0]);
111298aefe7cSBram Moolenaar if (*name == NUL)
111398aefe7cSBram Moolenaar {
1114f9e3e09fSBram Moolenaar emsg(_(e_invarg));
111598aefe7cSBram Moolenaar return;
111698aefe7cSBram Moolenaar }
111798aefe7cSBram Moolenaar
111898aefe7cSBram Moolenaar if (get_bufnr_from_arg(&argvars[1], &buf) == FAIL)
111998aefe7cSBram Moolenaar return;
112098aefe7cSBram Moolenaar dict = argvars[1].vval.v_dict;
112198aefe7cSBram Moolenaar
112298aefe7cSBram Moolenaar prop = find_prop(name, buf);
112398aefe7cSBram Moolenaar if (add)
112498aefe7cSBram Moolenaar {
112598aefe7cSBram Moolenaar hashtab_T **htp;
112698aefe7cSBram Moolenaar
112798aefe7cSBram Moolenaar if (prop != NULL)
112898aefe7cSBram Moolenaar {
1129f9e3e09fSBram Moolenaar semsg(_("E969: Property type %s already defined"), name);
113098aefe7cSBram Moolenaar return;
113198aefe7cSBram Moolenaar }
113247ed553fSBram Moolenaar prop = alloc_clear(offsetof(proptype_T, pt_name) + STRLEN(name) + 1);
113398aefe7cSBram Moolenaar if (prop == NULL)
113498aefe7cSBram Moolenaar return;
113598aefe7cSBram Moolenaar STRCPY(prop->pt_name, name);
113698aefe7cSBram Moolenaar prop->pt_id = ++proptype_id;
11370743ef9fSBram Moolenaar prop->pt_flags = PT_FLAG_COMBINE;
113898aefe7cSBram Moolenaar htp = buf == NULL ? &global_proptypes : &buf->b_proptypes;
113998aefe7cSBram Moolenaar if (*htp == NULL)
114098aefe7cSBram Moolenaar {
1141c799fe20SBram Moolenaar *htp = ALLOC_ONE(hashtab_T);
114298aefe7cSBram Moolenaar if (*htp == NULL)
1143fb95e212SBram Moolenaar {
1144fb95e212SBram Moolenaar vim_free(prop);
114598aefe7cSBram Moolenaar return;
1146fb95e212SBram Moolenaar }
114798aefe7cSBram Moolenaar hash_init(*htp);
114898aefe7cSBram Moolenaar }
1149fb95e212SBram Moolenaar hash_add(*htp, PT2HIKEY(prop));
115098aefe7cSBram Moolenaar }
115198aefe7cSBram Moolenaar else
115298aefe7cSBram Moolenaar {
115398aefe7cSBram Moolenaar if (prop == NULL)
115498aefe7cSBram Moolenaar {
1155f9e3e09fSBram Moolenaar semsg(_(e_type_not_exist), name);
115698aefe7cSBram Moolenaar return;
115798aefe7cSBram Moolenaar }
115898aefe7cSBram Moolenaar }
115998aefe7cSBram Moolenaar
116098aefe7cSBram Moolenaar if (dict != NULL)
116198aefe7cSBram Moolenaar {
116298aefe7cSBram Moolenaar di = dict_find(dict, (char_u *)"highlight", -1);
116398aefe7cSBram Moolenaar if (di != NULL)
116498aefe7cSBram Moolenaar {
116598aefe7cSBram Moolenaar char_u *highlight;
116698aefe7cSBram Moolenaar int hl_id = 0;
116798aefe7cSBram Moolenaar
11688f66717aSBram Moolenaar highlight = dict_get_string(dict, (char_u *)"highlight", FALSE);
116998aefe7cSBram Moolenaar if (highlight != NULL && *highlight != NUL)
117098aefe7cSBram Moolenaar hl_id = syn_name2id(highlight);
117198aefe7cSBram Moolenaar if (hl_id <= 0)
117298aefe7cSBram Moolenaar {
1173f9e3e09fSBram Moolenaar semsg(_("E970: Unknown highlight group name: '%s'"),
117498aefe7cSBram Moolenaar highlight == NULL ? (char_u *)"" : highlight);
117598aefe7cSBram Moolenaar return;
117698aefe7cSBram Moolenaar }
117798aefe7cSBram Moolenaar prop->pt_hl_id = hl_id;
117898aefe7cSBram Moolenaar }
117998aefe7cSBram Moolenaar
118058187f1cSBram Moolenaar di = dict_find(dict, (char_u *)"combine", -1);
118158187f1cSBram Moolenaar if (di != NULL)
118258187f1cSBram Moolenaar {
1183fa2e38dfSBram Moolenaar if (tv_get_bool(&di->di_tv))
118458187f1cSBram Moolenaar prop->pt_flags |= PT_FLAG_COMBINE;
118558187f1cSBram Moolenaar else
118658187f1cSBram Moolenaar prop->pt_flags &= ~PT_FLAG_COMBINE;
118758187f1cSBram Moolenaar }
118858187f1cSBram Moolenaar
118998aefe7cSBram Moolenaar di = dict_find(dict, (char_u *)"priority", -1);
119098aefe7cSBram Moolenaar if (di != NULL)
1191d155d7a8SBram Moolenaar prop->pt_priority = tv_get_number(&di->di_tv);
119298aefe7cSBram Moolenaar
119398aefe7cSBram Moolenaar di = dict_find(dict, (char_u *)"start_incl", -1);
119498aefe7cSBram Moolenaar if (di != NULL)
119598aefe7cSBram Moolenaar {
1196fa2e38dfSBram Moolenaar if (tv_get_bool(&di->di_tv))
119798aefe7cSBram Moolenaar prop->pt_flags |= PT_FLAG_INS_START_INCL;
119898aefe7cSBram Moolenaar else
119998aefe7cSBram Moolenaar prop->pt_flags &= ~PT_FLAG_INS_START_INCL;
120098aefe7cSBram Moolenaar }
120198aefe7cSBram Moolenaar
120298aefe7cSBram Moolenaar di = dict_find(dict, (char_u *)"end_incl", -1);
120398aefe7cSBram Moolenaar if (di != NULL)
120498aefe7cSBram Moolenaar {
1205fa2e38dfSBram Moolenaar if (tv_get_bool(&di->di_tv))
120698aefe7cSBram Moolenaar prop->pt_flags |= PT_FLAG_INS_END_INCL;
120798aefe7cSBram Moolenaar else
120898aefe7cSBram Moolenaar prop->pt_flags &= ~PT_FLAG_INS_END_INCL;
120998aefe7cSBram Moolenaar }
121098aefe7cSBram Moolenaar }
121198aefe7cSBram Moolenaar }
121298aefe7cSBram Moolenaar
121398aefe7cSBram Moolenaar /*
121498aefe7cSBram Moolenaar * prop_type_add({name}, {props})
121598aefe7cSBram Moolenaar */
121698aefe7cSBram Moolenaar void
f_prop_type_add(typval_T * argvars,typval_T * rettv UNUSED)121798aefe7cSBram Moolenaar f_prop_type_add(typval_T *argvars, typval_T *rettv UNUSED)
121898aefe7cSBram Moolenaar {
121998aefe7cSBram Moolenaar prop_type_set(argvars, TRUE);
122098aefe7cSBram Moolenaar }
122198aefe7cSBram Moolenaar
122298aefe7cSBram Moolenaar /*
122398aefe7cSBram Moolenaar * prop_type_change({name}, {props})
122498aefe7cSBram Moolenaar */
122598aefe7cSBram Moolenaar void
f_prop_type_change(typval_T * argvars,typval_T * rettv UNUSED)122698aefe7cSBram Moolenaar f_prop_type_change(typval_T *argvars, typval_T *rettv UNUSED)
122798aefe7cSBram Moolenaar {
122898aefe7cSBram Moolenaar prop_type_set(argvars, FALSE);
122998aefe7cSBram Moolenaar }
123098aefe7cSBram Moolenaar
123198aefe7cSBram Moolenaar /*
123298aefe7cSBram Moolenaar * prop_type_delete({name} [, {bufnr}])
123398aefe7cSBram Moolenaar */
123498aefe7cSBram Moolenaar void
f_prop_type_delete(typval_T * argvars,typval_T * rettv UNUSED)123598aefe7cSBram Moolenaar f_prop_type_delete(typval_T *argvars, typval_T *rettv UNUSED)
123698aefe7cSBram Moolenaar {
123798aefe7cSBram Moolenaar char_u *name;
123898aefe7cSBram Moolenaar buf_T *buf = NULL;
123998aefe7cSBram Moolenaar hashitem_T *hi;
124098aefe7cSBram Moolenaar
12414490ec4eSYegappan Lakshmanan if (in_vim9script()
12424490ec4eSYegappan Lakshmanan && (check_for_string_arg(argvars, 0) == FAIL
12434490ec4eSYegappan Lakshmanan || check_for_opt_dict_arg(argvars, 1) == FAIL))
12444490ec4eSYegappan Lakshmanan return;
12454490ec4eSYegappan Lakshmanan
1246d155d7a8SBram Moolenaar name = tv_get_string(&argvars[0]);
124798aefe7cSBram Moolenaar if (*name == NUL)
124898aefe7cSBram Moolenaar {
1249f9e3e09fSBram Moolenaar emsg(_(e_invarg));
125098aefe7cSBram Moolenaar return;
125198aefe7cSBram Moolenaar }
125298aefe7cSBram Moolenaar
125398aefe7cSBram Moolenaar if (argvars[1].v_type != VAR_UNKNOWN)
125498aefe7cSBram Moolenaar {
125598aefe7cSBram Moolenaar if (get_bufnr_from_arg(&argvars[1], &buf) == FAIL)
125698aefe7cSBram Moolenaar return;
125798aefe7cSBram Moolenaar }
125898aefe7cSBram Moolenaar
125998aefe7cSBram Moolenaar hi = find_prop_hi(name, buf);
126098aefe7cSBram Moolenaar if (hi != NULL)
126198aefe7cSBram Moolenaar {
126298aefe7cSBram Moolenaar hashtab_T *ht;
1263fb95e212SBram Moolenaar proptype_T *prop = HI2PT(hi);
126498aefe7cSBram Moolenaar
126598aefe7cSBram Moolenaar if (buf == NULL)
126698aefe7cSBram Moolenaar ht = global_proptypes;
126798aefe7cSBram Moolenaar else
126898aefe7cSBram Moolenaar ht = buf->b_proptypes;
126998aefe7cSBram Moolenaar hash_remove(ht, hi);
1270fb95e212SBram Moolenaar vim_free(prop);
127198aefe7cSBram Moolenaar }
127298aefe7cSBram Moolenaar }
127398aefe7cSBram Moolenaar
127498aefe7cSBram Moolenaar /*
1275e2390c7fSMartin Tournoij * prop_type_get({name} [, {props}])
127698aefe7cSBram Moolenaar */
127798aefe7cSBram Moolenaar void
f_prop_type_get(typval_T * argvars,typval_T * rettv)12783d8a513bSBram Moolenaar f_prop_type_get(typval_T *argvars, typval_T *rettv)
127998aefe7cSBram Moolenaar {
12804490ec4eSYegappan Lakshmanan char_u *name;
128198aefe7cSBram Moolenaar
12824490ec4eSYegappan Lakshmanan if (in_vim9script()
12834490ec4eSYegappan Lakshmanan && (check_for_string_arg(argvars, 0) == FAIL
12844490ec4eSYegappan Lakshmanan || check_for_opt_dict_arg(argvars, 1) == FAIL))
12854490ec4eSYegappan Lakshmanan return;
12864490ec4eSYegappan Lakshmanan
12874490ec4eSYegappan Lakshmanan name = tv_get_string(&argvars[0]);
128898aefe7cSBram Moolenaar if (*name == NUL)
128998aefe7cSBram Moolenaar {
1290f9e3e09fSBram Moolenaar emsg(_(e_invarg));
129198aefe7cSBram Moolenaar return;
129298aefe7cSBram Moolenaar }
129398aefe7cSBram Moolenaar if (rettv_dict_alloc(rettv) == OK)
129498aefe7cSBram Moolenaar {
129598aefe7cSBram Moolenaar proptype_T *prop = NULL;
129698aefe7cSBram Moolenaar buf_T *buf = NULL;
129798aefe7cSBram Moolenaar
129898aefe7cSBram Moolenaar if (argvars[1].v_type != VAR_UNKNOWN)
129998aefe7cSBram Moolenaar {
130098aefe7cSBram Moolenaar if (get_bufnr_from_arg(&argvars[1], &buf) == FAIL)
130198aefe7cSBram Moolenaar return;
130298aefe7cSBram Moolenaar }
130398aefe7cSBram Moolenaar
130498aefe7cSBram Moolenaar prop = find_prop(name, buf);
130598aefe7cSBram Moolenaar if (prop != NULL)
130698aefe7cSBram Moolenaar {
130798aefe7cSBram Moolenaar dict_T *d = rettv->vval.v_dict;
130898aefe7cSBram Moolenaar
130998aefe7cSBram Moolenaar if (prop->pt_hl_id > 0)
131098aefe7cSBram Moolenaar dict_add_string(d, "highlight", syn_id2name(prop->pt_hl_id));
131198aefe7cSBram Moolenaar dict_add_number(d, "priority", prop->pt_priority);
131258187f1cSBram Moolenaar dict_add_number(d, "combine",
131358187f1cSBram Moolenaar (prop->pt_flags & PT_FLAG_COMBINE) ? 1 : 0);
131498aefe7cSBram Moolenaar dict_add_number(d, "start_incl",
131598aefe7cSBram Moolenaar (prop->pt_flags & PT_FLAG_INS_START_INCL) ? 1 : 0);
131698aefe7cSBram Moolenaar dict_add_number(d, "end_incl",
131798aefe7cSBram Moolenaar (prop->pt_flags & PT_FLAG_INS_END_INCL) ? 1 : 0);
131898aefe7cSBram Moolenaar if (buf != NULL)
131998aefe7cSBram Moolenaar dict_add_number(d, "bufnr", buf->b_fnum);
132098aefe7cSBram Moolenaar }
132198aefe7cSBram Moolenaar }
132298aefe7cSBram Moolenaar }
132398aefe7cSBram Moolenaar
132498aefe7cSBram Moolenaar static void
list_types(hashtab_T * ht,list_T * l)132598aefe7cSBram Moolenaar list_types(hashtab_T *ht, list_T *l)
132698aefe7cSBram Moolenaar {
132798aefe7cSBram Moolenaar long todo;
132898aefe7cSBram Moolenaar hashitem_T *hi;
132998aefe7cSBram Moolenaar
133098aefe7cSBram Moolenaar todo = (long)ht->ht_used;
133198aefe7cSBram Moolenaar for (hi = ht->ht_array; todo > 0; ++hi)
133298aefe7cSBram Moolenaar {
133398aefe7cSBram Moolenaar if (!HASHITEM_EMPTY(hi))
133498aefe7cSBram Moolenaar {
133598aefe7cSBram Moolenaar proptype_T *prop = HI2PT(hi);
133698aefe7cSBram Moolenaar
133798aefe7cSBram Moolenaar list_append_string(l, prop->pt_name, -1);
133898aefe7cSBram Moolenaar --todo;
133998aefe7cSBram Moolenaar }
134098aefe7cSBram Moolenaar }
134198aefe7cSBram Moolenaar }
134298aefe7cSBram Moolenaar
134398aefe7cSBram Moolenaar /*
134498aefe7cSBram Moolenaar * prop_type_list([{bufnr}])
134598aefe7cSBram Moolenaar */
134698aefe7cSBram Moolenaar void
f_prop_type_list(typval_T * argvars,typval_T * rettv UNUSED)134798aefe7cSBram Moolenaar f_prop_type_list(typval_T *argvars, typval_T *rettv UNUSED)
134898aefe7cSBram Moolenaar {
134998aefe7cSBram Moolenaar buf_T *buf = NULL;
135098aefe7cSBram Moolenaar
135198aefe7cSBram Moolenaar if (rettv_list_alloc(rettv) == OK)
135298aefe7cSBram Moolenaar {
13534490ec4eSYegappan Lakshmanan if (in_vim9script() && check_for_opt_dict_arg(argvars, 0) == FAIL)
13544490ec4eSYegappan Lakshmanan return;
13554490ec4eSYegappan Lakshmanan
135698aefe7cSBram Moolenaar if (argvars[0].v_type != VAR_UNKNOWN)
135798aefe7cSBram Moolenaar {
135898aefe7cSBram Moolenaar if (get_bufnr_from_arg(&argvars[0], &buf) == FAIL)
135998aefe7cSBram Moolenaar return;
136098aefe7cSBram Moolenaar }
136198aefe7cSBram Moolenaar if (buf == NULL)
136298aefe7cSBram Moolenaar {
136398aefe7cSBram Moolenaar if (global_proptypes != NULL)
136498aefe7cSBram Moolenaar list_types(global_proptypes, rettv->vval.v_list);
136598aefe7cSBram Moolenaar }
136698aefe7cSBram Moolenaar else if (buf->b_proptypes != NULL)
136798aefe7cSBram Moolenaar list_types(buf->b_proptypes, rettv->vval.v_list);
136898aefe7cSBram Moolenaar }
136998aefe7cSBram Moolenaar }
137098aefe7cSBram Moolenaar
137198aefe7cSBram Moolenaar /*
137298aefe7cSBram Moolenaar * Free all property types in "ht".
137398aefe7cSBram Moolenaar */
137498aefe7cSBram Moolenaar static void
clear_ht_prop_types(hashtab_T * ht)137598aefe7cSBram Moolenaar clear_ht_prop_types(hashtab_T *ht)
137698aefe7cSBram Moolenaar {
137798aefe7cSBram Moolenaar long todo;
137898aefe7cSBram Moolenaar hashitem_T *hi;
137998aefe7cSBram Moolenaar
138098aefe7cSBram Moolenaar if (ht == NULL)
138198aefe7cSBram Moolenaar return;
138298aefe7cSBram Moolenaar
138398aefe7cSBram Moolenaar todo = (long)ht->ht_used;
138498aefe7cSBram Moolenaar for (hi = ht->ht_array; todo > 0; ++hi)
138598aefe7cSBram Moolenaar {
138698aefe7cSBram Moolenaar if (!HASHITEM_EMPTY(hi))
138798aefe7cSBram Moolenaar {
138898aefe7cSBram Moolenaar proptype_T *prop = HI2PT(hi);
138998aefe7cSBram Moolenaar
139098aefe7cSBram Moolenaar vim_free(prop);
139198aefe7cSBram Moolenaar --todo;
139298aefe7cSBram Moolenaar }
139398aefe7cSBram Moolenaar }
139498aefe7cSBram Moolenaar
139598aefe7cSBram Moolenaar hash_clear(ht);
139698aefe7cSBram Moolenaar vim_free(ht);
139798aefe7cSBram Moolenaar }
139898aefe7cSBram Moolenaar
139998aefe7cSBram Moolenaar #if defined(EXITFREE) || defined(PROTO)
140098aefe7cSBram Moolenaar /*
1401fb95e212SBram Moolenaar * Free all global property types.
140298aefe7cSBram Moolenaar */
140398aefe7cSBram Moolenaar void
clear_global_prop_types(void)140498aefe7cSBram Moolenaar clear_global_prop_types(void)
140598aefe7cSBram Moolenaar {
140698aefe7cSBram Moolenaar clear_ht_prop_types(global_proptypes);
140798aefe7cSBram Moolenaar global_proptypes = NULL;
140898aefe7cSBram Moolenaar }
140998aefe7cSBram Moolenaar #endif
141098aefe7cSBram Moolenaar
141198aefe7cSBram Moolenaar /*
141298aefe7cSBram Moolenaar * Free all property types for "buf".
141398aefe7cSBram Moolenaar */
141498aefe7cSBram Moolenaar void
clear_buf_prop_types(buf_T * buf)141598aefe7cSBram Moolenaar clear_buf_prop_types(buf_T *buf)
141698aefe7cSBram Moolenaar {
141798aefe7cSBram Moolenaar clear_ht_prop_types(buf->b_proptypes);
141898aefe7cSBram Moolenaar buf->b_proptypes = NULL;
141998aefe7cSBram Moolenaar }
142098aefe7cSBram Moolenaar
142187be9be1SBram Moolenaar // Struct used to return two values from adjust_prop().
142287be9be1SBram Moolenaar typedef struct
142387be9be1SBram Moolenaar {
142487be9be1SBram Moolenaar int dirty; // if the property was changed
142587be9be1SBram Moolenaar int can_drop; // whether after this change, the prop may be removed
142687be9be1SBram Moolenaar } adjustres_T;
142787be9be1SBram Moolenaar
142887be9be1SBram Moolenaar /*
142987be9be1SBram Moolenaar * Adjust the property for "added" bytes (can be negative) inserted at "col".
143087be9be1SBram Moolenaar *
143187be9be1SBram Moolenaar * Note that "col" is zero-based, while tp_col is one-based.
143287be9be1SBram Moolenaar * Only for the current buffer.
143387be9be1SBram Moolenaar * "flags" can have:
143487be9be1SBram Moolenaar * APC_SUBSTITUTE: Text is replaced, not inserted.
143587be9be1SBram Moolenaar */
143687be9be1SBram Moolenaar static adjustres_T
adjust_prop(textprop_T * prop,colnr_T col,int added,int flags)143787be9be1SBram Moolenaar adjust_prop(
143887be9be1SBram Moolenaar textprop_T *prop,
143987be9be1SBram Moolenaar colnr_T col,
144087be9be1SBram Moolenaar int added,
144187be9be1SBram Moolenaar int flags)
144287be9be1SBram Moolenaar {
144387be9be1SBram Moolenaar proptype_T *pt = text_prop_type_by_id(curbuf, prop->tp_type);
144487be9be1SBram Moolenaar int start_incl = (pt != NULL
144587be9be1SBram Moolenaar && (pt->pt_flags & PT_FLAG_INS_START_INCL))
144687be9be1SBram Moolenaar || (flags & APC_SUBSTITUTE);
144787be9be1SBram Moolenaar int end_incl = (pt != NULL
144887be9be1SBram Moolenaar && (pt->pt_flags & PT_FLAG_INS_END_INCL));
144987be9be1SBram Moolenaar // Do not drop zero-width props if they later can increase in
145087be9be1SBram Moolenaar // size.
145187be9be1SBram Moolenaar int droppable = !(start_incl || end_incl);
145287be9be1SBram Moolenaar adjustres_T res = {TRUE, FALSE};
145387be9be1SBram Moolenaar
145487be9be1SBram Moolenaar if (added > 0)
145587be9be1SBram Moolenaar {
145687be9be1SBram Moolenaar if (col + 1 <= prop->tp_col
145787be9be1SBram Moolenaar - (start_incl || (prop->tp_len == 0 && end_incl)))
145887be9be1SBram Moolenaar // Change is entirely before the text property: Only shift
145987be9be1SBram Moolenaar prop->tp_col += added;
146087be9be1SBram Moolenaar else if (col + 1 < prop->tp_col + prop->tp_len + end_incl)
146187be9be1SBram Moolenaar // Insertion was inside text property
146287be9be1SBram Moolenaar prop->tp_len += added;
146387be9be1SBram Moolenaar }
146487be9be1SBram Moolenaar else if (prop->tp_col > col + 1)
146587be9be1SBram Moolenaar {
146687be9be1SBram Moolenaar if (prop->tp_col + added < col + 1)
146787be9be1SBram Moolenaar {
146887be9be1SBram Moolenaar prop->tp_len += (prop->tp_col - 1 - col) + added;
146987be9be1SBram Moolenaar prop->tp_col = col + 1;
147087be9be1SBram Moolenaar if (prop->tp_len <= 0)
147187be9be1SBram Moolenaar {
147287be9be1SBram Moolenaar prop->tp_len = 0;
147387be9be1SBram Moolenaar res.can_drop = droppable;
147487be9be1SBram Moolenaar }
147587be9be1SBram Moolenaar }
147687be9be1SBram Moolenaar else
147787be9be1SBram Moolenaar prop->tp_col += added;
147887be9be1SBram Moolenaar }
147987be9be1SBram Moolenaar else if (prop->tp_len > 0 && prop->tp_col + prop->tp_len > col)
148087be9be1SBram Moolenaar {
148187be9be1SBram Moolenaar int after = col - added - (prop->tp_col - 1 + prop->tp_len);
148287be9be1SBram Moolenaar
148387be9be1SBram Moolenaar prop->tp_len += after > 0 ? added + after : added;
148487be9be1SBram Moolenaar res.can_drop = prop->tp_len <= 0 && droppable;
148587be9be1SBram Moolenaar }
148687be9be1SBram Moolenaar else
148787be9be1SBram Moolenaar res.dirty = FALSE;
148887be9be1SBram Moolenaar
148987be9be1SBram Moolenaar return res;
149087be9be1SBram Moolenaar }
149187be9be1SBram Moolenaar
1492b9c67a51SBram Moolenaar /*
1493b9c67a51SBram Moolenaar * Adjust the columns of text properties in line "lnum" after position "col" to
1494b9c67a51SBram Moolenaar * shift by "bytes_added" (can be negative).
149544746aa1SBram Moolenaar * Note that "col" is zero-based, while tp_col is one-based.
149644746aa1SBram Moolenaar * Only for the current buffer.
1497f3333b02SBram Moolenaar * "flags" can have:
1498f3333b02SBram Moolenaar * APC_SAVE_FOR_UNDO: Call u_savesub() before making changes to the line.
1499f3333b02SBram Moolenaar * APC_SUBSTITUTE: Text is replaced, not inserted.
15008055d173SBram Moolenaar * Caller is expected to check b_has_textprop and "bytes_added" being non-zero.
1501338dfdadSBram Moolenaar * Returns TRUE when props were changed.
1502b9c67a51SBram Moolenaar */
1503338dfdadSBram Moolenaar int
adjust_prop_columns(linenr_T lnum,colnr_T col,int bytes_added,int flags)1504196d157fSBram Moolenaar adjust_prop_columns(
1505196d157fSBram Moolenaar linenr_T lnum,
1506196d157fSBram Moolenaar colnr_T col,
1507338dfdadSBram Moolenaar int bytes_added,
1508f3333b02SBram Moolenaar int flags)
1509b9c67a51SBram Moolenaar {
151044746aa1SBram Moolenaar int proplen;
151144746aa1SBram Moolenaar char_u *props;
151244746aa1SBram Moolenaar int dirty = FALSE;
1513196d157fSBram Moolenaar int ri, wi;
1514196d157fSBram Moolenaar size_t textlen;
1515196d157fSBram Moolenaar
1516196d157fSBram Moolenaar if (text_prop_frozen > 0)
1517338dfdadSBram Moolenaar return FALSE;
151844746aa1SBram Moolenaar
151944746aa1SBram Moolenaar proplen = get_text_props(curbuf, lnum, &props, TRUE);
152044746aa1SBram Moolenaar if (proplen == 0)
1521338dfdadSBram Moolenaar return FALSE;
1522196d157fSBram Moolenaar textlen = curbuf->b_ml.ml_line_len - proplen * sizeof(textprop_T);
152344746aa1SBram Moolenaar
1524196d157fSBram Moolenaar wi = 0; // write index
1525196d157fSBram Moolenaar for (ri = 0; ri < proplen; ++ri)
152644746aa1SBram Moolenaar {
152712f20038SBram Moolenaar textprop_T prop;
152887be9be1SBram Moolenaar adjustres_T res;
1529f3333b02SBram Moolenaar
153087be9be1SBram Moolenaar mch_memmove(&prop, props + ri * sizeof(prop), sizeof(prop));
153187be9be1SBram Moolenaar res = adjust_prop(&prop, col, bytes_added, flags);
153287be9be1SBram Moolenaar if (res.dirty)
1533f3333b02SBram Moolenaar {
1534f3333b02SBram Moolenaar // Save for undo if requested and not done yet.
1535cf070112SBram Moolenaar if ((flags & APC_SAVE_FOR_UNDO) && !dirty
1536cf070112SBram Moolenaar && u_savesub(lnum) == FAIL)
1537cf070112SBram Moolenaar return FALSE;
1538f3333b02SBram Moolenaar dirty = TRUE;
15398902b31fSBram Moolenaar
15408902b31fSBram Moolenaar // u_savesub() may have updated curbuf->b_ml, fetch it again
15418902b31fSBram Moolenaar if (curbuf->b_ml.ml_line_lnum != lnum)
15428902b31fSBram Moolenaar proplen = get_text_props(curbuf, lnum, &props, TRUE);
1543f3333b02SBram Moolenaar }
154487be9be1SBram Moolenaar if (res.can_drop)
154587be9be1SBram Moolenaar continue; // Drop this text property
154612f20038SBram Moolenaar mch_memmove(props + wi * sizeof(textprop_T), &prop, sizeof(textprop_T));
1547196d157fSBram Moolenaar ++wi;
154844746aa1SBram Moolenaar }
154944746aa1SBram Moolenaar if (dirty)
155044746aa1SBram Moolenaar {
15514614f53eSBram Moolenaar colnr_T newlen = (int)textlen + wi * (colnr_T)sizeof(textprop_T);
15524614f53eSBram Moolenaar
15534614f53eSBram Moolenaar if ((curbuf->b_ml.ml_flags & ML_LINE_DIRTY) == 0)
15544614f53eSBram Moolenaar curbuf->b_ml.ml_line_ptr =
15554614f53eSBram Moolenaar vim_memsave(curbuf->b_ml.ml_line_ptr, newlen);
155644746aa1SBram Moolenaar curbuf->b_ml.ml_flags |= ML_LINE_DIRTY;
15574614f53eSBram Moolenaar curbuf->b_ml.ml_line_len = newlen;
155844746aa1SBram Moolenaar }
1559338dfdadSBram Moolenaar return dirty;
1560b9c67a51SBram Moolenaar }
1561b9c67a51SBram Moolenaar
15624164bb20SBram Moolenaar /*
15634164bb20SBram Moolenaar * Adjust text properties for a line that was split in two.
156445dd07f1SBram Moolenaar * "lnum_props" is the line that has the properties from before the split.
156545dd07f1SBram Moolenaar * "lnum_top" is the top line.
156645dd07f1SBram Moolenaar * "kept" is the number of bytes kept in the first line, while
15674164bb20SBram Moolenaar * "deleted" is the number of bytes deleted.
15684164bb20SBram Moolenaar */
15694164bb20SBram Moolenaar void
adjust_props_for_split(linenr_T lnum_props,linenr_T lnum_top,int kept,int deleted)157045dd07f1SBram Moolenaar adjust_props_for_split(
157145dd07f1SBram Moolenaar linenr_T lnum_props,
157245dd07f1SBram Moolenaar linenr_T lnum_top,
157345dd07f1SBram Moolenaar int kept,
157445dd07f1SBram Moolenaar int deleted)
15754164bb20SBram Moolenaar {
15764164bb20SBram Moolenaar char_u *props;
15774164bb20SBram Moolenaar int count;
15784164bb20SBram Moolenaar garray_T prevprop;
15794164bb20SBram Moolenaar garray_T nextprop;
15804164bb20SBram Moolenaar int i;
15814164bb20SBram Moolenaar int skipped = kept + deleted;
15824164bb20SBram Moolenaar
15834164bb20SBram Moolenaar if (!curbuf->b_has_textprop)
15844164bb20SBram Moolenaar return;
158545dd07f1SBram Moolenaar
158645dd07f1SBram Moolenaar // Get the text properties from "lnum_props".
158745dd07f1SBram Moolenaar count = get_text_props(curbuf, lnum_props, &props, FALSE);
15884164bb20SBram Moolenaar ga_init2(&prevprop, sizeof(textprop_T), 10);
15894164bb20SBram Moolenaar ga_init2(&nextprop, sizeof(textprop_T), 10);
15904164bb20SBram Moolenaar
15914164bb20SBram Moolenaar // Keep the relevant ones in the first line, reducing the length if needed.
15924164bb20SBram Moolenaar // Copy the ones that include the split to the second line.
15934164bb20SBram Moolenaar // Move the ones after the split to the second line.
15944164bb20SBram Moolenaar for (i = 0; i < count; ++i)
15954164bb20SBram Moolenaar {
15964164bb20SBram Moolenaar textprop_T prop;
159787be9be1SBram Moolenaar proptype_T *pt;
159887be9be1SBram Moolenaar int start_incl, end_incl;
159987be9be1SBram Moolenaar int cont_prev, cont_next;
16004164bb20SBram Moolenaar
16014164bb20SBram Moolenaar // copy the prop to an aligned structure
16024164bb20SBram Moolenaar mch_memmove(&prop, props + i * sizeof(textprop_T), sizeof(textprop_T));
16034164bb20SBram Moolenaar
160487be9be1SBram Moolenaar pt = text_prop_type_by_id(curbuf, prop.tp_type);
160587be9be1SBram Moolenaar start_incl = (pt != NULL && (pt->pt_flags & PT_FLAG_INS_START_INCL));
160687be9be1SBram Moolenaar end_incl = (pt != NULL && (pt->pt_flags & PT_FLAG_INS_END_INCL));
160787be9be1SBram Moolenaar cont_prev = prop.tp_col + !start_incl <= kept;
160887be9be1SBram Moolenaar cont_next = skipped <= prop.tp_col + prop.tp_len - !end_incl;
160987be9be1SBram Moolenaar
161087be9be1SBram Moolenaar if (cont_prev && ga_grow(&prevprop, 1) == OK)
16114164bb20SBram Moolenaar {
161287be9be1SBram Moolenaar textprop_T *p = ((textprop_T *)prevprop.ga_data) + prevprop.ga_len;
161387be9be1SBram Moolenaar
16144164bb20SBram Moolenaar *p = prop;
161587be9be1SBram Moolenaar ++prevprop.ga_len;
16164164bb20SBram Moolenaar if (p->tp_col + p->tp_len >= kept)
16174164bb20SBram Moolenaar p->tp_len = kept - p->tp_col;
161887be9be1SBram Moolenaar if (cont_next)
161987be9be1SBram Moolenaar p->tp_flags |= TP_FLAG_CONT_NEXT;
16204164bb20SBram Moolenaar }
16214164bb20SBram Moolenaar
16225c65e6a0SBram Moolenaar // Only add the property to the next line if the length is bigger than
16235c65e6a0SBram Moolenaar // zero.
162487be9be1SBram Moolenaar if (cont_next && ga_grow(&nextprop, 1) == OK)
16254164bb20SBram Moolenaar {
162687be9be1SBram Moolenaar textprop_T *p = ((textprop_T *)nextprop.ga_data) + nextprop.ga_len;
16274164bb20SBram Moolenaar *p = prop;
162887be9be1SBram Moolenaar ++nextprop.ga_len;
16294164bb20SBram Moolenaar if (p->tp_col > skipped)
16304164bb20SBram Moolenaar p->tp_col -= skipped - 1;
16314164bb20SBram Moolenaar else
16324164bb20SBram Moolenaar {
16334164bb20SBram Moolenaar p->tp_len -= skipped - p->tp_col;
16344164bb20SBram Moolenaar p->tp_col = 1;
16354164bb20SBram Moolenaar }
163687be9be1SBram Moolenaar if (cont_prev)
163787be9be1SBram Moolenaar p->tp_flags |= TP_FLAG_CONT_PREV;
16384164bb20SBram Moolenaar }
16394164bb20SBram Moolenaar }
16404164bb20SBram Moolenaar
164145dd07f1SBram Moolenaar set_text_props(lnum_top, prevprop.ga_data,
164245dd07f1SBram Moolenaar prevprop.ga_len * sizeof(textprop_T));
16434164bb20SBram Moolenaar ga_clear(&prevprop);
164445dd07f1SBram Moolenaar set_text_props(lnum_top + 1, nextprop.ga_data,
164545dd07f1SBram Moolenaar nextprop.ga_len * sizeof(textprop_T));
16464164bb20SBram Moolenaar ga_clear(&nextprop);
16474164bb20SBram Moolenaar }
16484164bb20SBram Moolenaar
164980e737ccSBram Moolenaar /*
165087be9be1SBram Moolenaar * Prepend properties of joined line "lnum" to "new_props".
165180e737ccSBram Moolenaar */
165280e737ccSBram Moolenaar void
prepend_joined_props(char_u * new_props,int propcount,int * props_remaining,linenr_T lnum,int add_all,long col,int removed)165387be9be1SBram Moolenaar prepend_joined_props(
165487be9be1SBram Moolenaar char_u *new_props,
165587be9be1SBram Moolenaar int propcount,
165687be9be1SBram Moolenaar int *props_remaining,
165780e737ccSBram Moolenaar linenr_T lnum,
165887be9be1SBram Moolenaar int add_all,
165980e737ccSBram Moolenaar long col,
166080e737ccSBram Moolenaar int removed)
166180e737ccSBram Moolenaar {
166280e737ccSBram Moolenaar char_u *props;
166387be9be1SBram Moolenaar int proplen = get_text_props(curbuf, lnum, &props, FALSE);
166487be9be1SBram Moolenaar int i;
166580e737ccSBram Moolenaar
166687be9be1SBram Moolenaar for (i = proplen; i-- > 0; )
166780e737ccSBram Moolenaar {
166887be9be1SBram Moolenaar textprop_T prop;
166987be9be1SBram Moolenaar int end;
167080e737ccSBram Moolenaar
167187be9be1SBram Moolenaar mch_memmove(&prop, props + i * sizeof(prop), sizeof(prop));
167287be9be1SBram Moolenaar end = !(prop.tp_flags & TP_FLAG_CONT_NEXT);
167387be9be1SBram Moolenaar
167487be9be1SBram Moolenaar adjust_prop(&prop, 0, -removed, 0); // Remove leading spaces
16758e7d6223SBram Moolenaar adjust_prop(&prop, -1, col, 0); // Make line start at its final column
167687be9be1SBram Moolenaar
167787be9be1SBram Moolenaar if (add_all || end)
167887be9be1SBram Moolenaar mch_memmove(new_props + --(*props_remaining) * sizeof(prop),
167987be9be1SBram Moolenaar &prop, sizeof(prop));
168080e737ccSBram Moolenaar else
168180e737ccSBram Moolenaar {
168287be9be1SBram Moolenaar int j;
168387be9be1SBram Moolenaar int found = FALSE;
168480e737ccSBram Moolenaar
168587be9be1SBram Moolenaar // Search for continuing prop.
168687be9be1SBram Moolenaar for (j = *props_remaining; j < propcount; ++j)
168780e737ccSBram Moolenaar {
168887be9be1SBram Moolenaar textprop_T op;
168980e737ccSBram Moolenaar
169087be9be1SBram Moolenaar mch_memmove(&op, new_props + j * sizeof(op), sizeof(op));
169187be9be1SBram Moolenaar if ((op.tp_flags & TP_FLAG_CONT_PREV)
169287be9be1SBram Moolenaar && op.tp_id == prop.tp_id && op.tp_type == prop.tp_type)
169380e737ccSBram Moolenaar {
169487be9be1SBram Moolenaar found = TRUE;
169587be9be1SBram Moolenaar op.tp_len += op.tp_col - prop.tp_col;
169687be9be1SBram Moolenaar op.tp_col = prop.tp_col;
169787be9be1SBram Moolenaar // Start/end is taken care of when deleting joined lines
169887be9be1SBram Moolenaar op.tp_flags = prop.tp_flags;
169987be9be1SBram Moolenaar mch_memmove(new_props + j * sizeof(op), &op, sizeof(op));
170087be9be1SBram Moolenaar break;
170180e737ccSBram Moolenaar }
1702277e79adSBram Moolenaar }
170387be9be1SBram Moolenaar if (!found)
170487be9be1SBram Moolenaar internal_error("text property above joined line not found");
170580e737ccSBram Moolenaar }
170687be9be1SBram Moolenaar }
170780e737ccSBram Moolenaar }
170880e737ccSBram Moolenaar
170905ad5ff0SBram Moolenaar #endif // FEAT_PROP_POPUP
1710