Last active
April 23, 2025 03:20
-
-
Save eyalk11/3a0c3404fba880fb11ffa853ea06c5c0 to your computer and use it in GitHub Desktop.
fix indentation of python code in vim
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function! AlignWithTopLine() range | |
" Handle case when selection is at the start of the file | |
if a:firstline <= 1 | |
" If at the start of file, use default indentation (0) | |
let reference_indent = 0 | |
let above_line = "" | |
else | |
" Find the first non-empty line above the selection | |
let above_line_num = a:firstline - 1 | |
while above_line_num >= 1 && getline(above_line_num) =~ '^\s*$' | |
let above_line_num = above_line_num - 1 | |
endwhile | |
" If we couldn't find a non-empty line, use default indentation | |
if above_line_num < 1 | |
let reference_indent = 0 | |
let above_line = "" | |
else | |
let above_line = getline(above_line_num) | |
let reference_indent = indent(above_line_num) | |
endif | |
endif | |
" Check characteristics of the line above (if it exists) | |
let ends_with_colon = !empty(above_line) && above_line =~# ':$' | |
let starts_with_special = !empty(above_line) && above_line =~# '^\s*\(with\|await\|if\|for\|while\|def\|class\)' | |
let is_continuation = !empty(above_line) && above_line =~# '\((\|\[\|{\).*\(,\|\\\)$' | |
" Determine target indentation | |
let target_indent = reference_indent | |
" Handle different cases for alignment | |
if ends_with_colon || starts_with_special | |
" For block starts or special constructs, add one level of indentation | |
let target_indent = reference_indent + &shiftwidth | |
elseif is_continuation | |
" For line continuations (ending with comma or backslash), align with extra indent | |
let target_indent = reference_indent + &shiftwidth | |
endif | |
" Get the first line's current indentation as reference for relative shifts | |
let first_line = getline(a:firstline) | |
let first_line_indent = indent(a:firstline) | |
let first_line_content = substitute(first_line, '^\s*', '', '') | |
" Check if the first line of selection is a continuation of a previous statement | |
let first_line_is_continuation = !empty(above_line) && | |
\ (above_line =~# '\((\|\[\|{\|\\$\|,$\)' && | |
\ first_line_content !~# '^\()\|]\|}\)') | |
" Calculate the indentation shift needed | |
let indent_shift = 0 | |
if first_line_is_continuation | |
let indent_shift = target_indent - first_line_indent | |
else | |
let indent_shift = target_indent - first_line_indent | |
endif | |
" Apply the indentation to all lines in the selection | |
for lineno in range(a:firstline, a:lastline) | |
let line = getline(lineno) | |
let current_indent = indent(lineno) | |
let current_line_content = substitute(line, '^\s*', '', '') | |
" Skip empty lines | |
if current_line_content == '' | |
continue | |
endif | |
" Calculate new indentation preserving relative structure | |
let new_indent = current_indent + indent_shift | |
" Special handling for closing brackets/braces/parentheses | |
if current_line_content =~# '^\()\|]\|}\)' | |
" Closing brackets align with the opening line (one level back) | |
let new_indent = max([0, target_indent - &shiftwidth]) | |
endif | |
" Ensure indentation is never negative | |
let new_indent = max([0, new_indent]) | |
" Create the new line with proper indentation | |
let new_line = repeat(' ', new_indent) . current_line_content | |
call setline(lineno, new_line) | |
endfor | |
endfunction | |
function! ConditionalAlign() | |
if &filetype == 'python' | |
" Store the original selection boundaries | |
let l:start_line = a:firstline | |
let l:end_line = a:lastline | |
" Apply indent to the selection. autopep8 will not align if | |
" with xx: | |
" dosomethin | |
" if there are not indentation | |
norm gv4> | |
" Run autopep8 on the selection, assume indentation = 0 | |
silent execute l:start_line . ',' . l:end_line . '!autopep8 -' | |
" Re-indent to above line | |
silent execute l:start_line . ',' . l:end_line . 'call AlignWithTopLine()' | |
else | |
normal! gv= | |
endif | |
endfunction | |
vnoremap = :'<,'>call ConditionalAlign()<CR> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment