Closed tyru closed 5 years ago
@eldesh 最新版ではこのバグ直ってるみたいです。 もしまだ使って頂けてるようなら再現するかどうか確認してくれると助かります。
@eldesh やっぱり先にcloseしてしまったので、まだ再現するようであればre-openしてください。
c8891ff で似たような現象が再現しました。 vim74-kaoriya-win32を使って確認しています。
空のmain関数だけのファイルでは問題が出なくなりましたが、ある程度以上大きいファイル同士のdiffsplitを取るとカーソルが関数定義の末尾(の閉じカッコ '}')からそれ以上下に動かなることがあります。
// reopenはリポジトリのCollaborator権限が必要なようです
試しにVimのソースコードのsrc/ex_cmds.c
とsrc/ex_cmds2.c
でやってみた所、重くはなりますが再現しませんでした。
できればサンプルコードを示してもらえると助かります。
cfi 使わせていただいています。大変便利で助かっています。 上のコードは既に見ることができない?ので同件かどうかは定かではないのですが、 現象が似ているのでこちらに書き込みさせていただきました。
私の場合 diffsplit ではないのですが、以下のコマンドを実行後、
:let &statusline = '%{cfi#format("%s", "")}'
:new test.c
挿入モードで以下のようにタイプすると、閉じ括弧 )
を入力した時点でハングします。
#include <stdio.h>
int main(void)
調べてみた感じでは ftplugin/c/cfi.vim
の while 1
で無限ループに陥っているようです。
function! s:finder.find_begin() "{{{
let NONE = []
let [orig_lnum, orig_col] = [line('.'), col('.')]
let vb = &vb
setlocal vb t_vb=
try
" Jump to function-like word, and check arguments, and block.
while 1
if search(s:FUNCTION_PATTERN, 'bW') == 0
return NONE
endif
" Function name when filetype=c has nothing about syntax info.
" (without this condition, if-statement is recognized as function)
if !empty(synstack(line('.'), col('.')))
continue
endif
let funcname = get(matchlist(getline('.'), s:FUNCTION_PATTERN), 1, '')
if funcname ==# ''
return NONE
endif
for [fn; args] in [
\ ['search', '(', 'W'],
\ ['searchpair', '(', '', ')'],
\]
if call(fn, args) == 0
return NONE
endif
endfor
if join(getline('.', '$'), '')[col('.') :] =~# '\s*[^;]'
let self.temp.funcname = funcname
break
endif
endwhile
finally
let &vb = vb
endtry
if search('{') == 0
return NONE
endif
if line('.') == orig_lnum && col('.') == orig_col
return NONE
endif
return [line('.'), col('.')]
endfunction "}}}
ここは search(s:FUNCTION_PATTERN, 'bW')
で関数っぽいものを探して、
empty(synstack(line('.'), col('.')))
で関数の definition かどうか判定して (loopして) いる箇所が肝だと理解しています。
その後 funcname を取得して、searchpair() で閉じ括弧の位置に移動して、
join(getline('.', '$'), '')[col('.') :] =~# '\s*[^;]'
の判定が真であれば確定とするようですが、
(この判定は関数の declaration を除外している?)
今回のようなケースだとこれが偽になってしまい loop の先頭に戻ります。
このときカーソルが閉じ括弧の位置になっているため、
次の search(s:FUNCTION_PATTERN, 'bW')
も最初と同じ関数を指してしまい無限 loop になっているようです。
私が考え付いた解決方法としては、以下の 2 つです。
return NONE
する。
(これは empty(synstack())
が真となった時点で definition であることは確定で、残りの処理は念のためのチェックという考え方)cursor()
で移動してさらに前方検索する。
(これは empty(synstack())
が真でも definition 確定ではなくて、この場合前方に本来の definition があるはずという考え方)私が empty(synstack())
の意味をきちんと理解できていないので、どちらが適切なのかよく分かっていません。。
@tyru 見る余力がないだけかと思いますが、念のため ping させていただきます。
related #26
今なら empty(synstack())
が何を見ているのかが分かります。一番外側にある関数かどうかを見ているんですね。例えば、
int main(void)
{
// foobar()
return 0;
}
みたいなときに、foobar() のコメントの箇所では empty() が偽になって、main() の関数の先頭では empty() が真になりますね。
- join() の判定が偽を返したら、その他のチェックと同様に return NONE する。
とりあえずこっちの patch を考えてみました。これは、「empty(synstack()) が真であれば関数名であることは間違いない」と考えて、join(getline('.', '$'), '')[col('.') :] =~# '^\s*;'
のときは関数宣言なので return NONE
、それ以外の時は関数名として確定、という考えです。
diff --git a/ftplugin/c/cfi.vim b/ftplugin/c/cfi.vim
index 704518d..f945bbb 100644
--- a/ftplugin/c/cfi.vim
+++ b/ftplugin/c/cfi.vim
@@ -54,7 +54,10 @@ function! s:finder.find_begin() "{{{
return NONE
endif
endfor
- if join(getline('.', '$'), '')[col('.') :] =~# '\s*[^;]'
+ if join(getline('.', '$'), '')[col('.') :] =~# '^\s*;'
+ " Function declaration
+ return NONE
+ else
let self.temp.funcname = funcname
break
endif
2 のほうの patch も考えてみます。
- join() の判定が偽を返したら、関数名の先頭に cursor() で移動してさらに前方検索する。
の patch です。Fix 的にはこちらのほうが分かりやすいと思いますが、修正としては 1 のほうが適切のような気がします。
diff --git a/ftplugin/c/cfi.vim b/ftplugin/c/cfi.vim
index 704518d..fda6241 100644
--- a/ftplugin/c/cfi.vim
+++ b/ftplugin/c/cfi.vim
@@ -37,6 +37,7 @@ function! s:finder.find_begin() "{{{
if search(s:FUNCTION_PATTERN, 'bW') == 0
return NONE
endif
+ let saved_cursor = getcurpos()
" Function name when filetype=c has nothing about syntax info.
" (without this condition, if-statement is recognized as function)
if !empty(synstack(line('.'), col('.')))
@@ -57,6 +58,8 @@ function! s:finder.find_begin() "{{{
if join(getline('.', '$'), '')[col('.') :] =~# '\s*[^;]'
let self.temp.funcname = funcname
break
+ else
+ call setpos('.', saved_cursor)
endif
endwhile
finally
@mnishz ありがとうございます! 1 の PR 作ってもらえないでしょうか?
PR 出しました!よろしくお願いします。
PR 出してもらったので close します
https://twitter.com/#!/eldesh/status/116697621569089536
https://twitter.com/#!/eldesh/status/116714444649803776
https://twitter.com/#!/eldesh/status/116716639994650624