| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231 |
- -- bookmarks.lua
- -- Save and jump between named positions in files
- -- ============================================================================
- local bookmarks = {}
- -- Configuration
- bookmarks.config = {
- enabled = true,
- max_bookmarks = 100,
- }
- -- Bookmarks storage: name -> {filepath, line, column}
- bookmarks.marks = {}
- -- Quick marks (single character, like Vim marks)
- bookmarks.quick_marks = {}
- -- Set a named bookmark at current position
- function bookmarks.set(name)
- if not name or name == "" then
- return {success = false, message = "Bookmark name required"}
- end
- local buffer = editor:current_buffer()
- local filepath = buffer.filepath or buffer.name
- local pos = editor:cursor_pos()
- bookmarks.marks[name] = {
- filepath = filepath,
- line = pos.line,
- column = pos.column,
- created = os.time(),
- }
- return {success = true, message = string.format("Bookmark '%s' set at %s:%d", name, filepath, pos.line + 1)}
- end
- -- Jump to a named bookmark
- function bookmarks.jump(name)
- if not name or name == "" then
- return {success = false, message = "Bookmark name required"}
- end
- local mark = bookmarks.marks[name]
- if not mark then
- return {success = false, message = "Bookmark not found: " .. name}
- end
- -- Check if we need to switch buffers
- local buffer = editor:current_buffer()
- local current_file = buffer.filepath or buffer.name
- if current_file ~= mark.filepath then
- -- Try to open the file
- local result = editor:execute_command("find-file", {mark.filepath})
- if not result.success then
- return {success = false, message = "Cannot open file: " .. mark.filepath}
- end
- end
- -- Move to the bookmark position
- editor:set_cursor(mark.line, mark.column)
- return {success = true, message = string.format("Jumped to '%s'", name)}
- end
- -- Delete a bookmark
- function bookmarks.delete(name)
- if not name or name == "" then
- return {success = false, message = "Bookmark name required"}
- end
- if not bookmarks.marks[name] then
- return {success = false, message = "Bookmark not found: " .. name}
- end
- bookmarks.marks[name] = nil
- return {success = true, message = "Deleted bookmark: " .. name}
- end
- -- List all bookmarks
- function bookmarks.list()
- local result = {}
- for name, mark in pairs(bookmarks.marks) do
- local display_file = mark.filepath:gsub("^" .. (os.getenv("HOME") or ""), "~")
- table.insert(result, {
- name = name,
- filepath = mark.filepath,
- display = string.format("%s: %s:%d", name, display_file, mark.line + 1),
- })
- end
- -- Sort by name
- table.sort(result, function(a, b) return a.name < b.name end)
- return result
- end
- -- Quick mark (single character, for quick navigation)
- function bookmarks.set_quick(char)
- if not char or #char ~= 1 then
- return {success = false, message = "Quick mark must be a single character"}
- end
- local buffer = editor:current_buffer()
- local filepath = buffer.filepath or buffer.name
- local pos = editor:cursor_pos()
- bookmarks.quick_marks[char] = {
- filepath = filepath,
- line = pos.line,
- column = pos.column,
- }
- return {success = true, message = string.format("Mark '%s' set", char)}
- end
- -- Jump to quick mark
- function bookmarks.jump_quick(char)
- if not char or #char ~= 1 then
- return {success = false, message = "Quick mark must be a single character"}
- end
- local mark = bookmarks.quick_marks[char]
- if not mark then
- return {success = false, message = "Mark not set: " .. char}
- end
- local buffer = editor:current_buffer()
- local current_file = buffer.filepath or buffer.name
- if current_file ~= mark.filepath then
- local result = editor:execute_command("find-file", {mark.filepath})
- if not result.success then
- return {success = false, message = "Cannot open file: " .. mark.filepath}
- end
- end
- editor:set_cursor(mark.line, mark.column)
- return {success = true, message = string.format("Jumped to mark '%s'", char)}
- end
- -- Setup function
- function bookmarks.setup(opts)
- opts = opts or {}
- for k, v in pairs(opts) do
- bookmarks.config[k] = v
- end
- if not bookmarks.config.enabled then
- return
- end
- -- Register commands
- editor:register_command("bookmark-set", "Set a named bookmark at current position", function(args)
- if #args == 0 then
- return {success = true, message = "Usage: bookmark-set <name>"}
- end
- return bookmarks.set(args[1])
- end, {}, true, "s")
- editor:register_command("bookmark-jump", "Jump to a named bookmark", function(args)
- if #args == 0 then
- -- List available bookmarks
- local list = bookmarks.list()
- if #list == 0 then
- return {success = true, message = "No bookmarks set"}
- end
- local displays = {}
- for _, b in ipairs(list) do
- table.insert(displays, b.display)
- end
- editor:set_echo_area(displays)
- return {success = true, message = ""}
- end
- return bookmarks.jump(args[1])
- end, {}, true, "s")
- editor:register_command("bookmark-delete", "Delete a bookmark", function(args)
- if #args == 0 then
- return {success = true, message = "Usage: bookmark-delete <name>"}
- end
- return bookmarks.delete(args[1])
- end, {}, true, "s")
- editor:register_command("bookmark-list", "List all bookmarks", function(args)
- local list = bookmarks.list()
- if #list == 0 then
- return {success = true, message = "No bookmarks set"}
- end
- local displays = {}
- for _, b in ipairs(list) do
- table.insert(displays, b.display)
- end
- editor:set_echo_area(displays)
- return {success = true, message = ""}
- end)
- -- Quick marks (Vim-style m<char> and '<char>)
- editor:register_command("mark-set", "Set a quick mark (single character)", function(args)
- if #args == 0 then
- return {success = true, message = "Usage: mark-set <char>"}
- end
- return bookmarks.set_quick(args[1])
- end, {}, true, "c")
- editor:register_command("mark-jump", "Jump to a quick mark", function(args)
- if #args == 0 then
- return {success = true, message = "Usage: mark-jump <char>"}
- end
- return bookmarks.jump_quick(args[1])
- end, {}, true, "c")
- -- Key bindings
- -- C-x r m - set bookmark (Emacs)
- -- C-x r b - jump to bookmark (Emacs)
- -- C-x r l - list bookmarks (Emacs)
- editor:bind_key("C-x r m", "bookmark-set", "Set bookmark")
- editor:bind_key("C-x r b", "bookmark-jump", "Jump to bookmark")
- editor:bind_key("C-x r l", "bookmark-list", "List bookmarks")
- -- Quick marks with F-keys for convenience
- editor:bind_key("F2", "mark-set", "Set quick mark")
- editor:bind_key("S-F2", "mark-jump", "Jump to quick mark")
- print("[bookmarks] Package loaded")
- end
- -- Auto-setup with defaults
- bookmarks.setup()
- return bookmarks
|