1
0
mirror of https://github.com/SpaceVim/SpaceVim.git synced 2025-02-03 07:10:05 +08:00
SpaceVim/bundle/splitjoin.vim/autoload/sj/python.vim
2024-06-27 18:10:36 +08:00

423 lines
11 KiB
VimL

let s:skip = sj#SkipSyntax(['pythonString', 'pythonComment', 'pythonStrInterpRegion'])
function! sj#python#SplitStatement()
if sj#SearchSkip('^[^:]*\zs:\s*\S', s:skip, 'c', line('.'))
call sj#Keeppatterns('s/\%#:\s*/:\r/')
normal! ==
return 1
else
return 0
endif
endfunction
function! sj#python#JoinStatement()
if sj#SearchSkip(':\s*$', s:skip, 'c', line('.')) > 0
join
return 1
else
return 0
endif
endfunction
function! sj#python#SplitDict()
let [from, to] = sj#LocateBracesAroundCursor('{', '}', ['pythonString'])
if from < 0 && to < 0
return 0
else
let pairs = sj#ParseJsonObjectBody(from + 1, to - 1)
let body = "{\n".join(pairs, ",\n")."\n}"
if sj#settings#Read('trailing_comma')
let body = substitute(body, ',\?\n}', ',\n}', '')
endif
call sj#ReplaceMotion('Va{', body)
let body_start = line('.') + 1
let body_end = body_start + len(pairs)
let base_indent = indent('.')
for line in range(body_start, body_end)
if base_indent == indent(line)
" then indentation didn't work quite right, let's just indent it
" ourselves
exe line.'normal! >>>>'
endif
endfor
exe body_start.','.body_end.'normal! =='
return 1
endif
endfunction
function! sj#python#JoinDict()
let line = getline('.')
if line =~ '{\s*$'
call search('{', 'c', line('.'))
let body = sj#GetMotion('Vi{')
let lines = sj#TrimList(split(body, "\n"))
if sj#settings#Read('normalize_whitespace')
let lines = map(lines, 'substitute(v:val, ":\\s\\+", ": ", "")')
endif
let body = join(lines, ' ')
if sj#settings#Read('trailing_comma')
let body = substitute(body, ',\?$', '', '')
endif
call sj#ReplaceMotion('Va{', '{'.body.'}')
return 1
else
return 0
endif
endfunction
function! sj#python#SplitArray()
return s:SplitList('\[.*]', '[', ']')
endfunction
function! sj#python#JoinArray()
return s:JoinList('\[[^]]*\s*$', '[', ']')
endfunction
function! sj#python#SplitTuple()
return s:SplitList('(.\{-})', '(', ')')
endfunction
function! sj#python#JoinTuple()
return s:JoinList('([^)]*\s*$', '(', ')')
endfunction
function! sj#python#SplitImport()
let import_pattern = '^from \%(.*\) import \zs.*$'
normal! 0
if search(import_pattern, 'Wc', line('.')) <= 0
return 0
endif
let import_list = sj#GetMotion('vg_')
if stridx(import_list, ',') < 0
return 0
endif
let imports = split(import_list, ',\s*')
call sj#ReplaceMotion('vg_', join(imports, ",\\\n"))
return 1
endfunction
function! sj#python#JoinImport()
let import_pattern = '^from \%(.*\) import .*\\\s*$'
if getline('.') !~ import_pattern
return 0
endif
let start_lineno = line('.')
let current_lineno = nextnonblank(start_lineno + 1)
while getline(current_lineno) =~ '\\\s*$' && current_lineno < line('$')
let current_lineno = nextnonblank(current_lineno + 1)
endwhile
let end_lineno = current_lineno
exe start_lineno.','.end_lineno.'s/,\\\n\s*/, /e'
return 1
endfunction
function! sj#python#SplitAssignment()
if sj#SearchUnderCursor('^\s*\%(\%(\k\|\.\)\+,\s*\)\+\%(\k\|\.\)\+\s*=\s*\S') <= 0
return 0
endif
let variables = split(sj#Trim(sj#GetMotion('vt=')), ',\s*')
normal! f=
call search('\S', 'W', line('.'))
let values = sj#ParseJsonObjectBody(col('.'), col('$'))
let indent = substitute(getline('.'), '^\(\s*\).*', '\1', '')
let lines = []
if len(variables) == len(values)
let index = 0
for variable in variables
call add(lines, indent.variable.' = '.values[index])
let index += 1
endfor
elseif len(values) == 1
" consider it an array, and index it
let index = 0
let array = values[0]
for variable in variables
call add(lines, indent.variable.' = '.array.'['.index.']')
let index += 1
endfor
else
" the sides don't match, let's give up
return 0
endif
call sj#ReplaceMotion('V', join(lines, "\n"))
if sj#settings#Read('align')
call sj#Align(line('.'), line('.') + len(lines) - 1, 'equals')
endif
endfunction
function! sj#python#JoinAssignment()
let assignment_pattern = '^\s*\%(\k\|\.\)\+\zs\s*=\s*\ze\S'
if search(assignment_pattern, 'W', line('.')) <= 0
return 0
endif
let start_line = line('.')
let [first_variable, first_value] = split(getline('.'), assignment_pattern)
let variables = [ first_variable ]
let values = [ first_value ]
let end_line = start_line
let next_line = line('.') + 1
while next_line > 0 && next_line <= line('$')
exe next_line
if search(assignment_pattern, 'W', line('.')) <= 0
break
else
let [variable, value] = split(getline(next_line), assignment_pattern)
call add(variables, sj#Trim(variable))
call add(values, sj#Trim(value))
let end_line = next_line
let next_line += 1
endif
if v:count > 0 && v:count == (end_line - start_line + 1)
" stop at the user-provided count
break
endif
endwhile
if len(variables) <= 1
return 0
endif
if len(values) > 1 && values[0] =~ '\[0\]$'
" it might be an array, so we could simplify it
let is_array = 1
let index = 1
let array_name = substitute(values[0], '\[0\]$', '', '')
for value in values[1:]
if value !~ '^'.array_name.'\s*\['.index.'\]'
let is_array = 0
break
endif
let index += 1
endfor
if is_array
" the entire right-hand side can be just one item
let values = [ array_name ]
endif
endif
let body = join(variables, ', ').' = '.join(values, ', ')
call sj#ReplaceLines(start_line, end_line, body)
return 1
endfunction
function! sj#python#SplitTernaryAssignment()
if getline('.') !~ '^\s*\%(\k\|\.\)\+\s*=\s*\S'
return 0
endif
normal! 0
let include_syntax = sj#IncludeSyntax(['pythonConditional'])
if sj#SearchSkip('\<if\>', include_syntax, 'W', line('.')) <= 0
return 0
endif
let if_col = col('.')
if sj#SearchSkip('\<else\>', include_syntax, 'W', line('.')) <= 0
return 0
endif
let else_col = col('.')
let line = getline('.')
let assignment_if_true = trim(strpart(line, 0, if_col - 1))
let if_clause = trim(strpart(line, if_col - 1, else_col - if_col))
let body_if_false = trim(strpart(line, else_col + len('else')))
let assignment_prefix = matchstr(assignment_if_true, '\%(\k\|\.\)\+\s*=')
let assignment_if_false = assignment_prefix . ' ' . body_if_false
let indent = repeat(' ', shiftwidth())
let base_indent = repeat(' ', indent(line('.')))
let body = join([
\ base_indent . if_clause . ':',
\ base_indent . indent . assignment_if_true,
\ base_indent . 'else:',
\ base_indent . indent . assignment_if_false,
\ ], "\n")
call sj#ReplaceMotion('V', body)
return 1
endfunction
function! sj#python#JoinTernaryAssignment()
let include_syntax = sj#IncludeSyntax(['pythonConditional'])
let start_lineno = line('.')
normal! 0
if sj#SearchSkip('^\s*\zsif\>', include_syntax, 'Wc', line('.')) <= 0
return 0
endif
let if_line = trim(getline('.'))
if if_line !~ ':$'
return 0
endif
let if_clause = strpart(if_line, 0, len(if_line) - 1)
if search('^\s*\zs\%(\k\|\.\)\+\s*=\s*\S', 'Wc', line('.') + 1) <= 0
return 0
endif
let assignment_if_true = trim(getline('.'))
let lhs_if_true = matchstr(assignment_if_true, '^\s*\zs\%(\k\|\.\)\+\s*=')
let body_if_true = trim(strpart(assignment_if_true, len(lhs_if_true)))
if sj#SearchSkip('^\s*\zselse:', include_syntax, 'Wc', line('.') + 2) <= 0
return 0
endif
let else_line = trim(getline('.'))
if else_line !~ ':$'
return 0
endif
if search('^\s*\zs\%(\k\|\.\)\+\s*=\s*\S', 'Wc', line('.') + 3) <= 0
return 0
endif
let assignment_if_false = trim(getline('.'))
let lhs_if_false = matchstr(assignment_if_false, '^\s*\zs\%(\k\|\.\)\+\s*=')
let body_if_false = trim(strpart(assignment_if_false, len(lhs_if_false)))
if lhs_if_true != lhs_if_false
return 0
endif
let body = lhs_if_true . ' ' . body_if_true . ' ' . if_clause . ' else ' . body_if_false
call sj#ReplaceLines(start_lineno, start_lineno + 3, body)
return 1
endfunction
function! s:SplitList(regex, opening_char, closing_char)
let [from, to] = sj#LocateBracesAroundCursor(a:opening_char, a:closing_char, ['pythonString'])
if from < 0 && to < 0
return 0
endif
call sj#PushCursor()
let items = sj#ParseJsonObjectBody(from + 1, to - 1)
if len(items) <= 1
call sj#PopCursor()
return 0
endif
if sj#settings#Read('python_brackets_on_separate_lines')
if sj#settings#Read('trailing_comma')
let body = a:opening_char."\n".join(items, ",\n").",\n".a:closing_char
else
let body = a:opening_char."\n".join(items, ",\n")."\n".a:closing_char
endif
else
let body = a:opening_char.join(items, ",\n").a:closing_char
endif
call sj#PopCursor()
call sj#ReplaceMotion('va'.a:opening_char, body)
return 1
endfunction
function! s:JoinList(regex, opening_char, closing_char)
if sj#SearchUnderCursor(a:regex) <= 0
return 0
endif
let body = sj#GetMotion('va'.a:opening_char)
let body = substitute(body, '\_s\+', ' ', 'g')
let body = substitute(body, '^'.a:opening_char.'\s\+', a:opening_char, '')
if sj#settings#Read('trailing_comma')
let body = substitute(body, ',\?\s\+'.a:closing_char.'$', a:closing_char, '')
else
let body = substitute(body, '\s\+'.a:closing_char.'$', a:closing_char, '')
endif
call sj#ReplaceMotion('va'.a:opening_char, body)
return 1
endfunction
function! sj#python#SplitListComprehension()
for [opening_char, closing_char] in [['(', ')'], ['[', ']'], ['{', '}']]
let [from, to] = sj#LocateBracesAroundCursor(opening_char, closing_char, ['pythonString'])
if from > 0 && to > 0
break
endif
endfor
if from < 0 && to < 0
return 0
endif
if to - from < 2
" empty list
return 0
endif
" Start after the opening bracket
let pos = getpos('.')
let pos[2] = from + 1
call setpos('.', pos)
let break_columns = []
let include_syntax = sj#IncludeSyntax(['pythonRepeat', 'pythonConditional'])
while sj#SearchSkip('\<\%(for\|if\)\>', include_syntax, 'W', line('.')) > 0
call add(break_columns, col('.') - from)
endwhile
if len(break_columns) <= 0
return 0
endif
let body = sj#GetMotion('vi' .. opening_char)
let parts = []
let last_break = 0
for break_column in break_columns
let part = strpart(body, last_break, break_column - last_break - 1)
call add(parts, sj#Trim(part))
let last_break = break_column - 1
endfor
let part = strpart(body, last_break, to - last_break - 1)
call add(parts, sj#Trim(part))
if sj#settings#Read('python_brackets_on_separate_lines')
let body = opening_char .. "\n" .. join(parts, "\n") .. "\n" .. closing_char
else
let body = opening_char .. join(parts, "\n") .. closing_char
endif
call sj#ReplaceMotion('va' .. opening_char, body)
return 1
endfunction