2011-12-21 37 views
13

所以我有這樣一個文本文件調用函數一次:Vim的腳本 - 所有選定行

Item a: <total> 
    Subitem: 10 min 
    Subitem 2: 20 min 

我想和總量的10和20,以取代<total>現在我做的它具有以下功能:

let g:S = 0 "result in global variable S 
function! Sum(number) 
    let g:S = g:S + a:number 
    return a:number 
endfunction 

function! SumSelection() 
    let g:S=0 
    '<,'>s/\d\+/\=Sum(submatch(0))/g 
    echo g:S 
endfunction 

vnoremap <s-e> call SumSelection()<cr> 

Sum得到傳遞數字的總和,SumSelection在選定線路要求在所有的數字之和,(據說)Shift + E在視覺模式調用SumSelection(或者不管你選擇叫它。)

問題是,當我點擊Shift + e時,我有一些行選擇,而不是:call SumSelection()我真的得到:'<,'>call SumSelection()這意味着函數被調用一次選定的行。不好,對吧?所以據我所知,這是無法解決的。我能做些什麼來獲得功能:

  1. 只調用一次
  2. 也許在一個更有效的方式

回答

18

孔的總這些,有一個功能,在一定範圍內只執行一次,附加range關鍵字。例如。

fun Foo() range 
    ... 
endfun 

然後你的函數可以照顧範圍本身具有的特殊參數:FIRSTLINE 和:lastLine所。 (見:help a:firstline瞭解詳細信息。)

但是我認爲,在這種情況下,你的需求只是簡單到足以與 完成使用:global一個班輪。

我們可以做更好的指定輸入和輸出。不過,假設含有

Item a: <total> 
    Subitem: 10 min 
    Subitem 2: 20 min 
Item b: <total> 
    Subitem: 10 min 
    Subitem 2: 23 min 
Item c: <total> 
    Subitem: 10 min 
    Subitem 2: 23 min 
    Subitem 3: 43 min 

一個文件和一個函數定義爲

fun! Sum(n) 
    let g:s += a:n 
    return a:n 
endfun 

那麼這將總結的子項(所有一行)

:g/^Item/ let g:s = 0 | mark a | +,-/\nItem\|\%$/ s/\v(\d+)\ze\s+min$/\=Sum(submatch(0))/g | 'a s/<total>/\=(g:s . ' min')/

和產生這個輸出

Item a: 30 min 
    Subitem: 10 min 
    Subitem 2: 20 min 
Item b: 33 min 
    Subitem: 10 min 
    Subitem 2: 23 min 
Item c: 76 min 
    Subitem: 10 min 
    Subitem 2: 23 min 
    Subitem 3: 43 min 

順便說一句,上面的命令作用於整個緩衝區。如果首先突出顯示一個範圍,然後點擊:鍵,vim將自動將您的命令預置爲'<,'>,這意味着以下命令將從突出顯示的範圍的開始到結束進行操作。

E.g.高亮線1〜5,然後運行該命令(:'<,'> g/...)產生該輸出

Item a: 30 min 
    Subitem: 10 min 
    Subitem 2: 20 min 
Item b: 33 min 
    Subitem: 10 min 
    Subitem 2: 23 min 
Item c: <total> 
    Subitem: 10 min 
    Subitem 2: 23 min 
    Subitem 3: 43 min 

最後需要注意。如果其中一個組沒有子項,例如

Item a: <total> 
    Subitem: 10 min 
    Subitem 2: 20 min 
Item b: <total> 
Item c: <total> 
    Subitem: 10 min 
    Subitem 2: 23 min 
    Subitem 3: 43 min 

然後,當命令到達第二個項目時,命令將終止並顯示'無效範圍'錯誤。你可以讓Vim忽略這個 並繼續進行,而不管前綴整個命令爲:silent!

編輯

我的工作流程只是一個快速的解釋,爲什麼長的單行。

  1. 我喜歡:g和前命令很多。
  2. 我使用命令行歷史記錄和命令行窗口進行交互式構建命令以進行編輯。
  3. 當我完成後,我將這樣的一次性命令粘貼到緩衝區中:我有一個映射來執行當前行作爲ex命令。這樣,當我需要時,命令總是可以輕鬆獲得。爲了更經常使用,您可能需要命令,函數或ftplugin。
+2

是......美......說真的,謝謝你的最好的SO回答我可能已經收到過。 – 2011-12-22 02:47:17

+2

順便說一句,對於所有認爲上面的:g命令看起來很複雜的人來說,它並不是真的。語法非常簡潔,可能會給它外觀,但操作非常簡單;沒有任何聰明的技巧。只需查看您無法獲得的精彩文檔中的內容,或查詢SO或vim_use Google小組。 – 1983 2011-12-22 03:32:20

+4

我花了很長的時間來了解......所以這裏是一個解釋: :g開頭全局命令 /^項目/在每個匹配^項目 設G行的命令運行:S = 0 mark a 將光標位置存儲在 +, -/\ nItem \ | \%$/s/\ v(\ d +)\ ze \ s + min $/\ = Sum(submatch(0))/g 這是一個替代命令的巨大野獸。直到第一個's'的部分是範圍,'s'和'$'之間的部分是模式,函數調用是替換(沒有任何實際替換) 'a 回到位置標記爲 s//\ =(g:s。'min')/ 最後取代 2012-07-31 01:59:21

1

我知道我的vim7.3-scriptadscriven的要長很多。
但它可以完成工作。

fun! SubTotal() 
    fun! Collect() 
     " define some global vars 
     if line('.') == 1 
      let g:dict = {} 
      let g:key = '' 
     endif 

     " set current key && add key to dict 
     fun! AddKey(k) 
      let g:key = a:k 
      let g:dict[a:k]= [] 
     endfun 

     " add value to dict[key] 
     fun! AddValue(v) 
      if g:key != '' 
       call add(g:dict[g:key], a:v) 
      endif 
     endfun 

     " scan every line 
     let line = getline('.') 
     if line =~ '^Item' 
      call AddKey(matchstr(line, '^Item\s\+\zs\w\+\ze\s*:')) 
     elseif line =~ '^\s*Subitem' 
      call AddValue(str2nr(matchstr(line, '\d\+\ze\s*min$'))) 
     endif 
     return line 
    endfun 

    " collect data line by line 
    silent %s/.*/\=Collect()/ 
    " replace <total> with sum 
    silent g/^Item/s/^Item\s\+\(\w\+\)\s*:\s*\zs.*/\=eval(join(g:dict[submatch(1)], '+'))/ 
endfun 

Item a: 30 
    Subitem: 10 min 
    Subitem 2: 20 min 
Item b: 33 
    Subitem: 10 min 
    Subitem 2: 23 min 
Item c: 76 
    Subitem: 10 min 
    Subitem 2: 23 min 
    Subitem 3: 43 min 

:echo dict 
{'a': [10, 20], 'b': [10, 23], 'c': [10, 23, 43]}