SmiteshP / nvim-navic

Simple winbar/statusline plugin that shows your current code context
Apache License 2.0
1.43k stars 50 forks source link

Feature suggestion: Provide a way to get actual display width of the string returned by `get_location()` #148

Open SirWrexes opened 4 months ago

SirWrexes commented 4 months ago

First of, very nifty little plugin, it pairs wonderfully with nvim-treesitter-context and they complement each other well~

However I have one small grudge with it at the moment. I have set my config to display a thin line as my statusline using fillchars, since I prefer using winbar Untitled Here's the config in question, I put it in my plugin spec for coloful-winsep.nvim because I thought that's how it made the most sense, given the nature of said plugin.

---@type LazyPluginSpec
local splitborders = {
  "nvim-zh/colorful-winsep.nvim",

  event = "WinLeave",

  init = function()
    -- Make the horizontal split separator be a thin straight line
    -- We override whatever has been set in order to make it look nice with this plugin.
    -- Did you set fillchars somewhere else but they kept getting overridden and you have been looking for minutes,
    -- hours, days, years even? You have come to the right place and I'm sorry.
    vim.opt.fillchars:append { stl = "─", stlnc = "─"}

    -- Couldn't find a way to do this in "pure lua"
    vim.cmd [[highlight! link StatusLine WinSeparator]]
  end,

  config = true,
}

return splitborders

Problem is: Using fillchars=...stl:─,stlnc:─ with nvim-navic ends up being very ugly and hardly legible. image

To fix this issue, I've made it so my hook to attach nvim-navic to a buffer overrides these values again with a whitespace if the LSP supports documentSymbolProvider and thus navic gets required and attached... But! I would like to keep the thin line after the displayed location so I also made the hook build a """simple""" status line with 1. location, 2. a repeated sequence of whatever the fillchar originally was:

---@type table<string, LspHookFunction>
local hooks = {
  format_on_save = function(client, buffer)
    if client.supports_method "textDocument/formatting" then
      vim.api.nvim_create_autocmd("BufWritePre", {
        group = vim.api.nvim_create_augroup("FormatOnSave", {}),
        buffer = buffer,
        callback = function()
          -- TODO: Find some better way than using _G.
          --       Sadly, I couldn't get it to work yet.
          --       Probably some module with a local state variable + a getter/setter pair ?
          --
          -- selene: allow(global_usage)
          _G["PLF_SHOULD_OPEN_PICKER"] = false
          require("plf").format {
            async = false,
          }
          -- selene: allow(global_usage)
          _G["PLF_SHOULD_OPEN_PICKER"] = true
        end,
      })
    end
  end,

  statusline_context = function(client, buffer)
    if client.server_capabilities.documentSymbolProvider then
      require("nvim-navic").attach(client, buffer)

      local location = "v:lua.require'nvim-navic'.get_location()"
      local locationlen = string.format("len(%s)", location)

      local fillchars = vim.opt.fillchars:get()
      local filling = fillchars.stl or fillchars.stlnc or "─"
      local fillerlen = string.format("%d - %s", vim.o.columns - 1, locationlen)
      local filler = string.format("repeat('%s', %s)", filling, fillerlen)

      local status = "%#Title#%{%" .. location .. "%}"
      .. " %#StatusLine#%{"
      .. filler
      .. "}"

      vim.opt.fillchars:remove { "stl", "stlnc" }
      vim.o.statusline = status
    end
  end,
}

---@type LspHookFunction
local function default_on_attach(client, buffer)
  vim
    .iter(pairs(hooks))
    :each(function(_, on_attach) on_attach(client, buffer) end)
end

return default_on_attach

image Great, that looks fantastic, I am very pleased! Here's the catch, though: VimScript len() or lua # operator both return the number of bytes in the string, and it doesn't correspond to the actual number of columns the string takes: it's bigger. For instance, when nvim-navic says 󰊕 get_fillchar, doing :lua vim.print(#require'nvim-navic'.get_location()) or echo len(v:lua.require'nvim-navic'.get_location()) respond with 17, whereas the actual column width is in fact 14.

The result is this: Untitled The bar gets shrunk noticeably.

It would be nice if we could do something like

vim.o.statusline =
  [[%{%v:lua.require 'nvim-navic'.get_location()} %{repeat('─',]]
    .. vim.o.columns - 1
    .. [[- v:lua.require'nvim-navic'.get_location_width())}]]

I think it can make integrations more easy, and surely be a welcome feature for all hackers like me out there who like to style their UI manually. Thank you for your time (and the plugin) ! :heart:

SirWrexes commented 4 months ago

Edited to reflect changes from wrex-dots/nvim@5f1b4fdd8397bfb47eea408024ddc1035df722c5.