Frage

Ich versuche, eine Live-Wortanzahl in der vim Statuszeile angezeigt werden soll. Ich tue dies, meine Statuszeile in meinem .vimrc durch Setzen und eine Funktion in sie eingefügt wird. Die Idee dieser Funktion ist es, die Anzahl der Wörter in den aktuellen Puffer zurückzukehren. Diese Zahl wird dann in der Statuszeile angezeigt. Dies sollte gut funktionieren wie die Statuszeile bei fast jeder Gelegenheit aktualisiert, so die Zählung immer ‚live‘ bleiben wird.

Das Problem ist, dass die Funktion, die ich zur Zeit definiert habe, ist langsam und so vim ist offensichtlich träge, wenn es für alle, aber die kleinsten Dateien verwendet wird; Durch diese Funktion so häufig ausgeführt wird.

Insgesamt hat jemand einen cleveren Trick für eine Funktion erzeugen, die sekundenschnell und ist die Anzahl der Worte in den aktuellen Puffern bei der Berechnung und die Rückkehr des Ergebnisses?

War es hilfreich?

Lösung

Hier ist eine brauchbare Version von Rodrigo Queiro Idee. Es ist nicht die Statusleiste ändern, und es wird für die Behandlung statusmsg Variable.

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

Dies scheint schnell genug zu sein, direkt in der Statuszeile umfassen z.

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

Andere Tipps

Ich mag Michael Dunn Antwort oben, aber ich fand, dass, wenn ich bearbeite es verursachte mich nicht in der Lage sein, die letzte Spalte zuzugreifen. Also ich habe eine kleine Änderung für die Funktion:

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

ich eingeschlossen habe es in meiner Statuszeile ohne Probleme:

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

eine Zählung für die aktuelle Zeile halten und eine separate Zählung für den Rest des Puffers. Wie Sie (oder löschen) Wörter auf der aktuellen Zeile eingeben, aktualisieren Sie die zählen nur, sondern die Summe der aktuellen Zeilenzahl und den Rest der Pufferanzahl angezeigt werden soll.

Wenn Sie Zeilen ändern, die aktuelle Zeilenzahl in die Pufferzahl hinzufügen, die Worte in der aktuellen Zeile zählen und a) setzten die aktuelle Zeilenzahl und b) subtrahieren sie von der Pufferzählwert.

Es wäre auch ratsam, den Puffer zu erzählen in regelmäßigen Abständen (beachten Sie, dass Sie nicht über den gesamten Puffer auf einmal zählen müssen, da Sie wissen, wo die Bearbeitung auftritt).

Damit wird die Anzahl der Wörter neu zu berechnen, wenn Sie für eine Weile aufhören Eingabe (genauer gesagt, 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

Genießen Sie!

So habe ich geschrieben:

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

Aber es druckt Informationen zum statusbar, so dass ich glaube nicht, dass es für Ihren Anwendungsfall geeignet ist. Es ist sehr schnell, aber!

Ich habe einen etwas anderen Ansatz dafür. Anstatt sicherstellen, dass die Wortzahl Funktion besonders schnell ist, kann ich nur nennen, wenn der Cursor nicht mehr bewegt. Diese Befehle werden es tun:

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

Vielleicht nicht ganz, was die Fragesteller wollten, aber viel einfacher als einige der Antworten hier, und gut genug für meinen Anwendungsfall (Blick nach unten Wort nach, um zu sehen zählt einen oder zwei Sätze eingeben).

Einstellung updatetime auf einen kleineren Wert hilft auch hier:

set updatetime=300

Es ist kein großer Aufwand Polling für das Wort zählen, weil CursorHold und CursorHoldI nur Feuer einmal , wenn der Cursor nicht mehr bewegt, nicht alle updatetime ms.

Hier ist eine Verfeinerung der Abslom Daak Antwort, die auch im visuellen Modus arbeitet.

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

In der Statuszeile enthält wie zuvor. Hier ist eine rechtsbündig Statuszeile:

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

Ich habe den Großteil davon aus den vim Hilfeseite zum Schreiben von Funktionen.

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

Natürlich, wie die anderen, die Sie benötigen, um:

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

Ich bin sicher, dass dies von jemandem gereinigt werden kann es vimmy zu machen (s:? N statt nur n)., Aber ich glaube, die grundlegende Funktionalität gibt es

Edit:

bei dieser Suche wieder, ich wie Mikael Jansson Lösung wirklich. Ich mag es nicht Beschuss aus wc (nicht tragbar und vielleicht langsam). Wenn wir seine UpdateWordCount Funktion mit dem Code ersetzen ich oben haben (meine Funktion UpdateWordCount Umbenennen), dann denke ich, dass wir eine bessere Lösung haben.

Mein Vorschlag:

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

Ich bin mir nicht sicher, wie dies einige der anderen Lösungen in der Geschwindigkeit vergleicht, aber es ist sicherlich viel einfacher als die meisten.

Ich bin neu in Vim Scripting, aber ich könnte darauf hindeuten,

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

als etwas sauberer sein, da es nicht die bestehende Statuszeile nicht überschrieben werden.

Mein Grund für die Buchung ist darauf hinzuweisen, dass diese Funktion einen rätselhaften Fehler hat: nämlich, es bricht den append-Befehl. Schlagen A sollten Sie in den Einfügemodus mit dem Cursor rechts von dem letzten Zeichen auf der Linie positioniert fallen. Doch mit dieser Bar benutzerdefinierten Status aktivieren, damit Sie sie links von dem letzten Zeichen setzen.

Wer noch keine Ahnung, was das verursacht?

Dies ist eine Verbesserung auf Michael Dunn Version , Caching das Wort zählt so noch weniger Verarbeitung benötigt wird.

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 

Das Verfahren unter Verwendung in der Antwort zur Verfügung gestellt von Steve Moyer konnte ich die folgende Lösung herzustellen. Es ist ein ziemlich unelegant Hack, den ich habe Angst, und ich fühle, dass es eine sauberere Lösung, aber es funktioniert, und ist viel schneller als einfach alle Worte in einem Puffer in der Statuszeile aktualisiert wird, jedes Mal zu zählen. Ich sollte auch beachten, dass diese Lösung plattformunabhängig ist und nicht davon ausgehen, ein System hat ‚WC‘ oder etwas ähnliches.

Meine Lösung nicht aktualisiert periodisch den Puffer, aber die Antwort von Mikael Jansson vorgesehen wäre in der Lage, diese Funktionalität zu bieten. Ich habe nicht, als der noch, fand eine Instanz, wo meine Lösung nicht synchron wird. Allerdings habe ich nur diese kurz als eine genaue Live-Wortzahl getestet, um meine Bedürfnisse nicht wesentlich ist. Das Muster ich für geschriebene Wörter ist auch einfach und ist für einfache Textdokumente vorgesehen. Wenn jemand eine bessere Idee für ein Muster oder andere Vorschläge hat wenden Sie sich bitte eine Antwort oder bearbeiten diesen Beitrag zu schreiben.

Meine Lösung:

"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

Dies wird dann in der Statuszeile hinzugefügt von:

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

Ich hoffe, das jemand für eine Live-Wortanzahl in Vim sucht hilft. Allerdings ist das nicht immer exakt. Alternativ natürlich g ctrl-g werden Sie mit Vim Wort bieten zählen!

Falls jemand anderes hier von Google kommt, modifizierte ich Abslom Daak Antwort auf die Arbeit mit Airline . Ich speicherte die folgenden als

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

und hinzugefügt

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

Eine Variation von Guy Gur-Ari Verfeinerung dass

  • zählt nur Worte, wenn die Rechtschreibprüfung aktiviert ist,
  • zählt die Anzahl der ausgewählten Wörter im visuellen Modus
  • hält stumm außerhalb des Einsatzes und Normalmodus und
  • hoffentlich ist Agnostiker auf die Systemsprache (wenn verschieden von Englisch)
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

, die auf die Statuszeile angehängt werden kann, sagen wir,

    set statusline+=%.10{StatuslineWordCount()}     " wordcount
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top