neovim

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

cmdline_highlight_spec.lua (27437B)


      1 local t = require('test.testutil')
      2 local n = require('test.functional.testnvim')()
      3 local Screen = require('test.functional.ui.screen')
      4 
      5 local eq = t.eq
      6 local feed = n.feed
      7 local clear = n.clear
      8 local api = n.api
      9 local fn = n.fn
     10 local source = n.source
     11 local exec_capture = n.exec_capture
     12 local dedent = t.dedent
     13 local command = n.command
     14 
     15 local screen
     16 
     17 -- Bug in input() handling: :redraw! will erase the whole prompt up until
     18 -- user types something. It exists in Vim as well, so using `h<BS>` as
     19 -- a workaround.
     20 local function redraw_input()
     21  feed('{REDRAW}h<BS>')
     22 end
     23 
     24 before_each(function()
     25  clear()
     26  screen = Screen.new(40, 8)
     27  source([[
     28    highlight RBP1 guibg=Red
     29    highlight RBP2 guibg=Yellow
     30    highlight RBP3 guibg=Green
     31    highlight RBP4 guibg=Blue
     32    let g:NUM_LVLS = 4
     33    function Redraw()
     34      mode
     35      return "\<Ignore>"
     36    endfunction
     37    let g:id = ''
     38    cnoremap <expr> {REDRAW} Redraw()
     39    function DoPrompt(do_return) abort
     40      let id = g:id
     41      let Cb = g:Nvim_color_input{g:id}
     42      let out = input({'prompt': ':', 'highlight': Cb})
     43      let g:out{id} = out
     44      return (a:do_return ? out : "\<Ignore>")
     45    endfunction
     46    nnoremap <expr> {PROMPT} DoPrompt(0)
     47    cnoremap <expr> {PROMPT} DoPrompt(1)
     48    function RainBowParens(cmdline)
     49      let ret = []
     50      let i = 0
     51      let lvl = 0
     52      while i < len(a:cmdline)
     53        if a:cmdline[i] is# '('
     54          call add(ret, [i, i + 1, 'RBP' . ((lvl % g:NUM_LVLS) + 1)])
     55          let lvl += 1
     56        elseif a:cmdline[i] is# ')'
     57          let lvl -= 1
     58          call add(ret, [i, i + 1, 'RBP' . ((lvl % g:NUM_LVLS) + 1)])
     59        endif
     60        let i += 1
     61      endwhile
     62      return ret
     63    endfunction
     64    function SplitMultibyteStart(cmdline)
     65      let ret = []
     66      let i = 0
     67      while i < len(a:cmdline)
     68        let char = nr2char(char2nr(a:cmdline[i:]))
     69        if a:cmdline[i:i +  len(char) - 1] is# char
     70          if len(char) > 1
     71            call add(ret, [i + 1, i + len(char), 'RBP2'])
     72          endif
     73          let i += len(char)
     74        else
     75          let i += 1
     76        endif
     77      endwhile
     78      return ret
     79    endfunction
     80    function SplitMultibyteEnd(cmdline)
     81      let ret = []
     82      let i = 0
     83      while i < len(a:cmdline)
     84        let char = nr2char(char2nr(a:cmdline[i:]))
     85        if a:cmdline[i:i +  len(char) - 1] is# char
     86          if len(char) > 1
     87            call add(ret, [i, i + 1, 'RBP1'])
     88          endif
     89          let i += len(char)
     90        else
     91          let i += 1
     92        endif
     93      endwhile
     94      return ret
     95    endfunction
     96    function Echoing(cmdline)
     97      echo 'HERE'
     98      return v:_null_list
     99    endfunction
    100    function Echoning(cmdline)
    101      echon 'HERE'
    102      return v:_null_list
    103    endfunction
    104    function Echomsging(cmdline)
    105      echomsg 'HERE'
    106      return v:_null_list
    107    endfunction
    108    function Echoerring(cmdline)
    109      echoerr 'HERE'
    110      return v:_null_list
    111    endfunction
    112    function Redrawing(cmdline)
    113      redraw!
    114      return v:_null_list
    115    endfunction
    116    function Throwing(cmdline)
    117      throw "ABC"
    118      return v:_null_list
    119    endfunction
    120    function Halting(cmdline)
    121      while 1
    122      endwhile
    123    endfunction
    124    function ReturningGlobal(cmdline)
    125      return g:callback_return
    126    endfunction
    127    function ReturningGlobal2(cmdline)
    128      return g:callback_return[:len(a:cmdline)-1]
    129    endfunction
    130    function ReturningGlobalN(n, cmdline)
    131      return g:callback_return{a:n}
    132    endfunction
    133    let g:recording_calls = []
    134    function Recording(cmdline)
    135      call add(g:recording_calls, a:cmdline)
    136      return []
    137    endfunction
    138  ]])
    139  screen:set_default_attr_ids({
    140    RBP1 = { background = Screen.colors.Red },
    141    RBP2 = { background = Screen.colors.Yellow },
    142    RBP3 = { background = Screen.colors.Green },
    143    RBP4 = { background = Screen.colors.Blue },
    144    EOB = { bold = true, foreground = Screen.colors.Blue1 },
    145    ERR = { foreground = Screen.colors.Grey100, background = Screen.colors.Red },
    146    SK = { foreground = Screen.colors.Blue },
    147    PE = { bold = true, foreground = Screen.colors.SeaGreen4 },
    148    NUM = { foreground = Screen.colors.Blue2 },
    149    NPAR = { foreground = Screen.colors.Yellow },
    150    SQ = { foreground = Screen.colors.Blue3 },
    151    SB = { foreground = Screen.colors.Blue4 },
    152    E = { foreground = Screen.colors.Red, background = Screen.colors.Blue },
    153    M = { bold = true },
    154    MSEP = { bold = true, reverse = true },
    155  })
    156 end)
    157 
    158 local function set_color_cb(funcname, callback_return, id)
    159  api.nvim_set_var('id', id or '')
    160  if id and id ~= '' and fn.exists('*' .. funcname .. 'N') == 1 then
    161    command(('let g:Nvim_color_input%s = {cmdline -> %sN(%s, cmdline)}'):format(id, funcname, id))
    162    if callback_return then
    163      api.nvim_set_var('callback_return' .. id, callback_return)
    164    end
    165  else
    166    api.nvim_set_var('Nvim_color_input', funcname)
    167    if callback_return then
    168      api.nvim_set_var('callback_return', callback_return)
    169    end
    170  end
    171 end
    172 local function start_prompt(text)
    173  feed('{PROMPT}' .. (text or ''))
    174 end
    175 
    176 describe('Command-line coloring', function()
    177  it('works', function()
    178    set_color_cb('RainBowParens')
    179    api.nvim_set_option_value('more', false, {})
    180    start_prompt()
    181    screen:expect([[
    182                                              |
    183      {EOB:~                                       }|*6
    184      :^                                       |
    185    ]])
    186    feed('e')
    187    screen:expect([[
    188                                              |
    189      {EOB:~                                       }|*6
    190      :e^                                      |
    191    ]])
    192    feed('cho ')
    193    screen:expect([[
    194                                              |
    195      {EOB:~                                       }|*6
    196      :echo ^                                  |
    197    ]])
    198    feed('(')
    199    screen:expect([[
    200                                              |
    201      {EOB:~                                       }|*6
    202      :echo {RBP1:(}^                                 |
    203    ]])
    204    feed('(')
    205    screen:expect([[
    206                                              |
    207      {EOB:~                                       }|*6
    208      :echo {RBP1:(}{RBP2:(}^                                |
    209    ]])
    210    feed('42')
    211    screen:expect([[
    212                                              |
    213      {EOB:~                                       }|*6
    214      :echo {RBP1:(}{RBP2:(}42^                              |
    215    ]])
    216    feed('))')
    217    screen:expect([[
    218                                              |
    219      {EOB:~                                       }|*6
    220      :echo {RBP1:(}{RBP2:(}42{RBP2:)}{RBP1:)}^                            |
    221    ]])
    222    feed('<BS>')
    223    screen:expect([[
    224                                              |
    225      {EOB:~                                       }|*6
    226      :echo {RBP1:(}{RBP2:(}42{RBP2:)}^                             |
    227    ]])
    228    redraw_input()
    229    screen:expect {
    230      grid = [[
    231                                              |
    232      {EOB:~                                       }|*6
    233      :echo {RBP1:(}{RBP2:(}42{RBP2:)}^                             |
    234    ]],
    235      reset = true,
    236    }
    237  end)
    238  for _, func_part in ipairs({ '', 'n', 'msg' }) do
    239    it('disables :echo' .. func_part .. ' messages', function()
    240      set_color_cb('Echo' .. func_part .. 'ing')
    241      start_prompt('echo')
    242      screen:expect([[
    243                                                |
    244        {EOB:~                                       }|*6
    245        :echo^                                   |
    246      ]])
    247    end)
    248  end
    249  it('does the right thing when hl start appears to split multibyte char', function()
    250    set_color_cb('SplitMultibyteStart')
    251    start_prompt('echo "«')
    252    screen:expect {
    253      grid = [[
    254                                              |
    255      {EOB:~                                       }|*2
    256      {MSEP:                                        }|
    257      :echo "                                 |
    258      {ERR:E5405: Chunk 0 start 7 splits multibyte }|
    259      {ERR:character}                               |
    260      :echo "«^                                |
    261    ]],
    262    }
    263    feed('»')
    264    screen:expect([[
    265                                              |
    266      {EOB:~                                       }|*2
    267      {MSEP:                                        }|
    268      :echo "                                 |
    269      {ERR:E5405: Chunk 0 start 7 splits multibyte }|
    270      {ERR:character}                               |
    271      :echo "«»^                               |
    272    ]])
    273  end)
    274  it('does the right thing when hl end appears to split multibyte char', function()
    275    set_color_cb('SplitMultibyteEnd')
    276    start_prompt('echo "«')
    277    screen:expect([[
    278                                              |
    279      {EOB:~                                       }|*2
    280      {MSEP:                                        }|
    281      :echo "                                 |
    282      {ERR:E5406: Chunk 0 end 7 splits multibyte ch}|
    283      {ERR:aracter}                                 |
    284      :echo "«^                                |
    285    ]])
    286  end)
    287  it('does the right thing when erroring', function()
    288    set_color_cb('Echoerring')
    289    start_prompt('e')
    290    screen:expect([[
    291                                              |
    292      {EOB:~                                       }|
    293      {MSEP:                                        }|
    294      :                                       |
    295      {ERR:E5407: Callback has thrown an exception:}|
    296      {ERR: function DoPrompt[3]..Echoerring, line }|
    297      {ERR:1: Vim(echoerr):HERE}                    |
    298      :e^                                      |
    299    ]])
    300  end)
    301  it('silences :echo', function()
    302    set_color_cb('Echoing')
    303    start_prompt('e')
    304    screen:expect([[
    305                                              |
    306      {EOB:~                                       }|*6
    307      :e^                                      |
    308    ]])
    309    eq('', exec_capture('messages'))
    310  end)
    311  it('silences :echon', function()
    312    set_color_cb('Echoning')
    313    start_prompt('e')
    314    screen:expect([[
    315                                              |
    316      {EOB:~                                       }|*6
    317      :e^                                      |
    318    ]])
    319    eq('', exec_capture('messages'))
    320  end)
    321  it('silences :echomsg', function()
    322    set_color_cb('Echomsging')
    323    start_prompt('e')
    324    screen:expect([[
    325                                              |
    326      {EOB:~                                       }|*6
    327      :e^                                      |
    328    ]])
    329    eq('', exec_capture('messages'))
    330  end)
    331  it('does the right thing when throwing', function()
    332    set_color_cb('Throwing')
    333    start_prompt('e')
    334    screen:expect([[
    335                                              |
    336      {EOB:~                                       }|
    337      {MSEP:                                        }|
    338      :                                       |
    339      {ERR:E5407: Callback has thrown an exception:}|
    340      {ERR: function DoPrompt[3]..Throwing, line 1:}|
    341      {ERR: ABC}                                    |
    342      :e^                                      |
    343    ]])
    344  end)
    345  it('stops executing callback after a number of errors', function()
    346    set_color_cb('SplitMultibyteStart')
    347    start_prompt('let x = "«»«»«»«»«»"')
    348    screen:expect([[
    349                                              |
    350      {EOB:~                                       }|*2
    351      {MSEP:                                        }|
    352      :let x = "                              |
    353      {ERR:E5405: Chunk 0 start 10 splits multibyte}|
    354      {ERR: character}                              |
    355      :let x = "«»«»«»«»«»"^                   |
    356    ]])
    357    feed('\n')
    358    screen:expect([[
    359      ^                                        |
    360      {EOB:~                                       }|*6
    361                                              |
    362    ]])
    363    feed('\n')
    364    eq('let x = "«»«»«»«»«»"', api.nvim_get_var('out'))
    365    local msg = '\nE5405: Chunk 0 start 10 splits multibyte character'
    366    eq(msg:rep(1), fn.execute('messages'))
    367  end)
    368  it('allows interrupting callback with <C-c>', function()
    369    set_color_cb('Halting')
    370    start_prompt('echo 42')
    371    screen:expect([[
    372      ^                                        |
    373      {EOB:~                                       }|*6
    374                                              |
    375    ]])
    376    screen:sleep(500)
    377    feed('<C-c>')
    378    screen:expect([[
    379                                              |
    380      {EOB:~                                       }|*2
    381      {MSEP:                                        }|
    382      :                                       |
    383      {ERR:E5407: Callback has thrown an exception:}|
    384      {ERR: Keyboard interrupt}                     |
    385      :echo 42^                                |
    386    ]])
    387    redraw_input()
    388    screen:expect([[
    389                                              |
    390      {EOB:~                                       }|*6
    391      :echo 42^                                |
    392    ]])
    393    feed('\n')
    394    screen:expect([[
    395      ^                                        |
    396      {EOB:~                                       }|*6
    397      :echo 42                                |
    398    ]])
    399    feed('\n')
    400    eq('echo 42', api.nvim_get_var('out'))
    401    feed('<C-c>')
    402    screen:expect([[
    403      ^                                        |
    404      {EOB:~                                       }|*6
    405      Type  :qa  and pre...nter> to exit Nvim |
    406    ]])
    407  end)
    408  it('works fine with NUL, NL, CR', function()
    409    set_color_cb('RainBowParens')
    410    start_prompt('echo ("<C-v><CR><C-v><Nul><C-v><NL>")')
    411    screen:expect([[
    412                                              |
    413      {EOB:~                                       }|*6
    414      :echo {RBP1:(}"{SK:^M^@^@}"{RBP1:)}^                        |
    415    ]])
    416  end)
    417  it('errors out when callback returns something wrong', function()
    418    command('cnoremap + ++')
    419    set_color_cb('ReturningGlobal', '')
    420    start_prompt('#')
    421    screen:expect([[
    422                                              |
    423      {EOB:~                                       }|*3
    424      {MSEP:                                        }|
    425      :                                       |
    426      {ERR:E5400: Callback should return list}      |
    427      :#^                                      |
    428    ]])
    429 
    430    feed('<CR><CR><CR>')
    431    set_color_cb('ReturningGlobal', { { 0, 1, 'Normal' }, 42 })
    432    start_prompt('#')
    433    screen:expect([[
    434                                              |
    435      {EOB:~                                       }|*3
    436      {MSEP:                                        }|
    437      :                                       |
    438      {ERR:E5401: List item 1 is not a List}        |
    439      :#^                                      |
    440    ]])
    441 
    442    feed('<CR><CR><CR>')
    443    set_color_cb('ReturningGlobal2', { { 0, 1, 'Normal' }, { 1 } })
    444    start_prompt('+')
    445    screen:expect([[
    446                                              |
    447      {EOB:~                                       }|*2
    448      {MSEP:                                        }|
    449      :+                                      |
    450      {ERR:E5402: List item 1 has incorrect length:}|
    451      {ERR: 1 /= 3}                                 |
    452      :++^                                     |
    453    ]])
    454 
    455    feed('<CR><CR><CR>')
    456    set_color_cb('ReturningGlobal2', { { 0, 1, 'Normal' }, { 2, 3, 'Normal' } })
    457    start_prompt('+')
    458    screen:expect([[
    459                                              |
    460      {EOB:~                                       }|*2
    461      {MSEP:                                        }|
    462      :+                                      |
    463      {ERR:E5403: Chunk 1 start 2 not in range [1, }|
    464      {ERR:2)}                                      |
    465      :++^                                     |
    466    ]])
    467 
    468    feed('<CR><CR><CR>')
    469    set_color_cb('ReturningGlobal2', { { 0, 1, 'Normal' }, { 1, 3, 'Normal' } })
    470    start_prompt('+')
    471    screen:expect([[
    472                                              |
    473      {EOB:~                                       }|*3
    474      {MSEP:                                        }|
    475      :+                                      |
    476      {ERR:E5404: Chunk 1 end 3 not in range (1, 2]}|
    477      :++^                                     |
    478    ]])
    479  end)
    480  it('does not error out when called from a errored out cycle', function()
    481    set_color_cb('ReturningGlobal', { { 0, 1, 'Normal' } })
    482    feed(dedent([[
    483      :set regexpengine=2
    484      :for pat in [' \ze*', ' \zs*']
    485      :  try
    486      :    let l = matchlist('x x', pat)
    487      :    $put =input({'prompt':'>','highlight':'ReturningGlobal'})
    488      :
    489      :    $put ='E888 NOT detected for ' . pat
    490      :  catch
    491      :    $put =input({'prompt':'>','highlight':'ReturningGlobal'})
    492      :
    493      :    $put ='E888 detected for ' . pat
    494      :  endtry
    495      :endfor
    496      :
    497      :
    498      :
    499      :
    500      :
    501      :
    502    ]]))
    503    eq(
    504      { '', ':', 'E888 detected for  \\ze*', ':', 'E888 detected for  \\zs*' },
    505      api.nvim_buf_get_lines(0, 0, -1, false)
    506    )
    507    eq('', fn.execute('messages'))
    508  end)
    509  it('allows nesting input()s', function()
    510    set_color_cb('ReturningGlobal', { { 0, 1, 'RBP1' } }, '')
    511    start_prompt('1')
    512    screen:expect([[
    513                                              |
    514      {EOB:~                                       }|*6
    515      :{RBP1:1}^                                      |
    516    ]])
    517 
    518    set_color_cb('ReturningGlobal', { { 0, 1, 'RBP2' } }, '1')
    519    start_prompt('2')
    520    screen:expect([[
    521                                              |
    522      {EOB:~                                       }|*6
    523      :{RBP2:2}^                                      |
    524    ]])
    525 
    526    set_color_cb('ReturningGlobal', { { 0, 1, 'RBP3' } }, '2')
    527    start_prompt('3')
    528    screen:expect([[
    529                                              |
    530      {EOB:~                                       }|*6
    531      :{RBP3:3}^                                      |
    532    ]])
    533 
    534    set_color_cb('ReturningGlobal', { { 0, 1, 'RBP4' } }, '3')
    535    start_prompt('4')
    536    screen:expect([[
    537                                              |
    538      {EOB:~                                       }|*6
    539      :{RBP4:4}^                                      |
    540    ]])
    541 
    542    feed('<CR>')
    543    screen:expect([[
    544                                              |
    545      {EOB:~                                       }|*6
    546      :{RBP3:3}4^                                     |
    547    ]])
    548    feed('<CR>')
    549    screen:expect([[
    550                                              |
    551      {EOB:~                                       }|*6
    552      :{RBP2:2}34^                                    |
    553    ]])
    554    feed('<CR>')
    555    screen:expect([[
    556                                              |
    557      {EOB:~                                       }|*6
    558      :{RBP1:1}234^                                   |
    559    ]])
    560    feed('<CR><CR><C-l>')
    561    screen:expect([[
    562      ^                                        |
    563      {EOB:~                                       }|*6
    564                                              |
    565    ]])
    566    eq('1234', api.nvim_get_var('out'))
    567    eq('234', api.nvim_get_var('out1'))
    568    eq('34', api.nvim_get_var('out2'))
    569    eq('4', api.nvim_get_var('out3'))
    570    eq(0, fn.exists('g:out4'))
    571  end)
    572  it('runs callback with the same data only once', function()
    573    local function new_recording_calls(...)
    574      eq({ ... }, api.nvim_get_var('recording_calls'))
    575      api.nvim_set_var('recording_calls', {})
    576    end
    577    set_color_cb('Recording')
    578    start_prompt('')
    579    -- Regression test. Disambiguation:
    580    --
    581    --     new_recording_calls(expected_result) -- (actual_before_fix)
    582    --
    583    feed('a')
    584    new_recording_calls('a') -- ('a', 'a')
    585    feed('b')
    586    new_recording_calls('ab') -- ('a', 'ab', 'ab')
    587    feed('c')
    588    new_recording_calls('abc') -- ('ab', 'abc', 'abc')
    589    feed('<BS>')
    590    new_recording_calls('ab') -- ('abc', 'ab', 'ab')
    591    feed('<BS>')
    592    new_recording_calls('a') -- ('ab', 'a', 'a')
    593    feed('<BS>')
    594    new_recording_calls() -- ('a')
    595    feed('<CR><CR>')
    596    eq('', api.nvim_get_var('out'))
    597  end)
    598  it('does not crash when callback has caught not-a-editor-command exception', function()
    599    source([[
    600      function CaughtExc(cmdline) abort
    601        try
    602          gibberish
    603        catch
    604          " Do nothing
    605        endtry
    606        return []
    607      endfunction
    608    ]])
    609    set_color_cb('CaughtExc')
    610    start_prompt('1')
    611    eq(1, api.nvim_eval('1'))
    612  end)
    613 end)
    614 describe('Ex commands coloring', function()
    615  it('works', function()
    616    api.nvim_set_var('Nvim_color_cmdline', 'RainBowParens')
    617    feed(':echo (((1)))')
    618    screen:expect([[
    619                                              |
    620      {EOB:~                                       }|*6
    621      :echo {RBP1:(}{RBP2:(}{RBP3:(}1{RBP3:)}{RBP2:)}{RBP1:)}^                           |
    622    ]])
    623  end)
    624  it('still executes command-line even if errored out', function()
    625    api.nvim_set_var('Nvim_color_cmdline', 'SplitMultibyteStart')
    626    feed(':let x = "«"\n')
    627    eq('«', api.nvim_get_var('x'))
    628    local msg = 'E5405: Chunk 0 start 10 splits multibyte character'
    629    eq('\n' .. msg, fn.execute('messages'))
    630  end)
    631  it('does not error out when called from a errored out cycle', function()
    632    -- Apparently when there is a cycle in which one of the commands errors out
    633    -- this error may be caught by color_cmdline before it is presented to the
    634    -- user.
    635    feed(dedent([[
    636      :set regexpengine=2
    637      :for pat in [' \ze*', ' \zs*']
    638      :  try
    639      :    let l = matchlist('x x', pat)
    640      :    $put ='E888 NOT detected for ' . pat
    641      :  catch
    642      :    $put ='E888 detected for ' . pat
    643      :  endtry
    644      :endfor
    645    ]]))
    646    eq(
    647      { '', 'E888 detected for  \\ze*', 'E888 detected for  \\zs*' },
    648      api.nvim_buf_get_lines(0, 0, -1, false)
    649    )
    650    eq('', fn.execute('messages'))
    651  end)
    652  it('does not crash when using `n` in debug mode', function()
    653    feed(':debug execute "echo 1"\n')
    654    screen:expect([[
    655                                              |
    656      {EOB:~                                       }|*2
    657      {MSEP:                                        }|
    658      Entering Debug mode.  Type "cont" to con|
    659      tinue.                                  |
    660      cmd: execute "echo 1"                   |
    661      >^                                       |
    662    ]])
    663    feed('n\n')
    664    screen:expect([[
    665                                              |
    666      {MSEP:                                        }|
    667      Entering Debug mode.  Type "cont" to con|
    668      tinue.                                  |
    669      cmd: execute "echo 1"                   |
    670      >n                                      |
    671      1                                       |
    672      {PE:Press ENTER or type command to continue}^ |
    673    ]])
    674    feed('\n')
    675    screen:expect([[
    676      ^                                        |
    677      {EOB:~                                       }|*6
    678                                              |
    679    ]])
    680  end)
    681  it('mapping error does not cancel prompt', function()
    682    command("cnoremap <expr> x execute('throw 42')[-1]")
    683    feed(':#x')
    684    screen:expect([[
    685                                              |
    686      {EOB:~                                       }|*2
    687      {MSEP:                                        }|
    688      :#                                      |
    689      {ERR:Error in :}                              |
    690      {ERR:E605: Exception not caught: 42}          |
    691      :#^                                      |
    692    ]])
    693    feed('<CR>')
    694    screen:expect([[
    695                                              |
    696      {EOB:~                                       }|
    697      {MSEP:                                        }|
    698      :#                                      |
    699      {ERR:Error in :}                              |
    700      {ERR:E605: Exception not caught: 42}          |
    701      {ERR:E749: Empty buffer}                      |
    702      {PE:Press ENTER or type command to continue}^ |
    703    ]])
    704    feed('<CR>')
    705    eq('Error in :\nE605: Exception not caught: 42\nE749: Empty buffer', exec_capture('messages'))
    706  end)
    707  it('errors out when failing to get callback', function()
    708    api.nvim_set_var('Nvim_color_cmdline', 42)
    709    feed(':#')
    710    screen:expect([[
    711                                              |
    712      {EOB:~                                       }|
    713      {MSEP:                                        }|
    714      :                                       |
    715      {ERR:E5408: Unable to get g:Nvim_color_cmdlin}|
    716      {ERR:e callback: Vim:E6000: Argument is not a}|
    717      {ERR: function or function name}              |
    718      :#^                                      |
    719    ]])
    720  end)
    721 end)
    722 describe('Expressions coloring support', function()
    723  it('works', function()
    724    command('hi clear NvimNumber')
    725    command('hi clear NvimNestingParenthesis')
    726    command('hi NvimNumber guifg=Blue2')
    727    command('hi NvimNestingParenthesis guifg=Yellow')
    728    feed(':echo <C-r>=(((1)))')
    729    screen:expect([[
    730                                              |
    731      {EOB:~                                       }|*6
    732      ={NPAR:(((}{NUM:1}{NPAR:)))}^                                |
    733    ]])
    734  end)
    735  it('does not use Nvim_color_expr', function()
    736    api.nvim_set_var('Nvim_color_expr', 42)
    737    -- Used to error out due to failing to get callback.
    738    command('hi clear NvimNumber')
    739    command('hi NvimNumber guifg=Blue2')
    740    feed(':<C-r>=1')
    741    screen:expect([[
    742                                              |
    743      {EOB:~                                       }|*6
    744      ={NUM:1}^                                      |
    745    ]])
    746  end)
    747  it('works correctly with non-ASCII and control characters', function()
    748    command('hi clear NvimStringBody')
    749    command('hi clear NvimStringQuote')
    750    command('hi clear NvimInvalid')
    751    command('hi NvimStringQuote guifg=Blue3')
    752    command('hi NvimStringBody guifg=Blue4')
    753    command('hi NvimInvalid guifg=Red guibg=Blue')
    754    feed('i<C-r>="«»"«»')
    755    screen:expect([[
    756                                              |
    757      {EOB:~                                       }|*6
    758      ={SQ:"}{SB:«»}{SQ:"}{E:«»}^                                 |
    759    ]])
    760    feed('<C-c>')
    761    screen:expect([[
    762      ^                                        |
    763      {EOB:~                                       }|*6
    764      {M:-- INSERT --}                            |
    765    ]])
    766    feed('<Esc>')
    767    screen:expect([[
    768      ^                                        |
    769      {EOB:~                                       }|*6
    770                                              |
    771    ]])
    772    feed(':<C-\\>e"<C-v><C-x>"<C-v><C-x>')
    773    -- TODO(ZyX-I): Parser highlighting should not override special character
    774    --              highlighting.
    775    screen:expect([[
    776                                              |
    777      {EOB:~                                       }|*6
    778      ={SQ:"}{SB:^X}{SQ:"}{ERR:^X}^                                 |
    779    ]])
    780    feed('<C-c>')
    781    screen:expect([[
    782                                              |
    783      {EOB:~                                       }|*6
    784      :^                                       |
    785    ]])
    786    fn.setreg('a', { '\192' })
    787    feed('<C-r>="<C-r><C-r>a"<C-r><C-r>a"foo"')
    788    screen:expect([[
    789                                              |
    790      {EOB:~                                       }|*6
    791      ={SQ:"}{SB:<c0>}{SQ:"}{E:<c0>"}{SB:foo}{E:"}^                        |
    792    ]])
    793  end)
    794 end)