patch 9.1.1224: cannot :put while keeping indent

Commit: 
https://github.com/vim/vim/commit/e08f10a55c3f15b0b4af631908551d88ec4fe502
Author: 64-bitman <60551350+64-bit...@users.noreply.github.com>
Date:   Tue Mar 18 22:14:34 2025 +0100

    patch 9.1.1224: cannot :put while keeping indent
    
    Problem:  cannot :put while keeping indent (Peter Aronoff)
    Solution: add the :iput ex command (64-bitman)
    
    fixes: #16225
    closes: #16886
    
    Co-authored-by: zeertzjq <zeert...@outlook.com>
    Co-authored-by: Hirohito Higashi <h.east....@gmail.com>
    Signed-off-by: 64-bitman <60551350+64-bit...@users.noreply.github.com>
    Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt
index 301eacee2..c656a117c 100644
--- a/runtime/doc/change.txt
+++ b/runtime/doc/change.txt
@@ -1,4 +1,4 @@
-*change.txt*    For Vim version 9.1.  Last change: 2024 Dec 15
+*change.txt*    For Vim version 9.1.  Last change: 2025 Mar 18
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -1149,6 +1149,11 @@ inside of strings can change!  Also see 'softtabstop' 
option. >
 :[line]pu[t]! [x]      Put the text [from register x] before [line] (default
                        current line).
 
+                                                       *:ip* *:iput*
+:[line]ip[ut] [x]      like |:put|, but adjust indent to the current line
+
+:[line]ip[ut]! [x]     like |:put|!, but adjust indent to the current line
+
 ["x]]p             or                                  *]p* *]<MiddleMouse>*
 ["x]]<MiddleMouse>     Like "p", but adjust the indent to the current line.
                        Using the mouse only works when 'mouse' contains 'n'
diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt
index 06a24e046..024102cdd 100644
--- a/runtime/doc/index.txt
+++ b/runtime/doc/index.txt
@@ -1,4 +1,4 @@
-*index.txt*     For Vim version 9.1.  Last change: 2025 Jan 03
+*index.txt*     For Vim version 9.1.  Last change: 2025 Mar 18
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -1400,6 +1400,8 @@ tag               command         action ~
 |:inoremenu|   :inoreme[nu]    like ":noremenu" but for Insert mode
 |:intro|       :int[ro]        print the introductory message
 |:interface|   :interface      start of an interface declaration
+|:iput|                :ip[ut]         like |:put|, but adjust the indent to 
the
+                               current line
 |:isearch|     :is[earch]      list one line where identifier matches
 |:isplit|      :isp[lit]       split window and jump to definition of
                                identifier
diff --git a/runtime/doc/tags b/runtime/doc/tags
index 8f3fd5034..f3153904a 100644
--- a/runtime/doc/tags
+++ b/runtime/doc/tags
@@ -2710,6 +2710,8 @@ $quote    eval.txt        /*$quote*
 :insert        insert.txt      /*:insert*
 :interface     vim9class.txt   /*:interface*
 :intro starting.txt    /*:intro*
+:ip    change.txt      /*:ip*
+:iput  change.txt      /*:iput*
 :is    tagsrch.txt     /*:is*
 :isearch       tagsrch.txt     /*:isearch*
 :isp   tagsrch.txt     /*:isp*
diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt
index b27e1245d..3e5e68126 100644
--- a/runtime/doc/version9.txt
+++ b/runtime/doc/version9.txt
@@ -1,4 +1,4 @@
-*version9.txt*  For Vim version 9.1.  Last change: 2025 Mar 15
+*version9.txt*  For Vim version 9.1.  Last change: 2025 Mar 18
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -41704,6 +41704,10 @@ Highlighting: ~
 Commands: ~
 
 |[r| and |]r|          to move the cursor to previous/next rare word
+
+Ex-Commands: ~
+
+|:iput|                        like |:put| but adjust indent
 |:pbuffer|             Edit buffer [N] from the buffer list in the preview
                        window
 
diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim
index 1baf5b574..6a21c5f21 100644
--- a/runtime/syntax/vim.vim
+++ b/runtime/syntax/vim.vim
@@ -2,7 +2,7 @@
 " Language:       Vim script
 " Maintainer:     Hirohito Higashi <h.east.727 ATMARK gmail.com>
 "         Doug Kearns <dougkea...@gmail.com>
-" Last Change:    2025 Mar 17
+" Last Change:    2025 Mar 18
 " Former Maintainer: Charles E. Campbell
 
 " DO NOT CHANGE DIRECTLY.
@@ -34,11 +34,11 @@ syn cluster vimCommentGroup contains=vimTodo,@Spell
 " regular vim commands {{{2
 " GEN_SYN_VIM: vimCommand normal, START_STR='syn keyword vimCommand 
contained', END_STR='nextgroup=vimBang'
 syn keyword vimCommand contained abo[veleft] al[l] ar[gs] arga[dd] argd[elete] 
argdo argded[upe] arge[dit] argg[lobal] argl[ocal] argu[ment] as[cii] b[uffer] 
bN[ext] ba[ll] bad[d] balt bd[elete] bel[owright] bf[irst] bl[ast] bm[odified] 
bn[ext] bo[tright] bp[revious] br[ewind] brea[k] breaka[dd] breakd[el] 
breakl[ist] bro[wse] buffers bufd[o] bun[load] bw[ipeout] c[hange] cN[ext] 
cNf[ile] cabo[ve] cad[dbuffer] cadde[xpr] caddf[ile] caf[ter] cb[uffer] 
cbe[fore] cbel[ow] cbo[ttom] cc ccl[ose] cd cdo ce[nter] cex[pr] cf[ile] cfd[o] 
cfir[st] cg[etfile] cgetb[uffer] cgete[xpr] chd[ir] changes che[ckpath] 
checkt[ime] chi[story] cl[ist] cla[st] clo[se] cle[arjumps] cn[ext] cnew[er] 
cnf[ile] co[py] col[der] colo[rscheme] com[mand] comc[lear] comp[iler] 
con[tinue] conf[irm] nextgroup=vimBang
-syn keyword vimCommand contained cons[t] cope[n] cp[revious] cpf[ile] cq[uit] 
cr[ewind] cs[cope] cst[ag] cw[indow] d[elete] delm[arks] deb[ug] defc[ompile] 
defe[r] delf[unction] di[splay] dif[fupdate] diffg[et] diffo[ff] diffp[atch] 
diffpu[t] diffs[plit] difft[his] dig[raphs] disa[ssemble] dj[ump] dli[st] 
dr[op] ds[earch] dsp[lit] e[dit] ea[rlier] el[se] em[enu] en[dif] endfo[r] 
endt[ry] endw[hile] ene[w] ev[al] ex exi[t] exu[sage] f[ile] files filet[ype] 
filt[er] fin[d] fina[lly] fini[sh] fir[st] fix[del] fo[ld] foldc[lose] 
foldd[oopen] folddoc[losed] foldo[pen] g[lobal] go[to] gr[ep] grepa[dd] gu[i] 
gv[im] h[elp] helpc[lose] helpf[ind] helpg[rep] helpt[ags] ha[rdcopy] 
hi[ghlight] hid[e] his[tory] hor[izontal] ij[ump] il[ist] imp[ort] int[ro] 
is[earch] isp[lit] nextgroup=vimBang
-syn keyword vimCommand contained j[oin] ju[mps] k kee[pmarks] keepj[umps] 
keepp[atterns] keepa[lt] l[ist] lN[ext] lNf[ile] la[st] lab[ove] lan[guage] 
lad[dexpr] laddb[uffer] laddf[ile] laf[ter] lat[er] lb[uffer] lbe[fore] 
lbel[ow] lbo[ttom] lc[d] lch[dir] lcl[ose] lcs[cope] ld[o] le[ft] lefta[bove] 
lex[pr] leg[acy] lf[ile] lfd[o] lfir[st] lg[etfile] lgetb[uffer] lgete[xpr] 
lgr[ep] lgrepa[dd] lh[elpgrep] lhi[story] ll lla[st] lli[st] lmak[e] lne[xt] 
lnew[er] lnf[ile] lo[adview] loc[kmarks] lockv[ar] lol[der] lop[en] lp[revious] 
lpf[ile] lr[ewind] lt[ag] lua luad[o] luaf[ile] lv[imgrep] lvimgrepa[dd] 
lw[indow] ls m[ove] ma[rk] mak[e] marks menut[ranslate] mes[sages] mk[exrc] 
mks[ession] mksp[ell] mkv[imrc] mkvie[w] mod[e] mz[scheme] mzf[ile] n[ext] 
nb[key] nbc[lose] nextgroup=vimBang
-syn keyword vimCommand contained nbs[tart] noa[utocmd] noh[lsearch] 
nos[wapfile] nu[mber] o[pen] ol[dfiles] on[ly] opt[ions] ow[nsyntax] p[rint] 
pa[ckadd] packl[oadall] pb[uffer] pc[lose] pe[rl] perld[o] ped[it] po[p] pp[op] 
pre[serve] prev[ious] pro[mptfind] promptr[epl] prof[ile] profd[el] ps[earch] 
pt[ag] ptN[ext] ptf[irst] ptj[ump] ptl[ast] ptn[ext] ptp[revious] ptr[ewind] 
pts[elect] pu[t] pw[d] py[thon] pyd[o] pyf[ile] py3 py3d[o] python3 py3f[ile] 
pyx pyxd[o] pythonx pyxf[ile] q[uit] quita[ll] qa[ll] r[ead] rec[over] red[o] 
redi[r] redr[aw] redraws[tatus] redrawt[abline] reg[isters] res[ize] ret[ab] 
rew[ind] ri[ght] rightb[elow] ru[ntime] rub[y] rubyd[o] rubyf[ile] rund[o] 
rv[iminfo] sN[ext] sa[rgument] sal[l] san[dbox] sav[eas] sb[uffer] sbN[ext] 
sba[ll] nextgroup=vimBang
-syn keyword vimCommand contained sbf[irst] sbl[ast] sbm[odified] sbn[ext] 
sbp[revious] sbr[ewind] scr[iptnames] scripte[ncoding] scriptv[ersion] 
scs[cope] setf[iletype] sf[ind] sfir[st] sh[ell] sim[alt] sig[n] sil[ent] 
sla[st] sn[ext] so[urce] sor[t] sp[lit] spe[llgood] spelld[ump] spelli[nfo] 
spellr[epall] spellra[re] spellu[ndo] spellw[rong] spr[evious] sre[wind] st[op] 
sta[g] star[tinsert] startg[replace] startr[eplace] stopi[nsert] stj[ump] 
sts[elect] sun[hide] sus[pend] sv[iew] sw[apname] synti[me] sync[bind] smi[le] 
t tN[ext] ta[g] tags tab tabc[lose] tabd[o] tabe[dit] tabf[ind] tabfir[st] 
tabm[ove] tabl[ast] tabn[ext] tabnew tabo[nly] tabp[revious] tabN[ext] 
tabr[ewind] tabs tc[d] tch[dir] tcl tcld[o] tclf[ile] te[aroff] ter[minal] 
tf[irst] tj[ump] tl[ast] nextgroup=vimBang
-syn keyword vimCommand contained tn[ext] to[pleft] tp[revious] tr[ewind] try 
ts[elect] u[ndo] undoj[oin] undol[ist] unh[ide] unlo[ckvar] uns[ilent] up[date] 
v[global] ve[rsion] verb[ose] vert[ical] vi[sual] vie[w] vim[grep] vimgrepa[dd] 
vim9[cmd] viu[sage] vne[w] vs[plit] w[rite] wN[ext] wa[ll] wi[nsize] winc[md] 
wind[o] winp[os] wn[ext] wp[revious] wq wqa[ll] wu[ndo] wv[iminfo] x[it] xa[ll] 
xr[estore] y[ank] z dl dell delel deletl deletel dp dep delp delep deletp 
deletep a i nextgroup=vimBang
+syn keyword vimCommand contained cons[t] cope[n] cp[revious] cpf[ile] cq[uit] 
cr[ewind] cs[cope] cst[ag] cw[indow] d[elete] delm[arks] deb[ug] defc[ompile] 
defe[r] delf[unction] di[splay] dif[fupdate] diffg[et] diffo[ff] diffp[atch] 
diffpu[t] diffs[plit] difft[his] dig[raphs] disa[ssemble] dj[ump] dli[st] 
dr[op] ds[earch] dsp[lit] e[dit] ea[rlier] el[se] em[enu] en[dif] endfo[r] 
endt[ry] endw[hile] ene[w] ev[al] ex exi[t] exu[sage] f[ile] files filet[ype] 
filt[er] fin[d] fina[lly] fini[sh] fir[st] fix[del] fo[ld] foldc[lose] 
foldd[oopen] folddoc[losed] foldo[pen] g[lobal] go[to] gr[ep] grepa[dd] gu[i] 
gv[im] h[elp] helpc[lose] helpf[ind] helpg[rep] helpt[ags] ha[rdcopy] 
hi[ghlight] hid[e] his[tory] hor[izontal] ij[ump] il[ist] imp[ort] int[ro] 
ip[ut] is[earch] nextgroup=vimBang
+syn keyword vimCommand contained isp[lit] j[oin] ju[mps] k kee[pmarks] 
keepj[umps] keepp[atterns] keepa[lt] l[ist] lN[ext] lNf[ile] la[st] lab[ove] 
lan[guage] lad[dexpr] laddb[uffer] laddf[ile] laf[ter] lat[er] lb[uffer] 
lbe[fore] lbel[ow] lbo[ttom] lc[d] lch[dir] lcl[ose] lcs[cope] ld[o] le[ft] 
lefta[bove] lex[pr] leg[acy] lf[ile] lfd[o] lfir[st] lg[etfile] lgetb[uffer] 
lgete[xpr] lgr[ep] lgrepa[dd] lh[elpgrep] lhi[story] ll lla[st] lli[st] lmak[e] 
lne[xt] lnew[er] lnf[ile] lo[adview] loc[kmarks] lockv[ar] lol[der] lop[en] 
lp[revious] lpf[ile] lr[ewind] lt[ag] lua luad[o] luaf[ile] lv[imgrep] 
lvimgrepa[dd] lw[indow] ls m[ove] ma[rk] mak[e] marks menut[ranslate] 
mes[sages] mk[exrc] mks[ession] mksp[ell] mkv[imrc] mkvie[w] mod[e] mz[scheme] 
mzf[ile] n[ext] nb[key] nextgroup=vimBang
+syn keyword vimCommand contained nbc[lose] nbs[tart] noa[utocmd] noh[lsearch] 
nos[wapfile] nu[mber] o[pen] ol[dfiles] on[ly] opt[ions] ow[nsyntax] p[rint] 
pa[ckadd] packl[oadall] pb[uffer] pc[lose] pe[rl] perld[o] ped[it] po[p] pp[op] 
pre[serve] prev[ious] pro[mptfind] promptr[epl] prof[ile] profd[el] ps[earch] 
pt[ag] ptN[ext] ptf[irst] ptj[ump] ptl[ast] ptn[ext] ptp[revious] ptr[ewind] 
pts[elect] pu[t] pw[d] py[thon] pyd[o] pyf[ile] py3 py3d[o] python3 py3f[ile] 
pyx pyxd[o] pythonx pyxf[ile] q[uit] quita[ll] qa[ll] r[ead] rec[over] red[o] 
redi[r] redr[aw] redraws[tatus] redrawt[abline] reg[isters] res[ize] ret[ab] 
rew[ind] ri[ght] rightb[elow] ru[ntime] rub[y] rubyd[o] rubyf[ile] rund[o] 
rv[iminfo] sN[ext] sa[rgument] sal[l] san[dbox] sav[eas] sb[uffer] sbN[ext] 
nextgroup=vimBang
+syn keyword vimCommand contained sba[ll] sbf[irst] sbl[ast] sbm[odified] 
sbn[ext] sbp[revious] sbr[ewind] scr[iptnames] scripte[ncoding] scriptv[ersion] 
scs[cope] setf[iletype] sf[ind] sfir[st] sh[ell] sim[alt] sig[n] sil[ent] 
sla[st] sn[ext] so[urce] sor[t] sp[lit] spe[llgood] spelld[ump] spelli[nfo] 
spellr[epall] spellra[re] spellu[ndo] spellw[rong] spr[evious] sre[wind] st[op] 
sta[g] star[tinsert] startg[replace] startr[eplace] stopi[nsert] stj[ump] 
sts[elect] sun[hide] sus[pend] sv[iew] sw[apname] synti[me] sync[bind] smi[le] 
t tN[ext] ta[g] tags tab tabc[lose] tabd[o] tabe[dit] tabf[ind] tabfir[st] 
tabm[ove] tabl[ast] tabn[ext] tabnew tabo[nly] tabp[revious] tabN[ext] 
tabr[ewind] tabs tc[d] tch[dir] tcl tcld[o] tclf[ile] te[aroff] ter[minal] 
tf[irst] tj[ump] nextgroup=vimBang
+syn keyword vimCommand contained tl[ast] tn[ext] to[pleft] tp[revious] 
tr[ewind] try ts[elect] u[ndo] undoj[oin] undol[ist] unh[ide] unlo[ckvar] 
uns[ilent] up[date] v[global] ve[rsion] verb[ose] vert[ical] vi[sual] vie[w] 
vim[grep] vimgrepa[dd] vim9[cmd] viu[sage] vne[w] vs[plit] w[rite] wN[ext] 
wa[ll] wi[nsize] winc[md] wind[o] winp[os] wn[ext] wp[revious] wq wqa[ll] 
wu[ndo] wv[iminfo] x[it] xa[ll] xr[estore] y[ank] z dl dell delel deletl 
deletel dp dep delp delep deletp deletep a i nextgroup=vimBang
 
 " Lower priority for _new_ to distinguish constructors from the command.
 syn match   vimCommand contained       "\<new\>(\@!"
diff --git a/src/ex_cmdidxs.h b/src/ex_cmdidxs.h
index 0b13a6c7f..609a81d39 100644
--- a/src/ex_cmdidxs.h
+++ b/src/ex_cmdidxs.h
@@ -14,23 +14,23 @@ static const unsigned short cmdidxs1[26] =
   /* g */ 184,
   /* h */ 190,
   /* i */ 200,
-  /* j */ 220,
-  /* k */ 222,
-  /* l */ 227,
-  /* m */ 290,
-  /* n */ 308,
-  /* o */ 328,
-  /* p */ 340,
-  /* q */ 381,
-  /* r */ 384,
-  /* s */ 404,
-  /* t */ 474,
-  /* u */ 521,
-  /* v */ 532,
-  /* w */ 553,
-  /* x */ 567,
-  /* y */ 577,
-  /* z */ 578
+  /* j */ 221,
+  /* k */ 223,
+  /* l */ 228,
+  /* m */ 291,
+  /* n */ 309,
+  /* o */ 329,
+  /* p */ 341,
+  /* q */ 382,
+  /* r */ 385,
+  /* s */ 405,
+  /* t */ 475,
+  /* u */ 522,
+  /* v */ 533,
+  /* w */ 554,
+  /* x */ 568,
+  /* y */ 578,
+  /* z */ 579
 };
 
 /*
@@ -49,7 +49,7 @@ static const unsigned char cmdidxs2[26][26] =
   /* f */ {  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 10,  0,  
0,  0,  0,  0, 16,  0,  0,  0,  0,  0 },
   /* g */ {  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1,  0,  
0,  2,  0,  0,  4,  5,  0,  0,  0,  0 },
   /* h */ {  5,  0,  0,  0,  0,  0,  0,  0,  6,  0,  0,  0,  0,  0,  9,  0,  
0,  0,  0,  0,  0,  0,  0,  0,  0,  0 },
-  /* i */ {  1,  0,  0,  0,  0,  3,  0,  0,  0,  4,  0,  5,  6,  0,  0,  0,  
0,  0, 15,  0, 17,  0,  0,  0,  0,  0 },
+  /* i */ {  1,  0,  0,  0,  0,  3,  0,  0,  0,  4,  0,  5,  6,  0,  0, 15,  
0,  0, 16,  0, 18,  0,  0,  0,  0,  0 },
   /* j */ {  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  
0,  0,  0,  0,  1,  0,  0,  0,  0,  0 },
   /* k */ {  0,  0,  0,  0,  1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  
0,  0,  0,  0,  0,  0,  0,  0,  0,  0 },
   /* l */ {  3, 11, 15, 19, 20, 25, 28, 33,  0,  0,  0, 35, 38, 41, 45, 51,  
0, 53, 62, 54, 55, 59, 61,  0,  0,  0 },
@@ -69,4 +69,4 @@ static const unsigned char cmdidxs2[26][26] =
   /* z */ {  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  
0,  0,  0,  0,  0,  0,  0,  0,  0,  0 }
 };
 
-static const int command_count = 595;
+static const int command_count = 596;
diff --git a/src/ex_cmds.h b/src/ex_cmds.h
index d217cd9f7..0659f87c2 100644
--- a/src/ex_cmds.h
+++ b/src/ex_cmds.h
@@ -761,6 +761,9 @@ EXCMD(CMD_intro,    "intro",        ex_intro,
 EXCMD(CMD_interface,   "interface",    ex_class,
        EX_EXTRA|EX_CMDWIN|EX_LOCK_OK|EX_WHOLE|EX_EXPORT,
        ADDR_NONE),
+EXCMD(CMD_iput,                "iput",         ex_iput,
+       
EX_RANGE|EX_WHOLEFOLD|EX_BANG|EX_REGSTR|EX_TRLBAR|EX_ZEROR|EX_CMDWIN|EX_LOCK_OK|EX_MODIFY,
+       ADDR_LINES),
 EXCMD(CMD_isearch,     "isearch",      ex_findpat,
        EX_BANG|EX_RANGE|EX_DFLALL|EX_WHOLEFOLD|EX_EXTRA|EX_CMDWIN|EX_LOCK_OK,
        ADDR_LINES),
diff --git a/src/ex_docmd.c b/src/ex_docmd.c
index 80ec8a1a6..bda20f54c 100644
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -231,6 +231,7 @@ static void ex_winpos(exarg_T *eap);
 #endif
 static void    ex_operators(exarg_T *eap);
 static void    ex_put(exarg_T *eap);
+static void    ex_iput(exarg_T *eap);
 static void    ex_copymove(exarg_T *eap);
 static void    ex_submagic(exarg_T *eap);
 static void    ex_join(exarg_T *eap);
@@ -2372,8 +2373,8 @@ do_one_cmd(
            goto doend;
        }
 #endif
-       if (valid_yank_reg(*ea.arg, (ea.cmdidx != CMD_put
-                                             && !IS_USER_CMDIDX(ea.cmdidx))))
+       if (valid_yank_reg(*ea.arg, (!IS_USER_CMDIDX(ea.cmdidx)
+                           && ea.cmdidx != CMD_put && ea.cmdidx != CMD_iput)))
        {
            ea.regname = *ea.arg++;
 #ifdef FEAT_EVAL
@@ -8520,6 +8521,25 @@ ex_put(exarg_T *eap)
                                                       PUT_LINE|PUT_CURSLINE);
 }
 
+/*
+ * ":iput".
+ */
+    static void
+ex_iput(exarg_T *eap)
+{
+    // ":0iput" works like ":1iput!".
+    if (eap->line2 == 0)
+    {
+       eap->line2 = 1;
+       eap->forceit = TRUE;
+    }
+    curwin->w_cursor.lnum = eap->line2;
+    check_cursor_col();
+    do_put(eap->regname, NULL, eap->forceit ? BACKWARD : FORWARD, 1L,
+                                                     PUT_LINE|PUT_CURSLINE
+                                                     |PUT_FIXINDENT);
+}
+
 /*
  * Handle ":copy" and ":move".
  */
diff --git a/src/proto/vim9cmds.pro b/src/proto/vim9cmds.pro
index bd2b5c248..12f0560ac 100644
--- a/src/proto/vim9cmds.pro
+++ b/src/proto/vim9cmds.pro
@@ -26,7 +26,7 @@ char_u *compile_eval(char_u *arg, cctx_T *cctx);
 int get_defer_var_idx(cctx_T *cctx);
 char_u *compile_defer(char_u *arg_start, cctx_T *cctx);
 char_u *compile_mult_expr(char_u *arg, int cmdidx, long cmd_count, cctx_T 
*cctx);
-char_u *compile_put(char_u *arg, exarg_T *eap, cctx_T *cctx);
+char_u *compile_put(char_u *arg, exarg_T *eap, cctx_T *cctx, int fixindent);
 char_u *compile_exec(char_u *line_arg, exarg_T *eap, cctx_T *cctx);
 char_u *compile_script(char_u *line, cctx_T *cctx);
 char_u *compile_substitute(char_u *arg, exarg_T *eap, cctx_T *cctx);
diff --git a/src/proto/vim9instr.pro b/src/proto/vim9instr.pro
index 3fcf08c09..641648f4e 100644
--- a/src/proto/vim9instr.pro
+++ b/src/proto/vim9instr.pro
@@ -68,7 +68,7 @@ int generate_ECHO(cctx_T *cctx, int with_white, int count);
 int generate_MULT_EXPR(cctx_T *cctx, isntype_T isn_type, int count);
 int generate_ECHOWINDOW(cctx_T *cctx, int count, long time);
 int generate_SOURCE(cctx_T *cctx, int sid);
-int generate_PUT(cctx_T *cctx, int regname, linenr_T lnum);
+int generate_PUT(cctx_T *cctx, int regname, linenr_T lnum, int fixindent);
 int generate_LOCKUNLOCK(cctx_T *cctx, char_u *line, int is_arg);
 int generate_EXEC_copy(cctx_T *cctx, isntype_T isntype, char_u *line);
 int generate_EXEC(cctx_T *cctx, isntype_T isntype, char_u *str);
diff --git a/src/testdir/test_put.vim b/src/testdir/test_put.vim
index 2af31db3d..26eb7f0eb 100644
--- a/src/testdir/test_put.vim
+++ b/src/testdir/test_put.vim
@@ -75,6 +75,8 @@ func Test_put_fails_when_nomodifiable()
   normal! yy
   call assert_fails(':put', 'E21:')
   call assert_fails(':put!', 'E21:')
+  call assert_fails(':iput', 'E21:')
+  call assert_fails(':iput!', 'E21:')
   call assert_fails(':normal! p', 'E21:')
   call assert_fails(':normal! gp', 'E21:')
   call assert_fails(':normal! P', 'E21:')
@@ -132,7 +134,7 @@ func Test_put_visual_delete_all_lines()
   let @r = ''
   normal! VG"rgp
   call assert_equal(1, line('$'))
-  close!
+  bw!
 endfunc
 
 func Test_gp_with_count_leaves_cursor_at_end()
@@ -328,6 +330,82 @@ func Test_put_list()
   bw!
 endfunc
 
+func Test_iput_multiline()
+  new
+  setlocal noexpandtab
+  call setline(1, "\<Tab>foo")
+  call setreg('0', "bar
\<Tab>bar2
bar3", 'l')
+  iput
+  call assert_equal(["\<Tab>bar", "\<Tab>\<Tab>bar2", "\<Tab>bar3"], 
getline(2, 4))
+  setlocal expandtab tabstop=8 shiftwidth=8 noshiftround
+  iput
+  call assert_equal([repeat(' ', 8) . "bar",
+        \ repeat(' ', 16) . "bar2",
+        \ repeat(' ', 8) . "bar3"], getline(5, 7))
+  bw!
+endfunc
+
+func Test_iput_beforeafter_tab()
+  new
+  setlocal noexpandtab
+  call setline(1, "\<Tab>foo")
+  call setreg('0', "bar", 'l')
+  iput
+  call assert_equal(["\<Tab>bar"], getline(2, '$'))
+  call feedkeys("k", 'x')
+  iput!
+  call assert_equal("\<Tab>bar", getline(1))
+  call assert_equal("\<Tab>bar", getline(3))
+  bw!
+endfunc
+
+func Test_iput_beforeafter_expandtab()
+  new
+  setlocal expandtab tabstop=8 shiftwidth=8 noshiftround
+  call setline(1, "\<Tab>foo")
+  call setreg('0', "bar", 'l')
+  iput
+  call assert_equal([repeat(' ', 8) . "bar"], getline(2, '$'))
+  :1iput!
+  call assert_equal(repeat(' ', 8) . "bar", getline(1))
+  bw!
+endfunc
+
+func Test_iput_invalidrange()
+  new
+  call setreg('0', "bar", 'l')
+  call assert_fails(':10iput', 'E16:')
+  bw!
+endfunc
+
+func Test_iput_zero_range()
+  new
+  let _var = [getreg('a'), getregtype('a')]
+  call setreg('a', 'foobar', 'l')
+  call setline(1, range(1, 2))
+  call cursor(1, 1)
+  0iput a
+  call assert_equal(['foobar', '1', '2'], getline(1, '$'))
+  %d
+  call setline(1, range(1, 2))
+  call cursor(1, 1)
+  0iput! a
+  call assert_equal(['foobar', '1', '2'], getline(1, '$'))
+  call setreg('a', _var[0], _var[1])
+  bw!
+endfunc
+
+func Test_iput_not_put()
+  new
+  call setline(1, "\<Tab>foo")
+  call setreg('0', "bar", 'l')
+  iput
+  call assert_equal("\<Tab>bar", getline(2))
+  put
+  call assert_equal("bar", getline(3))
+  bw!
+endfunc
+
 " Test pasting the '.' register
 func Test_put_inserted()
   new
diff --git a/src/testdir/test_vim9_cmd.vim b/src/testdir/test_vim9_cmd.vim
index 900bf8ae3..26a3f09a1 100644
--- a/src/testdir/test_vim9_cmd.vim
+++ b/src/testdir/test_vim9_cmd.vim
@@ -1350,6 +1350,59 @@ def Test_put_with_linebreak()
   bwipe!
 enddef
 
+def Test_iput()
+  new
+  set noexpandtab
+
+  call feedkeys("i\<Tab>foo", 'x')
+
+  @p = "ppp"
+  iput p
+  call assert_equal("\<Tab>ppp", getline(2))
+
+  iput ="below"
+  assert_equal("\<Tab>below", getline(3))
+  iput! ="above"
+  assert_equal("\<Tab>above", getline(3))
+  assert_equal("\<Tab>below", getline(4))
+
+  :2iput =['a', 'b', 'c']
+  assert_equal(["\<Tab>ppp", "\<Tab>a", "\<Tab>b", "\<Tab>c", "\<Tab>above"], 
getline(2, 6))
+
+  :0iput =  "\<Tab>\<Tab>first"
+  assert_equal("\<Tab>first", getline(1))
+  :1iput! ="first again"
+  assert_equal("\<Tab>first again", getline(1))
+
+  bw!
+  v9.CheckDefFailure(['iput =xxx'], 'E1001:')
+enddef
+
+def Test_iput_with_linebreak()
+  new
+  var lines =<< trim END
+    vim9script
+    ip =split('abc', '\zs')
+            ->join()
+  END
+  v9.CheckScriptSuccess(lines)
+  getline(2)->assert_equal('a b c')
+  bwipe!
+enddef
+
+def Test_iput_not_put()
+  new
+  call feedkeys("ggS\<Tab>foo", 'x')
+  @a = "putting"
+  :0iput a
+  assert_equal("\<Tab>putting", getline(1))
+  put a
+  assert_equal("putting", getline(2))
+  iput a
+  assert_equal("putting", getline(3))
+  bwipe!
+enddef
+
 def Test_command_star_range()
   new
   setline(1, ['xxx foo xxx', 'xxx bar xxx', 'xxx foo xx bar'])
diff --git a/src/testdir/test_vim9_disassemble.vim 
b/src/testdir/test_vim9_disassemble.vim
index 7a41b5d52..27b71feb2 100644
--- a/src/testdir/test_vim9_disassemble.vim
+++ b/src/testdir/test_vim9_disassemble.vim
@@ -286,6 +286,20 @@ def Test_disassemble_put_expr()
         res)
 enddef
 
+def s:IputExpr()
+  :3iput ="text"
+enddef
+
+def Test_disassemble_iput_expr()
+  var res = execute('disass s:IputExpr')
+  assert_match('<SNR>\d*_IputExpr.*' ..
+        ' :3iput ="text"\_s*' ..
+        '\d PUSHS "text"\_s*' ..
+        '\d IPUT = 3\_s*' ..
+        '\d RETURN void',
+        res)
+enddef
+
 def s:PutRange()
   :$-2put a
   :$-3put! b
@@ -305,6 +319,25 @@ def Test_disassemble_put_range()
         res)
 enddef
 
+def s:IputRange()
+  :$-2iput a
+  :$-3iput! b
+enddef
+
+def Test_disassemble_iput_range()
+  var res = execute('disass s:IputRange')
+  assert_match('<SNR>\d*_IputRange.*' ..
+        ' :$-2iput a\_s*' ..
+        '\d RANGE $-2\_s*' ..
+        '\d IPUT a range\_s*' ..
+
+        ' :$-3iput! b\_s*' ..
+        '\d RANGE $-3\_s*' ..
+        '\d IPUT b above range\_s*' ..
+        '\d RETURN void',
+        res)
+enddef
+
 def s:ScriptFuncPush()
   var localbool = true
   var localspec = v:none
diff --git a/src/version.c b/src/version.c
index b4f631248..02f30abf3 100644
--- a/src/version.c
+++ b/src/version.c
@@ -704,6 +704,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1224,
 /**/
     1223,
 /**/
diff --git a/src/vim9.h b/src/vim9.h
index 63d7116fd..7c731fa60 100644
--- a/src/vim9.h
+++ b/src/vim9.h
@@ -200,6 +200,7 @@ typedef enum {
     ISN_USEDICT,    // use or clear dict saved by ISN_MEMBER/ISN_STRINGMEMBER
 
     ISN_PUT,       // ":put", uses isn_arg.put
+    ISN_IPUT,      // ":iput", uses isn_arg.put
 
     ISN_CMDMOD,            // set cmdmod
     ISN_CMDMOD_REV, // undo ISN_CMDMOD
diff --git a/src/vim9cmds.c b/src/vim9cmds.c
index f8ebfb128..aeb742e57 100644
--- a/src/vim9cmds.c
+++ b/src/vim9cmds.c
@@ -2161,9 +2161,12 @@ compile_variable_range(exarg_T *eap, cctx_T *cctx)
 /*
  * :put r
  * :put ={expr}
+ * or if fixindent == TRUE
+ * :iput r
+ * :iput ={expr}
  */
     char_u *
-compile_put(char_u *arg, exarg_T *eap, cctx_T *cctx)
+compile_put(char_u *arg, exarg_T *eap, cctx_T *cctx, int fixindent)
 {
     char_u     *line = arg;
     linenr_T   lnum;
@@ -2202,7 +2205,8 @@ compile_put(char_u *arg, exarg_T *eap, cctx_T *cctx)
            --lnum;
     }
 
-    generate_PUT(cctx, eap->regname, lnum);
+    generate_PUT(cctx, eap->regname, lnum, fixindent);
+
     return line;
 }
 
diff --git a/src/vim9compile.c b/src/vim9compile.c
index 9d0bf71de..cb7b94822 100644
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -4595,7 +4595,12 @@ compile_def_function_body(
 
            case CMD_put:
                    ea.cmd = cmd;
-                   line = compile_put(p, &ea, cctx);
+                   line = compile_put(p, &ea, cctx, FALSE);
+                   break;
+
+           case CMD_iput:
+                   ea.cmd = cmd;
+                   line = compile_put(p, &ea, cctx, TRUE);
                    break;
 
            case CMD_substitute:
diff --git a/src/vim9execute.c b/src/vim9execute.c
index 4b69dfafa..c0c3103d4 100644
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -3252,6 +3252,58 @@ var_any_get_oc_member(class_T *current_class, isn_T 
*iptr, typval_T *tv)
     return OK;
 }
 
+/*
+ * do ISN_PUT or ISN_IPUT instruction depending on fixindent parameter
+ */
+    static void
+isn_put_do(ectx_T *ectx, isn_T *iptr, typval_T *tv, int fixindent)
+{
+    int                regname = iptr->isn_arg.put.put_regname;
+    linenr_T   lnum = iptr->isn_arg.put.put_lnum;
+    char_u     *expr = NULL;
+    int                dir = FORWARD;
+
+    if (lnum < -2)
+    {
+       // line number was put on the stack by ISN_RANGE
+       tv = STACK_TV_BOT(-1);
+       curwin->w_cursor.lnum = tv->vval.v_number;
+       if (lnum == LNUM_VARIABLE_RANGE_ABOVE)
+           dir = BACKWARD;
+       --ectx->ec_stack.ga_len;
+    }
+    else if (lnum == -2)
+       // :put! above cursor
+       dir = BACKWARD;
+    else if (lnum >= 0)
+    {
+       curwin->w_cursor.lnum = lnum;
+       if (lnum == 0)
+           // check_cursor() below will move to line 1
+           dir = BACKWARD;
+    }
+
+    if (regname == '=')
+    {
+       tv = STACK_TV_BOT(-1);
+       if (tv->v_type == VAR_STRING)
+           expr = tv->vval.v_string;
+       else
+       {
+           expr = typval2string(tv, TRUE); // allocates value
+           clear_tv(tv);
+       }
+       --ectx->ec_stack.ga_len;
+    }
+    check_cursor();
+
+    if (fixindent)
+       do_put(regname, expr, dir, 1L, PUT_LINE|PUT_CURSLINE|PUT_FIXINDENT);
+    else
+       do_put(regname, expr, dir, 1L, PUT_LINE|PUT_CURSLINE);
+    vim_free(expr);
+}
+
 /*
  * Execute instructions in execution context "ectx".
  * Return OK or FAIL;
@@ -5948,48 +6000,12 @@ exec_instructions(ectx_T *ectx)
                break;
 
            case ISN_PUT:
-               {
-                   int         regname = iptr->isn_arg.put.put_regname;
-                   linenr_T    lnum = iptr->isn_arg.put.put_lnum;
-                   char_u      *expr = NULL;
-                   int         dir = FORWARD;
-
-                   if (lnum < -2)
-                   {
-                       // line number was put on the stack by ISN_RANGE
-                       tv = STACK_TV_BOT(-1);
-                       curwin->w_cursor.lnum = tv->vval.v_number;
-                       if (lnum == LNUM_VARIABLE_RANGE_ABOVE)
-                           dir = BACKWARD;
-                       --ectx->ec_stack.ga_len;
-                   }
-                   else if (lnum == -2)
-                       // :put! above cursor
-                       dir = BACKWARD;
-                   else if (lnum >= 0)
-                   {
-                       curwin->w_cursor.lnum = lnum;
-                       if (lnum == 0)
-                           // check_cursor() below will move to line 1
-                           dir = BACKWARD;
-                   }
-
-                   if (regname == '=')
-                   {
-                       tv = STACK_TV_BOT(-1);
-                       if (tv->v_type == VAR_STRING)
-                           expr = tv->vval.v_string;
-                       else
-                       {
-                           expr = typval2string(tv, TRUE); // allocates value
-                           clear_tv(tv);
-                       }
-                       --ectx->ec_stack.ga_len;
-                   }
-                   check_cursor();
-                   do_put(regname, expr, dir, 1L, PUT_LINE|PUT_CURSLINE);
-                   vim_free(expr);
-               }
+               tv = NULL; // initialize it so we don't get a warning
+               isn_put_do(ectx, iptr, tv, FALSE);
+               break;
+           case ISN_IPUT:
+               tv = NULL;
+               isn_put_do(ectx, iptr, tv, TRUE);
                break;
 
            case ISN_CMDMOD:
@@ -7619,7 +7635,18 @@ list_instructions(char *pfx, isn_T *instr, int 
instr_count, ufunc_T *ufunc)
                                                 iptr->isn_arg.put.put_regname,
                                             (long)iptr->isn_arg.put.put_lnum);
                break;
-
+           case ISN_IPUT:
+               if (iptr->isn_arg.put.put_lnum == LNUM_VARIABLE_RANGE_ABOVE)
+                   smsg("%s%4d IPUT %c above range",
+                                 pfx, current, iptr->isn_arg.put.put_regname);
+               else if (iptr->isn_arg.put.put_lnum == LNUM_VARIABLE_RANGE)
+                   smsg("%s%4d IPUT %c range",
+                                 pfx, current, iptr->isn_arg.put.put_regname);
+               else
+                   smsg("%s%4d IPUT %c %ld", pfx, current,
+                                                iptr->isn_arg.put.put_regname,
+                                            (long)iptr->isn_arg.put.put_lnum);
+               break;
            case ISN_CMDMOD:
                {
                    char_u  *buf;
@@ -7846,5 +7873,4 @@ check_not_string(typval_T *tv)
     return OK;
 }
 
-
 #endif // FEAT_EVAL
diff --git a/src/vim9instr.c b/src/vim9instr.c
index 86fddd9f6..3da56bf97 100644
--- a/src/vim9instr.c
+++ b/src/vim9instr.c
@@ -2188,15 +2188,17 @@ generate_SOURCE(cctx_T *cctx, int sid)
 }
 
 /*
- * Generate an ISN_PUT instruction.
+ * Generate an ISN_PUT or ISN_IPUT instruction depending on fixindent.
  */
     int
-generate_PUT(cctx_T *cctx, int regname, linenr_T lnum)
+generate_PUT(cctx_T *cctx, int regname, linenr_T lnum, int fixindent)
 {
     isn_T      *isn;
 
     RETURN_OK_IF_SKIP(cctx);
-    if ((isn = generate_instr(cctx, ISN_PUT)) == NULL)
+    isn = (fixindent) ? generate_instr(cctx, ISN_IPUT) :
+                       generate_instr(cctx, ISN_PUT);
+    if (isn == NULL)
        return FAIL;
     isn->isn_arg.put.put_regname = regname;
     isn->isn_arg.put.put_lnum = lnum;
@@ -2814,6 +2816,7 @@ delete_instr(isn_T *isn)
        case ISN_PUSHOBJ:
        case ISN_PUSHSPEC:
        case ISN_PUT:
+       case ISN_IPUT:
        case ISN_REDIREND:
        case ISN_REDIRSTART:
        case ISN_RETURN:

-- 
-- 
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 vim_dev+unsubscr...@googlegroups.com.
To view this discussion visit 
https://groups.google.com/d/msgid/vim_dev/E1tuekB-001IB8-UW%40256bit.org.

Raspunde prin e-mail lui