1
0

javascript.vim 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. " Vim indent file Language: JavaScript
  2. " Author: Preston Koprivica (pkopriv2@gmail.com)
  3. " URL:
  4. " Last Change: April 30, 2010
  5. " 0. Standard Stuff
  6. " =================
  7. " Only load one indent script per buffer
  8. if exists('b:did_indent')
  9. finish
  10. endif
  11. let b:did_indent = 1
  12. " Set the global log variable 1 = logging enabled, 0 = logging disabled
  13. if !exists("g:js_indent_log")
  14. let g:js_indent_log = 0
  15. endif
  16. setlocal indentexpr=GetJsIndent(v:lnum)
  17. setlocal indentkeys=
  18. setlocal cindent
  19. setlocal autoindent
  20. " 1. Variables
  21. " ============
  22. " Inline comments (for anchoring other statements)
  23. let s:js_mid_line_comment = '\s*\(\/\*.*\*\/\)*\s*'
  24. let s:js_end_line_comment = s:js_mid_line_comment . '\s*\(//.*\)*'
  25. let s:js_line_comment = s:js_end_line_comment
  26. " Comment/String Syntax Key
  27. let s:syn_comment = '\(Comment\|String\|Regexp\)'
  28. " 2. Aux. Functions
  29. " =================
  30. " = Method: IsInComment
  31. "
  32. " Determines whether the specified position is contained in a comment. "Note:
  33. " This depends on a
  34. function! s:IsInComment(lnum, cnum)
  35. return synIDattr(synID(a:lnum, a:cnum, 1), 'name') =~? s:syn_comment
  36. endfunction
  37. " = Method: IsComment
  38. "
  39. " Determines whether a line is a comment or not.
  40. function! s:IsComment(lnum)
  41. let line = getline(a:lnum)
  42. return s:IsInComment(a:lnum, 1) && s:IsInComment(a:lnum, strlen(line)) "Doesn't absolutely work. Only Probably!
  43. endfunction
  44. " = Method: GetNonCommentLine
  45. "
  46. " Grabs the nearest non-commented line
  47. function! s:GetNonCommentLine(lnum)
  48. let lnum = prevnonblank(a:lnum)
  49. while lnum > 0
  50. if s:IsComment(lnum)
  51. let lnum = prevnonblank(lnum - 1)
  52. else
  53. return lnum
  54. endif
  55. endwhile
  56. return lnum
  57. endfunction
  58. " = Method: SearchForPair
  59. "
  60. " Returns the beginning tag of a given pair starting from the given line.
  61. function! s:SearchForPair(lnum, beg, end)
  62. " Save the cursor position.
  63. let curpos = getpos(".")
  64. " Set the cursor position to the beginning of the line (default
  65. " behavior when using ==)
  66. call cursor(a:lnum, 0)
  67. " Search for the opening tag
  68. let mnum = searchpair(a:beg, '', a:end, 'bW',
  69. \ 'synIDattr(synID(line("."), col("."), 0), "name") =~? s:syn_comment' )
  70. "Restore the cursor position
  71. call cursor(curpos)
  72. " Finally, return the matched line number
  73. return mnum
  74. endfunction
  75. " Object Helpers
  76. " ==============
  77. let s:object_beg = '{[^}]*' . s:js_end_line_comment . '$'
  78. let s:object_end = '^' . s:js_mid_line_comment . '}[;,]\='
  79. function! s:IsObjectBeg(line)
  80. return a:line =~ s:object_beg
  81. endfunction
  82. function! s:IsObjectEnd(line)
  83. return a:line =~ s:object_end
  84. endfunction
  85. function! s:GetObjectBeg(lnum)
  86. return s:SearchForPair(a:lnum, '{', '}')
  87. endfunction
  88. " Array Helpers
  89. " ==============
  90. let s:array_beg = '\[[^\]]*' . s:js_end_line_comment . '$'
  91. let s:array_end = '^' . s:js_mid_line_comment . '[^\[]*\][;,]*' . s:js_end_line_comment . '$'
  92. function! s:IsArrayBeg(line)
  93. return a:line =~ s:array_beg
  94. endfunction
  95. function! s:IsArrayEnd(line)
  96. return a:line =~ s:array_end
  97. endfunction
  98. function! s:GetArrayBeg(lnum)
  99. return s:SearchForPair(a:lnum, '\[', '\]')
  100. endfunction
  101. " MultiLine Declaration/Invocation Helpers
  102. " ========================================
  103. let s:paren_beg = '([^)]*' . s:js_end_line_comment . '$'
  104. let s:paren_end = '^' . s:js_mid_line_comment . '[^(]*)[;,]*'
  105. function! s:IsParenBeg(line)
  106. return a:line =~ s:paren_beg
  107. endfunction
  108. function! s:IsParenEnd(line)
  109. return a:line =~ s:paren_end
  110. endfunction
  111. function! s:GetParenBeg(lnum)
  112. return s:SearchForPair(a:lnum, '(', ')')
  113. endfunction
  114. " Continuation Helpers
  115. " ====================
  116. let s:continuation = '\(+\|\\\)\{1}' . s:js_line_comment . '$'
  117. function! s:IsContinuationLine(line)
  118. return a:line =~ s:continuation
  119. endfunction
  120. function! s:GetContinuationBegin(lnum)
  121. let cur = a:lnum
  122. while s:IsContinuationLine(getline(cur))
  123. let cur -= 1
  124. endwhile
  125. return cur + 1
  126. endfunction
  127. " Switch Helpers
  128. " ==============
  129. let s:switch_beg_next_line = 'switch\s*(.*)\s*' . s:js_mid_line_comment . s:js_end_line_comment . '$'
  130. let s:switch_beg_same_line = 'switch\s*(.*)\s*' . s:js_mid_line_comment . '{\s*' . s:js_line_comment . '$'
  131. let s:switch_mid = '^.*\(case.*\|default\)\s*:\s*'
  132. function! s:IsSwitchBeginNextLine(line)
  133. return a:line =~ s:switch_beg_next_line
  134. endfunction
  135. function! s:IsSwitchBeginSameLine(line)
  136. return a:line =~ s:switch_beg_same_line
  137. endfunction
  138. function! s:IsSwitchMid(line)
  139. return a:line =~ s:switch_mid
  140. endfunction
  141. " Control Helpers
  142. " ===============
  143. let s:cntrl_beg_keys = '\(\(\(if\|for\|with\|while\)\s*(.*)\)\|\(try\|do\)\)\s*'
  144. let s:cntrl_mid_keys = '\(\(\(else\s*if\|catch\)\s*(.*)\)\|\(finally\|else\)\)\s*'
  145. let s:cntrl_beg = s:cntrl_beg_keys . s:js_end_line_comment . '$'
  146. let s:cntrl_mid = s:cntrl_mid_keys . s:js_end_line_comment . '$'
  147. let s:cntrl_end = '\(while\s*(.*)\)\s*;\=\s*' . s:js_end_line_comment . '$'
  148. function! s:IsControlBeg(line)
  149. return a:line =~ s:cntrl_beg
  150. endfunction
  151. function! s:IsControlMid(line)
  152. return a:line =~ s:cntrl_mid
  153. endfunction
  154. function! s:IsControlMidStrict(line)
  155. return a:line =~ s:cntrl_mid
  156. endfunction
  157. function! s:IsControlEnd(line)
  158. return a:line =~ s:cntrl_end
  159. endfunction
  160. " = Method: Log
  161. "
  162. " Logs a message to the stdout.
  163. function! s:Log(msg)
  164. if g:js_indent_log
  165. echo "LOG: " . a:msg
  166. endif
  167. endfunction
  168. " 3. Indenter
  169. " ===========
  170. function! GetJsIndent(lnum)
  171. " Grab the first non-comment line prior to this line
  172. let pnum = s:GetNonCommentLine(a:lnum-1)
  173. " First line, start at indent = 0
  174. if pnum == 0
  175. call s:Log("No, noncomment lines prior to the current line.")
  176. return 0
  177. endif
  178. " Grab the second non-comment line prior to this line
  179. let ppnum = s:GetNonCommentLine(pnum-1)
  180. call s:Log("Line: " . a:lnum)
  181. call s:Log("PLine: " . pnum)
  182. call s:Log("PPLine: " . ppnum)
  183. " Grab the lines themselves.
  184. let line = getline(a:lnum)
  185. let pline = getline(pnum)
  186. let ppline = getline(ppnum)
  187. " Determine the current level of indentation
  188. let ind = indent(pnum)
  189. " Handle: Object Closers (ie })
  190. " =============================
  191. if s:IsObjectEnd(line) && !s:IsComment(a:lnum)
  192. call s:Log("Line matched object end")
  193. let obeg = s:GetObjectBeg(a:lnum)
  194. let oind = indent(obeg)
  195. let oline = getline(obeg)
  196. call s:Log("The object beg was found at: " . obeg)
  197. return oind
  198. endif
  199. if s:IsObjectBeg(pline)
  200. call s:Log("Pline matched object beg")
  201. return ind + &sw
  202. endif
  203. " Handle: Array Closer (ie ])
  204. " ============================
  205. if s:IsArrayEnd(line) && !s:IsComment(a:lnum)
  206. call s:Log("Line matched array end")
  207. let abeg = s:GetArrayBeg(a:lnum)
  208. let aind = indent(abeg)
  209. call s:Log("The array beg was found at: " . abeg)
  210. return aind
  211. endif
  212. if s:IsArrayBeg(pline)
  213. call s:Log("Pline matched array beg")
  214. return ind + &sw
  215. endif
  216. " Handle: Parens
  217. " ==============
  218. if s:IsParenEnd(line) && !s:IsComment(a:lnum)
  219. call s:Log("Line matched paren end")
  220. let abeg = s:GetParenBeg(a:lnum)
  221. let aind = indent(abeg)
  222. call s:Log("The paren beg was found at: " . abeg)
  223. return aind
  224. endif
  225. if s:IsParenBeg(pline)
  226. call s:Log("Pline matched paren beg")
  227. return ind + &sw
  228. endif
  229. " Handle: Continuation Lines.
  230. " ========================================================
  231. if s:IsContinuationLine(pline)
  232. call s:Log('Pline is a continuation line.')
  233. let cbeg = s:GetContinuationBegin(pnum)
  234. let cind = indent(cbeg)
  235. call s:Log('The continuation block begin found at: ' . cbeg)
  236. return cind + &sw
  237. endif
  238. if s:IsContinuationLine(ppline)
  239. call s:Log('PPline was a continuation line but pline wasnt.')
  240. return ind - &sw
  241. endif
  242. " Handle: Switch Control Blocks
  243. " =============================
  244. if s:IsSwitchMid(pline)
  245. call s:Log("PLine matched switch cntrl mid")
  246. if s:IsSwitchMid(line) || s:IsObjectEnd(line)
  247. call s:Log("Line matched a cntrl mid")
  248. return ind
  249. else
  250. call s:Log("Line didnt match a cntrl mid")
  251. return ind + &sw
  252. endif
  253. endif
  254. if s:IsSwitchMid(line)
  255. call s:Log("Line matched switch cntrl mid")
  256. return ind - &sw
  257. endif
  258. " Handle: Single Line Control Blocks
  259. " ==================================
  260. if s:IsControlBeg(pline)
  261. call s:Log("Pline matched control beginning")
  262. if s:IsControlMid(line)
  263. call s:Log("Line matched a control mid")
  264. return ind
  265. elseif line =~ '^\s*{\s*$'
  266. call s:Log("Line matched an object beg")
  267. return ind
  268. else
  269. return ind + &sw
  270. endif
  271. endif
  272. if s:IsControlMid(pline)
  273. call s:Log("Pline matched a control mid")
  274. if s:IsControlMid(line)
  275. call s:Log("Line matched a control mid")
  276. return ind
  277. elseif s:IsObjectBeg(line)
  278. call s:Log("Line matched an object beg")
  279. return ind
  280. else
  281. call s:Log("Line didn't match a control mid or object beg."
  282. return ind + &sw
  283. endif
  284. endif
  285. if s:IsControlMid(line)
  286. call s:Log("Line matched a control mid.")
  287. if s:IsControlEnd(pline) || s:IsObjectEnd(pline)
  288. call s:Log("PLine matched control end")
  289. return ind
  290. else
  291. call s:Log("Pline didn't match object end")
  292. return ind - &sw
  293. endif
  294. endif
  295. if ( s:IsControlBeg(ppline) || s:IsControlMid(ppline) ) &&
  296. \ !s:IsObjectBeg(pline) && !s:IsObjectEnd(pline)
  297. call s:Log("PPLine matched single line control beg or mid")
  298. return ind - &sw
  299. endif
  300. " Handle: No matches
  301. " ==================
  302. "call s:Log("Line didn't match anything. Retaining indent")
  303. return ind
  304. endfunction