aboutsummaryrefslogtreecommitdiff
path: root/lua/diagnostic_objects.lua
blob: f0fbf97dbc6b5587f846a8c8ee4a23697bfbb673 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
-- Contains object mappings for vim diagnostics.

local vim = assert(vim)

-- indw: next diagnostic warning
-- ildw: last diagnostic warning
-- inde: next diagnostic error
-- ilde: last diagnostic error
-- indi: next diagnostic info
-- ildi: last diagnostic info
-- inda: next any diagnostic
-- ilda: last any diagnostic

local M = {}

local function compare_pos(diag, pos)
  if diag.lnum + 1 < pos[1] then
    return -1
  end

  if diag.lnum + 1 > pos[1] then
    return 1
  end

  if diag.col > pos[1] then
    return 1
  end

  if diag.col < pos[1] then
    return -1
  end

  return 0
end

function M.get_matching_diagnostic(last_or_next, severity)
  local current_pos = vim.api.nvim_win_get_cursor(0)
  local bufnr = vim.api.nvim_get_current_buf()

  local all_diagnostics = vim.diagnostic.get(bufnr)

  local all_matching_diagnostics = {}
  for _, diag in pairs(all_diagnostics) do
    if diag.severity == severity or severity == -1 then
      table.insert(all_matching_diagnostics, diag)
    end
  end

  local last_matching_diag = nil

  for _, diag in ipairs(all_matching_diagnostics) do
    if compare_pos(diag, current_pos) < 0 then
      if (severity == -1 or diag.severity == severity) and last_or_next ~= 'next' then
        last_matching_diag = diag
      end
    elseif compare_pos(diag, current_pos) > 0  then
      if last_or_next ~= 'next' then
        break
      end

      if (severity == -1 or diag.severity == severity) then
        last_matching_diag = diag
        break
      end
    end
  end

  if not last_matching_diag and #all_matching_diagnostics > 0 then
    if last_or_next == 'next' then
      last_matching_diag = all_matching_diagnostics[1]
    else
      last_matching_diag = all_matching_diagnostics[-1]
    end
  end

  return last_matching_diag
end

local function normalize_position(diag)
  local lnum = diag.lnum + 1
  local elnum = math.max(diag.end_lnum + 1, lnum)

  local col = math.max(diag.col, 0)
  local ecol = diag.end_col

  if elnum == lnum then
    ecol = math.max(col, ecol)
  end

  return lnum, col, elnum, ecol
end

function M.highlight_matching_diagnostic(last_or_next, severity)
  local diag = M.get_matching_diagnostic(last_or_next, severity)


  if diag then
    local l, c, el, ec = normalize_position(diag);
    -- col, lnum, end_col, end_lnum
    vim.api.nvim_win_set_cursor(0, { l, c })

    if l == el and c == ec and vim.v.operator:match('[cd]') then
      -- hack to get zero-width matches to work.
      vim.cmd("normal! i ")
    end
    vim.cmd("normal! v")
    vim.api.nvim_win_set_cursor(0, { el, ec })
  else
    vim.fn.feedkeys('')
  end
end

return M