-
-
Notifications
You must be signed in to change notification settings - Fork 83
Usage Recipes
Examples of use cases for nvim-notify
These examples assume that you have set nvim-notify as your vim.notify handler
vim.notify = require("notify")
Uses jobstart
to run asynchronously.
local function notify_output(command, opts)
local output = ""
local notification
local notify = function(msg, level)
local notify_opts = vim.tbl_extend(
"keep",
opts or {},
{ title = table.concat(command, " "), replace = notification }
)
notification = vim.notify(msg, level, notify_opts)
end
local on_data = function(_, data)
output = output .. table.concat(data, "\n")
notify(output, "info")
end
vim.fn.jobstart(command, {
on_stdout = on_data,
on_stderr = on_data,
on_exit = function(_, code)
if #output == 0 then
notify("No output of command, exit code: " .. code, "warn")
end
end,
})
end
notify_output({ "echo", "hello world" })
Both LSP (Language Server Protocol) and DAP (Debug Adapter Protocol) provide progress notification.
The following snippets use nvim-notify
to render them.
As a base line, you will need the following functions
-- Utility functions shared between progress reports for LSP and DAP
local client_notifs = {}
local function get_notif_data(client_id, token)
if not client_notifs[client_id] then
client_notifs[client_id] = {}
end
if not client_notifs[client_id][token] then
client_notifs[client_id][token] = {}
end
return client_notifs[client_id][token]
end
local spinner_frames = { "⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷" }
local function update_spinner(client_id, token)
local notif_data = get_notif_data(client_id, token)
if notif_data.spinner then
local new_spinner = (notif_data.spinner + 1) % #spinner_frames
notif_data.spinner = new_spinner
notif_data.notification = vim.notify(nil, nil, {
hide_from_history = true,
icon = spinner_frames[new_spinner],
replace = notif_data.notification,
})
vim.defer_fn(function()
update_spinner(client_id, token)
end, 100)
end
end
local function format_title(title, client_name)
return client_name .. (#title > 0 and ": " .. title or "")
end
local function format_message(message, percentage)
return (percentage and percentage .. "%\t" or "") .. (message or "")
end
To integrate with progress notifications provided through Neovim's LSP functionality, you can use nvim-lsp-notify plugin, or do-it-yourself:
-- LSP integration
-- Make sure to also have the snippet with the common helper functions in your config!
vim.lsp.handlers["$/progress"] = function(_, result, ctx)
local client_id = ctx.client_id
local val = result.value
if not val.kind then
return
end
local notif_data = get_notif_data(client_id, result.token)
if val.kind == "begin" then
local message = format_message(val.message, val.percentage)
notif_data.notification = vim.notify(message, "info", {
title = format_title(val.title, vim.lsp.get_client_by_id(client_id).name),
icon = spinner_frames[1],
timeout = false,
hide_from_history = false,
})
notif_data.spinner = 1
update_spinner(client_id, result.token)
elseif val.kind == "report" and notif_data then
notif_data.notification = vim.notify(format_message(val.message, val.percentage), "info", {
replace = notif_data.notification,
hide_from_history = false,
})
elseif val.kind == "end" and notif_data then
notif_data.notification =
vim.notify(val.message and format_message(val.message) or "Complete", "info", {
icon = "",
replace = notif_data.notification,
timeout = 3000,
})
notif_data.spinner = nil
end
end
-- table from lsp severity to vim severity.
local severity = {
"error",
"warn",
"info",
"info", -- map both hint and info to info?
}
vim.lsp.handlers["window/showMessage"] = function(err, method, params, client_id)
vim.notify(method.message, severity[params.type])
end
To integrate with progress notifications provided by nvim-dap, you can use
-- DAP integration
-- Make sure to also have the snippet with the common helper functions in your config!
dap.listeners.before['event_progressStart']['progress-notifications'] = function(session, body)
local notif_data = get_notif_data("dap", body.progressId)
local message = format_message(body.message, body.percentage)
notif_data.notification = vim.notify(message, "info", {
title = format_title(body.title, session.config.type),
icon = spinner_frames[1],
timeout = false,
hide_from_history = false,
})
notif_data.notification.spinner = 1,
update_spinner("dap", body.progressId)
end
dap.listeners.before['event_progressUpdate']['progress-notifications'] = function(session, body)
local notif_data = get_notif_data("dap", body.progressId)
notif_data.notification = vim.notify(format_message(body.message, body.percentage), "info", {
replace = notif_data.notification,
hide_from_history = false,
})
end
dap.listeners.before['event_progressEnd']['progress-notifications'] = function(session, body)
local notif_data = client_notifs["dap"][body.progressId]
notif_data.notification = vim.notify(body.message and format_message(body.message) or "Complete", "info", {
icon = "",
replace = notif_data.notification,
timeout = 3000
})
notif_data.spinner = nil
end
Add the following snippet to your init.vim
, after notify
extension is loaded and vim.notify
is pointing to require("notify")
.
lua << EOF
local coc_status_record = {}
function coc_status_notify(msg, level)
local notify_opts = { title = "LSP Status", timeout = 500, hide_from_history = true, on_close = reset_coc_status_record }
-- if coc_status_record is not {} then add it to notify_opts to key called "replace"
if coc_status_record ~= {} then
notify_opts["replace"] = coc_status_record.id
end
coc_status_record = vim.notify(msg, level, notify_opts)
end
function reset_coc_status_record(window)
coc_status_record = {}
end
local coc_diag_record = {}
function coc_diag_notify(msg, level)
local notify_opts = { title = "LSP Diagnostics", timeout = 500, on_close = reset_coc_diag_record }
-- if coc_diag_record is not {} then add it to notify_opts to key called "replace"
if coc_diag_record ~= {} then
notify_opts["replace"] = coc_diag_record.id
end
coc_diag_record = vim.notify(msg, level, notify_opts)
end
function reset_coc_diag_record(window)
coc_diag_record = {}
end
EOF
function! s:DiagnosticNotify() abort
let l:info = get(b:, 'coc_diagnostic_info', {})
if empty(l:info) | return '' | endif
let l:msgs = []
let l:level = 'info'
if get(l:info, 'warning', 0)
let l:level = 'warn'
endif
if get(l:info, 'error', 0)
let l:level = 'error'
endif
if get(l:info, 'error', 0)
call add(l:msgs, ' Errors: ' . l:info['error'])
endif
if get(l:info, 'warning', 0)
call add(l:msgs, ' Warnings: ' . l:info['warning'])
endif
if get(l:info, 'information', 0)
call add(l:msgs, ' Infos: ' . l:info['information'])
endif
if get(l:info, 'hint', 0)
call add(l:msgs, ' Hints: ' . l:info['hint'])
endif
let l:msg = join(l:msgs, "\n")
if empty(l:msg) | let l:msg = ' All OK' | endif
call v:lua.coc_diag_notify(l:msg, l:level)
endfunction
function! s:StatusNotify() abort
let l:status = get(g:, 'coc_status', '')
let l:level = 'info'
if empty(l:status) | return '' | endif
call v:lua.coc_status_notify(l:status, l:level)
endfunction
function! s:InitCoc() abort
execute "lua vim.notify('Initialized coc.nvim for LSP support', 'info', { title = 'LSP Status' })"
endfunction
" notifications
autocmd User CocNvimInit call s:InitCoc()
autocmd User CocDiagnosticChange call s:DiagnosticNotify()
autocmd User CocStatusChange call s:StatusNotify()
coc.nvim
uses echo* commands to write variety of messages. In order to intercept those, monkey patching it by utilizing vim's after
directory (see :help after-directory
) is one approach.
First, create coc/ui.vim
file in the autoload
after-directory in your vim's runtime path (e.g. ~/.vim/after/autoload
). For instance, place the following content in the file ~/.vim/after/autoload/coc/ui.vim
--
scriptencoding utf-8
if has("nvim")
" overwrite coc#ui#echo_messages function to use notify
function! coc#ui#echo_messages(hl, msgs)
if a:hl !~# 'Error' && (mode() !~# '\v^(i|n)$')
return
endif
let msgs = filter(copy(a:msgs), '!empty(v:val)')
if empty(msgs)
return
endif
" map a:hl highlight groups to notify levels
" if hl matches Error then level is error
" if hl matches Warning then level is warn
" otherwise level is info
let level = 'info'
if a:hl =~# 'Error'
let level = 'error'
elseif a:hl =~# 'Warning'
let level = 'warn'
endif
let msg = join(msgs, '\n')
call v:lua.coc_notify(msg, level)
endfunction
function! coc#ui#echo_lines(lines)
let msg = join(a:lines, "\n")
call v:lua.coc_notify(msg, 'info')
endfunction
endif
Next, manually source this file after coc.nvim has initialized. You can modify s:InitCoc
function from the previous example/section and add the function coc_notify
--
function! s:InitCoc() abort
" load overrides
runtime! autoload/coc/ui.vim
execute "lua vim.notify('Initialized coc.nvim for LSP support', 'info', { title = 'LSP Status' })"
endfunction
function coc_notify(msg, level)
local notify_opts = { title = "LSP Message", timeout = 500 }
vim.notify(msg, level, notify_opts)
end