Vimscript'te bir temsilci kullanmak veya bir işlevi bağımsız değişken olarak geçirmek mümkün müdür?


11

Vimscript öğrenmek için küçük bir eklenti oluşturmaya çalışıyorum, amacım seçilen bir metni işleyen ve sonuçla değiştiren bazı işlevler oluşturmaktır. Komut dosyası aşağıdaki öğeleri içerir:

  • Metni işleyen iki işlev: bir dizeyi parametre olarak alır ve orijinal metni değiştirmek için kullanılması gereken dizeyi döndürür. Şimdilik sadece iki tane var ama birkaç zaman içinde çok daha fazlası olabilir.

  • Seçilen metni alan bir işlev: sadece son seçimi çeker ve döndürür.

  • Bir sarma işlevi: bir işleme işlevini çağıran, sonucunu alan ve eski seçimi bu sonuçla değiştiren.

Şimdilik benim sarma fonksiyonu şöyle:

function! Wrapper()
    " Get the string to insert
    let @x = Type1ProcessString(GetSelectedText())

    " remove the old selection
    normal gvd

    " insert the new string
    normal "xp
endfunction

Ve hat 3'ü değiştirerek ikinci bir ambalaj oluşturmalıyım

let @x = Type2ProcessString(GetSelectedText())

Benim sarmalayıcı işlevine 3 satırında genel bir çağrı yürütmek ve kullanmak için İşlem işlevini içeren bir parametre vermek istiyorum. Şimdilik call, örneğin, bu gibi farklı yollar kullanarak denedim :

let @x = call('a:functionToExecute', GetSelectedText()) 

ama gerçekten başarılı olamadım ve :h calldelege konusunda pek yardımcı olmadım.

Burada özetlemek gerekirse sorularım:

  • Tüm işleyenler için nasıl yalnızca bir sarmalayıcı işlevi yapabilirim?
  • Vimscript'te delege olarak çalışan bir şey var mı?
  • Delegeler yoksa, istediğimi yapmanın "iyi" bir yolu ne olurdu?

Yanıtlar:


16

Sorunuzu cevaplamak için: call()kılavuzdaki prototip call({func}, {arglist} [, {dict}]); {arglist}argüman bağımsız değişkenler listesi tam anlamıyla bir Liste nesnesi değil, olması gerekiyor. Yani, şöyle yazmalısınız:

let @x = call(a:functionToExecute, [GetSelectedText()])

Bu a:functionToExecute, bir Funcref (bkz. :help Funcref) Veya bir işlevin adıdır (örneğin, bir dize 'Type1ProcessString').

Şimdi, bu Vim'e bir çeşit LISP benzeri kalite veren güçlü bir özellik, ancak muhtemelen nadiren yukarıdaki gibi kullanabilirsiniz. Eğer a:functionToExecutebir dize, bir fonksiyonun adıdır, o zaman bunu yapabilirsiniz:

function! Wrapper(functionToExecute)
    " ...
    let s:processing = function(a:functionToExecute)
    let @x = s:processing(GetSelectedText())
    " ...
endfunction

ve sarmalayıcıyı işlevin adıyla çağırırsınız:

call Wrapper('Type1ProcessString')

Öte yandan a:functionToExecutebir Funcref ise, doğrudan arayabilirsiniz:

function! Wrapper(functionToExecute)
    " ...
    let @x = a:functionToExecute(GetSelectedText())
    " ...
endfunction

ancak sarmalayıcıyı şöyle çağırmanız gerekir:

call Wrapper(function('Type1ProcessString'))

İle fonksiyonların varlığını kontrol edebilirsiniz exists('*name'). Bu, aşağıdaki küçük numarayı mümkün kılar:

let s:width = function(exists('*strwidth') ? 'strwidth' : 'strlen')

yani strwidth()Vim'in sahip olacağı kadar yeniyse yerleşik olanı kullanan bir işlevdir ve strlen()aksi halde geri döner (böyle bir geri dönüşün mantıklı olduğunu savunmuyorum; sadece yapılabileceğini söylüyorum). :)

Sözlük işlevleriyle (bkz. :help Dictionary-function) Sınıflara benzeyen bir şey tanımlayabilirsiniz:

let g:MyClass = {}

function! g:MyClass.New(...)
    let newObj = copy(self)

    if a:0 && type(a:1) == type({})
        let newObj._attributes = deepcopy(a:1)
    endif
    if exists('*MyClassProcess')
        let newObj._process = function('MyClassProcess')
    else
        let newObj._process = function('s:_process_default')
    endif

    return newObj
endfunction

function! g:MyClass.getFoo() dict
    return get(get(self, '_attributes', {}), 'foo')
endfunction

function! g:MyClass.setFoo(val) dict
    if !has_key(self, '_attributes')
        let self._attributes = {}
    endif
    let self._attributes['foo'] = a:val
endfunction

function! g:MyClass.process() dict
    call self._process()
endfunction

function! s:_process_default()
    echomsg 'nothing to see here, define MyClassProcess() to make me interesting'
endfunction

Sonra bunun gibi nesneleri başlatırsınız:

let little_object = g:MyClass.New({'foo': 'bar'})

Ve yöntemlerini çağırın:

call little_object.setFoo('baz')
echomsg little_object.getFoo()
call little_object.process()

Ayrıca sınıf özniteliklerine ve yöntemlerine de sahip olabilirsiniz:

let g:MyClass.__meaning_of_life = 42

function g:MyClass.GetMeaningOfLife()
    return get(g:MyClass, '__meaning_of_life')
endfunction

( dictburada gerek olmadığına dikkat edin ).

Düzenleme: Alt sınıflama şöyle bir şeydir:

let g:MySubclass = copy(g:MyClass)
call extend(g:MySubclass, subclass_attributes)

Buradaki ince nokta copy()bunun yerine kullanımıdır deepcopy(). Bunun nedeni, üst sınıfın özniteliklerine başvuru ile erişebilmektir. Bu elde edilebilir, ancak son derece kırılgandır ve doğru yapmak önemsiz olmaktan uzaktır. Bir diğer potansiyel sorun alt sınıf bir arada tutmaya çalışıyor ki bu tür is-aile has-a. Bu nedenle sınıf özellikleri genellikle acıya değmez.

Tamam, bu düşünce için yiyecek vermek için yeterli olmalı.

İlk kod snippet'inize geri döndüğünüzde, geliştirilebilecek iki ayrıntı vardır:

  • İhtiyacınız olmayan normal gvd, eski seçimi kaldırmak için normal "xpönce onu öldürmek olmasa bile değiştirecektir
  • call setreg('x', [lines], type)yerine kullanın let @x = [lines]. Bu, kayıt türünü açıkça ayarlar x. Aksi takdirde, xzaten doğru tipe (yani, karakterize, çizgisel veya blok halinde) güveniyorsunuz .

Doğrudan sözlükte işlevler oluşturduğunuzda (örn. "Numaralı işlev"), dictanahtar kelimeye ihtiyacınız yoktur . Bu, "sınıf yöntemleri" için geçerlidir. Bkz :h numbered-function.
Karl Yngve Lervåg

@ KarlYngveLervåg Teknik olarak hem sınıf hem de nesne yöntemleri için geçerlidir (yani işlevlerin dictherhangi birine gerek yoktur MyClass). Ama bunu kafa karıştırıcı buluyorum, bu yüzden dictaçıkça ekleme eğilimindeyim .
lcd047

Anlıyorum. Yani dictamacınızı açıklığa kavuşturmak için nesne yöntemleri için eklersiniz , sınıf yöntemleri için değil mi?
Karl Yngve Lervåg

@ lcd047 Bu şaşırtıcı cevap için çok teşekkürler! Üzerinde çalışmam gerekecek ama tam da aradığım şey buydu!
statox

1
@ KarlYngveLervåg Burada bir incelik var, selfsınıf yöntemleri ve nesne yöntemleri için anlamı farklı - bu eski durumda sınıfın kendisi ve sonrakinde mevcut nesnenin örneğidir. Bu nedenle her zaman sınıfın kendisine g:MyClassasla kullanmıyor olarak atıfta bulunuyorum selfve çoğunlukla dictkullanmanın uygun olduğunu hatırlatan bir şey olarak görüyorum self(yani, dicther zaman bir nesne örneği üzerinde hareket eden bir işlev ). Yine, sınıf yöntemlerini fazla kullanmıyorum ve bunu yaptığımda da dicther yerde atlamak eğilimindeyim . Evet, öz tutarlılık benim orta adım. ;)
lcd047

1

Komutu bir dizede oluşturun ve :exeçalıştırmak için kullanın . Daha :help executefazla bilgi için bakınız.

Bu durumda, executeişleve çağrı yapmak ve sonucu kayıt defterine koymak için kullanılır, komutun farklı öğeleri işleçle .normal bir dize olarak birleştirilmelidir . 3. satır daha sonra:

execute "let @x = " . a:functionToExecute . "(GetSelectedText())"
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.