summaryrefslogtreecommitdiffstats
path: root/after/indent/javascript.vim
blob: 8c2254d65e20bfce83d54acff7ecab38f17a5b04 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
if exists('g:polyglot_disabled') && (index(g:polyglot_disabled, 'typescript') != -1 || index(g:polyglot_disabled, 'typescript') != -1 || index(g:polyglot_disabled, 'jsx') != -1)
  finish
endif

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Vim indent file
"
" Language: javascript.jsx
" Maintainer: MaxMellon <maxmellon1994@gmail.com>
"
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""

if exists('b:did_indent')
  let s:did_indent = b:did_indent
  unlet b:did_indent
endif

let s:keepcpo = &cpo
set cpo&vim

if exists('s:did_indent')
  let b:did_indent = s:did_indent
endif

setlocal indentexpr=GetJsxIndent()
setlocal indentkeys=0{,0},0),0],0\,,!^F,o,O,e,*<Return>,<>>,<<>,/

function! GetJsxIndent()
  return jsx_pretty#indent#get(function('GetJavascriptIndent'))
endfunction

let &cpo = s:keepcpo
unlet s:keepcpo
if exists('g:polyglot_disabled') && index(g:polyglot_disabled, 'styled-components') != -1
  finish
endif

" Vim syntax file
" Language:   styled-components (js/ts)
" Maintainer: Karl Fleischmann <fleischmann.karl@gmail.com>
" URL:        https://github.com/styled-components/vim-styled-components

" initialize variable to check, if the indentation expression is run
" multiple times in a row, which indicates an infinite recursion
let s:is_recursion = 0

" store current indentexpr for later
let b:js_ts_indent=&indentexpr

" set indentexpr for this filetype (styled-components)
setlocal indentexpr=GetStyledIndent()

" add the following keys to trigger reindenting, when in insert mode
" - *;    - Indent and insert on press of ';' key.
" - *<:>  - Indent and insert on press of ':' key.
set indentkeys+=*;,*<:>,*<Return>

fu! s:GetSyntaxNames(lnum, cnum)
  return map(synstack(a:lnum, a:cnum), 'synIDattr(v:val, "name")')
endfu

" re-implement SynSOL of vim-jsx
" TODO: add dependency to the readme and remove duplicate implementation
fu! s:SynSOL(lnum)
  return s:GetSyntaxNames(a:lnum, 1)
endfu

" re-implement SynEOL of vim-jsx
" TODO: add dependency to the readme and remove duplicate implementation
fu! s:SynEOL(lnum, offset)
  let l:lnum = prevnonblank(a:lnum)
  let l:col = strlen(getline(l:lnum))

  return s:GetSyntaxNames(l:lnum, l:col + a:offset)
endfu


"" Return whether the current line is a jsTemplateString
fu! s:IsStyledDefinition(lnum)
  " iterate through all syntax items in the given line
  for item in s:SynSOL(a:lnum)
    " if syntax-item is a jsTemplateString return 1 - true
    " `==#` is a match case comparison of the item
    if item ==# 'styledDefinition'
      return 1
    endif
  endfor

  " fallback to 0 - false
  return 0
endfu

"" Count occurences of `str` at the beginning of the given `lnum` line
fu! s:CountOccurencesInSOL(lnum, str)
  let l:occurence = 0

  " iterate through all items in the given line
  for item in s:SynSOL(a:lnum)
    " if the syntax-item equals the given str increment the counter
    " `==?` is a case isensitive equal operation
    if item ==? a:str
      let l:occurence += 1
    endif
  endfor

  " return the accumulated count of occurences
  return l:occurence
endfu

"" Count occurences of `str` at the end of the given `lnum` line
fu! s:CountOccurencesInEOL(lnum, str, offset)
  let l:occurence = 0

  " iterate through all items in the given line
  for item in s:SynEOL(a:lnum, a:offset)
    " if the syntax-item equals the given str increment the counter
    " `==?` is a case insensitive equal operation
    if item == a:str
      let l:occurence += 1
    endif
  endfor

  " return the accumulated count of occurences
  return l:occurence
endfu

"" Get the indentation of the current line
fu! GetStyledIndent()
  if s:IsStyledDefinition(v:lnum)
    let l:baseIndent = 0

    " find last non-styled line
    let l:cnum = v:lnum
    while s:IsStyledDefinition(l:cnum)
      let l:cnum -= 1
    endwhile

    " get indentation of the last non-styled line as base indentation
    let l:baseIndent = indent(l:cnum)

    " incrementally build indentation based on current indentation
    " - one shiftwidth for the styled definition region
    " - one shiftwidth per open nested definition region
    let l:styledIndent = &sw
    let l:styledIndent += min([
          \ s:CountOccurencesInSOL(v:lnum, 'styledNestedRegion'),
          \ s:CountOccurencesInEOL(v:lnum, 'styledNestedRegion', 0)
          \ ]) * &sw

    " decrease indentation by one shiftwidth, if the styled definition
    " region ends on the current line
    " - either directly via styled definition region, or
    " - if the very last
    if s:CountOccurencesInEOL(v:lnum, 'styledDefinition', 1) == 0
      let l:styledIndent -= &sw
    endif

    " return the base indentation
    " (for nested styles inside classes/objects/etc.) plus the actual
    " indentation inside the styled definition region
    return l:baseIndent + l:styledIndent
  elseif len(b:js_ts_indent)
    let l:result = 0
    let l:offset = 0

    " increase indentation by one shiftwidth, if the last line ended on a
    " styledXmlRegion and this line does not continue with it
    " this is a fix for an incorrectly indented xml prop after a
    " glamor-styled styledXmlRegion
    if s:CountOccurencesInEOL(v:lnum-1, 'styledXmlRegion', 0) == 1 &&
          \ s:CountOccurencesInSOL(v:lnum, 'styledXmlRegion') == 0
      let l:offset = &sw
    endif

    " make sure `GetStyledIndent` and `GetJsxIndent` don't infinitely
    " recurse by incrementing a counter variable, before evaluating the
    " stored indent expression
    if s:is_recursion == 0
      let s:is_recursion = 1
      let l:result = eval(b:js_ts_indent)
    endif

    " `is_recursion` being 0 at this point indicates, that
    " `eval(b:js_ts_indent)` did itself evaluate it's stored indentexpr
    " and thus it can be assumed, that the current line should be
    " indented as JS
    if s:is_recursion == 0
      " use one of `GetJavascriptIndent` or `GetJsIndent` if existing
      " fallback to cindent, if not
      if exists('*GetJavascriptIndent')
        let l:result = GetJavascriptIndent()
      elseif exists('*GetJsIndent')
        let l:result = GetJsIndent()
      else
        let l:result = cindent(v:lnum)
      endif
    endif

    " reset `is_recursion` counter and return the indentation value
    let s:is_recursion = 0
    return l:result + l:offset
  endif

  " if all else fails indent according to C-syntax
  return cindent(v:lnum)
endfu