Merge pull request #14761 from janlazo/vim-8.2.2966

vim-patch:8.2.{1702,2422,2966,2971,2974}
This commit is contained in:
Jan Edmund Lazo 2021-06-11 21:07:47 -04:00 committed by GitHub
commit d09b8ad715
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 195 additions and 37 deletions

View File

@ -1007,6 +1007,10 @@ inside of strings can change! Also see 'softtabstop' option. >
cursor to the end of line (which is more logical, cursor to the end of line (which is more logical,
but not Vi-compatible) use ":map Y y$". but not Vi-compatible) use ":map Y y$".
*zy*
["x]zy{motion} Yank {motion} text [into register x]. Only differs
from `y` when selecting a block of text, see |v_zy|.
*v_y* *v_y*
{Visual}["x]y Yank the highlighted text [into register x] (for {Visual}["x]y Yank the highlighted text [into register x] (for
{Visual} see |Visual-mode|). {Visual} see |Visual-mode|).
@ -1015,6 +1019,12 @@ inside of strings can change! Also see 'softtabstop' option. >
{Visual}["x]Y Yank the highlighted lines [into register x] (for {Visual}["x]Y Yank the highlighted lines [into register x] (for
{Visual} see |Visual-mode|). {Visual} see |Visual-mode|).
*v_zy*
{Visual}["x]zy Yank the highlighted text [into register x]. Trailing
whitespace at the end of each line of a selected block
won't be yanked. Especially useful in combination
with `zp`. (for {Visual} see |Visual-mode|)
*:y* *:yank* *E850* *:y* *:yank* *E850*
:[range]y[ank] [x] Yank [range] lines [into register x]. Yanking to the :[range]y[ank] [x] Yank [range] lines [into register x]. Yanking to the
"* or "+ registers is possible only when the "* or "+ registers is possible only when the
@ -1094,7 +1104,8 @@ inside of strings can change! Also see 'softtabstop' option. >
["x]zp or *zp* *zP* ["x]zp or *zp* *zP*
["x]zP Like "p" and "P", except without adding trailing spaces ["x]zP Like "p" and "P", except without adding trailing spaces
when pasting a block. Thus the inserted text will not when pasting a block. Thus the inserted text will not
always be a rectangle. always be a rectangle. Especially useful in
combination with |v_zy|.
You can use these commands to copy text from one place to another. Do this You can use these commands to copy text from one place to another. Do this
by first getting the text into a register with a yank, delete or change by first getting the text into a register with a yank, delete or change

View File

@ -865,6 +865,7 @@ tag char note action in Normal mode ~
|zv| zv open enough folds to view the cursor line |zv| zv open enough folds to view the cursor line
|zw| zw permanently mark word as incorrectly spelled |zw| zw permanently mark word as incorrectly spelled
|zx| zx re-apply 'foldlevel' and do "zv" |zx| zx re-apply 'foldlevel' and do "zv"
|zy| zy yank without trailing spaces
|zz| zz redraw, cursor line at center of window |zz| zz redraw, cursor line at center of window
|z<Left>| z<Left> same as "zh" |z<Left>| z<Left> same as "zh"
|z<Right>| z<Right> same as "zl" |z<Right>| z<Right> same as "zl"

View File

@ -2227,8 +2227,9 @@ static linenr_T foldUpdateIEMSRecurse(
if (getlevel == foldlevelMarker && flp->start <= flp->lvl - level if (getlevel == foldlevelMarker && flp->start <= flp->lvl - level
&& flp->lvl > 0) { && flp->lvl > 0) {
(void)foldFind(gap, startlnum - 1, &fp); (void)foldFind(gap, startlnum - 1, &fp);
if (fp >= ((fold_T *)gap->ga_data) + gap->ga_len if (fp != NULL
|| fp->fd_top >= startlnum) { && (fp >= ((fold_T *)gap->ga_data) + gap->ga_len
|| fp->fd_top >= startlnum)) {
fp = NULL; fp = NULL;
} }
} }

View File

@ -1207,6 +1207,7 @@ void ml_recover(bool checkext)
&& !(curbuf->b_ml.ml_flags & ML_EMPTY)) && !(curbuf->b_ml.ml_flags & ML_EMPTY))
ml_delete(curbuf->b_ml.ml_line_count, false); ml_delete(curbuf->b_ml.ml_line_count, false);
curbuf->b_flags |= BF_RECOVERED; curbuf->b_flags |= BF_RECOVERED;
check_cursor();
recoverymode = FALSE; recoverymode = FALSE;
if (got_int) if (got_int)

View File

@ -818,7 +818,7 @@ static bool normal_get_command_count(NormalState *s)
} }
if (s->ca.count0 < 0) { if (s->ca.count0 < 0) {
// got too large! // overflow
s->ca.count0 = 999999999L; s->ca.count0 = 999999999L;
} }
@ -1025,10 +1025,14 @@ static int normal_execute(VimState *state, int key)
// If you give a count before AND after the operator, they are // If you give a count before AND after the operator, they are
// multiplied. // multiplied.
if (s->ca.count0) { if (s->ca.count0) {
s->ca.count0 *= s->ca.opcount; s->ca.count0 = (long)((uint64_t)s->ca.count0 * (uint64_t)s->ca.opcount);
} else { } else {
s->ca.count0 = s->ca.opcount; s->ca.count0 = s->ca.opcount;
} }
if (s->ca.count0 < 0) {
// overflow
s->ca.count0 = 999999999L;
}
} }
// Always remember the count. It will be set to zero (on the next call, // Always remember the count. It will be set to zero (on the next call,
@ -1866,6 +1870,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
} }
} else { } else {
curwin->w_p_lbr = lbr_saved; curwin->w_p_lbr = lbr_saved;
oap->excl_tr_ws = cap->cmdchar == 'z';
(void)op_yank(oap, !gui_yank, false); (void)op_yank(oap, !gui_yank, false);
} }
check_cursor_col(); check_cursor_col();
@ -4389,6 +4394,9 @@ dozet:
case 'p': case 'p':
nv_put(cap); nv_put(cap);
break; break;
// "zy" Yank without trailing spaces
case 'y': nv_operator(cap);
break;
/* "zF": create fold command */ /* "zF": create fold command */
/* "zf": create fold operator */ /* "zf": create fold operator */
@ -5817,6 +5825,9 @@ static void nv_percent(cmdarg_T *cap)
curwin->w_cursor.lnum = (curbuf->b_ml.ml_line_count * curwin->w_cursor.lnum = (curbuf->b_ml.ml_line_count *
cap->count0 + 99L) / 100L; cap->count0 + 99L) / 100L;
} }
if (curwin->w_cursor.lnum < 1) {
curwin->w_cursor.lnum = 1;
}
if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) { if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) {
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
} }

View File

@ -48,6 +48,8 @@ typedef struct oparg_S {
colnr_T end_vcol; // end col for block mode operator colnr_T end_vcol; // end col for block mode operator
long prev_opcount; // ca.opcount saved for K_EVENT long prev_opcount; // ca.opcount saved for K_EVENT
long prev_count0; // ca.count0 saved for K_EVENT long prev_count0; // ca.count0 saved for K_EVENT
bool excl_tr_ws; // exclude trailing whitespace for yank of a
// block
} oparg_T; } oparg_T;
/* /*

View File

@ -156,6 +156,9 @@ int get_op_type(int char1, int char2)
// subtract // subtract
return OP_NR_SUB; return OP_NR_SUB;
} }
if (char1 == 'z' && char2 == 'y') { // OP_YANK
return OP_YANK;
}
for (i = 0;; i++) { for (i = 0;; i++) {
if (opchars[i][0] == char1 && opchars[i][1] == char2) { if (opchars[i][0] == char1 && opchars[i][1] == char2) {
break; break;
@ -2563,7 +2566,7 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append)
switch (reg->y_type) { switch (reg->y_type) {
case kMTBlockWise: case kMTBlockWise:
block_prep(oap, &bd, lnum, false); block_prep(oap, &bd, lnum, false);
yank_copy_line(reg, &bd, y_idx); yank_copy_line(reg, &bd, y_idx, oap->excl_tr_ws);
break; break;
case kMTLineWise: case kMTLineWise:
@ -2627,7 +2630,7 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append)
bd.textlen = endcol - startcol + oap->inclusive; bd.textlen = endcol - startcol + oap->inclusive;
} }
bd.textstart = p + startcol; bd.textstart = p + startcol;
yank_copy_line(reg, &bd, y_idx); yank_copy_line(reg, &bd, y_idx, false);
break; break;
} }
// NOTREACHED // NOTREACHED
@ -2714,7 +2717,11 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append)
return; return;
} }
static void yank_copy_line(yankreg_T *reg, struct block_def *bd, size_t y_idx) // Copy a block range into a register.
// If "exclude_trailing_space" is set, do not copy trailing whitespaces.
static void yank_copy_line(yankreg_T *reg, const struct block_def *bd,
size_t y_idx, bool exclude_trailing_space)
FUNC_ATTR_NONNULL_ALL
{ {
int size = bd->startspaces + bd->endspaces + bd->textlen; int size = bd->startspaces + bd->endspaces + bd->textlen;
assert(size >= 0); assert(size >= 0);
@ -2726,6 +2733,14 @@ static void yank_copy_line(yankreg_T *reg, struct block_def *bd, size_t y_idx)
pnew += bd->textlen; pnew += bd->textlen;
memset(pnew, ' ', (size_t)bd->endspaces); memset(pnew, ' ', (size_t)bd->endspaces);
pnew += bd->endspaces; pnew += bd->endspaces;
if (exclude_trailing_space) {
int s = bd->textlen + bd->endspaces;
while (ascii_iswhite(*(bd->textstart + s - 1)) && s > 0) {
s = s - utf_head_off(bd->textstart, bd->textstart + s - 1) - 1;
pnew--;
}
}
*pnew = NUL; *pnew = NUL;
} }

View File

@ -441,7 +441,8 @@ size_t spell_check(
MB_PTR_ADV(mi.mi_fend); MB_PTR_ADV(mi.mi_fend);
} }
(void)spell_casefold(ptr, (int)(mi.mi_fend - ptr), mi.mi_fword, MAXWLEN + 1); (void)spell_casefold(wp, ptr, (int)(mi.mi_fend - ptr), mi.mi_fword,
MAXWLEN + 1);
mi.mi_fwordlen = (int)STRLEN(mi.mi_fword); mi.mi_fwordlen = (int)STRLEN(mi.mi_fword);
if (camel_case) { if (camel_case) {
@ -869,10 +870,11 @@ static void find_word(matchinf_T *mip, int mode)
if (slang->sl_compsylmax < MAXWLEN) { if (slang->sl_compsylmax < MAXWLEN) {
// "fword" is only needed for checking syllables. // "fword" is only needed for checking syllables.
if (ptr == mip->mi_word) if (ptr == mip->mi_word) {
(void)spell_casefold(ptr, wlen, fword, MAXWLEN); (void)spell_casefold(mip->mi_win, ptr, wlen, fword, MAXWLEN);
else } else {
STRLCPY(fword, ptr, endlen[endidxcnt] + 1); STRLCPY(fword, ptr, endlen[endidxcnt] + 1);
}
} }
if (!can_compound(slang, fword, mip->mi_compflags)) if (!can_compound(slang, fword, mip->mi_compflags))
continue; continue;
@ -1315,9 +1317,9 @@ static int fold_more(matchinf_T *mip)
MB_PTR_ADV(mip->mi_fend); MB_PTR_ADV(mip->mi_fend);
} }
(void)spell_casefold(p, (int)(mip->mi_fend - p), (void)spell_casefold(mip->mi_win, p, (int)(mip->mi_fend - p),
mip->mi_fword + mip->mi_fwordlen, mip->mi_fword + mip->mi_fwordlen,
MAXWLEN - mip->mi_fwordlen); MAXWLEN - mip->mi_fwordlen);
flen = (int)STRLEN(mip->mi_fword + mip->mi_fwordlen); flen = (int)STRLEN(mip->mi_fword + mip->mi_fwordlen);
mip->mi_fwordlen += flen; mip->mi_fwordlen += flen;
return flen; return flen;
@ -2655,7 +2657,9 @@ static bool spell_iswordp_w(const int *p, const win_T *wp)
// Uses the character definitions from the .spl file. // Uses the character definitions from the .spl file.
// When using a multi-byte 'encoding' the length may change! // When using a multi-byte 'encoding' the length may change!
// Returns FAIL when something wrong. // Returns FAIL when something wrong.
int spell_casefold(char_u *str, int len, char_u *buf, int buflen) int spell_casefold(const win_T *wp, char_u *str, int len, char_u *buf,
int buflen)
FUNC_ATTR_NONNULL_ALL
{ {
if (len >= buflen) { if (len >= buflen) {
buf[0] = NUL; buf[0] = NUL;
@ -2670,8 +2674,22 @@ int spell_casefold(char_u *str, int len, char_u *buf, int buflen)
buf[outi] = NUL; buf[outi] = NUL;
return FAIL; return FAIL;
} }
const int c = mb_cptr2char_adv((const char_u **)&p); int c = mb_cptr2char_adv((const char_u **)&p);
outi += utf_char2bytes(SPELL_TOFOLD(c), buf + outi);
// Exception: greek capital sigma 0x03A3 folds to 0x03C3, except
// when it is the last character in a word, then it folds to
// 0x03C2.
if (c == 0x03a3 || c == 0x03c2) {
if (p == str + len || !spell_iswordp(p, wp)) {
c = 0x03c2;
} else {
c = 0x03c3;
}
} else {
c = SPELL_TOFOLD(c);
}
outi += utf_char2bytes(c, buf + outi);
} }
buf[outi] = NUL; buf[outi] = NUL;
@ -3155,7 +3173,8 @@ spell_find_suggest (
if (su->su_badlen >= MAXWLEN) if (su->su_badlen >= MAXWLEN)
su->su_badlen = MAXWLEN - 1; // just in case su->su_badlen = MAXWLEN - 1; // just in case
STRLCPY(su->su_badword, su->su_badptr, su->su_badlen + 1); STRLCPY(su->su_badword, su->su_badptr, su->su_badlen + 1);
(void)spell_casefold(su->su_badptr, su->su_badlen, su->su_fbadword, MAXWLEN); (void)spell_casefold(curwin, su->su_badptr, su->su_badlen, su->su_fbadword,
MAXWLEN);
// TODO(vim): make this work if the case-folded text is longer than the // TODO(vim): make this work if the case-folded text is longer than the
// original text. Currently an illegal byte causes wrong pointer // original text. Currently an illegal byte causes wrong pointer
@ -3535,7 +3554,7 @@ static void suggest_try_change(suginfo_T *su)
STRCPY(fword, su->su_fbadword); STRCPY(fword, su->su_fbadword);
n = (int)STRLEN(fword); n = (int)STRLEN(fword);
p = su->su_badptr + su->su_badlen; p = su->su_badptr + su->su_badlen;
(void)spell_casefold(p, (int)STRLEN(p), fword + n, MAXWLEN - n); (void)spell_casefold(curwin, p, (int)STRLEN(p), fword + n, MAXWLEN - n);
for (int lpi = 0; lpi < curwin->w_s->b_langp.ga_len; ++lpi) { for (int lpi = 0; lpi < curwin->w_s->b_langp.ga_len; ++lpi) {
lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi); lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi);
@ -5087,7 +5106,7 @@ stp_sal_score (
pbad = badsound; pbad = badsound;
else { else {
// soundfold the bad word with more characters following // soundfold the bad word with more characters following
(void)spell_casefold(su->su_badptr, stp->st_orglen, fword, MAXWLEN); (void)spell_casefold(curwin, su->su_badptr, stp->st_orglen, fword, MAXWLEN);
// When joining two words the sound often changes a lot. E.g., "t he" // When joining two words the sound often changes a lot. E.g., "t he"
// sounds like "t h" while "the" sounds like "@". Avoid that by // sounds like "t h" while "the" sounds like "@". Avoid that by
@ -5800,10 +5819,10 @@ void spell_soundfold(slang_T *slang, char_u *inword, bool folded, char_u *res)
spell_soundfold_sofo(slang, inword, res); spell_soundfold_sofo(slang, inword, res);
else { else {
// SAL items used. Requires the word to be case-folded. // SAL items used. Requires the word to be case-folded.
if (folded) if (folded) {
word = inword; word = inword;
else { } else {
(void)spell_casefold(inword, (int)STRLEN(inword), fword, MAXWLEN); (void)spell_casefold(curwin, inword, (int)STRLEN(inword), fword, MAXWLEN);
word = fword; word = fword;
} }

View File

@ -2942,9 +2942,9 @@ static void add_fromto(spellinfo_T *spin, garray_T *gap, char_u *from, char_u *t
char_u word[MAXWLEN]; char_u word[MAXWLEN];
fromto_T *ftp = GA_APPEND_VIA_PTR(fromto_T, gap); fromto_T *ftp = GA_APPEND_VIA_PTR(fromto_T, gap);
(void)spell_casefold(from, (int)STRLEN(from), word, MAXWLEN); (void)spell_casefold(curwin, from, (int)STRLEN(from), word, MAXWLEN);
ftp->ft_from = getroom_save(spin, word); ftp->ft_from = getroom_save(spin, word);
(void)spell_casefold(to, (int)STRLEN(to), word, MAXWLEN); (void)spell_casefold(curwin, to, (int)STRLEN(to), word, MAXWLEN);
ftp->ft_to = getroom_save(spin, word); ftp->ft_to = getroom_save(spin, word);
} }
@ -3764,7 +3764,7 @@ store_word (
char_u *word, char_u *word,
int flags, // extra flags, WF_BANNED int flags, // extra flags, WF_BANNED
int region, // supported region(s) int region, // supported region(s)
char_u *pfxlist, // list of prefix IDs or NULL const char_u *pfxlist, // list of prefix IDs or NULL
bool need_affix // only store word with affix ID bool need_affix // only store word with affix ID
) )
{ {
@ -3772,25 +3772,28 @@ store_word (
int ct = captype(word, word + len); int ct = captype(word, word + len);
char_u foldword[MAXWLEN]; char_u foldword[MAXWLEN];
int res = OK; int res = OK;
char_u *p;
(void)spell_casefold(word, len, foldword, MAXWLEN); (void)spell_casefold(curwin, word, len, foldword, MAXWLEN);
for (p = pfxlist; res == OK; ++p) { for (const char_u *p = pfxlist; res == OK; p++) {
if (!need_affix || (p != NULL && *p != NUL)) if (!need_affix || (p != NULL && *p != NUL)) {
res = tree_add_word(spin, foldword, spin->si_foldroot, ct | flags, res = tree_add_word(spin, foldword, spin->si_foldroot, ct | flags,
region, p == NULL ? 0 : *p); region, p == NULL ? 0 : *p);
if (p == NULL || *p == NUL) }
if (p == NULL || *p == NUL) {
break; break;
}
} }
++spin->si_foldwcount; ++spin->si_foldwcount;
if (res == OK && (ct == WF_KEEPCAP || (flags & WF_KEEPCAP))) { if (res == OK && (ct == WF_KEEPCAP || (flags & WF_KEEPCAP))) {
for (p = pfxlist; res == OK; ++p) { for (const char_u *p = pfxlist; res == OK; p++) {
if (!need_affix || (p != NULL && *p != NUL)) if (!need_affix || (p != NULL && *p != NUL)) {
res = tree_add_word(spin, word, spin->si_keeproot, flags, res = tree_add_word(spin, word, spin->si_keeproot, flags,
region, p == NULL ? 0 : *p); region, p == NULL ? 0 : *p);
if (p == NULL || *p == NUL) }
if (p == NULL || *p == NUL) {
break; break;
}
} }
++spin->si_keepwcount; ++spin->si_keepwcount;
} }

View File

@ -1,5 +1,8 @@
" Test editing line in Ex mode (see :help Q and :help gQ). " Test editing line in Ex mode (see :help Q and :help gQ).
source check.vim
source shared.vim
" Helper function to test editing line in Q Ex mode " Helper function to test editing line in Q Ex mode
func Ex_Q(cmd) func Ex_Q(cmd)
" Is there a simpler way to test editing Ex line? " Is there a simpler way to test editing Ex line?
@ -79,4 +82,20 @@ func Test_ex_mode_errors()
quit quit
endfunc endfunc
func Test_ex_mode_count_overflow()
" this used to cause a crash
let lines =<< trim END
call feedkeys("\<Esc>Q\<CR>")
v9|9silent! vi|333333233333y32333333%O
call writefile(['done'], 'Xdidexmode')
qall!
END
call writefile(lines, 'Xexmodescript')
call assert_equal(1, RunVim([], [], '-e -s -S Xexmodescript -c qa'))
call assert_equal(['done'], readfile('Xdidexmode'))
call delete('Xdidexmode')
call delete('Xexmodescript')
endfunc
" vim: shiftwidth=2 sts=2 expandtab " vim: shiftwidth=2 sts=2 expandtab

View File

@ -796,6 +796,26 @@ func Test_fold_delete_first_line()
set foldmethod& set foldmethod&
endfunc endfunc
func Test_undo_fold_deletion()
new
set fdm=marker
let lines =<< trim END
" {{{
" }}}1
" {{{
END
call setline(1, lines)
3d
g/"/d
undo
redo
" eval getline(1, '$')->assert_equal([''])
eval assert_equal(getline(1, '$'), [''])
set fdm&vim
bwipe!
endfunc
" this was crashing " this was crashing
func Test_move_no_folds() func Test_move_no_folds()
new new

View File

@ -1006,4 +1006,59 @@ func Test_visual_put_in_block_using_zp()
bwipe! bwipe!
endfunc endfunc
func Test_visual_put_in_block_using_zy_and_zp()
new
" Test 1) Paste using zp - after the cursor without trailing spaces
call setline(1, ['/path;text', '/path;text', '/path;text', '',
\ 'texttext /subdir columntext',
\ 'texttext /longsubdir columntext',
\ 'texttext /longlongsubdir columntext'])
exe "normal! 5G0f/\<c-v>2jezy"
norm! 1G0f;hzp
call assert_equal(['/path/subdir;text', '/path/longsubdir;text', '/path/longlongsubdir;text'], getline(1, 3))
" Test 2) Paste using zP - in front of the cursor without trailing spaces
%d
call setline(1, ['/path;text', '/path;text', '/path;text', '',
\ 'texttext /subdir columntext',
\ 'texttext /longsubdir columntext',
\ 'texttext /longlongsubdir columntext'])
exe "normal! 5G0f/\<c-v>2jezy"
norm! 1G0f;zP
call assert_equal(['/path/subdir;text', '/path/longsubdir;text', '/path/longlongsubdir;text'], getline(1, 3))
" Test 3) Paste using p - with trailing spaces
%d
call setline(1, ['/path;text', '/path;text', '/path;text', '',
\ 'texttext /subdir columntext',
\ 'texttext /longsubdir columntext',
\ 'texttext /longlongsubdir columntext'])
exe "normal! 5G0f/\<c-v>2jezy"
norm! 1G0f;hp
call assert_equal(['/path/subdir ;text', '/path/longsubdir ;text', '/path/longlongsubdir;text'], getline(1, 3))
" Test 4) Paste using P - with trailing spaces
%d
call setline(1, ['/path;text', '/path;text', '/path;text', '',
\ 'texttext /subdir columntext',
\ 'texttext /longsubdir columntext',
\ 'texttext /longlongsubdir columntext'])
exe "normal! 5G0f/\<c-v>2jezy"
norm! 1G0f;P
call assert_equal(['/path/subdir ;text', '/path/longsubdir ;text', '/path/longlongsubdir;text'], getline(1, 3))
" Test 5) Yank with spaces inside the block
%d
call setline(1, ['/path;text', '/path;text', '/path;text', '',
\ 'texttext /sub dir/ columntext',
\ 'texttext /lon gsubdir/ columntext',
\ 'texttext /lon glongsubdir/ columntext'])
exe "normal! 5G0f/\<c-v>2jf/zy"
norm! 1G0f;zP
call assert_equal(['/path/sub dir/;text', '/path/lon gsubdir/;text', '/path/lon glongsubdir/;text'], getline(1, 3))
bwipe!
endfunc
" vim: shiftwidth=2 sts=2 expandtab " vim: shiftwidth=2 sts=2 expandtab