" 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