Wie kann ich eine mehrzeilige Zeichenfolge in vim neu formatieren, wenn ich den Dateityp Python verwende?

23

Nehmen wir an, ich bearbeite einen Python-Code in vim, der ungefähr so ​​aussieht:

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

Ich würde es vorziehen, dies neu zu formatieren, damit es zu dem von textwidthmir festgelegten Wert passt :

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

Gibt es eine einfache Möglichkeit, dies zu tun? Wäre dies ein herkömmlicher gqipTextabsatz oder ähnliches, wäre dies nützlich, würde jedoch nicht die Anführungszeichen verarbeiten, die zum Abgrenzen der Zeichenfolge verwendet werden.

Hinweis : Ich frage hier speziell nach Python, aber im Idealfall ist diese Antwort für viele Arten von Programmiersprachen relevant, die die Fortsetzung von Zeichenfolgen ermöglichen.

Andrew Ferrier
quelle
1
Ähnliche Frage hier: vim.1045645.n5.nabble.com/…
Andrew Ferrier
3
Haben Sie auch darüber nachgedacht, den Code als neu zu schreiben """multi-line string"""?
200_success
1
@ 200_success, das ist ein toller Tipp, danke. Ich wusste überhaupt nichts über diese Funktion von Python. Meistens scheint es zu funktionieren - obwohl alle Parameter der Funktion neu fließen, was nicht ideal ist. Ich denke, meine Frage gilt immer noch für die Art der Saite, die ich habe, und sicherlich für andere langs.
Andrew Ferrier
2
Beachten Sie, dass eine mehrzeilige Zeichenfolge Leerzeichen hinzufügt. Dies kann ein Problem sein, wenn die Zeichenfolge Leerzeichen enthält.
Matt Boehm
Sehr verwandte Frage: vi.stackexchange.com/questions/2135/… ...
Martin Tournoij

Antworten:

6

Oh, das ist eine schwierige Frage. Ich denke, der beste Ansatz ist möglicherweise ein Makro, aber wahrscheinlicher ein Plugin. Hier ist ein Beispiel, das ich ausgepeitscht habe (ich brauche ein besseres Hobby). Es schien für mich zu funktionieren, würde aber das Python-Einrückungs-Plugin benötigen, um richtig einzurücken. Versuch es.

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

Dann könnten Sie es mit :call ReformatMultiLines()und / oder in einem Mapping verwenden.

Sukima
quelle
6

Wenn dies regelmäßig vorkommt, sollten Sie am besten nach einem Plugin suchen oder die Funktion von @Sukima verwenden. Wenn ich dies jedoch im laufenden Betrieb tun würde, würde ich wahrscheinlich Folgendes tun:

  1. Fügen Sie nach dem öffnenden und vor dem schließenden Paren eine neue Zeile ein, sodass sich die Zeichenfolgen in separaten Zeilen befinden.

    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. Wählen Sie Zeilen mit Zeichenfolgen aus und löschen Sie umgebende Anführungszeichen: :norm ^x$x

  3. Textbreite reduzieren (um fehlende Anführungszeichen zu berücksichtigen) :set tw-=2
  4. Erneut auswählen und formatieren: gvgq
  5. Feste Textbreite: :set tw+=2
  6. Re-add Zitate: gv:norm I"<Esc>A". Stattdessen <Esc>möchten Sie ein Escape-Zeichen einfügen, indem Sie Strg-V gefolgt von der Escape-Taste eingeben. Da ich jj zu Flucht zuordnen, gebe ich hier normalerweise nur jj ein.
  7. Optional können Sie mit J die ersten beiden / letzten beiden Zeilen wieder verbinden. Wenn ich eine sehr lange Zeichenfolge in Python wie dieser habe, ziehe ich es normalerweise vor, sie in der nächsten Zeile zu beginnen und sie nur eine Ebene mehr als in der vorherigen Zeile einzurücken. Dies gibt der Saite mehr horizontalen Raum und fühlt sich für mich natürlicher an. Alternativ können Sie die Zeichenfolge auch in einer Variablen oben speichern. Angenommen, es handelt sich um eine statische Zeichenfolge, können Sie sie sogar in einer globalen Variablen speichern, sodass sie sich auf einer viel niedrigeren Einrückungsstufe befindet und auf weniger Zeilen passt. Beachten Sie, dass Sie, wenn Sie sich in derselben Zeile wieder dem abschließenden Paren anschließen möchten, wahrscheinlich die Textbreite um 3 anstatt um 2 verringern / erhöhen möchten.
Matt Boehm
quelle
1
Vielen Dank. Ich habe es gerade ausprobiert, aber das :set tw-=2Ergebnis ist E487: Argument must be positive: tw-=2. Ist die Syntax falsch?
Andrew Ferrier
1
Überprüfen Sie den aktuellen Wert der Textbreite mit :set tw?. Ich vermute, Sie haben die Textbreite auf 0 gesetzt. Bei diesen Schritten wird davon ausgegangen, dass die Textbreite mit der Anzahl der Zeichen beginnt, die Sie pro Zeile benötigen.
Matt Boehm
In Ihrem Beispiel möchten Sie wahrscheinlich s/2/3/ein Leerzeichen vor dem abschließenden Anführungszeichen einfügen, da Ihre lange Zeichenfolge nicht den richtigen Abstand zwischen den Wörtern aufweist.
Cdosborn
1

Das Parsen und Formatieren von Code ist schwierig, insbesondere für dynamische Sprachen wie Python.

Anstatt zu versuchen, Python in Vimscript zu analysieren, empfehle ich Ihnen, einen externen Formatierer wie yapf zu verwenden . Mit meiner Antwort hier kannst du dies automatisch aufschreiben mit:

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

Die s:write_cmd()Funktion steht in der anderen Antwort - ich werde sie hier nicht hinzufügen, so dass ich sie nicht an zwei Stellen aktualisieren muss, wenn ich einen Fehler finde :-)

Sie können auch einstellen , formatprgum yapfes manuell formatieren mit gq.

Martin Tournoij
quelle
Hallo. Hilfreiche Antwort, aber ich stimme ab, da ich glaube, dass sie diese Frage nicht beantwortet: Diese Frage bezieht sich ganz konkret auf die Neuformatierung mehrzeiliger Zeichenfolgen, was yapfanscheinend nicht zutrifft . Trotzdem stimme ich zu, es sieht nach einem nützlichen Werkzeug aus.
Andrew Ferrier
@ AndrewFerrier Fair genug; Ich könnte schwören, dass ich gesehen habe, wie Saiten neu formatiert wurden, als ich dies neulich getestet habe, aber ich kann es jetzt auch nicht zum Laufen bringen: - / Ich schätze, ich war verwirrt.
Martin Tournoij
0

Die folgenden Funktionen und entsprechenden Zuordnungen haben dieses Problem für mich gelöst:

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
quelle