neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

json.vim (4644B)


      1 " Vim indent file
      2 " Language:		JSON
      3 " Maintainer:		Eli Parra <eli@elzr.com> https://github.com/elzr/vim-json
      4 " Last Change:          2020 Aug 30
      5 "   https://github.com/jakar/vim-json/commit/20b650e22aa750c4ab6a66aa646bdd95d7cd548a#diff-e81fc111b2052e306d126bd9989f7b7c
      6 "                       2022 Sep 07: b:undo_indent added by Doug Kearns
      7 " Original Author:	Rogerz Zhang <rogerz.zhang at gmail.com> http://github.com/rogerz/vim-json
      8 " Acknowledgement:      Based off of vim-javascript maintained by Darrick Wiebe 
      9 "                       http://www.vim.org/scripts/script.php?script_id=2765
     10 
     11 " 0. Initialization {{{1
     12 " =================
     13 
     14 " Only load this indent file when no other was loaded.
     15 if exists("b:did_indent")
     16  finish
     17 endif
     18 let b:did_indent = 1
     19 
     20 setlocal nosmartindent
     21 
     22 " Now, set up our indentation expression and keys that trigger it.
     23 setlocal indentexpr=GetJSONIndent(v:lnum)
     24 setlocal indentkeys=0{,0},0),0[,0],!^F,o,O,e
     25 
     26 let b:undo_indent = "setl inde< indk< si<"
     27 
     28 " Only define the function once.
     29 if exists("*GetJSONIndent")
     30  finish
     31 endif
     32 
     33 let s:cpo_save = &cpo
     34 set cpo&vim
     35 
     36 " 1. Variables {{{1
     37 " ============
     38 
     39 let s:line_term = '\s*\%(\%(\/\/\).*\)\=$'
     40 " Regex that defines blocks.
     41 let s:block_regex = '\%({\)\s*\%(|\%([*@]\=\h\w*,\=\s*\)\%(,\s*[*@]\=\h\w*\)*|\)\=' . s:line_term
     42 
     43 " 2. Auxiliary Functions {{{1
     44 " ======================
     45 
     46 " Check if the character at lnum:col is inside a string.
     47 function s:IsInString(lnum, col)
     48  return synIDattr(synID(a:lnum, a:col, 1), 'name') == 'jsonString'
     49 endfunction
     50 
     51 " Find line above 'lnum' that isn't empty, or in a string.
     52 function s:PrevNonBlankNonString(lnum)
     53  let lnum = prevnonblank(a:lnum)
     54  while lnum > 0
     55    " If the line isn't empty or in a string, end search.
     56    let line = getline(lnum)
     57    if !(s:IsInString(lnum, 1) && s:IsInString(lnum, strlen(line)))
     58      break
     59    endif
     60    let lnum = prevnonblank(lnum - 1)
     61  endwhile
     62  return lnum
     63 endfunction
     64 
     65 " Check if line 'lnum' has more opening brackets than closing ones.
     66 function s:LineHasOpeningBrackets(lnum)
     67  let open_0 = 0
     68  let open_2 = 0
     69  let open_4 = 0
     70  let line = getline(a:lnum)
     71  let pos = match(line, '[][(){}]', 0)
     72  while pos != -1
     73    let idx = stridx('(){}[]', line[pos])
     74    if idx % 2 == 0
     75      let open_{idx} = open_{idx} + 1
     76    else
     77      let open_{idx - 1} = open_{idx - 1} - 1
     78    endif
     79    let pos = match(line, '[][(){}]', pos + 1)
     80  endwhile
     81  return (open_0 > 0) . (open_2 > 0) . (open_4 > 0)
     82 endfunction
     83 
     84 function s:Match(lnum, regex)
     85  let col = match(getline(a:lnum), a:regex) + 1
     86  return col > 0 && !s:IsInString(a:lnum, col) ? col : 0
     87 endfunction
     88 
     89 " 3. GetJSONIndent Function {{{1
     90 " =========================
     91 
     92 function GetJSONIndent(...)
     93  " 3.1. Setup {{{2
     94  " ----------
     95  " For the current line, use the first argument if given, else v:lnum
     96  let clnum = a:0 ? a:1 : v:lnum
     97 
     98  " Set up variables for restoring position in file.  Could use clnum here.
     99  let vcol = col('.')
    100 
    101  " 3.2. Work on the current line {{{2
    102  " -----------------------------
    103 
    104  " Get the current line.
    105  let line = getline(clnum)
    106  let ind = -1
    107 
    108  " If we got a closing bracket on an empty line, find its match and indent
    109  " according to it.
    110  let col = matchend(line, '^\s*[]}]')
    111 
    112  if col > 0 && !s:IsInString(clnum, col)
    113    call cursor(clnum, col)
    114    let bs = strpart('{}[]', stridx('}]', line[col - 1]) * 2, 2)
    115 
    116    let pairstart = escape(bs[0], '[')
    117    let pairend = escape(bs[1], ']')
    118    let pairline = searchpair(pairstart, '', pairend, 'bW')
    119 
    120    if pairline > 0 
    121      let ind = indent(pairline)
    122    else
    123      let ind = virtcol('.') - 1
    124    endif
    125 
    126    return ind
    127  endif
    128 
    129  " If we are in a multi-line string, don't do anything to it.
    130  if s:IsInString(clnum, matchend(line, '^\s*') + 1)
    131    return indent('.')
    132  endif
    133 
    134  " 3.3. Work on the previous line. {{{2
    135  " -------------------------------
    136 
    137  let lnum = prevnonblank(clnum - 1)
    138 
    139  if lnum == 0
    140    return 0
    141  endif
    142 
    143  " Set up variables for current line.
    144  let line = getline(lnum)
    145  let ind = indent(lnum)
    146 
    147  " If the previous line ended with a block opening, add a level of indent.
    148  " if s:Match(lnum, s:block_regex)
    149    " return indent(lnum) + shiftwidth()
    150  " endif
    151 
    152  " If the previous line contained an opening bracket, and we are still in it,
    153  " add indent depending on the bracket type.
    154  if line =~ '[[({]'
    155    let counts = s:LineHasOpeningBrackets(lnum)
    156    if counts[0] == '1' || counts[1] == '1' || counts[2] == '1'
    157      return ind + shiftwidth()
    158    else
    159      call cursor(clnum, vcol)
    160    end
    161  endif
    162 
    163  " }}}2
    164 
    165  return ind
    166 endfunction
    167 
    168 " }}}1
    169 
    170 let &cpo = s:cpo_save
    171 unlet s:cpo_save
    172 
    173 " vim:set sw=2 sts=2 ts=8 noet: