neovim

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

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)