-- 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