fix: Improve options and header toggling; general improvements

This commit is contained in:
Myzel394 2023-10-07 11:20:12 +02:00
parent 25f7a873e8
commit c6752bd822
7 changed files with 174 additions and 148 deletions

View File

@ -1,11 +1,20 @@
local o = require("easytables.options")
local M = {}
---comment
---@param content string
---@param width number
---@return table
---@return string
function M:export_cell(content, width)
return "| " .. content .. string.rep(" ", width - #content) .. " "
local padding = string.rep(" ", o.options.export.markdown.padding)
return
o.options.export.markdown.characters.vertical
.. padding
.. content
.. string.rep(" ", width - #content)
.. padding
end
---Exports a line to a string
@ -21,7 +30,7 @@ function M:export_line(line, widths)
str = str .. self:export_cell(cell, width)
end
return str .. "|"
return str .. o.options.export.markdown.characters.vertical
end
---comment
@ -32,10 +41,13 @@ function M:create_header_line(widths)
-- No idea why, but "ipairs" is required otherwise lua complains
for _, width in ipairs(widths) do
str = str .. "|" .. string.rep("-", width + 2)
str =
str
.. o.options.export.markdown.characters.vertical
.. string.rep(o.options.export.markdown.characters.horizontal, width)
end
return str .. "|"
return str .. o.options.export.markdown.characters.vertical
end
---comment

View File

@ -1,7 +1,7 @@
local table = require("easytables.table")
local window = require("easytables.window")
local inputHelper = require("easytables.input")
local optionsHelper = require("easytables.options")
local o = require("easytables.options")
local function create_new_table(cols, rows)
local markdown_table = table:create(cols, rows)
@ -16,7 +16,8 @@ end
---Initialize `easytables` with the given options. This function **must** be called.
---@param options table See options.lua for available options
local function setup(options)
optionsHelper.merge_options(options)
options = options or {}
o.merge_options(options)
vim.api.nvim_create_user_command(
"EasyTablesCreateNew",

View File

@ -2,7 +2,7 @@ local string = require("string")
---Extracts the column info from the input
---@param raw_input string
---@return number, number
---@return table
local function extract_column_info(raw_input)
local _, _, cols, create_singular, rows = string.find(raw_input, "(%d+)(x?)(%d*)")

View File

@ -1,6 +1,8 @@
-- All available options are listed below. The default values are shown.
local DEFAULT = {
local options = {
table = {
-- Whether to enable the header by default
header_enabled_by_default = true,
window = {
preview_title = "Table Preview",
prompt_title = "Cell content",
@ -14,9 +16,6 @@ local DEFAULT = {
-- Filler character for empty cells
filler = " ",
align = "left",
-- Padding around the cell content, applied BOTH left AND right
-- E.g: padding = 1, content = "foo" -> " foo "
padding = 1,
},
-- Characters used to draw the table
-- Do not worry about multibyte characters, they are handled correctly
@ -39,17 +38,28 @@ local DEFAULT = {
header_horizontal = "",
}
},
export = {
markdown = {
-- Padding around the cell content, applied BOTH left AND right
-- E.g: padding = 1, content = "foo" -> " foo "
padding = 1,
-- What markdown characters are used for the export, you probably
-- don't want to change these
characters = {
horizontal = "-",
vertical = "|",
}
}
}
}
-- You can ignore everything below this line
local options = {}
local function tableMerge(t1, t2)
local function merge_tables(t1, t2)
for k, v in pairs(t2) do
if type(v) == "table" then
if type(t1[k] or false) == "table" then
tableMerge(t1[k] or {}, t2[k] or {})
merge_tables(t1[k] or {}, t2[k] or {})
else
t1[k] = v
end
@ -57,11 +67,10 @@ local function tableMerge(t1, t2)
t1[k] = v
end
end
return t1
end
local function merge_options(user_options)
options = tableMerge(DEFAULT, user_options)
merge_tables(options, user_options)
end
return {

View File

@ -1,3 +1,5 @@
local o = require("easytables.options")
local M = {};
function M:create(cols, rows)
@ -14,7 +16,12 @@ function M:create(cols, rows)
col = 1,
row = 1,
}
self.header_enabled = true
if rows > 1 then
self.header_enabled = o.options.table.header_enabled_by_default
else
self.header_enabled = false
end
return self
end
@ -28,6 +35,11 @@ function M:value_at(row, col)
end
function M:toggle_header()
if #self.table == 1 then
error("Cannot toggle header if table has only one row")
return
end
self.header_enabled = not self.header_enabled
end
@ -60,14 +72,17 @@ function M:get_largest_length()
return largest
end
function M:get_widths_for_columns(
min_width --[[ int ]],
should_use_strwidth --[[ bool ]]
) -- table
---
---@param should_use_strwidth boolean
---@return table
function M:get_widths_for_columns(should_use_strwidth)
local widths = {}
for i = 1, #self.table[1] do
widths[i] = math.max(min_width, self:get_largest_length_for_column(i, should_use_strwidth))
widths[i] = math.max(
o.options.table.cell.min_width,
self:get_largest_length_for_column(i, should_use_strwidth)
)
end
return widths
@ -172,7 +187,7 @@ function M:move_highlight_down()
end
function M:get_cell_positions(col, row, widths)
local length = #""
local length = #o.options.table.border.vertical
local start_position = 0
for i, _ in ipairs(self.table[row]) do
@ -193,50 +208,55 @@ function M:get_cell_positions(col, row, widths)
return start_position, end_position
end
function M:get_horizontal_border_width(
col, -- [[ int ]]
row, -- [[ int ]]
min_value_width -- [[ int ]]
)
local length = #""
---
---@param col boolean
---@param row boolean
---@return number, number
function M:get_horizontal_border_width(col, row)
local length = #o.options.table.border.horizontal
local start_position = 0
local widths = self:get_widths_for_columns(min_value_width, true)
local widths = self:get_widths_for_columns(true)
for i, _ in ipairs(self.table[1]) do
if i == col then
break
end
start_position = start_position + math.max(min_value_width, widths[i]) * length
start_position =
start_position
+ math.max(o.options.table.cell.min_width, widths[i]) * length
if row == 1 then
start_position = start_position + #""
start_position = start_position + #o.options.table.border.top_t
else
start_position = start_position + #""
start_position = start_position + #o.options.table.border.cross
end
end
local end_position = 0
if col == 1 then
end_position = #""
end_position = #o.options.table.border.top_t
else
end_position = #""
end_position = #o.options.table.border.right_t
end
end_position = end_position + start_position + math.max(min_value_width, widths[col]) * length
end_position =
end_position
+ start_position
+ math.max(o.options.table.cell.min_width, widths[col]) * length
if row == 1 then
if col == 1 then
end_position = end_position + #""
end_position = end_position + #o.options.table.border.top_t
else
end_position = end_position + #""
end_position = end_position + #o.options.table.border.top_right
end
else
if col == 1 then
end_position = end_position + #""
end_position = end_position + #o.options.table.border.left_t
else
end_position = end_position + #""
end_position = end_position + #o.options.table.border.right_t
end
end

View File

@ -1,27 +1,6 @@
local M = {};
local o = require("easytables.options")
DEFAULT_DRAW_REPRESENTATION_OPTIONS = {
min_width = 3,
filler = " ",
top_left = "",
top_right = "",
bottom_left = "",
bottom_right = "",
horizontal = "",
vertical = "",
left_t = "",
right_t = "",
top_t = "",
bottom_t = "",
cross = "",
header_left_t = "",
header_right_t = "",
header_bottom_t = "",
header_cross = "",
header_horizontal = "",
}
function create_horizontal_line(cell_widths, left, middle, right, middle_t)
local function create_horizontal_line(cell_widths, left, middle, right, middle_t)
local string = ""
string = string .. left
@ -39,81 +18,92 @@ function create_horizontal_line(cell_widths, left, middle, right, middle_t)
return string
end
-- Creates a horizontal divider like this:
-- `create_horizontal_divider(5, 5, {variant = "top"})`:
-- ┌─────┬─────┬─────┬─────┬─────┐
-- `create_horizontal_divider(5, 5, {variant = "between"})`:
-- ├─────┼─────┼─────┼─────┼─────┤
-- `create_horizontal_divider(5, 5, {variant = "bottom"})`:
-- └─────┴─────┴─────┴─────┴─────┘
function create_horizontal_divider(
---Creates a horizontal divider like this:
---`create_horizontal_divider(5, 5, {variant = "top"})`:
---┌─────┬─────┬─────┬─────┬─────┐
---`create_horizontal_divider(5, 5, {variant = "between"})`:
---├─────┼─────┼─────┼─────┼─────┤
---`create_horizontal_divider(5, 5, {variant = "bottom"})`:
---└─────┴─────┴─────┴─────┴─────┘
---@param table table
---@param[opt="between"] variant string Either "top", "between" or "bottom"
---@return string
local function create_horizontal_divider(
table,
options -- [[ table ]] -- optional
variant
)
local options = options or {}
local top_left = options.top_left or DEFAULT_DRAW_REPRESENTATION_OPTIONS.top_left
local top_right = options.top_right or DEFAULT_DRAW_REPRESENTATION_OPTIONS.top_right
local bottom_left = options.bottom_left or DEFAULT_DRAW_REPRESENTATION_OPTIONS.bottom_left
local bottom_right = options.bottom_right or DEFAULT_DRAW_REPRESENTATION_OPTIONS.bottom_right
local horizontal = options.horizontal or DEFAULT_DRAW_REPRESENTATION_OPTIONS.horizontal
local left_t = options.left_t or DEFAULT_DRAW_REPRESENTATION_OPTIONS.left_t
local right_t = options.right_t or DEFAULT_DRAW_REPRESENTATION_OPTIONS.right_t
local top_t = options.top_t or DEFAULT_DRAW_REPRESENTATION_OPTIONS.top_t
local bottom_t = options.bottom_t or DEFAULT_DRAW_REPRESENTATION_OPTIONS.bottom_t
local cross = options.cross or DEFAULT_DRAW_REPRESENTATION_OPTIONS.cross
local header_left_t = options.header_left_t or DEFAULT_DRAW_REPRESENTATION_OPTIONS.header_left_t
local header_right_t = options.header_right_t or DEFAULT_DRAW_REPRESENTATION_OPTIONS.header_right_t
local header_cross = options.header_cross or DEFAULT_DRAW_REPRESENTATION_OPTIONS.header_cross
local header_horizontal = options.header_horizontal or DEFAULT_DRAW_REPRESENTATION_OPTIONS.header_horizontal
local min_width = options.min_width or DEFAULT_DRAW_REPRESENTATION_OPTIONS.min_width
local variant = options.variant or "between"
variant = variant or "between"
local widths = table:get_widths_for_columns(min_width)
local widths = table:get_widths_for_columns()
if variant == "top" then
return create_horizontal_line(widths, top_left, horizontal, top_right, top_t)
elseif variant == "between" then
return create_horizontal_line(widths, left_t, horizontal, right_t, cross)
elseif variant == "bottom" then
return create_horizontal_line(widths, bottom_left, horizontal, bottom_right, bottom_t)
elseif variant == "header" then
return create_horizontal_line(widths, header_left_t, header_horizontal, header_right_t, header_cross)
end
end
function table.draw_representation(
table, -- [[ table ]]
options -- [[ table ]] -- optional
return create_horizontal_line(
widths,
o.options.table.border.top_left,
o.options.table.border.horizontal,
o.options.table.border.top_right,
o.options.table.border.top_t
)
local options = options or {}
local min_width = options.min_width or DEFAULT_DRAW_REPRESENTATION_OPTIONS.min_width
local filler = options.filler or DEFAULT_DRAW_REPRESENTATION_OPTIONS.filler
local vertical = options.vertical or DEFAULT_DRAW_REPRESENTATION_OPTIONS.vertical
elseif variant == "between" then
return create_horizontal_line(
widths,
o.options.table.border.left_t,
o.options.table.border.horizontal,
o.options.table.border.right_t,
o.options.table.border.cross
)
elseif variant == "bottom" then
return create_horizontal_line(
widths,
o.options.table.border.bottom_left,
o.options.table.border.horizontal,
o.options.table.border.bottom_right,
o.options.table.border.bottom_t
)
elseif variant == "header" then
return create_horizontal_line(
widths,
o.options.table.border.header_left_t,
o.options.table.border.header_horizontal,
o.options.table.border.header_right_t,
o.options.table.border.header_cross
)
end
return ""
end
---Draws a table representation for the preview
---@param table table
---@return table
local function draw_representation(table)
local representation = {}
local horizontal_divider = create_horizontal_divider(table, options)
local horizontal_divider = create_horizontal_divider(table, "between")
representation[#representation + 1] = create_horizontal_divider(table, { variant = "top" })
representation[#representation + 1] = create_horizontal_divider(table, "top")
local column_widths = table:get_widths_for_columns(min_width)
local column_widths = table:get_widths_for_columns()
for i = 1, table:rows_amount() do
local line = ""
for j = 1, table:cols_amount() do
local length = column_widths[j]
local cell = table:value_at(i, j)
local cell_width = vim.api.nvim_strwidth(cell)
if cell_width < length then
cell = cell .. string.rep(filler, length - cell_width)
cell = cell
.. string.rep(o.options.table.cell.filler, length - cell_width)
end
cell = vertical .. cell
-- Add left vertical divider
cell = o.options.table.border.vertical .. cell
-- Add most right vertical divider
if j == table:cols_amount() then
cell = cell .. vertical
cell = cell .. o.options.table.border.vertical
end
line = line .. cell
@ -122,25 +112,17 @@ function table.draw_representation(
representation[#representation + 1] = line
if i == 1 and table.header_enabled then
representation[#representation + 1] = create_horizontal_divider(table, { variant = "header" })
representation[#representation + 1] = create_horizontal_divider(table, "header")
elseif i ~= table:rows_amount() then
representation[#representation + 1] = horizontal_divider
end
end
representation[#representation + 1] = create_horizontal_divider(table, { variant = "bottom" })
representation[#representation + 1] = create_horizontal_divider(table, "bottom")
return representation
end
function table.from_representation(representation, options)
local opts = options or {}
local table = {}
for i = 1, #representation do
local character = representation[i]
end
end
return table
return {
draw_representation = draw_representation
}

View File

@ -1,37 +1,33 @@
local table_builder = require("easytables.tablebuilder")
local export = require("easytables.export")
local o = require("easytables.options")
local math = require("math")
local M = {}
DEFAULT_OPTIONS = {
title = "Table",
prompt_title = "Content",
width = 60,
height = 30,
min_value_width = 3,
}
function M:create(table, options)
options = options or {}
self.title = options.title or DEFAULT_OPTIONS.title
self.prompt_title = options.prompt_title or DEFAULT_OPTIONS.prompt_title
self.width = options.width or DEFAULT_OPTIONS.width
self.height = options.height or DEFAULT_OPTIONS.height
self.min_value_width = options.min_value_width or DEFAULT_OPTIONS.min_value_width
self.table = table
self.previous_window = vim.api.nvim_get_current_win()
return self
end
function M:_get_width()
return 60
end
function M:_get_preview_height()
return 20
end
function M:get_x()
return math.floor((vim.o.columns - self.width) / 2)
return math.floor((vim.o.columns - self:_get_width()) / 2)
end
function M:get_y()
return math.floor(((vim.o.lines - self.height) / 2) - 1)
return math.floor(((vim.o.lines - self:_get_preview_height()) / 2) - 1)
end
function M:_open_preview_window()
@ -40,30 +36,34 @@ function M:_open_preview_window()
relative = "win",
col = self:get_x(),
row = self:get_y(),
width = self.width,
height = self.height,
width = self:_get_width(),
height = self:_get_preview_height(),
style = "minimal",
border = "rounded",
title = self.title,
title = o.options.table.window.preview_title,
title_pos = "center",
})
-- Disable default highlight
vim.api.nvim_set_option_value("winhighlight", "Normal:Normal",
{ win = self.preview_window })
vim.api.nvim_set_option_value(
"winhighlight",
"Normal:Normal",
{ win = self.preview_window }
)
end
function M:_open_prompt_window()
self.prompt_buffer = vim.api.nvim_create_buf(false, false)
self.prompt_window = vim.api.nvim_open_win(self.prompt_buffer, true, {
relative = "win",
-- No idea why, but the window is shifted one cell to the right by default
col = self:get_x() - 1,
row = self:get_y() + self.height + 2,
width = self.width,
row = self:get_y() + self:_get_preview_height() + 2,
width = self:_get_width(),
height = 2,
style = "minimal",
border = "rounded",
title = self.prompt_title,
title = o.options.table.window.prompt_title,
title_pos = "center",
})
@ -147,7 +147,9 @@ function M:close()
vim.api.nvim_win_close(self.preview_window, true)
vim.api.nvim_win_close(self.prompt_window, true)
pcall(function()
vim.api.nvim_set_current_win(self.previous_window)
end)
self.preview_window = nil
self.prompt_window = nil