Skip to content

Commit

Permalink
feat: display correctly and strengthen tests
Browse files Browse the repository at this point in the history
  • Loading branch information
theKnightsOfRohan committed Jan 29, 2025
1 parent 9cf359a commit 9eb1ba3
Show file tree
Hide file tree
Showing 5 changed files with 175 additions and 90 deletions.
128 changes: 128 additions & 0 deletions lua/hexer/display.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
local Popup = require("nui.popup")
local Table = require("nui.table")
local Event = require("nui.utils.autocmd").event
local Parser = require("hexer.parser")

---@class HexerDisplay
---@field _window NuiPopup
---@field _table NuiTable
local M = {
_window = Popup({
enter = true,
focusable = true,
anchor = "SE",
border = {
style = "rounded",
},
position = {
row = "100%",
col = "100%",
},
size = {
width = 420,
height = 69,
},
}),
_table = Table({
bufnr = 0,
ns_id = "HexerWindow",
columns = {
{ accessor_key = "ascii", header = "Ascii" },
{ accessor_key = "value", header = "Value" },
{ accessor_key = "hex", header = "Hex" },
{ accessor_key = "binary", header = "Binary" },
{ accessor_key = "octal", header = "Octal" },
},
---@type HexerItem
data = Parser.parse_input("69"),
}),
_buf_opts = {
["buftype"] = "",
["modifiable"] = false,
["readonly"] = false,
}
}

function M.hide_window(self)
self._window:hide()
self:_apply_buf_opts(0)
end

function M._show_window(self)
self._window:mount()
self._window:show()
self._window:on(Event.BufLeave, function()
self:hide_window()
end)

self._window:map("n", "<Esc>", function() self:hide_window() end, {})
self._window:map("n", "q", function() self:hide_window() end, {})

self._table.bufnr = self._window.bufnr

vim.api.nvim_set_option_value('modifiable', true, { buf = self._window.bufnr })
vim.api.nvim_buf_set_lines(self._window.bufnr, 0, -1, false, {})
M._table:render()
vim.api.nvim_set_option_value('modifiable', false, { buf = self._window.bufnr })
end

---Update the table with the new arg
---@param self HexerDisplay
---@param arg string
function M._update_table(self, arg)
self._table = Table({
bufnr = 0,
ns_id = "HexerWindow",
columns = {
{ accessor_key = "ascii", header = "Ascii" },
{ accessor_key = "value", header = "Value" },
{ accessor_key = "hex", header = "Hex" },
{ accessor_key = "binary", header = "Binary" },
{ accessor_key = "octal", header = "Octal" },
},
---@type HexerItem
data = Parser.parse_input(arg),
})
end

function M._fit_table(self)
self._window:update_layout({
size = {
-- TODO: Why tf do i have to divide by 3, and why does it work??????
width = vim.api.nvim_buf_get_lines(self._window.bufnr, 0, -1, false)[1]:len() / 3,
height = vim.api.nvim_buf_line_count(self._window.bufnr),
},
relative = "win",
anchor = "NW",
position = {
row = "100%",
col = "100%"
},
})
end

function M.hexer_open(self, arg)
self:_save_buf_opts(0)

if arg ~= nil then
self:_update_table(arg)
end

self:_show_window()
self:_fit_table()
end

-- BUG: Why do we need these in order to prevent buf options from carrying over?
function M._save_buf_opts(self, buf)
for opt, _ in pairs(self._buf_opts) do
self._buf_opts[opt] = vim.api.nvim_get_option_value(opt, { buf = buf })
end
end

function M._apply_buf_opts(self, buf)
for opt, val in pairs(self._buf_opts) do
vim.api.nvim_set_option_value(opt, val, { buf = buf })
end
end

return M
93 changes: 9 additions & 84 deletions lua/hexer/init.lua
Original file line number Diff line number Diff line change
@@ -1,93 +1,18 @@
local Popup = require("nui.popup")
local Table = require("nui.table")
local Event = require("nui.utils.autocmd").event
local Parser = require("hexer.parser")

---@param data HexerItem
---@return NuiTable
local function new_table(data)
return Table({
bufnr = 0,
ns_id = "HexerWindow",
columns = {
{ accessor_key = "ascii", header = "Ascii" },
{ accessor_key = "value", header = "Value" },
{ accessor_key = "hex", header = "Hex" },
{ accessor_key = "binary", header = "Binary" },
{ accessor_key = "octal", header = "Octal" },
},
data = data,
})
end

---@class Hexer
---@field _parser HexerParser
---@field _display HexerDisplay
local M = {
---@type HexerItem
current_value = nil,
_window = Popup({
enter = true,
focusable = true,
border = {
style = "rounded",
},
position = {
row = "100%",
col = "100%",
},
size = {
width = 33,
height = 11,
},
}),
-- _table = new_table(Parser.parse_input("x69")),
_parser = require("hexer.parser"),
_display = require("hexer.display"),
}

function M.hide_window(self)
self._window:unmount()
end

function M.show_window(self)
self._window:mount()
self._window:on(Event.BufLeave, function()
self:hide_window()
end)

self._window:map("n", "<Esc>", function() self:hide_window() end, {})
self._window:map("n", "q", function() self:hide_window() end, {})

self._table.bufnr = self._window.bufnr

vim.api.nvim_buf_set_lines(self._window.bufnr, 0, -1, false, {})

M._table:render()
self._window:update_layout({
size = {
width = 33,
height = 20,
}
})
end

-- Create a hexer buffer; if the value is null, will display the previously displayed value.
---@param self table
function M.toggle_window(self)
if self.window ~= nil then
self:show_window()
else
self:hide_window()
end
end

function M.update_table(self, arg)
end
M.hexer_open = M._display.hexer_open
M.hexer_close = M._display.hide_window

function M.setup()
vim.api.nvim_create_user_command("Hexer", function(opts)
if opts.args ~= nil then
M:update_table(arg)
end

M:show_window()
end, {})
M._display:hexer_open(opts.args)
end, { nargs = "?" })
end

return M
23 changes: 18 additions & 5 deletions lua/hexer/parser.lua
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
---@class HexerParser
local M = {
---@private
_utils = require("hexer.parse_utils")
Expand Down Expand Up @@ -33,14 +34,25 @@ function M.parse_input(input)
local head, tail = 1, input:len()
local orig_head = head

local dec_val = 0

head = M._utils.check_header(input, { 'x', 'X' })
if head ~= orig_head then return { M.parse_from_int(M._utils.xtoi(input:sub(head)), { hex = input }) } end
dec_val = M._utils.xtoi(input:sub(head))
if dec_val ~= nil and head ~= orig_head then
return { M.parse_from_int(dec_val, { hex = input }) }
end

head = M._utils.check_header(input, { 'b', 'B' })
if head ~= orig_head then return { M.parse_from_int(M._utils.btoi(input:sub(head)), { binary = input }) } end
dec_val = M._utils.xtoi(input:sub(head))
if dec_val ~= nil and head ~= orig_head then
return { M.parse_from_int(M._utils.btoi(input:sub(head)), { binary = input }) }
end

head = M._utils.check_header(input, { 'o', 'O' })
if head ~= orig_head then return { M.parse_from_int(M._utils.otoi(input:sub(head)), { octal = input }) } end
dec_val = M._utils.xtoi(input:sub(head))
if dec_val ~= nil and head ~= orig_head then
return { M.parse_from_int(M._utils.otoi(input:sub(head)), { octal = input }) }
end

---@type HexerItem
local item = {}
Expand All @@ -50,8 +62,8 @@ function M.parse_input(input)
tail = tail - 1
end

for i = head, tail do
item[i - 1] = M.parse_from_int(input:byte(i), { ascii = input:sub(i, i) })
for i = 1, tail - head + 1 do
item[i] = M.parse_from_int(input:byte(head + i - 1), { ascii = input:sub(head + i - 1, head + i - 1) })
end

return item
Expand All @@ -63,6 +75,7 @@ end
---@return HexerChar
function M.parse_from_int(value, context)
---@type HexerChar
assert(value ~= nil)
local item = {
value = context.value or tostring(value),
hex = context.hex or ("0x" .. string.format("%X", value)),
Expand Down
6 changes: 5 additions & 1 deletion lua/hexer/tests/parse_utils_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ describe("Parse Utils", function()
["'p'"] = true,
['"p"'] = true,
["'potato'"] = true,
['"potato"'] = true
['"potato"'] = true,
["potato'"] = false,
["'potato"] = false,
['potato"'] = false,
['"potato'] = false,
}

local res
Expand Down
15 changes: 15 additions & 0 deletions lua/hexer/tests/parser_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -91,5 +91,20 @@ describe("Parser", function()
string.format("String: expected %s, actual %s", tostring(v), tostring(item[k])))
end
end

parsed = Parser.parse_input("EEEE")

assert(parsed)

assert(#parsed == 4, string.format("String: expected length 4, got %s", tostring(#parsed)))

for _, item in pairs(parsed) do
assert(item)

for k, v in pairs(base_target) do
assert(v == item[k],
string.format("String: expected %s, actual %s", tostring(v), tostring(item[k])))
end
end
end)
end)

0 comments on commit 9eb1ba3

Please sign in to comment.