To: vim_dev@googlegroups.com Subject: Patch 8.1.1333 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.1.1333 Problem: Text properties don't always move after changes. Solution: Update properties before reporting changes to listeners. Move text property when splitting a line. Files: src/change.c, src/ex_cmds.c, src/textprop.c, src/proto/textprop.pro, src/testdir/test_textprop.vim *** ../vim-8.1.1332/src/change.c 2019-05-14 21:20:32.597441034 +0200 --- src/change.c 2019-05-15 22:14:42.293974215 +0200 *************** *** 641,652 **** 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 } /* --- 641,652 ---- void inserted_bytes(linenr_T lnum, colnr_T col, int added UNUSED) { #ifdef FEAT_TEXT_PROP if (curbuf->b_has_textprop && added != 0) adjust_prop_columns(lnum, col, added); #endif + + changed_bytes(lnum, col); } /* *************** *** 2133,2138 **** --- 2133,2144 ---- ) 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 { *** ../vim-8.1.1332/src/ex_cmds.c 2019-05-09 15:12:45.168723969 +0200 --- src/ex_cmds.c 2019-05-15 22:02:51.794417449 +0200 *************** *** 5728,5734 **** last_line = lnum + 1; } #ifdef FEAT_TEXT_PROP ! adjust_props_for_split(lnum, plen, 1); #endif // all line numbers increase ++sub_firstlnum; --- 5728,5734 ---- last_line = lnum + 1; } #ifdef FEAT_TEXT_PROP ! adjust_props_for_split(lnum + 1, lnum, plen, 1); #endif // all line numbers increase ++sub_firstlnum; *** ../vim-8.1.1332/src/textprop.c 2019-05-05 16:33:44.490168111 +0200 --- src/textprop.c 2019-05-15 22:42:36.048949732 +0200 *************** *** 8,25 **** */ /* ! * 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. * * TODO: * - When using 'cursorline' attributes should be merged. (#3912) * - Adjust text property column and length when text is inserted/deleted. * -> a :substitute with a multi-line match * -> 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 --- 8,22 ---- */ /* ! * 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,35 **** * 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? */ --- 25,30 ---- *************** *** 1033,1044 **** /* * 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 * "deleted" is the number of bytes deleted. */ void ! adjust_props_for_split(linenr_T lnum, int kept, int deleted) { char_u *props; int count; --- 1028,1044 ---- /* * Adjust text properties for a line that was split in two. ! * "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_props, ! linenr_T lnum_top, ! int kept, ! int deleted) { char_u *props; int count; *************** *** 1049,1059 **** if (!curbuf->b_has_textprop) return; ! count = get_text_props(curbuf, lnum + 1, &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. --- 1049,1060 ---- if (!curbuf->b_has_textprop) return; ! ! // 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); // 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,1098 **** } } ! set_text_props(lnum, 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)); ga_clear(&nextprop); } --- 1090,1100 ---- } } ! set_text_props(lnum_top, prevprop.ga_data, ! prevprop.ga_len * sizeof(textprop_T)); ga_clear(&prevprop); ! set_text_props(lnum_top + 1, nextprop.ga_data, ! nextprop.ga_len * sizeof(textprop_T)); ga_clear(&nextprop); } *** ../vim-8.1.1332/src/proto/textprop.pro 2019-01-04 23:09:45.249360567 +0100 --- src/proto/textprop.pro 2019-05-15 22:05:41.925276920 +0200 *************** *** 14,18 **** 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); /* vim: set ft=c : */ --- 14,18 ---- 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_props, linenr_T lnum_top, int kept, int deleted); /* vim: set ft=c : */ *** ../vim-8.1.1332/src/testdir/test_textprop.vim 2019-05-05 15:47:37.825923529 +0200 --- src/testdir/test_textprop.vim 2019-05-15 22:41:42.505209936 +0200 *************** *** 151,156 **** --- 151,157 ---- 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'}) *************** *** 271,276 **** --- 272,337 ---- bwipe! 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\" + 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\\" + 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\\" + 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\\" + 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\\" + 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 *** ../vim-8.1.1332/src/version.c 2019-05-14 21:20:32.597441034 +0200 --- src/version.c 2019-05-15 22:44:49.468290481 +0200 *************** *** 769,770 **** --- 769,772 ---- { /* Add new patch number below this line */ + /**/ + 1333, /**/ -- BLACK KNIGHT: I'm invincible! ARTHUR: You're a looney. "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///