diff --git a/after/indent/javascript.vim b/after/indent/javascript.vim index 51add1c..4f0124f 100644 --- a/after/indent/javascript.vim +++ b/after/indent/javascript.vim @@ -172,8 +172,20 @@ fu! VHTL_countMatches(string, pattern) endwhile endfu +function! s:SynAt(l,c) " from $VIMRUNTIME/indent/javascript.vim + let byte = line2byte(a:l) + a:c - 1 + let pos = index(s:synid_cache[0], byte) + if pos == -1 + let s:synid_cache[:] += [[byte], [synIDattr(synID(a:l, a:c, 0), 'name')]] + endif + return s:synid_cache[1][pos] +endfunction + if exists('g:VHTL_debugging') set debug=msg " show errors in indentexpr + fu! SynAt(l,c) + return s:SynAt(a:l,a:c) + endfu endif fu! VHTL_debug(str) if exists('g:VHTL_debugging') @@ -181,9 +193,182 @@ fu! VHTL_debug(str) endif endfu +let s:StateClass={} +fu! s:StateClass.new() + let l:instance = copy(self) + let l:instance.currLine = v:lnum + let l:instance.prevLine = prevnonblank(v:lnum - 1) + let l:instance.currSynstack = VHTL_SynSOL(l:instance.currLine) + let l:instance.prevSynstack = VHTL_SynEOL(l:instance.prevLine) + return l:instance +endfu + +fu! s:StateClass.startsWithTemplateClose() dict + return (getline(self.currSynstack)) =~# '^\s*`' +endfu + +fu! s:StateClass.closedJsExpression() dict + return VHTL_closesJsExpression(getline(self.prevLine)) +endfu +fu! s:StateClass.closesJsExpression() dict + return VHTL_closesJsExpression(getline(self.currLine)) +endfu +fu! s:StateClass.openedJsExpression() dict + return (VHTL_getBracketDepthChange(getline(self.prevLine)) > 0) +endfu +fu! s:StateClass.opensLitHtmlTemplate() dict + return VHTL_opensTemplate(getline(self.currLine)) +endfu +fu! s:StateClass.openedLitHtmlTemplate() dict + return VHTL_opensTemplate(getline(self.prevLine)) +endfu +fu! s:StateClass.closesLitHtmlTemplate() dict + return VHTL_closesTemplate(getline(self.currLine)) +endfu +fu! s:StateClass.closedLitHtmlTemplate() dict + return VHTL_closesTemplate(getline(self.prevLine)) +endfu + +fu! s:StateClass.isInsideLitHtml() dict + return VHTL_isSynstackInsideLitHtml(self.currSynstack) +endfu +fu! s:StateClass.wasInsideLitHtml() dict + return VHTL_isSynstackInsideLitHtml(self.prevSynstack) +endfu +fu! s:StateClass.isInsideJsx() dict + return IsSynstackInsideJsx(self.currSynstack) +endfu + +fu! s:StateClass.wasHtml() dict + return get(self.prevSynstack, -1) =~# '^html' +endfu +fu! s:StateClass.isHtml() dict + return get(self.currSynstack, -1) =~# '^html' +endfu +fu! s:StateClass.isLitHtmlRegionCloser() dict + return get(self.currSynstack, -1) ==# 'litHtmlRegion' && getline(self.currLine) =~# '^\s*`' +endfu +fu! s:StateClass.wasJs() dict + return get(self.prevSynstack, -1) =~# '^js' +endfu +fu! s:StateClass.isJsTemplateBrace() dict + return get(self.currSynstack, -1) ==# 'jsTemplateBraces' +endfu +fu! s:StateClass.wasJsTemplateBrace() dict + return get(self.prevSynstack, -1) ==# 'jsTemplateBraces' +endfu +fu! s:StateClass.isJs() dict + return get(self.currSynstack, -1) =~# '^js' +endfu +fu! s:StateClass.wasCss() dict + return get(self.prevSynstack, -1) =~# '^css' +endfu +fu! s:StateClass.isCss() dict + return get(self.currSynstack, -1) =~# '^css' +endfu + +fu! s:StateClass.toStr() dict + return '{line ' . self.currLine . '}' +endfu + +fu! s:SkipFuncJsTemplateBraces() + " let l:char = getline(line('.'))[col('.')-1] + let l:syntax = s:SynAt(line('.'), col('.')) + if (l:syntax != 'jsTemplateBraces') + return 1 + endif +endfu + +fu! s:SkipFuncLitHtmlRegion() + " let l:char = getline(line('.'))[col('.')-1] + let l:syntax = s:SynAt(line('.'), col('.')) + if (l:syntax != 'litHtmlRegion') + return 1 + endif +endfu + +fu! s:getCloseWordsLeftToRight(lineNum) + let l:line = getline(a:lineNum) + + " The following regex converts a line to purely a list of closing words. + " Pretty cool but not useful + " echo split(getline(62), '.\{-}\ze\(}\|`\|<\/\w\+>\)') + + let l:anyCloserWord = '}\|`\|<\/\w\+>' + + + let l:index = 0 + let l:closeWords = [] + while v:true + let [l:term, l:index, l:trash] = matchstrpos(l:line, l:anyCloserWord, l:index) + if (l:index == -1) + break + else + let l:col = l:index + 1 + call add(l:closeWords, [l:term, l:col]) + endif + let l:index += 1 + endwhile + return l:closeWords +endfu + +fu! s:StateClass.getIndentDelta() dict + let l:closeWords = s:getCloseWordsLeftToRight(self.currLine) + if len(l:closeWords) == 0 + return 0 + endif + let [l:closeWord, l:col] = l:closeWords[0] + let l:syntax = s:SynAt(self.currLine, l:col) + if (l:syntax == 'htmlEndTag') + call VHTL_debug('indent_delta: html end tag') + return - &shiftwidth + endif + if (l:syntax == 'litHtmlRegion' && 'html`' != strpart(getline(self.currLine), l:col-5, len('html`'))) + call VHTL_debug('indent_delta: end of litHtmlRegion') + return - &shiftwidth + endif + return 0 +endfu + +" html tag, html template, or js expression on previous line +fu! s:StateClass.getIndentOfLastClose() dict + + let l:closeWords = s:getCloseWordsLeftToRight(self.prevLine) + + if (len(l:closeWords) == 0) + return -1 + endif + + for l:item in reverse(l:closeWords) + let [l:closeWord, l:col] = l:item + let l:syntax = s:SynAt(self.prevLine, l:col) + call cursor(self.prevLine, l:col) " sets start point for searchpair() + redraw + if ("}" == l:closeWord && l:syntax == 'jsTemplateBraces') + call searchpair('{', '', '}', 'b', 's:SkipFuncJsTemplateBraces()') + call VHTL_debug('js brace base indent') + elseif ("`" == l:closeWord && l:syntax == 'litHtmlRegion') + call searchpair('html`', '', '\(html\)\@', '', '') + call searchpair(l:openWord, '', l:closeWord, 'b') + call VHTL_debug('html tag region base indent ') + else + call VHTL_debug("UNRECOGNIZED CLOSER SYNTAX: '" . l:syntax . "'") + endif + return indent(line('.')) " cursor was moved by searchpair() + endfor +endfu + +" com! MyTest exec "call s:StateClass.new().getIndentOfLastClose()" + " Dispatch to indent method for js/html (use custom rules for transitions " between syntaxes) fu! ComputeLitHtmlIndent() + let s:synid_cache = [[],[]] + + let l:state = s:StateClass.new() " get most recent non-empty line let l:prev_lnum = prevnonblank(v:lnum - 1) @@ -191,80 +376,47 @@ fu! ComputeLitHtmlIndent() let l:currLineSynstack = VHTL_SynSOL(v:lnum) let l:prevLineSynstack = VHTL_SynEOL(l:prev_lnum) - if (!VHTL_isSynstackInsideLitHtml(l:currLineSynstack) && !VHTL_isSynstackInsideLitHtml(l:prevLineSynstack)) - call VHTL_debug('outside of litHtmlRegion') + if (!l:state.isInsideLitHtml() && !l:state.wasInsideLitHtml()) + call VHTL_debug('outside of litHtmlRegion: ' . b:litHtmlOriginalIndentExpression) + + if (exists('b:hi_indent') && has_key(b:hi_indent, 'blocklnr')) + call remove(b:hi_indent, 'blocklnr') + " This avoids a really weird behavior when indenting first line inside + " style tag and then indenting any normal javascript outside of + " lit-html region. 'blocklnr' is assigned to line number of