- β‘οΈ Features
- π― Supported Languages
- π Installation
- β Getting started
- β Configuration
- π§° Commands
- π§ API
- π¨ Customization
- β¨ Contributing
- π Wiki
- π Motivations
A powerful Neovim plugin for parsing and navigating stack traces from multiple programming languages.
Quickly jump to files and line numbers from stack traces with configurable layouts and visual indicators.
- Multi-language support: Built-in regex parsers for Python, Node.js, Ruby, Go, C#, Perl, and GDB/LLDB
- Smart parser selection: Automatically detects the right parser, or lets you choose when multiple match
- Flexible layouts: Open files in tabs, vertical splits, horizontal splits, floating windows, or quickfix list
- Visual indicators: Optional signs to highlight stack trace lines
- Multiple input methods: Parse from visual selection, clipboard, or tmux paste buffer
- Configurable keymaps: Customize the key binding for stack trace parsing
- Easy extension: Simple API for adding custom language parsers
- Zero dependencies: Pure Lua implementation with no external requirements
nvim-unstack comes with built-in support for parsing stack traces from:
- Python - Standard Python tracebacks with file paths and line numbers
- Pytest - Pytest test failure tracebacks and assertion errors
- Node.js - JavaScript stack traces with file locations
- Ruby - Ruby exception backtraces
- Go - Go panic stack traces and error messages
- C# - .NET exception stack traces
- Perl - Perl error messages with file references
- GDB/LLDB - Debugger stack traces and breakpoint information
New language parsers can be easily added - see the Customization section.
| Package manager | Snippet |
|---|---|
-- Stable version
use {"relf108/nvim-unstack", tag = "*" }
-- Development version
use {"relf108/nvim-unstack"} |
|
" Stable version
Plug 'relf108/nvim-unstack', { 'tag': '*' }
" Development version
Plug 'relf108/nvim-unstack' |
|
-- Stable version
{ "relf108/nvim-unstack", version = "*" }
-- Development version
{ "relf108/nvim-unstack" }
-- With configuration
{
"relf108/nvim-unstack",
event = "VeryLazy", -- Enable lazy loading
version = "*",
opts = {
debug = false, -- Disable debug logging (default)
showsigns = true, -- Enable signs (default)
layout = "tab", -- Use tab layout (default)
mapkey = "<leader>s", -- set keybinding (default)
use_first_parser = true, -- Use first matching parser (default)
exclude_patterns = { -- Filter out dependencies (default includes common paths)
"node_modules", -- JavaScript/TypeScript dependencies
".venv", -- Python virtual environment
"venv", -- Python virtual environment (alternate name)
"site%-packages", -- Python installed packages
"dist%-packages", -- Python system packages
"/usr/", -- System libraries (Unix)
"/lib/", -- System libraries
"%.cargo/", -- Rust dependencies
"vendor/", -- Ruby/Go dependencies
},
},
}
-- Lazy load on invocation
{
"relf108/nvim-unstack",
version = "*",
lazy = true,
cmd = "NvimUnstack",
keys = { { "<leader>ct", "<cmd>NvimUnstack<cr>", mode = { "v" } } },
opts = {
mapkey = false, -- Skip mapping during setup so it doesn't conflict with `keys` config
},
} |
After installation, you can start using nvim-unstack immediately with the default configuration:
require("nvim-unstack").setup()- Visual Selection: Select a stack trace (or part of one) and press
<leader>sto open the referenced files - From Clipboard: Use
:UnstackFromClipboardto parse a stack trace from your system clipboard - From Tmux: Use
:UnstackFromTmuxto parse a stack trace from tmux paste buffer
Given this Python traceback:
Traceback (most recent call last):
File "/path/to/myproject/main.py", line 42, in main
result = process_data(data)
File "/path/to/myproject/utils.py", line 15, in process_data
return transform(data)
Or this Pytest failure:
=================================== FAILURES ===================================
____________________________ test_my_function __________________________________
def test_my_function():
> assert result == expected
E AssertionError: assert 15 == 10
tests/test_example.py:42: AssertionError
Simply select the traceback text and press <leader>s. The plugin will:
- Parse the file paths and line numbers
- Open each file at the specified line
- Display them according to your configured layout (tabs by default)
nvim-unstack can be customized with the following options:
require("nvim-unstack").setup({
-- Print debug information (default: false)
debug = false,
-- Layout for opening files (default: "tab")
-- Options: "tab", "vsplit", "split", "floating", "quickfix_list"
layout = "tab",
-- Key mapping for visual selection unstacking (default: "<leader>s")
mapkey = "<leader>s",
-- Show signs on lines from stack trace (default: true)
showsigns = true,
-- Use first matching parser (default: true)
-- When false, shows a selection prompt if multiple parsers match
use_first_parser = true,
-- Exclude file paths matching these patterns from stack traces
-- Set to false to disable filtering, or provide a table of patterns
-- Patterns are matched against the absolute file path
exclude_patterns = {
"node_modules", -- JavaScript/TypeScript dependencies
".venv", -- Python virtual environment
"venv", -- Python virtual environment (alternate name)
"site%-packages", -- Python installed packages
"dist%-packages", -- Python system packages
"/usr/", -- System libraries (Unix)
"/lib/", -- System libraries
"%.cargo/", -- Rust dependencies
"vendor/", -- Ruby/Go dependencies
},
})"tab"(default): Opens all files as vertical splits in a new tab"vsplit": Opens each file in a new vertical split"split": Opens each file in a new horizontal split"floating": Opens each file in a floating window"quickfix_list": Populates the quickfix list with all stack trace entries
The exclude_patterns option filters out unwanted files from stack traces, particularly useful for hiding third-party dependencies and system libraries:
table(default): List of Lua patterns to match against absolute file pathsfalse: Disable filtering entirely (show all files from stack trace)
Common patterns included by default:
node_modules- JavaScript/TypeScript dependencies.venv,venv- Python virtual environmentssite%-packages,dist%-packages- Python installed packages/usr/,/lib/- System libraries%.cargo/- Rust dependenciesvendor/- Ruby/Go dependencies
You can customize this list to match your project needs:
require("nvim-unstack").setup({
-- Only exclude node_modules
exclude_patterns = { "node_modules" },
-- Or disable filtering completely
exclude_patterns = false,
})The use_first_parser option controls behavior when multiple parsers can parse the same stack trace:
true(default): Automatically uses the first matching parserfalse: Shows a selection prompt with parser names (e.g., "Python", "Pytest", "Node.js")
This is particularly useful for Python projects where both standard Python tracebacks and Pytest output might be present. Each parser has a descriptive name to help you choose the right one.
When showsigns = true, nvim-unstack will place visual indicators (>>) next to the lines referenced in the stack trace, making them easy to spot.
Enable debug = true to see detailed logging about:
- Which language parser was selected
- What files and line numbers were extracted
- Any parsing errors or warnings
nvim-unstack provides several commands for different use cases:
| Command | Description |
|---|---|
:NvimUnstack |
Parse stack trace from current visual selection |
:UnstackFromClipboard |
Parse stack trace from system clipboard |
:UnstackFromTmux |
Parse stack trace from tmux paste buffer |
" Parse visual selection (or use the default <leader>s keymap)
:'<,'>NvimUnstack
" Parse from clipboard
:UnstackFromClipboard
" Parse from tmux buffer
:UnstackFromTmuxnvim-unstack provides a Lua API for programmatic usage:
local nvim_unstack = require("nvim-unstack")
-- Parse and open files from visual selection
nvim_unstack.unstack()
-- Parse from system clipboard
nvim_unstack.unstack_from_clipboard()
-- Parse from tmux paste buffer
nvim_unstack.unstack_from_tmux()
-- Setup with custom configuration
nvim_unstack.setup({
layout = "floating",
mapkey = "<leader>u"
})-- Custom keymapping examples
vim.keymap.set("v", "<leader>u", function()
require("nvim-unstack").unstack()
end, { desc = "Unstack visual selection" })
vim.keymap.set("n", "<leader>uc", function()
require("nvim-unstack").unstack_from_clipboard()
end, { desc = "Unstack from clipboard" })
vim.keymap.set("n", "<leader>ut", function()
require("nvim-unstack").unstack_from_tmux()
end, { desc = "Unstack from tmux" })You can extend nvim-unstack to support additional languages by creating custom regex parsers. Here's the structure:
-- Example: Custom Java parser
-- Save to nvim-unstack/regex
local java = {}
-- Display name for parser selection prompt
java.name = "Java"
-- Regex pattern to match Java stack trace lines
java.regex = vim.regex([[at .*(\(.*\.java:[0-9]\+\))]])
-- Function to extract file and line numbers from entire traceback text
function java.extract_matches(text)
local matches = {}
-- Match Java stack trace format
for file, line_num in text:gmatch("%((.*)%.java:([0-9]+)%)") do
table.insert(matches, { file .. ".java", line_num })
end
return matches
end
return javaNote: The name field is required for displaying the parser in the selection prompt when use_first_parser = false.
You can create wrapper functions for specific layout preferences:
-- Quick functions for different layouts
local function unstack_floating()
local original_layout = require("nvim-unstack.config").options.layout
require("nvim-unstack.config").options.layout = "floating"
require("nvim-unstack").unstack()
require("nvim-unstack.config").options.layout = original_layout
end
-- Create custom commands
vim.api.nvim_create_user_command("UnstackFloat", unstack_floating, {})Customize the appearance of stack trace line indicators:
require("nvim-unstack").setup({
showsigns = true
})
-- Override sign appearance after setup
vim.fn.sign_define("UnstackLine", {
text = "βΆ",
texthl = "DiagnosticError",
linehl = "CursorLine",
})PRs and issues are always welcome. Make sure to provide as much context as possible when opening one.
You can find guides and showcase of the plugin on the Wiki
After using (and loving) mattboehm's vim-unstack for about a year I've collected a short list of gripes that I think are worth taking the time to fix, unfortunately the repo is no longer maintained so I've decided to rip out the regex and rewrite it in Lua.
- Lack of configurability, it's v-splits or nothin' pal and god help you if you want line numbers in those splits.
- Not extendable, stack trace parsing is incredibly useful in a wide array of languages and it should be easy for users of the plugin to add their favourites.
- Written in vimscript, this is fine and the plugin still works in neovim but it creates a barrier to entry when trying to contribute code and I think the Lua ecosystem has a lot to offer.