if !exists('g:polyglot_disabled') || !(index(g:polyglot_disabled, 'typescript') != -1 || index(g:polyglot_disabled, 'typescript') != -1 || index(g:polyglot_disabled, 'jsx') != -1) 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*>\)' " 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 end col of the non-space charactor function s:end_col(lnum) return len(substitute(getline(a:lnum), '\s*$', '', 'g')) 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 " Get the end syntax of a given line number function s:end_syntax(lnum) let end_col = len(substitute(getline(a:lnum), '\s*$', '', 'g')) return s:syntax_at(a:lnum, end_col) 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 an jsx-related syntax function s:is_jsx(syntax_name) return a:syntax_name =~ '^jsx' && a:syntax_name !=? 'jsxEscapeJs' endfunction " Whether the specified line is start with jsx-related syntax function s:start_with_jsx(lnum) let start_syntax = s:start_syntax(a:lnum) return s:is_jsx(start_syntax) endfunction " Whether the specified line is end with jsx-related syntax function s:end_with_jsx(lnum) let end_syntax = s:end_syntax(a:lnum) return s:is_jsx(end_syntax) endfunction " Whether the specified line is comment related syntax function s:is_comment(syntax) return a:syntax =~? 'comment' endfunction " Whether the specified line is embedded comment in jsxTag function s:is_embedded_comment(lnum) let start_col = s:start_col(a:lnum) let syntax_stack = s:syntax_stack_at(a:lnum, start_col) let last = get(syntax_stack, -1) let last_2nd = get(syntax_stack, -2) " Patch code for pangloss/vim-javascript "
if last_2nd =~ 'comment' let last_2nd = get(syntax_stack, -3) endif return last =~? 'comment' && last_2nd =~? 'jsxTag' && \ trim(getline(a:lnum)) =~ '\%(^/[*/]\)\|\%(\*/$\)' 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 stytax group is the jsxAttrib function s:is_jsx_attr(syntax) return a:syntax =~? 'jsxAttrib' 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 jsxTag function s:is_jsx_tag(syntax) return a:syntax =~? 'jsxTag' 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 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 " Given a lnum and get the information of its previous line function s:prev_info(lnum) let prev_lnum = s:prev_lnum(a:lnum) let prev_start_syntax = s:start_syntax(prev_lnum) let prev_end_syntax = s:end_syntax(prev_lnum) return [prev_lnum, prev_start_syntax, prev_end_syntax] endfunction " Get the length difference between syntax stack a and b function s:syntax_stack_length_compare(lnum_a, col_a, lnum_b, col_b) let stack_a = s:syntax_stack_at(a:lnum_a, a:col_a) let stack_b = s:syntax_stack_at(a:lnum_b, a:col_b) return len(stack_a) - len(stack_b) endfunction " Determine whether child is a element of parent function s:is_element_of(parent_lnum, child_lnum) let parent_stack = s:syntax_stack_at(a:parent_lnum, s:start_col(a:parent_lnum)) let child_stack = s:syntax_stack_at(a:child_lnum, s:start_col(a:child_lnum)) let element_index = len(child_stack) - index(reverse(child_stack), 'jsxElement') let rest = parent_stack[element_index:] return index(rest, 'jsxElement') == -1 endfunction " Compute the indention of the opening tag function s:jsx_indent_opening_tag(lnum) let [prev_lnum, prev_start_syntax, prev_end_syntax] = s:prev_info(a:lnum) " If current line is start with > if trim(getline(a:lnum)) =~ '^>' let pair_line = searchpair('<', '', '>', 'bW', 's:skip_if_not(a:lnum, "jsxOpenPunct", "jsxClose")') return indent(pair_line) endif " If both the start and the end of the previous line are not jsx-related syntax " return ( "
<-- " ) if !s:end_with_jsx(prev_lnum) && !s:start_with_jsx(prev_lnum) " return -1, so that it will use the default js indent function return -1 endif " If the end of the previous line is not jsx-related syntax " [ "
, "
<-- " ] " should not affect "
( "
<-- "
" )} " > "
"
" {items.map(() => ( " <-- " ))} "
if !s:end_with_jsx(prev_lnum) && \ !s:is_jsx_attr(prev_start_syntax) && \ !s:is_jsx_brace(prev_start_syntax) return indent(prev_lnum) endif " If the start of the previous line is not jsx-related syntax " return
"
<-- "
if !s:start_with_jsx(prev_lnum) return indent(prev_lnum) + s:sw() endif endif "
" " "
<-- "
"
if s:is_closing_tag(prev_start_syntax) return indent(prev_lnum) endif " If the previous line is end with a closing tag "
"
"
<-- "
" should not affect case like "

" <-- "
if s:is_closing_tag(prev_end_syntax) && \ s:syntax_stack_length_compare( \ prev_lnum, s:start_col(prev_lnum), prev_lnum, s:end_col(prev_lnum)) == 2 return indent(prev_lnum) endif " If the start of the previous line is the jsxElement "
" hello "
<-- "
if s:is_jsx_element(prev_start_syntax) || \ s:is_jsx_comment(prev_start_syntax) return indent(prev_lnum) endif " If the start of the prvious line is the jsxBraces { "
" {foo} "
<-- "
" should not affect case like "
" { "
<-- " } "
if s:is_jsx_brace(prev_start_syntax) && \ trim(getline(prev_lnum)) =~ '^[$]\?{' && \ s:syntax_stack_length_compare( \ prev_lnum, s:start_col(prev_lnum), a:lnum, s:start_col(a:lnum)) == -2 return indent(prev_lnum) endif " If the start of the prvious line is the jsxBraces } "
" { " foo " } "
<-- "
if s:is_jsx_brace(prev_start_syntax) && \ trim(getline(prev_lnum)) =~ '}' && \ s:syntax_stack_length_compare( \ prev_lnum, s:start_col(prev_lnum), a:lnum, s:start_col(a:lnum)) == -3 return indent(prev_lnum) endif " Otherwise, indent s:sw() spaces return indent(prev_lnum) + s:sw() 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 indention of the jsxAttrib function s:jsx_indent_attr(lnum) let [prev_lnum, prev_start_syntax, prev_end_syntax] = s:prev_info(a:lnum) " If the start of the previous line is not jsx-related syntax " return
" should not affect "
"
if !s:start_with_jsx(prev_lnum) && \ !s:is_comment(prev_start_syntax) return indent(prev_lnum) + s:sw() endif " If the start of the previous line is the opening tag "
if s:is_opening_tag(prev_start_syntax) return indent(prev_lnum) + s:sw() endif " Otherwise, align the attribute with its previous line return indent(prev_lnum) endfunction " Compute the indentation of the jsxElement function s:jsx_indent_element(lnum) let [prev_lnum, prev_start_syntax, prev_end_syntax] = s:prev_info(a:lnum) " Fix test case like " <-- press enter " should indent as "
<-- if trim(getline(a:lnum)) =~ '^>' && !s:is_opening_tag(prev_end_syntax) return indent(prev_lnum) endif " If the start of the previous line is start with > "
" text <-- "
if s:is_opening_tag(prev_start_syntax) && \ trim(getline(prev_lnum)) =~ '^>$' return indent(prev_lnum) + s:sw() endif "
" text <-- "
" should not affect case like "
"
" hello <-- "
if s:is_opening_tag(prev_start_syntax) && \ s:is_element_of(prev_lnum, a:lnum) return indent(prev_lnum) + s:sw() endif " return
" text <-- "
if !s:start_with_jsx(prev_lnum) return indent(prev_lnum) + s:sw() endif " Otherwise, align with the previous line "
" {foo} " text <-- "
return indent(prev_lnum) endfunction " Compute the indentation of jsxBraces function s:jsx_indent_brace(lnum) let [prev_lnum, prev_start_syntax, prev_end_syntax] = s:prev_info(a:lnum) " If the start of the previous line is start with > "
" {foo} <-- "
if s:is_opening_tag(prev_start_syntax) && \ trim(getline(prev_lnum)) =~ '^>$' return indent(prev_lnum) + s:sw() endif "
" {foo} <-- "
" should not affect case like "
"
" {foo} <-- " text " {foo} <-- "
if s:is_opening_tag(prev_start_syntax) && \ s:syntax_stack_length_compare( \ prev_lnum, s:start_col(prev_lnum), a:lnum, s:start_col(a:lnum)) == 1 return indent(prev_lnum) + s:sw() endif " If current line is the close brace } if trim(getline(a:lnum)) =~ '^}' let pair_line = searchpair('{', '', '}', 'bW', 's:skip_if_not(a:lnum, "jsxBraces")') return indent(pair_line) endif " return
" {foo} <-- "
" should not affect "
"
if !s:start_with_jsx(prev_lnum) && \ !s:is_comment(prev_start_syntax) return indent(prev_lnum) + s:sw() endif return indent(prev_lnum) endfunction " Compute the indentation of the comment function s:jsx_indent_comment(lnum) let [prev_lnum, prev_start_syntax, prev_end_syntax] = s:prev_info(a:lnum) " If current line is jsxComment if s:is_jsx_comment(s:start_syntax(a:lnum)) let line = trim(getline(a:lnum)) if line =~ '^' " Return the paired indent for the closing comment let pair_line = searchpair('', 'bW') return indent(pair_line) else if trim(getline(prev_lnum)) =~ '^ return indent(prev_lnum) + s:sw() else " return indent(prev_lnum) endif endif endif " For comment inside the jsxTag if s:is_opening_tag(prev_start_syntax) return indent(prev_lnum) + s:sw() endif if trim(getline(a:lnum)) =~ '\*/$' let pair_line = searchpair('/\*', '', '\*/', 'bW', 's:skip_if_not(a:lnum)') return indent(pair_line) + 1 endif return indent(prev_lnum) endfunction 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, '\|') . '\)\@ "
<-- press o "
" -------------------- "
" { " a > 0 ? 1
|
<-- press enter " } "
return 'jsxElement' elseif s:is_jsx_tag(item) return 'jsxTag' elseif s:is_jsx_brace(item) && trim(getline(prev_lnum)) =~ '{$' return 'jsxBraces' endif endfor return 'unknown' endfunction function! jsx_pretty#indent#get(js_indent) " The start column index of the current line (one-based) let start_col = s:start_col(v:lnum) " The end column index of the current line (one-based) let end_col = s:end_col(v:lnum) if s:start_with_jsx(v:lnum) let ind = s:jsx_indent(v:lnum) return ind == -1 ? a:js_indent() : ind elseif s:is_embedded_comment(v:lnum) return s:jsx_indent_comment(v:lnum) else let line = trim(getline(v:lnum)) let prev_lnum = s:prev_lnum(v:lnum) " Fix the case where pressing enter at the cursor " return
|
if line =~ '^' . s:end_tag && \ s:end_with_jsx(s:prev_lnum(v:lnum)) return s:jsx_indent_closing_tag(v:lnum) endif " Fix cases for the new line if empty(line) let context = s:jsx_context(v:lnum) if context == 'jsxElement' "
<-- press o "
return s:jsx_indent_element(v:lnum) elseif context == 'jsxTag' "
"
" ----------------------- "
"
return s:jsx_indent_attr(v:lnum) elseif context == 'jsxBraces' "
" { <-- press o " } "
return indent(prev_lnum) + s:sw() endif endif return a:js_indent() endif endfunction endif