-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathmarks.lua
442 lines (376 loc) · 12.4 KB
/
marks.lua
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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
--[[
All things marks.
Type "mx" while in normal mode to make a mark for "x"
Type "'x" or "`x" while in normal mode to go ot a mark for "x"
if x is a lowercase letter - the mark is local to this file
(you can have marks for one letter in different files)
(but you will only be able to go to local mark of the current file)
if x is an UPPERCASE letter - the mark is global
(you will go to the mark from any file, opening the file if it is not opened)
(new mark for the same uppercase letter will owerwrite the previous one)
You can list all marks using command "vibe:marks:show-all"
Also you can make named marks using <space><CR>
For now all marks are kept between sessions in .config/marks.lua file
]]--
local core = require "core"
local command = require "core.command"
local common = require "core.common"
local keymap = require "core.keymap"
local Doc = require "core.doc"
local style = require "core.style"
local misc = require "plugins.lite-xl-vibe.misc"
local kb = require "plugins.lite-xl-vibe.keyboard"
local ResultsView = require "plugins.lite-xl-vibe.ResultsView"
local function dv()
return core.active_view
end
local function doc()
return core.active_view.doc
end
local marks = {}
marks.global = {}
marks._local = {}
function marks.prep_mark(mark)
-- I don't save line_items since they have userdata and stuff
-- So first time after restart
-- when you open all marks through <space>om
-- you'll see just text for loaded marks
-- But once you go to a mark,
-- the items will be read from the doc's highlighter
-- and <space>om will show proper line appearance
-- (see misc.goto_mark)
return table.take_keys(
mark,
{'abs_filename','line','col','line_text','symbol','time'}
)
end
function marks.prep_marks_table(marks_table)
return table.map(
marks_table,
marks.prep_mark
)
end
function marks.save()
return table.list_to_dict_map(
{'global','_local'},
function(key)
return marks.prep_marks_table(marks[key])
end
)
end
function marks.load(v)
marks.global = v and v.global or {}
marks._local = v and v._local or {}
end
function marks.set_mark(symbol, global_flag)
local line,col = doc():get_selection()
local abs_filename = misc.doc_abs_filename(doc())
local mark = {
["abs_filename"] = abs_filename,
["line"] = line,
["col"] = col,
["line_text"] = doc().lines[line],
["line_items"] = core.active_view:get_line_draw_items(line),
["symbol"] = symbol,
["time"] = 1*os.time(),
}
if global_flag or symbol:isUpperCase() then
-- global
if marks.global[symbol] == nil then
marks.global[symbol] = {}
end
marks.global[symbol] = mark
else
-- local
if marks._local[abs_filename] == nil then
marks._local[abs_filename] = {}
end
marks._local[abs_filename][symbol] = mark
end
core.log("Mark [%s] set", symbol)
end
function marks.goto_global_mark(symbol)
-- also accepts mark as the argument
local mark = symbol.abs_filename and symbol or marks.global[symbol]
if mark then
misc.goto_mark(mark)
core.log("Jumped to (global) mark [%s]", symbol)
misc.update_mark_line_items(mark)
else
core.vibe.debug_str = 'no mark for '..symbol
end
end
function marks.goto_local_mark(symbol)
local cur_path = misc.doc_abs_filename(doc())
local mark = marks._local[cur_path] and marks._local[cur_path][symbol]
if mark then
misc.goto_mark(mark)
core.log("Jumped to (local) mark [%s]", symbol)
misc.update_mark_line_items(mark)
else
core.vibe.debug_str = 'no mark for ' .. symbol
end
end
function marks.goto_mark(symbol)
if marks.global[symbol] then
marks.goto_global_mark(symbol)
else
marks.goto_local_mark(symbol)
end
end
function marks.translation(symbol, doc) -- line, col are not needed
local doc_abs_filename = misc.doc_abs_filename(doc)
local mark = marks.global[symbol]
or (marks._local[doc_abs_filename]
and marks._local[doc_abs_filename][symbol])
if mark and mark.abs_filename == doc_abs_filename then
-- pass?
else
marks.goto_global_mark(symbol)
end
return mark.line, mark.col
end
function marks.translation_fun(symbol)
return function(doc)
return marks.translation(symbol, doc)
end
end
function marks.have_global_mark_fun(symbol)
return function() return marks.global[symbol] end
end
function marks.have_local_mark_fun(symbol)
return function()
return doc() and (
(
marks._local[misc.doc_abs_filename(doc())]
and marks._local[misc.doc_abs_filename(doc())][symbol]
) or (
marks.global[symbol]
and marks.global[symbol].abs_filename==misc.doc_abs_filename(doc())
and marks.global[symbol]
)
)
end
end
-------------------------------------------------------------------------------
-- commands and keymaps for one-symbol marks
local function commands_from_tranlations(translations, no_select, no_move)
local commands = {}
for name, fn in pairs(translations) do
if not no_move then
commands["doc:move-to-" .. name] = function() doc():move_to(fn, dv()) end
end
if not no_select then
commands["doc:select-to-" .. name] = function() doc():select_to(fn, dv()) end
commands["doc:delete-to-" .. name] = function() doc():delete_to(fn, dv()) end
end
end
return commands
end
for _,c in ipairs(kb.letters) do
local C = c:upper()
command.add("core.docview", {
['vibe:marks:set-local-'..c] = function()
marks.set_mark(c)
end,
['vibe:marks:set-global-'..C] = function()
marks.set_mark(C)
end,
})
command.add( marks.have_local_mark_fun(c),
commands_from_tranlations({['local-'..c] = marks.translation_fun(c)}))
local translations = {['global-'..C] = marks.translation_fun(C)}
command.add( marks.have_global_mark_fun(C), {
["doc:move-to-global-"..C] = function()
marks.goto_global_mark(C)
end
})
-- but selects only go to locally defined marks!
command.add( marks.have_local_mark_fun(C),
commands_from_tranlations(translations, false, true))
keymap.add_nmap({
['m'..c] = 'vibe:marks:set-local-'..c,
['m'..C] = 'vibe:marks:set-global-'..C,
["'"..c] = 'doc:move-to-local-'..c,
["`"..c] = 'doc:move-to-local-'..c,
["'"..C] = 'doc:move-to-global-'..C,
["`"..C] = 'doc:move-to-global-'..C,
})
end
-------------------------------------------------------------------------------
-- Shift marks' line numbers on doc edits
-------------------------------------------------------------------------------
-- -- based on lite-xl/lite-plugins/master/plugins/markers.lua
-------------------------------------------------------------------------------
function marks.shift_mark(mark, at, diff)
mark['line'] = mark['line'] >= at and mark['line'] + diff or mark['line']
end
function marks.shift_lines(doc, at, diff)
if diff == 0 then return end
for _, mark in pairs(marks.global) do
if mark.abs_filename == misc.doc_abs_filename(doc) then
marks.shift_mark(mark, at, diff)
end
end
if marks._local[misc.doc_abs_filename(doc)] then
for _, mark in pairs(marks._local[misc.doc_abs_filename(doc)]) do
marks.shift_mark(mark, at, diff)
end
end
end
local raw_insert = Doc.raw_insert
function Doc:raw_insert(line, col, text, ...)
raw_insert(self, line, col, text, ...)
local line_count = 0
for _ in text:gmatch("\n") do
line_count = line_count + 1
end
marks.shift_lines(self, line, line_count)
end
local raw_remove = Doc.raw_remove
function Doc:raw_remove(line1, col1, line2, col2, ...)
raw_remove(self, line1, col1, line2, col2, ...)
marks.shift_lines(self, line2, line1 - line2)
end
-------------------------------------------------------------------------------
-- DOOM Emacs kind of marks
command.add("core.docview", {
['vibe:marks:create-or-move-to-named-mark'] = function()
-- If you want, you could use doom's default bookmark name.. I won't
-- core.command_view:set_text(doc().filename)
local doc_filename = misc.doc_abs_filename(doc())
if misc.has_selection() then
core.command_view:set_text(doc():get_text(doc():get_selection()))
end
core.command_view:enter("Create or go to mark", function(text, item)
if misc.command_match_sug(text, item) then
marks.goto_mark(item.symbol)
else
if marks.have_local_mark_fun(text)() then
marks.goto_mark(text)
else
marks.set_mark(text, true)
end
end
end, function(text)
local items = {}
for symbol,mark in pairs(marks.global) do
table.insert(items, {
["text"] = symbol..'| '..mark.abs_filename..' | '..mark.line_text,
["symbol"] = symbol,
["global"] = true,
})
end
for symbol, mark in pairs(marks._local[doc_filename] or {}) do
table.insert(items, {
["text"] = symbol..'| '..mark.abs_filename..' | '..mark.line_text,
["symbol"] = symbol,
})
end
return misc.fuzzy_match_key(items, 'text', text)
end)
end,
})
-- and kinda DOOM Emacs keymap
keymap.add_direct({
['alt+return'] = 'vibe:marks:create-or-move-to-named-mark',
['alt+m'] = 'vibe:marks:create-or-move-to-named-mark',
})
-------------------------------------------------------------------------------
-- Save / Load
-------------------------------------------------------------------------------
function marks.filename()
return misc.USERDIR .. PATHSEP .. "marks.lua"
end
function marks.load_from_file(_filename)
local filename = _filename or marks.filename()
local load_f = loadfile(filename)
local _marks = load_f and load_f()
if _marks then
marks.global = _marks.global
marks._local = _marks._local
else
core.error("vibe: Error while loading marks file")
end
end
function marks.save_to_file(_filename)
local filename = _filename or marks.filename()
local fp = io.open(filename, "w")
if fp then
local global_text = common.serialize(marks.global)
local local_text = common.serialize(marks._local)
fp:write(string.format("return { global = %s, _local = %s }\n", global_text, local_text))
fp:close()
end
end
-- -- this is in vibeworkspace now
-- marks.load_from_file()
-- local on_quit_project = core.on_quit_project
-- function core.on_quit_project()
-- core.try(marks.save_to_file)
-- on_quit_project()
-- end
-- local on_enter_project = core.on_enter_project
-- function core.on_enter_project(new_dir)
-- on_enter_project(new_dir)
-- core.try(marks.load_from_file)
-- end
command.add(nil, {
["vibe:marks:save"] = marks.save_to_file,
["vibe:marks:load"] = marks.load_from_file,
})
-------------------------------------------------------------------------------
-- MarksView
-------------------------------------------------------------------------------
function marks.mark_to_results(mark)
return {
file=core.normalize_to_project_dir(mark.abs_filename) ,
text=mark.line_items or {style.code_font, mark.line_text},
line_text=mark.line_text,
line=mark.line,
col=mark.col,
data=mark,
symbol=mark.symbol
}
end
local function fill_results()
ResultsView.new_and_add({
title="(book-)Marks List",
items_fun = function()
local items = {}
-- global
for symbol, mark in pairs(marks.global) do
table.insert(items, marks.mark_to_results(mark))
end
-- local..
for filename, markss in pairs(marks._local) do
for symbol, mark in pairs(markss) do
table.insert(items, marks.mark_to_results(mark))
end
end
-- title: symbol and position
for _,item in ipairs(items) do
item.search_text = string.format("[%s] %s at line %d (col %d): %s",
item.data.symbol, item.file, item.line, item.col, item.data.line_text)
item.Symbol = (#item.symbol > 4) and (item.symbol:sub(1,4).."..") or item.symbol
item.File = misc.path_shorten(item.file)
end
core.log('items_fun : %i items',#items)
return items
end,
on_click_fun = function(res)
command.perform("root:close")
misc.goto_mark(res.data)
end,
column_names = {"Symbol","File","text"},
sort_fields = {"Symbol","File","line_text"},
})
end
command.add(nil, {
["vibe:marks:show-all"] = fill_results,
["vibe:marks:clear-all"] = function()
marks.global = {}
marks._local = {}
end,
})
return marks