Come posso riformattare una stringa multilinea in vim quando uso il tipo di file Python?


23

Supponiamo che sto modificando un po 'di codice Python in vim che assomigli un po' a:

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

Preferirei riformattare questo in modo che rispecchi fino a textwidthquando ho impostato:

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

C'è un modo semplice per farlo? Se fosse un paragrafo di testo convenzionale gqipo simile sarebbe utile, ma questo non gestirà le virgolette utilizzate per delimitare la stringa.

Nota : sto chiedendo specificamente di Python qui, ma idealmente, questa risposta sarebbe rilevante per molti tipi di linguaggio di programmazione che consentono la continuazione di stringhe.



3
Hai anche considerato di riscrivere il codice come """multi-line string"""?
200_successo

1
@ 200_success, è un grande consiglio, grazie. Non sapevo affatto di quella caratteristica di Python. Sembra funzionare, soprattutto - sebbene tutti i parametri della funzione vengano ridisposti, il che non è l'ideale. Penso che la mia domanda sia ancora valida per il tipo di stringa che ho, e sicuramente per altri lang.
Andrew Ferrier,

2
Si noti che una stringa a più righe aggiungerà caratteri di spazi bianchi, il che potrebbe essere un problema se gli spazi bianchi contano nella stringa.
Matt Boehm,

Risposte:


6

Oh è difficile. Penso che l'approccio migliore sia probabilmente una macro ma più probabilmente un plugin. Ecco un esempio che ho preparato (ho bisogno di un hobby migliore). Sembrava funzionare per me, ma avrebbe bisogno del plugin di rientro in Python per rientrare correttamente. Provalo.

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

Quindi puoi usarlo con :call ReformatMultiLines()e / o usarlo in una mappatura.


6

Se questo è un evento regolare, sarebbe meglio cercare un plugin o usare la funzione di @Sukima. Se lo facessi al volo, tuttavia, ecco cosa probabilmente farei:

  1. Aggiungi una nuova riga dopo la parentesi aperta e prima della parentesi chiusa in modo che le stringhe si trovino su righe separate.

    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. Seleziona le linee con le stringhe ed elimina le virgolette circostanti: :norm ^x$x

  3. Riduci larghezza di testo (per tenere conto delle virgolette mancanti) :set tw-=2
  4. Seleziona nuovamente e formatta: gvgq
  5. Fix textwidth: :set tw+=2
  6. Citazioni Re-add: gv:norm I"<Esc>A". Invece di <Esc>inserire una escape letterale digitando ctrl-v seguito dal tasto escape. Dato che mappo jj per scappare, di solito scrivo jj qui.
  7. Facoltativamente, utilizzare J per ricollegare le prime due / ultime due righe. Quando ho una stringa molto lunga in Python come questa, in genere preferisco avviarla sulla riga successiva e inserirla solo di un livello in più rispetto alla riga precedente. Questo dà più spazio orizzontale per la corda e mi sembra più naturale. In alternativa, è possibile salvare la stringa in una variabile da qualche parte sopra. Supponendo che sia una stringa statica, potresti persino salvarla in una variabile globale in modo che sia a un livello di indentazione molto più basso e possa adattarsi su un numero inferiore di righe. Si noti che si desidera ricongiungersi alla parentesi di chiusura sulla stessa riga, probabilmente si desidera ridurre / aumentare la larghezza del testo di 3 anziché 2.

1
Grazie. Ho appena provato questo, ma :set tw-=2risulta E487: Argument must be positive: tw-=2. La sintassi è errata?
Andrew Ferrier,

1
Controllare il valore corrente di larghezza di testo con :set tw?. La mia ipotesi è che la larghezza del testo sia impostata su 0. Questi passaggi presuppongono che la larghezza del testo inizi come il numero di caratteri desiderati per riga.
Matt Boehm,

Nel tuo esempio probabilmente vorrai s/2/3/aggiungere uno spazio prima della virgoletta di chiusura, la tua lunga stringa non avrebbe una spaziatura corretta tra le parole.
cdosborn,

1

L'analisi e la formattazione del codice è difficile, specialmente per linguaggi dinamici come Python.

Invece di provare ad analizzare Python in Vimscript, ti consiglio di usare un formattatore esterno come yapf . Con la mia risposta qui puoi scriverlo automaticamente scrivendo con:

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

La s:write_cmd()funzione è nell'altra risposta: non la aggiungerò qui, quindi non dovrò aggiornarla in due punti se trovo un bug :-)

È anche possibile impostare formatprgper yapfe formattare manualmente con gq.


Ciao. Risposta utile, ma sto effettuando il downvoting in quanto non credo che risponda a questa domanda: questa domanda si sta chiedendo in modo molto specifico sulla riformattazione delle stringhe multilinea, che yapfsembra non fare. Tuttavia sono d'accordo che sembra uno strumento utile.
Andrew Ferrier,

@AndrewFerrier Abbastanza giusto; Potrei giurare di averlo visto riformattare le stringhe quando l'ho provato l'altro giorno, ma non riesco nemmeno a farlo funzionare ora: - / Suppongo di essere confuso.
Martin Tournoij,

0

Le seguenti funzioni e mappature corrispondenti hanno risolto questo problema per me:

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>
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.