pegar - Función de conteo rápido de palabras en Vim
vim linux (13)
Aquí hay un refinamiento de la respuesta de Abslom Daak que también funciona en modo visual.
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
Incluido en la línea de estado como antes. Aquí hay una línea de estado alineada a la derecha:
set statusline=%=%{WordCount()}/ words/
Estoy tratando de mostrar un recuento de palabras en vivo en la línea de estado de vim. Lo hago estableciendo mi línea de estado en mi .vimrc e insertando una función en ella. La idea de esta función es devolver el número de palabras en el búfer actual. Este número se muestra en la línea de estado. Esto debería funcionar bien ya que la línea de estado se actualiza en casi todas las oportunidades posibles, por lo que el recuento siempre permanecerá "en vivo".
El problema es que la función que tengo definida actualmente es lenta y, por tanto, vim es obviamente lenta cuando se usa para todos los archivos, excepto para los más pequeños; debido a que esta función se ejecuta con tanta frecuencia.
En resumen, ¿alguien tiene un truco ingenioso para producir una función que es tremendamente rápida al calcular el número de palabras en el búfer actual y devolver el resultado?
Aquí hay una versión utilizable de la idea de Rodrigo Queiro. No cambia la barra de estado, y restaura 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
Esto parece ser lo suficientemente rápido como para incluirlo directamente en la línea de estado, por ejemplo:
:set statusline=wc:%{WordCount()}
Así que he escrito:
func CountWords() exe "normal g/" let words = substitute(v:statusmsg, "^.*Word [^ ]* of ", "", "") let words = substitute(words, ";.*", "", "") return words endfunc
Pero imprime información en la barra de estado, por lo que no creo que sea adecuada para su caso de uso. ¡Es muy rápido, sin embargo!
En caso de que alguien más venga desde Google, modifiqué la respuesta de Abslom Daak para trabajar con Airline . Guardé lo siguiente como
~/.vim/bundle/vim-airline/autoload/airline/extensions/pandoc.vim
y agregado
call airline#extensions#pandoc#init(s:ext)
a 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
Esta es una mejora en la versión de Michael Dunn , almacenando en caché el conteo de palabras, por lo que aún se necesita menos procesamiento.
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
Esto recalculará el número de palabras cada vez que deje de escribir durante un tiempo (específicamente, 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
¡Disfrutar!
Mantenga un conteo para la línea actual y un conteo separado para el resto del buffer. A medida que escribe (o elimina) palabras en la línea actual, actualice solo ese conteo, pero muestre la suma del conteo de línea actual y el resto del recuento de memoria intermedia.
Cuando cambie líneas, agregue el recuento de línea actual al recuento de memoria intermedia, cuente las palabras en la línea actual y a) establezca el recuento de línea actual y b) reste de la cuenta de memoria intermedia.
También sería aconsejable volver a contar el búfer de forma periódica (tenga en cuenta que no tiene que contar todo el búfer de una vez, ya que sabe dónde se está produciendo la edición).
Me gustó mucho la respuesta de Michael Dunn, pero descubrí que cuando editaba no podía acceder a la última columna. Así que tengo un cambio menor para la función:
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
Lo he incluido en mi línea de estado sin ningún problema:
:set statusline=wc:%{WordCount()}
Mi sugerencia:
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)}''
No estoy seguro de cómo se compara en velocidad con algunas de las otras soluciones, pero ciertamente es mucho más simple que la mayoría.
Soy nuevo en el scripting de Vim, pero podría sugerir
function WordCount()
redir => l:status
exe "silent normal g/<c-g>"
redir END
return str2nr(split(l:status)[11])
endfunction
como un poco más limpio ya que no sobrescribe la línea de estado existente.
Mi razón para publicar es señalar que esta función tiene un error desconcertante: es decir, rompe el comando de agregar. Al presionar A debe colocarse en el modo de inserción con el cursor colocado a la derecha del carácter final en la línea. Sin embargo, con esta barra de estado personalizada habilitada, te pondrá a la izquierda del personaje final.
Alguien tiene alguna idea de lo que hace esto?
Tomé la mayor parte de esto de las páginas de ayuda de vim sobre las funciones de escritura.
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
Por supuesto, como los demás, necesitarás:
:set statusline=wc:%{WordCount()}
Estoy seguro de que esto puede ser limpiado por alguien para que sea más vimmy (s: n en lugar de simplemente n?), Pero creo que la funcionalidad básica está ahí.
Editar:
Al ver esto de nuevo, realmente me gusta la solución de Mikael Jansson. No me gusta bombardear a wc
(no portátil y quizás lento). Si reemplazamos su función UpdateWordCount
con el código que tengo arriba (renombrando mi función a UpdateWordCount
), entonces creo que tenemos una mejor solución.
Usé un enfoque ligeramente diferente para esto. En lugar de asegurarme de que la función de conteo de palabras sea especialmente rápida, solo la invoco cuando el cursor deja de moverse. Estos comandos lo harán:
:au CursorHold * exe "normal g/<c-g>"
:au CursorHoldI * exe "normal g/<c-g>"
Tal vez no exactamente lo que quería el interrogador, pero es mucho más simple que algunas de las respuestas aquí, y lo suficientemente bueno para mi caso de uso (mire hacia abajo para ver el recuento de palabras después de escribir una o dos oraciones).
Establecer el tiempo de updatetime
a un valor más pequeño también ayuda aquí:
set updatetime=300
No hay un gran CursorHold
aéreo para el recuento de palabras porque CursorHold
y CursorHoldI
solo disparan una vez cuando el cursor deja de moverse, no cada updatetime
ms.
Usando el método en la respuesta proporcionada por Steve Moyer, pude producir la siguiente solución. Es un truco bastante poco elegante, me temo, y creo que debe haber una solución más ordenada, pero funciona, y es mucho más rápido que simplemente contar todas las palabras en un buffer cada vez que se actualiza la línea de estado. También debo señalar que esta solución es independiente de la plataforma y no supone que un sistema tenga ''wc'' o algo similar.
Mi solución no actualiza periódicamente el búfer, pero la respuesta proporcionada por Mikael Jansson podría proporcionar esta funcionalidad. Hasta el momento, no he encontrado una instancia en la que mi solución no esté sincronizada. Sin embargo, solo he probado esto brevemente, ya que un conteo exacto de palabras en vivo no es esencial para mis necesidades. El patrón que uso para emparejar palabras también es simple y está destinado a documentos de texto simples. Si alguien tiene una mejor idea para un patrón o cualquier otra sugerencia, no dude en publicar una respuesta o editar esta publicación.
Mi solución:
"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
Esto luego se agrega a la línea de estado al:
:set statusline=wc:%{WordCount()}
Espero que esto ayude a cualquiera que esté buscando un recuento de palabras en vivo en Vim. Aunque uno que no siempre es exacto. ¡Alternativamente, por supuesto, g ctrl-g le proporcionará el recuento de palabras de Vim!