notex.nvim/lua/notex/ui/view.lua

525 lines
No EOL
14 KiB
Lua

-- Query result visualization module
local M = {}
local buffer_manager = require('notex.ui.buffer')
local utils = require('notex.utils')
-- View configurations
local view_configs = {}
-- Create query view
function M.create_query_view(query_results, options)
options = options or {}
local view_type = options.view_type or "table"
local view_config = {
view_type = view_type,
query_results = query_results,
options = options,
created_at = os.time()
}
-- Create buffer based on view type
if view_type == "table" then
return M.create_table_view(query_results, options)
elseif view_type == "cards" then
return M.create_cards_view(query_results, options)
elseif view_type == "list" then
return M.create_list_view(query_results, options)
elseif view_type == "tree" then
return M.create_tree_view(query_results, options)
else
return M.create_table_view(query_results, options)
end
end
-- Create table view
function M.create_table_view(query_results, options)
local table_options = vim.tbl_deep_extend("force", options, {
name = "notex://table-view",
view_type = "table",
include_properties = M.get_table_properties(query_results),
max_width = 120,
show_help = true
})
return buffer_manager.create_query_buffer(query_results, table_options)
end
-- Create cards view
function M.create_cards_view(query_results, options)
local cards_options = vim.tbl_deep_extend("force", options, {
name = "notex://cards-view",
view_type = "cards",
show_help = true,
wrap = true
})
local buffer_id = vim.api.nvim_create_buf(false, true)
buffer_manager.setup_buffer_options(buffer_id, cards_options)
local lines = M.generate_cards_content(query_results, cards_options)
vim.api.nvim_buf_set_lines(buffer_id, 0, -1, false, lines)
local window_id = buffer_manager.create_query_window(buffer_id, cards_options)
local config = {
buffer_id = buffer_id,
window_id = window_id,
query_results = query_results,
options = cards_options,
created_at = os.time(),
mappings = buffer_manager.setup_buffer_mappings(buffer_id, cards_options)
}
return config
end
-- Create list view
function M.create_list_view(query_results, options)
local list_options = vim.tbl_deep_extend("force", options, {
name = "notex://list-view",
view_type = "list",
show_help = true
})
local buffer_id = vim.api.nvim_create_buf(false, true)
buffer_manager.setup_buffer_options(buffer_id, list_options)
local lines = M.generate_list_content(query_results, list_options)
vim.api.nvim_buf_set_lines(buffer_id, 0, -1, false, lines)
local window_id = buffer_manager.create_query_window(buffer_id, list_options)
local config = {
buffer_id = buffer_id,
window_id = window_id,
query_results = query_results,
options = list_options,
created_at = os.time(),
mappings = buffer_manager.setup_buffer_mappings(buffer_id, list_options)
}
return config
end
-- Create tree view
function M.create_tree_view(query_results, options)
local tree_options = vim.tbl_deep_extend("force", options, {
name = "notex://tree-view",
view_type = "tree",
group_by = options.group_by or "status",
show_help = true
})
local buffer_id = vim.api.nvim_create_buf(false, true)
buffer_manager.setup_buffer_options(buffer_id, tree_options)
local lines = M.generate_tree_content(query_results, tree_options)
vim.api.nvim_buf_set_lines(buffer_id, 0, -1, false, lines)
local window_id = buffer_manager.create_query_window(buffer_id, tree_options)
local config = {
buffer_id = buffer_id,
window_id = window_id,
query_results = query_results,
options = tree_options,
created_at = os.time(),
mappings = buffer_manager.setup_buffer_mappings(buffer_id, tree_options)
}
return config
end
-- Get table properties
function M.get_table_properties(query_results)
if not query_results.documents or #query_results.documents == 0 then
return {"title", "status", "priority"}
end
-- Find most common properties
local property_counts = {}
for _, doc in ipairs(query_results.documents) do
if doc.properties then
for prop, _ in pairs(doc.properties) do
property_counts[prop] = (property_counts[prop] or 0) + 1
end
end
end
-- Sort by frequency
local sorted_props = {}
for prop, count in pairs(property_counts) do
table.insert(sorted_props, {property = prop, count = count})
end
table.sort(sorted_props, function(a, b) return a.count > b.count end)
-- Return top properties
local result = {}
for i, item in ipairs(sorted_props) do
if i > 6 then break end -- Limit to 6 columns
table.insert(result, item.property)
end
return result
end
-- Generate cards content
function M.generate_cards_content(query_results, options)
local lines = {}
-- Header
table.insert(lines, "Query Results - Card View")
table.insert(lines, string.rep("=", 50))
table.insert(lines, "")
-- Query info
if query_results.query_string then
table.insert(lines, "Query: " .. query_results.query_string)
table.insert(lines, "")
end
table.insert(lines, string.format("Found %d documents (%.2fms)",
query_results.total_count or 0,
query_results.execution_time_ms or 0))
table.insert(lines, "")
-- Document cards
if query_results.documents and #query_results.documents > 0 then
for i, doc in ipairs(query_results.documents) do
lines = M.add_document_card(lines, doc, i, options)
table.insert(lines, "")
end
else
table.insert(lines, "No documents found.")
table.insert(lines, "")
end
-- Help
lines = buffer_manager.add_help_section(lines, options)
return lines
end
-- Add document card
function M.add_document_card(lines, doc, index, options)
local max_width = options.max_width or 80
table.insert(lines, string.format("Document %d: %s", index, doc.properties and doc.properties.title or "Untitled"))
table.insert(lines, string.rep("-", max_width))
-- File path
table.insert(lines, "Path: " .. doc.file_path)
-- Properties
if doc.properties then
local sorted_props = {}
for key, value in pairs(doc.properties) do
table.insert(sorted_props, {key = key, value = value})
end
table.sort(sorted_props, function(a, b) return a.key < b.key end)
for _, prop in ipairs(sorted_props) do
if prop.key ~= "title" then
local formatted = string.format(" %s: %s", prop.key, tostring(prop.value))
if #formatted > max_width - 2 then
formatted = formatted:sub(1, max_width - 5) .. "..."
end
table.insert(lines, formatted)
end
end
end
-- Metadata
table.insert(lines, string.format(" Modified: %s", os.date("%Y-%m-%d %H:%M", doc.updated_at)))
return lines
end
-- Generate list content
function M.generate_list_content(query_results, options)
local lines = {}
-- Header
table.insert(lines, "Query Results - List View")
table.insert(lines, string.rep("=", 50))
table.insert(lines, "")
-- Query info
table.insert(lines, string.format("%d documents (%.2fms)",
query_results.total_count or 0,
query_results.execution_time_ms or 0))
table.insert(lines, "")
-- Document list
if query_results.documents and #query_results.documents > 0 then
for i, doc in ipairs(query_results.documents) do
local title = doc.properties and doc.properties.title or vim.fn.fnamemodify(doc.file_path, ":t")
local status = doc.properties and doc.properties.status or ""
local priority = doc.properties and doc.properties.priority or ""
local line = string.format("%3d. %-40s %-12s %-8s", i, M.truncate_string(title, 40), status, priority)
table.insert(lines, line)
end
else
table.insert(lines, "No documents found.")
end
table.insert(lines, "")
-- Help
lines = buffer_manager.add_help_section(lines, options)
return lines
end
-- Generate tree content
function M.generate_tree_content(query_results, options)
local lines = {}
local group_by = options.group_by or "status"
-- Header
table.insert(lines, string.format("Query Results - Tree View (Grouped by %s)", group_by))
table.insert(lines, string.rep("=", 50))
table.insert(lines, "")
-- Group documents
local groups = M.group_documents(query_results.documents, group_by)
-- Create tree structure
for group_name, group_docs in pairs(groups) do
table.insert(lines, string.format("▼ %s (%d)", group_name, #group_docs))
for i, doc in ipairs(group_docs) do
local title = doc.properties and doc.properties.title or vim.fn.fnamemodify(doc.file_path, ":t")
local line = string.format(" ├─ %s", title)
if i == #group_docs then
line = string.format(" └─ %s", title)
end
table.insert(lines, line)
end
table.insert(lines, "")
end
-- Help
lines = buffer_manager.add_help_section(lines, options)
return lines
end
-- Group documents by property
function M.group_documents(documents, group_by)
local groups = {}
for _, doc in ipairs(documents) do
local group_value = "Unknown"
if doc.properties and doc.properties[group_by] then
group_value = tostring(doc.properties[group_by])
end
if not groups[group_value] then
groups[group_value] = {}
end
table.insert(groups[group_value], doc)
end
-- Sort groups
local sorted_groups = {}
for group_name, group_docs in pairs(groups) do
table.insert(sorted_groups, {name = group_name, docs = group_docs})
end
table.sort(sorted_groups, function(a, b) return a.name < b.name end)
local result = {}
for _, group in ipairs(sorted_groups) do
result[group.name] = group.docs
end
return result
end
-- Truncate string
function M.truncate_string(str, max_length)
if #str <= max_length then
return str
end
return str:sub(1, max_length - 3) .. "..."
end
-- Switch view type
function M.switch_view_type(buffer_id, new_view_type)
local config = require('notex.ui.buffer').get_active_buffers()[buffer_id]
if not config then
return false, "Buffer not found"
end
-- Close current view
if config.window_id then
vim.api.nvim_win_close(config.window_id, true)
end
-- Create new view
local new_options = vim.tbl_deep_extend("force", config.options, {
view_type = new_view_type
})
local new_config = M.create_query_view(config.query_results, new_options)
if new_config then
return true, "View switched to " .. new_view_type
else
return false, "Failed to create new view"
end
end
-- Get available view types
function M.get_available_view_types()
return {
{
name = "table",
description = "Tabular view with sortable columns",
icon = ""
},
{
name = "cards",
description = "Card-based view with detailed information",
icon = "📄"
},
{
name = "list",
description = "Compact list view",
icon = "📋"
},
{
name = "tree",
description = "Hierarchical view grouped by properties",
icon = "🌳"
}
}
end
-- Export view to different formats
function M.export_view(buffer_id, format)
local config = require('notex.ui.buffer').get_active_buffers()[buffer_id]
if not config then
return false, "Buffer not found"
end
format = format or "markdown"
if format == "markdown" then
return M.export_to_markdown(config.query_results, config.options)
elseif format == "csv" then
return M.export_to_csv(config.query_results, config.options)
elseif format == "json" then
return M.export_to_json(config.query_results, config.options)
else
return false, "Unsupported export format: " .. format
end
end
-- Export to markdown
function M.export_to_markdown(query_results, options)
local lines = {}
table.insert(lines, "# Query Results")
table.insert(lines, "")
if query_results.query_string then
table.insert(lines, "## Query")
table.insert(lines, "```")
table.insert(lines, query_results.query_string)
table.insert(lines, "```")
table.insert(lines, "")
end
table.insert(lines, string.format("**Found %d documents** (%.2fms)",
query_results.total_count or 0,
query_results.execution_time_ms or 0))
table.insert(lines, "")
if query_results.documents and #query_results.documents > 0 then
table.insert(lines, "## Documents")
table.insert(lines, "")
for i, doc in ipairs(query_results.documents) do
table.insert(lines, string.format("### %d. %s", i, doc.properties and doc.properties.title or "Untitled"))
table.insert(lines, "")
table.insert(lines, "**File:** `" .. doc.file_path .. "`")
table.insert(lines, "")
if doc.properties then
table.insert(lines, "**Properties:**")
for key, value in pairs(doc.properties) do
table.insert(lines, string.format("- **%s:** %s", key, tostring(value)))
end
table.insert(lines, "")
end
end
end
return true, table.concat(lines, "\n")
end
-- Export to CSV
function M.export_to_csv(query_results, options)
local lines = {}
-- Header
local headers = {"#", "File", "Title", "Status", "Priority", "Created", "Modified"}
table.insert(lines, table.concat(headers, ","))
-- Data rows
if query_results.documents then
for i, doc in ipairs(query_results.documents) do
local row = {
i,
doc.file_path,
doc.properties and doc.properties.title or "",
doc.properties and doc.properties.status or "",
doc.properties and doc.properties.priority or "",
doc.created_at and os.date("%Y-%m-%d", doc.created_at) or "",
doc.updated_at and os.date("%Y-%m-%d", doc.updated_at) or ""
}
-- Escape CSV values
for j, value in ipairs(row) do
if value:find("[,\"]") then
row[j] = '"' .. value:gsub('"', '""') .. '"'
end
end
table.insert(lines, table.concat(row, ","))
end
end
return true, table.concat(lines, "\n")
end
-- Export to JSON
function M.export_to_json(query_results, options)
local export_data = {
query = query_results.query_string,
total_count = query_results.total_count,
execution_time_ms = query_results.execution_time_ms,
documents = query_results.documents,
exported_at = os.time(),
exported_by = "notex.nvim"
}
return true, vim.json.encode(export_data)
end
return M