diff --git a/lua/fidget/buf.lua b/lua/fidget/buf.lua new file mode 100644 index 0000000..f11f759 --- /dev/null +++ b/lua/fidget/buf.lua @@ -0,0 +1,156 @@ +---@class fidget.buf +local M = {} + +---@class fidget.buf.BuilderOpts + +---@class fidget.buf.Builder +---@field lines string[] +---@field row number +---@field col number +---@field opts fidget.buf.BuilderOpts +---@field hls table[] +local Builder = {} +Builder.__index = Builder + +---Write content +---@param text string +---@param hl_group? string | number | vim.api.keyset.highlight +---@return fidget.buf.Builder +function Builder:write(text, hl_group) + local row, col = self.row, self.col + local parts = vim.split(text, "\n", { plain = true }) + + for i, part in ipairs(parts) do + local line = self.lines[row + 1] or "" + self.lines[row + 1] = line .. part + col = col + #part + + -- new line + if i < #parts then + row = row + 1 + col = 0 + end + end + + if hl_group then + table.insert(self.hls, { + row = self.row, + col = self.col, + end_row = row, + end_col = col, + hl_group = hl_group, + }) + end + + self.row = row + self.col = col + + return self +end + +---Write new line. +---@param n? number +---@return fidget.buf.Builder +function Builder:ln(n) + n = n or 1 + for _ = 1, n do + -- check first line is empty, because row is 0-base, self.lines is a 1-base array + if self.row == 0 and self.lines[1] == nil then + self.lines[1] = "" + end + + self.row = self.row + 1 + self.lines[self.row + 1] = "" + end + self.col = 0 + + return self +end + +---Write {n} spaces +---@param n? number +---@return fidget.buf.Builder +function Builder:space(n) + return self:write(string.rep(" ", n or 1)) +end + +function Builder:separator(sep, hl_group) + sep = sep or require("config.icons").icons.sep + hl_group = hl_group or "WinSeparator" + + if self.col ~= 0 then + -- new line + self:ln() + end + local row = self.row + + table.insert(self.hls, function(bufnr, ns) + vim.api.nvim_buf_set_extmark(bufnr, ns, row, 0, { + virt_text = { { sep:rep(200), hl_group } }, + virt_text_pos = "overlay", + virt_text_win_col = 0, + }) + end) + + self:ln() + + return self +end + +---Render content, set extmarks. +---@param bufnr number +---@param ns? number namespace +function Builder:render(bufnr, ns) + if #self.lines == 0 then + return + end + + ns = ns + or vim.api.nvim_create_namespace(string.format("content_builder_%s", bufnr)) + + vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, self.lines) + + local id = 0 + local set_extmark = function(hl) + if type(hl) == "function" then + hl(bufnr, ns) + + return + end + + local hl_name = hl.hl_group + if type(hl_name) == "table" then + hl_name = vim.api.nvim_set_hl( + ns, + string.format("NS_%s_HL_%s", ns, id), + hl.hl_group + ) + id = id + 1 + end + + vim.api.nvim_buf_set_extmark(bufnr, ns, hl.row, hl.col, { + end_row = hl.end_row, + end_col = hl.end_col, + hl_group = hl_name, + }) + end + + for _, hl in ipairs(self.hls) do + set_extmark(hl) + end +end + +---Create a buf content builder. +---@param opts? fidget.buf.BuilderOpts +---@return fidget.buf.Builder +function M.new_builder(opts) + return setmetatable({ + row = 0, + col = 0, + lines = {}, + hls = {}, + opts = opts, + }, Builder) +end + +return M diff --git a/lua/telescope/_extensions/fidget.lua b/lua/telescope/_extensions/fidget.lua index e886030..c918aae 100644 --- a/lua/telescope/_extensions/fidget.lua +++ b/lua/telescope/_extensions/fidget.lua @@ -7,6 +7,7 @@ local notification = require("fidget.notification") local pickers = require("telescope.pickers") local telescope = require("telescope") local previewers = require("telescope.previewers") +local buf = require("fidget.buf") --- Format HistoryItem, used in Telescope or Neovim messages. --- @@ -36,56 +37,22 @@ local format_entry = function(entry) return chunks end -local notification_previewer = function() +local function previewer() return previewers.new_buffer_previewer({ title = "Notification Details", - define_preview = function(self, entry, _) - local notification_entry = entry.value + define_preview = function(self, entry) + local data = entry.value local bufnr = self.state.bufnr - vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, {}) - - local lines = { - "Timestamp: " .. vim.fn.strftime("%c", notification_entry.last_updated), - "Group: " .. (notification_entry.group_name or ""), - "Annotation: " .. (notification_entry.annote or ""), - "Style: " .. (notification_entry.style or ""), - "", - "Message:", - "--------", - "", - } - - local message_lines = vim.split(notification_entry.message, "\n") - for _, line in ipairs(message_lines) do - table.insert(lines, line) - end - - vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines) - - vim.api.nvim_buf_set_option(bufnr, "filetype", "markdown") - - local ns_id = vim.api.nvim_create_namespace("fidget_preview") - - vim.api.nvim_buf_add_highlight(bufnr, ns_id, "Title", 0, 0, 10) - vim.api.nvim_buf_add_highlight(bufnr, ns_id, "Title", 1, 0, 6) - vim.api.nvim_buf_add_highlight(bufnr, ns_id, "Title", 2, 0, 11) - vim.api.nvim_buf_add_highlight(bufnr, ns_id, "Title", 3, 0, 6) - - vim.api.nvim_buf_add_highlight(bufnr, ns_id, "Identifier", 0, 11, -1) - vim.api.nvim_buf_add_highlight(bufnr, ns_id, "Special", 1, 7, -1) - vim.api.nvim_buf_add_highlight(bufnr, ns_id, "String", 2, 12, -1) - vim.api.nvim_buf_add_highlight(bufnr, ns_id, "Comment", 3, 7, -1) - - vim.api.nvim_buf_add_highlight(bufnr, ns_id, "Title", 5, 0, -1) - vim.api.nvim_buf_add_highlight(bufnr, ns_id, "Comment", 6, 0, -1) - - if notification_entry.style then - local hl_group = notification_entry.style - for i = 8, #lines do - vim.api.nvim_buf_add_highlight(bufnr, ns_id, hl_group, i - 1, 0, -1) - end - end + buf + .new_builder() + :write(string.format(" %s ", data.annote or " "), data.style) + :write(vim.fn.strftime("%c", data.last_updated), "Comment") + :space(1) + :write(data.group_name or "", "Special") + :separator() + :write(data.message or "") + :render(bufnr) end, }) end @@ -124,29 +91,24 @@ local create_entry_maker = function(wrap) end end -local fidget_picker = function(opts) - opts = opts or {} - - local default_config = { - wrap_text = false, - use_previewer = true, - } +local default_config = { + wrap_text = false, + previewer = true, +} - local config = vim.tbl_deep_extend("force", default_config, opts) +local fidget_picker = function(opts) + opts = vim.tbl_extend("force", default_config, opts or {}) local picker_opts = { prompt_title = "Notifications", finder = finders.new_table({ results = notification.get_history(), - entry_maker = create_entry_maker(config.wrap_text), + entry_maker = create_entry_maker(opts.wrap_text), }), sorter = conf.generic_sorter(opts), + previewer = previewer(), } - if config.use_previewer then - picker_opts.previewer = notification_previewer() - end - picker_opts.attach_mappings = function(prompt_bufnr, _) actions.select_default:replace(function() actions.close(prompt_bufnr) @@ -179,16 +141,9 @@ end return telescope.register_extension({ setup = function(ext_config) - _G.__fidget_telescope_config = ext_config or {} + default_config = vim.tbl_extend("force", default_config, ext_config or {}) end, exports = { - fidget = function(opts) - local config = vim.tbl_deep_extend( - "force", - _G.__fidget_telescope_config or {}, - opts or {} - ) - fidget_picker(config) - end, + fidget = fidget_picker, }, })