neovim

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

hlstate_spec.lua (19891B)


      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 clear, insert = n.clear, n.insert
      7 local command = n.command
      8 local api = n.api
      9 local testprg = n.testprg
     10 local skip = t.skip
     11 local is_os = t.is_os
     12 
     13 describe('ext_hlstate detailed highlights', function()
     14  local screen
     15 
     16  before_each(function()
     17    clear()
     18    command('syntax on')
     19    command('hi VertSplit gui=reverse')
     20    screen = Screen.new(40, 8, { ext_hlstate = true })
     21  end)
     22 
     23  it('work with combined UI and syntax highlights', function()
     24    insert([[
     25      these are some lines
     26      with colorful text]])
     27    api.nvim_buf_add_highlight(0, -1, 'String', 0, 10, 14)
     28    api.nvim_buf_add_highlight(0, -1, 'Statement', 1, 5, -1)
     29    command('/th co')
     30 
     31    screen:expect {
     32      grid = [[
     33      these are {1:some} lines                    |
     34      ^wi{2:th }{4:co}{3:lorful text}                      |
     35      {5:~                                       }|*5
     36      {8:search hit BOTTOM, continuing at TOP}{6:    }|
     37    ]],
     38      attr_ids = {
     39        [1] = {
     40          { foreground = Screen.colors.Magenta1 },
     41          { { kind = 'syntax', hi_name = 'Constant' } },
     42        },
     43        [2] = {
     44          { background = Screen.colors.Yellow1 },
     45          { { kind = 'ui', ui_name = 'Search', hi_name = 'Search' } },
     46        },
     47        [3] = {
     48          { foreground = Screen.colors.Brown, bold = true },
     49          { { kind = 'syntax', hi_name = 'Statement' } },
     50        },
     51        [4] = {
     52          { background = Screen.colors.Yellow1, bold = true, foreground = Screen.colors.Brown },
     53          { 3, 2 },
     54        },
     55        [5] = {
     56          { foreground = Screen.colors.Blue, bold = true },
     57          { { kind = 'ui', ui_name = 'EndOfBuffer', hi_name = 'NonText' } },
     58        },
     59        [6] = { {}, { { kind = 'ui', ui_name = 'MsgArea', hi_name = 'MsgArea' } } },
     60        [7] = {
     61          { foreground = Screen.colors.Red1 },
     62          { { kind = 'syntax', hi_name = 'WarningMsg' } },
     63        },
     64        [8] = { { foreground = Screen.colors.Red1 }, { 6, 7 } },
     65      },
     66    }
     67  end)
     68 
     69  it('work with cleared UI highlights', function()
     70    screen:set_default_attr_ids({
     71      [1] = { {}, { { hi_name = 'Normal', ui_name = 'WinSeparator', kind = 'ui' } } },
     72      [2] = {
     73        { bold = true, foreground = Screen.colors.Blue1 },
     74        { { hi_name = 'NonText', ui_name = 'EndOfBuffer', kind = 'ui' } },
     75      },
     76      [3] = {
     77        { bold = true, reverse = true },
     78        { { hi_name = 'StatusLine', ui_name = 'StatusLine', kind = 'ui' } },
     79      },
     80      [4] = {
     81        { reverse = true },
     82        { { hi_name = 'StatusLineNC', ui_name = 'StatusLineNC', kind = 'ui' } },
     83      },
     84      [5] = { {}, { { hi_name = 'StatusLine', ui_name = 'StatusLine', kind = 'ui' } } },
     85      [6] = { {}, { { hi_name = 'StatusLineNC', ui_name = 'StatusLineNC', kind = 'ui' } } },
     86      [7] = { {}, { { hi_name = 'MsgArea', ui_name = 'MsgArea', kind = 'ui' } } },
     87    })
     88    command('hi clear WinSeparator')
     89    command('vsplit')
     90 
     91    screen:expect([[
     92      ^                    {1:}                   |
     93      {2:~                   }{1:}{2:~                  }|*5
     94      {3:[No Name]            }{4:[No Name]          }|
     95      {7:                                        }|
     96    ]])
     97 
     98    command('hi clear StatusLine | hi clear StatuslineNC')
     99    screen:expect([[
    100      ^                    {1:}                   |
    101      {2:~                   }{1:}{2:~                  }|*5
    102      {5:[No Name]            }{6:[No Name]          }|
    103      {7:                                        }|
    104    ]])
    105 
    106    -- redrawing is done even if visible highlights didn't change
    107    command('wincmd w')
    108    screen:expect([[
    109                         {1:}^                    |
    110      {2:~                  }{1:}{2:~                   }|*5
    111      {6:[No Name]           }{5:[No Name]           }|
    112      {7:                                        }|
    113    ]])
    114  end)
    115 
    116  it('work with window-local highlights', function()
    117    screen:set_default_attr_ids({
    118      [1] = {
    119        { foreground = Screen.colors.Brown },
    120        { { hi_name = 'LineNr', ui_name = 'LineNr', kind = 'ui' } },
    121      },
    122      [2] = {
    123        { bold = true, foreground = Screen.colors.Blue1 },
    124        { { hi_name = 'NonText', ui_name = 'EndOfBuffer', kind = 'ui' } },
    125      },
    126      [3] = {
    127        { bold = true, reverse = true },
    128        { { hi_name = 'StatusLine', ui_name = 'StatusLine', kind = 'ui' } },
    129      },
    130      [4] = {
    131        { reverse = true },
    132        { { hi_name = 'StatusLineNC', ui_name = 'StatusLineNC', kind = 'ui' } },
    133      },
    134      [5] = {
    135        { background = Screen.colors.Red, foreground = Screen.colors.Grey100 },
    136        { { hi_name = 'ErrorMsg', ui_name = 'LineNr', kind = 'ui' } },
    137      },
    138      [6] = {
    139        { bold = true, reverse = true },
    140        { { hi_name = 'Normal', ui_name = 'Normal', kind = 'ui' } },
    141      },
    142      [7] = { { foreground = Screen.colors.Brown, bold = true, reverse = true }, { 6, 1 } },
    143      [8] = { { foreground = Screen.colors.Blue1, bold = true, reverse = true }, { 6, 14 } },
    144      [9] = {
    145        { bold = true, foreground = Screen.colors.Brown },
    146        { { hi_name = 'NormalNC', ui_name = 'NormalNC', kind = 'ui' } },
    147      },
    148      [10] = { { bold = true, foreground = Screen.colors.Brown }, { 9, 1 } },
    149      [11] = { { bold = true, foreground = Screen.colors.Blue1 }, { 9, 14 } },
    150      [12] = { {}, { { hi_name = 'MsgArea', ui_name = 'MsgArea', kind = 'ui' } } },
    151      [13] = {
    152        { background = Screen.colors.Red1, foreground = Screen.colors.Gray100 },
    153        { { ui_name = 'LineNr', kind = 'ui', hi_name = 'LineNr' } },
    154      },
    155      [14] = {
    156        { bold = true, foreground = Screen.colors.Blue },
    157        { { ui_name = 'EndOfBuffer', kind = 'ui', hi_name = 'EndOfBuffer' } },
    158      },
    159    })
    160 
    161    command('set number')
    162    command('split')
    163    -- NormalNC is not applied if not set, to avoid spurious redraws
    164    screen:expect([[
    165      {1:  1 }^                                    |
    166      {2:~                                       }|*2
    167      {3:[No Name]                               }|
    168      {1:  1 }                                    |
    169      {2:~                                       }|
    170      {4:[No Name]                               }|
    171      {12:                                        }|
    172    ]])
    173 
    174    command('set winhl=LineNr:ErrorMsg')
    175    screen:expect {
    176      grid = [[
    177      {13:  1 }^                                    |
    178      {14:~                                       }|*2
    179      {3:[No Name]                               }|
    180      {1:  1 }                                    |
    181      {2:~                                       }|
    182      {4:[No Name]                               }|
    183      {12:                                        }|
    184    ]],
    185    }
    186 
    187    command('set winhl=Normal:MsgSeparator,NormalNC:Statement')
    188    screen:expect([[
    189      {7:  1 }{6:^                                    }|
    190      {8:~                                       }|*2
    191      {3:[No Name]                               }|
    192      {1:  1 }                                    |
    193      {2:~                                       }|
    194      {4:[No Name]                               }|
    195      {12:                                        }|
    196    ]])
    197 
    198    command('wincmd w')
    199    screen:expect([[
    200      {10:  1 }{9:                                    }|
    201      {11:~                                       }|*2
    202      {4:[No Name]                               }|
    203      {1:  1 }^                                    |
    204      {2:~                                       }|
    205      {3:[No Name]                               }|
    206      {12:                                        }|
    207    ]])
    208  end)
    209 
    210  it('work with :terminal', function()
    211    skip(is_os('win'))
    212 
    213    screen:set_default_attr_ids({
    214      [1] = { {}, { { hi_name = 'TermCursorNC', ui_name = 'TermCursorNC', kind = 'ui' } } },
    215      [2] = { { foreground = tonumber('0x00ccff'), fg_indexed = true }, { { kind = 'term' } } },
    216      [3] = {
    217        { bold = true, foreground = tonumber('0x00ccff'), fg_indexed = true },
    218        {
    219          { kind = 'term' },
    220        },
    221      },
    222      [4] = { { foreground = tonumber('0x00ccff'), fg_indexed = true }, { 2, 1 } },
    223      [5] = { { foreground = tonumber('0x40ffff'), fg_indexed = true }, { { kind = 'term' } } },
    224      [6] = { { foreground = tonumber('0x40ffff'), fg_indexed = true }, { 5, 1 } },
    225      [7] = { {}, { { hi_name = 'MsgArea', ui_name = 'MsgArea', kind = 'ui' } } },
    226    })
    227    command(("enew | call jobstart(['%s'],{'term':v:true})"):format(testprg('tty-test')))
    228    screen:expect([[
    229      ^tty ready                               |
    230                                              |*6
    231      {7:                                        }|
    232    ]])
    233 
    234    tt.feed_data('x ')
    235    tt.set_fg(45)
    236    tt.feed_data('y ')
    237    tt.set_bold()
    238    tt.feed_data('z\n')
    239    -- TODO(bfredl): check if this distinction makes sense
    240    if is_os('win') then
    241      screen:expect([[
    242        ^tty ready                               |
    243        x {5:y z}                                   |
    244                                                |*5
    245        {7:                                        }|
    246      ]])
    247    else
    248      screen:expect([[
    249        ^tty ready                               |
    250        x {2:y }{3:z}                                   |
    251                                                |*5
    252        {7:                                        }|
    253      ]])
    254    end
    255 
    256    tt.feed_termcode('[A')
    257    tt.feed_termcode('[2C')
    258    if is_os('win') then
    259      screen:expect([[
    260        ^tty ready                               |
    261        x {6:y}{5: z}                                   |
    262                                                |*5
    263        {7:                                        }|
    264      ]])
    265    else
    266      screen:expect([[
    267        ^tty ready                               |
    268        x {2:y }{3:z}                                   |
    269                                                |*5
    270        {7:                                        }|
    271      ]])
    272    end
    273  end)
    274 
    275  it('can use independent cterm and rgb colors', function()
    276    -- tell test module to save all attributes (doesn't change nvim options)
    277    screen:set_rgb_cterm(true)
    278 
    279    screen:set_default_attr_ids({
    280      [1] = {
    281        { bold = true, foreground = Screen.colors.Blue1 },
    282        { foreground = 12 },
    283        { { hi_name = 'NonText', ui_name = 'EndOfBuffer', kind = 'ui' } },
    284      },
    285      [2] = {
    286        { reverse = true, foreground = Screen.colors.Red },
    287        { foreground = 10, italic = true },
    288        { { hi_name = 'NonText', ui_name = 'EndOfBuffer', kind = 'ui' } },
    289      },
    290      [3] = { {}, {}, { { hi_name = 'MsgArea', ui_name = 'MsgArea', kind = 'ui' } } },
    291    })
    292    screen:expect([[
    293      ^                                        |
    294      {1:~                                       }|*6
    295      {3:                                        }|
    296    ]])
    297 
    298    command('hi NonText guifg=Red gui=reverse ctermfg=Green cterm=italic')
    299    screen:expect([[
    300      ^                                        |
    301      {2:~                                       }|*6
    302      {3:                                        }|
    303    ]])
    304  end)
    305 
    306  it('combines deleted extmark highlights', function()
    307    insert([[
    308      line1
    309        line2
    310        line3
    311        line4
    312        line5
    313      line6]])
    314 
    315    screen:expect {
    316      grid = [[
    317      line1                                   |
    318        line2                                 |
    319        line3                                 |
    320        line4                                 |
    321        line5                                 |
    322      line^6                                   |
    323      {1:~                                       }|
    324      {2:                                        }|
    325    ]],
    326      attr_ids = {
    327        [1] = {
    328          { foreground = Screen.colors.Blue, bold = true },
    329          { { ui_name = 'EndOfBuffer', hi_name = 'NonText', kind = 'ui' } },
    330        },
    331        [2] = { {}, { { ui_name = 'MsgArea', hi_name = 'MsgArea', kind = 'ui' } } },
    332      },
    333    }
    334 
    335    local ns = api.nvim_create_namespace('test')
    336 
    337    local add_indicator = function(line, col)
    338      api.nvim_buf_set_extmark(0, ns, line, col, {
    339        hl_mode = 'combine',
    340        priority = 2,
    341        right_gravity = false,
    342        virt_text = { { '|', 'Delimiter' } },
    343        virt_text_win_col = 0,
    344        virt_text_pos = 'overlay',
    345      })
    346    end
    347 
    348    add_indicator(1, 0)
    349    add_indicator(2, 0)
    350    add_indicator(3, 0)
    351    add_indicator(4, 0)
    352 
    353    screen:expect {
    354      grid = [[
    355      line1                                   |
    356      {1:|} line2                                 |
    357      {1:|} line3                                 |
    358      {1:|} line4                                 |
    359      {1:|} line5                                 |
    360      line^6                                   |
    361      {2:~                                       }|
    362      {3:                                        }|
    363    ]],
    364      attr_ids = {
    365        [1] = {
    366          { foreground = Screen.colors.SlateBlue },
    367          { { hi_name = 'Special', kind = 'syntax' } },
    368        },
    369        [2] = {
    370          { bold = true, foreground = Screen.colors.Blue },
    371          { { ui_name = 'EndOfBuffer', kind = 'ui', hi_name = 'NonText' } },
    372        },
    373        [3] = { {}, { { ui_name = 'MsgArea', kind = 'ui', hi_name = 'MsgArea' } } },
    374      },
    375    }
    376 
    377    n.feed('3ggV2jd')
    378    --screen:redraw_debug()
    379    screen:expect {
    380      grid = [[
    381      line1                                   |
    382      {1:|} line2                                 |
    383      {2:^|}ine6                                   |
    384      {3:~                                       }|*4
    385      {4:3 fewer lines                           }|
    386    ]],
    387      attr_ids = {
    388        [1] = {
    389          { foreground = Screen.colors.SlateBlue },
    390          { { kind = 'syntax', hi_name = 'Special' } },
    391        },
    392        [2] = { { foreground = Screen.colors.SlateBlue }, { 1, 1, 1 } },
    393        [3] = {
    394          { bold = true, foreground = Screen.colors.Blue },
    395          { { kind = 'ui', ui_name = 'EndOfBuffer', hi_name = 'NonText' } },
    396        },
    397        [4] = { {}, { { kind = 'ui', ui_name = 'MsgArea', hi_name = 'MsgArea' } } },
    398      },
    399    }
    400  end)
    401 
    402  it('removes deleted extmark highlights with invalidate', function()
    403    insert([[
    404      line1
    405        line2
    406        line3
    407        line4
    408        line5
    409      line6]])
    410 
    411    screen:expect {
    412      grid = [[
    413      line1                                   |
    414        line2                                 |
    415        line3                                 |
    416        line4                                 |
    417        line5                                 |
    418      line^6                                   |
    419      {1:~                                       }|
    420      {2:                                        }|
    421    ]],
    422      attr_ids = {
    423        [1] = {
    424          { foreground = Screen.colors.Blue, bold = true },
    425          { { ui_name = 'EndOfBuffer', hi_name = 'NonText', kind = 'ui' } },
    426        },
    427        [2] = { {}, { { ui_name = 'MsgArea', hi_name = 'MsgArea', kind = 'ui' } } },
    428      },
    429    }
    430 
    431    local ns = api.nvim_create_namespace('test')
    432 
    433    local add_indicator = function(line, col)
    434      api.nvim_buf_set_extmark(0, ns, line, col, {
    435        hl_mode = 'combine',
    436        priority = 2,
    437        right_gravity = false,
    438        virt_text = { { '|', 'Delimiter' } },
    439        virt_text_win_col = 0,
    440        virt_text_pos = 'overlay',
    441        invalidate = true,
    442      })
    443    end
    444 
    445    add_indicator(1, 0)
    446    add_indicator(2, 0)
    447    add_indicator(3, 0)
    448    add_indicator(4, 0)
    449 
    450    screen:expect {
    451      grid = [[
    452      line1                                   |
    453      {1:|} line2                                 |
    454      {1:|} line3                                 |
    455      {1:|} line4                                 |
    456      {1:|} line5                                 |
    457      line^6                                   |
    458      {2:~                                       }|
    459      {3:                                        }|
    460    ]],
    461      attr_ids = {
    462        [1] = {
    463          { foreground = Screen.colors.SlateBlue },
    464          { { hi_name = 'Special', kind = 'syntax' } },
    465        },
    466        [2] = {
    467          { bold = true, foreground = Screen.colors.Blue },
    468          { { ui_name = 'EndOfBuffer', kind = 'ui', hi_name = 'NonText' } },
    469        },
    470        [3] = { {}, { { ui_name = 'MsgArea', kind = 'ui', hi_name = 'MsgArea' } } },
    471      },
    472    }
    473 
    474    n.feed('3ggV2jd')
    475    --screen:redraw_debug()
    476    screen:expect {
    477      grid = [[
    478      line1                                   |
    479      {1:|} line2                                 |
    480      ^line6                                   |
    481      {2:~                                       }|*4
    482      {3:3 fewer lines                           }|
    483    ]],
    484      attr_ids = {
    485        [1] = {
    486          { foreground = Screen.colors.SlateBlue },
    487          { { kind = 'syntax', hi_name = 'Special' } },
    488        },
    489        [2] = {
    490          { foreground = Screen.colors.Blue, bold = true },
    491          { { kind = 'ui', ui_name = 'EndOfBuffer', hi_name = 'NonText' } },
    492        },
    493        [3] = { {}, { { kind = 'ui', ui_name = 'MsgArea', hi_name = 'MsgArea' } } },
    494      },
    495    }
    496  end)
    497 
    498  it('does not hang when combining too many highlights', function()
    499    local num_lines = 500
    500    insert('first line\n')
    501    for _ = 1, num_lines do
    502      api.nvim_paste('  line\n', false, -1)
    503    end
    504    insert('last line')
    505 
    506    n.feed('gg')
    507    screen:expect {
    508      grid = [[
    509      ^first line                              |
    510        line                                  |*6
    511      {1:                                        }|
    512    ]],
    513      attr_ids = {
    514        [1] = { {}, { { kind = 'ui', hi_name = 'MsgArea', ui_name = 'MsgArea' } } },
    515      },
    516    }
    517    local ns = api.nvim_create_namespace('test')
    518 
    519    local add_indicator = function(line, col)
    520      api.nvim_buf_set_extmark(0, ns, line, col, {
    521        hl_mode = 'combine',
    522        priority = 2,
    523        right_gravity = false,
    524        virt_text = { { '|', 'Delimiter' } },
    525        virt_text_win_col = 0,
    526        virt_text_pos = 'overlay',
    527      })
    528    end
    529 
    530    for i = 1, num_lines do
    531      add_indicator(i, 0)
    532    end
    533 
    534    screen:expect {
    535      grid = [[
    536      ^first line                              |
    537      {1:|} line                                  |*6
    538      {2:                                        }|
    539    ]],
    540      attr_ids = {
    541        [1] = {
    542          { foreground = Screen.colors.SlateBlue },
    543          { { kind = 'syntax', hi_name = 'Special' } },
    544        },
    545        [2] = { {}, { { kind = 'ui', ui_name = 'MsgArea', hi_name = 'MsgArea' } } },
    546      },
    547    }
    548 
    549    n.feed(string.format('3ggV%ijd', num_lines - 2))
    550    --screen:redraw_debug(nil, nil, 100000)
    551 
    552    local expected_ids = {}
    553    for i = 1, num_lines - 1 do
    554      expected_ids[i] = 1
    555    end
    556    screen:expect {
    557      grid = string.format(
    558        [[
    559        first line                              |
    560        {1:|} line                                  |
    561        {2:^|}ast line                               |
    562        {3:~                                       }|*4
    563        {4:%-40s}|
    564    ]],
    565        tostring(num_lines - 1) .. ' fewer lines'
    566      ),
    567      attr_ids = {
    568        [1] = {
    569          { foreground = Screen.colors.SlateBlue },
    570          { { kind = 'syntax', hi_name = 'Special' } },
    571        },
    572        [2] = { { foreground = Screen.colors.SlateBlue }, expected_ids },
    573        [3] = {
    574          { foreground = Screen.colors.Blue, bold = true },
    575          { { kind = 'ui', hi_name = 'NonText', ui_name = 'EndOfBuffer' } },
    576        },
    577        [4] = { {}, { { kind = 'ui', hi_name = 'MsgArea', ui_name = 'MsgArea' } } },
    578      },
    579      timeout = 100000,
    580    }
    581  end)
    582 end)