سؤال

أحاول عرض عدد الكلمات المباشر في سطر الحالة vim.أفعل ذلك عن طريق تعيين سطر الحالة الخاص بي في ملف .vimrc الخاص بي وإدراج وظيفة فيه.فكرة هذه الوظيفة هي إرجاع عدد الكلمات الموجودة في المخزن المؤقت الحالي.ثم يتم عرض هذا الرقم على سطر الحالة.من المفترض أن يعمل هذا بشكل جيد حيث يتم تحديث خط الحالة في كل فرصة ممكنة تقريبًا بحيث يظل العدد دائمًا "مباشرًا".

تكمن المشكلة في أن الوظيفة التي حددتها حاليًا بطيئة ومن الواضح أن vim تكون بطيئة عند استخدامها لجميع الملفات باستثناء أصغرها؛بسبب تنفيذ هذه الوظيفة بشكل متكرر.

باختصار، هل لدى أي شخص خدعة ذكية لإنتاج دالة سريعة للغاية في حساب عدد الكلمات في المخزن المؤقت الحالي وإرجاع النتيجة؟

هل كانت مفيدة؟

المحلول

إليكم نسخة قابلة للاستخدام من فكرة رودريغو كويرو.لا يغير شريط الحالة، بل يستعيد متغير 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()}

نصائح أخرى

تعجبني حقًا إجابة مايكل دن أعلاه ولكنني وجدت أنه عندما كنت أقوم بالتحرير كان ذلك يتسبب في عدم تمكني من الوصول إلى العمود الأخير.لذلك لدي تغيير طفيف للوظيفة:

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

احتفظ بعدد للسطر الحالي وعدد منفصل لبقية المخزن المؤقت.أثناء قيامك بكتابة (أو حذف) الكلمات في السطر الحالي، قم بتحديث هذا العدد فقط، ولكن قم بعرض مجموع عدد السطر الحالي وبقية عدد المخزن المؤقت.

عندما تقوم بتغيير الأسطر، قم بإضافة عدد الأسطر الحالي إلى عدد المخزن المؤقت، وقم بعد الكلمات الموجودة في السطر الحالي وأ) قم بتعيين عدد الأسطر الحالي و ب) اطرحه من عدد المخزن المؤقت.

سيكون من الحكمة أيضًا إعادة حساب المخزن المؤقت بشكل دوري (لاحظ أنه ليس عليك حساب المخزن المؤقت بالكامل مرة واحدة، نظرًا لأنك تعرف مكان إجراء التحرير).

سيؤدي هذا إلى إعادة حساب عدد الكلمات كلما توقفت عن الكتابة لفترة من الوقت (على وجه التحديد، 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

يتمتع!

لذلك كتبت:

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

ولكنها تطبع المعلومات على شريط الحالة، لذلك لا أعتقد أنها ستكون مناسبة لحالة الاستخدام الخاصة بك.إنه سريع جدًا، رغم ذلك!

لقد استخدمت نهجا مختلفا قليلا لهذا الغرض.بدلاً من التأكد من أن وظيفة عدد الكلمات سريعة بشكل خاص، أقوم باستدعائها فقط عندما يتوقف المؤشر عن الحركة.هذه الأوامر سوف تفعل ذلك:

: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--'
    إذا كان STAT = ~ "^محدد" Let S: Word_Count = str2nr (split (v: statusMsg) [5]) آخر دع S: word_count = str2NR
    let v:statusmsg = s:old_status
  end
  call setpos('.', position)
  return s:word_count 
endfunction

المدرجة في سطر الحالة كما كان من قبل.فيما يلي سطر الحالة بمحاذاة إلى اليمين:

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

لقد أخذت الجزء الأكبر من هذا من صفحات المساعدة الخاصة بوظائف الكتابة.

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

لست متأكدًا من مدى سرعة مقارنة هذا ببعض الحلول الأخرى، ولكنه بالتأكيد أبسط كثيرًا من معظم الحلول.

أنا جديد في البرمجة النصية لـ Vim، لكن قد أقترح ذلك

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

باعتباره أكثر نظافة بعض الشيء لأنه لا يحل محل سطر الحالة الحالي.

سبب النشر هو الإشارة إلى أن هذه الوظيفة بها خطأ محير:أي أنه يكسر أمر الإلحاق.ضرب أ يجب أن ينقلك إلى وضع الإدراج مع وضع المؤشر على يمين الحرف الأخير على السطر.ومع ذلك، مع تمكين شريط الحالة المخصص هذا، فإنه سيضعك على يسار الحرف الأخير.

هل لدى أي شخص أي فكرة عن سبب هذا؟

وهذا تحسن على نسخة مايكل دن, ، تخزين عدد الكلمات مؤقتًا، لذلك هناك حاجة إلى معالجة أقل.

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 

باستخدام الطريقة الموجودة في الإجابة التي قدمها ستيف موير، تمكنت من إنتاج الحل التالي.أخشى أن هذا اختراق غير أنيق إلى حد ما، وأشعر أنه يجب أن يكون هناك حل أكثر دقة، ولكنه يعمل، وهو أسرع بكثير من مجرد حساب كل الكلمات في المخزن المؤقت في كل مرة يتم فيها تحديث سطر الحالة.يجب أن أشير أيضًا إلى أن هذا الحل مستقل عن النظام الأساسي ولا يفترض أن النظام يحتوي على "مرحاض" أو شيء مشابه.

الحل الخاص بي لا يقوم بتحديث المخزن المؤقت بشكل دوري، ولكن الإجابة المقدمة من 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!

في حالة قدوم شخص آخر إلى هنا من Google، قمت بتعديل إجابة Abslom Daak للعمل معها شركة طيران.لقد قمت بحفظ ما يلي باسم

~/.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

نسخة مختلفة من جاي جور آري التنقيح الذي - التي

  • لا يحسب الكلمات إلا في حالة تمكين التدقيق الإملائي،
  • يحسب عدد الكلمات المحددة في الوضع المرئي
  • يحافظ على كتم الصوت خارج وضع الإدخال والوضع العادي، و
  • نأمل أن تكون أكثر حيادًا عن لغة النظام (عندما تكون مختلفة عن اللغة الإنجليزية)
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
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top