echasnovski / mini.nvim

Library of 40+ independent Lua modules improving overall Neovim (version 0.8 and higher) experience with minimal effort
MIT License
5.02k stars 185 forks source link

mini.clue: dynamic width normalization #855

Closed wroyca closed 4 months ago

wroyca commented 5 months ago

Contributing guidelines

Module(s)

clue

Description

Hello o/

When displaying clues with varying line widths, the float width is determined by the widest line, which is far too aggressive. One solution I'd like to propose here would be to calculate a width suitable to comfortably display "most" lines while truncating the excessively wide ones.

Example (late night pseudo code, don't use):

H.buffer_get_width = function()
  if not H.is_valid_buf(H.state.buf_id) then return end

  local lines = vim.api.nvim_buf_get_lines(H.state.buf_id, 0, -1, false)
  local lines_count = #lines
  local lines_width_total = 0

  for _, l in ipairs(lines) do
    lines_width_total = lines_width_total + vim.fn.strdisplaywidth(l)
  end

  local lines_width_average = lines_width_total / lines_count

  table.sort(lines, function(a, b)
    return vim.fn.strdisplaywidth(a) < vim.fn.strdisplaywidth(b)
  end)

  local lines_width = 0
  local lines_width_cumulative = 0
  local lines_width_factor = 2

  -- (too tired, revisit properly with coffee)
  for i, l in ipairs(lines) do
    lines_width_cumulative = lines_width_cumulative + vim.fn.strdisplaywidth(l)
    if lines_width_cumulative <= lines_width_average * lines_width_factor then
      lines_width = i
    else
      break
    end
  end

  local res = 0
  for i = 1, lines_width do
    res = res + vim.fn.strdisplaywidth(lines[i])
  end

  print (res) -- debug
  return res
end

Before:

image image

After:

image

image

And more:

Screenshot from 2024-05-04 20-59-33

echasnovski commented 5 months ago

Thanks for the suggestion!

There is already a functionality for users to tweak window config to their liking, as config.window.config can be callable.

The main it seems to be one or two really long lines which make automated width too large. Here is one approach at dynamic width that I find reasonable compromise:

local compute_dynamic_width = function(buf_id)
  local max_width = 0.4 * vim.o.columns
  local widths = vim.tbl_map(vim.fn.strdisplaywidth, vim.api.nvim_buf_get_lines(buf_id, 0, -1, false))
  table.sort(widths)
  for i = #widths, 1, -1 do
    if widths[i] <= max_width then return widths[i] end
  end

  return max_width
end

local win_config = function(buf_id) return { width = compute_dynamic_width(buf_id) } end

local miniclue = require('mini.clue')
miniclue.setup({
  -- Clues and triggers setup
  window = { config = win_config },
})

It has user-defined maximum width (as a share of current Neovim instance width) and picks the window width so as to fit the largest present line below that threshold (if there is one).


My initial reaction was to leave it as possible configuration, but I think I might change my mind about somehow adding this type of behavior directly in 'mini.clue' (maybe by allowing fractional width?).

I'll think about it.

echasnovski commented 4 months ago

Sorry, but I think leaving it for a manual configuration is a better solution in the long term. Maybe if/when there is a wiki with all the suggested custom solutions, they will be easier to find.

Besides, all those descriptions can be adjusted manually with set_mapping_desc() and the original culprit of those long built-in descriptions was solved before 0.10 made stable release.

Closing as not planned.