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

207 lines
6.0 KiB
VimL

" Constructor:
" ============
function! sj#argparser#common#Construct(start_column, end_column, line)
let parser = {
\ 'args': [],
\ 'opts': [],
\ 'body': a:line,
\ 'index': a:start_column - 1,
\ 'current_arg': '',
\ 'current_arg_type': 'normal',
\
\ 'Process': function('sj#argparser#common#Process'),
\ 'PushArg': function('sj#argparser#common#PushArg'),
\ 'PushChar': function('sj#argparser#common#PushChar'),
\ 'Next': function('sj#argparser#common#Next'),
\ 'JumpPair': function('sj#argparser#common#JumpPair'),
\ 'AtFunctionEnd': function('sj#argparser#common#AtFunctionEnd'),
\ 'Finished': function('sj#argparser#common#Finished'),
\ }
if a:start_column > 1
let parser.body = strpart(parser.body, a:start_column - 1)
endif
if a:end_column > 1
let parser.body = strpart(parser.body, 0, (a:end_column - a:start_column) + 1)
endif
return parser
endfunction
" Methods:
" ========
function! sj#argparser#common#Process() dict
throw "Not implemented"
" " Implementation might go something like this:
"
" while !self.Finished()
" if self.body[0] == ','
" call self.PushArg()
" call self.Next()
" continue
" elseif self.AtFunctionEnd()
" break
" else
" " ...
" endif
" call self.PushChar()
" endwhile
" if len(self.current_arg) > 0
" call self.PushArg()
" endif
endfunction
" Pushes the current argument to the args and initializes a new one.
function! sj#argparser#common#PushArg() dict
call add(self.args, sj#Trim(self.current_arg))
let self.current_arg = ''
let self.current_arg_type = 'normal'
endfunction
" Moves the parser to the next char and consumes the current
function! sj#argparser#common#PushChar() dict
let self.current_arg .= self.body[0]
call self.Next()
endfunction
" Moves the parser to the next char without consuming it.
function! sj#argparser#common#Next() dict
let self.body = strpart(self.body, 1)
let self.index = self.index + 1
endfunction
" Finds the current char in a:start_chars and jumps to its match in a:end_chars.
"
" Example:
" call parser.JumpPair("([", ")]")
"
" This will parse matching round and square brackets.
"
" Note: nesting doesn't work properly if there's a string containing unmatched
" braces within the pair.
function! sj#argparser#common#JumpPair(start_chars, end_chars) dict
let char_index = stridx(a:start_chars, self.body[0])
let start_char = a:start_chars[char_index]
let target_char = a:end_chars[char_index]
" prepare a stack for nested braces and the like
let stack = 1
let n = 0
let limit = len(self.body)
" Note: if the start and end chars are the same (quotes, for example), this
" will still work, because we're checking for the target_char before the
" start_char
while stack > 0 && n < limit
let n = n + 1
if self.body[n] == target_char
let stack = stack - 1
elseif self.body[n] == start_char
let stack = stack + 1
endif
endwhile
let self.current_arg .= strpart(self.body, 0, n)
let self.body = strpart(self.body, n)
let self.index = self.index + n
endfunction
" Returns true if the parser has finished parsing the arguments.
function! sj#argparser#common#Finished() dict
return len(self.body) <= 0
endfunction
" Returns true if the parser is at the function's end
function! sj#argparser#common#AtFunctionEnd() dict
return (self.body[0] == ')')
endfunction
" Public functions:
" =================
" This code was originally created for ruby functions, but seems to be useful
" for elixir as well, with some modifications.
"
function! sj#argparser#common#LocateRubylikeFunction(keyword_pattern, syntax_groups)
call sj#PushCursor()
let start_col = col('.')
let skip = sj#SkipSyntax(a:syntax_groups)
" The first pattern matches functions with brackets and consists of the
" following:
"
" - a keyword
" - an opening round bracket
" - something that's not a comma and doesn't look like an operator
" (to avoid a few edge cases)
"
let pattern = '\v(^|\s|\.|::)\m' . a:keyword_pattern . '\v\(\s*[^,=<>+-/*^%})\]]'
let found = sj#SearchSkip(pattern, skip, 'bcW', line('.'))
if found <= 0
" try searching forward
let found = sj#SearchSkip(pattern, skip, 'cW', line('.'))
endif
if found > 0
" first, figure out the function name
call search('\k\+', 'cW', line('.'))
let function_name = expand('<cword>')
" go to the end of the matching pattern
call search(pattern, 'cWe', line('.'))
" look for the starting bracket
if sj#SearchSkip(a:keyword_pattern . '\s*\zs(\s*\%#', skip, 'bcW', line('.'))
let function_type = 'with_round_braces'
let from = col('.') + 1
normal! h%h
let to = col('.')
if sj#ColBetween(start_col, from - 1, to + 1)
return [function_name, from, to, function_type]
endif
endif
endif
call sj#PopCursor()
" The second pattern matches functions without brackets:
"
" - a keyword
" - at least one space
" - something that's not a comma and doesn't look like an operator
" (to avoid a few edge cases)
"
let pattern = '\v(^|\s|\.|::)\m' . a:keyword_pattern . '\v\s+[^ ,=<>+-/*^%})\]]'
let found = sj#SearchSkip(pattern, skip, 'bcW', line('.'))
if found <= 0
" try searching forward
let found = sj#SearchSkip(pattern, skip, 'cW', line('.'))
endif
if found > 0
" first, figure out the function name
call search('\k\+', 'cW', line('.'))
let function_name = expand('<cword>')
let function_start_col = col('.')
" go to the end of the matching pattern
call search(pattern, 'cWe', line('.'))
let function_type = 'with_spaces'
let from = col('.')
let to = -1 " we're not sure about the end
if sj#ColBetween(start_col, function_start_col - 1, col('$'))
return [function_name, from, to, function_type]
endif
endif
return ['', -1, -1, 'none']
endfunction