vim-jp / issues

有志で既知のバグや要望を検討・管理し、オフィシャルへの還元をしていきます。
https://vim-jp.org/
341 stars 11 forks source link

def関数のコマンドライン初回使用時にスクロールする #1402

Closed iranoan closed 1 year ago

iranoan commented 1 year ago

質問の内容

function で定義した関数では起きないのですが、def 関数で定義した関数だと、最初の使用時にスクロールが発生するケースが有ります 結果、入力中のコマンドが途切れているように見えますが、コマンド自体は問題なく動作します 初回時だけで、2 度目以降は起きません これは仕様なのでしょうか?

cnoremap_def

再現する設定とプラグイン例は次のとおりです \~/.vim/vimrc

scriptversion 4
set viminfo+=n~/.vim/viminfo
set background=dark
colorscheme habamax
filetype plugin on
syntax enable
set shortmess+=IWc
set autoindent smartindent
set hidden

\~/.vim/pack/sample/start/sample/plugin/sample.vim

vim9script

augroup SAMPLE
    autocmd!
    autocmd CmdlineEnter * sample#Init() | autocmd! SAMPLE | augroup! SAMPLE
augroup END

\~/.vim/pack/sample/start/sample/autoload/sample.vim

vim9script

export def Init(): void
    cnoremap <expr>" <SID>Quote('"')
    # defcompile # このコメントを外すと問題が解消できる
enddef

def Quote(str: string): string
    var line: string
    var pline: string
    var column: number
    def InPair(): string
        return str .. str .. (mode(1) =~# '^c' ? "\<Left>" : "\<C-G>U\<Left>")
    enddef
    if mode(1) =~# '^c'
        column = getcmdpos()
        line = getcmdline()
    else
        column = col('.')
        line = getline('.')
    endif
    if mode(1) =~# '^R' || (getcmdtype() =~# '[/?]' || getcmdwintype() =~# '/?:')
        return str
    endif
    pline = strpart(line, 0, column - 1)
    if strlen(matchstr(pline, '\\\+$')) % 2
        return str
    endif
    return InPair()
enddef

関数がある程度複雑になると発生するようで、上記例の場合 InPair() を態々関数にしなければ問題は起きません またコメントに有るように、初期化時に defcompile すれば、関数の複雑さに関係なく問題は起きません

Vimのバージョン

$ vim --version VIM - Vi IMproved 9.0 (2022 Jun 28, compiled Aug 23 2022 20:18:58) 適用済パッチ: 1-242 Modified by team+vim@tracker.debian.org Compiled by team+vim@tracker.debian.org Huge 版 with GTK3 GUI. 機能の一覧 有効(+)/無効(-)

OSの種類/ディストリ/バージョン

$ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 22.10 Release: 22.10 Codename: kinetic $ uname -a Linux xxx 5.19.0-26-generic #27-Ubuntu SMP PREEMPT_DYNAMIC Wed Nov 23 20:44:15 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux

h-east commented 1 year ago

以下の手順とscriptで再現しました。(Vim 9.0.1128 on Fedora 32 via PuTTY 0.73)

用意するファイル

~/.vim/pack/my/start/sample/plugin/sample.vim

vim9script
augroup SAMPLE
    autocmd!
    autocmd CmdlineEnter * sample#Init()
augroup END

~/.vim/pack/my/start/sample/autoload/sample.vim

vim9script
export def Init(): void
    cnoremap <expr>" <SID>Quote('"')
enddef
def Quote(str: string): string
    def InPair(): number
        return 0
    enddef
    return str
enddef


再現条件に必要なのは:defのネストのようです。 実際に改行している部分は以下の userfunc.c の define_function() 内の msg_putchar('\n'); なのですが、今回の現象とコメント // don't overwrite the function name が結びつかないんですよね。 ifの判定が足りていないのか、この動作が仕様なのか。。

    /*
     * Read the body of the function, until "}", ":endfunction" or ":enddef" is
     * found.
     */
    if (KeyTyped)
    {
    // Check if the function already exists, don't let the user type the
    // whole function before telling him it doesn't work!  For a script we
    // need to skip the body to be able to find what follows.
    if (!eap->skip && !eap->forceit)
    {
        if (fudi.fd_dict != NULL && fudi.fd_newkey == NULL)
        emsg(_(e_dictionary_entry_already_exists));
        else if (name != NULL && find_func(name, is_global) != NULL)
        emsg_funcname(e_function_str_already_exists_add_bang_to_replace, name);
    }

    if (!eap->skip && did_emsg)
        goto erret;

    msg_putchar('\n');      // don't overwrite the function name
    cmdline_row = msg_row;
    }

backtraceも載せときます(関連部分のみ)

~
#8  0x0000000000706ed0 in msg_putchar (c=10) at message.c:1518
#9  0x00000000006653aa in define_function
    (eap=0x7ffdc108fe30, name_arg=0x1fd0ec0 "<lambda>2", lines_to_free=0x7ffdc10
901c0, in_class=0) at userfunc.c:4892
#10 0x00000000006730b5 in compile_nested_function
        (eap=0x7ffdc108fe30, cctx=0x7ffdc1090030, lines_to_free=0x7ffdc10901c0)
    at vim9compile.c:1004
#11 0x0000000000678774 in compile_def_function
    (ufunc=0x20229a0, check_return_type=0, compile_type=CT_NONE, outer_cctx=0x0)
 at vim9compile.c:3421
#12 0x00000000006878ae in call_def_function
    (ufunc=0x20229a0, argc_arg=1, argv=0x7ffdc1090d90, flags=0, partial=0x0, obj
ect=0x0, funccal=0x1fcd560, rettv=0x7ffdc1091840) at vim9execute.c:5684
#13 0x0000000000660ef3 in call_user_func
    (fp=0x20229a0, argcount=1, argvars=0x7ffdc1090d90, rettv=0x7ffdc1091840, fun
cexe=0x7ffdc1090f40, selfdict=0x0) at userfunc.c:2771
#14 0x0000000000661f3d in call_user_func_check
    (fp=0x20229a0, argcount=1, argvars=0x7ffdc1090d90, rettv=0x7ffdc1091840, fun
cexe=0x7ffdc1090f40, selfdict=0x0) at userfunc.c:3189
#15 0x0000000000662d1e in call_func
    (funcname=0x20207b0 "\200\375R15_Quote('\"')", len=11, rettv=0x7ffdc1091840,
 argcount_in=1, argvars_in=0x7ffdc1090d90, funcexe=0x7ffdc1090f40)
    at userfunc.c:3745
#16 0x000000000065f61c in get_func_tv
    (name=0x20207b0 "\200\375R15_Quote('\"')", len=11, rettv=0x7ffdc1091840, arg
=0x7ffdc1091710, evalarg=0x7ffdc10917b0, funcexe=0x7ffdc1090f40)
    at userfunc.c:1923
#17 0x000000000046d005 in eval_func
    (arg=0x7ffdc1091710, evalarg=0x7ffdc10917b0, name=0x2036ca0 "\200\375R15_Quo
te('\"')", name_len=11, rettv=0x7ffdc1091840, flags=1, basetv=0x0)
    at eval.c:2335
~
h-east commented 1 year ago

vim_devに報告しました。 https://github.com/vim/vim/issues/11773

iranoan commented 1 year ago

詳しく検証していただきありがとうございます

再現条件に必要なのは:defのネストのようです。 確かに def が多いと改行も多くなりますね

tsuyoshicho commented 1 year ago

defコンパイル時になんかあるのかな

h-east commented 1 year ago

Patch 9.0.1130で修正されました。 scriptのロード中かつ関数ネスト中は KeyTyped (キー入力したよフラグ)をリセットする修正で現象を回避するようにしたようです。