https://github.com/relf108/nvim-unstack
A Lua implementation of mattboehm's vim-unstack (https://github.com/mattboehm/vim-unstack)
https://github.com/relf108/nvim-unstack
lua neovim neovim-plugin vim
Last synced: 5 months ago
JSON representation
A Lua implementation of mattboehm's vim-unstack (https://github.com/mattboehm/vim-unstack)
- Host: GitHub
- URL: https://github.com/relf108/nvim-unstack
- Owner: relf108
- License: mit
- Created: 2024-11-18T05:25:34.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2025-08-28T07:11:43.000Z (10 months ago)
- Last Synced: 2025-10-18T14:40:56.785Z (8 months ago)
- Topics: lua, neovim, neovim-plugin, vim
- Language: Lua
- Homepage:
- Size: 33.2 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Funding: FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
README
- [⚡️ Features](#️-features)
- [🎯 Supported Languages](#-supported-languages)
- [📋 Installation](#-installation)
- [☄ Getting started](#-getting-started)
- [⚙ Configuration](#-configuration)
- [🧰 Commands](#-commands)
- [🔧 API](#-api)
- [🎨 Customization](#-customization)
- [⌨ Contributing](#-contributing)
- [🗞 Wiki](#-wiki)
- [🎭 Motivations](#-motivations)
nvim-unstack
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.
## ⚡️ Features
- **Multi-language support**: Built-in regex parsers for Python, Node.js, Ruby, Go, C#, Perl, and GDB/LLDB
- **Flexible layouts**: Open files in tabs, vertical splits, horizontal splits, or floating windows
- **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
## 🎯 Supported Languages
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](#-customization) section.
## 📋 Installation
Package manager
Snippet
[wbthomason/packer.nvim](https://github.com/wbthomason/packer.nvim)
```lua
-- Stable version
use {"relf108/nvim-unstack", tag = "*" }
-- Development version
use {"relf108/nvim-unstack"}
```
[junegunn/vim-plug](https://github.com/junegunn/vim-plug)
```vim
" Stable version
Plug 'relf108/nvim-unstack', { 'tag': '*' }
" Development version
Plug 'relf108/nvim-unstack'
```
[folke/lazy.nvim](https://github.com/folke/lazy.nvim)
```lua
-- 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 = "s", -- set keybinding (default)
},
}
-- Lazy load on invocation
{
"relf108/nvim-unstack",
version = "*",
lazy = true,
cmd = "NvimUnstack",
keys = { { "ct", "NvimUnstack", mode = { "v" } } },
opts = {
mapkey = false, -- Skip mapping during setup so it doesn't conflict with `keys` config
},
}
```
## ☄ Getting started
### Basic Setup
After installation, you can start using nvim-unstack immediately with the default configuration:
```lua
require("nvim-unstack").setup()
```
### Quick Usage
1. **Visual Selection**: Select a stack trace (or part of one) and press `s` to open the referenced files
2. **From Clipboard**: Use `:UnstackFromClipboard` to parse a stack trace from your system clipboard
3. **From Tmux**: Use `:UnstackFromTmux` to parse a stack trace from tmux paste buffer
### Example
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 `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)
## ⚙ Configuration
nvim-unstack can be customized with the following options:
```lua
require("nvim-unstack").setup({
-- Print debug information (default: false)
debug = false,
-- Layout for opening files (default: "tab")
-- Options: "tab", "vsplit", "split", "floating"
layout = "tab",
-- Key mapping for visual selection unstacking (default: "s")
mapkey = "s",
-- Show signs on lines from stack trace (default: true)
showsigns = true,
})
```
### Configuration Options Explained
#### Layout Options
- **`"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
#### Visual Signs
When `showsigns = true`, nvim-unstack will place visual indicators (`>>`) next to the lines referenced in the stack trace, making them easy to spot.
#### Debug Mode
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
## 🧰 Commands
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 |
### Command Usage Examples
```vim
" Parse visual selection (or use the default s keymap)
:'<,'>NvimUnstack
" Parse from clipboard
:UnstackFromClipboard
" Parse from tmux buffer
:UnstackFromTmux
```
## 🔧 API
nvim-unstack provides a Lua API for programmatic usage:
### Core Functions
```lua
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 = "u"
})
```
### Advanced Usage
```lua
-- Custom keymapping examples
vim.keymap.set("v", "u", function()
require("nvim-unstack").unstack()
end, { desc = "Unstack visual selection" })
vim.keymap.set("n", "uc", function()
require("nvim-unstack").unstack_from_clipboard()
end, { desc = "Unstack from clipboard" })
vim.keymap.set("n", "ut", function()
require("nvim-unstack").unstack_from_tmux()
end, { desc = "Unstack from tmux" })
```
## 🎨 Customization
### Adding New Language Parsers
You can extend nvim-unstack to support additional languages by creating custom regex parsers. Here's the structure:
```lua
-- Example: Custom Java parser
-- Save to nvim-unstack/regex
local java = {}
-- Regex pattern to match Java stack trace lines
java.regex = vim.regex([[at .*(\(.*\.java:[0-9]\+\))]])
-- Function to extract file and line number from matched line
function java.format_match(line, lines, index)
local file = line:match("%((.*)%.java:")
local line_num = line:match(":([0-9]+)%)")
if file and line_num then
return { file .. ".java", line_num }
end
return nil
end
return java
```
### Custom Layout Configurations
You can create wrapper functions for specific layout preferences:
```lua
-- 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, {})
```
### Sign Customization
Customize the appearance of stack trace line indicators:
```lua
require("nvim-unstack").setup({
showsigns = true
})
-- Override sign appearance after setup
vim.fn.sign_define("UnstackLine", {
text = "▶",
texthl = "DiagnosticError",
linehl = "CursorLine",
})
```
## ⌨ Contributing
PRs and issues are always welcome. Make sure to provide as much context as possible when opening one.
## 🗞 Wiki
You can find guides and showcase of the plugin on [the Wiki](https://github.com/relf108/nvim-unstack/wiki)
## 🎭 Motivations
After using (and loving) [mattboehm's vim-unstack](https://github.com/mattboehm/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.