Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions lua/smart-motion/actions/history_browse.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
--- Triggered by `g.`: browse all history entries, pick one to jump back to.
--- Uses nui.nvim for proper split layout if available, falls back to simple popup.
local consts = require("smart-motion.consts")
local utils = require("smart-motion.utils")

local M = {}

Expand Down Expand Up @@ -656,6 +657,9 @@ function M.run()

-- Mount layout
layout:mount()
for _, popup in ipairs({ header_popup, preview_popup, list_popup, hint_popup }) do
utils.mark_floating_window(popup.winid)
end
refresh()

-- Cleanup
Expand Down
41 changes: 33 additions & 8 deletions lua/smart-motion/collectors/lines.lua
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,39 @@ function M.run()
local start_line = math.max(0, cursor_line - window_size)
local end_line = math.min(total_lines - 1, cursor_line + window_size)

for line_number = start_line, end_line do
local line = vim.api.nvim_buf_get_lines(ctx.bufnr, line_number, line_number + 1, false)[1]

if line then
coroutine.yield({
line_number = line_number,
text = line,
})
local line_number = start_line
while line_number <= end_line do
-- Handle closed folds to match Neovim's native j/k behavior
-- where a fold counts as a single line but is still jumpable
-- foldclosed() returns -1 if line is not in a fold, otherwise returns the first line of the fold
local fold_start = vim.fn.foldclosed(line_number + 1)
if fold_start ~= -1 then
-- Line is inside a closed fold
-- Check if this is the FIRST line of the fold
if fold_start == line_number + 1 then
-- This is the first line of the fold, yield it so we can jump to it
local line = vim.api.nvim_buf_get_lines(ctx.bufnr, line_number, line_number + 1, false)[1]
if line then
coroutine.yield({
line_number = line_number,
text = line,
})
end
end
-- Skip to the line after the fold (whether we yielded it or not)
-- foldclosedend() returns the 1-based line number of the last line in the fold
line_number = vim.fn.foldclosedend(line_number + 1)
else
-- Line is visible (not in a closed fold), collect it
local line = vim.api.nvim_buf_get_lines(ctx.bufnr, line_number, line_number + 1, false)[1]

if line then
coroutine.yield({
line_number = line_number,
text = line,
})
end
line_number = line_number + 1
end
end
end)
Expand Down
19 changes: 16 additions & 3 deletions lua/smart-motion/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,23 @@ local history = require("smart-motion.core.history")
local exit = require("smart-motion.core.events.exit")

local EXIT_TYPE = consts.EXIT_TYPE
local FLOAT_WINDOW_OWNER_VAR = "smart_motion_owned"

local M = {}

--- Closes all diagnostic and completion floating windows.
--- Marks a window as being owned by smart-motion.
---@param winid integer
function M.mark_floating_window(winid)
if not vim.api.nvim_win_is_valid(winid) then
return
end

pcall(vim.api.nvim_win_set_var, winid, FLOAT_WINDOW_OWNER_VAR, true)
end

--- Closes floating windows created by smart-motion.
function M.close_floating_windows()
log.debug("Closing floating windows (diagnostics & completion)")
log.debug("Closing smart-motion floating windows")

for _, winid in ipairs(vim.api.nvim_list_wins()) do
local ok, win_config = pcall(vim.api.nvim_win_get_config, winid)
Expand All @@ -25,7 +36,9 @@ function M.close_floating_windows()
goto continue
end

if vim.tbl_contains({ "cursor", "win" }, win_config.relative) then
local has_owner_var, owned = pcall(vim.api.nvim_win_get_var, winid, FLOAT_WINDOW_OWNER_VAR)

if has_owner_var and owned and win_config.relative ~= "" then
local success, err = pcall(vim.api.nvim_win_close, winid, true)

if not success then
Expand Down
38 changes: 38 additions & 0 deletions tests/test_utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,44 @@ T["prepare_motion"]["returns ctx, cfg, and motion_state"] = function()
expect.equality(ms.jump_target_count, 0)
end

-- =============================================================================
-- close_floating_windows
-- =============================================================================

T["close_floating_windows"] = MiniTest.new_set()

T["close_floating_windows"]["closes only smart-motion owned floating windows"] = function()
helpers.create_buf({ "test" })

local utils = require("smart-motion.utils")
local owned_buf = vim.api.nvim_create_buf(false, true)
local foreign_buf = vim.api.nvim_create_buf(false, true)

local owned_win = vim.api.nvim_open_win(owned_buf, false, {
relative = "editor",
row = 1,
col = 1,
width = 10,
height = 1,
style = "minimal",
})
local foreign_win = vim.api.nvim_open_win(foreign_buf, false, {
relative = "editor",
row = 3,
col = 1,
width = 10,
height = 1,
style = "minimal",
})

vim.api.nvim_win_set_var(owned_win, "smart_motion_owned", true)

utils.close_floating_windows()

expect.equality(vim.api.nvim_win_is_valid(owned_win), false)
expect.equality(vim.api.nvim_win_is_valid(foreign_win), true)
end

-- =============================================================================
-- module_wrapper
-- =============================================================================
Expand Down