Skip to content

Commit 03ffbb9

Browse files
committed
feat: toggle showing loaded and unloaded scopes in UI with '<s-cr>'
1 parent 9350912 commit 03ffbb9

7 files changed

+141
-76
lines changed

README.md

+12-4
Original file line numberDiff line numberDiff line change
@@ -825,21 +825,29 @@ Open a floating window with all loaded scopes. This buffer is not modifiable. So
825825

826826
- **Selection** (`<cr>`): open the [tags window](#tags-window) for the loaded scope under the cursor
827827
- **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
828+
- **Deletion** (`x`): reset the tags for the loaded scope under the cursor
829+
- **Toggle** (`<s-cr>`): toggle showing both loaded and unloaded scopes
829830
- **Go up** (`-`): navigate across to the [scopes window](#scopes-window)
830831
- **Help** (`?`): open the help window
831832

832833
**API**:
833834

834-
- `require("grapple").open_loaded()`
835-
- `require("grapple").toggle_loaded()`
835+
- `require("grapple").open_loaded(opts)`
836+
- `require("grapple").toggle_loaded(opts)`
837+
838+
**`opts?`**: `table`
839+
840+
- **`all`**: `boolean` (default: `false`)
836841

837842
<details>
838843
<summary><b>Examples</b></summary>
839844

840845
```lua
841-
-- Open the scopes window
846+
-- Open the loaded scopes window, show only loaded scopes
842847
require("grapple").open_loaded()
848+
849+
-- Open the loaded scopes window, show both loaded and unloaded scopes
850+
require("grapple").open_loaded({ all = true })
843851
```
844852

845853
</details>

lua/grapple.lua

+16-12
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,7 @@ function Grapple.reset(opts)
395395
return vim.notify(err, vim.log.levels.ERROR)
396396
end
397397

398-
vim.notify("Scope reset", vim.log.levels.INFO)
398+
vim.notify(string.format("Scope reset: %s", opts.scope or opts.id), vim.log.levels.INFO)
399399
end
400400

401401
---Create a user-defined scope
@@ -519,27 +519,30 @@ end
519519

520520
---Open a floating window populated with all defined scopes
521521
function Grapple.open_scopes()
522-
local App = require("grapple.app")
523522
local ScopeContent = require("grapple.scope_content")
524-
523+
local App = require("grapple.app")
525524
local app = App.get()
526-
local content = ScopeContent:new(app.scope_manager, app.settings.scope_hook, app.settings.scope_title)
525+
526+
local content = ScopeContent:new(app, app.settings.scope_hook, app.settings.scope_title)
527527

528528
open(content)
529529
end
530530

531531
---Toggle a floating window populated with all loaded scopes
532-
function Grapple.toggle_loaded()
533-
toggle(Grapple.open_loaded)
532+
---@param opts? { all: boolean }
533+
function Grapple.toggle_loaded(opts)
534+
toggle(Grapple.open_loaded, opts)
534535
end
535536

536537
---Open a floating window populated with all loaded scopes
537-
function Grapple.open_loaded()
538-
local App = require("grapple.app")
538+
---@param opts? { all: boolean }
539+
function Grapple.open_loaded(opts)
539540
local ContainerContent = require("grapple.container_content")
540-
541+
local App = require("grapple.app")
541542
local app = App.get()
542-
local content = ContainerContent:new(app.tag_manager, app.settings.loaded_hook, app.settings.loaded_title)
543+
544+
local show_all = opts and opts.all or false
545+
local content = ContainerContent:new(app, app.settings.loaded_hook, app.settings.loaded_title, show_all)
543546

544547
open(content)
545548
end
@@ -633,15 +636,15 @@ function Grapple.initialize()
633636
cycle = { args = { "direction" }, kwargs = use_kwargs },
634637
cycle_backward = { args = {}, kwargs = use_kwargs },
635638
cycle_forward = { args = {}, kwargs = use_kwargs },
636-
open_loaded = { args = {}, kwargs = {} },
639+
open_loaded = { args = {}, kwargs = { "all" } },
637640
open_scopes = { args = {}, kwargs = {} },
638641
open_tags = { args = {}, kwargs = window_kwargs },
639642
quickfix = { args = {}, kwargs = scope_kwargs },
640643
reset = { args = {}, kwargs = scope_kwargs },
641644
select = { args = {}, kwargs = use_kwargs },
642645
tag = { args = {}, kwargs = new_kwargs },
643646
toggle = { args = {}, kwargs = tag_kwargs },
644-
toggle_loaded = { args = {}, kwargs = {} },
647+
toggle_loaded = { args = {}, kwargs = { "all" } },
645648
toggle_scopes = { args = {}, kwargs = {} },
646649
toggle_tags = { args = {}, kwargs = window_kwargs },
647650
untag = { args = {}, kwargs = use_kwargs },
@@ -650,6 +653,7 @@ function Grapple.initialize()
650653

651654
-- Lookup table of arguments and their known values
652655
local argument_lookup = {
656+
all = { "true", "false" },
653657
direction = { "forward", "backward" },
654658
scope = Util.sort(vim.tbl_keys(app.scope_manager.scopes), Util.as_lower),
655659
style = Util.sort(vim.tbl_keys(app.settings.styles), Util.as_lower),

lua/grapple/container_actions.lua

+8
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ local ContainerActions = {}
55
---Provided by Window
66
---@field window grapple.window
77
---
8+
---Provided by ContainerContent
9+
---@field show_all boolean
10+
---
811
---User-provided information
912
---@field id? string
1013

@@ -19,6 +22,11 @@ function ContainerActions.reset(opts)
1922
opts.window:render()
2023
end
2124

25+
---@param opts grapple.action.container_options
26+
function ContainerActions.toggle_all(opts)
27+
require("grapple").open_loaded({ all = not opts.show_all })
28+
end
29+
2230
function ContainerActions.open_scopes()
2331
require("grapple").open_scopes()
2432
end

lua/grapple/container_content.lua

+75-36
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,24 @@ local Path = require("grapple.path")
22
local Util = require("grapple.util")
33

44
---@class grapple.container_content
5-
---@field tag_manager grapple.tag_manager
5+
---@field app grapple.app
66
---@field hook_fn grapple.hook_fn
77
---@field title_fn grapple.title_fn
8+
---@field show_all boolean
89
local ContainerContent = {}
910
ContainerContent.__index = ContainerContent
1011

11-
---@param tag_manager grapple.tag_manager
12+
---@param app grapple.app
1213
---@param hook_fn? grapple.hook_fn
1314
---@param title_fn? grapple.title_fn
15+
---@param show_all boolean
1416
---@return grapple.container_content
15-
function ContainerContent:new(tag_manager, hook_fn, title_fn)
17+
function ContainerContent:new(app, hook_fn, title_fn, show_all)
1618
return setmetatable({
17-
tag_manager = tag_manager,
19+
app = app,
1820
hook_fn = hook_fn,
1921
title_fn = title_fn,
22+
show_all = show_all,
2023
}, self)
2124
end
2225

@@ -69,34 +72,45 @@ function ContainerContent:sync(original, parsed) end
6972

7073
---@return grapple.window.entity[] | nil, string? error
7174
function ContainerContent:entities()
72-
local App = require("grapple.app")
73-
local app = App.get()
74-
75-
local current_scope, err = app:current_scope()
75+
local current_scope, err = self.app:current_scope()
7676
if not current_scope then
7777
return nil, err
7878
end
7979

80-
---@param cont_a grapple.tag_container
81-
---@param cont_b grapple.tag_container
82-
local function by_id(cont_a, cont_b)
83-
return string.lower(cont_a.id) < string.lower(cont_b.id)
80+
---@param item_a grapple.tag_container_item
81+
---@param item_b grapple.tag_container_item
82+
---@return boolean
83+
local function by_loaded_then_id(item_a, item_b)
84+
local loaded_a = item_a.loaded and 1 or 0
85+
local loaded_b = item_b.loaded and 1 or 0
86+
if loaded_a ~= loaded_b then
87+
return loaded_a > loaded_b
88+
else
89+
return string.lower(item_a.id) < string.lower(item_b.id)
90+
end
8491
end
8592

86-
---@type grapple.tag_container[]
87-
local containers = vim.tbl_values(self.tag_manager.containers)
88-
table.sort(containers, by_id)
93+
local container_list = self.app.tag_manager:list()
94+
table.sort(container_list, by_loaded_then_id)
8995

9096
local entities = {}
9197

92-
for _, container in ipairs(containers) do
98+
for _, item in ipairs(container_list) do
99+
if not self.show_all and not item.loaded then
100+
goto continue
101+
end
102+
93103
---@class grapple.container_content.entity
94104
local entity = {
95-
container = container,
96-
current = container.id == current_scope.id,
105+
id = item.id,
106+
container = item.container,
107+
loaded = item.loaded,
108+
current = item.id == current_scope.id,
97109
}
98110

99111
table.insert(entities, entity)
112+
113+
::continue::
100114
end
101115

102116
return entities, nil
@@ -106,51 +120,72 @@ end
106120
---@param index integer
107121
---@return grapple.window.entry
108122
function ContainerContent:create_entry(entity, index)
109-
local App = require("grapple.app")
110-
local app = App.get()
111-
112123
local container = entity.container
113124

114125
-- A string representation of the index
115126
local id = string.format("/%03d", index)
116127

117128
-- Don't try to modify IDs which are not paths, like "global"
118-
local rel_id
119-
if Path.is_absolute(container.id) then
120-
rel_id = vim.fn.fnamemodify(container.id, ":~")
129+
local container_id
130+
if Path.is_absolute(entity.id) then
131+
container_id = vim.fn.fnamemodify(entity.id, ":~")
121132
else
122-
rel_id = container.id
133+
container_id = entity.id
123134
end
124135

125136
-- In compliance with "grapple" syntax
126-
local line = string.format("%s %s", id, rel_id)
137+
local line = string.format("%s %s", id, container_id)
127138
local min_col = assert(string.find(line, "%s")) -- width of id
128139

140+
-- Define line highlights for line and extmarks
141+
---@type grapple.vim.highlight[]
142+
local highlights = {}
143+
129144
local sign_highlight
130-
if app.settings.status and entity.current then
145+
if self.app.settings.status and entity.current then
131146
sign_highlight = "GrappleCurrent"
147+
elseif not entity.loaded then
148+
sign_highlight = "GrappleHint"
149+
end
150+
151+
local loaded_highlight
152+
if not entity.loaded then
153+
local col_start, col_end = assert(string.find(line, container_id))
154+
loaded_highlight = {
155+
hl_group = "GrappleHint",
156+
line = index - 1,
157+
col_start = col_start - 1,
158+
col_end = col_end,
159+
}
132160
end
133161

162+
highlights = vim.tbl_filter(Util.not_nil, {
163+
loaded_highlight,
164+
})
165+
134166
-- Define line extmarks
135167
---@type grapple.vim.extmark[]
136168
local extmarks = {}
137169

138170
---@type grapple.vim.mark
139171
local sign_mark
140-
local quick_select = app.settings:quick_select()[index]
172+
local quick_select = self.app.settings:quick_select()[index]
141173
if quick_select then
142174
sign_mark = {
143175
sign_text = string.format("%s", quick_select),
144176
sign_hl_group = sign_highlight,
145177
}
146178
end
147179

148-
local count = container:len()
149-
local count_text = count == 1 and "tag" or "tags"
150-
local count_mark = {
151-
virt_text = { { string.format("[%d %s]", count, count_text) } },
152-
virt_text_pos = "eol",
153-
}
180+
local count_mark
181+
if container then
182+
local count = container:len()
183+
local count_text = count == 1 and "tag" or "tags"
184+
count_mark = {
185+
virt_text = { { string.format("[%d %s]", count, count_text) } },
186+
virt_text_pos = "eol",
187+
}
188+
end
154189

155190
extmarks = vim.tbl_filter(Util.not_nil, { sign_mark, count_mark })
156191
extmarks = vim.tbl_map(function(mark)
@@ -165,15 +200,15 @@ function ContainerContent:create_entry(entity, index)
165200
local entry = {
166201
---@class grapple.scope_content.data
167202
data = {
168-
id = container.id,
203+
id = entity.id,
169204
},
170205

171206
line = line,
172207
index = index,
173208
min_col = min_col,
174209

175210
---@type grapple.vim.highlight[]
176-
highlights = {},
211+
highlights = highlights,
177212

178213
---@type grapple.vim.extmark[]
179214
extmarks = extmarks,
@@ -202,6 +237,10 @@ end
202237
---@param opts? grapple.action.options
203238
---@return string? error
204239
function ContainerContent:perform(action, opts)
240+
opts = vim.tbl_extend("force", opts or {}, {
241+
show_all = self.show_all,
242+
})
243+
205244
return action(opts)
206245
end
207246

lua/grapple/scope_content.lua

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
local Util = require("grapple.util")
22

33
---@class grapple.scope_content
4-
---@field scope_manager grapple.scope_manager
4+
---@field app grapple.app
55
---@field hook_fn grapple.hook_fn
66
---@field title_fn grapple.title_fn
77
local ScopeContent = {}
88
ScopeContent.__index = ScopeContent
99

10-
---@param scope_manager grapple.scope_manager
10+
---@param app grapple.app
1111
---@param hook_fn? grapple.hook_fn
1212
---@param title_fn? grapple.title_fn
1313
---@return grapple.scope_content
14-
function ScopeContent:new(scope_manager, hook_fn, title_fn)
14+
function ScopeContent:new(app, hook_fn, title_fn)
1515
return setmetatable({
16-
scope_manager = scope_manager,
16+
app = app,
1717
hook_fn = hook_fn,
1818
title_fn = title_fn,
1919
}, self)
@@ -77,7 +77,7 @@ function ScopeContent:entities()
7777
return string.lower(scope_a.name) < string.lower(scope_b.name)
7878
end
7979

80-
local scopes = vim.tbl_values(self.scope_manager.scopes)
80+
local scopes = vim.tbl_values(self.app.scope_manager.scopes)
8181
table.sort(scopes, by_name)
8282

8383
local entities = {}

lua/grapple/settings.lua

+5
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,11 @@ local DEFAULT_SETTINGS = {
313313
end, { desc = string.format("Quick select %d", i) })
314314
end
315315

316+
-- Toggle
317+
window:map("n", "<s-cr>", function()
318+
window:perform_close(ContainerActions.toggle_all)
319+
end, { desc = "Toggle show all" })
320+
316321
-- Reset
317322
window:map("n", "x", function()
318323
local entry = window:current_entry()

0 commit comments

Comments
 (0)