feat(lua): add notify_once() (#17010)

Like vim.notify(), but only displays the notification once.

This function prints a warning message to the user only once per Nvim
session. This is useful for things we want the user to see without being
overwhelmed with warning messages (for example, the deprecation messages
in LSP diagnostics).
This commit is contained in:
github-actions[bot]
2022-01-09 10:42:30 -07:00
committed by GitHub
parent 11dafcaf05
commit f365e68293
5 changed files with 106 additions and 48 deletions

View File

@@ -1206,19 +1206,32 @@ inspect({object}, {options}) *vim.inspect()*
https://github.com/kikito/inspect.lua
https://github.com/mpeterv/vinspect
notify({msg}, {log_level}, {opts}) *vim.notify()*
Notification provider
notify({msg}, {level}, {opts}) *vim.notify()*
Display a notification to the user.
Without a runtime, writes to :Messages
This function can be overridden by plugins to display
notifications using a custom provider (such as the system
notification provider). By default, writes to |:messages|.
Parameters: ~
{msg} string Content of the notification to show to
the user
{log_level} number|nil enum from vim.log.levels
{opts} table|nil additional options (timeout, etc)
{msg} string Content of the notification to show to the
user.
{level} number|nil One of the values from
|vim.log.levels|.
{opts} table|nil Optional parameters. Unused by default.
See also: ~
:help nvim_notify
notify_once({msg}, {level}, {opts}) *vim.notify_once()*
Display a notification only one time.
Like |vim.notify()|, but subsequent calls with the same
message will not display a notification.
Parameters: ~
{msg} string Content of the notification to show to the
user.
{level} number|nil One of the values from
|vim.log.levels|.
{opts} table|nil Optional parameters. Unused by default.
on_key({fn}, {ns_id}) *vim.on_key()*
Adds Lua function {fn} with namespace id {ns_id} as a listener

View File

@@ -243,7 +243,7 @@ end
---@param client_id number
---@private
function M.save(diagnostics, bufnr, client_id)
vim.api.nvim_echo({{'vim.lsp.diagnostic.save is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
vim.notify_once('vim.lsp.diagnostic.save is deprecated. See :h deprecated', vim.log.levels.WARN)
local namespace = M.get_namespace(client_id)
vim.diagnostic.set(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id))
end
@@ -257,7 +257,7 @@ end
--- If nil, diagnostics of all clients are included.
---@return table with diagnostics grouped by bufnr (bufnr: Diagnostic[])
function M.get_all(client_id)
vim.api.nvim_echo({{'vim.lsp.diagnostic.get_all is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
vim.notify_once('vim.lsp.diagnostic.get_all is deprecated. See :h deprecated', vim.log.levels.WARN)
local result = {}
local namespace
if client_id then
@@ -279,7 +279,7 @@ end
--- Else, return just the diagnostics associated with the client_id.
---@param predicate function|nil Optional function for filtering diagnostics
function M.get(bufnr, client_id, predicate)
vim.api.nvim_echo({{'vim.lsp.diagnostic.get is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
vim.notify_once('vim.lsp.diagnostic.get is deprecated. See :h deprecated', vim.log.levels.WARN)
predicate = predicate or function() return true end
if client_id == nil then
local all_diagnostics = {}
@@ -341,7 +341,7 @@ end
---@param severity DiagnosticSeverity
---@param client_id number the client id
function M.get_count(bufnr, severity, client_id)
vim.api.nvim_echo({{'vim.lsp.diagnostic.get_count is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
vim.notify_once('vim.lsp.diagnostic.get_count is deprecated. See :h deprecated', vim.log.levels.WARN)
severity = severity_lsp_to_vim(severity)
local opts = { severity = severity }
if client_id ~= nil then
@@ -358,7 +358,7 @@ end
---@param opts table See |vim.lsp.diagnostic.goto_next()|
---@return table Previous diagnostic
function M.get_prev(opts)
vim.api.nvim_echo({{'vim.lsp.diagnostic.get_prev is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
vim.notify_once('vim.lsp.diagnostic.get_prev is deprecated. See :h deprecated', vim.log.levels.WARN)
if opts then
if opts.severity then
opts.severity = severity_lsp_to_vim(opts.severity)
@@ -376,7 +376,7 @@ end
---@param opts table See |vim.lsp.diagnostic.goto_next()|
---@return table Previous diagnostic position
function M.get_prev_pos(opts)
vim.api.nvim_echo({{'vim.lsp.diagnostic.get_prev_pos is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
vim.notify_once('vim.lsp.diagnostic.get_prev_pos is deprecated. See :h deprecated', vim.log.levels.WARN)
if opts then
if opts.severity then
opts.severity = severity_lsp_to_vim(opts.severity)
@@ -393,7 +393,7 @@ end
---
---@param opts table See |vim.lsp.diagnostic.goto_next()|
function M.goto_prev(opts)
vim.api.nvim_echo({{'vim.lsp.diagnostic.goto_prev is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
vim.notify_once('vim.lsp.diagnostic.goto_prev is deprecated. See :h deprecated', vim.log.levels.WARN)
if opts then
if opts.severity then
opts.severity = severity_lsp_to_vim(opts.severity)
@@ -411,7 +411,7 @@ end
---@param opts table See |vim.lsp.diagnostic.goto_next()|
---@return table Next diagnostic
function M.get_next(opts)
vim.api.nvim_echo({{'vim.lsp.diagnostic.get_next is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
vim.notify_once('vim.lsp.diagnostic.get_next is deprecated. See :h deprecated', vim.log.levels.WARN)
if opts then
if opts.severity then
opts.severity = severity_lsp_to_vim(opts.severity)
@@ -429,7 +429,7 @@ end
---@param opts table See |vim.lsp.diagnostic.goto_next()|
---@return table Next diagnostic position
function M.get_next_pos(opts)
vim.api.nvim_echo({{'vim.lsp.diagnostic.get_next_pos is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
vim.notify_once('vim.lsp.diagnostic.get_next_pos is deprecated. See :h deprecated', vim.log.levels.WARN)
if opts then
if opts.severity then
opts.severity = severity_lsp_to_vim(opts.severity)
@@ -444,7 +444,7 @@ end
---
---@deprecated Prefer |vim.diagnostic.goto_next()|
function M.goto_next(opts)
vim.api.nvim_echo({{'vim.lsp.diagnostic.goto_next is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
vim.notify_once('vim.lsp.diagnostic.goto_next is deprecated. See :h deprecated', vim.log.levels.WARN)
if opts then
if opts.severity then
opts.severity = severity_lsp_to_vim(opts.severity)
@@ -468,7 +468,7 @@ end
--- - severity_limit (DiagnosticSeverity):
--- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid.
function M.set_signs(diagnostics, bufnr, client_id, _, opts)
vim.api.nvim_echo({{'vim.lsp.diagnostic.set_signs is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
vim.notify_once('vim.lsp.diagnostic.set_signs is deprecated. See :h deprecated', vim.log.levels.WARN)
local namespace = M.get_namespace(client_id)
if opts and not opts.severity and opts.severity_limit then
opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)}
@@ -489,7 +489,7 @@ end
--- - severity_limit (DiagnosticSeverity):
--- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid.
function M.set_underline(diagnostics, bufnr, client_id, _, opts)
vim.api.nvim_echo({{'vim.lsp.diagnostic.set_underline is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
vim.notify_once('vim.lsp.diagnostic.set_underline is deprecated. See :h deprecated', vim.log.levels.WARN)
local namespace = M.get_namespace(client_id)
if opts and not opts.severity and opts.severity_limit then
opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)}
@@ -511,7 +511,7 @@ end
--- - severity_limit (DiagnosticSeverity):
--- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid.
function M.set_virtual_text(diagnostics, bufnr, client_id, _, opts)
vim.api.nvim_echo({{'vim.lsp.diagnostic.set_virtual_text is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
vim.notify_once('vim.lsp.diagnostic.set_virtual_text is deprecated. See :h deprecated', vim.log.levels.WARN)
local namespace = M.get_namespace(client_id)
if opts and not opts.severity and opts.severity_limit then
opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)}
@@ -530,7 +530,7 @@ end
---@return an array of [text, hl_group] arrays. This can be passed directly to
--- the {virt_text} option of |nvim_buf_set_extmark()|.
function M.get_virtual_text_chunks_for_line(bufnr, _, line_diags, opts)
vim.api.nvim_echo({{'vim.lsp.diagnostic.get_virtual_text_chunks_for_line is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
vim.notify_once('vim.lsp.diagnostic.get_virtual_text_chunks_for_line is deprecated. See :h deprecated', vim.log.levels.WARN)
return vim.diagnostic._get_virt_text_chunks(diagnostic_lsp_to_vim(line_diags, bufnr), opts)
end
@@ -548,7 +548,7 @@ end
---@param position table|nil The (0,0)-indexed position
---@return table {popup_bufnr, win_id}
function M.show_position_diagnostics(opts, buf_nr, position)
vim.api.nvim_echo({{'vim.lsp.diagnostic.show_position_diagnostics is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
vim.notify_once('vim.lsp.diagnostic.show_position_diagnostics is deprecated. See :h deprecated', vim.log.levels.WARN)
opts = opts or {}
opts.scope = "cursor"
opts.pos = position
@@ -572,7 +572,7 @@ end
---@param client_id number|nil the client id
---@return table {popup_bufnr, win_id}
function M.show_line_diagnostics(opts, buf_nr, line_nr, client_id)
vim.api.nvim_echo({{'vim.lsp.diagnostic.show_line_diagnostics is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
vim.notify_once('vim.lsp.diagnostic.show_line_diagnostics is deprecated. See :h deprecated', vim.log.levels.WARN)
opts = opts or {}
opts.scope = "line"
opts.pos = line_nr
@@ -596,7 +596,7 @@ end
--- client. The default is to redraw diagnostics for all attached
--- clients.
function M.redraw(bufnr, client_id)
vim.api.nvim_echo({{'vim.lsp.diagnostic.redraw is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
vim.notify_once('vim.lsp.diagnostic.redraw is deprecated. See :h deprecated', vim.log.levels.WARN)
bufnr = get_bufnr(bufnr)
if not client_id then
return vim.lsp.for_each_buffer_client(bufnr, function(client)
@@ -624,7 +624,7 @@ end
--- - {workspace}: (boolean, default true)
--- - Set the list with workspace diagnostics
function M.set_qflist(opts)
vim.api.nvim_echo({{'vim.lsp.diagnostic.set_qflist is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
vim.notify_once('vim.lsp.diagnostic.set_qflist is deprecated. See :h deprecated', vim.log.levels.WARN)
opts = opts or {}
if opts.severity then
opts.severity = severity_lsp_to_vim(opts.severity)
@@ -656,7 +656,7 @@ end
--- - {workspace}: (boolean, default false)
--- - Set the list with workspace diagnostics
function M.set_loclist(opts)
vim.api.nvim_echo({{'vim.lsp.diagnostic.set_loclist is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
vim.notify_once('vim.lsp.diagnostic.set_loclist is deprecated. See :h deprecated', vim.log.levels.WARN)
opts = opts or {}
if opts.severity then
opts.severity = severity_lsp_to_vim(opts.severity)
@@ -684,7 +684,7 @@ end
-- send diagnostic information and the client will still process it. The
-- diagnostics are simply not displayed to the user.
function M.disable(bufnr, client_id)
vim.api.nvim_echo({{'vim.lsp.diagnostic.disable is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
vim.notify_once('vim.lsp.diagnostic.disable is deprecated. See :h deprecated', vim.log.levels.WARN)
if not client_id then
return vim.lsp.for_each_buffer_client(bufnr, function(client)
M.disable(bufnr, client.id)
@@ -705,7 +705,7 @@ end
--- client. The default is to enable diagnostics for all attached
--- clients.
function M.enable(bufnr, client_id)
vim.api.nvim_echo({{'vim.lsp.diagnostic.enable is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
vim.notify_once('vim.lsp.diagnostic.enable is deprecated. See :h deprecated', vim.log.levels.WARN)
if not client_id then
return vim.lsp.for_each_buffer_client(bufnr, function(client)
M.enable(bufnr, client.id)

View File

@@ -10,14 +10,6 @@ local uv = vim.loop
local npcall = vim.F.npcall
local split = vim.split
local _warned = {}
local warn_once = function(message)
if not _warned[message] then
vim.api.nvim_err_writeln(message)
_warned[message] = true
end
end
local M = {}
local default_border = {
@@ -1933,7 +1925,6 @@ function M.lookup_section(settings, section)
end
M._get_line_byte_from_position = get_line_byte_from_position
M._warn_once = warn_once
M.buf_versions = {}

View File

@@ -419,23 +419,43 @@ function vim.defer_fn(fn, timeout)
end
--- Notification provider
--- Display a notification to the user.
---
--- Without a runtime, writes to :Messages
---@see :help nvim_notify
---@param msg string Content of the notification to show to the user
---@param log_level number|nil enum from |vim.log.levels|
---@param opts table|nil additional options (timeout, etc)
function vim.notify(msg, log_level, opts) -- luacheck: no unused
if log_level == vim.log.levels.ERROR then
--- This function can be overridden by plugins to display notifications using a
--- custom provider (such as the system notification provider). By default,
--- writes to |:messages|.
---
---@param msg string Content of the notification to show to the user.
---@param level number|nil One of the values from |vim.log.levels|.
---@param opts table|nil Optional parameters. Unused by default.
function vim.notify(msg, level, opts) -- luacheck: no unused args
if level == vim.log.levels.ERROR then
vim.api.nvim_err_writeln(msg)
elseif log_level == vim.log.levels.WARN then
elseif level == vim.log.levels.WARN then
vim.api.nvim_echo({{msg, 'WarningMsg'}}, true, {})
else
vim.api.nvim_echo({{msg}}, true, {})
end
end
do
local notified = {}
--- Display a notification only one time.
---
--- Like |vim.notify()|, but subsequent calls with the same message will not
--- display a notification.
---
---@param msg string Content of the notification to show to the user.
---@param level number|nil One of the values from |vim.log.levels|.
---@param opts table|nil Optional parameters. Unused by default.
function vim.notify_once(msg, level, opts) -- luacheck: no unused args
if not notified[msg] then
vim.notify(msg, level, opts)
notified[msg] = true
end
end
end
---@private
function vim.register_keystroke_callback()

View File

@@ -2200,6 +2200,40 @@ describe('lua stdlib', function()
end)
end)
it('vim.notify_once', function()
local screen = Screen.new(60,5)
screen:set_default_attr_ids({
[0] = {bold=true, foreground=Screen.colors.Blue},
[1] = {foreground=Screen.colors.Red},
})
screen:attach()
screen:expect{grid=[[
^ |
{0:~ }|
{0:~ }|
{0:~ }|
|
]]}
exec_lua [[vim.notify_once("I'll only tell you this once...", vim.log.levels.WARN)]]
screen:expect{grid=[[
^ |
{0:~ }|
{0:~ }|
{0:~ }|
{1:I'll only tell you this once...} |
]]}
feed('<C-l>')
screen:expect{grid=[[
^ |
{0:~ }|
{0:~ }|
{0:~ }|
|
]]}
exec_lua [[vim.notify_once("I'll only tell you this once...")]]
screen:expect_unchanged()
end)
describe('vim.schedule_wrap', function()
it('preserves argument lists', function()
exec_lua [[