我正在尝试在 vim 状态行中显示实时字数。为此,我在 .vimrc 中设置状态行并在其中插入一个函数。这个函数的想法是返回当前缓冲区中的单词数。这个数字随后会显示在状态行上。这应该可以很好地工作,因为几乎每一个可能的机会都会更新状态行,因此计数将始终保持“实时”。
问题是我当前定义的函数很慢,所以当 vim 用于除最小文件之外的所有文件时,它显然很慢;由于此功能执行如此频繁。
总而言之,有没有人有一个聪明的技巧来生成一个函数,该函数在计算当前缓冲区中的单词数并返回结果时非常快?
我正在尝试在 vim 状态行中显示实时字数。为此,我在 .vimrc 中设置状态行并在其中插入一个函数。这个函数的想法是返回当前缓冲区中的单词数。这个数字随后会显示在状态行上。这应该可以很好地工作,因为几乎每一个可能的机会都会更新状态行,因此计数将始终保持“实时”。
问题是我当前定义的函数很慢,所以当 vim 用于除最小文件之外的所有文件时,它显然很慢;由于此功能执行如此频繁。
总而言之,有没有人有一个聪明的技巧来生成一个函数,该函数在计算当前缓冲区中的单词数并返回结果时非常快?
我真的很喜欢上面迈克尔邓恩的回答,但我发现当我编辑它导致我无法访问最后一列。所以我对函数做了一个小改动:
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()}
这是 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()}
为当前行保留一个计数,为缓冲区的其余部分保留一个单独的计数。当您在当前行键入(或删除)单词时,仅更新该计数,但显示当前行计数和其余缓冲区计数的总和。
当您更改行时,将当前行数添加到缓冲区计数中,计算当前行中的字数,然后 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
享受!
所以我写了:
函数计数字() exe“正常g\” 让单词=替换(v:statusmsg,“^.*单词[^]* of”,“”,“”) 让单词 = 替代(单词,“;.*”,“”,“”) 回话 结束函数
但它会将信息打印到状态栏,所以我认为它不适合您的用例。不过,它非常快!
为此,我使用了稍微不同的方法。我没有确保字数统计功能特别快,而是仅在光标停止移动时调用它。这些命令将执行此操作:
:au CursorHold * exe "normal g\<c-g>"
:au CursorHoldI * exe "normal g\<c-g>"
也许不是提问者想要的,但比这里的一些答案要简单得多,而且对于我的用例来说已经足够好了(在输入一两个句子后向下看字数)。
设置updatetime
为较小的值在这里也有帮助:
set updatetime=300
字数统计没有巨大的开销轮询,因为CursorHold
并且CursorHoldI
仅在光标停止移动时触发一次updatetime
,而不是每毫秒触发一次。
这是对 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\
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 帮助页面中获取了大部分内容。
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()}
我确信有人可以清理它以使其更具活力(s:n 而不仅仅是 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
因为它不会覆盖现有的状态行,所以更清洁。
我发帖的原因是指出这个函数有一个令人费解的错误:即它破坏了 append 命令。点击A应该让您进入插入模式,光标位于该行最后一个字符的右侧。但是,启用此自定义状态栏后,它会将您置于最后一个字符的左侧。
有人知道是什么原因造成的吗?
这是对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
由于 vim 现在本机支持此功能:
:echo wordcount().words
使用史蒂夫莫耶提供的答案中的方法,我能够产生以下解决方案。恐怕这是一个相当不雅的hack,我觉得必须有一个更简洁的解决方案,但它有效,并且比每次更新状态行时简单地计算缓冲区中的所有单词要快得多。我还应该注意,此解决方案与平台无关,并且不假定系统具有“wc”或类似的东西。
我的解决方案不会定期更新缓冲区,但 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 的字数统计!
万一其他人从谷歌来到这里,我修改了 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
Guy Gur-Ari改进的一种变体
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
基于https://stackoverflow.com/a/60310471/11001018,我的建议是:
"new in vim 7.4.1042
let g:word_count=wordcount().words
function WordCount()
if has_key(wordcount(),'visual_words')
let g:word_count=wordcount().visual_words."/".wordcount().words
else
let g:word_count=wordcount().cursor_words."/".wordcount().words
endif
return g:word_count
endfunction
进而:
set statusline+=\ w:%{WordCount()},