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

250 lines
5.7 KiB
VimL

" vim: foldmethod=marker
" Split / Join {{{1
"
" function! sj#elm#JoinBraces() {{{2
"
" Attempts to join the multiline tuple, record or list closest
" to current cursor position.
function! sj#elm#JoinBraces()
let [from, to] = s:ClosestMultilineBraces()
if from[0] < 0
return 0
endif
let original = sj#GetByPosition([0, from[0], from[1]], [0, to[0], to[1]])
let joined = s:JoinOuterBraces(s:JoinLines(original))
call sj#ReplaceByPosition([0, from[0], from[1]], [0, to[0], to[1]], joined)
return 1
endfunction
" function! sj#elm#SplitBraces() {{{2
"
" Attempts to split the tuple, record or list furthest to current
" cursor position on the same line.
function! sj#elm#SplitBraces()
try
call sj#PushCursor()
let [from, to] = s:CurrentLineOutermostBraces(col('.'))
if from < 0
return 0
endif
let parts = s:SplitParts(from, to)
if len(parts) <= 2
return 0
endif
let replacement = join(parts, "\n")
call cursor(line('.'), from)
let [previousLine, previousCol] = searchpos('\S', 'bn')
if previousLine == line('.') && previousCol > 0 && s:CharAt(previousCol) =~ '[=:]'
let replacement = "\n".replacement
let from = previousCol + 1
end
call sj#ReplaceCols(from, to, replacement)
return 1
finally
call sj#PopCursor()
endtry
endfunction
" Helper functions {{{1
" function! s:SkipSyntax() {{{2
"
" Returns Elm's typical skippable syntax (strings and comments)
function! s:SkipSyntax()
return sj#SkipSyntax(['elmString', 'elmTripleString', 'elmComment'])
endfunction
" function! s:CurrentLineClosestBraces(column) {{{2
"
" Returns the columns corresponding to the bracing pair closest
" to and surrounding given column as a list ([opening, closing]).
" Returns [-1, -1] when there is none on the same line.
function! s:CurrentLineClosestBraces(column)
try
call sj#PushCursor()
let skip = s:SkipSyntax()
let currentLine = line('.')
call cursor(currentLine, a:column)
let from = searchpairpos('[{(\[]', '', '[})\]]', 'bcn', skip, currentLine)
if from[0] == 0
return [-1, -1]
end
call cursor(from[0], from[1])
normal! %
if line('.') != currentLine
return [-1, -1]
endif
let to = col('.')
return [from[1], to]
finally
call sj#PopCursor()
endtry
endfunction
" function! s:CurrentLineOutermostBraces(column) {{{2
"
" Returns the colums corresponding to the bracing pair furthest
" to and surrounding given column as a list ([opening, closing]).
" Returns [-1, -1] when there is none on the same line.
function! s:CurrentLineOutermostBraces(column)
if a:column < 1
return [-1, -1]
endif
let currentMatch = s:CurrentLineClosestBraces(a:column)
if currentMatch[0] < 1
return [-1, -1]
endif
let betterMatch = s:CurrentLineOutermostBraces(currentMatch[0] - 1)
if betterMatch[0] < 1
return currentMatch
endif
return betterMatch
endfunction
" function! s:ClosestMultilineBraces() {{{2
"
" Returns the position of the closest pair of multiline braces
" around the cursor.
"
" The positions are given as a couple of arrays:
"
" [[startLine, startCol], [endLine, endCol]]
"
" When no position is found, returns:
"
" [[-1, -1], [-1, -1]]
function! s:ClosestMultilineBraces()
try
call sj#PushCursor()
let skip = s:SkipSyntax()
let currentLine = line('.')
normal! $
let [toLine, toCol] = searchpairpos('[{(\[]', '', '[})\]]', '', skip, line('$'))
if toLine < currentLine
return [[-1, -1], [-1, -1]]
endif
normal! %
let fromLine = line('.')
let fromCol = col('.')
if fromLine >= toLine
return [[-1, -1], [-1, -1]]
endif
return [[fromLine, fromCol], [toLine, toCol]]
finally
call sj#PopCursor()
endtry
endfunction
" function! s:JoinOuterBraces(text)
"
" Removes spaces separating the first and last char of a string
" with the closest non-space char found.
"
" ex:
"
" [ 1, 2, 3 ] => [1, 2, 3]
function! s:JoinOuterBraces(text)
return substitute(substitute(a:text, '\s*\(.\)$', '\1', ''), '^\(.\)\s*', '\1', '')
endfunction
" function! s:JoinLines(text)
"
" Joins lines in text, removing complementary spaces around
" newline chars when the last line starts with a ',' and keeping
" one whitespace for other cases.
function! s:JoinLines(text)
return substitute(substitute(a:text, '\s*\n\s*,', ',', 'g'), '\s*\n\s*', ' ', 'g')
endfunction
" function! s:SplitParts(from, to)
"
" Splits a section of a line according to bracing characters
" rules.
"
" ex:
" "[1,2,3]"
" v
" [ "[ 1", ", 2", ", 3", "]" ]
"
" "{a | foo = bar, baz = buzz}"
" v
" [ "{ a", "| foo = bar", ", baz = buzz", "}" ]
function! s:SplitParts(from, to)
try
call sj#PushCursor()
let skip = s:SkipSyntax()
let currentLine = line('.')
call cursor(currentLine, a:from)
let openingCol = a:from
let openingChar = s:CurrentChar()
let parts = []
while col('.') < a:to
call searchpair('[{(\[]', ',\|\(\(<\)\@<!|\(>\)\@!\)', '[})\]]', '', skip, currentLine)
let closingCol = col('.')
let closingChar = s:CurrentChar()
let part = openingChar.' '.sj#Trim(sj#GetByPosition([0, currentLine, openingCol + 1, 0], [0, currentLine, closingCol - 1, 0]))
call add(parts, part)
let openingCol = closingCol
let openingChar = closingChar
call cursor(currentLine, openingCol)
endwhile
call add(parts, s:CharAt(a:to))
return parts
finally
call sj#PopCursor()
endtry
endfunction
" function! s:CharAt(column)
"
" Returns the char sitting at given column of current line.
function! s:CharAt(column)
return getline('.')[a:column - 1]
endfunction
" function! s:CurrentChar()
"
" Returns the character at current cursor's position
function! s:CurrentChar()
return s:CharAt(col('.'))
endfunction