Skip to content

Commit 5b184b6

Browse files
committed
feat: unload scopes
1 parent b2b0586 commit 5b184b6

8 files changed

+125
-36
lines changed

README.md

+6-5
Original file line numberDiff line numberDiff line change
@@ -821,12 +821,13 @@ require("grapple").open_scopes()
821821

822822
<img width="1080" alt="image" src="https://github.com/cbochs/grapple.nvim/assets/2467016/8b91222f-cf5e-43b9-9286-56379a6a80f0">
823823

824-
Open a floating window with all loaded scopes. This buffer is not modifiable. Some basic actions are available by default:
824+
Open a floating window with all loaded scope IDs. This buffer is not modifiable. Some basic actions are available by default:
825825

826-
- **Selection** (`<cr>`): open the [tags window](#tags-window) for the loaded scope under the cursor
827-
- **Quick select** (default: `1-9`): open tags window for the loaded scope at a given index
828-
- **Deletion** (`x`): reset the tags for the loaded scope under the cursor
829-
- **Toggle** (`<s-cr>`): toggle showing both loaded and unloaded scopes
826+
- **Selection** (`<cr>`): open the [tags window](#tags-window) for the loaded scope ID under the cursor
827+
- **Quick select** (default: `1-9`): open tags window for the loaded scope ID at a given index
828+
- **Toggle** (`<s-cr>`): toggle showing both loaded and unloaded scope IDs
829+
- **Unload** (`x`): unload the tags for the scope ID under the cursor
830+
- **Deletion** (`X`): reset the tags for the scope ID under the cursor
830831
- **Go up** (`-`): navigate across to the [scopes window](#scopes-window)
831832
- **Help** (`?`): open the help window
832833

lua/grapple.lua

+44-12
Original file line numberDiff line numberDiff line change
@@ -378,19 +378,50 @@ function Grapple.statusline(opts)
378378
return statusline
379379
end
380380

381+
---@param opts? { scope?: string, id?: string, notify?: boolean }
382+
---@return string? error
383+
function Grapple.unload(opts)
384+
local App = require("grapple.app")
385+
local app = App.get()
386+
387+
opts = opts or {}
388+
389+
local err = app:unload(opts)
390+
if err then
391+
if opts.notify then
392+
return vim.notify(err, vim.log.levels.ERROR)
393+
else
394+
return err
395+
end
396+
end
397+
398+
if opts.notify then
399+
vim.notify(string.format("Scope unloaded: %s", opts.scope or opts.id), vim.log.levels.INFO)
400+
end
401+
end
402+
381403
---Reset tags for a given (scope) name or loaded scope (id)
382404
---By default, uses the current scope
383-
---@param opts? { scope?: string, id?: string }
405+
---@param opts? { scope?: string, id?: string, notify?: boolean }
406+
---@return string? error
384407
function Grapple.reset(opts)
385408
local App = require("grapple.app")
386409
local app = App.get()
387410

411+
opts = opts or {}
412+
388413
local err = app:reset(opts)
389414
if err then
390-
return vim.notify(err, vim.log.levels.ERROR)
415+
if opts.notify then
416+
return vim.notify(err, vim.log.levels.ERROR)
417+
else
418+
return err
419+
end
391420
end
392421

393-
vim.notify(string.format("Scope reset: %s", opts.scope or opts.id), vim.log.levels.INFO)
422+
if opts.notify then
423+
vim.notify(string.format("Scope reset: %s", opts.scope or opts.id), vim.log.levels.INFO)
424+
end
394425
end
395426

396427
---Create a user-defined scope
@@ -412,20 +443,20 @@ function Grapple.delete_scope(scope)
412443
end
413444

414445
---Change the currently selected scope
415-
---@param scope string
416-
function Grapple.use_scope(scope)
446+
---@param scope_name string
447+
function Grapple.use_scope(scope_name)
417448
local App = require("grapple.app")
418449
local app = App.get()
419450

420-
local resolved, err = app.scope_manager:get(scope)
421-
if not resolved then
451+
local scope, err = app.scope_manager:get(scope_name)
452+
if not scope then
422453
---@diagnostic disable-next-line: param-type-mismatch
423454
return vim.notify(err, vim.log.levels.ERROR)
424455
end
425456

426-
if resolved.name ~= app.settings.scope then
427-
app.settings:update({ scope = resolved.name })
428-
vim.notify(string.format("Changing scope: %s", resolved.name))
457+
if scope.name ~= app.settings.scope then
458+
app.settings:update({ scope = scope.name })
459+
vim.notify(string.format("Changing scope: %s", scope.name))
429460
end
430461
end
431462

@@ -499,9 +530,9 @@ function Grapple.open_tags(opts)
499530
-- stylua: ignore
500531
local content = TagContent:new(
501532
scope,
502-
app.settings.styles[opts.style or app.settings.style],
503533
app.settings.tag_hook,
504-
app.settings.tag_title
534+
app.settings.tag_title,
535+
app.settings.styles[opts.style or app.settings.style]
505536
)
506537

507538
open(content)
@@ -642,6 +673,7 @@ function Grapple.initialize()
642673
toggle_loaded = { args = {}, kwargs = { "all" } },
643674
toggle_scopes = { args = {}, kwargs = {} },
644675
toggle_tags = { args = {}, kwargs = window_kwargs },
676+
unload = { args = {}, kwargs = scope_kwargs },
645677
untag = { args = {}, kwargs = use_kwargs },
646678
use_scope = { args = { "scope" }, kwargs = {} },
647679
}

lua/grapple/app.lua

+33-9
Original file line numberDiff line numberDiff line change
@@ -89,11 +89,9 @@ function App:current_scope()
8989
return self.scope_manager:get_resolved(self.settings.scope)
9090
end
9191

92-
---Reset tags for a given scope (name) or loaded scope (id)
93-
---By default, uses the current scope
9492
---@param opts? { scope?: string, id?: string }
95-
---@return string? error
96-
function App:reset(opts)
93+
---@return string | nil id, string | nil name, string? error
94+
function App:lookup(opts)
9795
opts = vim.tbl_extend("keep", opts or {}, {
9896
scope = self.settings.scope,
9997
})
@@ -113,26 +111,52 @@ function App:reset(opts)
113111
local scope, err = app.scope_manager:get_resolved(opts.scope)
114112
if not scope then
115113
---@diagnostic disable-next-line: param-type-mismatch
116-
return err
114+
return nil, nil, err
117115
end
118116

119117
id = scope.id
120118
name = scope.name
121119
end
122120

123121
if not id then
124-
return string.format("must provide a valid scope or id: %s", vim.inspect(opts))
122+
return nil, nil, string.format("must provide a valid scope or id: %s", vim.inspect(opts))
123+
end
124+
125+
return id, name, nil
126+
end
127+
128+
---Unload tags for a given scope (name) or loaded scope (id)
129+
---By default, uses the current scope
130+
---@param opts? { scope?: string, id?: string }
131+
---@return string? error
132+
function App:unload(opts)
133+
local id, name, err = self:lookup(opts)
134+
if not id then
135+
return err
125136
end
126137

127138
if name then
128139
self.scope_manager.cache:unwatch(name)
129140
end
130141

131-
---@diagnostic disable-next-line: redefined-local
132-
local err = self.tag_manager:reset(id)
133-
if err then
142+
self.tag_manager:unload(id)
143+
end
144+
145+
---Reset tags for a given scope (name) or loaded scope (id)
146+
---By default, uses the current scope
147+
---@param opts? { scope?: string, id?: string }
148+
---@return string? error
149+
function App:reset(opts)
150+
local id, name, err = self:lookup(opts)
151+
if not id then
134152
return err
135153
end
154+
155+
if name then
156+
self.scope_manager.cache:unwatch(name)
157+
end
158+
159+
self.tag_manager:reset(id)
136160
end
137161

138162
---@param scope_name? string

lua/grapple/container_actions.lua

+6-1
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,14 @@ function ContainerActions.select(opts)
1616
require("grapple").open_tags({ id = opts.id })
1717
end
1818

19+
function ContainerActions.unload(opts)
20+
require("grapple").unload({ id = opts.id, notify = true })
21+
opts.window:render()
22+
end
23+
1924
---@param opts grapple.action.container_options
2025
function ContainerActions.reset(opts)
21-
require("grapple").reset({ id = opts.id })
26+
require("grapple").reset({ id = opts.id, notify = true })
2227
opts.window:render()
2328
end
2429

lua/grapple/scope_manager.lua

+18-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
local ResolvedScope = require("grapple.resolved_scope")
12
local Scope = require("grapple.scope")
23

34
---@class grapple.scope_manager
@@ -63,11 +64,25 @@ end
6364
---@return grapple.resolved_scope | nil, string? error
6465
function ScopeManager:lookup(id)
6566
local resolved = self.resolved_lookup[id]
66-
if not resolved then
67-
return nil, string.format("could not find resolved scope for id: %s", id)
67+
68+
if resolved then
69+
return resolved, nil
70+
end
71+
72+
---@param item grapple.tag_container_item
73+
---@return string id
74+
local to_id = function(item)
75+
return item.id
76+
end
77+
78+
-- TODO: This lookup is using the tag_manager. Maybe it should be moved
79+
-- somewhere else? Looks like an opportunity for refactoring
80+
local ids = vim.tbl_map(to_id, self.app.tag_manager:list())
81+
if vim.tbl_contains(ids, id) then
82+
return ResolvedScope:new(self.app, "unknown", id, nil)
6883
end
6984

70-
return self.resolved_lookup[id], nil
85+
return nil, string.format("could not find resolved scope for id: %s", id)
7186
end
7287

7388
---@param name string

lua/grapple/settings.lua

+8-1
Original file line numberDiff line numberDiff line change
@@ -318,8 +318,15 @@ local DEFAULT_SETTINGS = {
318318
window:perform_retain(ContainerActions.toggle_all)
319319
end, { desc = "Toggle show all" })
320320

321-
-- Reset
321+
-- Unload
322322
window:map("n", "x", function()
323+
local entry = window:current_entry()
324+
local id = entry.data.id
325+
window:perform_retain(ContainerActions.unload, { id = id })
326+
end)
327+
328+
-- Reset
329+
window:map("n", "X", function()
323330
local entry = window:current_entry()
324331
local id = entry.data.id
325332
window:perform_retain(ContainerActions.reset, { id = id })

lua/grapple/tag_content.lua

+4-4
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,24 @@ local Util = require("grapple.util")
33

44
---@class grapple.tag_content
55
---@field scope grapple.resolved_scope
6-
---@field style_fn grapple.style_fn
76
---@field hook_fn grapple.hook_fn
87
---@field title_fn grapple.title_fn
8+
---@field style_fn grapple.style_fn
99
---@field current_selection string | nil path of the current buffer
1010
local TagContent = {}
1111
TagContent.__index = TagContent
1212

1313
---@param scope grapple.resolved_scope
14-
---@param style_fn grapple.style_fn
1514
---@param hook_fn? grapple.hook_fn
1615
---@param title_fn? grapple.title_fn
16+
---@param style_fn grapple.style_fn
1717
---@return grapple.tag_content
18-
function TagContent:new(scope, style_fn, hook_fn, title_fn)
18+
function TagContent:new(scope, hook_fn, title_fn, style_fn)
1919
return setmetatable({
2020
scope = scope,
21-
style_fn = style_fn,
2221
hook_fn = hook_fn,
2322
title_fn = title_fn,
23+
style_fn = style_fn,
2424
current_selection = nil,
2525
}, self)
2626
end

lua/grapple/tag_manager.lua

+6-1
Original file line numberDiff line numberDiff line change
@@ -134,10 +134,15 @@ function TagManager:load(id)
134134
return container, nil
135135
end
136136

137+
---@param id string
138+
function TagManager:unload(id)
139+
self.containers[id] = nil
140+
end
141+
137142
---@param id string
138143
---@return string? error
139144
function TagManager:reset(id)
140-
self.containers[id] = nil
145+
self:unload(id)
141146

142147
if self.state:exists(id) then
143148
local err = self.state:remove(id)

0 commit comments

Comments
 (0)