Question

J'essaie d'afficher un nombre de mots en direct dans la ligne d'état de vim. Je le fais en définissant ma ligne d'état dans mon fichier .vimrc et en y insérant une fonction. L'idée de cette fonction est de renvoyer le nombre de mots dans le tampon courant. Ce numéro est ensuite affiché sur la ligne d'état. Cela devrait fonctionner correctement, car la ligne de statut est mise à jour à peu près à toutes les occasions possibles, de sorte que le décompte reste toujours en direct.

Le problème est que la fonction que j'ai actuellement définie est lente et que vim est donc visiblement lente lorsqu'elle est utilisée pour tous les fichiers, à l'exception des plus petits. en raison de l'exécution si fréquente de cette fonction.

En résumé, quelqu’un at-il une astuce astucieuse pour produire une fonction extrêmement rapide pour calculer le nombre de mots dans la mémoire tampon actuelle et renvoyer le résultat?

Était-ce utile?

La solution

Voici une version utilisable de l’idée de Rodrigo Queiro. Cela ne change pas la barre d'état et restaure la variable 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

Cela semble être assez rapide pour inclure directement dans la ligne d'état, par exemple:

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

Autres conseils

J'aime beaucoup la réponse de Michael Dunn ci-dessus, mais j’ai constaté que lors de l’édition, cela me rendait incapable d’accéder à la dernière colonne. J'ai donc un changement mineur pour la fonction:

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

Je l'ai inclus dans ma ligne d'état sans problème:

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

Conservez un compte pour la ligne en cours et un compte séparé pour le reste de la mémoire tampon. Lorsque vous tapez (ou supprimez) des mots sur la ligne en cours, mettez à jour uniquement ce nombre, mais affichez la somme du nombre de lignes en cours et du reste du nombre de tampons.

Lorsque vous changez de ligne, ajoutez le nombre de lignes en cours au nombre de tampons, comptez les mots de la ligne en cours et a) définissez le nombre de lignes en cours et b) soustrayez-les du nombre de tampons.

Il serait également sage de recompter le tampon périodiquement (notez qu'il n'est pas nécessaire de compter tout le tampon à la fois, car vous savez où se déroule l'édition).

Ceci recalculera le nombre de mots chaque fois que vous arrêtez de taper pendant un certain temps (plus précisément, 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

Profitez!

J'ai donc écrit:

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

Mais il affiche les informations dans la barre d'état, je ne pense donc pas que cela conviendra à votre cas d'utilisation. C'est très rapide, cependant!

J'ai utilisé une approche légèrement différente pour cela. Plutôt que de m'assurer que la fonction de comptage de mots est particulièrement rapide, je ne l'appelle que lorsque le curseur ne bouge plus. Ces commandes le feront:

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

Peut-être pas tout à fait ce que le questionneur voulait, mais beaucoup plus simple que certaines des réponses fournies ici, et assez bon pour mon cas d'utilisation (regardez en bas pour voir le nombre de mots après avoir tapé une phrase ou deux).

Définir une valeur inférieure pour updatetime est également utile ici:

set updatetime=300

Il n’ya pas d’énorme interrogation pour le nombre de mots car CursorHold et CursorHoldI n’envoient que une fois lorsque le curseur ne bouge plus. chaque updatetime ms.

Voici un raffinement de la réponse d'Abslom Daak qui fonctionne également en mode visuel.

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

Inclus dans la ligne d'état comme précédemment. Voici une ligne d'état alignée à droite:

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

J'ai pris l'essentiel de cela dans les pages d'aide de vim sur les fonctions d'écriture.

function! WordCount()
  let lnum = 1
  let n = 0
  while lnum <= line('

Bien sûr, comme les autres, vous devrez:

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

Je suis sûr que cela peut être nettoyé par quelqu'un pour le rendre plus vimmy (s: n au lieu de n?), mais je pense que les fonctionnalités de base sont là.

Modifier:

En y repensant, j’aime beaucoup la solution de Mikael Jansson. Je n'aime pas appeler wc (non portable et peut-être lent). Si nous remplaçons sa fonction UpdateWordCount par le code que j'ai ci-dessus (renommer ma fonction en UpdateWordCount ), je pense que nous avons une meilleure solution.

) let n = n + len(split(getline(lnum))) let lnum = lnum + 1 endwhile return n endfunction

Bien sûr, comme les autres, vous devrez:

<*>

Je suis sûr que cela peut être nettoyé par quelqu'un pour le rendre plus vimmy (s: n au lieu de n?), mais je pense que les fonctionnalités de base sont là.

Modifier:

En y repensant, j’aime beaucoup la solution de Mikael Jansson. Je n'aime pas appeler wc (non portable et peut-être lent). Si nous remplaçons sa fonction UpdateWordCount par le code que j'ai ci-dessus (renommer ma fonction en UpdateWordCount ), je pense que nous avons une meilleure solution.

Ma suggestion:

function! UpdateWordCount()
  let b:word_count = eval(join(map(getline("1", "<*>quot;), "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)}'

Je ne suis pas sûr que la vitesse soit comparable à celle d'autres solutions, mais c'est certainement beaucoup plus simple que la plupart des autres.

Je suis nouveau dans les scripts Vim, mais je pourrais suggérer

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

comme étant un peu plus propre car il ne remplace pas la ligne d'état existante.

Mon but est de signaler que cette fonction a un bogue déroutant: elle interrompt la commande append. Si vous appuyez sur Un devrait vous placer en mode insertion avec le curseur placé à droite du dernier caractère de la ligne. Cependant, avec cette barre d'état personnalisée activée, vous serez placé à gauche du dernier caractère.

Quelqu'un a-t-il une idée de ce qui le cause?

Il s'agit d'une amélioration de la version de Michael Dunn , qui met en cache le nombre de mots, de sorte que le traitement est encore moins long.

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 

En utilisant la méthode de la réponse fournie par Steve Moyer, j'ai pu produire la solution suivante. Je crains fort que ce ne soit un bidouillage plutôt inélégant et j'estime qu'il doit exister une solution plus rationnelle, mais cela fonctionne et est beaucoup plus rapide que de simplement compter tous les mots dans une mémoire tampon chaque fois que la ligne d'état est mise à jour. Il convient également de noter que cette solution est indépendante de la plate-forme et ne suppose pas qu'un système dispose de «wc» ou d'un système similaire.

Ma solution ne met pas régulièrement à jour le tampon, mais la réponse fournie par Mikael Jansson serait en mesure de fournir cette fonctionnalité. Je n'ai pas encore trouvé de cas où ma solution ne serait plus synchronisée. Cependant, je n'ai que brièvement testé ceci, car un nombre de mots précis et réel n'est pas essentiel à mes besoins. Le modèle que j'utilise pour faire correspondre les mots est également simple et est destiné à de simples documents texte. Si quelqu'un a une meilleure idée d'un motif ou de toute autre suggestion, n'hésitez pas à poster une réponse ou à modifier ce message.

Ma solution:

"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("
:set statusline=wc:%{WordCount()}
quot;) let data = data + getline(line(".")+1, "<*>quot;) 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

Ceci est ensuite ajouté à la ligne d'état par:

<*>

J'espère que cela aidera tous ceux qui recherchent un décompte de mots en direct dans Vim. Bien que ce ne soit pas toujours exact. Sinon, bien sûr, ctrl-g vous fournira le nombre de mots de Vim!

Au cas où quelqu'un d'autre viendrait de Google, j'ai modifié la réponse d'Abslom Daak pour fonctionner avec la compagnie aérienne . J'ai enregistré ce qui suit sous

~ / .vim / bundle / vim-compagnie aérienne / autoload / compagnie aérienne / extensions / pandoc.vim

et ajouté

appeler la compagnie aérienne # 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

Une variante du raffinement de Guy Gur-Ari qui

  • ne compte les mots que si la vérification orthographique est activée,
  • compte le nombre de mots sélectionnés en mode visuel
  • reste muet en dehors des modes insertion et normal et
  • heureusement, est plus agnostique pour la langue du système (quand elle est différente de l'anglais)
function! StatuslineWordCount()
  if !&l:spell
    return ''
  endif

  if empty(getline(line('

pouvant être ajouté à la ligne d'état, par exemple,

    set statusline+=%.10{StatuslineWordCount()}     " wordcount
))) 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

pouvant être ajouté à la ligne d'état, par exemple,

<*>
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top