mirror of
				https://github.com/vim/vim.git
				synced 2025-10-30 09:47:20 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			171 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			VimL
		
	
	
	
	
	
			
		
		
	
	
			171 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			VimL
		
	
	
	
	
	
| " Vim indent file
 | |
| " Language:		JSON
 | |
| " Maintainer:		Eli Parra <eli@elzr.com> https://github.com/elzr/vim-json
 | |
| " Last Change:          2020 Aug 30
 | |
| "   https://github.com/jakar/vim-json/commit/20b650e22aa750c4ab6a66aa646bdd95d7cd548a#diff-e81fc111b2052e306d126bd9989f7b7c
 | |
| " Original Author:	Rogerz Zhang <rogerz.zhang at gmail.com> http://github.com/rogerz/vim-json
 | |
| " Acknowledgement:      Based off of vim-javascript maintained by Darrick Wiebe 
 | |
| "                       http://www.vim.org/scripts/script.php?script_id=2765
 | |
| 
 | |
| " 0. Initialization {{{1
 | |
| " =================
 | |
| 
 | |
| " Only load this indent file when no other was loaded.
 | |
| if exists("b:did_indent")
 | |
|   finish
 | |
| endif
 | |
| let b:did_indent = 1
 | |
| 
 | |
| setlocal nosmartindent
 | |
| 
 | |
| " Now, set up our indentation expression and keys that trigger it.
 | |
| setlocal indentexpr=GetJSONIndent(v:lnum)
 | |
| setlocal indentkeys=0{,0},0),0[,0],!^F,o,O,e
 | |
| 
 | |
| " Only define the function once.
 | |
| if exists("*GetJSONIndent")
 | |
|   finish
 | |
| endif
 | |
| 
 | |
| let s:cpo_save = &cpo
 | |
| set cpo&vim
 | |
| 
 | |
| " 1. Variables {{{1
 | |
| " ============
 | |
| 
 | |
| let s:line_term = '\s*\%(\%(\/\/\).*\)\=$'
 | |
| " Regex that defines blocks.
 | |
| let s:block_regex = '\%({\)\s*\%(|\%([*@]\=\h\w*,\=\s*\)\%(,\s*[*@]\=\h\w*\)*|\)\=' . s:line_term
 | |
| 
 | |
| " 2. Auxiliary Functions {{{1
 | |
| " ======================
 | |
| 
 | |
| " Check if the character at lnum:col is inside a string.
 | |
| function s:IsInString(lnum, col)
 | |
|   return synIDattr(synID(a:lnum, a:col, 1), 'name') == 'jsonString'
 | |
| endfunction
 | |
| 
 | |
| " Find line above 'lnum' that isn't empty, or in a string.
 | |
| function s:PrevNonBlankNonString(lnum)
 | |
|   let lnum = prevnonblank(a:lnum)
 | |
|   while lnum > 0
 | |
|     " If the line isn't empty or in a string, end search.
 | |
|     let line = getline(lnum)
 | |
|     if !(s:IsInString(lnum, 1) && s:IsInString(lnum, strlen(line)))
 | |
|       break
 | |
|     endif
 | |
|     let lnum = prevnonblank(lnum - 1)
 | |
|   endwhile
 | |
|   return lnum
 | |
| endfunction
 | |
| 
 | |
| " Check if line 'lnum' has more opening brackets than closing ones.
 | |
| function s:LineHasOpeningBrackets(lnum)
 | |
|   let open_0 = 0
 | |
|   let open_2 = 0
 | |
|   let open_4 = 0
 | |
|   let line = getline(a:lnum)
 | |
|   let pos = match(line, '[][(){}]', 0)
 | |
|   while pos != -1
 | |
|     let idx = stridx('(){}[]', line[pos])
 | |
|     if idx % 2 == 0
 | |
|       let open_{idx} = open_{idx} + 1
 | |
|     else
 | |
|       let open_{idx - 1} = open_{idx - 1} - 1
 | |
|     endif
 | |
|     let pos = match(line, '[][(){}]', pos + 1)
 | |
|   endwhile
 | |
|   return (open_0 > 0) . (open_2 > 0) . (open_4 > 0)
 | |
| endfunction
 | |
| 
 | |
| function s:Match(lnum, regex)
 | |
|   let col = match(getline(a:lnum), a:regex) + 1
 | |
|   return col > 0 && !s:IsInString(a:lnum, col) ? col : 0
 | |
| endfunction
 | |
| 
 | |
| " 3. GetJSONIndent Function {{{1
 | |
| " =========================
 | |
| 
 | |
| function GetJSONIndent(...)
 | |
|   " 3.1. Setup {{{2
 | |
|   " ----------
 | |
|   " For the current line, use the first argument if given, else v:lnum
 | |
|   let clnum = a:0 ? a:1 : v:lnum
 | |
| 
 | |
|   " Set up variables for restoring position in file.  Could use clnum here.
 | |
|   let vcol = col('.')
 | |
| 
 | |
|   " 3.2. Work on the current line {{{2
 | |
|   " -----------------------------
 | |
| 
 | |
|   " Get the current line.
 | |
|   let line = getline(clnum)
 | |
|   let ind = -1
 | |
| 
 | |
|   " If we got a closing bracket on an empty line, find its match and indent
 | |
|   " according to it.
 | |
|   let col = matchend(line, '^\s*[]}]')
 | |
| 
 | |
|   if col > 0 && !s:IsInString(clnum, col)
 | |
|     call cursor(clnum, col)
 | |
|     let bs = strpart('{}[]', stridx('}]', line[col - 1]) * 2, 2)
 | |
| 
 | |
|     let pairstart = escape(bs[0], '[')
 | |
|     let pairend = escape(bs[1], ']')
 | |
|     let pairline = searchpair(pairstart, '', pairend, 'bW')
 | |
| 
 | |
|     if pairline > 0 
 | |
|       let ind = indent(pairline)
 | |
|     else
 | |
|       let ind = virtcol('.') - 1
 | |
|     endif
 | |
| 
 | |
|     return ind
 | |
|   endif
 | |
| 
 | |
|   " If we are in a multi-line string, don't do anything to it.
 | |
|   if s:IsInString(clnum, matchend(line, '^\s*') + 1)
 | |
|     return indent('.')
 | |
|   endif
 | |
| 
 | |
|   " 3.3. Work on the previous line. {{{2
 | |
|   " -------------------------------
 | |
| 
 | |
|   let lnum = prevnonblank(clnum - 1)
 | |
| 
 | |
|   if lnum == 0
 | |
|     return 0
 | |
|   endif
 | |
| 
 | |
|   " Set up variables for current line.
 | |
|   let line = getline(lnum)
 | |
|   let ind = indent(lnum)
 | |
| 
 | |
|   " If the previous line ended with a block opening, add a level of indent.
 | |
|   " if s:Match(lnum, s:block_regex)
 | |
|     " return indent(lnum) + shiftwidth()
 | |
|   " endif
 | |
| 
 | |
|   " If the previous line contained an opening bracket, and we are still in it,
 | |
|   " add indent depending on the bracket type.
 | |
|   if line =~ '[[({]'
 | |
|     let counts = s:LineHasOpeningBrackets(lnum)
 | |
|     if counts[0] == '1' || counts[1] == '1' || counts[2] == '1'
 | |
|       return ind + shiftwidth()
 | |
|     else
 | |
|       call cursor(clnum, vcol)
 | |
|     end
 | |
|   endif
 | |
| 
 | |
|   " }}}2
 | |
| 
 | |
|   return ind
 | |
| endfunction
 | |
| 
 | |
| " }}}1
 | |
| 
 | |
| let &cpo = s:cpo_save
 | |
| unlet s:cpo_save
 | |
| 
 | |
| " vim:set sw=2 sts=2 ts=8 noet:
 |