Skip to content

Commit 097cd62

Browse files
committed
feat: Add Treesitter support, marks caching, and fixed-size UI
Add marks file caching in storage module that validates against file modification times to reduce I/O operations. Implement comprehensive Treesitter integration for more accurate context detection when generating mark names, with fallback to regex patterns. Redesign the UI with a fixed 80x20 window size, featuring dimmed help text at the bottom and visual separators between sections. The changes maintain backward compatibility while providing significant performance gains through caching and more intelligent mark naming through Treesitter AST analysis.
1 parent ace36b6 commit 097cd62

File tree

4 files changed

+475
-126
lines changed

4 files changed

+475
-126
lines changed

README.md

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ Vim's built-in marks are great, but they're global and get messy fast. Marksman
1818

1919
- **Project-scoped marks** - Each project gets its own isolated set of bookmarks
2020
- **Persistent storage** - Your marks survive Neovim restarts with automatic backup
21-
- **Smart naming** - Context-aware auto-generation based on code structure
21+
- **Smart naming** - Context-aware auto-generation using Treesitter and pattern matching
2222
- **Quick access** - Jump to marks with single keys or interactive UI
2323
- **Enhanced search** - Find marks by name, file path, or content with real-time filtering
2424
- **Mark reordering** - Move marks up/down to organize them as needed
2525
- **Multiple integrations** - Works with Telescope, Snacks.nvim, and more
26-
- **Memory efficient** - Lazy loading, cleanup, and debounced operations
26+
- **Memory efficient** - Lazy loading, marks caching, cleanup, and debounced operations
2727
- **Robust error handling** - Graceful fallbacks and comprehensive validation
2828

2929
## Requirements
@@ -135,6 +135,9 @@ require("marksman").setup({
135135
ProjectMarksFile = { fg = "#56B6C2" },
136136
ProjectMarksLine = { fg = "#D19A66" },
137137
ProjectMarksText = { fg = "#5C6370", italic = true },
138+
ProjectMarksHelp = { fg = "#5C6370" },
139+
ProjectMarksBorder = { fg = "#5A5F8C" },
140+
ProjectMarksSeparator = { fg = "#3E4451" },
138141
},
139142
})
140143
```
@@ -253,15 +256,19 @@ marksman.show_marks("optional_search")
253256

254257
## How it works
255258

256-
**Storage**: Marks are stored in `~/.local/share/nvim/marksman_[hash].json` per project with automatic backup.
259+
**Storage**: Marks are stored in `~/.local/share/nvim/marksman_[hash].json` per project with automatic backup and caching for improved performance.
257260

258-
**Smart Naming**: Auto-generates context-aware names:
261+
**Smart Naming**: Auto-generates context-aware names using Treesitter when available, falling back to pattern matching:
259262
- `fn:calculate_total` for functions
260263
- `class:UserModel` for classes
261264
- `var:api_key` for variables
265+
- `method:process_data` for methods
266+
- `interface:Serializable` for interfaces
267+
- `trait:Iterator` for Rust traits
268+
- `type:Config` for type definitions
262269
- `filename:line` as fallback
263270

264-
**Project Detection**: Uses Git root, common project files (.git, package.json, etc.), or current directory.
271+
**Project Detection**: Uses Git root, common project files (.git, package.json, etc.), or current directory with 30-second caching.
265272

266273
**Search**: Multi-field search across mark names, file paths, and code content.
267274

lua/marksman/storage.lua

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ local config = {}
1616
local project_root_cache = {}
1717
local cache_expiry = {}
1818

19+
-- Cache for marks file
20+
local marks_cache = nil
21+
local marks_cache_timestamp = nil
22+
local marks_cache_project = nil
23+
1924
---Helper function for conditional notifications
2025
---@param message string The notification message
2126
---@param level number The log level
@@ -202,12 +207,23 @@ local function validate_marks_data(data)
202207
return true
203208
end
204209

205-
---Load marks from storage file
210+
---Load marks from storage file with caching
206211
---@return table marks The loaded marks
207212
local function load_marks()
208213
current_project = get_project_root()
209214
local file = get_marks_file()
210215

216+
-- Check cache validity
217+
if marks_cache and marks_cache_project == current_project then
218+
local file_mtime = vim.fn.getftime(file)
219+
if file_mtime > 0 and marks_cache_timestamp == file_mtime then
220+
-- Cache is valid, return cached data
221+
marks = marks_cache.marks
222+
mark_order = marks_cache.mark_order
223+
return marks
224+
end
225+
end
226+
211227
-- Initialize empty state
212228
marks = {}
213229
mark_order = {}
@@ -246,6 +262,14 @@ local function load_marks()
246262
end
247263
end
248264
mark_order = valid_order
265+
266+
-- Update cache
267+
marks_cache = {
268+
marks = vim.deepcopy(marks),
269+
mark_order = vim.deepcopy(mark_order),
270+
}
271+
marks_cache_timestamp = vim.fn.getftime(file)
272+
marks_cache_project = current_project
249273
end
250274
end
251275
end
@@ -325,6 +349,14 @@ function M.save_marks()
325349
if not rename_ok then
326350
error("Failed to move temporary file to final location")
327351
end
352+
353+
-- Update cache after successful save
354+
marks_cache = {
355+
marks = vim.deepcopy(marks),
356+
mark_order = vim.deepcopy(mark_order),
357+
}
358+
marks_cache_timestamp = vim.fn.getftime(file)
359+
marks_cache_project = current_project
328360
end)
329361

330362
if not ok then
@@ -409,6 +441,10 @@ function M.add_mark(name, mark)
409441
end
410442

411443
marks_data[name] = mark
444+
445+
-- Invalidate cache for immediate update
446+
marks_cache = nil
447+
412448
return true -- Save is handled by debounced save in main module
413449
end
414450

@@ -429,6 +465,9 @@ function M.delete_mark(name)
429465
end
430466
end
431467

468+
-- Invalidate cache
469+
marks_cache = nil
470+
432471
return true
433472
end
434473

@@ -466,6 +505,9 @@ function M.rename_mark(old_name, new_name)
466505
end
467506
end
468507

508+
-- Invalidate cache
509+
marks_cache = nil
510+
469511
return true
470512
end
471513

@@ -505,6 +547,9 @@ function M.move_mark(name, direction)
505547
-- Swap positions
506548
mark_order[current_index], mark_order[new_index] = mark_order[new_index], mark_order[current_index]
507549

550+
-- Invalidate cache
551+
marks_cache = nil
552+
508553
return true
509554
end
510555

@@ -513,6 +558,10 @@ end
513558
function M.clear_all_marks()
514559
marks = {}
515560
mark_order = {}
561+
562+
-- Invalidate cache
563+
marks_cache = nil
564+
516565
return true
517566
end
518567

@@ -628,6 +677,9 @@ function M.import_marks()
628677
return { success = false, message = "Import cancelled" }
629678
end
630679

680+
-- Invalidate cache
681+
marks_cache = nil
682+
631683
if M.save_marks() then
632684
notify("󰃀 Marks imported successfully", vim.log.levels.INFO)
633685
return { success = true, message = "Marks imported successfully" }
@@ -665,6 +717,9 @@ function M.cleanup()
665717
-- Clear caches
666718
project_root_cache = {}
667719
cache_expiry = {}
720+
marks_cache = nil
721+
marks_cache_timestamp = nil
722+
marks_cache_project = nil
668723

669724
-- Reset state
670725
marks = {}

0 commit comments

Comments
 (0)