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)