diff src/textprop.c @ 25640:78ef12e0ce8c v8.2.3356

patch 8.2.3356: adding many text properties requires a lot of function calls Commit: https://github.com/vim/vim/commit/ccfb7c6758510e0fe5f390149ea14aee6ff4f55e Author: Yegappan Lakshmanan <yegappan@yahoo.com> Date: Mon Aug 16 21:39:09 2021 +0200 patch 8.2.3356: adding many text properties requires a lot of function calls Problem: Adding many text properties requires a lot of function calls. Solution: Add the prop_add_list() function. (Yegappan Lakshmanan, closes #8751)
author Bram Moolenaar <Bram@vim.org>
date Mon, 16 Aug 2021 21:45:03 +0200
parents 3678a11e71fa
children 685206b54ecf
line wrap: on
line diff
--- a/src/textprop.c
+++ b/src/textprop.c
@@ -183,6 +183,200 @@ f_prop_add(typval_T *argvars, typval_T *
 }
 
 /*
+ * Attach a text property 'type_name' to the text starting
+ * at [start_lnum, start_col] and ending at [end_lnum, end_col] in
+ * the buffer 'buf' and assign identifier 'id'.
+ */
+    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)
+{
+    proptype_T	*type;
+    linenr_T	lnum;
+    int		proplen;
+    char_u	*props = NULL;
+    char_u	*newprops;
+    size_t	textlen;
+    char_u	*newtext;
+    int		i;
+    textprop_T	tmp_prop;
+
+    type = lookup_prop_type(type_name, buf);
+    if (type == NULL)
+	return FAIL;
+
+    if (start_lnum < 1 || start_lnum > buf->b_ml.ml_line_count)
+    {
+	semsg(_(e_invalid_lnum), (long)start_lnum);
+	return FAIL;
+    }
+    if (end_lnum < start_lnum || end_lnum > buf->b_ml.ml_line_count)
+    {
+	semsg(_(e_invalid_lnum), (long)end_lnum);
+	return FAIL;
+    }
+
+    if (buf->b_ml.ml_mfp == NULL)
+    {
+	emsg(_("E275: Cannot add text property to unloaded buffer"));
+	return FAIL;
+    }
+
+    for (lnum = start_lnum; lnum <= end_lnum; ++lnum)
+    {
+	colnr_T col;	// start column
+	long	length;	// in bytes
+
+	// Fetch the line to get the ml_line_len field updated.
+	proplen = get_text_props(buf, lnum, &props, TRUE);
+	textlen = buf->b_ml.ml_line_len - proplen * sizeof(textprop_T);
+
+	if (lnum == start_lnum)
+	    col = start_col;
+	else
+	    col = 1;
+	if (col - 1 > (colnr_T)textlen)
+	{
+	    semsg(_(e_invalid_col), (long)start_col);
+	    return FAIL;
+	}
+
+	if (lnum == end_lnum)
+	    length = end_col - col;
+	else
+	    length = (int)textlen - col + 1;
+	if (length > (long)textlen)
+	    length = (int)textlen;	// can include the end-of-line
+	if (length < 0)
+	    length = 0;		// zero-width property
+
+	// Allocate the new line with space for the new property.
+	newtext = alloc(buf->b_ml.ml_line_len + sizeof(textprop_T));
+	if (newtext == NULL)
+	    return FAIL;
+	// Copy the text, including terminating NUL.
+	mch_memmove(newtext, buf->b_ml.ml_line_ptr, textlen);
+
+	// Find the index where to insert the new property.
+	// Since the text properties are not aligned properly when stored with
+	// the text, we need to copy them as bytes before using it as a struct.
+	for (i = 0; i < proplen; ++i)
+	{
+	    mch_memmove(&tmp_prop, props + i * sizeof(textprop_T),
+							   sizeof(textprop_T));
+	    if (tmp_prop.tp_col >= col)
+		break;
+	}
+	newprops = newtext + textlen;
+	if (i > 0)
+	    mch_memmove(newprops, props, sizeof(textprop_T) * i);
+
+	tmp_prop.tp_col = col;
+	tmp_prop.tp_len = length;
+	tmp_prop.tp_id = id;
+	tmp_prop.tp_type = type->pt_id;
+	tmp_prop.tp_flags = (lnum > start_lnum ? TP_FLAG_CONT_PREV : 0)
+			  | (lnum < end_lnum ? TP_FLAG_CONT_NEXT : 0);
+	mch_memmove(newprops + i * sizeof(textprop_T), &tmp_prop,
+							   sizeof(textprop_T));
+
+	if (i < proplen)
+	    mch_memmove(newprops + (i + 1) * sizeof(textprop_T),
+					    props + i * sizeof(textprop_T),
+					    sizeof(textprop_T) * (proplen - i));
+
+	if (buf->b_ml.ml_flags & ML_LINE_DIRTY)
+	    vim_free(buf->b_ml.ml_line_ptr);
+	buf->b_ml.ml_line_ptr = newtext;
+	buf->b_ml.ml_line_len += sizeof(textprop_T);
+	buf->b_ml.ml_flags |= ML_LINE_DIRTY;
+    }
+
+    changed_lines_buf(buf, start_lnum, end_lnum + 1, 0);
+    return OK;
+}
+
+/*
+ * prop_add_list()
+ * First argument specifies the text property:
+ *   {'type': <str>, 'id': <num>, 'bufnr': <num>}
+ * Second argument is a List where each item is a List with the following
+ * entries: [lnum, start_col, end_col]
+ */
+    void
+f_prop_add_list(typval_T *argvars, typval_T *rettv UNUSED)
+{
+    dict_T	*dict;
+    char_u	*type_name;
+    buf_T	*buf = curbuf;
+    int		id = 0;
+    listitem_T	*li;
+    list_T	*pos_list;
+    linenr_T	start_lnum;
+    colnr_T	start_col;
+    linenr_T	end_lnum;
+    colnr_T	end_col;
+    int		error = FALSE;
+
+    if (check_for_dict_arg(argvars, 0) == FAIL
+	    || check_for_list_arg(argvars, 1) == FAIL)
+	return;
+
+    if (argvars[1].vval.v_list == NULL)
+    {
+	emsg(_(e_listreq));
+	return;
+    }
+
+    dict = argvars[0].vval.v_dict;
+    if (dict == NULL || dict_find(dict, (char_u *)"type", -1) == NULL)
+    {
+	emsg(_("E965: missing property type name"));
+	return;
+    }
+    type_name = dict_get_string(dict, (char_u *)"type", FALSE);
+
+    if (dict_find(dict, (char_u *)"id", -1) != NULL)
+	id = dict_get_number(dict, (char_u *)"id");
+
+    if (get_bufnr_from_arg(&argvars[0], &buf) == FAIL)
+	return;
+
+    FOR_ALL_LIST_ITEMS(argvars[1].vval.v_list, li)
+    {
+	if (li->li_tv.v_type != VAR_LIST || li->li_tv.vval.v_list == NULL)
+	{
+	    emsg(_(e_listreq));
+	    return;
+	}
+
+	pos_list = li->li_tv.vval.v_list;
+	start_lnum = list_find_nr(pos_list, 0L, &error);
+	start_col = list_find_nr(pos_list, 1L, &error);
+	end_lnum = list_find_nr(pos_list, 2L, &error);
+	end_col = list_find_nr(pos_list, 3L, &error);
+	if (error || start_lnum <= 0 || start_col <= 0
+		|| end_lnum <= 0 || end_col <= 0)
+	{
+	    emsg(_(e_invarg));
+	    return;
+	}
+	if (prop_add_one(buf, type_name, id, start_lnum, end_lnum,
+						start_col, end_col) == FAIL)
+	    return;
+    }
+
+    buf->b_has_textprop = TRUE;  // this is never reset
+    redraw_buf_later(buf, VALID);
+}
+
+/*
  * Shared between prop_add() and popup_create().
  * "dict_arg" is the function argument of a dict containing "bufnr".
  * it is NULL for popup_create().
@@ -195,20 +389,11 @@ prop_add_common(
 	buf_T	    *default_buf,
 	typval_T    *dict_arg)
 {
-    linenr_T	lnum;
     linenr_T	end_lnum;
     colnr_T	end_col;
     char_u	*type_name;
-    proptype_T	*type;
     buf_T	*buf = default_buf;
     int		id = 0;
-    char_u	*newtext;
-    int		proplen;
-    size_t	textlen;
-    char_u	*props = NULL;
-    char_u	*newprops;
-    textprop_T	tmp_prop;
-    int		i;
 
     if (dict == NULL || dict_find(dict, (char_u *)"type", -1) == NULL)
     {
@@ -260,99 +445,9 @@ prop_add_common(
     if (dict_arg != NULL && get_bufnr_from_arg(dict_arg, &buf) == FAIL)
 	return;
 
-    type = lookup_prop_type(type_name, buf);
-    if (type == NULL)
-	return;
-
-    if (start_lnum < 1 || start_lnum > buf->b_ml.ml_line_count)
-    {
-	semsg(_(e_invalid_lnum), (long)start_lnum);
-	return;
-    }
-    if (end_lnum < start_lnum || end_lnum > buf->b_ml.ml_line_count)
-    {
-	semsg(_(e_invalid_lnum), (long)end_lnum);
-	return;
-    }
-
-    if (buf->b_ml.ml_mfp == NULL)
-    {
-	emsg(_("E275: Cannot add text property to unloaded buffer"));
-	return;
-    }
-
-    for (lnum = start_lnum; lnum <= end_lnum; ++lnum)
-    {
-	colnr_T col;	// start column
-	long	length;	// in bytes
-
-	// Fetch the line to get the ml_line_len field updated.
-	proplen = get_text_props(buf, lnum, &props, TRUE);
-	textlen = buf->b_ml.ml_line_len - proplen * sizeof(textprop_T);
-
-	if (lnum == start_lnum)
-	    col = start_col;
-	else
-	    col = 1;
-	if (col - 1 > (colnr_T)textlen)
-	{
-	    semsg(_(e_invalid_col), (long)start_col);
-	    return;
-	}
-
-	if (lnum == end_lnum)
-	    length = end_col - col;
-	else
-	    length = (int)textlen - col + 1;
-	if (length > (long)textlen)
-	    length = (int)textlen;	// can include the end-of-line
-	if (length < 0)
-	    length = 0;		// zero-width property
-
-	// Allocate the new line with space for the new property.
-	newtext = alloc(buf->b_ml.ml_line_len + sizeof(textprop_T));
-	if (newtext == NULL)
-	    return;
-	// Copy the text, including terminating NUL.
-	mch_memmove(newtext, buf->b_ml.ml_line_ptr, textlen);
-
-	// Find the index where to insert the new property.
-	// Since the text properties are not aligned properly when stored with
-	// the text, we need to copy them as bytes before using it as a struct.
-	for (i = 0; i < proplen; ++i)
-	{
-	    mch_memmove(&tmp_prop, props + i * sizeof(textprop_T),
-							   sizeof(textprop_T));
-	    if (tmp_prop.tp_col >= col)
-		break;
-	}
-	newprops = newtext + textlen;
-	if (i > 0)
-	    mch_memmove(newprops, props, sizeof(textprop_T) * i);
-
-	tmp_prop.tp_col = col;
-	tmp_prop.tp_len = length;
-	tmp_prop.tp_id = id;
-	tmp_prop.tp_type = type->pt_id;
-	tmp_prop.tp_flags = (lnum > start_lnum ? TP_FLAG_CONT_PREV : 0)
-			  | (lnum < end_lnum ? TP_FLAG_CONT_NEXT : 0);
-	mch_memmove(newprops + i * sizeof(textprop_T), &tmp_prop,
-							   sizeof(textprop_T));
-
-	if (i < proplen)
-	    mch_memmove(newprops + (i + 1) * sizeof(textprop_T),
-					    props + i * sizeof(textprop_T),
-					    sizeof(textprop_T) * (proplen - i));
-
-	if (buf->b_ml.ml_flags & ML_LINE_DIRTY)
-	    vim_free(buf->b_ml.ml_line_ptr);
-	buf->b_ml.ml_line_ptr = newtext;
-	buf->b_ml.ml_line_len += sizeof(textprop_T);
-	buf->b_ml.ml_flags |= ML_LINE_DIRTY;
-    }
+    prop_add_one(buf, type_name, id, start_lnum, end_lnum, start_col, end_col);
 
     buf->b_has_textprop = TRUE;  // this is never reset
-    changed_lines_buf(buf, start_lnum, end_lnum + 1, 0);
     redraw_buf_later(buf, VALID);
 }