Jak mogę sformatować ciąg wielu wierszy w vimie, używając typu pliku Python?

23

Załóżmy, że edytuję jakiś kod Pythona w vimie, który wygląda trochę jak:

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

Wolałbym sformatować to tak, aby powróciło do textwidthustawienia:

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

Czy jest na to łatwy sposób? Gdyby to był konwencjonalny akapit tekstowy gqiplub podobny, przydałby się, ale nie poradzi sobie z cudzysłowami używanymi do rozróżnienia łańcucha.

Uwaga : Pytam tutaj konkretnie o Pythona, ale idealnie, odpowiedź ta byłaby odpowiednia dla wielu rodzajów języka programowania, które pozwalają na ciąg znaków.

Andrew Ferrier
źródło
1
Podobne pytanie tutaj: vim.1045645.n5.nabble.com/…
Andrew Ferrier
3
Czy rozważałeś też przepisanie kodu jako """multi-line string"""?
200_success
1
@ 200_success, to świetna wskazówka, dzięki. W ogóle nie wiedziałem o tej funkcji Pythona. Wygląda na to, że działa głównie - chociaż wszystkie parametry funkcji są przepełnione, co nie jest idealne. Wydaje mi się, że moje pytanie jest nadal aktualne dla rodzaju łańcucha, który mam, a na pewno dla innych języków.
Andrew Ferrier,
2
Zauważ, że łańcuch wielowierszowy doda znaki białych znaków, co może być problemem, jeśli białe znaki mają znaczenie w łańcuchu.
Matt Boehm
Bardzo powiązane pytanie: vi.stackexchange.com/questions/2135/ ... ...
Martin Tournoij

Odpowiedzi:

6

Och, to trudne. Myślę, że najlepszym podejściem może być makro, ale bardziej prawdopodobne jest wtyczka. Oto przykład, który wymyśliłem (potrzebuję lepszego hobby). Wydawało mi się, że to działa, ale do poprawnego wcięcia potrzebna byłaby wtyczka wcięcia w Pythonie. Wypróbuj to.

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

Następnie możesz użyć go :call ReformatMultiLines()i / lub użyć go do mapowania.

Sukima
źródło
6

Jeśli jest to normalne zjawisko, najlepiej poszukaj wtyczki lub skorzystaj z funkcji @Sukima. Gdybym jednak robił to w locie, prawdopodobnie zrobiłbym to:

  1. Dodaj nową linię za paren otwierającym i przed paren zamykającym, aby ciągi znaków znajdowały się w osobnych wierszach.

    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. Wybierz linie z ciągami i usuń otaczające cudzysłowy: :norm ^x$x

  3. Zmniejsz szerokość tekstu (aby uwzględnić brakujące cytaty) :set tw-=2
  4. Wybierz ponownie i sformatuj: gvgq
  5. Napraw szerokość tekstu: :set tw+=2
  6. Ponownie dodać cytaty: gv:norm I"<Esc>A". Zamiast tego <Esc>chcesz wstawić dosłowne wyjście, wpisując ctrl-v, a następnie klawisz Escape. Ponieważ mapuję jj do ucieczki, zwykle po prostu wpisuję tutaj jj.
  7. Opcjonalnie użyj J, aby ponownie połączyć pierwsze dwie / ostatnie dwie linie. Kiedy mam bardzo długi ciąg znaków w Pythonie, zazwyczaj wolę zacząć od następnego wiersza i wciąć go tylko o jeden poziom więcej niż poprzedni wiersz. To daje więcej poziomej przestrzeni na sznurek i po prostu wydaje mi się bardziej naturalne. Alternatywnie możesz zapisać ciąg znaków do zmiennej gdzieś powyżej. Zakładając, że jest to ciąg statyczny, możesz nawet zapisać go w zmiennej globalnej, aby był na znacznie niższym poziomie wcięcia i zmieścił się na mniejszej liczbie wierszy. Zauważ, że masz ponownie dołączyć do zamykającego parena w tym samym wierszu, prawdopodobnie chcesz zmniejszyć / zwiększyć szerokość tekstu o 3 zamiast 2.
Matt Boehm
źródło
1
Dzięki. Właśnie próbowałem tego, ale :set tw-=2wyniki E487: Argument must be positive: tw-=2. Czy składnia jest nieprawidłowa?
Andrew Ferrier
1
Sprawdź bieżącą wartość szerokości tekstu za pomocą :set tw?. Domyślam się, że masz szerokość tekstu ustawioną na 0. W tych krokach założono, że szerokość tekstu zaczyna się od liczby znaków, które chcesz w wierszu.
Matt Boehm
W twoim przykładzie prawdopodobnie chcesz s/2/3/dodać spację przed cytatem zamykającym, długi łańcuch nie będzie miał odpowiednich odstępów między słowami.
cdosborn
1

Parsowanie i formatowanie kodu jest trudne, szczególnie w przypadku dynamicznych języków, takich jak Python.

Zamiast próbować parsować Pythona w Vimscript, zalecam użycie zewnętrznego formatera, takiego jak yapf . Z moją odpowiedzią tutaj możesz napisać to automatycznie po napisaniu za pomocą:

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

s:write_cmd()Funkcja jest w innej odpowiedzi - Nie będę je dodać tutaj, więc nie będą musieli aktualizować go w dwóch miejscach, jeśli znajdę błąd :-)

Można również ustawić formatprgna yapfi ręcznie formatować je gq.

Martin Tournoij
źródło
Cześć. Pomocna odpowiedź, ale oddaję głos, ponieważ nie sądzę, aby odpowiedziała na to pytanie: to pytanie bardzo konkretnie dotyczy sformatowania ciągów wielowierszowych, co yapfwydaje się nie działać. Niemniej jednak zgadzam się, że wygląda na użyteczne narzędzie.
Andrew Ferrier
@AndrewFerrier Wystarczająco; Mógłbym przysiąc, że widziałem, jak formatowałam łańcuchy, kiedy testowałem to innego dnia, ale nie mogę go teraz uruchomić: - / Chyba byłem zdezorientowany.
Martin Tournoij
0

Następujące funkcje i odpowiadające im odwzorowania rozwiązały dla mnie ten problem:

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
źródło