2011-12-21 26 views
13

Vì vậy, tôi có một tập tin văn bản như thế này:Vim Scripting - chức năng gọi một lần cho tất cả các dòng chọn

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

Tôi muốn thay thế <total> với tổng số 10 và 20. Ngay bây giờ tôi đang làm nó với các chức năng sau:

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 được tổng số thông qua tại, SumSelection gọi tổng trên tất cả các con số trong dòng được chọn, và (được cho là) ​​Shift + e gọi SumSelection trong chế độ hình ảnh (hoặc bất cứ điều gì bạn chọn để gọi nó.)

Vấn đề là, khi tôi nhấn Shift + e khi tôi có một số dòng được chọn, thay vì :call SumSelection() Tôi thực sự nhận được :'<,'>call SumSelection() có nghĩa là hàm được gọi một lần cho mỗi dòng được chọn. Không tốt, phải không? Vì vậy, theo như tôi có thể nói không có cách nào xung quanh điều này. Tôi có thể làm gì để có được những chức năng để:

  1. được chỉ gọi một lần
  2. lẽ tổng số này trong một cách hiệu quả hơn

Trả lời

18

Vâng, có một chức năng thực hiện chỉ một lần trên một phạm vi, nối thêm từ khóa range. Ví dụ.

fun Foo() range 
    ... 
endfun 

Sau đó, chức năng của bạn có thể tự quản lý phạm vi bằng các thông số đặc biệt a: firstline và: lastline. (Xem :help a:firstline để biết chi tiết.)

Tuy nhiên tôi nghĩ rằng trong trường hợp này yêu cầu của bạn chỉ đơn giản là đủ để được hoàn thành với một lớp lót sử dụng :global.

Chúng tôi có thể thực hiện với các đầu vào và đầu ra được chỉ định tốt hơn. Nhưng giả sử một tập tin chứa

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 

và một chức năng được xác định như

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

thì đây sẽ tổng hợp các subitems (tất cả cùng một dòng)

: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')/

và sản xuất sản lượng này

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 

Nhân tiện, lệnh trên hoạt động trên toàn bộ bộ đệm. Nếu bạn đánh dấu một dãy đầu tiên và sau đó nhấn phím :, vim sẽ tự động thêm lệnh của bạn với '<,'> có nghĩa là lệnh sau sẽ hoạt động từ đầu đến cuối phạm vi được đánh dấu.

Ví dụ: làm nổi bật các dòng 1 đến 5 và sau đó chạy lệnh (:'<,'> g/...) tạo ra kết quả này

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 

Một lưu ý cuối cùng. Nếu một trong các nhóm không có phụ đề, ví dụ:

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 

sau đó lệnh sẽ hủy bỏ lỗi 'Phạm vi không hợp lệ' khi nó đến mục thứ hai. Bạn có thể yêu cầu Vim bỏ qua điều này và tiếp tục bất kể bằng cách thêm tiền tố cho toàn bộ lệnh với :silent!.

EDIT

Chỉ cần một lời giải thích nhanh chóng của công việc của tôi và tại sao lâu một lót.

  1. Tôi thích :g và các lệnh cũ.
  2. Tôi đã xây dựng lệnh tương tác bằng cách sử dụng lịch sử dòng lệnh và cửa sổ dòng lệnh để chỉnh sửa.
  3. Khi hoàn thành, tôi dán các lệnh một lần như thế này vào bộ đệm: Tôi có ánh xạ để thực thi dòng hiện tại như một lệnh cũ. Bằng cách đó, lệnh luôn luôn có sẵn khi tôi cần nó. Để sử dụng thường xuyên hơn, bạn có thể muốn có một lệnh, hàm hoặc một ftplugin.
+2

nó ... đẹp ... Nghiêm túc, cảm ơn cho tốt nhất câu trả lời SO Tôi đã có thể bao giờ nhận được. –

+2

Btw, đối với bất kỳ ai nghĩ rằng lệnh g: ở trên trông phức tạp, nó không thực sự. Cú pháp là rất ngắn gọn mà có thể cung cấp cho nó sự xuất hiện, nhưng các hoạt động thực sự khá đơn giản; không có bất kỳ thủ đoạn thông minh nào. Chỉ cần tra cứu các bit bạn không nhận được trong tài liệu tuyệt vời, hoặc hỏi về SO hoặc nhóm vim_use Google. – 1983

+4

Nó đã cho tôi một thời gian dài để hiểu ... vì vậy đây là một lời giải thích: : g bắt đầu một lệnh toàn cầu /^ Item/Lệnh chạy ở mỗi dòng phù hợp với^mục let g: s = 0 đánh dấu Lưu vị trí của con trỏ trong một số +, -/\ nMục \ | \% $/s/\ v (\ d +) \ ze \ s + phút $/\ = Tổng (số phụ (0))/g Đây là một con quái vật khổng lồ của một lệnh thay thế. Phần cho đến 'đầu tiên' là phạm vi, phần giữa 's' và '$' là mẫu và hàm gọi là thay thế (không có gì thực sự được thay thế) 'a quay lại vị trí được đánh dấu là s//\ = (g: s. 'min')/ cuối cùng thay thế

1

Tôi biết số vim7.3-script của tôi dài hơn nhiều so với số adscriven.
Nhưng nó có thể hoàn thành công việc.

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]} 
Các vấn đề liên quan