Bagaimana saya bisa memformat ulang string multi-line di vim saat menggunakan filetype Python?

23

Mari kita asumsikan saya sedang mengedit beberapa kode Python di vim yang agak mirip:

myobj.myfunc("Some string parameter that goes on and on and on and on and sometimes doesn't"
             "split very"
             "neatly over different lines so that"
             "it is formatted attractively")

Saya lebih suka memformat ulang ini sehingga mencerminkan hingga yang textwidthtelah saya tetapkan:

myobj.myfunc("Some string parameter that goes on and on and "
             "on and on and sometimes doesn't split very "
             "neatly over different lines so that it is "
             "formatted attractively")

Apakah ada cara mudah untuk melakukan ini? Jika ini adalah paragraf teks konvensional, gqipatau serupa akan berguna, tetapi ini tidak akan menangani tanda kutip yang digunakan untuk menghapus string.

Catatan : Saya bertanya secara khusus tentang Python di sini, tetapi idealnya, jawaban ini akan relevan untuk banyak jenis bahasa pemrograman yang memungkinkan kelanjutan string.

Andrew Ferrier
sumber
1
Pertanyaan serupa di sini: vim.1045645.n5.nabble.com/…
Andrew Ferrier
3
Sudahkah Anda mempertimbangkan untuk menulis ulang kode """multi-line string"""?
200_sukses
1
@ 200_sukses, itu tip yang bagus, terima kasih. Saya tidak tahu tentang fitur Python sama sekali. Tampaknya berfungsi, sebagian besar - meskipun semua parameter ke fungsi direfleksikan, yang tidak ideal. Saya pikir pertanyaan saya masih berlaku untuk jenis string yang saya miliki, dan tentu saja untuk langs lainnya.
Andrew Ferrier
2
Perhatikan bahwa string multi-baris akan menambahkan karakter spasi putih, yang mungkin menjadi masalah jika spasi putih penting dalam string.
Matt Boehm
Pertanyaan yang sangat terkait: vi.stackexchange.com/questions/2135/… ...
Martin Tournoij

Jawaban:

6

Oh, itu yang sulit. Saya pikir pendekatan terbaik mungkin adalah makro tetapi lebih mungkin sebuah plugin. Ini adalah contoh yang saya buat (saya butuh hobi yang lebih baik). Tampaknya bekerja untuk saya tetapi perlu plugin indentasi python untuk indentasi dengan benar. Cobalah.

function ReformatMultiLines()
  let brx = '^\s*"'
  let erx = '"\s*$'
  let fullrx = brx . '\(.\+\)' . erx
  let startLine = line(".")
  let endLine   = line(".")
  while getline(startLine) =~ fullrx
    let startLine -= 1
  endwhile
  if getline(endLine) =~ erx
    let endLine += 1
  endif
  while getline(endLine) =~ fullrx
    let endLine += 1
  endwhile
  if startLine != endLine
    exec endLine . ' s/' . brx . '//'
    exec startLine . ' s/' . erx . '//'
    exec startLine . ',' . endLine . ' s/' . fullrx . '/\1/'
    exec startLine . ',' . endLine . ' join'
  endif
  exec startLine
  let orig_tw = &tw
  if &tw == 0
    let &tw = &columns
    if &tw > 79
      let &tw = 79
    endif
  endif
  let &tw -= 3 " Adjust for missing quotes and space characters
  exec "normal A%-%\<Esc>gqq"
  let &tw = orig_tw
  let endLine = search("%-%$")
  exec endLine . ' s/%-%$//'
  if startLine == endLine
    return
  endif
  exec endLine
  exec 'normal I"'
  exec startLine
  exec 'normal A "'
  if endLine - startLine == 1
    return
  endif
  let startLine += 1
  while startLine != endLine
    exec startLine
    exec 'normal I"'
    exec 'normal A "'
    let startLine += 1
  endwhile
endfunction

Kemudian Anda bisa menggunakannya dengan :call ReformatMultiLines()dan / atau menggunakannya dalam pemetaan.

Sukima
sumber
6

Jika ini kejadian biasa, Anda sebaiknya mencari plugin atau menggunakan fungsi @Sukima. Namun, jika saya melakukan ini dengan cepat, inilah yang mungkin akan saya lakukan:

  1. Tambahkan baris baru setelah paren pembuka dan sebelum paren penutup sehingga string berada pada garis yang terpisah.

    myobj.myfunc(
                 "Some string parameter that goes on and on and on and on and sometimes doesn't"
                 "split very"
                 "neatly over different lines so that"
                 "it is formatted attractively"
    )
    
  2. Pilih baris dengan string dan hapus kutipan di sekitarnya: :norm ^x$x

  3. Kurangi lebar teks (untuk memperhitungkan kutipan yang hilang) :set tw-=2
  4. Pilih kembali dan format: gvgq
  5. Perbaiki lebar teks: :set tw+=2
  6. Kutipan Re-add: gv:norm I"<Esc>A". Alih-alih <Esc>Anda ingin memasukkan escape literal dengan mengetikkan ctrl-v diikuti oleh key escape. Karena saya memetakan jj untuk melarikan diri, saya biasanya ketik jj di sini.
  7. Opsional gunakan J untuk menggabungkan kembali dua baris pertama / dua terakhir. Ketika saya memiliki string yang sangat panjang dalam python seperti ini, saya biasanya lebih suka memulainya di baris berikutnya dan hanya membuat indentasi satu tingkat lebih tinggi dari baris sebelumnya. Ini memberi ruang lebih horizontal untuk string dan terasa lebih alami bagi saya. Atau, Anda bisa menyimpan string ke variabel di suatu tempat di atas. Dengan asumsi itu adalah string statis, Anda bahkan dapat menyimpannya ke variabel global sehingga berada pada tingkat lekukan yang jauh lebih rendah dan dapat ditampung pada baris yang lebih sedikit. Perhatikan bahwa Anda harus bergabung kembali dengan paren penutup pada baris yang sama, Anda mungkin ingin mengurangi / menambah bandwidth dengan 3 bukannya 2.
Matt Boehm
sumber
1
Terima kasih. Saya baru saja mencoba ini tetapi :set tw-=2menghasilkan E487: Argument must be positive: tw-=2. Apakah sintaksanya salah?
Andrew Ferrier
1
Periksa nilai textwidth saat ini dengan :set tw?. Dugaan saya adalah Anda telah mengatur lebar teks ke 0. Langkah-langkah ini mengasumsikan bahwa lebar teks dimulai sebagai jumlah karakter yang Anda inginkan per baris.
Matt Boehm
Dalam contoh Anda, Anda mungkin ingin s/2/3/dan menambahkan spasi sebelum kutipan penutup, string panjang Anda tidak akan memiliki jarak antar kata yang tepat.
cdosborn
1

Memisahkan dan memformat kode sulit, terutama untuk bahasa dinamis seperti Python.

Alih-alih mencoba mengurai Python dalam Vimscript, saya sarankan Anda dapat menggunakan formatter eksternal seperti yapf . Dengan jawaban saya di sini, Anda dapat menulis ini secara otomatis saat menulis dengan:

augroup write_cmd
    autocmd BufWritePre *.py call s:write_cmd('yapf')
augroup end

The s:write_cmd()fungsi dalam jawaban lain - saya tidak akan menambahkannya di sini jadi aku tidak perlu memperbaruinya di dua tempat jika saya menemukan bug :-)

Anda juga dapat mengatur formatprguntuk yapfdan format secara manual dengan gq.

Martin Tournoij
sumber
Hai. Jawaban yang membantu, tapi saya tidak bisa menjawab karena saya pikir ini tidak menjawab pertanyaan ini: pertanyaan ini secara khusus menanyakan tentang memformat ulang string multiline, yang yapfsepertinya tidak dilakukan. Meskipun demikian saya setuju itu terlihat seperti alat yang berguna.
Andrew Ferrier
@AndrewFerrier Cukup adil; Aku berani bersumpah aku melihatnya memformat ulang string ketika aku menguji ini beberapa hari yang lalu, tetapi tidak bisa membuatnya bekerja sekarang juga: - / Kira aku bingung.
Martin Tournoij
0

Fungsi-fungsi berikut dan pemetaan yang sesuai memecahkan masalah ini untuk saya:

function! BreakMultlineString(quote)
let c = 100
while c == 100
    normal 100|
    let c = virtcol('.')
    if c == 100
        execute "normal ," . a:quote
    endif
endwhile
endfunction

function! JoinMultilineString()
    let c = "'"
    while c == "'" || c == '"'
        normal gj^
        let c = matchstr(getline('.'), '\%' . col('.') . 'c.')
        normal k
        if c == "'" || c == '"'
            normal ,j
        endif
    endwhile
endfunction

" break lines
nnoremap <Leader>" 100\|Bi"<CR>"<Esc>
nnoremap <Leader><Leader>" :call BreakMultlineString('"')<CR>
nnoremap <Leader>' 100\|Bi'<CR>'<Esc>
nnoremap <Leader><Leader>' :call BreakMultlineString("'")<CR>

" join lines
nmap <Leader>j Jds'ds"x
nnoremap <Leader>J :call JoinMultilineString()<CR>
Bryan Bugyi
sumber