mirror of
https://github.com/sheerun/vim-polyglot.git
synced 2025-11-08 11:33:52 -05:00
307 lines
8.3 KiB
VimL
307 lines
8.3 KiB
VimL
if polyglot#init#is_disabled(expand('<sfile>:p'), 'jsx', 'autoload/jsx_pretty/indent.vim')
|
|
finish
|
|
endif
|
|
|
|
if exists('*shiftwidth')
|
|
function! s:sw()
|
|
return shiftwidth()
|
|
endfunction
|
|
else
|
|
function! s:sw()
|
|
return &sw
|
|
endfunction
|
|
endif
|
|
|
|
" Regexp for the start tag
|
|
let s:start_tag = '<\_s*\%(>\|\${\|\%(\<[-:._$A-Za-z0-9]\+\>\)\)'
|
|
" Regexp for the end tag
|
|
let s:end_tag = '\%(<\_s*/\_s*\%(\<[-:._$A-Za-z0-9]\+\>\)\_s*>\|/\_s*>\)'
|
|
|
|
function s:trim(line)
|
|
return substitute(a:line, '^\s*\|\s*$', '', 'g')
|
|
endfunction
|
|
|
|
" Get the syntax stack at the given position
|
|
function s:syntax_stack_at(lnum, col)
|
|
return map(synstack(a:lnum, a:col), 'synIDattr(v:val, "name")')
|
|
endfunction
|
|
|
|
" Get the syntax at the given position
|
|
function s:syntax_at(lnum, col)
|
|
return synIDattr(synID(a:lnum, a:col, 1), 'name')
|
|
endfunction
|
|
|
|
" Get the start col of the non-space charactor
|
|
function s:start_col(lnum)
|
|
return len(matchstr(getline(a:lnum), '^\s*')) + 1
|
|
endfunction
|
|
|
|
" Get the start syntax of a given line number
|
|
function s:start_syntax(lnum)
|
|
return s:syntax_at(a:lnum, s:start_col(a:lnum))
|
|
endfunction
|
|
|
|
" The skip function for searchpair
|
|
function s:skip_if_not(current_lnum, ...)
|
|
" Skip the match in current line
|
|
if line('.') == a:current_lnum
|
|
return 1
|
|
endif
|
|
|
|
let syntax = s:syntax_at(line('.'), col('.'))
|
|
return syntax !~? join(a:000, '\|')
|
|
endfunction
|
|
|
|
" Whether the specified stytax group is the opening tag
|
|
function s:is_opening_tag(syntax)
|
|
return a:syntax =~? 'jsxOpenPunct'
|
|
endfunction
|
|
|
|
" Whether the specified stytax group is the closing tag
|
|
function s:is_closing_tag(syntax)
|
|
return a:syntax =~? 'jsxClose'
|
|
endfunction
|
|
|
|
" Whether the specified syntax group is the jsxRegion
|
|
function s:is_jsx_region(syntax)
|
|
return a:syntax =~? 'jsxRegion'
|
|
endfunction
|
|
|
|
" Whether the specified syntax group is the jsxElement
|
|
function s:is_jsx_element(syntax)
|
|
return a:syntax =~? 'jsxElement'
|
|
endfunction
|
|
|
|
" Whether the specified syntax group is the jsxExpressionBlock
|
|
function s:is_jsx_expression(syntax)
|
|
return a:syntax =~? 'jsxExpressionBlock'
|
|
endfunction
|
|
|
|
" Whether the specified syntax group is the jsxBraces
|
|
function s:is_jsx_brace(syntax)
|
|
return a:syntax =~? 'jsxBraces'
|
|
endfunction
|
|
|
|
" Whether the specified syntax group is the jsxComment
|
|
function s:is_jsx_comment(syntax)
|
|
return a:syntax =~? 'jsxComment'
|
|
endfunction
|
|
|
|
" Whether the specified line is comment related syntax
|
|
function s:is_comment(syntax)
|
|
return a:syntax =~? 'comment'
|
|
endfunction
|
|
|
|
" Whether the specified syntax group is the jsxComment
|
|
function s:is_jsx_backticks(syntax)
|
|
return a:syntax =~? 'jsxBackticks'
|
|
endfunction
|
|
|
|
" Get the prvious line number
|
|
function s:prev_lnum(lnum)
|
|
return prevnonblank(a:lnum - 1)
|
|
endfunction
|
|
|
|
" Whether the given pos is the parent of the given element who has
|
|
" element_count jsxElement syntax
|
|
function s:is_parent_element(pos, element_count)
|
|
let syntax_stack = s:syntax_stack_at(a:pos[0], a:pos[1])
|
|
return s:is_opening_tag(syntax_stack[-1]) &&
|
|
\ count(syntax_stack, 'jsxElement') <= a:element_count
|
|
endfunction
|
|
|
|
" Compute the indention of the trail punct
|
|
function s:jsx_indent_trail_punct(lnum)
|
|
let pair_line = searchpair('<', '', '>', 'bW', 's:skip_if_not(a:lnum, "jsxOpenPunct", "jsxClose")')
|
|
return indent(pair_line)
|
|
endfunction
|
|
|
|
" Compute the indention of the closing tag
|
|
function s:jsx_indent_closing_tag(lnum)
|
|
let pair_line = searchpair(s:start_tag, '', s:end_tag, 'bW', 's:skip_if_not(a:lnum, "jsxOpenPunct", "jsxClose")')
|
|
return pair_line ? indent(pair_line) : indent(a:lnum)
|
|
endfunction
|
|
|
|
" Compute the indentation of the jsxElement
|
|
function s:jsx_indent_element(lnum)
|
|
let syntax_stack = s:syntax_stack_at(a:lnum, s:start_col(a:lnum))
|
|
let syntax_name = syntax_stack[-1]
|
|
let element_count = count(syntax_stack, 'jsxElement')
|
|
|
|
if s:trim(getline(a:lnum)) =~ '^>'
|
|
return s:jsx_indent_trail_punct(a:lnum)
|
|
endif
|
|
|
|
" If current tag is closing tag
|
|
if s:is_closing_tag(syntax_name)
|
|
return s:jsx_indent_closing_tag(a:lnum)
|
|
endif
|
|
|
|
" Normalize the jsxElement count for opening tag
|
|
if s:is_opening_tag(syntax_name)
|
|
" <div>
|
|
" <div></div> <-- jsxRegion->jsxElement->jsxElement->jsxTag->jsxOpenTag->jsxOpenPunct
|
|
" </div>
|
|
if s:is_jsx_element(syntax_stack[-4]) && s:is_jsx_element(syntax_stack[-5])
|
|
let element_count = element_count - 1
|
|
endif
|
|
endif
|
|
|
|
let start_time = localtime()
|
|
let pos = searchpos(s:start_tag, 'bW')
|
|
|
|
while !s:is_parent_element(pos, element_count)
|
|
if localtime() - start_time >= 0.5
|
|
return -1
|
|
endif
|
|
let pos = searchpos(s:start_tag, 'bW')
|
|
endwhile
|
|
|
|
return indent(pos[0]) + s:sw()
|
|
endfunction
|
|
|
|
" Compute the indentation of the comment
|
|
function s:jsx_indent_comment(lnum)
|
|
let line = s:trim(getline(a:lnum))
|
|
|
|
if s:is_jsx_comment(s:start_syntax(a:lnum))
|
|
if line =~ '^<!--' || line =~ '^-->'
|
|
return s:jsx_indent_element(a:lnum)
|
|
else
|
|
return s:jsx_indent_element(a:lnum) + s:sw()
|
|
endif
|
|
else
|
|
if line =~ '^/\*' || line =~ '^//'
|
|
return s:jsx_indent_element(a:lnum)
|
|
else
|
|
return s:jsx_indent_element(a:lnum) + 1
|
|
endif
|
|
endif
|
|
endfunction
|
|
|
|
" Compute the indentation of jsxBackticks
|
|
function s:jsx_indent_backticks(lnum)
|
|
let tags = get(g:, 'vim_jsx_pretty_template_tags', ['html', 'jsx'])
|
|
let start_tag = '\%(' . join(tags, '\|') . '\)`'
|
|
let end_tag = '\%(' . join(tags, '\|') . '\)\@<!`'
|
|
let pair_line = searchpair(start_tag, '', end_tag, 'bW', 's:skip_if_not(a:lnum)')
|
|
|
|
return indent(pair_line)
|
|
endfunction
|
|
|
|
" Syntax context types:
|
|
" - jsxRegion
|
|
" - jsxTaggedRegion
|
|
" - jsxElement
|
|
" - jsxExpressionBlock
|
|
" - Other
|
|
function s:syntax_context(lnum)
|
|
let start_col = s:start_col(a:lnum)
|
|
let syntax_stack = s:syntax_stack_at(a:lnum, start_col)
|
|
let start_syntax = syntax_stack[-1]
|
|
let reversed = reverse(syntax_stack)
|
|
let i = 0
|
|
|
|
for syntax_name in reversed
|
|
" If the current line is jsxExpressionBlock and not starts with jsxBraces
|
|
if s:is_jsx_expression(syntax_name)
|
|
return 'jsxExpressionBlock'
|
|
endif
|
|
|
|
if s:is_jsx_region(syntax_name)
|
|
return 'jsxRegion'
|
|
endif
|
|
|
|
if s:is_jsx_element(syntax_name)
|
|
" If current line starts with the opening tag
|
|
if s:is_opening_tag(start_syntax) || s:is_closing_tag(start_syntax)
|
|
" And the next syntax is jsxRegion
|
|
if s:is_jsx_region(reversed[i+1])
|
|
return 'jsxRegion'
|
|
elseif reversed[i+1] =~ 'jsxTaggedRegion'
|
|
return 'jsxTaggedRegion'
|
|
else
|
|
return 'jsxElement'
|
|
endif
|
|
elseif reversed[i+1] =~ 'jsxTaggedRegion'
|
|
return 'jsxTaggedRegion'
|
|
else
|
|
return 'jsxElement'
|
|
endif
|
|
endif
|
|
|
|
let i = i + 1
|
|
endfor
|
|
|
|
return 'Other'
|
|
endfunction
|
|
|
|
|
|
function! jsx_pretty#indent#get(js_indent)
|
|
let line = s:trim(getline(v:lnum))
|
|
let start_syntax = s:start_syntax(v:lnum)
|
|
|
|
if s:is_jsx_backticks(start_syntax)
|
|
return s:jsx_indent_backticks(v:lnum)
|
|
endif
|
|
|
|
if s:is_jsx_brace(start_syntax)
|
|
return s:jsx_indent_element(v:lnum)
|
|
endif
|
|
|
|
if s:is_opening_tag(start_syntax) && line =~ '^>'
|
|
return s:jsx_indent_trail_punct(v:lnum)
|
|
endif
|
|
|
|
let syntax_context = s:syntax_context(v:lnum)
|
|
|
|
if syntax_context == 'jsxRegion'
|
|
if s:is_closing_tag(start_syntax)
|
|
return s:jsx_indent_closing_tag(v:lnum)
|
|
endif
|
|
|
|
let prev_lnum = s:prev_lnum(v:lnum)
|
|
let prev_line = s:trim(getline(prev_lnum))
|
|
|
|
if prev_line =~ '[([{=?]$'
|
|
return indent(prev_lnum) + s:sw()
|
|
elseif prev_line =~ '[:|&<>]$' &&
|
|
\ s:trim(getline(s:prev_lnum(prev_lnum))) !~ '[?:|&<>]$'
|
|
return indent(prev_lnum) + s:sw()
|
|
else
|
|
return indent(prev_lnum)
|
|
endif
|
|
elseif syntax_context == 'jsxTaggedRegion'
|
|
if s:is_closing_tag(start_syntax)
|
|
return s:jsx_indent_closing_tag(v:lnum)
|
|
elseif s:is_jsx_comment(start_syntax)
|
|
return s:jsx_indent_comment(v:lnum)
|
|
else
|
|
return indent(s:prev_lnum(v:lnum)) + s:sw()
|
|
endif
|
|
elseif syntax_context == 'jsxElement'
|
|
if s:is_jsx_comment(start_syntax)
|
|
return s:jsx_indent_comment(v:lnum)
|
|
endif
|
|
|
|
if s:is_comment(start_syntax)
|
|
return s:jsx_indent_comment(v:lnum)
|
|
endif
|
|
|
|
return s:jsx_indent_element(v:lnum)
|
|
elseif syntax_context == 'jsxExpressionBlock'
|
|
let prev_lnum = s:prev_lnum(v:lnum)
|
|
let prev_line = s:trim(getline(prev_lnum))
|
|
|
|
if line =~ '^?'
|
|
return indent(prev_lnum) + s:sw()
|
|
elseif line =~ '^:'
|
|
return indent(prev_lnum)
|
|
else
|
|
return a:js_indent()
|
|
endif
|
|
endif
|
|
|
|
return a:js_indent()
|
|
endfunction
|