# HG changeset patch # User Bram Moolenaar # Date 1545777007 -3600 # Node ID 19e79a1ed6b6de69c1cebdfe29eddd60fb520e4e # Parent 7790044a7348d44c98a1c41df93e50922c7f6a44 patch 8.1.0636: line2byte() gives wrong values with text properties commit https://github.com/vim/vim/commit/b413d2e6a8cc7b1611a41bfa9462b986393ca5fe Author: Bram Moolenaar 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) diff --git a/src/memline.c b/src/memline.c --- 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) { diff --git a/src/proto/textprop.pro b/src/proto/textprop.pro --- 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); diff --git a/src/structs.h b/src/structs.h --- 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) diff --git a/src/testdir/test_textprop.vim b/src/testdir/test_textprop.vim --- 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 diff --git a/src/textprop.c b/src/textprop.c --- 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. diff --git a/src/version.c b/src/version.c --- 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,