aboutsummaryrefslogtreecommitdiff
path: root/plugins/navigate.lua
blob: d83c02fb9fdcf5e87ee2c2bd6fdfb7805d523f00 (plain)
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
-- mod-version:3

local core = require "core"
local common = require "core.common"
local command = require "core.command"
local keymap = require "core.keymap"
local Doc = require "core.doc"
local DocView = require "core.docview"

local navigate = {
  list = {},
  current = nil,
  index = 0
}

--
-- Private functions
--
local function get_active_view()
  if getmetatable(core.active_view) == DocView then
    return core.active_view
  end
  return nil
end

-- Solution to safely remove elements from array table:
-- found at https://stackoverflow.com/a/53038524
local function array_remove(t, fnKeep)
  local j, n = 1, #t;

  for i=1, n do
    if (fnKeep(t, i, j)) then
      if (i ~= j) then
        t[j] = t[i];
        t[i] = nil;
      end
      j = j + 1;
    else
      t[i] = nil;
    end
  end

  return t;
end

local function add(doc)
  -- Make new navigation point last in list
  if navigate.index > 0 and navigate.index < #navigate.list then
    local remove_start = navigate.index + 1
    local remove_end = #navigate.list
    array_remove(navigate.list, function(_, i)
      if i >= remove_start and i <= remove_end then
        return false
      end
      return true
    end)
  end

  local line, col = doc:get_selection()
  table.insert(navigate.list, {
    filename = doc.filename,
    line = line,
    col = col
  })

  navigate.current = navigate.list[#navigate.list]
  navigate.index = #navigate.list
end

local function open_doc(doc)
  core.root_view:open_doc(
    core.open_doc(
      common.home_expand(
        doc.filename
      )
    )
  )

  local av_doc = get_active_view().doc
  local line, col = av_doc:get_selection()
  if doc.line ~= line or doc.col ~= col then
    av_doc:set_selection(doc.line, doc.col, doc.line, doc.col)
  end
end

--
-- Public functions
--
function navigate.next()
  if navigate.index < #navigate.list then
    navigate.index = navigate.index + 1
    navigate.current = navigate.list[navigate.index]
    open_doc(navigate.current)
  end
end

function navigate.prev()
  if navigate.index > 1 then
    navigate.index = navigate.index - 1
    navigate.current = navigate.list[navigate.index]
    open_doc(navigate.current)
  end
end

--
-- Thread
--
core.add_thread(function()
  while true do
    local av = get_active_view()
    if av and av.doc and av.doc.filename then
      local doc = av.doc
      local line, col = doc:get_selection()
      local current = navigate.current
      if
        not current
        or
        current.filename ~= doc.filename
        or
        current.line ~= line
      then
        add(doc)
      else
        current.col = col
      end
    end
    coroutine.yield(0.5)
  end
end)

--
-- Patching
--
local doc_on_close = Doc.on_close

function Doc:on_close()
  local filename = self.filename
  -- remove all positions referencing closed file
  array_remove(navigate.list, function(t, i)
    if t[i].filename == filename then
      return false
    end
    return true
  end)

  doc_on_close(self)
end

--
-- Commands
--
command.add("core.docview", {
  ["navigate:previous"] = function()
    navigate.prev()
  end,

  ["navigate:next"] = function()
    navigate.next()
  end,
})

--
-- Default Keybindings
--
keymap.add {
  ["alt+left"]    = "navigate:previous",
  ["alt+right"]   = "navigate:next",
}

return navigate