Luigit
repositories / dotfiles

dotfiles

bugabingas dorkfiles

owned by admin

neovim/tests/bugabinga/pivi/editor/navigate_spec.lua

Raw
---@diagnostic disable: duplicate-set-field, need-check-nil, param-type-mismatch, assign-type-mismatch, redundant-parameter
local navigate = require 'bugabinga.pivi.navigate'
local entry = require 'bugabinga.pivi.entry'
local transcript = require 'bugabinga.pivi.transcript'
local follow = require 'bugabinga.pivi.follow'
local fake = dofile 'tests/helpers/pivi_fake.lua'

local temp_file = function( lines )
  local path = vim.fs.normalize( vim.fn.tempname() .. '.txt' )
  vim.fn.writefile( lines, path )
  return path
end

local block = function( bufnr, kind, meta, lines )
  local id = entry.open( bufnr, kind, meta )
  for _, text in ipairs( lines ) do entry.line( bufnr, id, text ) end
  entry.close( bufnr, id )
  return id
end

describe( 'pivi navigate', function()
  local bufnr, win

  before_each( function()
    bufnr = transcript.ensure 'moving'
    transcript.clear( bufnr )

    block( bufnr, 'prompt', { text = 'ask', }, { 'ask', } )
    block( bufnr, 'tool', { tool = 'read', }, { 'read a.lua', 'one', } )
    block( bufnr, 'answer', {}, { 'reply', } )
    block( bufnr, 'tool', { tool = 'edit', }, { 'edit a.lua', } )

    win = transcript.show( bufnr )
    vim.api.nvim_set_current_win( win )
  end )

  after_each( function()
    follow.reset()
    transcript.forget_all()
    fake.close_extra_windows()
  end )

  describe( 'locations', function()
    it( 'reads the place an entry names', function()
      local located = navigate.location_of {
        kind = 'tool',
        meta = { args = { path = 'src/main.lua', line = 12, }, cwd = '/work', },
      }

      assert.equal( '/work/src/main.lua', located.path )
      assert.equal( 12, located.line )
    end )

    it( 'keeps an absolute path as it is', function()
      local located = navigate.location_of {
        kind = 'tool',
        meta = { args = { path = '/elsewhere/a.lua', }, cwd = '/work', },
      }

      assert.equal( '/elsewhere/a.lua', located.path )
    end )

    it( 'reaches the first changed line of an edit', function()
      local located = navigate.location_of {
        kind = 'tool',
        meta = {
          tool = 'edit',
          args = { path = 'a.lua', },
          result = { details = { diff = '-7 old\n+7 new', firstChangedLine = 7, }, },
        },
      }

      assert.equal( 7, located.line )
    end )

    it( 'prefers the reported change over an argument line', function()
      local located = navigate.location_of {
        kind = 'tool',
        meta = {
          tool = 'edit',
          args = { path = 'a.lua', line = 1, },
          result = { details = { firstChangedLine = 12, }, },
        },
      }

      assert.equal( 12, located.line )
    end )

    it( 'keeps the argument line when no change was reported', function()
      local located = navigate.location_of {
        kind = 'tool',
        meta = { tool = 'read', args = { path = 'a.lua', line = 4, }, },
      }

      assert.equal( 4, located.line )
    end )

    it( 'lands on an edit at the line it changed', function()
      local path = temp_file { 'one', 'two', 'three', 'four', 'five', }
      block( bufnr, 'tool', {
        tool = 'edit',
        args = { path = path, },
        result = { details = { diff = '-4 four\n+4 FOUR', firstChangedLine = 4, }, },
      }, { 'edit', '-4 four', '+4 FOUR', } )

      local listed = entry.list( bufnr )
      local target = navigate.locate( bufnr, listed[#listed].first )

      assert.is_number( target )
      assert.equal( 4, vim.api.nvim_win_get_cursor( target )[1] )
    end )

    it( 'reaches the full output of a truncated result', function()
      local located = navigate.location_of {
        kind = 'tool',
        meta = {
          tool = 'bash',
          args = { command = 'ls -R', },
          result = { details = { truncation = { truncated = true, }, fullOutputPath = '/tmp/pi-bash-abc.log', }, },
        },
      }

      assert.equal( '/tmp/pi-bash-abc.log', located.path )
    end )

    it( 'prefers what a tool acted on over where its output spilled', function()
      local located = navigate.location_of {
        kind = 'tool',
        meta = {
          tool = 'read',
          args = { path = '/work/a.lua', },
          result = { details = { fullOutputPath = '/tmp/spill.log', }, },
        },
      }

      assert.equal( '/work/a.lua', located.path )
    end )

    it( 'reports no place for an entry that names none', function()
      assert.is_nil( navigate.location_of { kind = 'answer', meta = {}, } )
      assert.is_nil( navigate.location_of( nil ) )
    end )

    it( 'reaches the place an entry names without taking the working window', function()
      local path = temp_file { 'one', 'two', 'three', }
      block( bufnr, 'tool', { tool = 'read', args = { path = path, line = 3, }, }, { 'read', } )

      local listed = entry.list( bufnr )
      local tool = listed[#listed]
      vim.api.nvim_win_set_cursor( win, { tool.first, 0, } )

      local target = navigate.locate( bufnr, tool.first )

      assert.is_number( target )
      assert.is_not.equal( win, target )
      assert.equal( win, vim.api.nvim_get_current_win() )
      assert.equal( 3, vim.api.nvim_win_get_cursor( target )[1] )
    end )

    it( 'reaches nothing from an entry without a location', function()
      local listed = entry.list( bufnr )
      local answer = listed[3]
      assert.is_nil( navigate.locate( bufnr, answer.first ) )
    end )

    it( 'publishes going to a location as an intent', function()
      local published
      for _, mapping in ipairs( vim.api.nvim_buf_get_keymap( bufnr, 'n' ) ) do
        if mapping.lhs == '<Plug>(pivi-goto-location)' then published = mapping end
      end

      assert.is_table( published )
    end )
  end )

  it( 'moves to the next entry regardless of kind', function()
    local listed = entry.list( bufnr )
    vim.api.nvim_win_set_cursor( win, { listed[1].first, 0, } )

    local moved = navigate.next_entry( bufnr )

    assert.equal( listed[2].id, moved.id )
    assert.equal( listed[2].first, vim.api.nvim_win_get_cursor( win )[1] )
  end )

  it( 'moves to the previous entry', function()
    local listed = entry.list( bufnr )
    vim.api.nvim_win_set_cursor( win, { listed[3].first, 0, } )

    local moved = navigate.previous_entry( bufnr )

    assert.equal( listed[2].id, moved.id )
  end )

  it( 'moves between tool calls only', function()
    local listed = entry.list( bufnr )
    vim.api.nvim_win_set_cursor( win, { listed[1].first, 0, } )

    local first_tool = navigate.next_entry( bufnr, 'tool' )
    assert.equal( 'tool', first_tool.kind )
    assert.equal( 'read', first_tool.meta.tool )

    local second_tool = navigate.next_entry( bufnr, 'tool' )
    assert.equal( 'edit', second_tool.meta.tool )
  end )

  it( 'moves backwards between tool calls', function()
    local listed = entry.list( bufnr )
    vim.api.nvim_win_set_cursor( win, { listed[4].first, 0, } )

    local moved = navigate.previous_entry( bufnr, 'tool' )

    assert.equal( 'read', moved.meta.tool )
  end )

  it( 'wraps around at both ends', function()
    local listed = entry.list( bufnr )

    vim.api.nvim_win_set_cursor( win, { listed[#listed].first, 0, } )
    assert.equal( listed[1].id, navigate.next_entry( bufnr ).id )

    vim.api.nvim_win_set_cursor( win, { listed[1].first, 0, } )
    assert.equal( listed[#listed].id, navigate.previous_entry( bufnr ).id )
  end )

  it( 'moves nowhere in a transcript without matching entries', function()
    local empty = transcript.ensure 'nothing'
    transcript.clear( empty )

    assert.is_nil( navigate.after( empty, 1 ) )
    assert.is_nil( navigate.before( empty, 1 ) )
  end )

  it( 'publishes each motion as an intent', function()
    local published = {}
    for _, mapping in ipairs( vim.api.nvim_buf_get_keymap( bufnr, 'n' ) ) do
      published[mapping.lhs] = true
    end

    for _, intent in ipairs {
      '<Plug>(pivi-next-entry)', '<Plug>(pivi-previous-entry)',
      '<Plug>(pivi-next-tool)', '<Plug>(pivi-previous-tool)',
    } do
      assert.is_true( published[intent], intent .. ' is not published' )
    end
  end )

  it( 'never changes the window or the buffer while moving', function()
    local listed = entry.list( bufnr )
    vim.api.nvim_win_set_cursor( win, { listed[1].first, 0, } )
    local before = transcript.lines( bufnr )

    navigate.next_entry( bufnr )

    assert.equal( win, vim.api.nvim_get_current_win() )
    assert.equal( bufnr, vim.api.nvim_win_get_buf( win ) )
    assert.same( before, transcript.lines( bufnr ) )
  end )
end )