fix: change deprecation presentation

Deprecation with vim.deprecate is currently too noisy. Show the
following warning instead:

[function] is deprecated. Run ":checkhealth vim.deprecated" for more information.

The important part is that the full message needs to be short enough to
fit in one line in order to not trigger the "Press ENTER or type command
to continue" prompt.

The full information and stack trace for the deprecated functions will
be shown in the new healthcheck `vim.deprecated`.
This commit is contained in:
dundargoc 2024-05-16 18:33:09 +02:00 committed by dundargoc
parent f864b68c5b
commit d123202ae6
5 changed files with 121 additions and 43 deletions

View File

@ -1031,6 +1031,42 @@ function vim._cs_remote(rcid, server_addr, connect_error, args)
}
end
do
local function truncated_echo(msg)
-- Truncate message to avoid hit-enter-prompt
local max_width = vim.o.columns * math.max(vim.o.cmdheight - 1, 0) + vim.v.echospace
local msg_truncated = string.sub(msg, 1, max_width)
vim.api.nvim_echo({ { msg_truncated, 'WarningMsg' } }, true, {})
end
local notified = false
function vim._truncated_echo_once(msg)
if not notified then
truncated_echo(msg)
notified = true
return true
end
return false
end
end
--- This is basically the same as debug.traceback(), except the full paths are shown.
local function traceback()
local level = 4
local backtrace = { 'stack traceback:' }
while true do
local info = debug.getinfo(level, 'Sl')
if not info then
break
end
local msg = (' %s:%s'):format(info.source:sub(2), info.currentline)
table.insert(backtrace, msg)
level = level + 1
end
return table.concat(backtrace, '\n')
end
--- Shows a deprecation message to the user.
---
---@param name string Deprecated feature (function, API, etc.).
@ -1043,12 +1079,12 @@ end
---@return string|nil # Deprecated message, or nil if no message was shown.
function vim.deprecate(name, alternative, version, plugin, backtrace)
plugin = plugin or 'Nvim'
local will_be_removed = 'will be removed'
-- Only issue warning if feature is hard-deprecated as specified by MAINTAIN.md.
-- Example: if removal_version is 0.12 (soft-deprecated since 0.10-dev), show warnings starting at
-- 0.11, including 0.11-dev
if plugin == 'Nvim' then
require('vim.deprecated.health').add(name, version, traceback(), alternative)
-- Only issue warning if feature is hard-deprecated as specified by MAINTAIN.md.
-- Example: if removal_version is 0.12 (soft-deprecated since 0.10-dev), show warnings starting at
-- 0.11, including 0.11-dev
local major, minor = version:match('(%d+)%.(%d+)')
major, minor = tonumber(major), tonumber(minor)
@ -1059,8 +1095,12 @@ function vim.deprecate(name, alternative, version, plugin, backtrace)
return
end
local removal_version = string.format('nvim-%d.%d', major, minor)
will_be_removed = vim.fn.has(removal_version) == 1 and 'was removed' or will_be_removed
local msg = ('%s is deprecated. Run ":checkhealth vim.deprecated" for more information'):format(
name
)
local displayed = vim._truncated_echo_once(msg)
return displayed and msg or nil
else
vim.validate {
name = { name, 'string' },
@ -1068,22 +1108,16 @@ function vim.deprecate(name, alternative, version, plugin, backtrace)
version = { version, 'string', true },
plugin = { plugin, 'string', true },
}
end
local msg = ('%s is deprecated'):format(name)
msg = alternative and ('%s, use %s instead.'):format(msg, alternative) or (msg .. '.')
msg = ('%s%s\nFeature %s in %s %s'):format(
msg,
(plugin == 'Nvim' and ' :help deprecated' or ''),
will_be_removed,
plugin,
version
)
local displayed = vim.notify_once(msg, vim.log.levels.WARN)
if displayed and backtrace ~= false then
vim.notify(debug.traceback('', 2):sub(2), vim.log.levels.WARN)
local msg = ('%s is deprecated'):format(name)
msg = alternative and ('%s, use %s instead.'):format(msg, alternative) or (msg .. '.')
msg = ('%s\nFeature will be removed in %s %s'):format(msg, plugin, version)
local displayed = vim.notify_once(msg, vim.log.levels.WARN)
if displayed and backtrace ~= false then
vim.notify(debug.traceback('', 2):sub(2), vim.log.levels.WARN)
end
return displayed and msg or nil
end
return displayed and msg or nil
end
require('vim._options')

View File

@ -0,0 +1,42 @@
local M = {}
local health = vim.health
local deprecated = {}
function M.check()
if next(deprecated) == nil then
health.ok('No deprecated functions detected')
return
end
for name, v in vim.spairs(deprecated) do
health.start('')
local version, backtraces, alternative = v[1], v[2], v[3]
local major, minor = version:match('(%d+)%.(%d+)')
major, minor = tonumber(major), tonumber(minor)
local removal_version = string.format('nvim-%d.%d', major, minor)
local will_be_removed = vim.fn.has(removal_version) == 1 and 'was removed' or 'will be removed'
local msg = ('%s is deprecated. Feature %s in Nvim %s'):format(name, will_be_removed, version)
local msg_alternative = alternative and ('use %s instead.'):format(alternative)
local advice = { msg_alternative }
table.insert(advice, backtraces)
advice = vim.iter(advice):flatten():totable()
health.warn(msg, advice)
end
end
function M.add(name, version, backtrace, alternative)
if deprecated[name] == nil then
deprecated[name] = { version, { backtrace }, alternative }
return
end
local it = vim.iter(deprecated[name][2])
if it:find(backtrace) == nil then
table.insert(deprecated[name][2], backtrace)
end
end
return M

View File

@ -1279,9 +1279,7 @@ M.handlers.signs = {
vim.deprecate(
'Defining diagnostic signs with :sign-define or sign_define()',
'vim.diagnostic.config()',
'0.12',
nil,
false -- suppress backtrace
'0.12'
)
if not opts.signs.text then

View File

@ -135,42 +135,46 @@ describe('lua stdlib', function()
-- See MAINTAIN.md for the soft/hard deprecation policy
describe(('vim.deprecate prerel=%s,'):format(prerel or 'nil'), function()
it('plugin=nil', function()
local curver = exec_lua('return vim.version()') --[[@as {major:number, minor:number}]]
-- "0.10" or "0.10-dev+xxx"
local curstr = ('%s.%s%s'):format(curver.major, curver.minor, prerel or '')
-- "0.10" or "0.11"
local nextver = ('%s.%s'):format(curver.major, curver.minor + (prerel and 0 or 1))
local was_removed = prerel and 'was removed' or 'will be removed'
local curver = exec_lua('return vim.version()') --[[@as {major:number, minor:number}]]
-- "0.10" or "0.10-dev+xxx"
local curstr = ('%s.%s%s'):format(curver.major, curver.minor, prerel or '')
-- "0.10" or "0.11"
local nextver = ('%s.%s'):format(curver.major, curver.minor + (prerel and 0 or 1))
local was_removed = prerel and 'was removed' or 'will be removed'
it('plugin=nil, same message skipped', function()
eq(
dedent([[
foo.bar() is deprecated, use zub.wooo{ok=yay} instead. :help deprecated
Feature was removed in Nvim %s]]):format(curstr),
dedent(
[[
foo.bar() is deprecated. Run ":checkhealth vim.deprecated" for more information]]
):format(curstr),
exec_lua('return vim.deprecate(...)', 'foo.bar()', 'zub.wooo{ok=yay}', curstr)
)
-- Same message as above; skipped this time.
eq(vim.NIL, exec_lua('return vim.deprecate(...)', 'foo.bar()', 'zub.wooo{ok=yay}', curstr))
end)
-- No error if soft-deprecated.
it('plugin=nil, no error if soft-deprecated', function()
eq(
vim.NIL,
exec_lua('return vim.deprecate(...)', 'foo.baz()', 'foo.better_baz()', '0.99.0')
)
end)
-- Show error if hard-deprecated.
it('plugin=nil, show error if hard-deprecated', function()
eq(
dedent([[
foo.hard_dep() is deprecated, use vim.new_api() instead. :help deprecated
Feature %s in Nvim %s]]):format(was_removed, nextver),
dedent(
[[
foo.hard_dep() is deprecated. Run ":checkhealth vim.deprecated" for more information]]
):format(was_removed, nextver),
exec_lua('return vim.deprecate(...)', 'foo.hard_dep()', 'vim.new_api()', nextver)
)
end)
-- To be deleted in the next major version (1.0)
it('plugin=nil, to be deleted in the next major version (1.0)', function()
eq(
dedent [[
foo.baz() is deprecated. :help deprecated
Feature will be removed in Nvim 1.0]],
foo.baz() is deprecated. Run ":checkhealth vim.deprecated" for more information]],
exec_lua [[ return vim.deprecate('foo.baz()', nil, '1.0') ]]
)
end)

View File

@ -42,7 +42,7 @@ describe(':checkhealth', function()
it('completions can be listed via getcompletion()', function()
clear()
eq('vim.health', getcompletion('vim', 'checkhealth')[1])
eq('vim.deprecated', getcompletion('vim', 'checkhealth')[1])
eq('vim.provider', getcompletion('vim.prov', 'checkhealth')[1])
eq('vim.lsp', getcompletion('vim.ls', 'checkhealth')[1])
end)