changeset 15255:19e79a1ed6b6 v8.1.0636

patch 8.1.0636: line2byte() gives wrong values with text properties commit https://github.com/vim/vim/commit/b413d2e6a8cc7b1611a41bfa9462b986393ca5fe Author: Bram Moolenaar <Bram@vim.org> Date: Tue Dec 25 23:15:46 2018 +0100 patch 8.1.0636: line2byte() gives wrong values with text properties Problem: line2byte() gives wrong values with text properties. (Bjorn Linse) Solution: Compute byte offsets differently when text properties were added. (closes #3718)
author Bram Moolenaar <Bram@vim.org>
date Tue, 25 Dec 2018 23:30:07 +0100
parents 7790044a7348
children 9f0d16a7148d
files src/memline.c src/proto/textprop.pro src/structs.h src/testdir/test_textprop.vim src/textprop.c src/version.c
diffstat 6 files changed, 66 insertions(+), 26 deletions(-) [+]
line wrap: on
line diff
--- a/src/memline.c
+++ b/src/memline.c
@@ -3179,14 +3179,14 @@ ml_replace_len(linenr_T lnum, char_u *li
 	curbuf->b_ml.ml_flags &= ~ML_LINE_DIRTY;
 
 #ifdef FEAT_TEXT_PROP
-	if (has_any_text_properties(curbuf))
+	if (curbuf->b_has_textprop)
 	    // Need to fetch the old line to copy over any text properties.
 	    ml_get_buf(curbuf, lnum, TRUE);
 #endif
     }
 
 #ifdef FEAT_TEXT_PROP
-    if (has_any_text_properties(curbuf))
+    if (curbuf->b_has_textprop)
     {
 	size_t	oldtextlen = STRLEN(curbuf->b_ml.ml_line_ptr) + 1;
 
@@ -5131,6 +5131,7 @@ ml_updatechunk(
 	{
 	    int	    count;	    /* number of entries in block */
 	    int	    idx;
+	    int	    end_idx;
 	    int	    text_end;
 	    int	    linecnt;
 
@@ -5154,23 +5155,39 @@ ml_updatechunk(
 			(long)(buf->b_ml.ml_locked_low) + 1;
 		idx = curline - buf->b_ml.ml_locked_low;
 		curline = buf->b_ml.ml_locked_high + 1;
-		if (idx == 0)/* first line in block, text at the end */
-		    text_end = dp->db_txt_end;
-		else
-		    text_end = ((dp->db_index[idx - 1]) & DB_INDEX_MASK);
-		/* Compute index of last line to use in this MEMLINE */
+
+		// compute index of last line to use in this MEMLINE
 		rest = count - idx;
 		if (linecnt + rest > MLCS_MINL)
 		{
-		    idx += MLCS_MINL - linecnt - 1;
+		    end_idx = idx + MLCS_MINL - linecnt - 1;
 		    linecnt = MLCS_MINL;
 		}
 		else
 		{
-		    idx = count - 1;
+		    end_idx = count - 1;
 		    linecnt += rest;
 		}
-		size += text_end - ((dp->db_index[idx]) & DB_INDEX_MASK);
+#ifdef FEAT_TEXT_PROP
+		if (buf->b_has_textprop)
+		{
+		    int i;
+
+		    // We cannot use the text pointers to get the text length,
+		    // the text prop info would also be counted.  Go over the
+		    // lines.
+		    for (i = end_idx; i < idx; ++i)
+			size += STRLEN((char_u *)dp + (dp->db_index[i] & DB_INDEX_MASK)) + 1;
+		}
+		else
+#endif
+		{
+		    if (idx == 0)/* first line in block, text at the end */
+			text_end = dp->db_txt_end;
+		    else
+			text_end = ((dp->db_index[idx - 1]) & DB_INDEX_MASK);
+		    size += text_end - ((dp->db_index[end_idx]) & DB_INDEX_MASK);
+		}
 	    }
 	    buf->b_ml.ml_chunksize[curix].mlcs_numlines = linecnt;
 	    buf->b_ml.ml_chunksize[curix + 1].mlcs_numlines -= linecnt;
@@ -5360,7 +5377,20 @@ ml_find_line_or_offset(buf_T *buf, linen
 		idx++;
 	    }
 	}
-	len = text_end - ((dp->db_index[idx]) & DB_INDEX_MASK);
+#ifdef FEAT_TEXT_PROP
+	if (buf->b_has_textprop)
+	{
+	    int i;
+
+	    // cannot use the db_index pointer, need to get the actual text
+	    // lengths.
+	    len = 0;
+	    for (i = start_idx; i <= idx; ++i)
+		len += STRLEN((char_u *)dp + ((dp->db_index[idx]) & DB_INDEX_MASK)) + 1;
+	}
+	else
+#endif
+	    len = text_end - ((dp->db_index[idx]) & DB_INDEX_MASK);
 	size += len;
 	if (offset != 0 && size >= offset)
 	{
--- a/src/proto/textprop.pro
+++ b/src/proto/textprop.pro
@@ -1,6 +1,5 @@
 /* textprop.c */
 void f_prop_add(typval_T *argvars, typval_T *rettv);
-int has_any_text_properties(buf_T *buf);
 int get_text_props(buf_T *buf, linenr_T lnum, char_u **props, int will_change);
 proptype_T *text_prop_type_by_id(buf_T *buf, int id);
 void f_prop_clear(typval_T *argvars, typval_T *rettv);
--- a/src/structs.h
+++ b/src/structs.h
@@ -2411,7 +2411,8 @@ struct file_buffer
     dict_T	*b_vars;	/* internal variables, local to buffer */
 #endif
 #ifdef FEAT_TEXT_PROP
-    hashtab_T	*b_proptypes;	/* text property types local to buffer */
+    int		b_has_textprop;	// TRUE when text props were added
+    hashtab_T	*b_proptypes;	// text property types local to buffer
 #endif
 
 #if defined(FEAT_BEVAL) && defined(FEAT_EVAL)
--- a/src/testdir/test_textprop.vim
+++ b/src/testdir/test_textprop.vim
@@ -226,5 +226,17 @@ func Test_prop_multiline()
   call prop_type_delete('comment')
 endfunc
 
+func Test_prop_byteoff()
+  call prop_type_add('comment', {'highlight': 'Directory'})
+  new
+  call setline(1, ['line1', 'line2', ''])
+  call assert_equal(13, line2byte(3))
+  call prop_add(1, 1, {'end_col': 3, 'type': 'comment'})
+  call assert_equal(13, line2byte(3))
+
+  bwipe!
+  call prop_type_delete('comment')
+endfunc
+
 
 " TODO: screenshot test with highlighting
--- a/src/textprop.c
+++ b/src/textprop.c
@@ -17,12 +17,16 @@
  * Text properties have a type, which can be used to specify highlighting.
  *
  * TODO:
+ * - mismatch in column 1 being the first column
+ * - Let props overrule syntax HL.
  * - When deleting a line where a prop ended, adjust flag of previous line.
  * - When deleting a line where a prop started, adjust flag of next line.
  * - When inserting a line add props that continue from previous line.
  * - Adjust property column and length when text is inserted/deleted
  * - Add an arrray for global_proptypes, to quickly lookup a proptype by ID
  * - Add an arrray for b_proptypes, to quickly lookup a proptype by ID
+ * - Also test line2byte() with many lines, so that ml_updatechunk() is taken
+ *   into account.
  * - add mechanism to keep track of changed lines.
  */
 
@@ -261,7 +265,7 @@ f_prop_add(typval_T *argvars, typval_T *
 	    length = end_col - col + 1;
 	else
 	    length = textlen - col + 1;
-	if (length > textlen)
+	if (length > (long)textlen)
 	    length = textlen;  // can include the end-of-line
 	if (length < 1)
 	    length = 1;
@@ -308,20 +312,11 @@ f_prop_add(typval_T *argvars, typval_T *
 	buf->b_ml.ml_flags |= ML_LINE_DIRTY;
     }
 
+    buf->b_has_textprop = TRUE;  // this is never reset
     redraw_buf_later(buf, NOT_VALID);
 }
 
 /*
- * Return TRUE if any text properties are defined globally or for buffer
- * "buf".
- */
-    int
-has_any_text_properties(buf_T *buf)
-{
-    return buf->b_proptypes != NULL || global_proptypes != NULL;
-}
-
-/*
  * Fetch the text properties for line "lnum" in buffer "buf".
  * Returns the number of text properties and, when non-zero, a pointer to the
  * first one in "props" (note that it is not aligned, therefore the char_u
@@ -334,8 +329,9 @@ get_text_props(buf_T *buf, linenr_T lnum
     size_t textlen;
     size_t proplen;
 
-    // Be quick when no text property types are defined.
-    if (!has_any_text_properties(buf))
+    // Be quick when no text property types have been defined or the buffer,
+    // unless we are adding one.
+    if (!buf->b_has_textprop && !will_change)
 	return 0;
 
     // Fetch the line to get the ml_line_len field updated.
--- a/src/version.c
+++ b/src/version.c
@@ -800,6 +800,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    636,
+/**/
     635,
 /**/
     634,