質問

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

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

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

役に立ちましたか?

解決

これは、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()}

他のヒント

上記の 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()}

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

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

バッファを定期的に再カウントすることも賢明です (編集が行われている場所がわかっているため、バッファ全体を一度にカウントする必要はないことに注意してください)。

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

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

楽しむ!

そこで私はこう書きました:

func CountWords()
    exe "normal g\"
    let words = substitute(v:statusmsg, "^.*Word [^ ]* of ", "", "")
    let words = substitute(words, ";.*", "", "")
    return words
endfunc

ただし、ステータスバーに情報を出力するため、あなたのユースケースには適さないと思います。それにしてもすごく速いですね!

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

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

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

設定 updatetime 値を小さくすることもここで役に立ちます。

set updatetime=300

単語数に関する大規模なオーバーヘッド ポーリングはありません。 CursorHold そして CursorHoldI 火だけ 一度 カーソルの動きが止まるとき、すべてではありません updatetime MS。

これは、ビジュアルモードでも機能する 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--'
    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\

この大部分は、関数の作成に関する 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)それなら、もっと良い解決策があると思います。

私のおすすめ:

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)}'

これが他のソリューションと速度の点でどのように比較できるかはわかりませんが、ほとんどのソリューションよりもはるかにシンプルであることは確かです。

私は Vim スクリプトの初心者ですが、お勧めします

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

既存のステータス行を上書きしないので、少しすっきりします。

投稿した理由は、この関数には不可解なバグがあることを指摘するためです。つまり、追加コマンドが中断されます。ヒッティング 挿入モードになり、カーソルが行の最後の文字の右側に配置されます。ただし、このカスタム ステータス バーを有効にすると、最後の文字の左側に表示されます。

これの原因が何かわかる人はいますか?

これは、 マイケル・ダンのバージョン, 、単語数をキャッシュするので、必要な処理はさらに少なくなります。

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 

Steve Moyer が提供した回答の方法を使用すると、次の解決策を生成することができました。これは残念ながらかなり洗練されていないハックであり、もっときちんとした解決策があるに違いないと感じていますが、これは機能し、ステータス行が更新されるたびにバッファ内のすべての単語を単純にカウントするよりもはるかに高速です。このソリューションはプラットフォームに依存せず、システムに「トイレ」などの機能があることを前提としていないことにも注意してください。

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

私の解決策:

"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 で実際の単語数を調べたい人にとって、これが役立つことを願っています。必ずしも正確であるとは限りませんが。もちろん g ctrl-g を押すと Vim の単語数が表示されます。

他の誰かがGoogleからここに来ている場合に備えて、Abslom Daakの回答を修正して機能するようにしました 航空会社. 。以下のように保存しました

~/.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

ガイ・グリ・アリのバリエーション 洗練 それ

  • スペルチェックが有効な場合にのみ単語をカウントします。
  • ビジュアルモードで選択された単語の数をカウントします
  • 挿入モードおよび通常モード以外ではミュートを維持し、
  • システム言語にもっと依存しないことを願っています (英語と異なる場合)
function! StatuslineWordCount()
  if !&l:spell
    return ''
  endif

  if empty(getline(line('$')))
    return ''
  endif
  let mode = mode()
  if !(mode ==# 'v' || mode ==# 'V' || mode ==# "\<c-v>" || mode =~# '[ni]')
    return ''
  endif

  let s:old_status = v:statusmsg
  let position = getpos('.')
  let stat = v:statusmsg
  let s:word_count = 0
  exe ":silent normal g\<c-g>"
  try
    if mode ==# 'v' || mode ==# 'V'
      let s:word_count = split(split(v:statusmsg, ';')[1])[0]
    elseif mode ==# "\<c-v>"
      let s:word_count = split(split(v:statusmsg, ';')[2])[0]
    elseif mode =~# '[ni]'
      let s:word_count = split(split(v:statusmsg, ';')[2])[3]
    end
  " index out of range
  catch /^Vim\%((\a\+)\)\=:E\%(684\|116\)/
    return ''
  endtry
  let v:statusmsg = s:old_status
  call setpos('.', position)

  return "\ \|\ " . s:word_count . 'w'
endfunction

たとえば、次のようにしてステータスラインに追加できます。

    set statusline+=%.10{StatuslineWordCount()}     " wordcount
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top