diff --git a/lua/trouble/config/init.lua b/lua/trouble/config/init.lua index 493fa429..190d0524 100644 --- a/lua/trouble/config/init.lua +++ b/lua/trouble/config/init.lua @@ -90,11 +90,22 @@ local defaults = { zi = "fold_toggle_enable", gb = { -- example of a custom action that toggles the active view filter action = function(view) - view.state.filter_buffer = not view.state.filter_buffer - view:filter(view.state.filter_buffer and { buf = 0 } or nil) + view:filter({ buf = 0 }, { toggle = true }) end, desc = "Toggle Current Buffer Filter", }, + s = { -- example of a custom action that toggles the severity + action = function(view) + local f = view:get_filter("severity") + local severity = ((f and f.filter.severity or 0) + 1) % 5 + view:filter({ severity = severity }, { + id = "severity", + template = "{hl:Title}Filter:{hl} {severity}", + del = severity == 0, + }) + end, + desc = "Toggle Severity Filter", + }, }, ---@type table modes = { diff --git a/lua/trouble/filter.lua b/lua/trouble/filter.lua index ed1d011a..6aed7dab 100644 --- a/lua/trouble/filter.lua +++ b/lua/trouble/filter.lua @@ -1,5 +1,20 @@ +local Util = require("trouble.util") + local M = {} +---@class trouble.ViewFilter.opts +---@field id? string +---@field template? string +---@field data? table +---@field toggle? boolean +---@field del? boolean + +---@class trouble.ViewFilter +---@field id string +---@field filter trouble.Filter +---@field template? string +---@field data? table + ---@param opts? {lines:boolean} ---@param range trouble.Range ---@param pos trouble.Pos @@ -62,6 +77,15 @@ M.filters = { ---@param filter trouble.Filter ---@param ctx trouble.Filter.ctx function M.is(item, filter, ctx) + if type(filter) == "table" and Util.islist(filter) then + for _, f in ipairs(filter) do + if not M.is(item, f, ctx) then + return false + end + end + return true + end + filter = type(filter) == "table" and filter or { filter } for k, v in pairs(filter) do ---@type trouble.FilterFn? diff --git a/lua/trouble/format.lua b/lua/trouble/format.lua index baf5fb26..b3abab53 100644 --- a/lua/trouble/format.lua +++ b/lua/trouble/format.lua @@ -12,6 +12,9 @@ local M = {} ---@param source string ---@param field string function M.default_hl(source, field) + if not source then + return "Trouble" .. Util.camel(field) + end local key = source .. field local value = Cache.default_hl[key] if value then @@ -104,6 +107,12 @@ M.formatters = { text = vim.fn.fnamemodify(ctx.item.dirname, ":p:~:."), } end, + filter = function(ctx) + return { + text = vim.inspect(ctx.item.filter):gsub("%s+", " "), + hl = "ts.lua", + } + end, kind_icon = function(ctx) if not ctx.item.kind then return diff --git a/lua/trouble/sources/lsp.lua b/lua/trouble/sources/lsp.lua index c0a8a99c..58a44dfe 100644 --- a/lua/trouble/sources/lsp.lua +++ b/lua/trouble/sources/lsp.lua @@ -4,7 +4,14 @@ local Filter = require("trouble.filter") local Item = require("trouble.item") local Util = require("trouble.util") -local get_line_col = vim.lsp.util._str_byteindex_enc +---@param line string line to be indexed +---@param index integer UTF index +---@param encoding string utf-8|utf-16|utf-32| defaults to utf-16 +---@return integer byte (utf-8) index of `encoding` index `index` in `line` +local function get_line_col(line, index, encoding) + local ok, ret = pcall(vim.lsp.util._str_byteindex_enc, line, index, encoding) + return ok and ret or #line +end ---@class trouble.Source.lsp: trouble.Source ---@diagnostic disable-next-line: missing-fields diff --git a/lua/trouble/view/init.lua b/lua/trouble/view/init.lua index 12e7b660..0262afc6 100644 --- a/lua/trouble/view/init.lua +++ b/lua/trouble/view/init.lua @@ -1,3 +1,4 @@ +local Format = require("trouble.format") local Main = require("trouble.view.main") local Preview = require("trouble.view.preview") local Render = require("trouble.view.render") @@ -17,6 +18,7 @@ local Window = require("trouble.view.window") ---@field moving uv_timer_t ---@field opening? boolean ---@field state table +---@field _filters table ---@field _waiting (fun())[] ---@field private _main? trouble.Main local M = {} @@ -38,6 +40,7 @@ function M.new(opts) self.state = {} self.opts = opts or {} self._waiting = {} + self._filters = {} self.first_render = true self.opts.win = self.opts.win or {} self.opts.win.on_mount = function() @@ -108,14 +111,6 @@ function M.get(filter) return ret end ----@param filter trouble.Filter -function M:filter(filter) - for _, section in ipairs(self.sections) do - section.filter = filter - end - self:refresh() -end - function M:on_mount() vim.w[self.win.win].trouble = { mode = self.opts.mode, @@ -518,6 +513,62 @@ function M:update() end end +---@param filter trouble.Filter +---@param opts? trouble.ViewFilter.opts +function M:filter(filter, opts) + opts = opts or {} + + ---@type trouble.ViewFilter + local view_filter = vim.tbl_deep_extend("force", { + id = vim.inspect(filter), + filter = filter, + data = opts.data, + template = opts.template, + }, opts) + + if opts.del or (opts.toggle and self._filters[view_filter.id]) then + self._filters[view_filter.id] = nil + else + self._filters[view_filter.id] = view_filter + end + + local filters = vim.tbl_count(self._filters) > 0 + and vim.tbl_map(function(f) + return f.filter + end, vim.tbl_values(self._filters)) + or nil + + for _, section in ipairs(self.sections) do + section.filter = filters + end + self:refresh() +end + +function M:header() + local ret = {} ---@type trouble.Format[][] + for _, filter in pairs(self._filters) do + local data = vim.tbl_deep_extend("force", { + filter = filter.filter, + }, type(filter.filter) == "table" and filter.filter or {}, filter.data or {}) + local template = filter.template or "{hl:Title}Filter:{hl} {filter}" + ret[#ret + 1] = self:format(template, data) + end + return ret +end + +---@param id string +function M:get_filter(id) + return self._filters[id] +end + +---@param template string +---@param data table +function M:format(template, data) + data.source = "view" + assert(self.opts, "opts is nil") + return Format.format(template, { item = data, opts = self.opts }) +end + -- render the results function M:render() if not self.win:valid() then @@ -537,6 +588,15 @@ function M:render() for _ = 1, vim.tbl_get(self.opts.win, "padding", "top") or 0 do self.renderer:nl() end + + local header = self:header() + for _, h in ipairs(header) do + for _, ff in ipairs(h) do + self.renderer:append(ff.text, ff) + end + self.renderer:nl() + end + self.renderer:sections(self.sections) self.renderer:trim()