2008-09-22 7 views
31

में फास्ट वर्ड गिनती फ़ंक्शन मैं विम स्टेटसलाइन में लाइव शब्द गणना प्रदर्शित करने का प्रयास कर रहा हूं। मैं अपने .vimrc में अपनी स्थिति रेखा सेट करके और इसमें एक फ़ंक्शन डालने से ऐसा करता हूं। इस फ़ंक्शन का विचार वर्तमान बफर में शब्दों की संख्या को वापस करना है। यह संख्या तब स्थिति रेखा पर प्रदर्शित होती है। यह अच्छी तरह से काम करना चाहिए क्योंकि स्टेटसलाइन को हर संभव अवसर पर अपडेट किया गया है, इसलिए गिनती हमेशा 'लाइव' रहेगी।विम

समस्या यह है कि जो फ़ंक्शन मैंने वर्तमान में परिभाषित किया है वह धीमा है और इसलिए यह स्पष्ट रूप से सुस्त है जब यह सभी छोटी फ़ाइलों के लिए उपयोग किया जाता है; इस समारोह को इतनी बार निष्पादित किया जा रहा है।

संक्षेप में, क्या किसी के पास एक ऐसे फ़ंक्शन का निर्माण करने के लिए एक चालाक चाल है जो वर्तमान बफर में शब्दों की संख्या की गणना करने और परिणाम लौटने पर तेज गति से तेज है?

+0

अपने वर्तमान कार्य क्या है करने के लिए

call airline#extensions#pandoc#init(s:ext)

जोड़ा? –

+10

सामान्य शब्द गणना के लिए यहां आने वाले अन्य लोगों के लिए, 'g Ctrl-g' का उपयोग करें। – naught101

उत्तर

23

यहां रोड्रिगो क्विरो के विचार का एक उपयोगी संस्करण है। यह स्टेटस बार नहीं बदलता है, और यह 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()} 
+0

एस/एस: // जी; या एस/एस:/एल:/जी अगर आप वास्तव में एक स्पष्ट दायरा चाहते हैं। –

+4

एफडब्ल्यूआईडब्ल्यू, यह उबंटू 12.04 पर विम 7.3 के साथ काम नहीं करता है। मैं लाइन के अंत में शामिल नहीं हो सकता! यही है, दोनों "ए" और "ए" (बाद में एक पंक्ति में अंतिम चरित्र पर) कर्सर को अंतिम चरित्र से परे नहीं छोड़ते हैं, लेकिन सीधे इसके सामने। मैं कल्पना नहीं कर सकता क्यों। – chreekat

8

वर्तमान लाइन के लिए एक गिनती और बफर के बाकी के लिए एक अलग गिनती रखें। जैसे ही आप वर्तमान पंक्ति पर शब्दों को टाइप (या हटाएं) करते हैं, केवल उस गिनती को अपडेट करें, लेकिन वर्तमान लाइन गिनती और बाकी बफर गिनती का योग प्रदर्शित करें।

जब आप रेखाएं बदलते हैं, तो बफर गिनती में वर्तमान लाइन गिनती जोड़ें, वर्तमान लाइन में शब्दों को गिनें और ए) वर्तमान लाइन गिनती सेट करें और बी) इसे बफर गिनती से घटाएं।

यह भी (, ध्यान दें कि आप एक ही बार में पूरे बफर गिनती करने के लिए की जरूरत नहीं है कि जब आप जानते हैं कि जहां संपादन हो रहा है) समय-समय पर बफर बयान करने में बुद्धिमान होगा।

3

तो मैं लिखा है:

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

लेकिन यह स्थिति बार को जानकारी बाहर प्रिंट, इसलिए मुझे नहीं लगता कि यह आपके यूज-केस के लिए उपयुक्त हो जाएगा। हालांकि, यह बहुत तेज़ है!

4

जब भी आप थोड़ी देर के लिए टाइप करना बंद कर देंगे तो यह शब्दों की संख्या को फिर से गणना करेगा (विशेष रूप से, 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 

आनंद लें!

0

स्टीव मॉयर द्वारा प्रदान की जवाब में विधि का उपयोग मैं निम्नलिखित समाधान का उत्पादन करने में सक्षम था। यह एक नहीं बल्कि असजीला हैक मुझे डर लग रहा है और मुझे लगता है कि वहाँ एक neater समाधान होना चाहिए है, लेकिन यह काम करता है, और बस एक बफर में हर बार स्थिति लाइन अद्यतन किया जाता है शब्दों के सभी गिनती की तुलना में बहुत तेजी से होता है। मुझे यह भी ध्यान रखना चाहिए कि यह समाधान मंच स्वतंत्र है और यह नहीं मानता कि एक प्रणाली में 'wc' या कुछ समान है।

मेरे समाधान समय-समय पर बफर को अद्यतन नहीं है, लेकिन माइकल जैनसन द्वारा प्रदान की जवाब यह सुविधा प्रदान करने में सक्षम होगा। मैंने अभी तक एक उदाहरण नहीं पाया है जहां मेरा समाधान सिंक से बाहर हो जाता है। हालांकि मैंने केवल संक्षेप में इसका परीक्षण किया है क्योंकि मेरी ज़रूरतों के लिए एक सटीक लाइव शब्द गणना आवश्यक नहीं है। मिलान करने वाले शब्दों के लिए मैं जिस पैटर्न का उपयोग करता हूं वह भी सरल है और सरल टेक्स्ट दस्तावेज़ों के लिए है। अगर किसी के पास पैटर्न या किसी अन्य सुझाव के लिए बेहतर विचार है, तो कृपया उत्तर पोस्ट करने या इस पोस्ट को संपादित करने के लिए स्वतंत्र महसूस करें।

मेरे समाधान:

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

मुझे आशा है कि इस विम में एक जीवित शब्द गणना की तलाश में किसी को भी मदद करता है। यद्यपि वह हमेशा सटीक नहीं है।वैकल्पिक रूप से जी ctrl-g आपको विम की शब्द गणना प्रदान करेगा!

1

मैं कार्यों लेखन पर 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 
बेशक

, दूसरों की तरह, आप की आवश्यकता होगी करने के लिए: n बस के बजाय:

:set statusline=wc:%{WordCount()} 

मुझे यकीन है कि यह इसे और अधिक vimmy (रों बनाने के लिए किसी ने साफ किया जा सकता हूँ एन?), लेकिन मेरा मानना ​​है कि बुनियादी कार्यक्षमता वहां है।

संपादित करें:

इस को देखते हुए फिर से, मैं वास्तव में माइकल जैनसन के समाधान की तरह। मुझे wc (पोर्टेबल और शायद धीमी नहीं) तक शेलिंग पसंद नहीं है। यदि हम ऊपर दिए गए कोड के साथ अपने UpdateWordCount फ़ंक्शन को प्रतिस्थापित करते हैं (मेरे फ़ंक्शन को UpdateWordCount पर नामित करना), तो मुझे लगता है कि हमारे पास एक बेहतर समाधान है।

+0

स्थानीय चर स्थानीय रहना चाहिए। एस: चर स्क्रिप्ट-स्थानीय वैश्विक चर हैं, जैसे सी में स्थिर चर जैसे। यदि आप चाहते हैं, तो आप + = का उपयोग कर सकते हैं। आप यह भी कर सकते हैं: गेटलाइन (1, '$') पर foreach, लेकिन मुझे नहीं पता कि कौन सा समाधान तेज है। –

1

मेरे सुझाव:

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

मुझे यकीन है कि यह कैसे अन्य समाधान के कुछ करने के लिए गति में तुलना नहीं कर रहा हूँ, लेकिन यह निश्चित रूप से सबसे अधिक से एक बहुत आसान है।

24

मुझे वास्तव में माइकल डन के उत्तर को पसंद है लेकिन मुझे पता चला कि जब मैं संपादन कर रहा था तो यह मुझे अंतिम कॉलम तक पहुंचने में असमर्थ रहा। इसलिए मैं समारोह के लिए एक छोटा सा परिवर्तन किया है:

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

+0

हां, यह स्वीकार्य समाधान होना चाहिए। :) – chreekat

+2

यहां एक छोटा टाइपो है। पहली बोल्ड लाइन के नीचे की रेखा को 'exe' पढ़ना चाहिए: इसके बजाए चुप सामान्य जी \ "'। –

+1

@ जैकफ्रैंकलिन यह एक टाइपो नहीं था। ओपी ने अपने मार्कडाउन में हाथ से कोडित एचटीएमएल का इस्तेमाल किया था (कोड के एक वर्ग के भीतर बोल्ड बनाने के लिए), और एसओ के प्रतिपादन इंजन ने बुरी तरह प्रतिक्रिया दी थी। मैंने समस्या को ठीक करने के लिए HTML को हटा दिया है। यह नोट करने के लिए धन्यवाद कि कोड गलत लग रहा था। – Telemachus

1

मैं विम पटकथा के लिए नया हूँ, लेकिन मैं हो सकता है सुझाव

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

थोड़ा क्लीनर होने के कारण से यह मौजूदा स्थिति रेखा को ओवरराइट नहीं करता है।

पोस्टिंग का मेरा कारण यह इंगित करना है कि इस फ़ंक्शन में एक गूढ़ बग है: अर्थात्, यह एपेंड कमांड को तोड़ देता है। मारना आपको लाइन पर अंतिम चरित्र के दाईं ओर स्थित कर्सर के साथ सम्मिलित मोड में छोड़ देना चाहिए। हालांकि, इस कस्टम स्टेटस बार सक्षम होने के साथ यह आपको अंतिम चरित्र के बाईं ओर रखेगा।

किसी को भी यह पता है कि इसका क्या कारण है?

1

यह Michael Dunn's version पर एक सुधार है, शब्द गणना को कैश करना ताकि कम प्रोसेसिंग की आवश्यकता हो।

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 
2

मैंने इसके लिए थोड़ा अलग दृष्टिकोण उपयोग किया।यह सुनिश्चित करने के बजाय कि शब्द गणना फ़ंक्शन विशेष रूप से तेज़ है, मैं कर्सर बंद होने पर ही इसे कॉल करता हूं। इन आदेशों यह करना होगा:

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

शायद काफी नहीं क्या प्रश्नकर्ता चाहता था, लेकिन जवाब यहां के कुछ तुलना में बहुत सरल है, और नीचे मेरी यूज-केस के लिए काफी अच्छा (नज़र एक वाक्य लिखने के बाद शब्द गणना देखने के लिए या दो)।

एक छोटे मूल्य के लिए updatetime स्थापना भी यहाँ मदद करता है:

set updatetime=300 

वहाँ गिनती शब्द के लिए एक बहुत बड़ा भूमि के ऊपर मतदान नहीं है क्योंकि CursorHold और CursorHoldI केवल आग एक बार कर्सर ले जाकर, हर नहीं बंद हो जाता है जब updatetime एमएस।

+0

मैंने इस पृष्ठ पर कई फ़ंक्शंस की कोशिश की लेकिन लिनक्स पर मेरे जीवीम (संस्करण 8.0.550) में काम करने वाला एकमात्र ऐसा 3-लाइन उत्तर मिला। – rnso

2

यहां अबस्लोम दाक के उत्तर का एक परिष्करण है जो दृश्य मोड में भी काम करता है।

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\

0

मामले में किसी और गूगल से यहां आ रहा है, मैं Airline के साथ काम करने Abslom Daak के जवाब संशोधित। मैं के रूप में

~/.vim/bundle/vim-airline/autoload/airline/extensions/pandoc.vim

निम्नलिखित बचाया और 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