Skip to content

Commit

Permalink
Merge pull request #6 from stefanwatt/feature/telescope
Browse files Browse the repository at this point in the history
Telescope support & Refactor
  • Loading branch information
LintaoAmons authored Jan 26, 2024
2 parents cab50c7 + e34293d commit 8d082de
Show file tree
Hide file tree
Showing 11 changed files with 204 additions and 107 deletions.
4 changes: 0 additions & 4 deletions lua/cd-project/_types.lua

This file was deleted.

12 changes: 12 additions & 0 deletions lua/cd-project/adapter/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
local function cd_project()
local adapter = vim.g.cd_project_config.adapter
if adapter == "telescope" then
return require("cd-project.adapter.telescope").cd_project()
end

require("cd-project.adapter.vim-ui").cd_project()
end

return {
cd_project = cd_project,
}
48 changes: 48 additions & 0 deletions lua/cd-project/adapter/telescope.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
local project = require("cd-project.project-repo")
local api = require("cd-project.api")

---@param opts? table
local cd_project = function(opts)
local utils = require("cd-project.utils")
local success, picker = pcall(require, "telescope.pickers")
if not success then
utils.log_error("telescope not installed")
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")
opts = opts or {}
pickers
.new(opts, {
attach_mappings = function(prompt_bufnr, map)
actions.select_default:replace(function()
actions.close(prompt_bufnr)
---@type CdProject.Project
local selected_project = action_state.get_selected_entry().value
api.cd_project(selected_project.path)
end)
return true
end,
prompt_title = "cd to project",
finder = finders.new_table({
results = project.get_projects(),
---@param project_entry CdProject.Project
entry_maker = function(project_entry)
return {
value = project_entry,
display = project_entry.path,
ordinal = project_entry.path,
}
end,
}),
sorter = conf.generic_sorter(opts),
})
:find()
end

return {
cd_project = cd_project,
}
11 changes: 5 additions & 6 deletions lua/cd-project/adapter/vim-ui.lua
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
local utils = require("cd-project.utils")
local api = require("cd-project.api")
local function logErr(msg)
vim.notify(msg, vim.log.levels.ERROR, { title = "cd-project.nvim" })
end

-- TODO: how to make this level purely to get user input and pass to the api functions
local function cd_project()
---@param opts? table
local function cd_project(opts)
opts = opts or {}
vim.ui.select(api.get_project_paths(), {
prompt = "Select a directory",
}, function(dir)
if not dir then
return logErr("Must select a valid dir")
return utils.log_error("Must select a valid dir")
end
api.cd_project(dir)
end)
Expand Down
22 changes: 11 additions & 11 deletions lua/cd-project/api.lua
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
local config = require("cd-project.config")
local function logErr(msg)
vim.notify(msg, vim.log.levels.ERROR, { title = "cd-project.nvim" })
end
local cd_hooks = require("cd-project.hooks")
local project = require("cd-project.project-repo")
local utils = require("cd-project.utils")

---@return string|nil
function find_project_dir()
local function find_project_dir()
local found = vim.fs.find(
config.config.project_dir_pattern,
vim.g.cd_project_config.project_dir_pattern,
{ upward = true, stop = vim.loop.os_homedir(), path = vim.fs.dirname(vim.fn.expand("%:p")) }
)

Expand All @@ -29,7 +28,7 @@ end

---@return string[]
local function get_project_paths()
local projects = config.get_projects()
local projects = project.get_projects()
local paths = {}
for _, value in ipairs(projects) do
table.insert(paths, value.path)
Expand All @@ -43,7 +42,8 @@ local function cd_project(dir)
vim.g.cd_project_current_project = dir
vim.fn.execute("cd " .. dir)

local hooks = config.get_hooks(dir, "AFTER_CD")
local hooks = cd_hooks.get_hooks(vim.g.cd_project_config.hooks, dir, "AFTER_CD")
print("DEBUGPRINT[1]: api.lua:45: hooks=" .. vim.inspect(hooks))
for _, hook in ipairs(hooks) do
hook(dir)
end
Expand All @@ -53,10 +53,10 @@ local function add_current_project()
local project_dir = find_project_dir()

if not project_dir then
return logErr("Can't find project path of current file")
return utils.log_err("Can't find project path of current file")
end

local projects = config.get_projects()
local projects = project.get_projects()

if vim.tbl_contains(get_project_paths(), project_dir) then
return vim.notify("Project already exists: " .. project_dir)
Expand All @@ -67,7 +67,7 @@ local function add_current_project()
name = "name place holder", -- TODO: allow user to edit the name of the project
}
table.insert(projects, new_project)
config.write_projects(projects)
project.write_projects(projects)
vim.notify("Project added: \n" .. project_dir)
end

Expand Down
95 changes: 11 additions & 84 deletions lua/cd-project/config.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
---@alias CdProject.Adapter "telescope"|"vim-ui"

---@class CdProject.Config
---@field projects_config_filepath string
---@field project_dir_pattern string[]
---@field hooks? CdProject.Hook[]
---@field project_peeker? CdProject.Adapter

---@type CdProject.Config
local default_config = {
Expand Down Expand Up @@ -28,95 +32,18 @@ local default_config = {
end,
},
},
projects_picker = "vim-ui", -- optional, you can switch to `telescope`
}

local M = {
config = default_config,
}
local M = {}

---@type CdProject.Config
vim.g.cd_project_config = default_config

---@param user_config? CdProject.Config
M.setup = function(user_config)
M.config = vim.tbl_deep_extend("force", default_config, user_config or {})
end

---@param tbl table
---@param path string
local write_json_file = function(tbl, path)
local content = vim.fn.json_encode(tbl) -- Encoding table to JSON string

local file, err = io.open(path, "w")
if not file then
error("Could not open file: " .. err)
return nil
end

file:write(content)
file:close()
end

---@param path string
---@return table
local read_or_init_json_file = function(path)
local file, _ = io.open(path, "r")
if not file then
write_json_file({}, path)
return {}
end

local content = file:read("*a") -- Read the entire content
file:close()

return vim.fn.json_decode(content) or {}
end

---@return CdProject.Project[]
M.get_projects = function()
return read_or_init_json_file(M.config.projects_config_filepath)
end

---@param projects CdProject.Project[]
M.write_projects = function(projects)
write_json_file(projects, M.config.projects_config_filepath)
end

---@param dir string
---@return function[]
M.get_hooks = function(dir, point)
local hooks = M.config.hooks
local matching_hooks = {}

for _, hook in ipairs(hooks) do
local matches = false
local trigger_point = hook.trigger_point or "AFTER_CD"

-- Check if match_rule exists and returns true
if hook.match_rule == nil and hook.pattern == nil then
matches = true
elseif hook.match_rule and hook.match_rule(dir) and trigger_point == point then
matches = true
-- If no match_rule, check if pattern exists in dir
elseif hook.pattern and dir:find(hook.pattern) and trigger_point == point then
matches = true
end

-- Add hook to matching_hooks if it matches
if matches then
table.insert(matching_hooks, hook)
end
end

-- Sort hooks by order if order is defined
table.sort(matching_hooks, function(a, b)
return (a.order or 0) < (b.order or 0)
end)

-- Extract and return the callback functions from the matching hooks
local callbacks = {}
for _, hook in ipairs(matching_hooks) do
table.insert(callbacks, hook.callback)
end

return callbacks
local previous_config = vim.g.cd_project_config or default_config
vim.g.cd_project_config = vim.tbl_deep_extend("force", previous_config, user_config or {}) or default_config
end

return M
51 changes: 51 additions & 0 deletions lua/cd-project/hooks.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
---@class CdProject.Hook
---@field callback fun(param: string)
---@field name? string
---@field order? number
---@field trigger_point? string
---@field pattern? string
---@field match_rule? fun(dir: string): boolean
---
---@param hooks CdProject.Hook[]
---@param dir string
---@param point string
---@return function[]
local get_hooks = function(hooks, dir, point)
local matching_hooks = {}
for _, hook in ipairs(hooks) do
local matches = false
local trigger_point = hook.trigger_point or "AFTER_CD"

-- Check if match_rule exists and returns true
if hook.match_rule == nil and hook.pattern == nil then
matches = true
elseif hook.match_rule and hook.match_rule(dir) and trigger_point == point then
matches = true
-- If no match_rule, check if pattern exists in dir
elseif hook.pattern and dir:find(hook.pattern) and trigger_point == point then
matches = true
end

-- Add hook to matching_hooks if it matches
if matches then
table.insert(matching_hooks, hook)
end
end

-- Sort hooks by order if order is defined
table.sort(matching_hooks, function(a, b)
return (a.order or 0) < (b.order or 0)
end)

-- Extract and return the callback functions from the matching hooks
local callbacks = {}
for _, hook in ipairs(matching_hooks) do
table.insert(callbacks, hook.callback)
end

return callbacks
end

return {
get_hooks = get_hooks,
}
34 changes: 34 additions & 0 deletions lua/cd-project/json.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---@param tbl table
---@param path string
local write_json_file = function(tbl, path)
local content = vim.fn.json_encode(tbl) -- Encoding table to JSON string

local file, err = io.open(path, "w")
if not file then
error("Could not open file: " .. err)
return nil
end

file:write(content)
file:close()
end

---@param path string
---@return table
local read_or_init_json_file = function(path)
local file, _ = io.open(path, "r")
if not file then
write_json_file({}, path)
return {}
end

local content = file:read("*a") -- Read the entire content
file:close()

return vim.fn.json_decode(content) or {}
end

return {
write_json_file = write_json_file,
read_or_init_json_file = read_or_init_json_file,
}
20 changes: 20 additions & 0 deletions lua/cd-project/project-repo.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
local json = require("cd-project.json")
---@class CdProject.Project
---@field path string
---@field name string
---@field desc string|nil

---@return CdProject.Project[]
local get_projects = function()
return json.read_or_init_json_file(vim.g.cd_project_config.projects_config_filepath)
end

---@param projects CdProject.Project[]
local write_projects = function(projects)
json.write_json_file(projects, vim.g.cd_project_config.projects_config_filepath)
end

return {
get_projects = get_projects,
write_projects = write_projects,
}
10 changes: 10 additions & 0 deletions lua/cd-project/utils.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
local M = {}

---@param msg string
local function log_error(msg)
vim.notify(msg, vim.log.levels.ERROR, { title = "cd-project.nvim" })
end

return {
log_error = log_error,
}
4 changes: 2 additions & 2 deletions plugin/cd-project.lua
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ end
vim.g.loaded_cd_project = 1

require("cd-project").setup()
local adapter = require("cd-project.adapter")
local api = require("cd-project.api")
local vimui = require("cd-project.adapter.vim-ui")
vim.g.cd_project_current_project = api.find_project_dir()

vim.api.nvim_create_user_command("CdProject", vimui.cd_project, {})
vim.api.nvim_create_user_command("CdProject", adapter.cd_project, {})
vim.api.nvim_create_user_command("CdProjectAdd", api.add_current_project, {})
vim.api.nvim_create_user_command("CdProjectBack", api.back, {})

0 comments on commit 8d082de

Please sign in to comment.