neovim

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

vhdl.vim (14778B)


      1 " VHDL indent ('93 syntax)
      2 " Language:    VHDL
      3 " Maintainer:  Gerald Lai <laigera+vim?gmail.com>
      4 " Version:     1.62
      5 " Last Change: 2017 Oct 17
      6 "              2023 Aug 28 by Vim Project (undo_indent)
      7 " URL:         http://www.vim.org/scripts/script.php?script_id=1450
      8 
      9 " only load this indent file when no other was loaded
     10 if exists("b:did_indent")
     11  finish
     12 endif
     13 let b:did_indent = 1
     14 
     15 " setup indent options for local VHDL buffer
     16 setlocal indentexpr=GetVHDLindent()
     17 setlocal indentkeys=!^F,o,O,0(,0)
     18 setlocal indentkeys+==~begin,=~end\ ,=~end\	,=~is,=~select,=~when
     19 setlocal indentkeys+==~if,=~then,=~elsif,=~else
     20 setlocal indentkeys+==~case,=~loop,=~for,=~generate,=~record,=~units,=~process,=~block,=~function,=~component,=~procedure
     21 setlocal indentkeys+==~architecture,=~configuration,=~entity,=~package
     22 
     23 let b:undo_indent = "setlocal indentexpr< indentkeys<"
     24 
     25 " constants
     26 " not a comment
     27 let s:NC = '\%(--.*\)\@<!'
     28 " end of string
     29 let s:ES = '\s*\%(--.*\)\=$'
     30 " no "end" keyword in front
     31 let s:NE = '\%(\<end\s\+\)\@<!'
     32 
     33 " option to disable alignment of generic/port mappings
     34 if !exists("g:vhdl_indent_genportmap")
     35  let g:vhdl_indent_genportmap = 1
     36 endif
     37 
     38 " option to disable alignment of right-hand side assignment "<=" statements
     39 if !exists("g:vhdl_indent_rhsassign")
     40  let g:vhdl_indent_rhsassign = 1
     41 endif
     42 
     43 " only define indent function once
     44 if exists("*GetVHDLindent")
     45  finish
     46 endif
     47 
     48 function GetVHDLindent()
     49  " store current line & string
     50  let curn = v:lnum
     51  let curs = getline(curn)
     52 
     53  " find previous line that is not a comment
     54  let prevn = prevnonblank(curn - 1)
     55  let prevs = getline(prevn)
     56  while prevn > 0 && prevs =~ '^\s*--'
     57    let prevn = prevnonblank(prevn - 1)
     58    let prevs = getline(prevn)
     59  endwhile
     60  let prevs_noi = substitute(prevs, '^\s*', '', '')
     61 
     62  " default indent starts as previous non-comment line's indent
     63  let ind = prevn > 0 ? indent(prevn) : 0
     64  " backup default
     65  let ind2 = ind
     66 
     67  " indent:   special; kill string so it would not affect other filters
     68  " keywords: "report" + string
     69  " where:    anywhere in current or previous line
     70  let s0 = s:NC.'\<report\>\s*".*"'
     71  if curs =~? s0
     72    let curs = ""
     73  endif
     74  if prevs =~? s0
     75    let prevs = ""
     76  endif
     77 
     78  " indent:   previous line's comment position, otherwise follow next non-comment line if possible
     79  " keyword:  "--"
     80  " where:    start of current line
     81  if curs =~ '^\s*--'
     82    let pn = curn - 1
     83    let ps = getline(pn)
     84    if curs =~ '^\s*--\s' && ps =~ '--'
     85      return indent(pn) + stridx(substitute(ps, '^\s*', '', ''), '--')
     86    else
     87      " find nextnonblank line that is not a comment
     88      let nn = nextnonblank(curn + 1)
     89      let ns = getline(nn)
     90      while nn > 0 && ns =~ '^\s*--'
     91        let nn = nextnonblank(nn + 1)
     92        let ns = getline(nn)
     93      endwhile
     94      let n = indent(nn)
     95      return n != -1 ? n : ind
     96    endif
     97  endif
     98 
     99  " ****************************************************************************************
    100  " indent:   align generic variables & port names
    101  " keywords: "procedure" + name, "generic", "map", "port" + "(", provided current line is part of mapping
    102  " where:    anywhere in previous 2 lines
    103  " find following previous non-comment line
    104  let pn = prevnonblank(prevn - 1)
    105  let ps = getline(pn)
    106  while pn > 0 && ps =~ '^\s*--'
    107    let pn = prevnonblank(pn - 1)
    108    let ps = getline(pn)
    109  endwhile
    110  if (curs =~ '^\s*)' || curs =~? '^\s*\%(\<\%(procedure\|generic\|map\|port\)\>.*\)\@<!\w\+\s*\w*\s*\((.*)\)*\s*\%(=>\s*\S\+\|:[^=]\@=\s*\%(\%(in\|out\|inout\|buffer\|linkage\)\>\|\s\+\)\)') && (prevs =~? s:NC.'\<\%(procedure\s\+\S\+\|generic\|map\|port\)\s*(\%(\s*\w\)\=' || (ps =~? s:NC.'\<\%(procedure\|generic\|map\|port\)'.s:ES && prevs =~ '^\s*('))
    111    " align closing ")" with opening "("
    112    if curs =~ '^\s*)'
    113      return ind2 + stridx(prevs_noi, '(')
    114    endif
    115    let m = matchend(prevs_noi, '(\s*\ze\w')
    116    if m != -1
    117      return ind2 + m
    118    else
    119      if g:vhdl_indent_genportmap
    120        return ind2 + stridx(prevs_noi, '(') + shiftwidth()
    121      else
    122        return ind2 + shiftwidth()
    123      endif
    124    endif
    125  endif
    126 
    127  " indent:   align conditional/select statement
    128  " keywords: variable + "<=" without ";" ending
    129  " where:    start of previous line
    130  if prevs =~? '^\s*\S\+\s*<=[^;]*'.s:ES
    131    if g:vhdl_indent_rhsassign
    132      return ind2 + matchend(prevs_noi, '<=\s*\ze.')
    133    else
    134      return ind2 + shiftwidth()
    135    endif
    136  endif
    137 
    138  " indent:   backtrace previous non-comment lines for next smaller or equal size indent
    139  " keywords: "end" + "record", "units"
    140  " where:    start of previous line
    141  " keyword:  ")"
    142  " where:    start of previous line
    143  " keyword:  without "<=" + ";" ending
    144  " where:    anywhere in previous line
    145  " keyword:  "=>" + ")" ending, provided current line does not begin with ")"
    146  " where:    anywhere in previous line
    147  " _note_:   indent allowed to leave this filter
    148  let m = 0
    149  if prevs =~? '^\s*end\s\+\%(record\|units\)\>'
    150    let m = 3
    151  elseif prevs =~ '^\s*)'
    152    let m = 1
    153  elseif prevs =~ s:NC.'\%(<=.*\)\@<!;'.s:ES || (curs !~ '^\s*)' && prevs =~ s:NC.'=>.*'.s:NC.')'.s:ES)
    154    let m = 2
    155  endif
    156 
    157  if m > 0
    158    let pn = prevnonblank(prevn - 1)
    159    let ps = getline(pn)
    160    while pn > 0
    161      let t = indent(pn)
    162      if ps !~ '^\s*--' && (t < ind || (t == ind && m == 3))
    163        " make sure one of these is true
    164        " keywords: variable + "<=" without ";" ending
    165        " where:    start of previous non-comment line
    166        " keywords: "procedure", "generic", "map", "port"
    167        " where:    anywhere in previous non-comment line
    168        " keyword:  "("
    169        " where:    start of previous non-comment line
    170        if m < 3 && ps !~? '^\s*\S\+\s*<=[^;]*'.s:ES
    171          if ps =~? s:NC.'\<\%(procedure\|generic\|map\|port\)\>' || ps =~ '^\s*('
    172            let ind = t
    173          endif
    174          break
    175        endif
    176        let ind = t
    177        if m > 1
    178          " find following previous non-comment line
    179          let ppn = prevnonblank(pn - 1)
    180          let pps = getline(ppn)
    181          while ppn > 0 && pps =~ '^\s*--'
    182            let ppn = prevnonblank(ppn - 1)
    183            let pps = getline(ppn)
    184          endwhile
    185          " indent:   follow
    186          " keyword:  "select"
    187          " where:    end of following previous non-comment line
    188          " keyword:  "type"
    189          " where:    start of following previous non-comment line
    190          if m == 2
    191            let s1 = s:NC.'\<select'.s:ES
    192            if ps !~? s1 && pps =~? s1
    193              let ind = indent(ppn)
    194            endif
    195          elseif m == 3
    196            let s1 = '^\s*type\>'
    197            if ps !~? s1 && pps =~? s1
    198              let ind = indent(ppn)
    199            endif
    200          endif
    201        endif
    202        break
    203      endif
    204      let pn = prevnonblank(pn - 1)
    205      let ps = getline(pn)
    206    endwhile
    207  endif
    208 
    209  " indent:   follow indent of previous opening statement, otherwise -sw
    210  " keyword:  "begin"
    211  " where:    anywhere in current line
    212  if curs =~? s:NC.'\<begin\>'
    213    " find previous opening statement of
    214    " keywords: "architecture", "block", "entity", "function", "generate", "procedure", "process"
    215    let s2 = s:NC.s:NE.'\<\%(architecture\|block\|entity\|function\|generate\|procedure\|process\)\>'
    216 
    217    let pn = prevnonblank(curn - 1)
    218    let ps = getline(pn)
    219    while pn > 0 && (ps =~ '^\s*--' || ps !~? s2)
    220      let pn = prevnonblank(pn - 1)
    221      let ps = getline(pn)
    222 
    223      if (ps =~? s:NC.'\<begin\>')
    224        return indent(pn) - shiftwidth()
    225      endif
    226    endwhile
    227 
    228    if (pn == 0)
    229      return ind - shiftwidth()
    230    else
    231      return indent(pn)
    232    endif
    233  endif
    234 
    235  " indent:   +sw if previous line is previous opening statement
    236  " keywords: "record", "units"
    237  " where:    anywhere in current line
    238  if curs =~? s:NC.s:NE.'\<\%(record\|units\)\>'
    239    " find previous opening statement of
    240    " keyword: "type"
    241    let s3 = s:NC.s:NE.'\<type\>'
    242    if curs !~? s3.'.*'.s:NC.'\<\%(record\|units\)\>.*'.s:ES && prevs =~? s3
    243      let ind = ind + shiftwidth()
    244    endif
    245    return ind
    246  endif
    247 
    248  " ****************************************************************************************
    249  " indent:   0
    250  " keywords: "architecture", "configuration", "entity", "library", "package"
    251  " where:    start of current line
    252  if curs =~? '^\s*\%(architecture\|configuration\|entity\|library\|package\)\>'
    253    return 0
    254  endif
    255 
    256  " indent:   maintain indent of previous opening statement
    257  " keyword:  "is"
    258  " where:    start of current line
    259  " find previous opening statement of
    260  " keywords: "architecture", "block", "configuration", "entity", "function", "package", "procedure", "process", "type"
    261  if curs =~? '^\s*\<is\>' && prevs =~? s:NC.s:NE.'\<\%(architecture\|block\|configuration\|entity\|function\|package\|procedure\|process\|type\)\>'
    262    return ind2
    263  endif
    264 
    265  " indent:   maintain indent of previous opening statement
    266  " keyword:  "then"
    267  " where:    start of current line
    268  " find previous opening statement of
    269  " keywords: "elsif", "if"
    270  if curs =~? '^\s*\<then\>' && prevs =~? s:NC.'\%(\<elsif\>\|'.s:NE.'\<if\>\)'
    271    return ind2
    272  endif
    273 
    274  " indent:   maintain indent of previous opening statement
    275  " keyword:  "generate"
    276  " where:    start of current line
    277  " find previous opening statement of
    278  " keywords: "for", "if"
    279  if curs =~? '^\s*\<generate\>' && prevs =~? s:NC.s:NE.'\%(\%(\<wait\s\+\)\@<!\<for\|\<if\)\>'
    280    return ind2
    281  endif
    282 
    283  " indent:   +sw
    284  " keywords: "block", "process"
    285  " removed:  "begin", "case", "elsif", "if", "loop", "record", "units", "while"
    286  " where:    anywhere in previous line
    287  if prevs =~? s:NC.s:NE.'\<\%(block\|process\)\>'
    288    return ind + shiftwidth()
    289  endif
    290 
    291  " indent:   +sw
    292  " keywords: "architecture", "configuration", "entity", "package"
    293  " removed:  "component", "for", "when", "with"
    294  " where:    start of previous line
    295  if prevs =~? '^\s*\%(architecture\|configuration\|entity\|package\)\>'
    296    return ind + shiftwidth()
    297  endif
    298 
    299  " indent:   +sw
    300  " keyword:  "select"
    301  " removed:  "generate", "is", "=>"
    302  " where:    end of previous line
    303  if prevs =~? s:NC.'\<select'.s:ES
    304    return ind + shiftwidth()
    305  endif
    306 
    307  " indent:   +sw
    308  " keyword:  "begin", "loop", "record", "units"
    309  " where:    anywhere in previous line
    310  " keyword:  "component", "else", "for"
    311  " where:    start of previous line
    312  " keyword:  "generate", "is", "then", "=>"
    313  " where:    end of previous line
    314  " _note_:   indent allowed to leave this filter
    315  if prevs =~? s:NC.'\%(\<begin\>\|'.s:NE.'\<\%(loop\|record\|units\)\>\)' || prevs =~? '^\s*\%(component\|else\|for\)\>' || prevs =~? s:NC.'\%('.s:NE.'\<generate\|\<\%(is\|then\)\|=>\)'.s:ES
    316    let ind = ind + shiftwidth()
    317  endif
    318 
    319  " ****************************************************************************************
    320  " indent:   -sw
    321  " keywords: "when", provided previous line does not begin with "when", does not end with "is"
    322  " where:    start of current line
    323  let s4 = '^\s*when\>'
    324  if curs =~? s4
    325    if prevs =~? s:NC.'\<is'.s:ES
    326      return ind
    327    elseif prevs !~? s4
    328      return ind - shiftwidth()
    329    else
    330      return ind2
    331    endif
    332  endif
    333 
    334  " indent:   -sw
    335  " keywords: "else", "elsif", "end" + "block", "for", "function", "generate", "if", "loop", "procedure", "process", "record", "units"
    336  " where:    start of current line
    337  let s5 = 'block\|for\|function\|generate\|if\|loop\|procedure\|process\|record\|units'
    338  if curs =~? '^\s*\%(else\|elsif\|end\s\+\%('.s5.'\)\)\>'
    339    if prevs =~? '^\s*\%(elsif\|'.s5.'\)'
    340      return ind
    341    else
    342      return ind - shiftwidth()
    343    endif
    344  endif
    345 
    346  " indent:   backtrace previous non-comment lines
    347  " keyword:  "end" + "case", "component"
    348  " where:    start of current line
    349  let m = 0
    350  if curs =~? '^\s*end\s\+case\>'
    351    let m = 1
    352  elseif curs =~? '^\s*end\s\+component\>'
    353    let m = 2
    354  endif
    355 
    356  if m > 0
    357    " find following previous non-comment line
    358    let pn = prevn
    359    let ps = getline(pn)
    360    while pn > 0
    361      if ps !~ '^\s*--'
    362        "indent:   -2sw
    363        "keywords: "end" + "case"
    364        "where:    start of previous non-comment line
    365        "indent:   -sw
    366        "keywords: "when"
    367        "where:    start of previous non-comment line
    368        "indent:   follow
    369        "keywords: "case"
    370        "where:    start of previous non-comment line
    371        if m == 1
    372          if ps =~? '^\s*end\s\+case\>'
    373            return indent(pn) - 2 * shiftwidth()
    374          elseif ps =~? '^\s*when\>'
    375            return indent(pn) - shiftwidth()
    376          elseif ps =~? '^\s*case\>'
    377            return indent(pn)
    378          endif
    379        "indent:   follow
    380        "keyword:  "component"
    381        "where:    start of previous non-comment line
    382        elseif m == 2
    383          if ps =~? '^\s*component\>'
    384            return indent(pn)
    385          endif
    386        endif
    387      endif
    388      let pn = prevnonblank(pn - 1)
    389      let ps = getline(pn)
    390    endwhile
    391    return ind - shiftwidth()
    392  endif
    393 
    394  " indent:   -sw
    395  " keyword:  ")"
    396  " where:    start of current line
    397  if curs =~ '^\s*)'
    398    return ind - shiftwidth()
    399  endif
    400 
    401  " indent:   0
    402  " keywords: "end" + "architecture", "configuration", "entity", "package"
    403  " where:    start of current line
    404  if curs =~? '^\s*end\s\+\%(architecture\|configuration\|entity\|package\)\>'
    405    return 0
    406  endif
    407 
    408  " indent:   -sw
    409  " keywords: "end" + identifier, ";"
    410  " where:    start of current line
    411  "if curs =~? '^\s*end\s\+\w\+\>'
    412  if curs =~? '^\s*end\%(\s\|;'.s:ES.'\)'
    413    return ind - shiftwidth()
    414  endif
    415 
    416  " ****************************************************************************************
    417  " indent:   maintain indent of previous opening statement
    418  " keywords: without "procedure", "generic", "map", "port" + ":" but not ":=" + "in", "out", "inout", "buffer", "linkage", variable & ":="
    419  " where:    start of current line
    420  if curs =~? '^\s*\%(\<\%(procedure\|generic\|map\|port\)\>.*\)\@<!\w\+\s*\w*\s*:[^=]\@=\s*\%(\%(in\|out\|inout\|buffer\|linkage\)\>\|\w\+\s\+:=\)'
    421    return ind2
    422  endif
    423 
    424  " ****************************************************************************************
    425  " indent:     maintain indent of previous opening statement, corner case which
    426  "             does not end in ;, but is part of a mapping
    427  " keywords:   without "procedure", "generic", "map", "port" + ":" but not ":=", never + ;$ and
    428  "             prevline without "procedure", "generic", "map", "port" + ":" but not ":=" + eventually ;$
    429  " where:      start of current line
    430  if curs =~? '^\s*\%(\<\%(procedure\|generic\|map\|port\)\>.*\)\@<!\w\+\s*\w*\s*:[^=].*[^;].*$'
    431    if prevs =~? '^\s*\%(\<\%(procedure\|generic\|map\|port\)\>.*\)\@<!\w\+\s*\w*\s*:[^=].*;.*$'
    432      return ind2
    433    endif
    434  endif
    435 
    436  " return leftover filtered indent
    437  return ind
    438 endfunction