Быстрая функция для подсчета количества слов в Vim

Я пытаюсь отобразить живой подсчет слов в энергии statusline. Я делаю это путем установки моей строки состояния в моем .vimrc и вставки функции в него. Идея этой функции состоит в том, чтобы возвратить количество слов в текущем буфере. Это число тогда отображено на строке состояния. Это должно работать приятно, поскольку statusline обновляется в примерно каждой возможной возможности, таким образом, количество будет всегда оставаться 'живым'.

Проблема состоит в том, что функция, которую я в настоящее время определял, является медленной и таким образом, энергия очевидно вяла, когда она используется для всех кроме самых маленьких файлов; из-за этой функции, выполняемой так часто.

Таким образом, у кого-либо есть умный прием для создания функции, которая ослепительно быстра при вычислении количества слов в текущем буфере и возврате результата?

34
задан Greg Sexton 22 September 2008 в 22:58
поделиться

7 ответов

Вот применимая версия идеи 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()}
23
ответ дан 27 November 2019 в 16:34
поделиться

Сохраните счет для текущей строки и отдельного счета для остальной части буфера. Поскольку Вы вводите (или удаляете), слова на текущей строке, обновите только, что количество, но отображают сумму текущего количества строки и остальной части числа буферов.

при изменении строк добавьте текущее количество строки к числу буферов, считайте слова в текущей строке и a) набор текущее количество строки и b) вычтите его из числа буферов.

также было бы мудро рассказать буфер периодически (обратите внимание, что Вы не должны считать целый буфер сразу, так как Вы знаете, где редактирование происходит).

9
ответ дан Steve Moyer 24 September 2019 в 07:02
поделиться

Таким образом, я записал:

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

, Но это распечатывает информацию к строке состояния, таким образом, я не думаю, что это подойдет для Вашего примера использования. Это очень быстро, хотя!

3
ответ дан Rodrigo Queiro 24 September 2019 в 07:02
поделиться

Это повторно вычислит количество слов каждый раз, когда Вы прекращаете вводить некоторое время (а именно, 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

Обладают!

4
ответ дан Mikael Jansson 24 September 2019 в 07:02
поделиться

Используя метод в ответе, предоставленном 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!

0
ответ дан Greg Sexton 24 September 2019 в 07:02
поделиться

Я взял большую часть этого из справочных страниц 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 ), то я думаю, что у нас есть лучшее решение.

1
ответ дан 27 November 2019 в 16:34
поделиться

Мое предложение:

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

Я не уверен, что это сравнится по скорости с некоторыми другими решениями, но это определенно намного проще, чем большинство.

1
ответ дан 27 November 2019 в 16:34
поделиться
Другие вопросы по тегам:

Похожие вопросы: