Patch 8.1.1341
Problem: Text properties are lost when joining lines.
Solution: Move the text properties to the joined line.
Files: src/ops.c, src/textprop.c, src/proto/textprop.pro,
src/testdir/test_textprop.vim,
src/testdir/dumps/Test_textprop_01.dump
*** ../vim-8.1.1340/src/ops.c 2019-04-27 22:06:33.348200718 +0200
--- src/ops.c 2019-05-17 16:42:27.190882289 +0200
***************
*** 1211,1217 ****
int retval = OK;
int remap;
! if (regname == '@') /* repeat previous one */
{
if (execreg_lastc == NUL)
{
--- 1211,1218 ----
int retval = OK;
int remap;
! // repeat previous one
! if (regname == '@')
{
if (execreg_lastc == NUL)
{
***************
*** 1220,1226 ****
}
regname = execreg_lastc;
}
! /* check for valid regname */
if (regname == '%' || regname == '#' || !valid_yank_reg(regname, FALSE))
{
emsg_invreg(regname);
--- 1221,1227 ----
}
regname = execreg_lastc;
}
! // check for valid regname
if (regname == '%' || regname == '#' || !valid_yank_reg(regname, FALSE))
{
emsg_invreg(regname);
***************
*** 1232,1242 ****
regname = may_get_selection(regname);
#endif
! if (regname == '_') /* black hole: don't stuff
anything */
return OK;
#ifdef FEAT_CMDHIST
! if (regname == ':') /* use last command line */
{
if (last_cmdline == NULL)
{
--- 1233,1245 ----
regname = may_get_selection(regname);
#endif
! // black hole: don't stuff anything
! if (regname == '_')
return OK;
#ifdef FEAT_CMDHIST
! // use last command line
! if (regname == ':')
{
if (last_cmdline == NULL)
{
***************
*** 4438,4444 ****
&& has_format_option(FO_REMOVE_COMS);
int prev_was_comment;
#endif
!
if (save_undo && u_save((linenr_T)(curwin->w_cursor.lnum - 1),
(linenr_T)(curwin->w_cursor.lnum + count)) == FAIL)
--- 4441,4450 ----
&& has_format_option(FO_REMOVE_COMS);
int prev_was_comment;
#endif
! #ifdef FEAT_TEXT_PROP
! textprop_T **prop_lines = NULL;
! int *prop_lengths = NULL;
! #endif
if (save_undo && u_save((linenr_T)(curwin->w_cursor.lnum - 1),
(linenr_T)(curwin->w_cursor.lnum + count)) == FAIL)
***************
*** 4463,4470 ****
#endif
/*
! * Don't move anything, just compute the final line length
* and setup the array of space strings lengths
*/
for (t = 0; t < count; ++t)
{
--- 4469,4477 ----
#endif
/*
! * Don't move anything yet, just compute the final line length
* and setup the array of space strings lengths
+ * This loops forward over the joined lines.
*/
for (t = 0; t < count; ++t)
{
***************
*** 4556,4563 ****
--- 4563,4586 ----
cend = newp + sumsize;
*cend = 0;
+ #ifdef FEAT_TEXT_PROP
+ // We need to move properties of the lines that are going to be deleted to
+ // the new long one.
+ if (curbuf->b_has_textprop && !text_prop_frozen)
+ {
+ // Allocate an array to copy the text properties of joined lines into.
+ // And another array to store the number of properties in each line.
+ prop_lines = (textprop_T **)alloc_clear(
+ (int)(count - 1) * sizeof(textprop_T *));
+ prop_lengths = (int *)alloc_clear((int)(count - 1) * sizeof(int));
+ if (prop_lengths == NULL)
+ VIM_CLEAR(prop_lines);
+ }
+ #endif
+
/*
* Move affected lines to the new long one.
+ * This loops backwards over the joined lines, including the original
line.
*
* Move marks from each deleted line to the joined line, adjusting the
* column. This is not Vi compatible, but Vi deletes the marks, thus that
***************
*** 4583,4590 ****
(long)(cend - newp - spaces_removed), spaces_removed);
if (t == 0)
break;
curr = curr_start = ml_get((linenr_T)(curwin->w_cursor.lnum + t - 1));
! #if defined(FEAT_COMMENTS) || defined(PROTO)
if (remove_comments)
curr += comments[t - 1];
#endif
--- 4606,4620 ----
(long)(cend - newp - spaces_removed), spaces_removed);
if (t == 0)
break;
+ #ifdef FEAT_TEXT_PROP
+ if (prop_lines != NULL)
+ adjust_props_for_join(curwin->w_cursor.lnum + t,
+ prop_lines + t - 1, prop_lengths + t - 1,
+ (long)(cend - newp - spaces_removed), spaces_removed);
+ #endif
+
curr = curr_start = ml_get((linenr_T)(curwin->w_cursor.lnum + t - 1));
! #if defined(FEAT_COMMENTS)
if (remove_comments)
curr += comments[t - 1];
#endif
***************
*** 4592,4598 ****
curr = skipwhite(curr);
currsize = (int)STRLEN(curr);
}
! ml_replace(curwin->w_cursor.lnum, newp, FALSE);
if (setmark)
{
--- 4622,4635 ----
curr = skipwhite(curr);
currsize = (int)STRLEN(curr);
}
!
! #ifdef FEAT_TEXT_PROP
! if (prop_lines != NULL)
! join_prop_lines(curwin->w_cursor.lnum, newp,
! prop_lines, prop_lengths, count);
! else
! #endif
! ml_replace(curwin->w_cursor.lnum, newp, FALSE);
if (setmark)
{
***************
*** 4605,4611 ****
* the deleted line. */
changed_lines(curwin->w_cursor.lnum, currsize,
curwin->w_cursor.lnum + 1, 0L);
-
/*
* Delete following lines. To do this we move the cursor there
* briefly, and then move it back. After del_lines() the cursor may
--- 4642,4647 ----
*** ../vim-8.1.1340/src/textprop.c 2019-05-17 13:05:03.795770160 +0200
--- src/textprop.c 2019-05-17 19:31:14.135899352 +0200
***************
*** 13,20 ****
* TODO:
* - Adjust text property column and length when text is inserted/deleted.
* -> 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
* - Add an arrray for b_proptypes, to quickly lookup a prop type by ID
--- 13,20 ----
* TODO:
* - 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
+ * -> search for mark_col_adjust()
* - 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
* - Add an arrray for b_proptypes, to quickly lookup a prop type by ID
***************
*** 1097,1100 ****
--- 1097,1205 ----
ga_clear(&nextprop);
}
+ /*
+ * Line "lnum" has been joined and will end up at column "col" in the new
line.
+ * "removed" bytes have been removed from the start of the line, properties
+ * there are to be discarded.
+ * Move the adjusted text properties to an allocated string, store it in
+ * "prop_line" and adjust the columns.
+ */
+ void
+ adjust_props_for_join(
+ linenr_T lnum,
+ textprop_T **prop_line,
+ int *prop_length,
+ long col,
+ int removed)
+ {
+ int proplen;
+ char_u *props;
+ int ri;
+ int wi = 0;
+
+ proplen = get_text_props(curbuf, lnum, &props, FALSE);
+ if (proplen > 0)
+ {
+ *prop_line = (textprop_T *)alloc(proplen * (int)sizeof(textprop_T));
+ if (*prop_line != NULL)
+ {
+ for (ri = 0; ri < proplen; ++ri)
+ {
+ textprop_T *cp = *prop_line + wi;
+
+ mch_memmove(cp, props + ri * sizeof(textprop_T),
+ sizeof(textprop_T));
+ if (cp->tp_col + cp->tp_len > removed)
+ {
+ if (cp->tp_col > removed)
+ cp->tp_col += col;
+ else
+ {
+ // property was partly deleted, make it shorter
+ cp->tp_len -= removed - cp->tp_col;
+ cp->tp_col = col;
+ }
+ ++wi;
+ }
+ }
+ }
+ *prop_length = wi;
+ }
+ }
+
+ /*
+ * After joining lines: concatenate the text and the properties of all joined
+ * lines into one line and replace the line.
+ */
+ void
+ join_prop_lines(
+ linenr_T lnum,
+ char_u *newp,
+ textprop_T **prop_lines,
+ int *prop_lengths,
+ int count)
+ {
+ size_t proplen = 0;
+ size_t oldproplen;
+ char_u *props;
+ int i;
+ int len;
+ char_u *line;
+ size_t l;
+
+ for (i = 0; i < count - 1; ++i)
+ proplen += prop_lengths[i];
+ if (proplen == 0)
+ {
+ ml_replace(lnum, newp, FALSE);
+ return;
+ }
+
+ // get existing properties of the joined line
+ oldproplen = get_text_props(curbuf, lnum, &props, FALSE);
+
+ len = (int)STRLEN(newp) + 1;
+ line = alloc(len + (oldproplen + proplen) * (int)sizeof(textprop_T));
+ if (line == NULL)
+ return;
+ mch_memmove(line, newp, len);
+ l = oldproplen * sizeof(textprop_T);
+ mch_memmove(line + len, props, l);
+ len += l;
+
+ for (i = 0; i < count - 1; ++i)
+ if (prop_lines[i] != NULL)
+ {
+ l = prop_lengths[i] * sizeof(textprop_T);
+ mch_memmove(line + len, prop_lines[i], l);
+ len += l;
+ vim_free(prop_lines[i]);
+ }
+
+ ml_replace_len(lnum, line, len, TRUE, FALSE);
+ vim_free(newp);
+ vim_free(prop_lines);
+ vim_free(prop_lengths);
+ }
+
#endif // FEAT_TEXT_PROP
*** ../vim-8.1.1340/src/proto/textprop.pro 2019-05-15 22:45:33.956067651
+0200
--- src/proto/textprop.pro 2019-05-17 16:55:50.965270535 +0200
***************
*** 15,18 ****
--- 15,20 ----
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);
+ void adjust_props_for_join(linenr_T lnum, textprop_T **prop_line, int
*prop_length, long col, int removed);
+ void join_prop_lines(linenr_T lnum, char_u *newp, textprop_T **prop_lines,
int *prop_lengths, int count);
/* vim: set ft=c : */
*** ../vim-8.1.1340/src/testdir/test_textprop.vim 2019-05-17
13:05:03.795770160 +0200
--- src/testdir/test_textprop.vim 2019-05-17 19:34:08.751016230 +0200
***************
*** 624,629 ****
--- 624,633 ----
\ .. "'Numbér 123 änd thœn 4¾7.',"
\ .. "'--aa--bb--cc--dd--',"
\ .. "'// comment with error in it',"
+ \ .. "'first line',"
+ \ .. "' second line ',"
+ \ .. "'third line',"
+ \ .. "' fourth line',"
\ .. "])",
\ "hi NumberProp ctermfg=blue",
\ "hi LongProp ctermbg=yellow",
***************
*** 645,650 ****
--- 649,658 ----
\ "call prop_add(3, 15, {'length': 2, 'type': 'both'})",
\ "call prop_add(4, 12, {'length': 10, 'type': 'background'})",
\ "call prop_add(4, 17, {'length': 5, 'type': 'error'})",
+ \ "call prop_add(5, 7, {'length': 4, 'type': 'long'})",
+ \ "call prop_add(6, 1, {'length': 8, 'type': 'long'})",
+ \ "call prop_add(8, 1, {'length': 1, 'type': 'long'})",
+ \ "call prop_add(8, 11, {'length': 4, 'type': 'long'})",
\ "set number cursorline",
\ "hi clear SpellBad",
\ "set spell",
***************
*** 652,659 ****
\ "hi Comment ctermfg=green",
\ "normal
3G0llix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>",
\ "normal 3G0lli\<BS>\<Esc>",
\], 'XtestProp')
! let buf = RunVimInTerminal('-S XtestProp', {'rows': 7})
call VerifyScreenDump(buf, 'Test_textprop_01', {})
" clean up
--- 660,670 ----
\ "hi Comment ctermfg=green",
\ "normal
3G0llix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>",
\ "normal 3G0lli\<BS>\<Esc>",
+ \ "normal 6G0i\<BS>\<Esc>",
+ \ "normal 3J",
+ \ "normal 3G",
\], 'XtestProp')
! let buf = RunVimInTerminal('-S XtestProp', {'rows': 8})
call VerifyScreenDump(buf, 'Test_textprop_01', {})
" clean up
*** ../vim-8.1.1340/src/testdir/dumps/Test_textprop_01.dump 2019-05-17
13:05:03.795770160 +0200
--- src/testdir/dumps/Test_textprop_01.dump 2019-05-17 19:34:56.874772440
+0200
***************
*** 2,7 ****
--- 2,8 ----
| +0#af5f00255&@1|2| |N+0#0000000#ffff4012|u|m|b|é|r| |1+0#4040ff13&|2|3|
+0#0000000&|ä|n|d| |t|h|œ|n| |4+0#4040ff13&|¾|7|.+0#0000000&| +0&#ffffff0@46
| +0#af5f00255&@1|3|
>-+8#0000000#ffff4012|x+8&#ffffff0|a+8#4040ff13&@1|x+8#0000000&|-@1|x+8#4040ff13&|b@1|x+8#0000000&|-@1|x|c+8#4040ff13&@1|x|-+8#0000000&@1|x+8#4040ff13&|d@1|x|-+8#0000000&@1|
@45
| +0#af5f00255&@1|4| |/+0#40ff4011&@1| |c|o|m@1|e|n|t| |w+0&#e0e0e08|i|t|h|
|e+8&&|r@1|o|r| +0&#ffffff0|i|n| |i|t| +0#0000000&@43
+ | +0#af5f00255&@1|5| |f+0#0000000&|i|r|s|t| |l+0&#ffff4012|i|n|e|
@1|s|e|c|o|n|d| +0&#ffffff0|l|i|n|e| @1|t|h|i|r|d| |l|i|n|e| |f|o|u|r|t|h|
|l+0&#ffff4012|i|n|e| +0&#ffffff0@23
|~+0#4040ff13&| @73
|~| @73
| +0#0000000&@56|3|,|1| @10|A|l@1|
*** ../vim-8.1.1340/src/version.c 2019-05-17 13:05:03.795770160 +0200
--- src/version.c 2019-05-17 19:35:36.942569315 +0200
***************
*** 769,770 ****
--- 769,772 ----
{ /* Add new patch number below this line */
+ /**/
+ 1341,
/**/
--
Lower life forms have more fun!
/// Bram Moolenaar -- [email protected] -- 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 ///
--
--
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php
---
You received this message because you are subscribed to the Google Groups
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/vim_dev/201905171756.x4HHunXo018667%40masaka.moolenaar.net.
For more options, visit https://groups.google.com/d/optout.