Skip to content

Commit

Permalink
feat: toggle showing loaded and unloaded scopes in UI with '<s-cr>'
Browse files Browse the repository at this point in the history
  • Loading branch information
cbochs committed Mar 21, 2024
1 parent 9350912 commit 03ffbb9
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 76 deletions.
16 changes: 12 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -825,21 +825,29 @@ Open a floating window with all loaded scopes. This buffer is not modifiable. So

- **Selection** (`<cr>`): open the [tags window](#tags-window) for the loaded scope under the cursor
- **Quick select** (default: `1-9`): open tags window for the loaded scope at a given index
- **Deletion (`x`)**: reset the tags for the loaded scope under the cursor
- **Deletion** (`x`): reset the tags for the loaded scope under the cursor
- **Toggle** (`<s-cr>`): toggle showing both loaded and unloaded scopes
- **Go up** (`-`): navigate across to the [scopes window](#scopes-window)
- **Help** (`?`): open the help window

**API**:

- `require("grapple").open_loaded()`
- `require("grapple").toggle_loaded()`
- `require("grapple").open_loaded(opts)`
- `require("grapple").toggle_loaded(opts)`

**`opts?`**: `table`

- **`all`**: `boolean` (default: `false`)

<details>
<summary><b>Examples</b></summary>

```lua
-- Open the scopes window
-- Open the loaded scopes window, show only loaded scopes
require("grapple").open_loaded()

-- Open the loaded scopes window, show both loaded and unloaded scopes
require("grapple").open_loaded({ all = true })
```

</details>
Expand Down
28 changes: 16 additions & 12 deletions lua/grapple.lua
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,7 @@ function Grapple.reset(opts)
return vim.notify(err, vim.log.levels.ERROR)
end

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

---Create a user-defined scope
Expand Down Expand Up @@ -519,27 +519,30 @@ end

---Open a floating window populated with all defined scopes
function Grapple.open_scopes()
local App = require("grapple.app")
local ScopeContent = require("grapple.scope_content")

local App = require("grapple.app")
local app = App.get()
local content = ScopeContent:new(app.scope_manager, app.settings.scope_hook, app.settings.scope_title)

local content = ScopeContent:new(app, app.settings.scope_hook, app.settings.scope_title)

open(content)
end

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

---Open a floating window populated with all loaded scopes
function Grapple.open_loaded()
local App = require("grapple.app")
---@param opts? { all: boolean }
function Grapple.open_loaded(opts)
local ContainerContent = require("grapple.container_content")

local App = require("grapple.app")
local app = App.get()
local content = ContainerContent:new(app.tag_manager, app.settings.loaded_hook, app.settings.loaded_title)

local show_all = opts and opts.all or false
local content = ContainerContent:new(app, app.settings.loaded_hook, app.settings.loaded_title, show_all)

open(content)
end
Expand Down Expand Up @@ -633,15 +636,15 @@ function Grapple.initialize()
cycle = { args = { "direction" }, kwargs = use_kwargs },
cycle_backward = { args = {}, kwargs = use_kwargs },
cycle_forward = { args = {}, kwargs = use_kwargs },
open_loaded = { args = {}, kwargs = {} },
open_loaded = { args = {}, kwargs = { "all" } },
open_scopes = { args = {}, kwargs = {} },
open_tags = { args = {}, kwargs = window_kwargs },
quickfix = { args = {}, kwargs = scope_kwargs },
reset = { args = {}, kwargs = scope_kwargs },
select = { args = {}, kwargs = use_kwargs },
tag = { args = {}, kwargs = new_kwargs },
toggle = { args = {}, kwargs = tag_kwargs },
toggle_loaded = { args = {}, kwargs = {} },
toggle_loaded = { args = {}, kwargs = { "all" } },
toggle_scopes = { args = {}, kwargs = {} },
toggle_tags = { args = {}, kwargs = window_kwargs },
untag = { args = {}, kwargs = use_kwargs },
Expand All @@ -650,6 +653,7 @@ function Grapple.initialize()

-- Lookup table of arguments and their known values
local argument_lookup = {
all = { "true", "false" },
direction = { "forward", "backward" },
scope = Util.sort(vim.tbl_keys(app.scope_manager.scopes), Util.as_lower),
style = Util.sort(vim.tbl_keys(app.settings.styles), Util.as_lower),
Expand Down
8 changes: 8 additions & 0 deletions lua/grapple/container_actions.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ local ContainerActions = {}
---Provided by Window
---@field window grapple.window
---
---Provided by ContainerContent
---@field show_all boolean
---
---User-provided information
---@field id? string

Expand All @@ -19,6 +22,11 @@ function ContainerActions.reset(opts)
opts.window:render()
end

---@param opts grapple.action.container_options
function ContainerActions.toggle_all(opts)
require("grapple").open_loaded({ all = not opts.show_all })
end

function ContainerActions.open_scopes()
require("grapple").open_scopes()
end
Expand Down
111 changes: 75 additions & 36 deletions lua/grapple/container_content.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,24 @@ local Path = require("grapple.path")
local Util = require("grapple.util")

---@class grapple.container_content
---@field tag_manager grapple.tag_manager
---@field app grapple.app
---@field hook_fn grapple.hook_fn
---@field title_fn grapple.title_fn
---@field show_all boolean
local ContainerContent = {}
ContainerContent.__index = ContainerContent

---@param tag_manager grapple.tag_manager
---@param app grapple.app
---@param hook_fn? grapple.hook_fn
---@param title_fn? grapple.title_fn
---@param show_all boolean
---@return grapple.container_content
function ContainerContent:new(tag_manager, hook_fn, title_fn)
function ContainerContent:new(app, hook_fn, title_fn, show_all)
return setmetatable({
tag_manager = tag_manager,
app = app,
hook_fn = hook_fn,
title_fn = title_fn,
show_all = show_all,
}, self)
end

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

---@return grapple.window.entity[] | nil, string? error
function ContainerContent:entities()
local App = require("grapple.app")
local app = App.get()

local current_scope, err = app:current_scope()
local current_scope, err = self.app:current_scope()
if not current_scope then
return nil, err
end

---@param cont_a grapple.tag_container
---@param cont_b grapple.tag_container
local function by_id(cont_a, cont_b)
return string.lower(cont_a.id) < string.lower(cont_b.id)
---@param item_a grapple.tag_container_item
---@param item_b grapple.tag_container_item
---@return boolean
local function by_loaded_then_id(item_a, item_b)
local loaded_a = item_a.loaded and 1 or 0
local loaded_b = item_b.loaded and 1 or 0
if loaded_a ~= loaded_b then
return loaded_a > loaded_b
else
return string.lower(item_a.id) < string.lower(item_b.id)
end
end

---@type grapple.tag_container[]
local containers = vim.tbl_values(self.tag_manager.containers)
table.sort(containers, by_id)
local container_list = self.app.tag_manager:list()
table.sort(container_list, by_loaded_then_id)

local entities = {}

for _, container in ipairs(containers) do
for _, item in ipairs(container_list) do
if not self.show_all and not item.loaded then
goto continue
end

---@class grapple.container_content.entity
local entity = {
container = container,
current = container.id == current_scope.id,
id = item.id,
container = item.container,
loaded = item.loaded,
current = item.id == current_scope.id,
}

table.insert(entities, entity)

::continue::
end

return entities, nil
Expand All @@ -106,51 +120,72 @@ end
---@param index integer
---@return grapple.window.entry
function ContainerContent:create_entry(entity, index)
local App = require("grapple.app")
local app = App.get()

local container = entity.container

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

-- Don't try to modify IDs which are not paths, like "global"
local rel_id
if Path.is_absolute(container.id) then
rel_id = vim.fn.fnamemodify(container.id, ":~")
local container_id
if Path.is_absolute(entity.id) then
container_id = vim.fn.fnamemodify(entity.id, ":~")
else
rel_id = container.id
container_id = entity.id
end

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

-- Define line highlights for line and extmarks
---@type grapple.vim.highlight[]
local highlights = {}

local sign_highlight
if app.settings.status and entity.current then
if self.app.settings.status and entity.current then
sign_highlight = "GrappleCurrent"
elseif not entity.loaded then
sign_highlight = "GrappleHint"
end

local loaded_highlight
if not entity.loaded then
local col_start, col_end = assert(string.find(line, container_id))
loaded_highlight = {
hl_group = "GrappleHint",
line = index - 1,
col_start = col_start - 1,
col_end = col_end,
}
end

highlights = vim.tbl_filter(Util.not_nil, {
loaded_highlight,
})

-- Define line extmarks
---@type grapple.vim.extmark[]
local extmarks = {}

---@type grapple.vim.mark
local sign_mark
local quick_select = app.settings:quick_select()[index]
local quick_select = self.app.settings:quick_select()[index]
if quick_select then
sign_mark = {
sign_text = string.format("%s", quick_select),
sign_hl_group = sign_highlight,
}
end

local count = container:len()
local count_text = count == 1 and "tag" or "tags"
local count_mark = {
virt_text = { { string.format("[%d %s]", count, count_text) } },
virt_text_pos = "eol",
}
local count_mark
if container then
local count = container:len()
local count_text = count == 1 and "tag" or "tags"
count_mark = {
virt_text = { { string.format("[%d %s]", count, count_text) } },
virt_text_pos = "eol",
}
end

extmarks = vim.tbl_filter(Util.not_nil, { sign_mark, count_mark })
extmarks = vim.tbl_map(function(mark)
Expand All @@ -165,15 +200,15 @@ function ContainerContent:create_entry(entity, index)
local entry = {
---@class grapple.scope_content.data
data = {
id = container.id,
id = entity.id,
},

line = line,
index = index,
min_col = min_col,

---@type grapple.vim.highlight[]
highlights = {},
highlights = highlights,

---@type grapple.vim.extmark[]
extmarks = extmarks,
Expand Down Expand Up @@ -202,6 +237,10 @@ end
---@param opts? grapple.action.options
---@return string? error
function ContainerContent:perform(action, opts)
opts = vim.tbl_extend("force", opts or {}, {
show_all = self.show_all,
})

return action(opts)
end

Expand Down
10 changes: 5 additions & 5 deletions lua/grapple/scope_content.lua
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
local Util = require("grapple.util")

---@class grapple.scope_content
---@field scope_manager grapple.scope_manager
---@field app grapple.app
---@field hook_fn grapple.hook_fn
---@field title_fn grapple.title_fn
local ScopeContent = {}
ScopeContent.__index = ScopeContent

---@param scope_manager grapple.scope_manager
---@param app grapple.app
---@param hook_fn? grapple.hook_fn
---@param title_fn? grapple.title_fn
---@return grapple.scope_content
function ScopeContent:new(scope_manager, hook_fn, title_fn)
function ScopeContent:new(app, hook_fn, title_fn)
return setmetatable({
scope_manager = scope_manager,
app = app,
hook_fn = hook_fn,
title_fn = title_fn,
}, self)
Expand Down Expand Up @@ -77,7 +77,7 @@ function ScopeContent:entities()
return string.lower(scope_a.name) < string.lower(scope_b.name)
end

local scopes = vim.tbl_values(self.scope_manager.scopes)
local scopes = vim.tbl_values(self.app.scope_manager.scopes)
table.sort(scopes, by_name)

local entities = {}
Expand Down
5 changes: 5 additions & 0 deletions lua/grapple/settings.lua
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,11 @@ local DEFAULT_SETTINGS = {
end, { desc = string.format("Quick select %d", i) })
end

-- Toggle
window:map("n", "<s-cr>", function()
window:perform_close(ContainerActions.toggle_all)
end, { desc = "Toggle show all" })

-- Reset
window:map("n", "x", function()
local entry = window:current_entry()
Expand Down
Loading

0 comments on commit 03ffbb9

Please sign in to comment.