*mini.notify* Show notifications MIT License Copyright (c) 2024 Evgeni Chasnovski ------------------------------------------------------------------------------ *MiniNotify* Features: - Show one or more highlighted notifications in a single floating window. - Manage notifications (add, update, remove, clear). - Custom |vim.notify()| implementation. To adjust, use |MiniNotify.make_notify()| or save-restore `vim.notify` manually after calling |MiniNotify.setup()|. - Automated show of LSP progress report. - Track history which can be accessed with |MiniNotify.get_all()| and shown with |MiniNotify.show_history()|. # Setup ~ This module needs a setup with `require('mini.notify').setup({})` (replace `{}` with your `config` table). It will create global Lua table `MiniNotify` which you can use for scripting or manually (with `:lua MiniNotify.*`). See |MiniNotify.config| for `config` structure and default values. You can override runtime config settings locally to buffer inside `vim.b.mininotify_config` which should have same structure as `MiniNotify.config`. See |mini.nvim-buffer-local-config| for more details. # Comparisons ~ - [j-hui/fidget.nvim](https://github.com/j-hui/fidget.nvim): - Basic goals of providing interface for notifications are similar. - Has more configuration options and visual effects, while this module does not (by design). - [rcarriga/nvim-notify](https://github.com/rcarriga/nvim-notify): - Similar to `j-hui/fidget.nvim`. # Highlight groups ~ *MiniNotify-hl-groups* - `MiniNotifyBorder` - window border. - `MiniNotifyLspProgress` - notifications from built-in LSP progress report. - `MiniNotifyNormal` - basic foreground/background highlighting. - `MiniNotifyTitle` - window title. To change any highlight group, set it directly with |nvim_set_hl()|. NOTE: |vim.notify()| override after |MiniNotify.make_notify()| uses own highlight groups per notification level. # Disabling ~ To disable showing notifications, set `vim.g.mininotify_disable` (globally) or `vim.b.mininotify_disable` (for a buffer) to `true`. Considering high number of different scenarios and customization intentions, writing exact rules for disabling module's functionality is left to user. See |mini.nvim-disabling-recipes| for common recipes. ------------------------------------------------------------------------------ *MiniNotify-specification* # Notification specification ~ Notification is a table with the following keys: - `(string)` - single string with notification message. Use `\n` to delimit several lines. - `(string)` - notification level as key of |vim.log.levels|. Like "ERROR", "WARN", "INFO", etc. - `(string)` - highlight group with which notification is shown. - `(table)` - extra data to store in notification (like `source`, etc.). - `(number)` - timestamp of when notification is added. - `(number)` - timestamp of the latest notification update. - `(number|nil)` - timestamp of when notification is removed. It is `nil` if notification was never removed and thus considered "active". Notes: - Timestamps are compatible with |strftime()| and have fractional part. ------------------------------------------------------------------------------ *MiniNotify.setup()* `MiniNotify.setup`({config}) Module setup This will also: - Set |vim.notify()| custom implementation (see |MiniNotify.make_notify()|). - Clean the history. Use `MiniNotify.setup(MiniNotify.config)` to force clean history while preserving the config. Parameters ~ {config} `(table|nil)` Module config table. See |MiniNotify.config|. Usage ~ >lua require('mini.notify').setup() -- use default config -- OR require('mini.notify').setup({}) -- replace {} with your config table < ------------------------------------------------------------------------------ *MiniNotify.config* `MiniNotify.config` Defaults ~ >lua MiniNotify.config = { -- Content management content = { -- Function which formats the notification message -- By default prepends message with notification time format = nil, -- Function which orders notification array from most to least important -- By default orders first by level and then by update timestamp sort = nil, }, -- Notifications about LSP progress lsp_progress = { -- Whether to enable showing enable = true, -- Notification level level = 'INFO', -- Duration (in ms) of how long last message should be shown duration_last = 1000, }, -- Window options window = { -- Floating window config config = {}, -- Maximum window width as share (between 0 and 1) of available columns max_width_share = 0.382, -- Value of 'winblend' option winblend = 25, }, } < # Content ~ `config.content` defines how notifications are shown. `content.format` is a function which takes single notification object (see |MiniNotify-specification|) and returns a string to be used directly when showing notification. Default: `nil` for |MiniNotify.default_format()|. `content.sort` is a function which takes array of notification objects (see |MiniNotify-specification|) and returns an array of such objects. It can be used to define custom order and/or filter for notifications which are shown simultaneously. Note: Input contains notifications before applying `content.format`. Default: `nil` for |MiniNotify.default_sort()|. Example: >lua require('mini.notify').setup({ content = { -- Use notification message as is for LSP progress format = function(notif) if notif.data.source == 'lsp_progress' then return notif.msg end return MiniNotify.default_format(notif) end, -- Show more recent notifications first sort = function(notif_arr) table.sort( notif_arr, function(a, b) return a.ts_update > b.ts_update end ) return notif_arr end, }, }) < # LSP progress ~ `config.lsp_progress` defines automated notifications for LSP progress. It is implemented as a single updating notification per progress with all information about it. Setting up is done inside |MiniNotify.setup()| via scheduled (|vim.schedule()|) setting of |lsp-handler| for "$/progress" method. `lsp_progress.enable` is a boolean indicating whether LSP progress should be shown in notifications. Can be disabled in current session. Default: `true`. Note: Should be `true` during |MiniNotify.setup()| call to be able to enable it in current session. `lsp_progress.level` is a level to be used in |MiniNotify.add()|. Default: `'INFO'`. `lsp_progress.duration_last` is a number of milliseconds for the last progress report to be shown on screen before removing it. Default: 1000. Notes: - This respects previously set handler by saving and calling it. - Overriding "$/progress" method of `vim.lsp.handlers` disables notifications. - All LSP progress notifications set the following fields in `data`: - is `"lsp_progress"`. - is set to client's name (provided by client or inferred). - is the latest LSP request context (`ctx` arg of |lsp-handler|). - is the latest LSP response (`result` arg of |lsp-handler|). # Window ~ `config.window` defines behavior of notification window. `window.config` is a table defining floating window characteristics or a callable returning such table (will be called with identifier of window's buffer already showing notifications). It should have the same structure as in |nvim_open_win()|. It has the following default values which show notifications in the upper right corner with upper limit on width: - `width` is chosen to fit buffer content but at most `window.max_width_share` share of |'columns'|. To have higher maximum width, use function in `config.window` which computes dimensions inside of it (based on buffer content). - `height` is chosen to fit buffer content with enabled |'wrap'| (assuming default value of `width`). - `anchor`, `col`, and `row` are "NE", |'columns'|, and 0 or 1 (depending on tabline). - `border` is "single". - `zindex` is 999 to be as much on top as reasonably possible. `window.max_width_share` defines maximum window width as a share of |'columns'|. Should be a number between 0 (not included) and 1. Default: 0.382. Example for showing notifications in bottom right corner: >lua local win_config = function() local has_statusline = vim.o.laststatus > 0 local pad = vim.o.cmdheight + (has_statusline and 1 or 0) return { anchor = 'SE', col = vim.o.columns, row = vim.o.lines - pad } end require('mini.notify').setup({ window = { config = win_config } }) < `window.winblend` defines |'winblend'| value for notification window. Default: 25. ------------------------------------------------------------------------------ *MiniNotify.make_notify()* `MiniNotify.make_notify`({opts}) Make vim.notify wrapper Calling this function creates an implementation of |vim.notify()| powered by this module. General idea is to show notification as soon as safely possible (see |vim.schedule_wrap()|) and remove it after a configurable amount of time. All notifications set `source = "vim.notify"` in their `data` field. This is used with default options inside |MiniNotify.setup()|. To adjust, call manually after `setup()`. For example, to show errors longer: >lua require('mini.notify').setup() vim.notify = MiniNotify.make_notify({ ERROR = { duration = 10000 } }) < To preserve original `vim.notify`: >lua local notify_orig = vim.notify require('mini.notify').setup() vim.notify = notify_orig < Parameters ~ {opts} `(table|nil)` Options to configure behavior of notification `level` (as in |MiniNotify.add()|). Fields are the same as names of `vim.log.levels` with values being tables with possible fields: - `(number)` - duration (in ms) of how much a notification should be shown. If 0 or negative, notification is not shown at all. - `(string)` - highlight group of notification. Only data different to default can be supplied. Default: >lua { ERROR = { duration = 5000, hl_group = 'DiagnosticError' }, WARN = { duration = 5000, hl_group = 'DiagnosticWarn' }, INFO = { duration = 5000, hl_group = 'DiagnosticInfo' }, DEBUG = { duration = 0, hl_group = 'DiagnosticHint' }, TRACE = { duration = 0, hl_group = 'DiagnosticOk' }, OFF = { duration = 0, hl_group = 'MiniNotifyNormal' }, } < ------------------------------------------------------------------------------ *MiniNotify.add()* `MiniNotify.add`({msg}, {level}, {hl_group}, {data}) Add notification Add notification to history. It is considered "active" and is shown. To hide, call |MiniNotify.remove()| with identifier this function returns. Example: >lua local id = MiniNotify.add('Hello', 'WARN', 'Comment') vim.defer_fn(function() MiniNotify.remove(id) end, 1000) < Parameters ~ {msg} `(string)` Notification message. {level} `(string|nil)` Notification level as key of |vim.log.levels|. Default: `'INFO'`. {hl_group} `(string|nil)` Notification highlight group. Default: `'MiniNotifyNormal'`. {data} `(table|nil)` Extra data to store in the notification. Default: `{}`. Return ~ `(number)` Notification identifier. ------------------------------------------------------------------------------ *MiniNotify.update()* `MiniNotify.update`({id}, {new}) Update active notification Modify contents of active notification. Parameters ~ {id} `(number)` Identifier of currently active notification as returned by |MiniNotify.add()|. {new} `(table)` Table with contents to update. Keys should be as non-timestamp fields of |MiniNotify-specification| and values - new content values. If present, field `data` is updated as is. Use |MiniNotify.get()| together with |vim.tbl_deep_extend()| to change only part of it. ------------------------------------------------------------------------------ *MiniNotify.remove()* `MiniNotify.remove`({id}) Remove notification If notification is active, make it not active (by setting `ts_remove` field). If not active, do nothing. Parameters ~ {id} `(number|nil)` Identifier of previously added notification. If it is not, nothing is done (silently). ------------------------------------------------------------------------------ *MiniNotify.clear()* `MiniNotify.clear`() Remove all active notifications Hide all active notifications and stop showing window (if shown). ------------------------------------------------------------------------------ *MiniNotify.refresh()* `MiniNotify.refresh`() Refresh notification window Make notification window show relevant information: - Create an array of active notifications (see |MiniNotify-specification|). - Apply `config.content.sort` to an array. If output has zero notifications, make notification window to not show. - Apply `config.content.format` to each element of notification array and update its message. - Construct content from notifications and show them in a window. Note: effects are delayed if inside fast event (|vim.in_fast_event()|). ------------------------------------------------------------------------------ *MiniNotify.get()* `MiniNotify.get`({id}) Get previously added notification by id Parameters ~ {id} `(number)` Identifier of notification. Return ~ `(table)` Notification object (see |MiniNotify-specification|). ------------------------------------------------------------------------------ *MiniNotify.get_all()* `MiniNotify.get_all`() Get all previously added notifications Get map of used notifications with keys being notification identifiers. Can be used to get only active notification objects. Example: >lua -- Get active notifications vim.tbl_filter( function(notif) return notif.ts_remove == nil end, MiniNotify.get_all() ) < Return ~ `(table)` Map with notification object values (see |MiniNotify-specification|). Note: messages are taken from last valid update. ------------------------------------------------------------------------------ *MiniNotify.show_history()* `MiniNotify.show_history`() Show history Open or reuse a scratch buffer with all previously shown notifications. Notes: - Content is ordered from oldest to newest based on latest update time. - Message is formatted with `config.content.format`. ------------------------------------------------------------------------------ *MiniNotify.default_format()* `MiniNotify.default_format`({notif}) Default content format Used by default as `config.content.format`. Prepends notification message with the human readable update time and a separator. Parameters ~ {notif} `(table)` Notification object (see |MiniNotify-specification|). Return ~ `(string)` Formatted notification message. ------------------------------------------------------------------------------ *MiniNotify.default_sort()* `MiniNotify.default_sort`({notif_arr}) Default content sort Used by default as `config.content.sort`. First sorts by notification's `level` ("ERROR" > "WARN" > "INFO" > "DEBUG" > "TRACE" > "OFF"; the bigger the more important); if draw - by latest update time (the later the more important). Parameters ~ {notif_arr} `(table)` Array of notifications (see |MiniNotify-specification|). Return ~ `(table)` Sorted array of notifications. vim:tw=78:ts=8:noet:ft=help:norl: