mirror of
https://github.com/Myzel394/easytables.nvim.git
synced 2025-06-18 14:55:26 +02:00
275 lines
7.1 KiB
Lua
275 lines
7.1 KiB
Lua
local o = require("easytables.options")
|
|
|
|
local M = {};
|
|
|
|
function M:create(cols, rows)
|
|
local table = {}
|
|
for i = 1, rows do
|
|
table[i] = {}
|
|
for j = 1, cols do
|
|
table[i][j] = ""
|
|
end
|
|
end
|
|
|
|
self.table = table
|
|
self.highlighted_cell = {
|
|
col = 1,
|
|
row = 1,
|
|
}
|
|
|
|
if rows > 1 then
|
|
self.header_enabled = o.options.table.header_enabled_by_default
|
|
else
|
|
self.header_enabled = false
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
function M:insert(col, row, value)
|
|
self.table[row][col] = value
|
|
end
|
|
|
|
function M:value_at(row, col)
|
|
return self.table[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
|
|
|
|
function M:get_largest_length_for_column(
|
|
col, --[[ int ]]
|
|
should_use_strwidth --[[ bool ]]
|
|
) -- int
|
|
should_use_strwidth = should_use_strwidth or false
|
|
|
|
local largest = 0
|
|
for _, row in ipairs(self.table) do
|
|
if #row[col] > largest then
|
|
largest = should_use_strwidth and vim.api.nvim_strwidth(row[col]) or #row[col]
|
|
end
|
|
end
|
|
|
|
return largest
|
|
end
|
|
|
|
function M:get_largest_length()
|
|
local largest = #self.table[1][1]
|
|
for _, row in ipairs(self.table) do
|
|
for _, col in ipairs(row) do
|
|
if #col > largest then
|
|
largest = #col
|
|
end
|
|
end
|
|
end
|
|
|
|
return largest
|
|
end
|
|
|
|
---
|
|
---@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(
|
|
o.options.table.cell.min_width,
|
|
self:get_largest_length_for_column(i, should_use_strwidth)
|
|
)
|
|
end
|
|
|
|
return widths
|
|
end
|
|
|
|
function M:rows_amount()
|
|
return #self.table
|
|
end
|
|
|
|
function M:cols_amount()
|
|
return #self.table[1]
|
|
end
|
|
|
|
function M:set_highlighted_cell(cell)
|
|
self.highlighted_cell = cell
|
|
end
|
|
|
|
function M:get_highlighted_cell()
|
|
return self.highlighted_cell
|
|
end
|
|
|
|
-- Jumps to next cell in row. If there is no next cell, it jumps to the first cell in the next row.
|
|
function M:move_highlight_to_next_cell()
|
|
if self.highlighted_cell.col == self:cols_amount() then
|
|
if self.highlighted_cell.row == self:rows_amount() then
|
|
-- Reset highlight to the first cell
|
|
self.highlighted_cell = {
|
|
col = 1,
|
|
row = 1,
|
|
}
|
|
else
|
|
self.highlighted_cell = {
|
|
col = 1,
|
|
row = self.highlighted_cell.row + 1,
|
|
}
|
|
end
|
|
else
|
|
self.highlighted_cell = {
|
|
col = self.highlighted_cell.col + 1,
|
|
row = self.highlighted_cell.row,
|
|
}
|
|
end
|
|
end
|
|
|
|
-- Jumps to previous cell in row. If there is no previous cell, it jumps to the last cell in the previous row.
|
|
function M:move_highlight_to_previous_cell()
|
|
if self.highlighted_cell.col == 1 then
|
|
if self.highlighted_cell.row == 1 then
|
|
-- Reset highlight to the last cell
|
|
self.highlighted_cell = {
|
|
col = self:cols_amount(),
|
|
row = self:rows_amount(),
|
|
}
|
|
else
|
|
self.highlighted_cell = {
|
|
col = self:cols_amount(),
|
|
row = self.highlighted_cell.row - 1,
|
|
}
|
|
end
|
|
else
|
|
self.highlighted_cell = {
|
|
col = self.highlighted_cell.col - 1,
|
|
row = self.highlighted_cell.row,
|
|
}
|
|
end
|
|
end
|
|
|
|
-- Moves highlight to the right, jumps back to the first cell in the same row if it is already at the rightmost cell.
|
|
function M:move_highlight_right()
|
|
if self.highlighted_cell.col == self:cols_amount() then
|
|
self.highlighted_cell.col = 1
|
|
else
|
|
self.highlighted_cell.col = self.highlighted_cell.col + 1
|
|
end
|
|
end
|
|
|
|
-- Moves highlight to the left, jumps back to the last cell in the same row if it is already at the leftmost cell.
|
|
function M:move_highlight_left()
|
|
if self.highlighted_cell.col == 1 then
|
|
self.highlighted_cell.col = self:cols_amount()
|
|
else
|
|
self.highlighted_cell.col = self.highlighted_cell.col - 1
|
|
end
|
|
end
|
|
|
|
-- Moves highlight to the top, jumps back to the last row if it is already at the topmost row.
|
|
function M:move_highlight_up()
|
|
if self.highlighted_cell.row == 1 then
|
|
self.highlighted_cell.row = self:rows_amount()
|
|
else
|
|
self.highlighted_cell.row = self.highlighted_cell.row - 1
|
|
end
|
|
end
|
|
|
|
-- Moves highlight to the bottom, jumps back to the first row if it is already at the bottommost row.
|
|
function M:move_highlight_down()
|
|
if self.highlighted_cell.row == self:rows_amount() then
|
|
self.highlighted_cell.row = 1
|
|
else
|
|
self.highlighted_cell.row = self.highlighted_cell.row + 1
|
|
end
|
|
end
|
|
|
|
function M:get_cell_positions(col, row, widths)
|
|
local length = #o.options.table.border.vertical
|
|
local start_position = 0
|
|
|
|
for i, _ in ipairs(self.table[row]) do
|
|
if i == col then
|
|
break
|
|
end
|
|
|
|
start_position = start_position + widths[i] + length
|
|
end
|
|
|
|
local end_position = math.max(length, start_position) + widths[col] + length
|
|
|
|
if col ~= 1 then
|
|
-- Add `length again because of the border left and right
|
|
end_position = end_position + length
|
|
end
|
|
|
|
return start_position, end_position
|
|
end
|
|
|
|
---
|
|
---@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(true)
|
|
|
|
for i, _ in ipairs(self.table[1]) do
|
|
if i == col then
|
|
break
|
|
end
|
|
|
|
start_position =
|
|
start_position
|
|
+ math.max(o.options.table.cell.min_width, widths[i]) * length
|
|
|
|
if row == 1 then
|
|
start_position = start_position + #o.options.table.border.top_t
|
|
else
|
|
start_position = start_position + #o.options.table.border.cross
|
|
end
|
|
end
|
|
|
|
local end_position = 0
|
|
|
|
if col == 1 then
|
|
end_position = #o.options.table.border.top_t
|
|
else
|
|
end_position = #o.options.table.border.right_t
|
|
end
|
|
|
|
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 + #o.options.table.border.top_t
|
|
else
|
|
end_position = end_position + #o.options.table.border.top_right
|
|
end
|
|
else
|
|
if col == 1 then
|
|
end_position = end_position + #o.options.table.border.left_t
|
|
else
|
|
end_position = end_position + #o.options.table.border.right_t
|
|
end
|
|
end
|
|
|
|
return start_position, end_position
|
|
end
|
|
|
|
function M:swap_contents(first, second)
|
|
local first_value = self:value_at(first.row, first.col)
|
|
local second_value = self:value_at(second.row, second.col)
|
|
|
|
self:insert(first.col, first.row, second_value)
|
|
self:insert(second.col, second.row, first_value)
|
|
end
|
|
|
|
return M
|