summaryrefslogtreecommitdiffstats
path: root/autoload/jsx_pretty
diff options
context:
space:
mode:
authorAdam Stankiewicz <sheerun@sher.pl>2019-06-08 13:25:41 +0200
committerAdam Stankiewicz <sheerun@sher.pl>2019-06-08 13:25:41 +0200
commit26c678b08d88b658ec6069eb2ada2b2ba21c5d39 (patch)
treeebf7500bafee56b195bf52ced286fe29e5b3f485 /autoload/jsx_pretty
parent17ecfbdabcfdf294dae27c506fb68b58175199b5 (diff)
downloadvim-polyglot-26c678b08d88b658ec6069eb2ada2b2ba21c5d39.tar.gz
vim-polyglot-26c678b08d88b658ec6069eb2ada2b2ba21c5d39.zip
Change jsx/tsx provider, closes #400
Diffstat (limited to 'autoload/jsx_pretty')
-rw-r--r--autoload/jsx_pretty/comment.vim41
-rw-r--r--autoload/jsx_pretty/indent.vim204
-rw-r--r--autoload/jsx_pretty/syntax.vim218
3 files changed, 463 insertions, 0 deletions
diff --git a/autoload/jsx_pretty/comment.vim b/autoload/jsx_pretty/comment.vim
new file mode 100644
index 00000000..d5d58f5d
--- /dev/null
+++ b/autoload/jsx_pretty/comment.vim
@@ -0,0 +1,41 @@
+if exists('g:polyglot_disabled') && index(g:polyglot_disabled, 'jsx') != -1
+ finish
+endif
+
+function! jsx_pretty#comment#update_commentstring(original)
+ let syn_current = s:syn_name(line('.'), col('.'))
+ let syn_start = s:syn_name(line('.'), 1)
+ let save_cursor = getcurpos()
+
+ if syn_start =~ '^jsx'
+ let line = getline(".")
+ let start = len(matchstr(line, '^\s*'))
+ let syn_name = s:syn_name(line('.'), start + 1)
+
+ if line =~ '^\s*//'
+ let &l:commentstring = '// %s'
+ elseif s:syn_contains(line('.'), col('.'), 'jsxTaggedRegion')
+ let &l:commentstring = '<!-- %s -->'
+ elseif syn_name =~ '^jsxAttrib'
+ let &l:commentstring = '// %s'
+ else
+ let &l:commentstring = '{/* %s */}'
+ endif
+ else
+ let &l:commentstring = a:original
+ endif
+
+ " Restore the cursor position
+ call setpos('.', save_cursor)
+endfunction
+
+function! s:syn_name(lnum, cnum)
+ let syn_id = get(synstack(a:lnum, a:cnum), -1)
+ return synIDattr(syn_id, "name")
+endfunction
+
+function! s:syn_contains(lnum, cnum, syn_name)
+ let stack = synstack(a:lnum, a:cnum)
+ let syn_names = map(stack, 'synIDattr(v:val, "name")')
+ return index(syn_names, a:syn_name) >= 0
+endfunction
diff --git a/autoload/jsx_pretty/indent.vim b/autoload/jsx_pretty/indent.vim
new file mode 100644
index 00000000..a4e7a839
--- /dev/null
+++ b/autoload/jsx_pretty/indent.vim
@@ -0,0 +1,204 @@
+if exists('g:polyglot_disabled') && index(g:polyglot_disabled, 'jsx') != -1
+ finish
+endif
+
+if exists('*shiftwidth')
+ function! s:sw()
+ return shiftwidth()
+ endfunction
+else
+ function! s:sw()
+ return &sw
+ endfunction
+endif
+
+" Get the syntax group of start of line
+function! s:syn_sol(lnum)
+ let line = getline(a:lnum)
+ let sol = matchstr(line, '^\s*')
+ return map(synstack(a:lnum, len(sol) + 1), 'synIDattr(v:val, "name")')
+endfunction
+
+" Get the syntax group of end of line
+function! s:syn_eol(lnum)
+ let lnum = prevnonblank(a:lnum)
+ let col = strlen(getline(lnum))
+ return map(synstack(lnum, col), 'synIDattr(v:val, "name")')
+endfunction
+
+function! s:prev_indent(lnum)
+ let lnum = prevnonblank(a:lnum - 1)
+ return indent(lnum)
+endfunction
+
+function! s:prev_line(lnum)
+ let lnum = prevnonblank(a:lnum - 1)
+ return substitute(getline(lnum), '^\s*\|\s*$', '', 'g')
+endfunction
+
+function! s:syn_attr_jsx(synattr)
+ return a:synattr =~ "^jsx"
+endfunction
+
+function! s:syn_xmlish(syns)
+ return s:syn_attr_jsx(get(a:syns, -1))
+endfunction
+
+function! s:syn_jsx_element(syns)
+ return get(a:syns, -1) =~ 'jsxElement'
+endfunction
+
+function! s:syn_js_comment(syns)
+ return get(a:syns, -1) =~ 'Comment$'
+endfunction
+
+function! s:syn_jsx_escapejs(syns)
+ return get(a:syns, -1) =~ '\(\(js\(Template\)\?\|javaScript\(Embed\)\?\|typescript\)Braces\|javascriptTemplateSB\|typescriptInterpolationDelimiter\)' &&
+ \ (get(a:syns, -2) =~ 'jsxEscapeJs' ||
+ \ get(a:syns, -3) =~ 'jsxEscapeJs')
+endfunction
+
+function! s:syn_jsx_attrib(syns)
+ return len(filter(copy(a:syns), 'v:val =~ "jsxAttrib"'))
+endfunction
+
+let s:start_tag = '<\s*\([-:_\.\$0-9A-Za-z]\+\|>\)'
+" match `/end_tag>` and `//>`
+let s:end_tag = '/\%(\s*[-:_\.\$0-9A-Za-z]*\s*\|/\)>'
+let s:opfirst = '^' . get(g:,'javascript_opfirst',
+ \ '\C\%([<>=,.?^%|/&]\|\([-:+]\)\1\@!\|\*\+\|!=\|in\%(stanceof\)\=\>\)')
+
+function! jsx_pretty#indent#get(js_indent)
+ let lnum = v:lnum
+ let line = substitute(getline(lnum), '^\s*\|\s*$', '', 'g')
+ let current_syn = s:syn_sol(lnum)
+ let current_syn_eol = s:syn_eol(lnum)
+ let prev_syn_sol = s:syn_sol(lnum - 1)
+ let prev_syn_eol = s:syn_eol(lnum - 1)
+ let prev_line = s:prev_line(lnum)
+ let prev_ind = s:prev_indent(lnum)
+
+ if s:syn_xmlish(current_syn)
+
+ " {
+ " <div></div>
+ " ##} <--
+ if s:syn_jsx_element(current_syn) && line =~ '}$'
+ let pair_line = searchpair('{', '', '}', 'b')
+ return indent(pair_line)
+ elseif line =~ '^-->$'
+ if prev_line =~ '^<!--'
+ return prev_ind
+ else
+ return prev_ind - s:sw()
+ endif
+ elseif prev_line =~ '-->$'
+ return prev_ind
+ " close tag </tag> or /> including </>
+ elseif prev_line =~ s:end_tag . '$'
+ if line =~ '^<\s*' . s:end_tag
+ return prev_ind - s:sw()
+ elseif s:syn_jsx_attrib(prev_syn_sol)
+ return prev_ind - s:sw()
+ else
+ return prev_ind
+ endif
+ elseif line =~ '^\(>\|/\s*>\)'
+ if prev_line =~ '^<'
+ return prev_ind
+ else
+ return prev_ind - s:sw()
+ endif
+ elseif prev_line =~ '^\(<\|>\)' &&
+ \ (s:syn_xmlish(prev_syn_eol) || s:syn_js_comment(prev_syn_eol))
+ if line =~ '^<\s*' . s:end_tag
+ return prev_ind
+ else
+ return prev_ind + s:sw()
+ endif
+ elseif line =~ '^<\s*' . s:end_tag
+ if !s:syn_xmlish(prev_syn_sol)
+ if s:syn_jsx_escapejs(prev_syn_eol)
+ \ || s:syn_jsx_escapejs(prev_syn_sol)
+ return prev_ind - s:sw()
+ else
+ return prev_ind
+ endif
+ else
+ return prev_ind - s:sw()
+ endif
+ elseif !s:syn_xmlish(prev_syn_eol)
+ if prev_line =~ '\(&&\|||\|=>\|[([{]\|`\)$'
+ " <div>
+ " {
+ " }
+ " </div>
+ if line =~ '^[)\]}]'
+ return prev_ind
+ else
+ return prev_ind + s:sw()
+ endif
+ else
+ return prev_ind
+ endif
+ elseif !s:syn_xmlish(prev_syn_sol)
+ if prev_line =~ '^\<\(return\|default\|await\|yield\)'
+ if line !~ '^/\s*>' || line !~ '^<\s*' . s:end_tag
+ return prev_ind + s:sw()
+ else
+ return prev_ind
+ endif
+ else
+ return prev_ind
+ endif
+ else
+ return prev_ind
+ endif
+ elseif s:syn_jsx_escapejs(current_syn)
+ if line =~ '^}'
+ let char = getline('.')[col('.') - 1]
+ " When pressing enter after the }, keep the indent
+ if char != '}' && search('}', 'b', lnum)
+ return indent(lnum)
+ else
+ let pair_line = searchpair('{', '', '}', 'bW')
+ return indent(pair_line)
+ endif
+ elseif line =~ '^{' || line =~ '^\${'
+ if s:syn_jsx_escapejs(prev_syn_eol)
+ \ || s:syn_jsx_attrib(prev_syn_sol)
+ return prev_ind
+ elseif s:syn_xmlish(prev_syn_eol) && (prev_line =~ s:end_tag || prev_line =~ '-->$')
+ return prev_ind
+ else
+ return prev_ind + s:sw()
+ endif
+ endif
+ elseif s:syn_jsx_escapejs(current_syn_eol)
+ let pair_line = searchpair('{', '', '}', 'bW')
+ return indent(pair_line)
+ elseif line =~ '^/[/*]' " js comment in jsx tag
+ if get(prev_syn_sol, -1) =~ 'Punct'
+ return prev_ind + s:sw()
+ elseif synIDattr(synID(lnum - 1, 1, 1), 'name') =~ 'jsxTag'
+ return prev_ind
+ else
+ return a:js_indent()
+ endif
+ else
+ let ind = a:js_indent()
+
+ " If current syntax is not a jsx syntax group
+ if s:syn_xmlish(prev_syn_eol) && line !~ '^[)\]}]'
+ let sol = matchstr(line, s:opfirst)
+ if sol is ''
+ " Fix javascript continue indent
+ return ind - s:sw()
+ else
+ return ind
+ endif
+ endif
+ return ind
+ endif
+
+endfunction
diff --git a/autoload/jsx_pretty/syntax.vim b/autoload/jsx_pretty/syntax.vim
new file mode 100644
index 00000000..eac1f92a
--- /dev/null
+++ b/autoload/jsx_pretty/syntax.vim
@@ -0,0 +1,218 @@
+if exists('g:polyglot_disabled') && index(g:polyglot_disabled, 'jsx') != -1
+ finish
+endif
+
+function! jsx_pretty#syntax#highlight()
+
+ let s:highlight_close_tag = get(g:, 'vim_jsx_pretty_highlight_close_tag', 0)
+
+ " <tag id="sample">
+ " ~~~~~~~~~~~~~~~~~
+ " and self close tag
+ " <tag id="sample" />
+ " ~~~~~~~~~~~~~~~~~~~
+ syntax region jsxTag
+ \ start=+<+
+ \ matchgroup=jsxOpenPunct
+ \ end=+>+
+ \ matchgroup=NONE
+ \ end=+\(/\_s*>\)\@=+
+ \ contained
+ \ contains=jsxOpenTag,jsxEscapeJs,jsxAttrib,jsComment,@javascriptComments,javaScriptLineComment,javaScriptComment,typescriptLineComment,typescriptComment,jsxSpreadOperator
+ \ keepend
+ \ extend
+
+ " <tag></tag>
+ " ~~~~~~~~~~~
+ " and fragment
+ " <></>
+ " ~~~~~
+ " and self close tag
+ " <tag />
+ " ~~~~~~~
+ syntax region jsxElement
+ \ start=+<\_s*\(>\|\${\|\z(\<[-:_\.\$0-9A-Za-z]\+\>\)\)+
+ \ end=+/\_s*>+
+ \ end=+<\_s*/\_s*\z1\_s*>+
+ \ contains=jsxElement,jsxEscapeJs,jsxTag,jsxComment,jsxCloseString,jsxCloseTag,@Spell
+ \ keepend
+ \ extend
+ \ contained
+ \ fold
+
+ " detect jsx region
+ syntax region jsxRegion
+ \ start=+\(\(\_[([,?:=+\-*/<>{}]\|&&\|||\|=>\|\<return\|\<default\|\<await\|\<yield\)\_s*\)\@<=<\_s*\(>\|\z(\(script\)\@!\<[_\$A-Za-z][-:_\.\$0-9A-Za-z]*\>\)\(\_s*\([-+*)\]}&|?]\|/\([/*]\|\_s*>\)\@!\)\)\@!\)+
+ \ end=++
+ \ contains=jsxElement
+
+ " <tag key={this.props.key}>
+ " ~~~~~~~~~~~~~~~~
+ syntax region jsxEscapeJs
+ \ start=+{+
+ \ end=++
+ \ extend
+ \ contained
+ \ contains=jsBlock,javascriptBlock,javaScriptBlockBuildIn,typescriptBlock
+
+ " <tag key={this.props.key}>
+ " ~~~~
+ " and fragment start tag
+ " <>
+ " ~~
+ exe 'syntax region jsxOpenTag
+ \ matchgroup=jsxOpenPunct
+ \ start=+<+
+ \ end=+>+
+ \ matchgroup=NONE
+ \ end=+\>+
+ \ contained
+ \ contains=jsxTagName
+ \ nextgroup=jsxAttrib
+ \ skipwhite
+ \ skipempty ' .(s:highlight_close_tag ? 'transparent' : '')
+
+ " <foo.bar>
+ " ~
+ syntax match jsxDot +\.+ contained display
+
+ " <foo:bar>
+ " ~
+ syntax match jsxNamespace +:+ contained display
+
+ " <tag id="sample">
+ " ~
+ syntax match jsxEqual +=+ contained display nextgroup=jsxString,jsxEscapeJs,jsxRegion skipwhite
+
+ " <tag />
+ " ~~
+ syntax match jsxCloseString +/\_s*>+ contained
+
+ " </tag>
+ " ~~~~~~
+ " and fragment close tag
+ " </>
+ " ~~~
+ syntax region jsxCloseTag
+ \ matchgroup=jsxClosePunct
+ \ start=+<\_s*/+
+ \ end=+>+
+ \ contained
+ \ contains=jsxTagName
+
+ " <tag key={this.props.key}>
+ " ~~~
+ syntax match jsxAttrib
+ \ +\<[-A-Za-z_][-:_\$0-9A-Za-z]*\>+
+ \ contained
+ \ nextgroup=jsxEqual
+ \ skipwhite
+ \ skipempty
+ \ contains=jsxAttribKeyword
+ \ display
+
+ " <MyComponent ...>
+ " ~~~~~~~~~~~
+ " NOT
+ " <someCamel ...>
+ " ~~~~~
+ exe 'syntax match jsxComponentName
+ \ +\<[A-Z][\$0-9A-Za-z]\+\>+
+ \ contained
+ \ display ' .(s:highlight_close_tag ? 'transparent' : '')
+
+ " <tag key={this.props.key}>
+ " ~~~
+ exe 'syntax match jsxTagName
+ \ +\<[-:_\.\$0-9A-Za-z]\+\>+
+ \ contained
+ \ contains=jsxComponentName,jsxDot,jsxNamespace
+ \ nextgroup=jsxAttrib
+ \ skipempty
+ \ skipwhite
+ \ display ' .(s:highlight_close_tag ? 'transparent' : '')
+
+ " <tag id="sample">
+ " ~~~~~~~~
+ " and
+ " <tag id='sample'>
+ " ~~~~~~~~
+ syntax region jsxString start=+\z(["']\)+ skip=+\\\%(\z1\|$\)+ end=+\z1+ contained contains=@Spell display
+
+ let s:tags = get(g:, 'vim_jsx_pretty_template_tags', ['html', 'raw'])
+ let s:enable_tagged_jsx = !empty(s:tags)
+
+ " add support to JSX inside the tagged template string
+ " https://github.com/developit/htm
+ if s:enable_tagged_jsx
+ exe 'syntax region jsxTaggedRegion
+ \ start=+\%('. join(s:tags, '\|') .'\)\@<=`+ms=s+1
+ \ end=+`+me=e-1
+ \ extend
+ \ contained
+ \ containedin=jsTemplateString,javascriptTemplate,javaScriptStringT,typescriptStringB
+ \ contains=jsxElement'
+
+ syntax region jsxEscapeJs
+ \ start=+\${+
+ \ end=++
+ \ extend
+ \ contained
+ \ contains=jsTemplateExpression,javascriptTemplateSubstitution,javaScriptEmbed,typescriptInterpolation
+
+ syntax region jsxOpenTag
+ \ matchgroup=jsxOpenPunct
+ \ start=+<\%(\${\)\@=+
+ \ matchgroup=NONE
+ \ end=++
+ \ contained
+ \ contains=jsxEscapeJs
+ \ nextgroup=jsxAttrib,jsxSpreadOperator
+ \ skipwhite
+ \ skipempty
+
+ syntax keyword jsxAttribKeyword class contained display
+
+ syntax match jsxSpreadOperator +\.\.\.+ contained display nextgroup=jsxEscapeJs skipwhite
+
+ syntax match jsxCloseTag +<//>+ display
+
+ syntax match jsxComment +<!--\_.\{-}-->+ display
+ endif
+
+ " Highlight the tag name
+ highlight def link jsxTag Function
+ highlight def link jsxTagName Identifier
+ highlight def link jsxComponentName Function
+
+ highlight def link jsxAttrib Type
+ highlight def link jsxAttribKeyword jsxAttrib
+ highlight def link jsxEqual Operator
+ highlight def link jsxString String
+ highlight def link jsxDot Operator
+ highlight def link jsxNamespace Operator
+
+ " Highlight the jsxCloseString (i.e. />), jsxPunct (i.e. <,>) and jsxCloseTag (i.e. <//>)
+ highlight def link jsxCloseString Comment
+ highlight def link jsxPunct jsxCloseString
+ highlight def link jsxOpenPunct jsxPunct
+ highlight def link jsxClosePunct jsxPunct
+ highlight def link jsxCloseTag jsxCloseString
+
+ highlight def link jsxComment Comment
+ highlight def link jsxSpreadOperator Operator
+
+ if s:highlight_close_tag
+ highlight! def link jsxOpenPunct jsxTag
+ highlight! def link jsxCloseString Identifier
+ endif
+
+ let s:vim_jsx_pretty_colorful_config = get(g:, 'vim_jsx_pretty_colorful_config', 0)
+
+ if s:vim_jsx_pretty_colorful_config == 1
+ highlight def link jsObjectKey Label
+ highlight def link jsArrowFuncArgs Type
+ highlight def link jsFuncArgs Type
+ endif
+
+endfunction