Comment puis-je reformater une chaîne multi-lignes dans vim lors de l'utilisation du type de fichier Python?

23

Supposons que je modifie du code Python dans vim qui ressemble un peu à:

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

Je préférerais reformater ceci afin qu'il se redirige jusqu'à ce que textwidthj'ai défini:

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

Y a-t-il un moyen facile de faire ceci? S'il s'agissait d'un paragraphe de texte conventionnel, gqipou similaire serait utile, mais cela ne traitera pas les guillemets utilisés pour délimiter la chaîne.

Remarque : Je pose des questions spécifiques sur Python ici, mais idéalement, cette réponse serait pertinente pour de nombreux types de langage de programmation qui permettent la continuation de chaînes.

Andrew Ferrier
la source
1
Question similaire ici: vim.1045645.n5.nabble.com/…
Andrew Ferrier
3
Avez-vous également envisagé de réécrire le code en tant que """multi-line string"""?
200_success
1
@ 200_success, c'est un excellent conseil, merci. Je ne connaissais pas du tout cette fonctionnalité de Python. Cela semble fonctionner, surtout - bien que tous les paramètres de la fonction soient redistribués, ce qui n'est pas idéal. Je pense que ma question est toujours valable pour le type de chaîne que j'ai, et certainement pour d'autres langs.
Andrew Ferrier
2
Notez qu'une chaîne multi-lignes ajoutera des caractères d'espacement, ce qui peut être un problème si les espaces sont importants dans la chaîne.
Matt Boehm
Question très connexe: vi.stackexchange.com/questions/2135/… ...
Martin Tournoij

Réponses:

6

Oh, c'est difficile. Je pense que la meilleure approche est peut-être une macro mais plus probablement un plugin. Voici un exemple que j'ai fouetté (j'ai besoin d'un meilleur passe-temps). Cela semblait fonctionner pour moi mais aurait besoin du plugin d'indentation python pour indenter correctement. Essaye le.

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

Ensuite, vous pouvez l'utiliser avec :call ReformatMultiLines()et / ou l'utiliser dans un mappage.

Sukima
la source
6

Si cela se produit régulièrement, il vaut mieux chercher un plugin ou utiliser la fonction de @Sukima. Si je faisais cela à la volée, voici ce que je ferais probablement:

  1. Ajoutez une nouvelle ligne après la parenthèse ouvrante et avant la parenthèse fermante afin que les chaînes soient sur des lignes distinctes.

    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. Sélectionnez des lignes avec des chaînes et supprimez les guillemets environnants: :norm ^x$x

  3. Réduisez la largeur du texte (pour tenir compte des guillemets manquants) :set tw-=2
  4. Re-sélectionner et formater: gvgq
  5. Correction de la largeur du texte: :set tw+=2
  6. Re-add citations: gv:norm I"<Esc>A". Au lieu de <Esc>vous souhaitez insérer un échappement littéral en tapant ctrl-v suivi de la touche d'échappement. Puisque je mappe jj pour m'échapper, je tape généralement jj ici.
  7. Utilisez éventuellement J pour joindre à nouveau les deux / deux dernières lignes. Lorsque j'ai une très longue chaîne en python comme celle-ci, je préfère généralement la démarrer sur la ligne suivante et la mettre en retrait d'un niveau de plus que la ligne précédente. Cela donne plus d'espace horizontal pour la corde et me semble plus naturel. Vous pouvez également enregistrer la chaîne dans une variable quelque part au-dessus. En supposant qu'il s'agit d'une chaîne statique, vous pouvez même l'enregistrer dans une variable globale afin qu'elle soit à un niveau d'indentation beaucoup plus faible et puisse tenir sur moins de lignes. Notez que vous devez rejoindre le paren de fermeture sur la même ligne, vous voulez probablement décrémenter / incrémenter la largeur du texte de 3 au lieu de 2.
Matt Boehm
la source
1
Merci. Je viens d'essayer cela, mais cela se :set tw-=2traduit par E487: Argument must be positive: tw-=2. La syntaxe est-elle incorrecte?
Andrew Ferrier
1
Vérifiez la valeur actuelle de la largeur de texte avec :set tw?. Je suppose que la largeur de texte est définie sur 0. Ces étapes supposent que la largeur de texte commence comme le nombre de caractères que vous souhaitez par ligne.
Matt Boehm
Dans votre exemple, vous voudrez probablement s/2/3/ajouter un espace avant la citation de fermeture, votre longue chaîne n'aurait pas l'espacement approprié entre les mots.
cdosborn le
1

L'analyse et le formatage du code est difficile, en particulier pour les langages dynamiques tels que Python.

Au lieu d'essayer d'analyser Python dans Vimscript, je vous recommande d'utiliser un formateur externe tel que yapf . Avec ma réponse ici, vous pouvez l'écrire automatiquement en écrivant avec:

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

La s:write_cmd()fonction est dans l'autre réponse - je ne l'ajouterai pas ici donc je n'aurai pas à la mettre à jour à deux endroits si je trouve un bug :-)

Vous pouvez également définir formatprgà yapfet formater manuellement avec gq.

Martin Tournoij
la source
Salut. Réponse utile, mais je vote en aval car je ne pense pas que cela réponde à cette question: cette question pose très précisément la question du reformatage des chaînes multilignes, ce qui yapfne semble pas le cas. Néanmoins, je conviens que cela ressemble à un outil utile.
Andrew Ferrier
@AndrewFerrier Assez juste; Je pourrais jurer l'avoir vu reformater les chaînes quand je l'ai testé l'autre jour, mais je ne peux pas le faire fonctionner non plus: - / Je suppose que j'étais confus.
Martin Tournoij
0

Les fonctions suivantes et les mappages correspondants ont résolu ce problème pour moi:

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
la source