The gold standard for debugging a program or script is to use a real debugger.
For NeoVim, the 'standard' way to integrate this is to use something like
nvim-dap. However, many folks prefer
a more low-tech approach; the 'print' statement, or the equivalent in a
particular language, to trace the output of a program during execution.
debugprint
is a NeoVim plugin for them, as it can easily generate 'print'
statements appropriate to the language being edited, which include:
-
A tag string unique to
debugprint
. -
The filename/line number they are being inserted on.
-
A counter which increases over the duration of a NeoVim session each time a statement is generated.
-
(optionally) A variable (which may be automatically picked up from an identifier/variable name under the cursor).
debugprint
comes with the generation logic built in for many common
programming languages, and can be extended to support more.
debugprint
is inspired by
vim-debugstring, which I've
used for several years, but is updated and refreshed for the NeoVim generation.
It provides various improvements:
-
Its configuration system is more 'NeoVim-like' and it is easier to add custom languages in your configuration.
-
It dot-repeats with NeoVim.
-
It can pick up a variable name from under the cursor.
-
It provides keymappings for visual mode, so you can select a variable visually and print it out.
-
It provides keymappings for operator-pending mode, so you can select a variable using a motion.
-
It indents the lines it inserts more accurately.
-
The output when printing a 'plain' debug line, or a variable, is more consistent.
-
It provides a command to delete all debugging lines added to the current buffer.
-
Able to optionally move to the inserted line (or not).
NOTE: This demo gives an idea of what debugprint
does, but does not show some of
the more recent capabilities such as using treesitter to pick up a variable name, deleting
debug lines with a command, and uses older deprecated keymappings - the new ones are g?p
, g?P
,
g?v
and g?V
).
demo.mp4
Requires NeoVim 0.7+.
Optional dependency:
nvim-treesitter. If this
is not installed, debugprint
will not find variable names under the cursor and
will always prompt for a variable name.
Example for packer.nvim
:
packer.startup(function(use)
...
use({
"andrewferrier/debugprint.nvim",
config = function()
require("debugprint").setup()
end,
})
...
end)
Note that you can add an opts
object to the setup method:
opts = { ... }
...
require("debugprint").setup(opts)
...
})
The sections below detail the allowed options.
Please subscribe to this GitHub issue,
especially while debugprint
is still in active development, to be notified of
any breaking issues.
By default, the plugin will create some keymappings and commands, which are the standard way to use it. There are also some function invocations which are not mapped to any keymappings or commands by default, but could be. This is all shown in the following table.
Mode | Default Keymap/Command | Purpose | Equivalent Lua Function |
---|---|---|---|
Normal | g?p |
Insert a 'plain' debug line appropriate to the filetype just below the current line | require('debugprint').debugprint() |
Normal | g?P |
The same, but above the current line | require('debugprint').debugprint({above = true}) |
Normal | g?v |
Insert a variable debugging line below the current line. If the cursor is on a variable name, use that, otherwise prompt for one. | require('debugprint').debugprint({variable = true}) |
Normal | g?V |
The same, but above the current line | require('debugprint').debugprint({above = true, variable = true}) |
Normal | None by default | Always prompt for a variable name, and insert a debugging line just below the current line which outputs it | require('debugprint').debugprint({ignore_treesitter = true, variable = true}) |
Normal | None by default | Always prompt for a variable name, and insert a debugging line just above the current line which outputs it | require('debugprint').debugprint({ignore_treesitter = true, above = true, variable = true}) |
Visual | g?v |
Find the visually select variable name, and insert a debugging line just below the current line which outputs it | require('debugprint').debugprint({variable = true}) |
Visual | g?v |
Find the visually select variable name, and insert a debugging line just below the current line which outputs it | require('debugprint').debugprint({variable = true}) |
Operator-pending | g?o |
Locate a variable using a motion, and insert a debugging line just above the current line which outputs it | require('debugprint').debugprint({above = true, variable = true}) |
Operator-pending | g?O |
Locate a variable using a motion, and insert a debugging line just above the current line which outputs it | require('debugprint').debugprint({above = true, variable = true}) |
Command | :DeleteDebugPrints |
Delete all debug lines added to this buffer. | `require('debugprint').deleteprints() |
The keymappings are chosen specifically because by default in NeoVim they are
used to convert sections to ROT-13, which most folks don't use. You can disable
the defaults above from being created by setting create_keymaps
and/or
create_commands
, and map them yourself to something else if you prefer:
opts = {
create_keymaps = false,
create_commands = false
...
}
require("debugprint").setup(opts)
vim.keymap.set("n", "<Leader>d", function()
require('debugprint').debugprint()
end)
vim.keymap.set("n", "<Leader>D", function()
require('debugprint').debugprint({ above = true })
end)
vim.keymap.set("n", "<Leader>dq", function()
require('debugprint').debugprint({ variable = true })
end)
vim.keymap.set("n", "<Leader>Dq", function()
require('debugprint').debugprint({ above = true, variable = true })
end)
vim.api.nvim_create_user_command("DeleteDebugs", function(opts)
-- Note: you must set `range=true` and pass through opts for ranges to work
M.deleteprints(opts)
end, {
range = true})
end)
...
or, to have a keymapping instead for deleting debug lines (this will only affect the entire buffer, visual and operator-pending modes will not work):
vim.keymap.set("n", "g?d", function()
M.deleteprints()
end)
debugprint
supports the following options in its global opts
object:
Option | Default | Purpose |
---|---|---|
create_keymaps |
true |
Creates default keymappings - see above |
move_to_debugline |
false |
When adding a debug line, moves the cursor to that line |
filetypes |
See below | Custom filetypes - see below |
ignore_treesitter |
false |
Never use treesitter to find a variable under the cursor, always prompt for it - overrides the same setting on debugprint() if set to true |
print_tag |
DEBUGPRINT |
The string inserted into each print statement, which can be used to uniquely identify statements inserted by debugprint . |
Note: Since debugprint.nvim
is still relatively new,
if you work out a configuration for a filetype not listed here, particularly a
standard or common one that NeoVim supports out-of-the-box, it would be really
appreciated if you can open an
issue to have it
supported out-of-the-box in debugprint
so others can benefit from it.
Similarly, if you spot any issues with, or improvements to, the language
configurations out-of-the-box, please open an issue also.
debugprint
supports the following filetypes out-of-the-box:
bash
c
cpp
(C++)go
javascript
lua
make
python
ruby
rust
sh
(Sh/Bash)typescript
vim
zsh
If debugprint
doesn't support your filetype, you can add it as a custom
filetype in one of two ways:
-
In the
opts.filetypes
object insetup()
. -
Using the
require('debugprint').add_custom_filetypes()
method (designed for use fromftplugin/
directories, etc.
In either case, the format is the same. For example, if adding via setup()
:
local my_fileformat = {
left = 'print "',
right = '"',
mid_var = "${",
right_var = '}"',
}
require('debugprint').setup({ filetypes = { my_fileformat, another_of_my_fileformats, ... }})
or add_custom_filetypes()
:
require('debugprint').add_custom_filetypes({ my_fileformat, ... })
Your new file format will be merged in with those that already exist. If you pass in one that already exists, your configuration will override the built-in configuration.
The keys in the configuration are used like this:
Type of debug line | Default keys | How debug line is constructed |
---|---|---|
Plain debug line | g?p /g?P |
my_fileformat.left .. "auto-gen DEBUG string" .. my_fileformat.right |
Variable debug line | g?v /g?V /g?o /g?O |
my_fileformat.left .. "auto-gen DEBUG string, variable=" .. my_file_format.mid_var .. variable .. my_fileformat.right_var |
If it helps to understand these, you can look at the built-in configurations in filetypes.lua.
debugprint
only supports variable names or simple expressions when usingg?v
/g?V
- in particular, it does not make any attempt to escape expressions, and may generate invalid syntax if you try to be too clever. There's an issue to look at ways of improving this.
-
refactoring.nvim - similar capabilities to debugprint, supports a slighter smaller set of languages. Can remove added statement - this is on the debugprint roadmap.
-
logsitter - supports a smaller set of languages and requires you to set up your own autocmds/keymappings.