Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
name: CI

on:
push:
branches: [main]
pull_request:

jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

# PUC Lua, not LuaJIT: luarocks fails to load the (large) luarocks.org
# manifest under LuaJIT with "main function has more than 65536 constants"
- uses: leafo/gh-actions-lua@v10
with:
luaVersion: "5.4"
- uses: leafo/gh-actions-luarocks@v4

- name: Install luacheck
run: luarocks install luacheck

- name: luacheck
run: luacheck lua/ ftplugin/ tests/

# informational only: stylua reformats the manual column alignment in the
# config tables, so a failure here is not treated as a build break
- name: stylua (informational)
uses: JohnnyMorganz/stylua-action@v4
continue-on-error: true
with:
token: ${{ secrets.GITHUB_TOKEN }}
version: latest
args: --check lua/ ftplugin/ tests/

test:
name: Test (nvim ${{ matrix.nvim }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
nvim: [stable, nightly]
steps:
- uses: actions/checkout@v4

- name: Install Neovim
uses: rhysd/action-setup-vim@v1
with:
neovim: true
version: ${{ matrix.nvim }}

- name: Install binutils (readelf, size, strings)
run: sudo apt-get update && sudo apt-get install -y binutils

- name: Fetch plenary.nvim
run: git clone --depth 1 https://github.com/nvim-lua/plenary.nvim .tests/plenary.nvim

- name: Run tests
run: |
nvim --headless -u tests/minimal_init.lua \
-c "PlenaryBustedDirectory tests/ {minimal_init = 'tests/minimal_init.lua'}"
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# local test dependencies (plenary.nvim is cloned here by CI / contributors)
.tests/

# the vhs script used to record demo.gif is a local tool, not part of the repo
demo.tape
17 changes: 17 additions & 0 deletions .luacheckrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
-- luacheck configuration for bin.nvim
std = "luajit"
cache = true

-- Neovim's global; everything hangs off it
globals = { "vim" }

-- the test files use plenary's busted-style globals
files["tests/"] = {
globals = { "describe", "it", "before_each", "after_each", "pending", "assert" },
}

-- generated / vendored
exclude_files = { ".tests/" }

-- the rendered output and long readelf command lines run wide on purpose
max_line_length = false
24 changes: 24 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Changelog

All notable changes to this project are documented here. The format follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project aims to
follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.0.0] - 2026-06-08

First public release.

### Added

- Open any ELF binary as a structured, collapsible inspection buffer built from
`readelf` and a handful of optional tools (`file`, `size`, `strings`, `ldd`,
`checksec`, `c++filt`, `rustfilt`).
- Sections load in parallel; a missing optional tool leaves a short notice in
place rather than failing the whole view.
- `:Bin` and `:Bin /path/to/binary`, alongside `require("bin").open()`.
- Folding (`zM` / `zR` / `za`) and `]]` / `[[` section navigation.
- Buffer-local syntax highlighting under a private `binview` filetype.
- `:checkhealth bin`, reporting the Neovim version floor and which tools are
present.

[1.0.0]: https://github.com/a-mango/bin.nvim/releases/tag/v1.0.0
21 changes: 19 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@ When you open an ELF file, the buffer is populated with the output of several
analysis tools organised into collapsible sections. All sections are loaded in
parallel; missing optional tools display a short notice rather than an error.

## Demo

![bin.nvim opening /usr/bin/ls](demo.gif)

## Requirements

- Neovim >= 0.9
- Neovim >= 0.10 (for `vim.system`)
- `readelf` — required (part of binutils)
- `file`, `size`, `strings`, `ldd` — optional (binutils / glibc)
- `checksec` — optional
Expand Down Expand Up @@ -42,6 +46,9 @@ use {
}
```

Your plugin manager generates the help tags, so `:help bin.nvim` works once it
is installed.

## Configuration

`setup()` accepts an options table. All keys are optional and fall back to the
Expand Down Expand Up @@ -80,7 +87,9 @@ These keymaps are active only inside ELF inspection buffers.

## Output

Opening any ELF file (e.g. `nvim /usr/bin/ls`) replaces the buffer with:
Opening any ELF file (e.g. `nvim /usr/bin/ls`) replaces the buffer with the
inspection view. You can also inspect a file at any time with `:Bin` (current
file) or `:Bin /path/to/binary`. The buffer then looks like:

```
bin.nvim — /usr/bin/ls
Expand Down Expand Up @@ -170,6 +179,14 @@ Press `zM` to collapse all sections to their titles for a quick overview:

Use `]]` / `[[` to move between sections, and `za` to expand the one you need.

## Limitations

Detection happens on `BufReadPost`, so Neovim reads the whole file into a buffer
before the inspection view replaces it. On a large or stripped binary that costs
some memory and a brief pause before the summary shows. Intercepting earlier
would avoid the read but blanks ordinary non-ELF buffers, so the current
behaviour is a deliberate trade.

## License

MIT
Binary file added demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
49 changes: 40 additions & 9 deletions doc/bin.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,60 @@

USAGE *bin-usage*

bin.nvim intercepts ELF files via |BufReadCmd| and replaces the binary
buffer with a human-readable summary produced by readelf(1).

Keymaps in the info buffer:
q close buffer
r reload / refresh
bin.nvim watches |BufReadPost| and, when the file it just read is an ELF
binary, replaces it with a readable summary built from readelf(1) and a
handful of optional tools. Each section loads in parallel; a missing
optional tool leaves a short notice in place of an error.

Requires Neovim 0.10 or newer (it uses |vim.system()|).

Keymaps inside the inspection buffer:
q close the buffer
r reload and refresh
]] jump to the next section
[[ jump to the previous section
zM collapse every section to its title
zR expand every section
za toggle the section under the cursor

CONFIGURATION *bin-config*

setup() takes an options table; every key is optional and falls back to the
default shown here.

>lua
require("bin").setup({
sections = { "header", "sections", "segments", "symbols", "dynamic" },
max_symbols = 200,
sections = {
"file", "header", "checksec", "size",
"sections", "segments", "dynamic",
"notes", "versions", "relocations",
"symbols", "strings", "ldd", "dwarf",
},
max_symbols = 200,
max_strings = 300,
min_string_len = 10,
max_dwarf = 300,
demangle = true,
})
<

COMMANDS *bin-commands*

:lua require("bin").open() inspect current file
:Bin inspect the current file
:Bin /path/to/binary inspect a specific file
:lua require("bin").open() inspect the current file
:lua require("bin").open("/path") inspect a specific file

HEALTH *bin-health*

:checkhealth bin

LIMITATIONS *bin-limitations*

Detection runs on |BufReadPost|, which means Neovim has already read the whole
file into a buffer by the time the inspection view replaces it. For a large or
stripped binary that read costs memory and a moment of lag before the summary
appears. Reading nothing would mean intercepting before the load, but that path
blanks ordinary non-ELF buffers, so the trade here is deliberate.

vim:tw=78:ft=help:norl:
20 changes: 20 additions & 0 deletions ftplugin/binview.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
vim.wo.wrap = false
vim.wo.number = false
vim.wo.relativenumber = false
vim.wo.signcolumn = "no"
vim.wo.cursorline = true

-- folding: section chrome (┌│└, blank, header) stays visible at all levels;
-- content lines fold at level 1. Use zM to collapse all, zR to open all.
vim.wo.foldmethod = "expr"
vim.wo.foldexpr = "v:lua.require('bin').foldexpr(v:lnum)"
vim.wo.foldtext = "v:lua.require('bin').foldtext()"
vim.wo.foldlevel = 99
vim.wo.foldminlines = 1

-- section navigation
local o = { noremap = true, silent = true, buffer = true }
vim.keymap.set("n", "]]", function() vim.fn.search("^┌", "W") end, o)
vim.keymap.set("n", "[[", function() vim.fn.search("^┌", "bW") end, o)

-- syntax highlighting lives in syntax/binview.vim (buffer-local, no accumulation)
63 changes: 0 additions & 63 deletions ftplugin/elf.lua

This file was deleted.

6 changes: 3 additions & 3 deletions lua/bin/health.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ local M = {}
function M.check()
vim.health.start("bin.nvim")

if vim.fn.has("nvim-0.9") == 1 then
vim.health.ok("Neovim >= 0.9")
if vim.fn.has("nvim-0.10") == 1 then
vim.health.ok("Neovim >= 0.10")
else
vim.health.error("Neovim >= 0.9 required (vim.system API)")
vim.health.error("Neovim >= 0.10 required (vim.system API)")
end

local function check(name, pkg, required)
Expand Down
Loading
Loading