]> granicus.if.org Git - vim/commitdiff
patch 8.1.1333: text properties don't always move after changes v8.1.1333
authorBram Moolenaar <Bram@vim.org>
Wed, 15 May 2019 20:45:37 +0000 (22:45 +0200)
committerBram Moolenaar <Bram@vim.org>
Wed, 15 May 2019 20:45:37 +0000 (22:45 +0200)
Problem:    Text properties don't always move after changes.
Solution:   Update properties before reporting changes to listeners. Move text
            property when splitting a line.

src/change.c
src/ex_cmds.c
src/proto/textprop.pro
src/testdir/test_textprop.vim
src/textprop.c
src/version.c

index 9b71596ec7867d815e66e32cd126ff85d633d17f..86f1ffcfb4869c78db25a974ba77461afd24810f 100644 (file)
@@ -641,12 +641,12 @@ changed_bytes(linenr_T lnum, colnr_T col)
     void
 inserted_bytes(linenr_T lnum, colnr_T col, int added UNUSED)
 {
-    changed_bytes(lnum, col);
-
 #ifdef FEAT_TEXT_PROP
     if (curbuf->b_has_textprop && added != 0)
        adjust_prop_columns(lnum, col, added);
 #endif
+
+    changed_bytes(lnum, col);
 }
 
 /*
@@ -2133,6 +2133,12 @@ open_line(
            )
            mark_adjust(curwin->w_cursor.lnum + 1, (linenr_T)MAXLNUM, 1L, 0L);
        did_append = TRUE;
+#ifdef FEAT_TEXT_PROP
+       if ((State & INSERT) && !(State & VREPLACE_FLAG))
+           // properties after the split move to the next line
+           adjust_props_for_split(curwin->w_cursor.lnum, curwin->w_cursor.lnum,
+                                                 curwin->w_cursor.col + 1, 0);
+#endif
     }
     else
     {
index 8da5dfd7cadd2d55c07afb25368a94d2e7f8921a..b99e54bce3dc29b868a6bdb263bdfcf941e89f6e 100644 (file)
@@ -5728,7 +5728,7 @@ do_sub(exarg_T *eap)
                                last_line = lnum + 1;
                            }
 #ifdef FEAT_TEXT_PROP
-                           adjust_props_for_split(lnum, plen, 1);
+                           adjust_props_for_split(lnum + 1, lnum, plen, 1);
 #endif
                            // all line numbers increase
                            ++sub_firstlnum;
index 0eac557e3e25d9f96361272d448f437d2f8f5505..6d9553e287b814aede4798a84c55222b72d310a8 100644 (file)
@@ -14,5 +14,5 @@ void f_prop_type_list(typval_T *argvars, typval_T *rettv);
 void clear_global_prop_types(void);
 void clear_buf_prop_types(buf_T *buf);
 void adjust_prop_columns(linenr_T lnum, colnr_T col, int bytes_added);
-void adjust_props_for_split(linenr_T lnum, int kept, int deleted);
+void adjust_props_for_split(linenr_T lnum_props, linenr_T lnum_top, int kept, int deleted);
 /* vim: set ft=c : */
index a48aa91a434788b3d55a437f85640bb513a7bea8..6cc38d56634770da65cb9057eebcd73ef36dc141 100644 (file)
@@ -151,6 +151,7 @@ endfunc
 
 func SetupOneLine()
   call setline(1, 'xonex xtwoxx')
+  normal gg0
   call AddPropTypes()
   call prop_add(1, 2, {'length': 3, 'id': 11, 'type': 'one'})
   call prop_add(1, 8, {'length': 3, 'id': 12, 'type': 'two'})
@@ -272,6 +273,66 @@ func Test_prop_replace()
   set bs&
 endfunc
 
+func Test_prop_open_line()
+  new
+
+  " open new line, props stay in top line
+  let expected = SetupOneLine() " 'xonex xtwoxx'
+  exe "normal o\<Esc>"
+  call assert_equal('xonex xtwoxx', getline(1))
+  call assert_equal('', getline(2))
+  call assert_equal(expected, prop_list(1))
+  call DeletePropTypes()
+
+  " move all props to next line
+  let expected = SetupOneLine() " 'xonex xtwoxx'
+  exe "normal 0i\<CR>\<Esc>"
+  call assert_equal('', getline(1))
+  call assert_equal('xonex xtwoxx', getline(2))
+  call assert_equal(expected, prop_list(2))
+  call DeletePropTypes()
+
+  " split just before prop, move all props to next line
+  let expected = SetupOneLine() " 'xonex xtwoxx'
+  exe "normal 0li\<CR>\<Esc>"
+  call assert_equal('x', getline(1))
+  call assert_equal('onex xtwoxx', getline(2))
+  let expected[0].col -= 1
+  let expected[1].col -= 1
+  call assert_equal(expected, prop_list(2))
+  call DeletePropTypes()
+
+  " split inside prop, split first prop
+  let expected = SetupOneLine() " 'xonex xtwoxx'
+  exe "normal 0lli\<CR>\<Esc>"
+  call assert_equal('xo', getline(1))
+  call assert_equal('nex xtwoxx', getline(2))
+  let exp_first = [deepcopy(expected[0])]
+  let exp_first[0].length = 1
+  call assert_equal(exp_first, prop_list(1))
+  let expected[0].col = 1
+  let expected[0].length = 2
+  let expected[1].col -= 2
+  call assert_equal(expected, prop_list(2))
+  call DeletePropTypes()
+
+  " split just after first prop, empty prop and second prop move to next line
+  let expected = SetupOneLine() " 'xonex xtwoxx'
+  exe "normal 0fea\<CR>\<Esc>"
+  call assert_equal('xone', getline(1))
+  call assert_equal('x xtwoxx', getline(2))
+  let exp_first = expected[0:0]
+  call assert_equal(exp_first, prop_list(1))
+  let expected[0].col = 1
+  let expected[0].length = 0
+  let expected[1].col -= 4
+  call assert_equal(expected, prop_list(2))
+  call DeletePropTypes()
+
+  bwipe!
+  set bs&
+endfunc
+
 func Test_prop_clear()
   new
   call AddPropTypes()
index b44810a8276ebd87eb9333ca4eb401085759ffda..8c1e46ca680e2b07377d24c4336f983bd17e2015 100644 (file)
@@ -8,18 +8,15 @@
  */
 
 /*
- * Text properties implementation.
- *
- * Text properties are attached to the text.  They move with the text when
- * text is inserted/deleted.
- *
- * Text properties have a user specified ID number, which can be unique.
- * Text properties have a type, which can be used to specify highlighting.
+ * Text properties implementation.  See ":help text-properties".
  *
  * TODO:
  * - When using 'cursorline' attributes should be merged. (#3912)
  * - Adjust text property column and length when text is inserted/deleted.
+ *   -> splitting a line can create a zero-length property.  Don't highlight it
+ *      and extend it when inserting text.
  *   -> a :substitute with a multi-line match
+ *   -> join two lines, also with BS in Insert mode
  *   -> search for changed_bytes() from misc1.c
  * - Perhaps we only need TP_FLAG_CONT_NEXT and can drop TP_FLAG_CONT_PREV?
  * - Add an arrray for global_proptypes, to quickly lookup a prop type by ID
@@ -28,8 +25,6 @@
  *   the index, like DB_MARKED?
  * - Also test line2byte() with many lines, so that ml_updatechunk() is taken
  *   into account.
- * - Add mechanism to keep track of changed lines, so that plugin can update
- *   text properties in these.
  * - Perhaps have a window-local option to disable highlighting from text
  *   properties?
  */
@@ -1033,12 +1028,17 @@ adjust_prop_columns(
 
 /*
  * Adjust text properties for a line that was split in two.
- * "lnum" is the newly inserted line.  The text properties are now on the line
- * below it.  "kept" is the number of bytes kept in the first line, while
+ * "lnum_props" is the line that has the properties from before the split.
+ * "lnum_top" is the top line.
+ * "kept" is the number of bytes kept in the first line, while
  * "deleted" is the number of bytes deleted.
  */
     void
-adjust_props_for_split(linenr_T lnum, int kept, int deleted)
+adjust_props_for_split(
+       linenr_T lnum_props,
+       linenr_T lnum_top,
+       int kept,
+       int deleted)
 {
     char_u     *props;
     int                count;
@@ -1049,11 +1049,12 @@ adjust_props_for_split(linenr_T lnum, int kept, int deleted)
 
     if (!curbuf->b_has_textprop)
        return;
-    count = get_text_props(curbuf, lnum + 1, &props, FALSE);
+
+    // Get the text properties from "lnum_props".
+    count = get_text_props(curbuf, lnum_props, &props, FALSE);
     ga_init2(&prevprop, sizeof(textprop_T), 10);
     ga_init2(&nextprop, sizeof(textprop_T), 10);
 
-    // Get the text properties, which are at "lnum + 1".
     // Keep the relevant ones in the first line, reducing the length if needed.
     // Copy the ones that include the split to the second line.
     // Move the ones after the split to the second line.
@@ -1089,10 +1090,11 @@ adjust_props_for_split(linenr_T lnum, int kept, int deleted)
        }
     }
 
-    set_text_props(lnum, prevprop.ga_data, prevprop.ga_len * sizeof(textprop_T));
+    set_text_props(lnum_top, prevprop.ga_data,
+                                        prevprop.ga_len * sizeof(textprop_T));
     ga_clear(&prevprop);
-
-    set_text_props(lnum + 1, nextprop.ga_data, nextprop.ga_len * sizeof(textprop_T));
+    set_text_props(lnum_top + 1, nextprop.ga_data,
+                                        nextprop.ga_len * sizeof(textprop_T));
     ga_clear(&nextprop);
 }
 
index 0c61dab2067cd05348e5f0ba83179b96ed25fdb2..3a6c0c0080ab9a70ed37ad813599f4bd679d2b01 100644 (file)
@@ -767,6 +767,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1333,
 /**/
     1332,
 /**/