fix(statuscolumn): show open folds in consecutive levels (#1534)

## Description
This uses `vim.treesitter.foldexpr` to get the start of a foldlevel,
because `vim.fn.foldlevel()` will return the same number in adjacent
foldlevels, thus making the open fold markers inconsistent.

This will however introduce a dependency on `vim.treesitter.foldexpr`
and I haven't seen any other computations based on `vim.treesitter`, so
maybe this is not desirable?

Unfortunately, I wasn't able to come up with a better solution, so feel
free to disregard.

PS: I came across #1445 and this PR reverts those changes I realize. But
I fail to find a good way for both to co-exist. I tried checking the
value with `vim.api.nvim_get_option_value("foldexpr", {})`, but this
will only work if `vim.opt.foldexpr` will be set directly into one of
the 2. If it's set to another function that is required like in LazyVim,
I'm not able to distinguish what value `foldexpr` is actually set to.

PS2: I'm guessing the same thing happens when `foldexpr` is set to
`vim.lsp.foldexpr` and the ideal would be some way to distinguish which
one is currently active and then use either
`vim.treesitter.foldexpr(lnum):sub(1, 1) == ">"` or
`vim.lsp.foldexpr(lnum):sub(1, 1) == ">"` respectively.
<!-- Describe the big picture of your changes to communicate to the
maintainers
  why we should accept this pull request. -->

## Related Issue(s)
Fixes #1533 
<!--
  If this PR fixes any issues, please link to the issue here.
  - Fixes #<issue_number>
-->

## Screenshots
Before 

![2025-03-08_18-19](https://github.com/user-attachments/assets/ec911497-3883-4a94-a607-7ce0f7021a8d)

After

![2025-03-08_18-20](https://github.com/user-attachments/assets/ed80f174-759a-41e5-b5af-9366e66a2d4a)


<!-- Add screenshots of the changes if applicable. -->

---------

Co-authored-by: Folke Lemaitre <folke.lemaitre@gmail.com>
This commit is contained in:
Iordanis Petkakis 2025-10-21 10:16:15 +03:00 committed by GitHub
parent cb0df73188
commit 7bcd3baaf8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -13,6 +13,52 @@ M.meta = {
needs_setup = true,
}
---@class snacks.statuscolumn.FoldInfo
---@field start number Line number where deepest fold starts
---@field level number Fold level, when zero other fields are N/A
---@field llevel number Lowest level that starts in v:lnum
---@field lines number Number of lines from v:lnum to end of closed fold
---@type ffi.namespace*
local C
local function _ffi()
if not C then
local ffi = require("ffi")
ffi.cdef([[
typedef struct {} Error;
typedef struct {} win_T;
typedef struct {
int start; // line number where deepest fold starts
int level; // fold level, when zero other fields are N/A
int llevel; // lowest level that starts in v:lnum
int lines; // number of lines from v:lnum to end of closed fold
} foldinfo_T;
foldinfo_T fold_info(win_T* wp, int lnum);
win_T *find_window_by_handle(int Window, Error *err);
]])
C = ffi.C
end
return C
end
-- Returns fold info for a given window and line number
---@param win number
---@param lnum number
local function fold_info(win, lnum)
pcall(_ffi)
if not C then
return
end
local ffi = require("ffi")
local err = ffi.new("Error")
local wp = C.find_window_by_handle(win, err)
if wp == nil then
return
end
return C.fold_info(wp, lnum) ---@type snacks.statuscolumn.FoldInfo
end
---@alias snacks.statuscolumn.Component "mark"|"sign"|"fold"|"git"
---@alias snacks.statuscolumn.Components snacks.statuscolumn.Component[]|fun(win:number,buf:number,lnum:number):snacks.statuscolumn.Component[]
@ -144,8 +190,11 @@ function M.line_signs(win, buf, lnum)
vim.api.nvim_win_call(win, function()
if vim.fn.foldclosed(lnum) >= 0 then
signs[#signs + 1] = { text = vim.opt.fillchars:get().foldclose or "", texthl = "Folded", type = "fold" }
elseif config.folds.open and vim.fn.foldlevel(lnum) > vim.fn.foldlevel(lnum - 1) then
signs[#signs + 1] = { text = vim.opt.fillchars:get().foldopen or "", type = "fold" }
elseif config.folds.open then
local info = fold_info(win, lnum)
if info and info.level > 0 and info.start == lnum then
signs[#signs + 1] = { text = vim.opt.fillchars:get().foldopen or "", type = "fold" }
end
end
end)