36

vimステータスラインにライブワードカウントを表示しようとしています。これを行うには、.vimrcにステータス行を設定し、それに関数を挿入します。この関数の考え方は、現在のバッファー内のワード数を返すことです。この番号はステータス行に表示されます。ステータスラインはほぼすべての機会に更新されるため、これはうまく機能するはずです。そのため、カウントは常に「ライブ」のままになります。

問題は、私が現在定義している関数が遅いため、最小のファイルを除くすべてのファイルに使用すると、vimの動作が明らかに遅くなることです。この関数は非常に頻繁に実行されるためです。

要約すると、現在のバッファー内の単語数を計算して結果を返すのに非常に高速な関数を作成するための巧妙なトリックを持っている人はいますか?

4

17 に答える 17

25

上記のMichael Dunnの回答は本当に気に入っていますが、編集中に最後の列にアクセスできなくなっていることがわかりました。したがって、関数に小さな変更があります。

function! WordCount()
   let s:old_status = v:statusmsg
   let position = getpos(".")
   exe ":silent normal g\<c-g>"
   let stat = v:statusmsg
   let s:word_count = 0
   if stat != '--No lines in buffer--'
     let s:word_count = str2nr(split(v:statusmsg)[11])
     let v:statusmsg = s:old_status
   end
   call setpos('.', position)
   return s:word_count 
endfunction

問題なくステータス行に含めました:

:set statusline=wc:%{WordCount()}

于 2011-01-03T20:39:25.360 に答える
23

これは、Rodrigo Queiro のアイデアの使用可能なバージョンです。ステータス バーは変更されず、statusmsg 変数が復元されます。

function WordCount()
  let s:old_status = v:statusmsg
  exe "silent normal g\<c-g>"
  let s:word_count = str2nr(split(v:statusmsg)[11])
  let v:statusmsg = s:old_status
  return s:word_count
endfunction

これは、ステータス行に直接含めるのに十分速いようです。たとえば、次のようになります。

:set statusline=wc:%{WordCount()}
于 2009-02-16T13:13:01.353 に答える
9

現在の行のカウントと残りのバッファーの別のカウントを保持します。現在の行で単語を入力 (または削除) すると、その数だけが更新されますが、現在の行数と残りのバッファー数の合計が表示されます。

行を変更するときは、現在の行数をバッファー数に追加し、現在の行の単語を数えて、a) 現在の行数を設定し、b) バッファー数からそれを減算します。

また、バッファを定期的に再カウントすることも賢明です (編集が行われている場所はわかっているので、一度にバッファ全体をカウントする必要はありません)。

于 2008-09-22T12:09:42.157 に答える
4

これにより、しばらく入力を停止するたびに(具体的には、updatetimems)単語数が再計算されます。

let g:word_count="<unknown>"
fun! WordCount()
    return g:word_count
endfun
fun! UpdateWordCount()
    let s = system("wc -w ".expand("%p"))
    let parts = split(s, ' ')
    if len(parts) > 1
        let g:word_count = parts[0]
    endif
endfun

augroup WordCounter
    au! CursorHold * call UpdateWordCount()
    au! CursorHoldI * call UpdateWordCount()
augroup END

" how eager are you? (default is 4000 ms)
set updatetime=500

" modify as you please...
set statusline=%{WordCount()}\ words

楽しみ!

于 2008-09-22T18:04:59.790 に答える
3

だから私は書いた:

関数 CountWords()
    exe「ノーマルg\」
    let words = Substitute(v:statusmsg, "^.*Word [^ ]* of ", "", "")
    言葉を=代用する(言葉、「;.*」、「」、「」)
    言葉を返す
終了機能

ただし、ステータスバーに情報を出力するため、ユースケースには適していないと思います。めっちゃ速いですけどね!

于 2008-09-22T12:51:53.773 に答える
2

これには、少し異なるアプローチを使用しました。単語カウント関数が特に高速であることを確認するのではなく、カーソルの移動が停止したときにのみ呼び出します。これらのコマンドはそれを行います:

:au CursorHold * exe "normal g\<c-g>"
:au CursorHoldI * exe "normal g\<c-g>"

質問者が望んでいたものではないかもしれませんが、ここでの回答のいくつかよりもはるかに単純であり、私のユースケースには十分です (1、2 文を入力した後、下を見て単語数を確認してください)。

小さい値に設定updatetimeすると、次の場合にも役立ちます。

set updatetime=300

単語数のポーリングに大きなオーバーヘッドはありません。ミリ秒ごとではなく、カーソルの移動が停止したときにCursorHold1CursorHoldIだけ発生するためです。updatetime

于 2012-11-24T09:25:54.677 に答える
2

これは、ビジュアルモードでも機能するAbslom Daakの回答を改良したものです。

function! WordCount()
  let s:old_status = v:statusmsg
  let position = getpos(".")
  exe ":silent normal g\<c-g>"
  let stat = v:statusmsg
  let s:word_count = 0
  if stat != '--No lines in buffer--'
    if stat =~ "^Selected"
      let s:word_count = str2nr(split(v:statusmsg)[5])
    else
      let s:word_count = str2nr(split(v:statusmsg)[11])
    end
    let v:statusmsg = s:old_status
  end
  call setpos('.', position)
  return s:word_count 
endfunction

前と同じようにステータス ラインに含まれます。以下は、右揃えのステータス ラインです。

set statusline=%=%{WordCount()}\ words\

于 2013-09-19T13:17:01.143 に答える
2

vimバージョン 7.4.1042以降

vimバージョン 7.4.1042以降statusline、次のように簡単に変更できます。

set statusline+=%{wordcount().words}\ words
set laststatus=2    " enables the statusline.

単語数vim-airline

ワード カウントはvim-airline、執筆時点では、多くのファイル タイプに対して 標準で提供されています。asciidoc, help, mail, markdown, org, rst, tex ,text

に単語数が表示されない場合vim-airline、多くの場合、ファイルの種類が認識されていないことが原因です。たとえば、少なくとも今のところ、複合ファイル タイプはワード カウントmarkdown.pandocでは認識されません。これは、次のようvim-airlineに修正することで簡単に修正できます。.vimrc

let g:airline#extensions#wordcount#filetypes = '\vasciidoc|help|mail|markdown|markdown.pandoc|org|rst|tex|text'
set laststatus=2    " enables vim-airline.

この\vステートメントは、デフォルトg:airline#extensions#wordcount#filetypes変数をオーバーライドします。最後の行vim-airlineは、有効であることを保証します。

疑わしい場合は&filetype、次のコマンドを発行すると、開いているファイルの が返されます。

:echo &filetype

ここにメタ例があります:

vim-airline ワードカウント

于 2020-02-19T22:47:21.973 に答える
1

私はVimスクリプトを初めて使用しますが、提案するかもしれません

function WordCount()
    redir => l:status
    exe "silent normal g\<c-g>"
    redir END
    return str2nr(split(l:status)[11])
endfunction

既存のステータス行を上書きしないため、少しクリーンになります。

私が投稿する理由は、この関数には不可解なバグがあることを指摘することです。つまり、appendコマンドが壊れます。押すAと、カーソルが行の最後の文字の右側に配置された状態で挿入モードになります。ただし、このカスタムステータスバーを有効にすると、最後の文字の左側に移動します。

誰もがこれを引き起こす原因を知っていますか?

于 2012-01-07T22:01:03.740 に答える
1

これの大部分は、関数の作成に関するvimヘルプページから取得しました。

function! WordCount()
  let lnum = 1
  let n = 0
  while lnum <= line('$')
    let n = n + len(split(getline(lnum)))
    let lnum = lnum + 1
  endwhile
  return n
endfunction

もちろん、他のものと同様に、次のことを行う必要があります。

:set statusline=wc:%{WordCount()}

誰かがこれをクリーンアップして、より魅力的なものにすることができると確信していますが(単なるnではなくs:n?)、基本的な機能はそこにあると思います。

編集:

これをもう一度見てみると、Mikael Jansson のソリューションがとても気に入っています。wc私は(移植性がなく、おそらく遅い)に砲撃するのは好きではありません。UpdateWordCount彼の関数を上記のコード (関数の名前を に変更)に置き換えるUpdateWordCountと、より良い解決策が得られると思います。

于 2010-05-08T23:14:37.873 に答える
1

これは、Michael Dunn のバージョンの改良であり、ワード カウントをキャッシュするため、必要な処理がさらに少なくなります。

function! WC()
    if &modified || !exists("b:wordcount") 
            let l:old_status = v:statusmsg  
            execute "silent normal g\<c-g>"
            let b:wordcount = str2nr(split(v:statusmsg)[11])
            let v:statusmsg = l:old_status  
            return b:wordcount
    else
            return b:wordcount
    endif
endfunction 
于 2012-08-16T18:57:22.083 に答える
1

私のおすすめ:

function! UpdateWordCount()
  let b:word_count = eval(join(map(getline("1", "$"), "len(split(v:val, '\\s\\+'))"), "+"))
endfunction

augroup UpdateWordCount
  au!
  autocmd BufRead,BufNewFile,BufEnter,CursorHold,CursorHoldI,InsertEnter,InsertLeave * call UpdateWordCount()
augroup END

let &statusline='wc:%{get(b:, "word_count", 0)}'

これが他のいくつかのソリューションと速度を比較する方法はわかりませんが、ほとんどのソリューションよりもはるかに単純であることは確かです.

于 2010-05-11T04:38:57.817 に答える
0

スティーブモイヤーによって提供された答えの方法を使用して、私は次の解決策を生み出すことができました。これはかなり洗練されていないハックであり、より適切な解決策が必要だと感じていますが、それは機能し、ステータス行が更新されるたびにバッファ内のすべての単語を単純にカウントするよりもはるかに高速です。このソリューションはプラットフォームに依存せず、システムに「wc」などがあるとは想定していないことにも注意してください。

私のソリューションは定期的にバッファを更新しませんが、MikaelJanssonによって提供された答えはこの機能を提供することができます。私はまだ、私のソリューションが同期しなくなるインスタンスを見つけていません。ただし、正確なライブ単語数は私のニーズに不可欠ではないため、これを簡単にテストしただけです。単語のマッチングに使用するパターンも単純で、単純なテキストドキュメントを対象としています。パターンやその他の提案について誰かがより良いアイデアを持っている場合は、回答を投稿するか、この投稿を編集してください。

私の解決策:

"returns the count of how many words are in the entire file excluding the current line 
"updates the buffer variable Global_Word_Count to reflect this
fu! OtherLineWordCount() 
    let data = []
    "get lines above and below current line unless current line is first or last
    if line(".") > 1
        let data = getline(1, line(".")-1)
    endif   
    if line(".") < line("$")
        let data = data + getline(line(".")+1, "$") 
    endif   
    let count_words = 0
    let pattern = "\\<\\(\\w\\|-\\|'\\)\\+\\>"
    for str in data
        let count_words = count_words + NumPatternsInString(str, pattern)
    endfor  
    let b:Global_Word_Count = count_words
    return count_words
endf    

"returns the word count for the current line
"updates the buffer variable Current_Line_Number 
"updates the buffer variable Current_Line_Word_Count 
fu! CurrentLineWordCount()
    if b:Current_Line_Number != line(".") "if the line number has changed then add old count
        let b:Global_Word_Count = b:Global_Word_Count + b:Current_Line_Word_Count
    endif   
    "calculate number of words on current line
    let line = getline(".")
    let pattern = "\\<\\(\\w\\|-\\|'\\)\\+\\>"
    let count_words = NumPatternsInString(line, pattern)
    let b:Current_Line_Word_Count = count_words "update buffer variable with current line count
    if b:Current_Line_Number != line(".") "if the line number has changed then subtract current line count
        let b:Global_Word_Count = b:Global_Word_Count - b:Current_Line_Word_Count
    endif   
    let b:Current_Line_Number = line(".") "update buffer variable with current line number
    return count_words
endf    

"returns the word count for the entire file using variables defined in other procedures
"this is the function that is called repeatedly and controls the other word
"count functions.
fu! WordCount()
    if exists("b:Global_Word_Count") == 0 
        let b:Global_Word_Count = 0
        let b:Current_Line_Word_Count = 0
        let b:Current_Line_Number = line(".")
        call OtherLineWordCount()
    endif   
    call CurrentLineWordCount()
    return b:Global_Word_Count + b:Current_Line_Word_Count
endf

"returns the number of patterns found in a string 
fu! NumPatternsInString(str, pat)
    let i = 0
    let num = -1
    while i != -1
        let num = num + 1
        let i = matchend(a:str, a:pat, i)
    endwhile
    return num
endf

次に、これは次の方法でステータス行に追加されます。

:set statusline=wc:%{WordCount()}

これがVimでライブ単語数を探している人に役立つことを願っています。常に正確であるとは限りませんが。あるいはもちろん、gctrl-gはVimの単語数を提供します!

于 2008-09-23T11:19:05.547 に答える
0

他の誰かが Google からここに来る場合に備えて、Abslom Daak の回答をAirlineと連携するように変更しました。次のように保存しました

~/.vim/bundle/vim-airline/autoload/airline/extensions/pandoc.vim

と追加

call airline#extensions#pandoc#init(s:ext)

extensions.vim

let s:spc = g:airline_symbols.space

function! airline#extensions#pandoc#word_count()
if mode() == "s"
    return 0
else
    let s:old_status = v:statusmsg
    let position = getpos(".")
    let s:word_count = 0
    exe ":silent normal g\<c-g>"
    let stat = v:statusmsg
    let s:word_count = 0
    if stat != '--No lines in buffer--'
        let s:word_count = str2nr(split(v:statusmsg)[11])
        let v:statusmsg = s:old_status
    end
    call setpos('.', position)
    return s:word_count 
end
endfunction

function! airline#extensions#pandoc#apply(...)
if &ft == "pandoc"
    let w:airline_section_x = "%{airline#extensions#pandoc#word_count()} Words"
endif
endfunction

function! airline#extensions#pandoc#init(ext)
call a:ext.add_statusline_func('airline#extensions#pandoc#apply')
endfunction
于 2014-07-03T18:16:59.637 に答える