Функция быстрого подсчета слов в Vim - PullRequest
34 голосов
/ 22 сентября 2008

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

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

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

Ответы [ 13 ]

25 голосов
/ 03 января 2011

Мне очень нравится ответ Майкла Данна выше, но я обнаружил, что когда я редактировал его, я не мог получить доступ к последнему столбцу. Итак, у меня есть небольшое изменение для функции:

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

23 голосов
/ 16 февраля 2009

Вот полезная версия идеи Родриго Кейро. Он не меняет строку состояния и восстанавливает переменную 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

Это кажется достаточно быстрым для непосредственного включения в строку состояния, например ::100100

:set statusline=wc:%{WordCount()}
9 голосов
/ 22 сентября 2008

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

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

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

4 голосов
/ 22 сентября 2008

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

Наслаждайтесь!

3 голосов
/ 22 сентября 2008

Итак, я написал:

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

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

2 голосов
/ 19 сентября 2013

Вот уточнение ответа Абслома Даака, которое также работает в визуальном режиме.

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--'
    <strong>if stat =~ "^Selected"
      let s:word_count = str2nr(split(v:statusmsg)[5])
    else
      let s:word_count = str2nr(split(v:statusmsg)[11])
    end</strong>
    let v:statusmsg = s:old_status
  end
  call setpos('.', position)
  return s:word_count 
endfunction

Включено в строку состояния, как и раньше. Вот строка состояния, выровненная по правому краю:

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

2 голосов
/ 24 ноября 2012

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

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

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

Установка updatetime на меньшее значение также помогает здесь:

set updatetime=300

Не существует большого опроса для подсчета слов, потому что CursorHold и CursorHoldI только запускают один раз , когда курсор перестает двигаться, не каждые updatetime мс.

1 голос
/ 16 августа 2012

Это улучшение версии Майкла Данна , кэширующее количество слов, поэтому требуется еще меньше обработки.

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 
1 голос
/ 08 января 2012

Я новичок в скриптах Vim, но могу предложить

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

немного чище, поскольку не перезаписывает существующую строку состояния.

Моя причина публикации состоит в том, чтобы указать, что у этой функции есть загадочная ошибка: она прерывает команду добавления. При нажатии A вы попадете в режим вставки с курсором, расположенным справа от последнего символа в строке. Однако, если эта пользовательская строка состояния включена, она будет слева от финального символа.

Кто-нибудь знает, что это вызывает?

1 голос
/ 11 мая 2010

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...