Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions lua/tabi/ui/selector/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---@class TabiSelectorFactory
local M = {}

local config = require("tabi.config")

--- Get the appropriate selector based on configuration
---@return table Selector module with select_session function
function M.get_selector()
local selector_type = config.get().ui.selector
if selector_type == "telescope" then
return require("tabi.ui.selector.telescope")
elseif selector_type == "float" then
return require("tabi.ui.selector.float")
else
return require("tabi.ui.selector.native")
end
end

return M
142 changes: 140 additions & 2 deletions lua/tabi/ui/selector/telescope.lua
Original file line number Diff line number Diff line change
@@ -1,7 +1,145 @@
---@class TabiSelector
local M = {}

-- Telescope session selector
-- Implementation will be added in Task 10
local session_module = require("tabi.session")
local config = require("tabi.config")

--- Select a session using telescope.nvim
---@param on_select function Callback with selected session
---@param opts table|nil Options
function M.select_session(on_select, opts)
opts = opts or {}

local sessions = session_module.list()

if #sessions == 0 then
vim.notify("Tabi: No sessions found", vim.log.levels.WARN)
return
end

-- Check if telescope is available
local has_telescope, _ = pcall(require, "telescope")
if not has_telescope then
vim.notify("Tabi: telescope.nvim is not installed", vim.log.levels.ERROR)
return
end

local pickers = require("telescope.pickers")
local finders = require("telescope.finders")
local conf = require("telescope.config").values
local actions = require("telescope.actions")
local action_state = require("telescope.actions.state")
local entry_display = require("telescope.pickers.entry_display")
local previewers = require("telescope.previewers")

-- Get telescope config
local telescope_config = config.get().ui.telescope

-- Create entry display
local displayer = entry_display.create({
separator = " ",
items = {
{ width = 30 },
{ width = 12 },
{ width = 15 },
{ remaining = true },
},
})

local make_display = function(entry)
return displayer({
entry.name,
string.format("%d notes", entry.note_count),
entry.date,
entry.branch or "N/A",
})
end

-- Create previewer to show session notes
local previewer = previewers.new_buffer_previewer({
title = "Session Notes",
define_preview = function(self, entry)
local session = entry.session
local lines = {
"Session: " .. session.name,
"ID: " .. session.id,
"Branch: " .. (session.branch or "N/A"),
"Created: " .. session.created_at,
"Updated: " .. session.updated_at,
"Notes: " .. #session.notes,
"",
"--- Notes ---",
}

for i, note in ipairs(session.notes) do
table.insert(lines, string.format("%d. %s:%d", i, note.file, note.line))
-- Add first line of note content
local first_line = note.content:match("^[^\n]+") or note.content
if #first_line > 60 then
first_line = first_line:sub(1, 57) .. "..."
end
table.insert(lines, " " .. first_line)
table.insert(lines, "")
end

vim.api.nvim_buf_set_lines(self.state.bufnr, 0, -1, false, lines)
end,
})

-- Build picker options
local picker_opts = {
prompt_title = "Select Session",
finder = finders.new_table({
results = sessions,
entry_maker = function(session)
local date = vim.fn.strftime("%Y-%m-%d", vim.fn.strptime("%Y-%m-%dT%H:%M:%SZ", session.updated_at))
return {
value = session,
display = make_display,
ordinal = session.name,
name = session.name,
note_count = #session.notes,
date = date,
branch = session.branch,
session = session,
}
end,
}),
sorter = conf.generic_sorter({}),
previewer = previewer,
attach_mappings = function(prompt_bufnr)
actions.select_default:replace(function()
local selection = action_state.get_selected_entry()
actions.close(prompt_bufnr)
if selection then
vim.schedule(function()
on_select(selection.session)
end)
elseif opts.on_cancel then
vim.schedule(function()
opts.on_cancel()
end)
end
end)
return true
end,
}

-- Apply theme if configured
if telescope_config.theme then
local theme_func = require("telescope.themes")["get_" .. telescope_config.theme]
if theme_func then
picker_opts = theme_func(picker_opts)
end
end

-- Apply layout config
if telescope_config.layout_config and next(telescope_config.layout_config) then
picker_opts.layout_config =
vim.tbl_deep_extend("force", picker_opts.layout_config or {}, telescope_config.layout_config)
end

pickers.new({}, picker_opts):find()
end

return M
5 changes: 3 additions & 2 deletions plugin/tabi.lua
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ local note_module = require("tabi.note")
local float = require("tabi.ui.float")
local display = require("tabi.ui.display")
local retrace = require("tabi.retrace")
local selector = require("tabi.ui.selector.native")
local selector_factory = require("tabi.ui.selector")

-- Initialize display
display.init()
Expand Down Expand Up @@ -261,7 +261,8 @@ function commands.retrace(args)

retrace.start(session)
else
-- Show session selector
-- Show session selector based on configuration
local selector = selector_factory.get_selector()
selector.select_session(function(session)
retrace.start(session)
end)
Expand Down
114 changes: 114 additions & 0 deletions test/tabi/ui/selector_factory_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
local selector_factory = require("tabi.ui.selector")
local config = require("tabi.config")

describe("tabi.ui.selector factory", function()
local original_config

before_each(function()
-- Save original config
original_config = vim.deepcopy(config.options)
end)

after_each(function()
-- Restore original config
config.options = original_config

-- Clear cached requires to ensure fresh loads
package.loaded["tabi.ui.selector.native"] = nil
package.loaded["tabi.ui.selector.telescope"] = nil
package.loaded["tabi.ui.selector.float"] = nil
end)

describe("get_selector", function()
it("should return native selector when ui.selector is 'native'", function()
config.options.ui.selector = "native"

local selector = selector_factory.get_selector()

-- Verify it's the native module
local native = require("tabi.ui.selector.native")
assert.are.equal(native, selector)
end)

it("should return telescope selector when ui.selector is 'telescope'", function()
config.options.ui.selector = "telescope"

local selector = selector_factory.get_selector()

-- Verify it's the telescope module
local telescope = require("tabi.ui.selector.telescope")
assert.are.equal(telescope, selector)
end)

it("should return float selector when ui.selector is 'float'", function()
config.options.ui.selector = "float"

local selector = selector_factory.get_selector()

-- Verify it's the float module
local float = require("tabi.ui.selector.float")
assert.are.equal(float, selector)
end)

it("should default to native selector when ui.selector is invalid", function()
config.options.ui.selector = "invalid"

local selector = selector_factory.get_selector()

-- Should fallback to native
local native = require("tabi.ui.selector.native")
assert.are.equal(native, selector)
end)

it("should default to native selector when ui.selector is nil", function()
config.options.ui.selector = nil

local selector = selector_factory.get_selector()

-- Should fallback to native
local native = require("tabi.ui.selector.native")
assert.are.equal(native, selector)
end)

it("should return selector with select_session function", function()
config.options.ui.selector = "native"

local selector = selector_factory.get_selector()

assert.is_not_nil(selector.select_session)
assert.are.equal("function", type(selector.select_session))
end)

it("should return different selectors based on config", function()
-- Test native
config.options.ui.selector = "native"
local native_selector = selector_factory.get_selector()

-- Test telescope
config.options.ui.selector = "telescope"
local telescope_selector = selector_factory.get_selector()

-- They should be different modules
assert.are_not.equal(native_selector, telescope_selector)
end)

it("should respect runtime config changes", function()
-- Initially native
config.options.ui.selector = "native"
local first = selector_factory.get_selector()
local native = require("tabi.ui.selector.native")
assert.are.equal(native, first)

-- Change to telescope
config.options.ui.selector = "telescope"
local second = selector_factory.get_selector()
local telescope = require("tabi.ui.selector.telescope")
assert.are.equal(telescope, second)

-- Change back to native
config.options.ui.selector = "native"
local third = selector_factory.get_selector()
assert.are.equal(native, third)
end)
end)
end)
Loading