local cd_hooks = require("cd-project.hooks") local repo = require("cd-project.project-repo") local utils = require("cd-project.utils") ---@param auto boolean are we auto creating a project? ---@return string|nil local function find_project_dir() local found = vim.fs.find( vim.g.cd_project_config.project_dir_pattern, { upward = true, stop = vim.loop.os_homedir(), path = vim.fs.dirname(vim.fn.expand("%:p")) } ) if #found == 0 then return nil end local project_dir = vim.fs.dirname(found[1]) if not project_dir or project_dir == "." or project_dir == "" or project_dir == " " then project_dir = string.match(vim.fn.execute("pwd"), "^%s*(.-)%s*$") end if not project_dir or project_dir == "." or project_dir == "" or project_dir == " " then return nil end return project_dir end ---@return string[] local function get_project_paths() local projects = repo.get_projects() local paths = {} for _, value in ipairs(projects) do table.insert(paths, value.path) end return paths end ---@return string[] local function get_project_names() local projects = repo.get_projects() local path_names = {} for _, value in ipairs(projects) do table.insert(path_names, value.name) end return path_names end ---@param path string local function path_exists_in_tab(path) for _, tabpage in ipairs(vim.api.nvim_list_tabpages()) do local tab_cwd = vim.fn.getcwd(-1, vim.api.nvim_tabpage_get_number(tabpage)) local normalized_path = utils.remove_trailing_slash(path) local normalized_tab_cwd = utils.remove_trailing_slash(tab_cwd) if normalized_path == normalized_tab_cwd then return tabpage end end return nil end ---@param dir string local function cd_project_in_tab(dir) vim.g.cd_project_last_project = vim.g.cd_project_current_project vim.g.cd_project_current_project = dir local tabnr = path_exists_in_tab(dir) if tabnr ~= nil then -- Remain in current tab if current tab holds target dir if vim.api.nvim_get_current_tabpage() == tabnr then vim.notify( "Dir already open in current tab (#" .. tabnr .. "). No action taken.", vim.log.levels.INFO, { title = "cd-project.nvim" } ) return end -- Switch to tab that holds target dir vim.api.nvim_set_current_tabpage(tabnr) vim.notify( "Dir already open in tab #" .. tabnr .. ". You have been redirected.", vim.log.levels.INFO, { title = "cd-project.nvim" } ) return end require("persistence").save() if vim.fn.bufnr("Untitled") ~= -1 then vim.api.nvim_command("bdelete! Untitled") end vim.api.nvim_command("%bdelete") vim.fn.execute("tabe | tcd " .. vim.fn.fnameescape(dir)) require("persistence").load() local hooks = cd_hooks.get_hooks(vim.g.cd_project_config.hooks, dir, "AFTER_CD") for _, hook in ipairs(hooks) do hook(dir) end end ---@param dir string local function cd_project(dir) vim.g.cd_project_last_project = vim.g.cd_project_current_project vim.g.cd_project_current_project = dir require("persistence").save() if vim.fn.bufnr("Untitled") ~= -1 then vim.api.nvim_command("bdelete! Untitled") end vim.api.nvim_command("%bdelete") vim.fn.execute("cd " .. vim.fn.fnameescape(dir)) require("persistence").load() local hooks = cd_hooks.get_hooks(vim.g.cd_project_config.hooks, dir, "AFTER_CD") for _, hook in ipairs(hooks) do hook(dir) end end ---@param path string ---@param name? string ---@param desc? string|nil ---@return CdProject.Project|nil local function build_project_obj(path, name, desc) local normalized_path = vim.fn.expand(path) if vim.fn.isdirectory(normalized_path) == 0 then return utils.log_error(normalized_path .. " is not a directory") end return { path = normalized_path, name = name or utils.get_tail_of_path(normalized_path), desc = desc, } end ---@param project CdProject.Project ---@param opts? {show_duplicate_hints: boolean} local function add_project(project, opts) local projects = repo.get_projects() opts = opts or { show_duplicate_hints = true } if vim.tbl_contains(get_project_paths(), project.path) then if opts.show_duplicate_hints then vim.notify("Project already exists: " .. project.path, vim.log.levels.INFO, { title = "cd-project.nvim" }) end return end table.insert(projects, project) repo.write_projects(projects) vim.notify("Project added: \n" .. project.path, vim.log.levels.INFO, { title = "cd-project.nvim" }) end ---@param project CdProject.Project local function delete_project(project) local projects = repo.get_projects() local new_projects = vim.tbl_filter(function(p) return p.name ~= project.name end, projects) repo.write_projects(new_projects) vim.notify("Project deleted: \n" .. project.name, vim.log.levels.INFO, { title = "cd-project.nvim" }) end ---@param opts? {auto: boolean, show_duplicate_hints: boolean} local function add_current_project(opts) local project_dir = find_project_dir() opts = vim.tbl_deep_extend("force", { auto = false, show_duplicate_hints = true }, opts) if not project_dir then if opts.auto then utils.log_info("Can't find project path of current file.\nProject not automatically created.") return end project_dir = vim.fn.getcwd() end local project = build_project_obj(project_dir) if not project then return end add_project(project, opts) end local function back() local last_project = vim.g.cd_project_last_project if not last_project then vim.notify("Can't find last project. Haven't switch project yet.") end cd_project(last_project) end return { cd_project = cd_project, cd_project_in_tab = cd_project_in_tab, build_project_obj = build_project_obj, get_project_paths = get_project_paths, get_project_names = get_project_names, add_current_project = add_current_project, add_project = add_project, delete_project = delete_project, back = back, find_project_dir = find_project_dir, }