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
12 changes: 12 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
root = true

[*]
charset = utf-8
trim_trailing_whitespace = true

[*.lua]
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
max_line_length = 120
92 changes: 65 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Hex.nvim

![Lua](https://img.shields.io/badge/Made%20with%20Lua-blueviolet.svg?style=for-the-badge&logo=lua)

Hex editing done right.
Expand All @@ -7,65 +8,102 @@ Shift address lines around, edit and delete bytes, it will all work itself out o

![demo](https://user-images.githubusercontent.com/16624558/211962886-f5e67052-03d8-41c2-844f-720550c935b4.gif)

## Install
## Installation

```lua
{ 'RaafatTurki/hex.nvim' }
{ "RaafatTurki/hex.nvim" }
```

This plugin makes use of the `xxd` utility by default, make sure it's on `$PATH`:
- `xxd-standalone` from aur
- compile from [source](https://github.com/vim/vim/tree/master/src/xxd)
- install vim (it comes with it)

> [!NOTE]
>
> No setup or require needed; simply place it in your runtimepath to use.

## Setup
```lua
require 'hex'.setup()
```
## Usage

## Use
```lua
require 'hex'.dump() -- switch to hex view
require 'hex'.assemble() -- go back to normal view
require 'hex'.toggle() -- switch back and forth
require("hex").dump() -- switch to hex view
require("hex").assemble() -- go back to normal view
require("hex").toggle() -- switch back and forth
```
or their vim cmds

Or their Vim cmds:

```
:HexDump
:HexAssemble
:HexToggle
:Hex dump
:Hex assemble
:Hex toggle
```
any file opens in hex view if opened with `-b`:

Any file opens in hex view if opened with `-b`:

```bash
nvim -b file
nvim -b file1 file2
```

## Config
```lua
-- defaults
require 'hex'.setup {
## Configuration

-- cli command used to dump hex data
dump_cmd = 'xxd -g 1 -u',
Hex.nvim uses a global table for configuration. User options are merged
with the default table prior to initialization unless `vim.g.hex.default`
is set to false. Below are the available options and their default values:

-- cli command used to assemble from hex data
assemble_cmd = 'xxd -r',

```lua
vim.g.hex = {
default = true,
cmd = {
-- cli command used to dump hex data
dump = "xxd -g 1 -u",
-- cli command used to assemble from hex data
revert = "xxd -r",
},
-- function that runs on BufReadPre to determine if it's binary or not
is_file_binary_pre_read = function()
checkbin_pre = function()
-- logic that determines if a buffer contains binary data or not
-- must return a bool
local binary_ext = {
"bin",
"dll",
"exe",
"jpg",
"jpeg",
"png",
"out",
}
-- only work on normal buffers
if vim.bo.ft ~= "" then return false end
-- check -b flag
if vim.bo.binary then return true end
local ext = vim.fn.expand("%:e")
if vim.tbl_contains(binary_ext, ext) then return true end
return false
end,

-- function that runs on BufReadPost to determine if it's binary or not
is_file_binary_post_read = function()
checkbin_post = function()
-- logic that determines if a buffer contains binary data or not
-- must return a bool
local encoding = (vim.bo.fenc ~= "" and vim.bo.fenc) or vim.o.enc
if encoding ~= "utf-8" then return true end
return false
end,
keymaps = true,
prettify = {
enable = true,
unicode = true,
hl = {
border = "Special",
middle = "Special",
}
}
}
```

## Plans

- [ ] Implement pagination
- [x] Implement auto bin detection
- [ ] Transform cursor position across views
Expand Down
98 changes: 0 additions & 98 deletions lua/hex.lua

This file was deleted.

12 changes: 12 additions & 0 deletions lua/hex/health.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
local health = {}

function health.check()
vim.health.start("Dependencies ~")
if vim.fn.executable("xxd") == 0 then
vim.health.error("'xxd'" .. " is not installed")
else
vim.health.ok("'xxd'" .. " is installed")
end
end

return health
138 changes: 138 additions & 0 deletions lua/hex/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
local config = vim.g.hex
local render = require("hex.render")

local M = {}

function M.toggle()
if not vim.b.hex then
M.dump()
else
M.revert()
end
end

function M.dump()
if vim.b.hex then
vim.notify("Already dumped.", vim.log.levels.WARN)
return
end
M.hex_dump()
render.prettify()
end

function M.revert()
if not vim.b.hex then
vim.notify("Already reverted.", vim.log.levels.WARN)
return
end
M.hex_revert()
end

local function get_undo_path()
local buf = vim.api.nvim_buf_get_name(0)
local resolved_fname = vim.fs.basename(vim.fn.undofile(buf))
local undofile = vim.fs.joinpath(vim.fn.stdpath("state"), "hex", resolved_fname)
return undofile
end

local function load_undo()
local undofile = get_undo_path()
if vim.fn.filereadable(undofile) == 1 then
vim.cmd("rundo " .. vim.fn.fnameescape(undofile))
end
end

function M.save_undo()
local undofile = get_undo_path()
local undodir = vim.fs.dirname(undofile)
if not vim.uv.fs_stat(undodir) then
vim.fn.mkdir(undodir, "p")
end
vim.cmd("wundo! " .. vim.fn.fnameescape(undofile))
end

local function xxd_common(cmd, patch)
local xxd = {}
for i in string.gmatch(cmd, "%S+") do
table.insert(xxd, i)
end
local text
local obj
if cmd == config.cmd.dump then
table.insert(xxd, vim.api.nvim_buf_get_name(0))
obj = vim.system(xxd, { text = true }):wait()
elseif cmd == config.cmd.revert then
text = vim.api.nvim_buf_get_lines(0, 0, -1, false)
obj = vim.system(xxd, { stdin = text }):wait()
end
local lines = vim.split(obj.stdout, "\n", { trimempty = true })
local ul = vim.o.undolevels
if not patch then
vim.o.undolevels = -1
end
vim.api.nvim_buf_set_lines(0, 0, -1, false, lines)
vim.o.undolevels = ul
vim.bo.modified = false
end

function M.hex_dump()
vim.bo.binary = true
xxd_common(config.cmd.dump, false)
vim.b.hex_ft = vim.bo.filetype
vim.bo.filetype = "xxd"
vim.b.hex = true
M.detach_lsp()
load_undo()
local str = vim.api.nvim_buf_get_lines(0, 0, 1, false)[1]
vim.b.hex_offset = string.find(str, ":", 1, true)
local groupsize = tonumber(config.cmd.dump:match("%-g(%d)%s") or
config.cmd.dump:match("%-g%s(%d)%s"))
if groupsize == 1 then
vim.b.hex_byte_separator = vim.b.hex_offset + 25
vim.b.hex_limit = 47
elseif groupsize == 2 then
vim.b.hex_byte_separator = vim.b.hex_offset + 21
vim.b.hex_limit = 39
elseif groupsize == 4 then
vim.b.hex_byte_separator = vim.b.hex_offset + 19
vim.b.hex_limit = 35
elseif groupsize == 8 then
vim.b.hex_byte_separator = vim.b.hex_offset + 18
vim.b.hex_limit = 33
end
end

function M.hex_revert()
xxd_common(config.cmd.revert, false)
vim.bo.filetype = vim.b.hex_ft
vim.b.hex = false
end

local undofile = vim.o.undofile

function M.patch_start()
vim.b.hex_curpos = vim.api.nvim_win_get_cursor(0)
vim.o.undofile = false
if not vim.b.hex_did_undoredo then
vim.cmd.undojoin()
else
vim.b.hex_did_undoredo = false
end
xxd_common(config.cmd.revert, true)
end

function M.patch_finish()
xxd_common(config.cmd.dump, true)
vim.api.nvim_win_set_cursor(0, vim.b.hex_curpos)
vim.o.undofile = undofile
render.prettify()
end

function M.detach_lsp()
local attached_servers = vim.lsp.get_clients({ bufnr = vim.api.nvim_get_current_buf() })
for _, server in ipairs(attached_servers) do
server:stop()
end
end

return M
Loading