On Tuesday, May 26, 2015 at 3:50:06 AM UTC+2, Justin M. Keyes wrote:
> In your implementation, you could save the './'^/jumplist/changelist
> and then restore it after the script ends. This would allow plugins to
> use marks et. al. as needed but the user can use :keepjumps! to
> prevent side effects from the plugin.

Ok, I did this, see the attached patch.
Everything seems to work great (and all tests pass etc), and I am quite pleased 
with the result.
However, this is now a considerably larger patch, and I really need some 
feedback on both the implementation and the interface.

Reagrding the implementation:

The way it works is that there are duplicated entries for all items which need 
to be restored at the end of the execution, and more precisely:
1) for buffers: last_cursor, last_insert, last_change, changelist, changelistlen
2) for windows: pcmark, prev_pcmark, jumplist, jumplistsen, changelistidx
There are also additional entries in the structs, to keep track if we're 
actively keeping a backup of those.

Then, within functions `mark_adjust`, `mark_col_adjust` and `cleanup_jumplist`, 
the backup entries are updated (if the backup tag is active).
This ensures that, if the code called by keepjumps! is making changes, those 
are reflected in the jumplist/changelist/etc when those are restored at the end.

I thought about alternative ways to achieve the same effect but I can't see any 
simpler way to keep that guarantee (which is essential in my opinion).
This is the major point on which I'd like to have feedback. Is this ok? Did I 
miss something? Will this get merged (possibly after some polishing, of course)?

Regarding the interface:

As discussed, I used :keepjumps! for the recursive version.
This has forced me to make some changes to the parser, to account for the 
possibility of the trailing bang in the command.
I don't particularly like those changes.
First, they are very slightly breaking, in that before, ":keepjumps! command" 
was parsed as "keepjumps" followed by "!command". I don't think this is 
important (note: all other commands still parse the same).
Second, ":keepjumps" and ":keepjumps!" behave in a slightly different way in 
some cases, besides the fact that :keepjumps! is recursive. Consider this 
example:

  :keepjumps normal ggdd``
  :keepjumps! normal ggdd``

In the first case, the ' mark is not set by the gg jump, and therefore the 
cursor is brought back to the *previous* location of the ' mark, if any.
In the second case, the ' mark is set by the gg jump, the cursor is brought 
back to the location from where the command was invoked, and the previous ' 
mark is restored afterwards (if it existed at all).

I have a proposal: instead of introducing keepjumps!, just redefine :keepjumps 
to act recursively.
This is slightly breaking for 2 reasons: 1) examples such as the one above, 2) 
cases in which :keepjumps would have been a no-op.
I don't know what's the vim policy about these kind of slightly breaking 
changes, but I think that the recursive version is what one would want most of 
the time.
I also doubt that anyone would be relying on such behaviours as the ones which 
would be broken by the change.


Carlo Baldassi

-- 
-- 
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].
For more options, visit https://groups.google.com/d/optout.
diff --git a/runtime/doc/motion.txt b/runtime/doc/motion.txt
--- a/runtime/doc/motion.txt
+++ b/runtime/doc/motion.txt
@@ -993,7 +993,7 @@
                        the same effect as using ":keepmarks".
 
                                                        *:keepj* *:keepjumps*
-:keepj[umps] {command}
+:keepj[umps][!] {command}
                        Moving around in {command} does not change the |''|,
                        |'.| and |'^| marks, the |jumplist| or the
                        |changelist|.
@@ -1009,9 +1009,13 @@
 <
                        Note that ":keepjumps" must be used for every command.
                        When invoking a function the commands in that function
-                       can still change the jumplist.  Also, for
-                       ":keepjumps exe 'command '" the "command" won't keep
-                       jumps.  Instead use: ":exe 'keepjumps command'"
+                       can still change the jumplist, unless the [!] is
+                       given. When the [!] is given, keepjump makes a backup
+                       of the jumplist, the changelist and the |''|, |'.|,
+                       |'^| marks, and restores them when finished.
+                       Also, for ":keepjumps exe 'command '" the "command"
+                       won't keep jumps.  Instead use: ":exe 'keepjumps
+                       command'" or ":keepjumps! exe 'command '".
 
 ==============================================================================
 8. Jumps                                       *jump-motions*
diff --git a/runtime/doc/tags b/runtime/doc/tags
--- a/runtime/doc/tags
+++ b/runtime/doc/tags
@@ -6733,6 +6733,7 @@
 lcs-extends    options.txt     /*lcs-extends*
 lcs-nbsp       options.txt     /*lcs-nbsp*
 lcs-precedes   options.txt     /*lcs-precedes*
+lcs-space      options.txt     /*lcs-space*
 lcs-tab        options.txt     /*lcs-tab*
 lcs-trail      options.txt     /*lcs-trail*
 left-right-motions     motion.txt      /*left-right-motions*
diff --git a/src/eval.c b/src/eval.c
--- a/src/eval.c
+++ b/src/eval.c
@@ -22464,7 +22464,7 @@
                ;
 
            /* Check for "endfunction". */
-           if (checkforcmd(&p, "endfunction", 4) && nesting-- == 0)
+           if (checkforcmd(&p, "endfunction", 4, FALSE) && nesting-- == 0)
            {
                if (line_arg == NULL)
                    vim_free(theline);
@@ -22482,7 +22482,7 @@
                indent += 2;
 
            /* Check for defining a function inside this function. */
-           if (checkforcmd(&p, "function", 2))
+           if (checkforcmd(&p, "function", 2, FALSE))
            {
                if (*p == '!')
                    p = skipwhite(p + 1);
diff --git a/src/ex_cmds.h b/src/ex_cmds.h
--- a/src/ex_cmds.h
+++ b/src/ex_cmds.h
@@ -679,7 +679,7 @@
                        NEEDARG|EXTRA|NOTRLCOM,
                        ADDR_LINES),
 EX(CMD_keepjumps,      "keepjumps",    ex_wrongmodifier,
-                       NEEDARG|EXTRA|NOTRLCOM,
+                       NEEDARG|BANG|EXTRA|NOTRLCOM,
                        ADDR_LINES),
 EX(CMD_keeppatterns,   "keeppatterns", ex_wrongmodifier,
                        NEEDARG|EXTRA|NOTRLCOM,
diff --git a/src/ex_docmd.c b/src/ex_docmd.c
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -1828,6 +1828,7 @@
     int                        did_sandbox = FALSE;
 #endif
     cmdmod_T           save_cmdmod;
+    int                        cmdcheck_ret;
     int                        ni;                     /* set when Not 
Implemented */
     char_u             *cmd;
 
@@ -1858,6 +1859,9 @@
     save_cmdmod = cmdmod;
     vim_memset(&cmdmod, 0, sizeof(cmdmod));
 
+    /* keepjumps! is recursive */
+    cmdmod.keepjumps_bang = save_cmdmod.keepjumps_bang;
+
     /* "#!anything" is handled like a comment. */
     if ((*cmdlinep)[0] == '#' && (*cmdlinep)[1] == '!')
        goto doend;
@@ -1902,83 +1906,93 @@
        switch (*p)
        {
            /* When adding an entry, also modify cmd_exists(). */
-           case 'a':   if (!checkforcmd(&ea.cmd, "aboveleft", 3))
+           case 'a':   if (!checkforcmd(&ea.cmd, "aboveleft", 3, FALSE))
                            break;
 #ifdef FEAT_WINDOWS
                        cmdmod.split |= WSP_ABOVE;
 #endif
                        continue;
 
-           case 'b':   if (checkforcmd(&ea.cmd, "belowright", 3))
+           case 'b':   if (checkforcmd(&ea.cmd, "belowright", 3, FALSE))
                        {
 #ifdef FEAT_WINDOWS
                            cmdmod.split |= WSP_BELOW;
 #endif
                            continue;
                        }
-                       if (checkforcmd(&ea.cmd, "browse", 3))
+                       if (checkforcmd(&ea.cmd, "browse", 3, FALSE))
                        {
 #ifdef FEAT_BROWSE_CMD
                            cmdmod.browse = TRUE;
 #endif
                            continue;
                        }
-                       if (!checkforcmd(&ea.cmd, "botright", 2))
+                       if (!checkforcmd(&ea.cmd, "botright", 2, FALSE))
                            break;
 #ifdef FEAT_WINDOWS
                        cmdmod.split |= WSP_BOT;
 #endif
                        continue;
 
-           case 'c':   if (!checkforcmd(&ea.cmd, "confirm", 4))
+           case 'c':   if (!checkforcmd(&ea.cmd, "confirm", 4, FALSE))
                            break;
 #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
                        cmdmod.confirm = TRUE;
 #endif
                        continue;
 
-           case 'k':   if (checkforcmd(&ea.cmd, "keepmarks", 3))
+           case 'k':   if (checkforcmd(&ea.cmd, "keepmarks", 3, FALSE))
                        {
                            cmdmod.keepmarks = TRUE;
                            continue;
                        }
-                       if (checkforcmd(&ea.cmd, "keepalt", 5))
+                       if (checkforcmd(&ea.cmd, "keepalt", 5, FALSE))
                        {
                            cmdmod.keepalt = TRUE;
                            continue;
                        }
-                       if (checkforcmd(&ea.cmd, "keeppatterns", 5))
+                       if (checkforcmd(&ea.cmd, "keeppatterns", 5, FALSE))
                        {
                            cmdmod.keeppatterns = TRUE;
                            continue;
                        }
-                       if (!checkforcmd(&ea.cmd, "keepjumps", 5))
-                           break;
-                       cmdmod.keepjumps = TRUE;
-                       continue;
+                       cmdcheck_ret = checkforcmd(&ea.cmd, "keepjumps", 5, 
TRUE);
+                       if (cmdcheck_ret == TRUER)
+                       {
+                           if (!cmdmod.keepjumps_bang)
+                               keepjumps_do_backup();
+                           cmdmod.keepjumps_bang = TRUE;
+                           continue;
+                       }
+                       else if (cmdcheck_ret == TRUE)
+                       {
+                           cmdmod.keepjumps = TRUE;
+                           continue;
+                       }
+                       break;
 
                        /* ":hide" and ":hide | cmd" are not modifiers */
-           case 'h':   if (p != ea.cmd || !checkforcmd(&p, "hide", 3)
+           case 'h':   if (p != ea.cmd || !checkforcmd(&p, "hide", 3, FALSE)
                                               || *p == NUL || ends_excmd(*p))
                            break;
                        ea.cmd = p;
                        cmdmod.hide = TRUE;
                        continue;
 
-           case 'l':   if (checkforcmd(&ea.cmd, "lockmarks", 3))
+           case 'l':   if (checkforcmd(&ea.cmd, "lockmarks", 3, FALSE))
                        {
                            cmdmod.lockmarks = TRUE;
                            continue;
                        }
 
-                       if (!checkforcmd(&ea.cmd, "leftabove", 5))
+                       if (!checkforcmd(&ea.cmd, "leftabove", 5, FALSE))
                            break;
 #ifdef FEAT_WINDOWS
                        cmdmod.split |= WSP_ABOVE;
 #endif
                        continue;
 
-           case 'n':   if (checkforcmd(&ea.cmd, "noautocmd", 3))
+           case 'n':   if (checkforcmd(&ea.cmd, "noautocmd", 3, FALSE))
                        {
 #ifdef FEAT_AUTOCMD
                            if (cmdmod.save_ei == NULL)
@@ -1992,19 +2006,19 @@
 #endif
                            continue;
                        }
-                       if (!checkforcmd(&ea.cmd, "noswapfile", 6))
+                       if (!checkforcmd(&ea.cmd, "noswapfile", 6, FALSE))
                            break;
                        cmdmod.noswapfile = TRUE;
                        continue;
 
-           case 'r':   if (!checkforcmd(&ea.cmd, "rightbelow", 6))
+           case 'r':   if (!checkforcmd(&ea.cmd, "rightbelow", 6, FALSE))
                            break;
 #ifdef FEAT_WINDOWS
                        cmdmod.split |= WSP_BELOW;
 #endif
                        continue;
 
-           case 's':   if (checkforcmd(&ea.cmd, "sandbox", 3))
+           case 's':   if (checkforcmd(&ea.cmd, "sandbox", 3, FALSE))
                        {
 #ifdef HAVE_SANDBOX
                            if (!did_sandbox)
@@ -2013,7 +2027,7 @@
 #endif
                            continue;
                        }
-                       if (!checkforcmd(&ea.cmd, "silent", 3))
+                       if (!checkforcmd(&ea.cmd, "silent", 3, FALSE))
                            break;
                        if (save_msg_silent == -1)
                            save_msg_silent = msg_silent;
@@ -2027,7 +2041,7 @@
                        }
                        continue;
 
-           case 't':   if (checkforcmd(&p, "tab", 3))
+           case 't':   if (checkforcmd(&p, "tab", 3, FALSE))
                        {
 #ifdef FEAT_WINDOWS
                            if (vim_isdigit(*ea.cmd))
@@ -2038,28 +2052,28 @@
 #endif
                            continue;
                        }
-                       if (!checkforcmd(&ea.cmd, "topleft", 2))
+                       if (!checkforcmd(&ea.cmd, "topleft", 2, FALSE))
                            break;
 #ifdef FEAT_WINDOWS
                        cmdmod.split |= WSP_TOP;
 #endif
                        continue;
 
-           case 'u':   if (!checkforcmd(&ea.cmd, "unsilent", 3))
+           case 'u':   if (!checkforcmd(&ea.cmd, "unsilent", 3, FALSE))
                            break;
                        if (save_msg_silent == -1)
                            save_msg_silent = msg_silent;
                        msg_silent = 0;
                        continue;
 
-           case 'v':   if (checkforcmd(&ea.cmd, "vertical", 4))
+           case 'v':   if (checkforcmd(&ea.cmd, "vertical", 4, FALSE))
                        {
 #ifdef FEAT_VERTSPLIT
                            cmdmod.split |= WSP_VERT;
 #endif
                            continue;
                        }
-                       if (!checkforcmd(&p, "verbose", 4))
+                       if (!checkforcmd(&p, "verbose", 4, FALSE))
                            break;
                        if (verbose_save < 0)
                            verbose_save = p_verbose;
@@ -2998,6 +3012,9 @@
     }
 #endif
 
+    if (cmdmod.keepjumps_bang && !save_cmdmod.keepjumps_bang)
+       keepjumps_do_restore();
+
     cmdmod = save_cmdmod;
 
     if (save_msg_silent != -1)
@@ -3039,23 +3056,28 @@
 
 /*
  * Check for an Ex command with optional tail.
- * If there is a match advance "pp" to the argument and return TRUE.
+ * If there is a match advance "pp" to the argument and return TRUE
+ * (return TRUER if the command is followed by a '!').
  */
     int
-checkforcmd(pp, cmd, len)
+checkforcmd(pp, cmd, len, parse_bang)
     char_u     **pp;           /* start of command */
     char       *cmd;           /* name of command */
     int                len;            /* required length */
-{
-    int                i;
+    int                parse_bang;     /* whether ! is part of the command */
+{
+    int                i, bang;
 
     for (i = 0; cmd[i] != NUL; ++i)
        if (((char_u *)cmd)[i] != (*pp)[i])
            break;
     if (i >= len && !isalpha((*pp)[i]))
     {
+       bang = parse_bang && ((*pp)[i] == '!');
+       if (bang)
+           ++i;
        *pp = skipwhite(*pp + i);
-       return TRUE;
+       return bang ? TRUER : TRUE;
     }
     return FALSE;
 }
@@ -3328,29 +3350,30 @@
     char       *name;
     int                minlen;
     int                has_count;  /* :123verbose  :3tab */
+    int                has_bang;
 } cmdmods[] = {
-    {"aboveleft", 3, FALSE},
-    {"belowright", 3, FALSE},
-    {"botright", 2, FALSE},
-    {"browse", 3, FALSE},
-    {"confirm", 4, FALSE},
-    {"hide", 3, FALSE},
-    {"keepalt", 5, FALSE},
-    {"keepjumps", 5, FALSE},
-    {"keepmarks", 3, FALSE},
-    {"keeppatterns", 5, FALSE},
-    {"leftabove", 5, FALSE},
-    {"lockmarks", 3, FALSE},
-    {"noautocmd", 3, FALSE},
-    {"noswapfile", 3, FALSE},
-    {"rightbelow", 6, FALSE},
-    {"sandbox", 3, FALSE},
-    {"silent", 3, FALSE},
-    {"tab", 3, TRUE},
-    {"topleft", 2, FALSE},
-    {"unsilent", 3, FALSE},
-    {"verbose", 4, TRUE},
-    {"vertical", 4, FALSE},
+    {"aboveleft", 3, FALSE, FALSE},
+    {"belowright", 3, FALSE, FALSE},
+    {"botright", 2, FALSE, FALSE},
+    {"browse", 3, FALSE, FALSE},
+    {"confirm", 4, FALSE, FALSE},
+    {"hide", 3, FALSE, FALSE},
+    {"keepalt", 5, FALSE, FALSE},
+    {"keepjumps", 5, FALSE, TRUE},
+    {"keepmarks", 3, FALSE, FALSE},
+    {"keeppatterns", 5, FALSE, FALSE},
+    {"leftabove", 5, FALSE, FALSE},
+    {"lockmarks", 3, FALSE, FALSE},
+    {"noautocmd", 3, FALSE, FALSE},
+    {"noswapfile", 3, FALSE, FALSE},
+    {"rightbelow", 6, FALSE, FALSE},
+    {"sandbox", 3, FALSE, FALSE},
+    {"silent", 3, FALSE, FALSE},
+    {"tab", 3, TRUE, FALSE},
+    {"topleft", 2, FALSE, FALSE},
+    {"unsilent", 3, FALSE, FALSE},
+    {"verbose", 4, TRUE, FALSE},
+    {"vertical", 4, FALSE, FALSE},
 };
 
 /*
@@ -3361,7 +3384,7 @@
 modifier_len(cmd)
     char_u     *cmd;
 {
-    int                i, j;
+    int                i, j, len;
     char_u     *p = cmd;
 
     if (VIM_ISDIGIT(*cmd))
@@ -3371,7 +3394,10 @@
        for (j = 0; p[j] != NUL; ++j)
            if (p[j] != cmdmods[i].name[j])
                break;
-       if (!ASCII_ISALPHA(p[j]) && j >= cmdmods[i].minlen
+       len = j;
+       if (cmdmods[i].has_bang && p[j] == '!')
+           ++j;
+       if (!ASCII_ISALPHA(p[j]) && len >= cmdmods[i].minlen
                                        && (p == cmd || cmdmods[i].has_count))
            return j + (int)(p - cmd);
     }
@@ -3391,6 +3417,7 @@
     int                full = FALSE;
     int                i;
     int                j;
+    int                len;
     char_u     *p;
 
     /* Check command modifiers. */
@@ -3399,7 +3426,10 @@
        for (j = 0; name[j] != NUL; ++j)
            if (name[j] != cmdmods[i].name[j])
                break;
-       if (name[j] == NUL && j >= cmdmods[i].minlen)
+       len = j;
+       if (cmdmods[i].has_bang && name[j] == '!')
+           ++j;
+       if (name[j] == NUL && len >= cmdmods[i].minlen)
            return (cmdmods[i].name[j] == NUL ? 2 : 1);
     }
 
@@ -5280,7 +5310,7 @@
        }
        else
            eap->force_bin = FORCE_BIN;
-       if (!checkforcmd(&arg, "binary", 3))
+       if (!checkforcmd(&arg, "binary", 3, FALSE))
            return FAIL;
        eap->arg = skipwhite(arg);
        return OK;
diff --git a/src/mark.c b/src/mark.c
--- a/src/mark.c
+++ b/src/mark.c
@@ -1068,11 +1068,24 @@
        if (!equalpos(curbuf->b_last_cursor, initpos))
            one_adjust(&(curbuf->b_last_cursor.lnum));
 
+       /* same as the above, for the keepjumps! backups */
+       if (cmdmod.keepjumps_bang && curbuf->b_bk_state)
+       {
+           one_adjust(&(curbuf->b_bk_last_insert.lnum));
+           one_adjust(&(curbuf->b_bk_last_change.lnum));
+           if (!equalpos(curbuf->b_bk_last_cursor, initpos))
+               one_adjust(&(curbuf->b_bk_last_cursor.lnum));
+       }
 
 #ifdef FEAT_JUMPLIST
        /* list of change positions */
        for (i = 0; i < curbuf->b_changelistlen; ++i)
            one_adjust_nodel(&(curbuf->b_changelist[i].lnum));
+
+       /* same as the above, for the keepjumps! backups */
+       if (cmdmod.keepjumps_bang && curbuf->b_bk_state)
+           for (i = 0; i < curbuf->b_bk_changelistlen; ++i)
+               one_adjust_nodel(&(curbuf->b_bk_changelist[i].lnum));
 #endif
 
        /* Visual area */
@@ -1098,6 +1111,13 @@
     /* previous pcmark */
     one_adjust(&(curwin->w_prev_pcmark.lnum));
 
+    /* same as the above, for the keepjumps! backups */
+    if (cmdmod.keepjumps_bang && curwin->w_bk_state)
+    {
+       one_adjust(&(curwin->w_bk_pcmark.lnum));
+       one_adjust(&(curwin->w_bk_prev_pcmark.lnum));
+    }
+
     /* saved cursor for formatting */
     if (saved_cursor.lnum != 0)
        one_adjust_nodel(&(saved_cursor.lnum));
@@ -1109,11 +1129,19 @@
     {
 #ifdef FEAT_JUMPLIST
        if (!cmdmod.lockmarks)
+       {
            /* Marks in the jumplist.  When deleting lines, this may create
             * duplicate marks in the jumplist, they will be removed later. */
            for (i = 0; i < win->w_jumplistlen; ++i)
                if (win->w_jumplist[i].fmark.fnum == fnum)
                    one_adjust_nodel(&(win->w_jumplist[i].fmark.mark.lnum));
+
+           if (cmdmod.keepjumps_bang && win->w_bk_state)
+               for (i = 0; i < win->w_bk_jumplistlen; ++i)
+                   if (win->w_bk_jumplist[i].fmark.fnum == fnum)
+                       one_adjust_nodel(
+                               &(win->w_bk_jumplist[i].fmark.mark.lnum));
+       }
 #endif
 
        if (win->w_buffer == curbuf)
@@ -1256,6 +1284,13 @@
     /* previous pcmark */
     col_adjust(&(curwin->w_prev_pcmark));
 
+    /* same as the above, for the keepjumps! backups */
+    if (cmdmod.keepjumps_bang && curwin->w_bk_state)
+    {
+       col_adjust(&(curwin->w_bk_pcmark));
+       col_adjust(&(curwin->w_bk_prev_pcmark));
+    }
+
     /* saved cursor for formatting */
     col_adjust(&saved_cursor);
 
@@ -1269,6 +1304,11 @@
        for (i = 0; i < win->w_jumplistlen; ++i)
            if (win->w_jumplist[i].fmark.fnum == fnum)
                col_adjust(&(win->w_jumplist[i].fmark.mark));
+
+       if (cmdmod.keepjumps_bang && win->w_bk_state)
+           for (i = 0; i < win->w_bk_jumplistlen; ++i)
+               if (win->w_bk_jumplist[i].fmark.fnum == fnum)
+                   col_adjust(&(win->w_bk_jumplist[i].fmark.mark));
 #endif
 
        if (win->w_buffer == curbuf)
@@ -1290,32 +1330,88 @@
  * When deleting lines, this may create duplicate marks in the
  * jumplist. They will be removed here for the current window.
  */
+
+    void
+cleanup_jumplist_(jumplist, lenp, idxp)
+    xfmark_T   *jumplist;
+    int                *lenp;
+    int                *idxp;
+{
+    int        len, idx;
+    int        i;
+    int        from, to;
+
+    len = *lenp;
+    idx = *idxp;
+
+    to = 0;
+    for (from = 0; from < len; ++from)
+    {
+       if (idx == from)
+           idx = to;
+       for (i = from + 1; i < len; ++i)
+           if (jumplist[i].fmark.fnum == jumplist[from].fmark.fnum
+                   && jumplist[from].fmark.fnum != 0
+                   && jumplist[i].fmark.mark.lnum
+                                 == jumplist[from].fmark.mark.lnum)
+               break;
+       if (i >= len)       /* no duplicate */
+           jumplist[to++] = jumplist[from];
+       else
+           vim_free(jumplist[from].fname);
+    }
+    if (idx == len)
+       idx = to;
+    len = to;
+
+    *lenp = len;
+    *idxp = idx;
+}
+
     static void
 cleanup_jumplist()
 {
-    int            i;
-    int            from, to;
+    cleanup_jumplist_(curwin->w_jumplist, &curwin->w_jumplistlen,
+                                                   &curwin->w_jumplistidx);
+    if (cmdmod.keepjumps_bang || curwin->w_bk_state)
+       cleanup_jumplist_(curwin->w_bk_jumplist, &curwin->w_bk_jumplistlen,
+                                                   &curwin->w_bk_jumplistidx);
+}
 
-    to = 0;
-    for (from = 0; from < curwin->w_jumplistlen; ++from)
+    void
+copy_jumplist(dest, src, len)
+    xfmark_T   *src;
+    xfmark_T   *dest;
+    int                len;
+{
+    int                i;
+
+    for (i = 0; i < len; ++i)
     {
-       if (curwin->w_jumplistidx == from)
-           curwin->w_jumplistidx = to;
-       for (i = from + 1; i < curwin->w_jumplistlen; ++i)
-           if (curwin->w_jumplist[i].fmark.fnum
-                                       == curwin->w_jumplist[from].fmark.fnum
-                   && curwin->w_jumplist[from].fmark.fnum != 0
-                   && curwin->w_jumplist[i].fmark.mark.lnum
-                                 == curwin->w_jumplist[from].fmark.mark.lnum)
-               break;
-       if (i >= curwin->w_jumplistlen)     /* no duplicate */
-           curwin->w_jumplist[to++] = curwin->w_jumplist[from];
-       else
-           vim_free(curwin->w_jumplist[from].fname);
+       dest[i] = src[i];
+       if (src[i].fname != NULL)
+           dest[i].fname = vim_strsave(src[i].fname);
     }
-    if (curwin->w_jumplistidx == curwin->w_jumplistlen)
-       curwin->w_jumplistidx = to;
-    curwin->w_jumplistlen = to;
+}
+
+    void
+copy_changelist(dest, src, size)
+    pos_T* dest;
+    pos_T* src;
+    int size;
+{
+    mch_memmove(dest, src, size * sizeof(pos_T));
+}
+
+    void
+free_jumplist(jumplist, len)
+    xfmark_T   *jumplist;
+    int                len;
+{
+    int                i;
+
+    for (i = 0; i < len; ++i)
+       vim_free(jumplist[i].fname);
 }
 
 # if defined(FEAT_WINDOWS) || defined(PROTO)
@@ -1323,33 +1419,35 @@
  * Copy the jumplist from window "from" to window "to".
  */
     void
-copy_jumplist(from, to)
+copy_w_jumplist(from, to)
     win_T      *from;
     win_T      *to;
 {
     int                i;
 
-    for (i = 0; i < from->w_jumplistlen; ++i)
-    {
-       to->w_jumplist[i] = from->w_jumplist[i];
-       if (from->w_jumplist[i].fname != NULL)
-           to->w_jumplist[i].fname = vim_strsave(from->w_jumplist[i].fname);
-    }
+    copy_jumplist(to->w_jumplist, from->w_jumplist, from->w_jumplistlen);
     to->w_jumplistlen = from->w_jumplistlen;
     to->w_jumplistidx = from->w_jumplistidx;
+
+    if (from->w_bk_state)
+    {
+       copy_jumplist(to->w_bk_jumplist, from->w_bk_jumplist,
+                                               from->w_bk_jumplistlen);
+       to->w_bk_jumplistlen = from->w_bk_jumplistlen;
+       to->w_bk_jumplistidx = from->w_bk_jumplistidx;
+    }
 }
 
 /*
  * Free items in the jumplist of window "wp".
  */
     void
-free_jumplist(wp)
+free_w_jumplist(wp)
     win_T      *wp;
 {
-    int                i;
-
-    for (i = 0; i < wp->w_jumplistlen; ++i)
-       vim_free(wp->w_jumplist[i].fname);
+    free_jumplist(wp->w_jumplist, wp->w_jumplistlen);
+    if (wp->w_bk_state)
+       free_jumplist(wp->w_bk_jumplist, wp->w_bk_jumplistlen);
 }
 # endif
 #endif /* FEAT_JUMPLIST */
@@ -1814,3 +1912,81 @@
     vim_free(name_buf);
 }
 #endif /* FEAT_VIMINFO */
+
+    void
+keepjumps_do_backup()
+{
+    win_T              *wp;
+    tabpage_T          *tp;
+    buf_T               *bp;
+
+    FOR_ALL_TAB_WINDOWS(tp, wp)
+       wp->w_buffer->b_bk_state = FALSE;
+    FOR_ALL_TAB_WINDOWS(tp, wp)
+    {
+#ifdef FEAT_JUMPLIST
+       copy_jumplist(wp->w_bk_jumplist,
+               wp->w_jumplist, wp->w_jumplistlen);
+       wp->w_bk_jumplistlen = wp->w_jumplistlen;
+       wp->w_bk_jumplistidx = wp->w_jumplistidx;
+       wp->w_bk_changelistidx = wp->w_changelistidx;
+#endif
+       wp->w_bk_pcmark = wp->w_pcmark;
+       wp->w_bk_prev_pcmark = wp->w_prev_pcmark;
+       wp->w_bk_state = TRUE;
+
+       bp = wp->w_buffer;
+       if (bp->b_bk_state == TRUE)
+           continue;
+
+       bp->b_bk_last_cursor = bp->b_last_cursor;
+       bp->b_bk_last_insert = bp->b_last_insert;
+       bp->b_bk_last_change = bp->b_last_change;
+#ifdef FEAT_JUMPLIST
+       copy_changelist(bp->b_bk_changelist,
+               bp->b_changelist, bp->b_changelistlen);
+       bp->b_bk_changelistlen = bp->b_changelistlen;
+#endif
+       bp->b_bk_state = TRUE;
+    }
+}
+
+    void
+keepjumps_do_restore()
+{
+    win_T              *wp;
+    tabpage_T          *tp;
+    buf_T               *bp;
+
+    FOR_ALL_TAB_WINDOWS(tp, wp)
+    {
+#ifdef FEAT_JUMPLIST
+       free_jumplist(wp->w_jumplist, wp->w_jumplistlen);
+       copy_jumplist(wp->w_jumplist,
+               wp->w_bk_jumplist, wp->w_bk_jumplistlen);
+       wp->w_jumplistlen = wp->w_bk_jumplistlen;
+       wp->w_jumplistidx = wp->w_bk_jumplistidx;
+       wp->w_changelistidx = wp->w_bk_changelistidx;
+       free_jumplist(wp->w_bk_jumplist, wp->w_bk_jumplistlen);
+       wp->w_bk_jumplistlen = 0;
+#endif
+       wp->w_pcmark = wp->w_bk_pcmark;
+       wp->w_prev_pcmark = wp->w_bk_prev_pcmark;
+       wp->w_bk_state = FALSE;
+
+       bp = wp->w_buffer;
+       if (!bp->b_bk_state)
+           continue;
+
+       bp->b_last_cursor = bp->b_bk_last_cursor;
+       bp->b_last_insert = bp->b_bk_last_insert;
+       bp->b_last_change = bp->b_bk_last_change;
+#ifdef FEAT_JUMPLIST
+       copy_changelist(bp->b_changelist,
+               bp->b_bk_changelist, bp->b_bk_changelistlen);
+       bp->b_changelistlen = bp->b_bk_changelistlen;
+       bp->b_bk_changelistlen = 0;
+#endif
+       bp->b_bk_state = FALSE;
+    }
+}
diff --git a/src/proto/ex_docmd.pro b/src/proto/ex_docmd.pro
--- a/src/proto/ex_docmd.pro
+++ b/src/proto/ex_docmd.pro
@@ -4,7 +4,7 @@
 int do_cmdline __ARGS((char_u *cmdline, char_u *(*fgetline)(int, void *, int), 
void *cookie, int flags));
 int getline_equal __ARGS((char_u *(*fgetline)(int, void *, int), void *cookie, 
char_u *(*func)(int, void *, int)));
 void *getline_cookie __ARGS((char_u *(*fgetline)(int, void *, int), void 
*cookie));
-int checkforcmd __ARGS((char_u **pp, char *cmd, int len));
+int checkforcmd __ARGS((char_u **pp, char *cmd, int len, int parse_bang));
 int modifier_len __ARGS((char_u *cmd));
 int cmd_exists __ARGS((char_u *name));
 char_u *set_one_cmd_context __ARGS((expand_T *xp, char_u *buff));
diff --git a/src/proto/mark.pro b/src/proto/mark.pro
--- a/src/proto/mark.pro
+++ b/src/proto/mark.pro
@@ -19,8 +19,11 @@
 void ex_changes __ARGS((exarg_T *eap));
 void mark_adjust __ARGS((linenr_T line1, linenr_T line2, long amount, long 
amount_after));
 void mark_col_adjust __ARGS((linenr_T lnum, colnr_T mincol, long lnum_amount, 
long col_amount));
-void copy_jumplist __ARGS((win_T *from, win_T *to));
-void free_jumplist __ARGS((win_T *wp));
+void copy_jumplist __ARGS((xfmark_T *dest, xfmark_T *src, int len));
+void copy_changelist __ARGS((pos_T *dest, pos_T *src, int len));
+void free_jumplist __ARGS((xfmark_T *jumplist, int len));
+void copy_w_jumplist __ARGS((win_T *from, win_T *to));
+void free_w_jumplist __ARGS((win_T *wp));
 void set_last_cursor __ARGS((win_T *win));
 void free_all_marks __ARGS((void));
 int read_viminfo_filemark __ARGS((vir_T *virp, int force));
@@ -28,4 +31,6 @@
 int removable __ARGS((char_u *name));
 int write_viminfo_marks __ARGS((FILE *fp_out));
 void copy_viminfo_marks __ARGS((vir_T *virp, FILE *fp_out, int count, int eof, 
int flags));
+void keepjumps_do_backup __ARGS(());
+void keepjumps_do_restore __ARGS(());
 /* vim: set ft=c : */
diff --git a/src/structs.h b/src/structs.h
--- a/src/structs.h
+++ b/src/structs.h
@@ -549,6 +549,7 @@
     int                keepalt;                /* TRUE when ":keepalt" was 
used */
     int                keepmarks;              /* TRUE when ":keepmarks" was 
used */
     int                keepjumps;              /* TRUE when ":keepjumps" was 
used */
+    int                keepjumps_bang;         /* TRUE when ":keepjumps!" was 
used */
     int                lockmarks;              /* TRUE when ":lockmarks" was 
used */
     int                keeppatterns;           /* TRUE when ":keeppatterns" 
was used */
     int                noswapfile;             /* TRUE when ":noswapfile" was 
used */
@@ -1456,6 +1457,14 @@
     pos_T      b_last_insert;  /* where Insert mode was left */
     pos_T      b_last_change;  /* position of last change: '. mark */
 
+    /*
+     * Backups used during keepjumps!
+     */
+    int                b_bk_state;     /* TRUE while backups are boing kept */
+    pos_T      b_bk_last_cursor;
+    pos_T      b_bk_last_insert;
+    pos_T      b_bk_last_change;
+
 #ifdef FEAT_JUMPLIST
     /*
      * the changelist contains old change positions
@@ -1463,6 +1472,12 @@
     pos_T      b_changelist[JUMPLISTSIZE];
     int                b_changelistlen;        /* number of active entries */
     int                b_new_change;           /* set by u_savecommon() */
+
+    /*
+     * Backups used during keepjumps!
+     */
+    pos_T      b_bk_changelist[JUMPLISTSIZE];
+    int                b_bk_changelistlen;
 #endif
 
     /*
@@ -2271,6 +2286,13 @@
     pos_T      w_pcmark;       /* previous context mark */
     pos_T      w_prev_pcmark;  /* previous w_pcmark */
 
+    /*
+     * Backups used during keepjumps!
+     */
+    int                w_bk_state;     /* TRUE while backups are boing kept */
+    pos_T      w_bk_pcmark;
+    pos_T      w_bk_prev_pcmark;
+
 #ifdef FEAT_JUMPLIST
     /*
      * the jumplist contains old cursor positions
@@ -2280,6 +2302,14 @@
     int                w_jumplistidx;          /* current position */
 
     int                w_changelistidx;        /* current position in 
b_changelist */
+
+    /*
+     * Backups used during keepjumps!
+     */
+    xfmark_T   w_bk_jumplist[JUMPLISTSIZE];
+    int                w_bk_jumplistlen;
+    int                w_bk_jumplistidx;
+    int                w_bk_changelistidx;
 #endif
 
 #ifdef FEAT_SEARCH_EXTRA
diff --git a/src/vim.h b/src/vim.h
--- a/src/vim.h
+++ b/src/vim.h
@@ -1411,6 +1411,8 @@
 #endif
 
 #define MAYBE  2           /* sometimes used for a variant on TRUE */
+#define TRUER  3           /* sometimes used for a variant on TRUE */
+
 
 #ifndef UINT32_T
 typedef UINT32_TYPEDEF UINT32_T;
diff --git a/src/window.c b/src/window.c
--- a/src/window.c
+++ b/src/window.c
@@ -1363,7 +1363,7 @@
     newp->w_fraction = oldp->w_fraction;
     newp->w_prev_fraction_row = oldp->w_prev_fraction_row;
 #ifdef FEAT_JUMPLIST
-    copy_jumplist(oldp, newp);
+    copy_w_jumplist(oldp, newp);
 #endif
 #ifdef FEAT_QUICKFIX
     if (flags & WSP_NEWLOC)
@@ -4765,7 +4765,7 @@
 #endif
 
 #ifdef FEAT_JUMPLIST
-    free_jumplist(wp);
+    free_w_jumplist(wp);
 #endif
 
 #ifdef FEAT_QUICKFIX

Raspunde prin e-mail lui