Feat/Add Project Scanner (#34)

* feat: support base_dir option for searching for git repos

* style: fix indentation

* fix: only close file if exists

* fix: explicitly set to false if base_dir not set

* refactor: always initialize project file

* refactor: separate project extraction and project writing

* fix: iterate with pairs and no arg for io.lines

* refactor: restructure, test, and lint

* refactor: use standard telescope file structure

* refactor: utilize utility functions for actions module

* refactor: support max_depth arg for recursive searching

* chore: add Makefile for linting and testing

* test: add utils general tests

* refactor: do not need file_exists function

* test: add tests for creating, writing and reading projects

* chore: move location of tests

* test: add tests for git project scanning

* test: do not need minimal_init.vim now

* test: clean up tests

* refactor: update git projects on setup
This commit is contained in:
Logan Connolly
2021-06-14 20:31:55 +02:00
committed by GitHub
parent 3ba1950531
commit 4038a73667
12 changed files with 574 additions and 308 deletions

7
.luacheckrc Normal file
View File

@@ -0,0 +1,7 @@
-- Rerun tests only if their modification time changed.
cache = true
-- Global objects defined by the C code
read_globals = {
"vim",
}

7
Makefile Normal file
View File

@@ -0,0 +1,7 @@
.PHONY: lint tests
lint:
luacheck ./lua/telescope
tests:
nvim --headless -c "PlenaryBustedDirectory lua/tests/"

View File

@@ -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 \<CR\>) |
\* *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',
'<C-p>',
":lua require'telescope'.extensions.project.project{}<CR>",
{noremap = true, silent = true}
'n',
'<C-p>',
":lua require'telescope'.extensions.project.project{}<CR>",
{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 \<CR\>)
## 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:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

34
lua/tests/git_spec.lua Normal file
View File

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

66
lua/tests/utils_spec.lua Normal file
View File

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