diff --git a/.luacheckrc b/.luacheckrc new file mode 100644 index 0000000..eaad807 --- /dev/null +++ b/.luacheckrc @@ -0,0 +1,7 @@ +-- Rerun tests only if their modification time changed. +cache = true + +-- Global objects defined by the C code +read_globals = { + "vim", +} diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..239b0e2 --- /dev/null +++ b/Makefile @@ -0,0 +1,7 @@ +.PHONY: lint tests + +lint: + luacheck ./lua/telescope + +tests: + nvim --headless -c "PlenaryBustedDirectory lua/tests/" diff --git a/README.md b/README.md index b3df402..cb9f410 100644 --- a/README.md +++ b/README.md @@ -15,51 +15,78 @@ You can setup the extension by adding the following to your config: require'telescope'.load_extension('project') ``` +You may skip explicitly loading extensions (they will then be lazy-loaded), but tab completions will not be available right away. + ## Available functions: ### Project -The projects picker. +The `projects` picker: ```lua require'telescope'.extensions.project.project{} ``` -## Example config: +## Default mappings (normal mode): + +| Key | Description | +|-----|---------------------------------------------------------------| +| `d` | delete currently selected project | +| `r` | rename currently selected project | +| `c` | create a project\* | +| `s` | search inside files within your project | +| `b` | browse inside files within your project | +| `w` | change to the selected project's directory without opening it | +| `R` | find a recently opened file within your project | +| `f` | find a file within your project (same as \) | + +\* *defaults to your git root if used inside a git project, otherwise, it will use your current working directory* + +Example key map config: ```lua vim.api.nvim_set_keymap( - 'n', - '', - ":lua require'telescope'.extensions.project.project{}", - {noremap = true, silent = true} + 'n', + '', + ":lua require'telescope'.extensions.project.project{}", + {noremap = true, silent = true} ) ``` - -## Default mappings (normal mode): - -d: delete currently selected project -r: rename currently selected project -c: create a project (defaults to your git root if used inside a git project, -otherwise it will use your current working directory) -s: search inside files within your project -b: browse inside files within your project -w: change to the selected project's directory without opening it -r: find a recently opened file within your project -f: find a file within your project (this works the same as \) - + ## Available options: -Options can be added when requiring telescope project, as shown below: -```lua require'telescope'.extensions.project.project{ display_type = 'full' }``` +| Keys | Description | Options | +|----------------|---------------------------------------------|-------------------------------| +| `display_type` | Show the title and the path of the project | 'full' or 'minimal' (default) | -display_type: +Options can be added when requiring telescope-project, as shown below: -- 'full' (Show the title and the path of the project) -- 'minimal' (Default. Show the title of the project only) +```lua +lua require'telescope'.extensions.project.project{ display_type = 'full' } +``` + +## Available setup settings: + +| Keys | Description | Options | +|-------------|--------------------------------------------------|------------------------| +| `base_dir` | path to projects (all git repos will be added) | string (default: nil) | +| `max_depth` | maximum depth to recursively search for projects | integer (default: 3) | + +Setup settings can be added when requiring telescope, as shown below: + +```lua +require('telescope').setup { + extensions = { + project = { + base_dir = '~/projects', + max_depth = 3 + } + } +} +``` ## Roadmap :blue_car: - order projects by last opened :heavy_check_mark: +- add all (git-enabled) subdirectories automatically :heavy_check_mark: - workspaces :construction: -- add all (git-enabled) subdirectories automatically :construction: diff --git a/lua/telescope/_extensions/project.lua b/lua/telescope/_extensions/project.lua index 637afe6..6e939ec 100644 --- a/lua/telescope/_extensions/project.lua +++ b/lua/telescope/_extensions/project.lua @@ -1,155 +1,14 @@ local has_telescope, telescope = pcall(require, 'telescope') +local main = require('telescope._extensions.project.main') +local utils = require('telescope._extensions.project.utils') if not has_telescope then error('This plugins requires nvim-telescope/telescope.nvim') end -local actions = require("telescope.actions") -local action_state = require("telescope.actions.state") -local finders = require("telescope.finders") -local pickers = require("telescope.pickers") -local conf = require("telescope.config").values -local entry_display = require("telescope.pickers.entry_display") -local utils = require("telescope.utils") +utils.init_file() -local project_actions = require("telescope._extensions.project_actions") - -local project_dirs_file = vim.fn.stdpath('data') .. '/telescope-projects.txt' - --- Checks if the file containing the list of project --- directories already exists. --- If it doesn't exist, it creates it. -local function check_for_project_dirs_file() - local f = io.open(project_dirs_file, "r") - if f ~= nil then - io.close(f) - return true - else - local newFile = io.open(project_dirs_file, "w") - newFile:write() - newFile:close() - end -end - --- Creates a Telescope `finder` based on the given options --- and list of projects -local create_finder = function(opts, projects) - local display_type = opts.display_type - local widths = { - title = 0, - dir = 0, - } - - -- Loop over all of the projects and find the maximum length of - -- each of the keys - for _,entry in pairs(projects) do - if display_type == 'full' then - entry.dir = '[' .. entry.path .. ']' - else - entry.dir = '' - end - for key, value in pairs(widths) do - widths[key] = math.max(value,utils.strdisplaywidth(entry[key] or '')) - end - end - - local displayer = entry_display.create { - separator = " ", - items = { - { width = widths.title }, - { width = widths.dir }, - } - } - local make_display = function(entry) - return displayer { - { entry.title }, - { entry.dir } - } - end - - return finders.new_table { - results = projects, - entry_maker = function(entry) - entry.value = entry.path - entry.ordinal = entry.title - entry.display = make_display - return entry - end, - } -end - -local get_last_accessed_time = function(path) - local expanded_path = vim.fn.expand(path) - local fs_stat = vim.loop.fs_stat(expanded_path) - if fs_stat then - return fs_stat.atime.sec - else - return 0 - end -end - --- Get information on all of the projects in the --- `project_dirs_file` and output it as a list -local get_projects = function() - check_for_project_dirs_file() - local projects = {} - - for line in io.lines(project_dirs_file) do - local title, path = line:match("^(.-)=(.-)$") - local last_accessed = get_last_accessed_time(path) - table.insert(projects, { - title = title, - path = path, - last_accessed = last_accessed - }) - end - - table.sort(projects, function(a,b) - return a.last_accessed > b.last_accessed - end) - - return projects -end - --- The main function. --- This creates a picker with a list of all of the projects, --- and attaches the appropriate mappings for associated --- actions. -local project = function(opts) - opts = opts or {} - - local projects = get_projects() - local new_finder = create_finder(opts, projects) - - pickers.new(opts, { - prompt_title = 'Select a project', - results_title = 'Projects', - finder = new_finder, - sorter = conf.file_sorter(opts), - attach_mappings = function(prompt_bufnr, map) - local refresh_projects = function() - local picker = action_state.get_current_picker(prompt_bufnr) - picker:refresh(create_finder(opts,get_projects()), {reset_prompt=true}) - end - project_actions.add_project:enhance({ post = refresh_projects }) - project_actions.delete_project:enhance({ post = refresh_projects }) - project_actions.rename_project:enhance({ post = refresh_projects }) - - map('n', 'd', project_actions.delete_project) - map('n', 'r', project_actions.rename_project) - map('n', 'c', project_actions.add_project) - map('n', 'f', project_actions.find_project_files) - map('n', 'b', project_actions.browse_project_files) - map('n', 's', project_actions.search_in_project_files) - map('n', 'R', project_actions.recent_project_files) - map('n', 'w', project_actions.change_working_directory) - local on_project_selected = function() - project_actions.find_project_files(prompt_bufnr) - end - actions.select_default:replace(on_project_selected) - return true - end - }):find() -end - -return telescope.register_extension {exports = {project = project}} +return telescope.register_extension{ + setup = main.setup, + exports = { project = main.project } +} diff --git a/lua/telescope/_extensions/project/actions.lua b/lua/telescope/_extensions/project/actions.lua new file mode 100644 index 0000000..c363a53 --- /dev/null +++ b/lua/telescope/_extensions/project/actions.lua @@ -0,0 +1,121 @@ +local builtin = require("telescope.builtin") +local actions = require("telescope.actions") +local transform_mod = require('telescope.actions.mt').transform_mod + +local _git = require("telescope._extensions.project.git") +local _utils = require("telescope._extensions.project.utils") + +local M = {} + +-- Extracts project title from current buffer selection +M.get_selected_title = function(prompt_bufnr) + return actions.get_selected_entry(prompt_bufnr).ordinal +end + +-- Extracts project path from current buffer selection +M.get_selected_path = function(prompt_bufnr) + return actions.get_selected_entry(prompt_bufnr).value +end + +-- Create a new project and add it to the list in the `telescope_projects_file` +M.add_project = function() + local path = _git.try_and_find_git_path() + local projects = _utils.get_project_objects() + local path_not_in_projects = true + + local file = io.open(_utils.telescope_projects_file, "w") + for _, project in pairs(projects) do + if project.path == path then + project.activated = 1 + path_not_in_projects = false + end + _utils.store_project(file, project) + end + + if path_not_in_projects then + local new_project = _utils.get_project_from_path(path) + _utils.store_project(file, new_project) + end + + io.close(file) + print('Project added: ' .. path) +end + +-- Rename the selected project within the `telescope_projects_file`. +M.rename_project = function(prompt_bufnr) + local selected_title = M.get_selected_title(prompt_bufnr) + local new_title = vim.fn.input('Rename ' ..selected_title.. ' to: ', selected_title) + local projects = _utils.get_project_objects() + + local file = io.open(_utils.telescope_projects_file, "w") + for _, project in pairs(projects) do + if project.title == selected_title then + project.title = new_title + end + _utils.store_project(file, project) + end + + io.close(file) +end + +-- Delete (deactivate) the selected project from the `telescope_projects_file` +M.delete_project = function(prompt_bufnr) + local projects = _utils.get_project_objects() + local selected_title = M.get_selected_title(prompt_bufnr) + + local file = io.open(_utils.telescope_projects_file, "w") + for _, project in pairs(projects) do + if project.title == selected_title then + project.activated = 0 + end + _utils.store_project(file, project) + end + + io.close(file) + print('Project deleted: ' .. selected_title) +end + +-- Find files within the selected project using the +-- Telescope builtin `find_files`. +M.find_project_files = function(prompt_bufnr) + local dir = actions.get_selected_entry(prompt_bufnr).value + actions._close(prompt_bufnr, true) + vim.fn.execute("cd " .. dir, "silent") + builtin.find_files({cwd = dir}) +end + +-- Browse through files within the selected project using +-- the Telescope builtin `file_browser`. +M.browse_project_files = function(prompt_bufnr) + local dir = actions.get_selected_entry(prompt_bufnr).value + actions._close(prompt_bufnr, true) + vim.fn.execute("cd " .. dir, "silent") + builtin.file_browser({cwd = dir}) +end + +-- Search within files in the selected project using +-- the Telescope builtin `live_grep`. +M.search_in_project_files = function(prompt_bufnr) + local dir = actions.get_selected_entry(prompt_bufnr).value + actions._close(prompt_bufnr, true) + vim.fn.execute("cd " .. dir, "silent") + builtin.live_grep({cwd = dir}) +end + +-- Search the recently used files within the selected project +-- using the Telescope builtin `oldfiles`. +M.recent_project_files = function(prompt_bufnr) + local dir = actions.get_selected_entry(prompt_bufnr).value + actions._close(prompt_bufnr, true) + vim.fn.execute("cd " .. dir, "silent") + builtin.oldfiles({cwd_only = true}) +end + +-- Change working directory to the selected project and close the picker. +M.change_working_directory = function(prompt_bufnr) + local dir = actions.get_selected_entry(prompt_bufnr).value + actions.close(prompt_bufnr) + vim.fn.execute("cd " .. dir, "silent") +end + +return transform_mod(M) diff --git a/lua/telescope/_extensions/project/finders.lua b/lua/telescope/_extensions/project/finders.lua new file mode 100644 index 0000000..26d17b2 --- /dev/null +++ b/lua/telescope/_extensions/project/finders.lua @@ -0,0 +1,54 @@ +local finders = require("telescope.finders") +local utils = require("telescope.utils") +local entry_display = require("telescope.pickers.entry_display") + +local M = {} + +-- Creates a Telescope `finder` based on the given options +-- and list of projects +M.project_finder = function(opts, projects) + local display_type = opts.display_type + local widths = { + title = 0, + dir = 0, + } + + -- Loop over all of the projects and find the maximum length of + -- each of the keys + for _, project in pairs(projects) do + if display_type == 'full' then + project.display_path = '[' .. project.path .. ']' + else + project.display_path = '' + end + for key, value in pairs(widths) do + widths[key] = math.max(value, utils.strdisplaywidth(project[key] or '')) + end + end + + local displayer = entry_display.create { + separator = " ", + items = { + { width = widths.title }, + { width = widths.dir }, + } + } + local make_display = function(project) + return displayer { + { project.title }, + { project.display_path } + } + end + + return finders.new_table { + results = projects, + entry_maker = function(project) + project.value = project.path + project.ordinal = project.title + project.display = make_display + return project + end, + } +end + +return M diff --git a/lua/telescope/_extensions/project/git.lua b/lua/telescope/_extensions/project/git.lua new file mode 100644 index 0000000..dc00a0b --- /dev/null +++ b/lua/telescope/_extensions/project/git.lua @@ -0,0 +1,60 @@ +local _utils = require("telescope._extensions.project.utils") + +local M = {} + +-- Temporary store for git repo list +M.tmp_path = "/tmp/found_projects.txt" + +-- Find and store git repos if base_dir provided +M.update_git_repos = function(base_dir, max_depth) + M.search_for_git_repos(base_dir, max_depth) + local git_projects = M.parse_git_repo_paths() + M.save_git_repos(git_projects) +end + +-- Recurses directories under base directory to find all git projects +M.search_for_git_repos = function(base_dir, max_depth) + if base_dir then + local max_depth_arg = " -maxdepth " .. max_depth + local find_args = " -type d -name .git -printf '%h\n'" + local shell_cmd = "find " .. base_dir .. max_depth_arg .. find_args + os.execute(shell_cmd .. " > " .. M.tmp_path) + end +end + +-- Reads tmp file, converting paths to projects +M.parse_git_repo_paths = function() + local git_projects = {} + for path in io.lines(M.tmp_path) do + local project = _utils.get_project_from_path(path) + table.insert(git_projects, project) + end + return git_projects +end + +-- Write project to telescope projects file +M.save_git_repos = function(git_projects) + local project_paths = _utils.get_project_paths() + local file = io.open(_utils.telescope_projects_file, "a") + + for _, project in pairs(git_projects) do + local path_exists = _utils.has_value(project_paths, project.path) + if not path_exists then + _utils.store_project(file, project) + end + end +end + +-- Attempt to locate git directory, else return cwd +M.try_and_find_git_path = function() + local git_cmd = "git -C " .. vim.loop.cwd() .. " rev-parse --show-toplevel" + local git_root = vim.fn.systemlist(git_cmd)[1] + local git_root_fatal = _utils.string_starts_with(git_root, 'fatal') + + if not git_root or git_root_fatal then + return vim.loop.cwd() + end + return git_root +end + +return M diff --git a/lua/telescope/_extensions/project/main.lua b/lua/telescope/_extensions/project/main.lua new file mode 100644 index 0000000..43910d1 --- /dev/null +++ b/lua/telescope/_extensions/project/main.lua @@ -0,0 +1,63 @@ +-- telescope modules +local actions = require("telescope.actions") +local action_state = require("telescope.actions.state") +local pickers = require("telescope.pickers") +local conf = require("telescope.config").values + +-- telescope-project modules +local _actions = require("telescope._extensions.project.actions") +local _finders = require("telescope._extensions.project.finders") +local _git = require("telescope._extensions.project.git") +local _utils = require("telescope._extensions.project.utils") + +local M = {} + +-- Variables that setup can change +local base_dir +local max_depth + +-- Allow user to set base_dir in setup +M.setup = function(setup_config) + base_dir = setup_config.base_dir or nil + max_depth = setup_config.max_depth or 3 + _git.update_git_repos(base_dir, max_depth) +end + +-- This creates a picker with a list of all of the projects +M.project = function(opts) + pickers.new(opts or {}, { + prompt_title = 'Select a project', + results_title = 'Projects', + finder = _finders.project_finder(opts, _utils.get_projects()), + sorter = conf.file_sorter(opts), + attach_mappings = function(prompt_bufnr, map) + + local refresh_projects = function() + local picker = action_state.get_current_picker(prompt_bufnr) + local finder = _finders.project_finder(opts, _utils.get_projects()) + picker:refresh(finder, { reset_prompt = true }) + end + + _actions.add_project:enhance({ post = refresh_projects }) + _actions.delete_project:enhance({ post = refresh_projects }) + _actions.rename_project:enhance({ post = refresh_projects }) + + map('n', 'd', _actions.delete_project) + map('n', 'r', _actions.rename_project) + map('n', 'c', _actions.add_project) + map('n', 'f', _actions.find_project_files) + map('n', 'b', _actions.browse_project_files) + map('n', 's', _actions.search_in_project_files) + map('n', 'R', _actions.recent_project_files) + map('n', 'w', _actions.change_working_directory) + + local on_project_selected = function() + _actions.find_project_files(prompt_bufnr) + end + actions.select_default:replace(on_project_selected) + return true + end + }):find() +end + +return M diff --git a/lua/telescope/_extensions/project/utils.lua b/lua/telescope/_extensions/project/utils.lua new file mode 100644 index 0000000..61beb4a --- /dev/null +++ b/lua/telescope/_extensions/project/utils.lua @@ -0,0 +1,103 @@ +local M = {} + +-- The file path to telescope projects +M.telescope_projects_file = vim.fn.stdpath('data') .. '/telescope-projects.txt' + +-- Initialize file if does not exist +M.init_file = function() + local file_path = require'plenary'.path:new(M.telescope_projects_file) + if not file_path:exists() then + file_path:touch() + end +end + +-- Fetches project information to be passed to picker +M.get_projects = function() + local filtered_projects = {} + for _, project in pairs(M.get_project_objects()) do + local is_activated = tonumber(project.activated) == 1 + if is_activated then + table.insert(filtered_projects, project) + end + end + table.sort(filtered_projects, function(a,b) + return a.last_accessed > b.last_accessed + end) + return filtered_projects +end + +-- Get project info for all (de)activated projects +M.get_project_objects = function() + local projects = {} + for line in io.lines(M.telescope_projects_file) do + local project = M.parse_project_line(line) + table.insert(projects, project) + end + return projects +end + +-- Extract paths from all project objects +M.get_project_paths = function() + local paths = {} + for _, project in pairs(M.get_project_objects()) do + table.insert(paths, project.path) + end + return paths +end + +-- Extracts information from telescope projects line +M.parse_project_line = function(line) + local title, path, activated = line:match("^(.-)=(.-)=(.-)$") + if not activated then + title, path = line:match("^(.-)=(.-)$") + activated = 1 + end + return { + title = title, + path = path, + last_accessed = M.get_last_accessed_time(path), + activated = activated + } +end + +-- Parses path into project object (activated by default) +M.get_project_from_path = function(path) + local title = path:match("[^/]+$") + local activated = 1 + local line = title .. "=" .. path .. "=" .. activated + return M.parse_project_line(line) +end + +-- Checks the last time a directory was last accessed +M.get_last_accessed_time = function(path) + local expanded_path = vim.fn.expand(path) + local fs_stat = vim.loop.fs_stat(expanded_path) + return fs_stat and fs_stat.atime.sec or 0 +end + +-- Standardized way of storing project to file +M.store_project = function(file, project) + local line = project.title .. "=" .. project.path .. "=" .. project.activated .. "\n" + file:write(line) +end + +-- Trim whitespace for strings +M.trim = function(s) + return s:match( "^%s*(.-)%s*$" ) +end + +-- Check if value exists in table +M.has_value = function(tbl, val) + for _, value in ipairs(tbl) do + if M.trim(value) == M.trim(val) then + return true + end + end + return false +end + +M.string_starts_with = function(text, start) + return string.sub(text, 1, string.len(start)) == start +end + +return M diff --git a/lua/telescope/_extensions/project_actions.lua b/lua/telescope/_extensions/project_actions.lua deleted file mode 100644 index b35ef32..0000000 --- a/lua/telescope/_extensions/project_actions.lua +++ /dev/null @@ -1,135 +0,0 @@ -local builtin = require("telescope.builtin") -local actions = require("telescope.actions") -local transform_mod = require('telescope.actions.mt').transform_mod - -local project_actions = {} - -local project_dirs_file = vim.fn.stdpath('data') .. '/telescope-projects.txt' - -function string.starts(String,Start) - return string.sub(String,1,string.len(Start))==Start -end - --- Create a new project and add it to the list in the `project_dirs_file` -project_actions.add_project = function(prompt_bufnr) - local git_root = vim.fn.systemlist("git -C " .. vim.loop.cwd() .. " rev-parse --show-toplevel")[ - 1 - ] - - local project_directory = git_root - if not git_root or string.starts(git_root,'fatal') then - project_directory = vim.loop.cwd() - end - - local project_title = project_directory:match("[^/]+$") - local project_to_add = project_title .. "=" .. project_directory .. "\n" - - local file = assert( - io.open(project_dirs_file, "a"), - "No project file exists" - ) - - local project_already_added = false - for line in io.lines(project_dirs_file) do - local project_exists_check = line .. "\n" == project_to_add - if project_exists_check then - project_already_added = true - print('This project already exists.') - return - end - end - - if not project_already_added then - io.output(file) - io.write(project_to_add) - print('project added: ' .. project_title) - end - io.close(file) -end - --- Rename the selected project within the `project_dirs_file`. --- Uses a name provided by the user. -project_actions.rename_project = function(prompt_bufnr) - local oldName = actions.get_selected_entry(prompt_bufnr).ordinal - local newName = vim.fn.input('Rename ' ..oldName.. ' to: ', oldName) - local newLines = "" - for line in io.lines(project_dirs_file) do - local title, path = line:match("^(.-)=(.-)$") - if title ~= oldName then - newLines = newLines .. title .. '=' .. path .. '\n' - else - newLines = newLines .. newName .. '=' .. actions.get_selected_entry(prompt_bufnr).value .. '\n' - end - end - local file = assert( - io.open(project_dirs_file, "w"), - "No project file exists" - ) - file:write(newLines) - file:close() - print('Project renamed: ' .. actions.get_selected_entry(prompt_bufnr).ordinal .. ' -> ' .. newName) -end - --- Delete the selected project from the `project_dirs_file` -project_actions.delete_project = function(prompt_bufnr) - local newLines = "" - for line in io.lines(project_dirs_file) do - local title, path = line:match("^(.-)=(.-)$") - if title ~= actions.get_selected_entry(prompt_bufnr).ordinal then - newLines = newLines .. title .. '=' .. path .. "\n" - end - end - local file = assert( - io.open(project_dirs_file, "w"), - "No project file exists" - ) - file:write(newLines) - file:close() - print('Project deleted: ' .. actions.get_selected_entry(prompt_bufnr).ordinal) -end - --- Find files within the selected project using the --- Telescope builtin `find_files`. -project_actions.find_project_files = function(prompt_bufnr) - local dir = actions.get_selected_entry(prompt_bufnr).value - actions._close(prompt_bufnr, true) - vim.fn.execute("cd " .. dir, "silent") - builtin.find_files({cwd = dir}) -end - --- Browse through files within the selected project using --- the Telescope builtin `file_browser`. -project_actions.browse_project_files = function(prompt_bufnr) - local dir = actions.get_selected_entry(prompt_bufnr).value - actions._close(prompt_bufnr, true) - vim.fn.execute("cd " .. dir, "silent") - builtin.file_browser({cwd = dir}) -end - --- Search within files in the selected project using --- the Telescope builtin `live_grep`. -project_actions.search_in_project_files = function(prompt_bufnr) - local dir = actions.get_selected_entry(prompt_bufnr).value - actions._close(prompt_bufnr, true) - vim.fn.execute("cd " .. dir, "silent") - builtin.live_grep({cwd = dir}) -end - --- Search the recently used files within the selected project --- using the Telescope builtin `oldfiles`. -project_actions.recent_project_files = function(prompt_bufnr) - local dir = actions.get_selected_entry(prompt_bufnr).value - actions._close(prompt_bufnr, true) - vim.fn.execute("cd " .. dir, "silent") - builtin.oldfiles({cwd_only = true}) -end - --- Change working directory to the selected project and close the picker. -project_actions.change_working_directory = function(prompt_bufnr) - local dir = actions.get_selected_entry(prompt_bufnr).value - actions.close(prompt_bufnr) - vim.fn.execute("cd " .. dir, "silent") -end - -project_actions = transform_mod(project_actions); -return project_actions diff --git a/lua/tests/git_spec.lua b/lua/tests/git_spec.lua new file mode 100644 index 0000000..0dee6bb --- /dev/null +++ b/lua/tests/git_spec.lua @@ -0,0 +1,34 @@ +local path = require("plenary.path") + +describe("git", function() + + local git = require("telescope._extensions.project.git") + local path_to_projects = path:new("/tmp/git_spec_projects") + local example_project_path = path:new(path_to_projects.filename .. "/example") + example_project_path:mkdir({ parents = true }) + os.execute("git init --quiet " .. example_project_path.filename) + + it("try and find path", function() + vim.fn.execute("cd " .. example_project_path.filename, "silent") + local git_path = git.try_and_find_git_path() + assert.equal(example_project_path.filename, git_path) + end) + + it("search for repos", function() + git.tmp_path = "/tmp/found_projects_git_spec.txt" + git.search_for_git_repos(path_to_projects.filename, 2) + local git_projects = git.parse_git_repo_paths() + + local found_git_project = false + for _, git_project in pairs(git_projects) do + if git_project.path == example_project_path.filename then + found_git_project = true + end + end + assert.equal(true, found_git_project) + end) + + path:new(git.tmp_path):rm() + path_to_projects:rm({ recursive = true }) + +end) diff --git a/lua/tests/utils_spec.lua b/lua/tests/utils_spec.lua new file mode 100644 index 0000000..1f278ae --- /dev/null +++ b/lua/tests/utils_spec.lua @@ -0,0 +1,66 @@ +local path = require("plenary.path") + +describe("utils", function() + + local utils = require("telescope._extensions.project.utils") + + describe("general", function() + + it("trim whitespace (left and right)", function() + local input_str = " text " + assert.equal("text", utils.trim(input_str)) + end) + + it("string start with 'fatal'", function() + -- expected text when running git.try_and_find_git_path() + local input_str = "fatal: not a git repository (or any parent up to mount point /)" + assert.equal(true, utils.string_starts_with(input_str, "fatal")) + end) + + it("table has a value (whitespace ignored)", function() + local paths = {"/projects/A", "/projects/B"} + assert.equal(true, utils.has_value(paths, "/projects/A")) + assert.equal(true, utils.has_value(paths, "/projects/B ")) + assert.equal(false, utils.has_value(paths, "/projects/C")) + end) + + end) + + describe("project", function() + + it("create, save, and read from file", function() + + -- initialize projects file where projects are stored + local test_projects_file = "/tmp/telescope-projects-test.txt" + utils.telescope_projects_file = test_projects_file + utils.init_file() + local test_projects_path = path:new(test_projects_file) + + -- extract project information from path + local example_project_path = "/projects/my_project" + local project = utils.get_project_from_path(example_project_path) + assert.equal(project.path, example_project_path) + assert.equal(project.title, "my_project") + assert.equal(project.activated, "1") + + -- store project in test file + local file = io.open(test_projects_path.filename, "w") + utils.store_project(file, project) + io.close(file) + + -- check that test project was found + local projects = utils.get_projects() + local found_test_project = false + for _, stored_project in pairs(projects) do + if stored_project.path == example_project_path then + found_test_project = true + end + end + + assert.equal(true, found_test_project) + test_projects_path:rm() + end) + + end) + +end)