Я пытаюсь отобразить живой подсчет слов в энергии statusline. Я делаю это путем установки моей строки состояния в моем .vimrc и вставки функции в него. Идея этой функции состоит в том, чтобы возвратить количество слов в текущем буфере. Это число тогда отображено на строке состояния. Это должно работать приятно, поскольку statusline обновляется в примерно каждой возможной возможности, таким образом, количество будет всегда оставаться 'живым'.
Проблема состоит в том, что функция, которую я в настоящее время определял, является медленной и таким образом, энергия очевидно вяла, когда она используется для всех кроме самых маленьких файлов; из-за этой функции, выполняемой так часто.
Таким образом, у кого-либо есть умный прием для создания функции, которая ослепительно быстра при вычислении количества слов в текущем буфере и возврате результата?
Вот применимая версия идеи 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) вычтите его из числа буферов.
также было бы мудро рассказать буфер периодически (обратите внимание, что Вы не должны считать целый буфер сразу, так как Вы знаете, где редактирование происходит).
Таким образом, я записал:
func CountWords() exe "normal g\" let words = substitute(v:statusmsg, "^.*Word [^ ]* of ", "", "") let words = substitute(words, ";.*", "", "") return words endfunc
, Но это распечатывает информацию к строке состояния, таким образом, я не думаю, что это подойдет для Вашего примера использования. Это очень быстро, хотя!
Это повторно вычислит количество слов каждый раз, когда Вы прекращаете вводить некоторое время (а именно, updatetime
мс).
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
Обладают!
Используя метод в ответе, предоставленном 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!
Я взял большую часть этого из справочных страниц 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?), Но я считаю, что базовая функциональность есть.
Редактировать:
Глядя на это снова, мне очень нравится решение Микаэля Янссона. Я не люблю обстреливать 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)}'
Я не уверен, что это сравнится по скорости с некоторыми другими решениями, но это определенно намного проще, чем большинство.