neovim

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

highlight_spec.lua (15614B)


      1 local t = require('test.testutil')
      2 local n = require('test.functional.testnvim')()
      3 local Screen = require('test.functional.ui.screen')
      4 local tt = require('test.functional.testterm')
      5 
      6 local assert_alive = n.assert_alive
      7 local feed, clear = n.feed, n.clear
      8 local api = n.api
      9 local testprg, command = n.testprg, n.command
     10 local fn = n.fn
     11 local nvim_set = n.nvim_set
     12 local is_os = t.is_os
     13 local skip = t.skip
     14 
     15 describe(':terminal highlight', function()
     16  local screen
     17 
     18  before_each(function()
     19    clear()
     20    screen = Screen.new(50, 7, { rgb = false })
     21    screen:set_default_attr_ids({
     22      [1] = { foreground = 45 },
     23      [2] = { background = 46 },
     24      [3] = { foreground = 45, background = 46 },
     25      [4] = { bold = true, italic = true, underline = true, strikethrough = true },
     26      [5] = { bold = true },
     27      [6] = { foreground = 12 },
     28      [7] = { bold = true, reverse = true },
     29      [8] = { background = 11 },
     30      [9] = { foreground = 130 },
     31      [10] = { reverse = true },
     32      [11] = { background = 11 },
     33      [12] = { bold = true, underdouble = true },
     34      [13] = { italic = true, undercurl = true },
     35    })
     36    command(("enew | call jobstart(['%s'], {'term':v:true})"):format(testprg('tty-test')))
     37    feed('i')
     38    screen:expect([[
     39      tty ready                                         |
     40      ^                                                  |
     41                                                        |*4
     42      {5:-- TERMINAL --}                                    |
     43    ]])
     44  end)
     45 
     46  local function descr(title, attr_num, set_attrs_fn)
     47    local function sub(s)
     48      local str = s:gsub('NUM', attr_num)
     49      return str
     50    end
     51 
     52    describe(title, function()
     53      before_each(function()
     54        set_attrs_fn()
     55        tt.feed_data('text')
     56        tt.clear_attrs()
     57        tt.feed_data('text')
     58      end)
     59 
     60      local function pass_attrs()
     61        screen:expect(sub([[
     62          tty ready                                         |
     63          {NUM:text}text^                                          |
     64                                                            |*4
     65          {5:-- TERMINAL --}                                    |
     66        ]]))
     67      end
     68 
     69      it('will pass the corresponding attributes', pass_attrs)
     70 
     71      it('will pass the corresponding attributes on scrollback', function()
     72        pass_attrs()
     73        local lines = {}
     74        for i = 1, 8 do
     75          table.insert(lines, 'line' .. tostring(i))
     76        end
     77        table.insert(lines, '')
     78        tt.feed_data(lines)
     79        screen:expect([[
     80          line4                                             |
     81          line5                                             |
     82          line6                                             |
     83          line7                                             |
     84          line8                                             |
     85          ^                                                  |
     86          {5:-- TERMINAL --}                                    |
     87        ]])
     88        feed('<c-\\><c-n>gg')
     89        screen:expect(sub([[
     90          ^tty ready                                         |
     91          {NUM:text}textline1                                     |
     92          line2                                             |
     93          line3                                             |
     94          line4                                             |
     95          line5                                             |
     96                                                            |
     97        ]]))
     98      end)
     99    end)
    100  end
    101 
    102  descr('foreground', 1, function()
    103    tt.set_fg(45)
    104  end)
    105  descr('background', 2, function()
    106    tt.set_bg(46)
    107  end)
    108  descr('foreground and background', 3, function()
    109    tt.set_fg(45)
    110    tt.set_bg(46)
    111  end)
    112  descr('bold, italics, underline and strikethrough', 4, function()
    113    tt.set_bold()
    114    tt.set_italic()
    115    tt.set_underline()
    116    tt.set_strikethrough()
    117  end)
    118  descr('bold and underdouble', 12, function()
    119    tt.set_bold()
    120    tt.set_underdouble()
    121  end)
    122  descr('italics and undercurl', 13, function()
    123    tt.set_italic()
    124    tt.set_undercurl()
    125  end)
    126 end)
    127 
    128 it(':terminal highlight has lower precedence than editor #9964', function()
    129  clear()
    130  local screen = Screen.new(30, 4, { rgb = true })
    131  screen:set_default_attr_ids({
    132    -- "Normal" highlight emitted by the child nvim process.
    133    N_child = {
    134      foreground = tonumber('0x4040ff'),
    135      background = tonumber('0xffff40'),
    136      fg_indexed = true,
    137      bg_indexed = true,
    138    },
    139    -- "Search" highlight in the parent nvim process.
    140    S = { background = Screen.colors.Green, italic = true, foreground = Screen.colors.Red },
    141    -- "Question" highlight in the parent nvim process.
    142    -- note: bg is indexed as it comes from the (cterm) child, while fg isn't as it comes from (rgb) parent
    143    Q = {
    144      background = tonumber('0xffff40'),
    145      bold = true,
    146      foreground = Screen.colors.SeaGreen4,
    147      bg_indexed = true,
    148    },
    149  })
    150  -- Child nvim process in :terminal (with cterm colors).
    151  fn.jobstart({
    152    n.nvim_prog,
    153    '-n',
    154    '-u',
    155    'NORC',
    156    '-i',
    157    'NONE',
    158    '--cmd',
    159    nvim_set .. ' notermguicolors',
    160    '+hi Normal ctermfg=Blue ctermbg=Yellow',
    161    '+norm! ichild nvim',
    162    '+norm! oline 2',
    163  }, {
    164    term = true,
    165    env = {
    166      VIMRUNTIME = os.getenv('VIMRUNTIME'),
    167    },
    168  })
    169  screen:expect([[
    170    {N_child:^child nvim                    }|
    171    {N_child:line 2                        }|
    172    {N_child:                              }|
    173                                  |
    174  ]])
    175  command('hi Search gui=italic guifg=Red guibg=Green cterm=italic ctermfg=Red ctermbg=Green')
    176  feed('/nvim<cr>')
    177  screen:expect([[
    178    {N_child:child }{S:^nvim}{N_child:                    }|
    179    {N_child:line 2                        }|
    180    {N_child:                              }|
    181    /nvim                         |
    182  ]])
    183  command('syntax keyword Question line')
    184  screen:expect([[
    185    {N_child:child }{S:^nvim}{N_child:                    }|
    186    {Q:line}{N_child: 2                        }|
    187    {N_child:                              }|
    188    /nvim                         |
    189  ]])
    190 end)
    191 
    192 it('CursorLine and CursorColumn work in :terminal buffer in Normal mode', function()
    193  clear()
    194  local screen = Screen.new(50, 7)
    195  screen:add_extra_attr_ids({ [100] = { background = Screen.colors.Gray90, reverse = true } })
    196  command(("enew | call jobstart(['%s'], {'term':v:true})"):format(testprg('tty-test')))
    197  screen:expect([[
    198    ^tty ready                                         |
    199                                                      |*6
    200  ]])
    201  tt.feed_data((' foobar'):rep(30))
    202  screen:expect([[
    203    ^tty ready                                         |
    204     foobar foobar foobar foobar foobar foobar foobar |
    205    foobar foobar foobar foobar foobar foobar foobar f|
    206    oobar foobar foobar foobar foobar foobar foobar fo|
    207    obar foobar foobar foobar foobar foobar foobar foo|
    208    bar foobar                                        |
    209                                                      |
    210  ]])
    211  command('set cursorline cursorcolumn')
    212  feed('j10w')
    213  screen:expect([[
    214    tty ready     {21: }                                   |
    215     foobar foobar{21: }foobar foobar foobar foobar foobar |
    216    {21:foobar foobar ^foobar foobar foobar foobar foobar f}|
    217    oobar foobar f{21:o}obar foobar foobar foobar foobar fo|
    218    obar foobar fo{21:o}bar foobar foobar foobar foobar foo|
    219    bar foobar    {21: }                                   |
    220                                                      |
    221  ]])
    222  -- Entering terminal mode disables 'cursorline' and 'cursorcolumn'.
    223  feed('i')
    224  screen:expect([[
    225    tty ready                                         |
    226     foobar foobar foobar foobar foobar foobar foobar |
    227    foobar foobar foobar foobar foobar foobar foobar f|
    228    oobar foobar foobar foobar foobar foobar foobar fo|
    229    obar foobar foobar foobar foobar foobar foobar foo|
    230    bar foobar^                                        |
    231    {5:-- TERMINAL --}                                    |
    232  ]])
    233  -- Leaving terminal mode restores old values.
    234  feed([[<C-\><C-N>]])
    235  screen:expect([[
    236    tty ready{21: }                                        |
    237     foobar f{21:o}obar foobar foobar foobar foobar foobar |
    238    foobar fo{21:o}bar foobar foobar foobar foobar foobar f|
    239    oobar foo{21:b}ar foobar foobar foobar foobar foobar fo|
    240    obar foob{21:a}r foobar foobar foobar foobar foobar foo|
    241    {21:bar fooba^r                                        }|
    242                                                      |
    243  ]])
    244 
    245  -- CursorLine and CursorColumn are combined with terminal colors.
    246  tt.set_reverse()
    247  tt.feed_data(' foobar')
    248  tt.clear_attrs()
    249  screen:expect([[
    250    tty ready{21: }                                        |
    251     foobar f{21:o}obar foobar foobar foobar foobar foobar |
    252    foobar fo{21:o}bar foobar foobar foobar foobar foobar f|
    253    oobar foo{21:b}ar foobar foobar foobar foobar foobar fo|
    254    obar foob{21:a}r foobar foobar foobar foobar foobar foo|
    255    {21:bar fooba^r}{100: foobar}{21:                                 }|
    256                                                      |
    257  ]])
    258  feed('2gg15|')
    259  screen:expect([[
    260    tty ready     {21: }                                   |
    261    {21: foobar foobar^ foobar foobar foobar foobar foobar }|
    262    foobar foobar {21:f}oobar foobar foobar foobar foobar f|
    263    oobar foobar f{21:o}obar foobar foobar foobar foobar fo|
    264    obar foobar fo{21:o}bar foobar foobar foobar foobar foo|
    265    bar foobar{2: foo}{100:b}{2:ar}                                 |
    266                                                      |
    267  ]])
    268 
    269  -- Set bg color to red
    270  tt.feed_csi('48;2;255:0:0m')
    271  tt.feed_data(' foobar')
    272  tt.clear_attrs()
    273  feed('2gg20|')
    274 
    275  -- Terminal color has higher precedence
    276  screen:expect([[
    277    tty ready          {21: }                              |
    278    {21: foobar foobar foob^ar foobar foobar foobar foobar }|
    279    foobar foobar fooba{21:r} foobar foobar foobar foobar f|
    280    oobar foobar foobar{21: }foobar foobar foobar foobar fo|
    281    obar foobar foobar {21:f}oobar foobar foobar foobar foo|
    282    bar foobar{2: foobar}{30: foobar}                          |
    283                                                      |
    284  ]])
    285  feed('G$')
    286  screen:expect([[
    287    tty ready              {21: }                          |
    288     foobar foobar foobar f{21:o}obar foobar foobar foobar |
    289    foobar foobar foobar fo{21:o}bar foobar foobar foobar f|
    290    oobar foobar foobar foo{21:b}ar foobar foobar foobar fo|
    291    obar foobar foobar foob{21:a}r foobar foobar foobar foo|
    292    {21:bar foobar}{100: foobar}{30: fooba^r}{21:                          }|
    293                                                      |
    294  ]])
    295 end)
    296 
    297 describe(':terminal highlight forwarding', function()
    298  local screen
    299 
    300  before_each(function()
    301    clear()
    302    screen = Screen.new(50, 7)
    303    screen:set_rgb_cterm(true)
    304    screen:set_default_attr_ids({
    305      [1] = { { bold = true }, { bold = true } },
    306      [2] = { { fg_indexed = true, foreground = tonumber('0xe0e000') }, { foreground = 3 } },
    307      [3] = { { foreground = tonumber('0xff8000') }, {} },
    308    })
    309    command(("enew | call jobstart(['%s'], {'term':v:true})"):format(testprg('tty-test')))
    310    feed('i')
    311    screen:expect([[
    312      tty ready                                         |
    313      ^                                                  |
    314                                                        |*4
    315      {1:-- TERMINAL --}                                    |
    316    ]])
    317  end)
    318 
    319  it('will handle cterm and rgb attributes', function()
    320    skip(is_os('win'))
    321    tt.set_fg(3)
    322    tt.feed_data('text')
    323    tt.feed_termcode('[38:2:255:128:0m')
    324    tt.feed_data('color')
    325    tt.clear_attrs()
    326    tt.feed_data('text')
    327    screen:expect([[
    328      tty ready                                         |
    329      {2:text}{3:color}text^                                     |
    330                                                        |*4
    331      {1:-- TERMINAL --}                                    |
    332    ]])
    333  end)
    334 end)
    335 
    336 --- @param buflocal boolean
    337 local function test_term_hl_custom_palette(buflocal)
    338  local screen
    339 
    340  before_each(function()
    341    clear()
    342    screen = Screen.new(50, 7, { rgb = true })
    343    command('enew')
    344    if buflocal then
    345      api.nvim_buf_set_var(0, 'terminal_color_3', '#123456')
    346    else
    347      api.nvim_set_var('terminal_color_3', '#123456')
    348    end
    349    screen:add_extra_attr_ids({ [100] = { foreground = tonumber('0x123456') } })
    350  end)
    351 
    352  it('will use the custom color with jobstart()', function()
    353    command(("call jobstart(['%s'], {'term': v:true})"):format(testprg('tty-test')))
    354    feed('i')
    355    screen:expect([[
    356      tty ready                                         |
    357      ^                                                  |
    358                                                        |*4
    359      {5:-- TERMINAL --}                                    |
    360    ]])
    361    tt.set_fg(3)
    362    tt.feed_data('text')
    363    tt.clear_attrs()
    364    tt.feed_data('text')
    365    screen:expect([[
    366      tty ready                                         |
    367      {100:text}text^                                          |
    368                                                        |*4
    369      {5:-- TERMINAL --}                                    |
    370    ]])
    371  end)
    372 
    373  it('will use the custom color with nvim_open_term() in non-curbuf', function()
    374    local oldbuf = api.nvim_get_current_buf()
    375    command('set laststatus=0 | vnew')
    376    local chan = api.nvim_open_term(oldbuf, {})
    377    api.nvim_chan_send(chan, '\027[38;5;3mtext\027[0;10mtext')
    378    screen:expect([[
    379      ^                         {100:text}text                |
    380      {1:~                        }                        |*5
    381                                                        |
    382    ]])
    383  end)
    384 end
    385 
    386 describe(':terminal highlight with custom palette', function()
    387  describe('using g:termimal_color_*', function()
    388    test_term_hl_custom_palette(false)
    389  end)
    390 
    391  describe('using b:termimal_color_*', function()
    392    test_term_hl_custom_palette(true)
    393  end)
    394 end)
    395 
    396 describe(':terminal', function()
    397  before_each(clear)
    398 
    399  it('can display URLs', function()
    400    local screen = Screen.new(50, 7)
    401    screen:add_extra_attr_ids({ [100] = { url = 'https://example.com' } })
    402    local chan = api.nvim_open_term(0, {})
    403    api.nvim_chan_send(
    404      chan,
    405      'This is an \027]8;;https://example.com\027\\example\027]8;;\027\\ of a link'
    406    )
    407    screen:expect([[
    408      ^This is an {100:example} of a link                      |
    409                                                        |*6
    410    ]])
    411    -- Also works if OSC 8 is split into multiple fragments.
    412    api.nvim_chan_send(chan, '\nThis is another \027]8;;https')
    413    n.poke_eventloop()
    414    api.nvim_chan_send(chan, '://')
    415    n.poke_eventloop()
    416    api.nvim_chan_send(chan, 'example')
    417    n.poke_eventloop()
    418    api.nvim_chan_send(chan, '.com\027\\EXAMPLE\027]8;;\027\\ of a link')
    419    screen:expect([[
    420      ^This is an {100:example} of a link                      |
    421      This is another {100:EXAMPLE} of a link                 |
    422                                                        |*5
    423    ]])
    424  end)
    425 
    426  it('zoomout with large horizontal output #30374', function()
    427    skip(is_os('win'))
    428 
    429    -- Start terminal smaller.
    430    local screen = Screen.new(50, 50, { rgb = false })
    431    feed([[:terminal<cr>]])
    432 
    433    -- Generate very wide output.
    434    feed('ifor i in $(seq 1 10000); do echo -n $i; done\r\n')
    435 
    436    -- Make terminal big.
    437    screen:try_resize(5000, 5000)
    438    command('call jobresize(b:terminal_job_id, 5000, 5000)')
    439 
    440    assert_alive()
    441  end)
    442 end)