mark_spec.lua (12825B)
1 local t = require('test.testutil') 2 local n = require('test.functional.testnvim')() 3 local Screen = require('test.functional.ui.screen') 4 5 local api = n.api 6 local clear = n.clear 7 local command = n.command 8 local fn = n.fn 9 local eq = t.eq 10 local feed = n.feed 11 local write_file = t.write_file 12 local pcall_err = t.pcall_err 13 local cursor = function() 14 return n.api.nvim_win_get_cursor(0) 15 end 16 17 describe('named marks', function() 18 local file1 = 'Xtestfile-functional-editor-marks' 19 local file2 = 'Xtestfile-functional-editor-marks-2' 20 before_each(function() 21 clear() 22 write_file(file1, '1test1\n1test2\n1test3\n1test4', false, false) 23 write_file(file2, '2test1\n2test2\n2test3\n2test4', false, false) 24 end) 25 after_each(function() 26 os.remove(file1) 27 os.remove(file2) 28 end) 29 30 it('can be set', function() 31 command('edit ' .. file1) 32 command('mark a') 33 eq({ 1, 0 }, api.nvim_buf_get_mark(0, 'a')) 34 feed('jmb') 35 eq({ 2, 0 }, api.nvim_buf_get_mark(0, 'b')) 36 feed('jmB') 37 eq({ 3, 0 }, api.nvim_buf_get_mark(0, 'B')) 38 command('4kc') 39 eq({ 4, 0 }, api.nvim_buf_get_mark(0, 'c')) 40 end) 41 42 it('errors when set out of range with :mark', function() 43 command('edit ' .. file1) 44 local err = pcall_err(n.exec_capture, '1000mark x') 45 eq('nvim_exec2(), line 1: Vim(mark):E16: Invalid range: 1000mark x', err) 46 end) 47 48 it('errors when set out of range with :k', function() 49 command('edit ' .. file1) 50 local err = pcall_err(n.exec_capture, '1000kx') 51 eq('nvim_exec2(), line 1: Vim(k):E16: Invalid range: 1000kx', err) 52 end) 53 54 it('errors on unknown mark name with :mark', function() 55 command('edit ' .. file1) 56 local err = pcall_err(n.exec_capture, 'mark #') 57 eq( 58 'nvim_exec2(), line 1: Vim(mark):E191: Argument must be a letter or forward/backward quote', 59 err 60 ) 61 end) 62 63 it("errors on unknown mark name with '", function() 64 command('edit ' .. file1) 65 local err = pcall_err(n.exec_capture, "normal! '#") 66 eq('nvim_exec2(), line 1: Vim(normal):E78: Unknown mark', err) 67 end) 68 69 it('errors on unknown mark name with `', function() 70 command('edit ' .. file1) 71 local err = pcall_err(n.exec_capture, 'normal! `#') 72 eq('nvim_exec2(), line 1: Vim(normal):E78: Unknown mark', err) 73 end) 74 75 it("errors when moving to a mark that is not set with '", function() 76 command('edit ' .. file1) 77 local err = pcall_err(n.exec_capture, "normal! 'z") 78 eq('nvim_exec2(), line 1: Vim(normal):E20: Mark not set', err) 79 err = pcall_err(n.exec_capture, "normal! '.") 80 eq('nvim_exec2(), line 1: Vim(normal):E20: Mark not set', err) 81 end) 82 83 it('errors when moving to a mark that is not set with `', function() 84 command('edit ' .. file1) 85 local err = pcall_err(n.exec_capture, 'normal! `z') 86 eq('nvim_exec2(), line 1: Vim(normal):E20: Mark not set', err) 87 err = pcall_err(n.exec_capture, 'normal! `>') 88 eq('nvim_exec2(), line 1: Vim(normal):E20: Mark not set', err) 89 end) 90 91 it("errors when moving to a global mark that is not set with '", function() 92 command('edit ' .. file1) 93 local err = pcall_err(n.exec_capture, "normal! 'Z") 94 eq('nvim_exec2(), line 1: Vim(normal):E20: Mark not set', err) 95 end) 96 97 it('errors when moving to a global mark that is not set with `', function() 98 command('edit ' .. file1) 99 local err = pcall_err(n.exec_capture, 'normal! `Z') 100 eq('nvim_exec2(), line 1: Vim(normal):E20: Mark not set', err) 101 end) 102 103 it("can move to them using '", function() 104 command('args ' .. file1 .. ' ' .. file2) 105 feed('j') 106 feed('ma') 107 feed("G'a") 108 eq({ 2, 0 }, cursor()) 109 feed('mA') 110 command('next') 111 feed("'A") 112 eq(1, api.nvim_get_current_buf()) 113 eq({ 2, 0 }, cursor()) 114 end) 115 116 it('can move to them using `', function() 117 command('args ' .. file1 .. ' ' .. file2) 118 feed('jll') 119 feed('ma') 120 feed('G`a') 121 eq({ 2, 2 }, cursor()) 122 feed('mA') 123 command('next') 124 feed('`A') 125 eq(1, api.nvim_get_current_buf()) 126 eq({ 2, 2 }, cursor()) 127 end) 128 129 it("can move to them using g'", function() 130 command('args ' .. file1 .. ' ' .. file2) 131 feed('jll') 132 feed('ma') 133 feed("Gg'a") 134 eq({ 2, 0 }, cursor()) 135 feed('mA') 136 command('next') 137 feed("g'A") 138 eq(1, api.nvim_get_current_buf()) 139 eq({ 2, 0 }, cursor()) 140 end) 141 142 it('can move to them using g`', function() 143 command('args ' .. file1 .. ' ' .. file2) 144 feed('jll') 145 feed('ma') 146 feed('Gg`a') 147 eq({ 2, 2 }, cursor()) 148 feed('mA') 149 command('next') 150 feed('g`A') 151 eq(1, api.nvim_get_current_buf()) 152 eq({ 2, 2 }, cursor()) 153 end) 154 155 it("can move to them using :'", function() 156 command('args ' .. file1 .. ' ' .. file2) 157 feed('j') 158 feed('ma') 159 feed('G') 160 command("'a") 161 eq({ 2, 0 }, cursor()) 162 feed('mA') 163 command('next') 164 command("'A") 165 eq(1, api.nvim_get_current_buf()) 166 eq({ 2, 0 }, cursor()) 167 end) 168 169 it("errors when it can't find the buffer", function() 170 command('args ' .. file1 .. ' ' .. file2) 171 feed('mA') 172 command('next') 173 command('bw! ' .. file1) 174 local err = pcall_err(n.exec_capture, "normal! 'A") 175 eq('nvim_exec2(), line 1: Vim(normal):E92: Buffer 1 not found', err) 176 os.remove(file1) 177 end) 178 179 it('errors when using a mark in another buffer in command range', function() 180 feed('ifoo<Esc>mA') 181 command('enew') 182 feed('ibar<Esc>') 183 eq("Vim(print):E20: Mark not set: 'Aprint", pcall_err(command, [['Aprint]])) 184 end) 185 186 it("leave a context mark when moving with '", function() 187 command('edit ' .. file1) 188 feed('llmamA') 189 feed('10j0') -- first col, last line 190 local pos = cursor() 191 feed("'a") 192 feed('<C-o>') 193 eq(pos, cursor()) 194 feed("'A") 195 feed('<C-o>') 196 eq(pos, cursor()) 197 end) 198 199 it('leave a context mark when moving with `', function() 200 command('edit ' .. file1) 201 feed('llmamA') 202 feed('10j0') -- first col, last line 203 local pos = cursor() 204 feed('`a') 205 feed('<C-o>') 206 eq(pos, cursor()) 207 feed('`A') 208 feed('<C-o>') 209 eq(pos, cursor()) 210 end) 211 212 it("leave a context mark when the mark changes buffer with g'", function() 213 command('args ' .. file1 .. ' ' .. file2) 214 local pos 215 feed('GmA') 216 command('next') 217 pos = cursor() 218 command('clearjumps') 219 feed("g'A") -- since the mark is in another buffer, it leaves a context mark 220 feed('<C-o>') 221 eq(pos, cursor()) 222 end) 223 224 it('leave a context mark when the mark changes buffer with g`', function() 225 command('args ' .. file1 .. ' ' .. file2) 226 local pos 227 feed('GmA') 228 command('next') 229 pos = cursor() 230 command('clearjumps') 231 feed('g`A') -- since the mark is in another buffer, it leaves a context mark 232 feed('<C-o>') 233 eq(pos, cursor()) 234 end) 235 236 it("do not leave a context mark when moving with g'", function() 237 command('edit ' .. file1) 238 local pos 239 feed('ma') 240 pos = cursor() -- Mark pos 241 feed('10j0') -- first col, last line 242 feed("g'a") 243 feed('<C-o>') -- should do nothing 244 eq(pos, cursor()) 245 feed('mA') 246 pos = cursor() -- Mark pos 247 feed('10j0') -- first col, last line 248 feed("g'a") 249 feed('<C-o>') -- should do nothing 250 eq(pos, cursor()) 251 end) 252 253 it('do not leave a context mark when moving with g`', function() 254 command('edit ' .. file1) 255 local pos 256 feed('ma') 257 pos = cursor() -- Mark pos 258 feed('10j0') -- first col, last line 259 feed('g`a') 260 feed('<C-o>') -- should do nothing 261 eq(pos, cursor()) 262 feed('mA') 263 pos = cursor() -- Mark pos 264 feed('10j0') -- first col, last line 265 feed("g'a") 266 feed('<C-o>') -- should do nothing 267 eq(pos, cursor()) 268 end) 269 270 it('open folds when moving to them', function() 271 command('edit ' .. file1) 272 feed('jzfG') -- Fold from the second line to the end 273 command('3mark a') 274 feed('G') -- On top of the fold 275 assert(fn.foldclosed('.') ~= -1) -- folded 276 feed("'a") 277 eq(-1, fn.foldclosed('.')) 278 279 feed('zc') 280 assert(fn.foldclosed('.') ~= -1) -- folded 281 -- TODO: remove this workaround after fixing #15873 282 feed('k`a') 283 eq(-1, fn.foldclosed('.')) 284 285 feed('zc') 286 assert(fn.foldclosed('.') ~= -1) -- folded 287 feed("kg'a") 288 eq(-1, fn.foldclosed('.')) 289 290 feed('zc') 291 assert(fn.foldclosed('.') ~= -1) -- folded 292 feed('kg`a') 293 eq(-1, fn.foldclosed('.')) 294 end) 295 296 it("do not open folds when moving to them doesn't move the cursor", function() 297 command('edit ' .. file1) 298 feed('jzfG') -- Fold from the second line to the end 299 assert(fn.foldclosed('.') == 2) -- folded 300 feed('ma') 301 feed("'a") 302 feed('`a') 303 feed("g'a") 304 feed('g`a') 305 -- should still be folded 306 eq(2, fn.foldclosed('.')) 307 end) 308 309 it("getting '{ '} '( ') does not move cursor", function() 310 api.nvim_buf_set_lines(0, 0, 0, true, { 'aaaaa', 'bbbbb', 'ccccc', 'ddddd', 'eeeee' }) 311 api.nvim_win_set_cursor(0, { 2, 0 }) 312 fn.getpos("'{") 313 eq({ 2, 0 }, api.nvim_win_get_cursor(0)) 314 fn.getpos("'}") 315 eq({ 2, 0 }, api.nvim_win_get_cursor(0)) 316 fn.getpos("'(") 317 eq({ 2, 0 }, api.nvim_win_get_cursor(0)) 318 fn.getpos("')") 319 eq({ 2, 0 }, api.nvim_win_get_cursor(0)) 320 end) 321 322 it('in command range does not move cursor #19248', function() 323 api.nvim_create_user_command('Test', ':', { range = true }) 324 api.nvim_buf_set_lines(0, 0, 0, true, { 'aaaaa', 'bbbbb', 'ccccc', 'ddddd', 'eeeee' }) 325 api.nvim_win_set_cursor(0, { 2, 0 }) 326 command([['{,'}Test]]) 327 eq({ 2, 0 }, api.nvim_win_get_cursor(0)) 328 end) 329 end) 330 331 describe('named marks view', function() 332 local file1 = 'Xtestfile-functional-editor-marks' 333 local file2 = 'Xtestfile-functional-editor-marks-2' 334 local function content() 335 local c = {} 336 for i = 1, 30 do 337 c[i] = i .. ' line' 338 end 339 return table.concat(c, '\n') 340 end 341 before_each(function() 342 clear() 343 write_file(file1, content(), false, false) 344 write_file(file2, content(), false, false) 345 command('set jumpoptions+=view') 346 end) 347 after_each(function() 348 os.remove(file1) 349 os.remove(file2) 350 end) 351 352 it('is restored in normal mode but not op-pending mode', function() 353 local screen = Screen.new(5, 8) 354 command('edit ' .. file1) 355 feed('<C-e>jWma') 356 feed("G'a") 357 local expected = [[ 358 2 line | 359 ^3 line | 360 4 line | 361 5 line | 362 6 line | 363 7 line | 364 8 line | 365 | 366 ]] 367 screen:expect({ grid = expected }) 368 feed('G`a') 369 screen:expect([[ 370 2 line | 371 3 ^line | 372 4 line | 373 5 line | 374 6 line | 375 7 line | 376 8 line | 377 | 378 ]]) 379 -- not in op-pending mode #20886 380 feed('ggj=`a') 381 screen:expect([[ 382 1 line | 383 ^2 line | 384 3 line | 385 4 line | 386 5 line | 387 6 line | 388 7 line | 389 | 390 ]]) 391 end) 392 393 it('is restored across files', function() 394 local screen = Screen.new(5, 5) 395 command('args ' .. file1 .. ' ' .. file2) 396 feed('<C-e>mA') 397 local mark_view = [[ 398 ^2 line | 399 3 line | 400 4 line | 401 5 line | 402 | 403 ]] 404 screen:expect(mark_view) 405 command('next') 406 screen:expect([[ 407 ^1 line | 408 2 line | 409 3 line | 410 4 line | 411 | 412 ]]) 413 feed("'A") 414 screen:expect(mark_view) 415 end) 416 417 it("fallback to standard behavior when view can't be recovered", function() 418 local screen = Screen.new(10, 10) 419 command('edit ' .. file1) 420 feed('7GzbmaG') -- Seven lines from the top 421 command('new') -- Screen size for window is now half the height can't be restored 422 feed("<C-w>p'a") 423 screen:expect([[ 424 | 425 {1:~ }|*3 426 {2:[No Name] }| 427 6 line | 428 ^7 line | 429 8 line | 430 {3:<itor-marks }| 431 | 432 ]]) 433 end) 434 435 it('fallback to standard behavior when mark is loaded from shada', function() 436 local screen = Screen.new(10, 6) 437 command('edit ' .. file1) 438 feed('G') 439 feed('mA') 440 screen:expect([[ 441 26 line | 442 27 line | 443 28 line | 444 29 line | 445 ^30 line | 446 | 447 ]]) 448 command('set shadafile=Xtestfile-functional-editor-marks-shada') 449 finally(function() 450 command('set shadafile=NONE') 451 os.remove('Xtestfile-functional-editor-marks-shada') 452 end) 453 command('wshada!') 454 command('bwipe!') 455 screen:expect([[ 456 ^ | 457 {1:~ }|*4 458 | 459 ]]) 460 command('rshada!') 461 command('edit ' .. file1) 462 feed('`"') 463 screen:expect([[ 464 26 line | 465 27 line | 466 28 line | 467 29 line | 468 ^30 line | 469 | 470 ]]) 471 feed('`A') 472 screen:expect_unchanged() 473 end) 474 end)