Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
jtzero committed Jun 21, 2024
0 parents commit dbdc557
Show file tree
Hide file tree
Showing 23 changed files with 605 additions and 0 deletions.
1 change: 1 addition & 0 deletions .envrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export PATH="${PWD}/lua_modules/bin:${PATH}"
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/luarocks
/lua_modules
/.luarocks
25 changes: 25 additions & 0 deletions go-to-test-file.nvim-test.init-1.rockspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package = "go-to-test-file.nvim"
version = "test.init-1"
source = {
url = "git+ssh://git@github.com/jtzero/go-to-test-file.nvim.git"
}
description = {
homepage = "*** please enter a project homepage ***",
license = "*** please specify a license ***"
}
dependencies = {
"lua >= 5.1, < 5.2",
"vusted >= 2.3.4-1, < 3",
"plenary.nvim >= scm-1"
}
build = {
type = "builtin",
modules = {
["go-to-test-file"] = "lua/go-to-test-file.lua",
["go-to-test-file.path"] = "lua/go-to-test-file/path.lua",
["go-to-test-file.project"] = "lua/go-to-test-file/project.lua",
["go-to-test-file.str"] = "lua/go-to-test-file/str.lua",
["go-to-test-file.system"] = "lua/go-to-test-file/system.lua",
["go-to-test-file.matrix"] = "lua/go-to-test-file/matrix.lua"
}
}
194 changes: 194 additions & 0 deletions lua/go_to_test_file.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
local str = require('go_to_test_file.str')
local path = require('go_to_test_file.path')
local project = require('go_to_test_file.project')
local git = require('go_to_test_file.git')
local list = require('go_to_test_file.list')
local peer = require('go_to_test_file.peer')
local system = require('go_to_test_file.system')

GoToTestFile = {
git = git
}
-- requirements fzf, rg, realpath, git, fd
-- TODO remove fzf


vim.api.nvim_set_var('src_chomp_prefixes', '^src/|lib/|app/|lua/')
vim.api.nvim_set_var('lua_src_chomp_prefixes', '^src/|^lib/|^app/|^lua/')
vim.api.nvim_set_var('test_file_identifiers', '(test|tests|spec)')
vim.api.nvim_set_var('lua_test_file_identifiers', 'test|tests|spec')
-- fd added end slashes to dirs after 8.4.0
vim.api.nvim_set_var('test_folder_prefixes', '^(test/|tests/|spec/)(unit/|integration/)?')
-- TODO add second level
vim.api.nvim_set_var('lua_test_folder_prefixes', '^test/|^tests/|^spec/')

GoToTestFile.DomainlessSourceFolderMatch = function(prefixes, source_folder)
local identifiers = str.split(prefixes, '|')
return list.match_one(source_folder .. "/", identifiers, '', '')
end

GoToTestFile.RemoveProjectDomainlessPrefix = function(domainless_source_folder_match, git_repo_pwd)
return string.gsub(git_repo_pwd, domainless_source_folder_match, '')
end

GoToTestFile.FindTestFolderTestFile = function(git_root, source_file_with_abs_path)
local source_file_folder_abs_path = vim.fn.fnamemodify(source_file_with_abs_path, ":h")
local test_folder_abs_path = project.FindProjectTestDirectory(git_root, source_file_folder_abs_path)
local project_abs_path_root = vim.fn.fnamemodify(test_folder_abs_path, ':h')
local source_file_folder_path_rel_to_project_root=path.difference_between_ancestor_folder_and_sub_folder(project_abs_path_root, source_file_folder_abs_path)
local project_source_folder = project.ProjectSourceFolder(project_abs_path_root, source_file_folder_path_rel_to_project_root)
local domainless_source_folder_match = GoToTestFile.DomainlessSourceFolderMatch(vim.g.lua_src_chomp_prefixes, project_source_folder)

local current_filename_no_ext = vim.fn.fnamemodify(source_file_with_abs_path, ':t:r')
local filepath_relative_to_project_root = source_file_folder_path_rel_to_project_root .. '/' .. current_filename_no_ext

local prefixless_source_file_path = ''
if domainless_source_folder_match ~= '' then
prefixless_source_file_path = GoToTestFile.RemoveProjectDomainlessPrefix(project_source_folder .. '/', filepath_relative_to_project_root)
else
prefixless_source_file_path = filepath_relative_to_project_root
end

local cmd = ''
if string.find(prefixless_source_file_path, path.path_separator(system.name)) then
cmd = "cd " .. test_folder_abs_path .. " > /dev/null && fd -t f -p '" .. prefixless_source_file_path .. "' | head -n 1 "
else
cmd = "cd " .. test_folder_abs_path .. " > /dev/null && fd -t f '" .. prefixless_source_file_path .. "' | head -n 1 "
end
local file_relative_to_root = vim.fn.trim(vim.fn.system(cmd)):gsub("^./", "")
if file_relative_to_root == '' then
return test_folder_abs_path
else
return test_folder_abs_path .. '/' .. file_relative_to_root
end
end

GoToTestFile.RemoveTestFileNameIdentifiers = function(test_filename_rel_path_from_project)
local identifiers = str.split(vim.g.lua_test_file_identifiers, '|')
local with_period = list.match_one(test_filename_rel_path_from_project, identifiers, '%.', '')
local with_underscore = list.match_one(test_filename_rel_path_from_project, identifiers, '_', '')
local path_without_infixes = string.gsub(test_filename_rel_path_from_project, with_period, '')
local path_without_infixes = string.gsub(test_filename_rel_path_from_project, with_underscore, '')

return path_without_infixes
end

GoToTestFile.RemoveTestFolderPrefixes = function(test_folder_prefixes, test_file_folder_path_rel_from_project_root)
local identifiers = str.split(test_folder_prefixes, '|')
local prefix_to_chomp = list.match_one(test_file_folder_path_rel_from_project_root, identifiers, '', '')
local path_without_prefixes = string.gsub(test_file_folder_path_rel_from_project_root, prefix_to_chomp, '')
return path_without_prefixes
end

GoToTestFile.FindSrcFolderCodeFile = function(git_root, test_file_with_abs_path)
local test_file_folder_abs_path = vim.fn.fnamemodify(test_file_with_abs_path, ":h")
local test_file_name = vim.fn.fnamemodify(test_file_with_abs_path, ":t")
local test_folder_abs_path = project.FindProjectTestDirectory(git_root, test_file_folder_abs_path)
local project_abs_path_root = vim.fn.fnamemodify(test_folder_abs_path, ':h')

local test_file_folder_path_rel_to_project_root=path.difference_between_ancestor_folder_and_sub_folder(project_abs_path_root, test_file_folder_abs_path)

local sep = path.path_separator(system.name)
local path_without_prefixes = ""
if test_file_folder_path_rel_to_project_root:sub(-#sep) == sep then
path_without_prefixes = GoToTestFile.RemoveTestFolderPrefixes(vim.g.lua_test_folder_prefixes, test_file_folder_path_rel_to_project_root)
else
path_without_prefixes = GoToTestFile.RemoveTestFolderPrefixes(vim.g.lua_test_folder_prefixes, test_file_folder_path_rel_to_project_root .. sep)
end
local current_filename_no_ext = vim.fn.fnamemodify(test_file_with_abs_path, ':t:r')
local test_filepath_without_test_identifiers
if path_without_prefixes == '' then
test_filepath_without_test_identifiers = GoToTestFile.RemoveTestFileNameIdentifiers(current_filename_no_ext)
else
test_filepath_without_test_identifiers = GoToTestFile.RemoveTestFileNameIdentifiers(path_without_prefixes .. '/' .. current_filename_no_ext)
end
-- TODO minimize pathing to make this faster and more precise
local command = "cd " .. project_abs_path_root .. " > /dev/null && rg --files | rg -v '^" .. test_file_name .. "$' | fzf --filter '" .. test_filepath_without_test_identifiers .. "' | head -1"
local relative_path = vim.fn.trim(vim.fn.system(command))
return path.join(sep, project_abs_path_root, relative_path)
end
vim.cmd('command! FindSrcFolderCodeFile :lua print(GoToTestFile.FindSrcFolderCodeFile(GoToTestFile.git.GitRootOfFile(vim.fn.expand("%:p")), vim.fn.expand("%:p")))')

-- TODO cd with abs path
GoToTestFile.FindPeerSourceCodeFile = function(file_with_path)
local test_filename = vim.fn.fnamemodify(file_with_path, ':t')
local patterns = str.split(vim.g.lua_test_file_identifiers, '|')
local matched = list.match_one(test_filename, patterns, '%.', '%.')
local expected_source_code_filename = string.gsub(test_filename, matched, '.')
local path = vim.fn.fnamemodify(file_with_path, ':.:h')
local command = "cd " .. path .. " > /dev/null && rg --files | rg -v '^" .. test_filename .. "$' | fzf --filter '" .. expected_source_code_filename .. "' | head -1"
local output = vim.fn.trim(vim.fn.system(command))
return path .. '/' .. output
end

vim.cmd('command! FindPeerSourceCodeFile :lua GoToTestFile.FindPeerSourceCodeFile(vim.fn.expand("%:p"))')

-- TODO cd with abs path
-- TODO filter current_file_name with path
GoToTestFile.FindPeerTestCodeFile = function(file_with_path)
local current_file_name_no_ext = vim.fn.fnamemodify(file_with_path, ':t:r')
local current_file_name = vim.fn.fnamemodify(file_with_path, ':t')
local path = vim.fn.fnamemodify(file_with_path, ':p:h')
local cmd = "cd " .. path .. " > /dev/null && fd -t f '" .. current_file_name_no_ext ..
"' | rg -v '" .. current_file_name .. "' | rg '" .. vim.g.test_file_identifiers .. "' | head -n 1 "
local output = vim.fn.trim(vim.fn.system(cmd))
if output == '' then
return ''
else
return path .. '/' .. output
end
end

vim.cmd('command! FindPeerTestCodeFile :lua print(GoToTestFile.FindPeerTestCodeFile(vim.fn.expand("%:p")))')

GoToTestFile.FindTestOrSrcCodeFile = function(git_root, current_file_abs_path)
if peer.HasPeerSourceCodeFile(current_file_abs_path) then
return GoToTestFile.FindPeerSourceCodeFile(current_file_abs_path)
elseif project.IsInATestFolder(git_root, current_file_abs_path) then
return GoToTestFile.FindSrcFolderCodeFile(git_root, current_file_abs_path)
else
local peer_test_code_file = GoToTestFile.FindPeerTestCodeFile(current_file_abs_path)
if peer_test_code_file ~= '' then
return peer_test_code_file
else
return GoToTestFile.FindTestFolderTestFile(git_root, current_file_abs_path)
end
end
end

vim.cmd('command! FindTestOrSrcCodeFile :lua print(GoToTestFile.FindTestOrSrcCodeFile(GoToTestFile.git.GitRootOfFile(vim.fn.expand("%:p")), vim.fn.expand("%:p")))')

-- rename to last resort, the above should provide some sane locations even if it cannot find the file
GoToTestFile.FindTestOrSrcCodeFileFolderOnFailure = function(current_file_abs_path)
local git_root = GoToTestFile.git.GitRootOfFile(current_file_abs_path)
local path = GoToTestFile.FindTestOrSrcCodeFile(git_root, current_file_abs_path)
if path == './' then
local test_path = project.RepoTestFolder()
if test_path ~= '' then
return test_path
else
return git_root
end
else
return path
end
end

GoToTestFile.setup = function(opts)
opts = opts or {}

--vim.notify_once("gruvbox.nvim: you must use neovim 0.8 or higher")

vim.api.nvim_create_user_command("FindTestOrSrcCodeFileFolderOnFailure",
function()
local path = GoToTestFile.FindTestOrSrcCodeFileFolderOnFailure(vim.fn.expand("%:p"))
print(path)
vim.cmd('e ' .. path)
end,
{}
)
--vim.cmd('command! FindTestOrSrcCodeFileFolderOnFailure :exec "e " . :lua FindTestOrSrcCodeFileFolderOnFailure(expand("%:p"))')
vim.keymap.set('n', '<M-T>', '<cmd>FindTestOrSrcCodeFileFolderOnFailure<CR>', {desc = 'Opens a corresponding test file or src file if not found opens the test folder', noremap = true})
end

return GoToTestFile
7 changes: 7 additions & 0 deletions lua/go_to_test_file/git.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
return {
GitRootOfFile = function(file_abs_path)
local file_folder_abs_path = vim.fn.fnamemodify(file_abs_path, ":h")
local cmd = 'cd ' .. file_folder_abs_path .. ' > /dev/null && git rev-parse --show-toplevel'
return vim.fn.trim(vim.fn.system(cmd))
end
}
18 changes: 18 additions & 0 deletions lua/go_to_test_file/list.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@

return {
match_one = function(test_str, list, prefix, suffix)
local exhausted = false
local i = 1
local found = ''
while (not found) or (not exhausted) do
if string.match(test_str, prefix .. list[i] .. suffix) then
found = prefix .. list[i] .. suffix
end
i = i + 1
if #list < i then
exhausted = true
end
end
return found or ''
end
}
19 changes: 19 additions & 0 deletions lua/go_to_test_file/matrix.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
return {
row_with_smallest_first_item = function(table_to_search)
local min = math.huge
local idx = -1
for i = 1, #table_to_search do
local new_min = min < table_to_search[i][1] and min or table_to_search[i][1]
if new_min == 0 then
idx = 1
return idx
else
if new_min < min then
min = new_min
idx = i
end
end
end
return idx
end
}
33 changes: 33 additions & 0 deletions lua/go_to_test_file/path.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
-- replace with plenary
local system = require('go_to_test_file.system')

local function path_separator(system_name)
if system_name == system.windows_name then
return '\\'
end
return '/'
end

local function script_path(system_name)
local ps = path_separator(system_name)
local str = vim.fn.getcwd() .. ps .. debug.getinfo(2, 'S').source:sub(2)
str = str:gsub('/', ps) -- ??
return str:match('(.*' .. ps .. ')')
end

return {
script_path = script_path,
path_separator = path_separator,
join = function(sep, a, ...)
local memo = a
for _key,value in ipairs({...}) do
memo = memo .. sep .. value
end
return memo
end,
-- ancestor has to be relative to PWD or you have to pass in abs
difference_between_ancestor_folder_and_sub_folder = function(ancestor, sub_folder)
local cmd = 'realpath --relative-to="' .. ancestor .. '" "' .. sub_folder .. '"'
return vim.fn.trim(vim.fn.system(cmd))
end
}
14 changes: 14 additions & 0 deletions lua/go_to_test_file/peer.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
local peer = {}

vim.api.nvim_set_var('test_file_identifiers', '(test|tests|spec)')

-- TODO cd with abs path
-- TODO should_have_peer_source_code_file_from_name
peer.HasPeerSourceCodeFile = function(file_with_path)
local current_file_name = vim.fn.fnamemodify(file_with_path, ':t')
local cmd = 'printf "' .. current_file_name .. '" | rg "\\.' .. vim.g.test_file_identifiers .. '\\."'
local output = vim.fn.trim(vim.fn.system(cmd))
return output ~= ''
end

return peer
62 changes: 62 additions & 0 deletions lua/go_to_test_file/project.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
vim.api.nvim_set_var('deep_test_folder_prefixes', '(test|tests|spec)(/unit|/integration)?$')

local str = require('go_to_test_file.str')
local path = require('go_to_test_file.path')
local matrix = require('go_to_test_file.matrix')
local project = {}

project.RepoTestFolder = function()
local folder_name = vim.fn.trim(vim.fn.system('cd "$(git rev-parse --show-toplevel)" > /dev/null && find . -type d -print -maxdepth 1 | cut -d/ -f2- | rg "^' .. vim.g.test_file_identifiers .. '$"'))
return vim.fn.trim(vim.fn.system('git rev-parse --show-toplevel')) .. '/' .. folder_name
end

project.ProjectSourceFolder = function(project_abs_path_root, source_file_folder_path_rel_to_project_root)
local project_source_folder = vim.fn.trim(vim.fn.system('realpath --relative-to="' .. project_abs_path_root .. '" "' .. source_file_folder_path_rel_to_project_root .. '"'))
return str.split(project_source_folder, '/')[1]
end

project.IsInATestFolder = function(git_root, current_file_with_abs_path)
local current_file_folder_abs_path = vim.fn.fnamemodify(current_file_with_abs_path, ":h")
local test_folder_abs_path = project.FindProjectTestDirectory(git_root, current_file_folder_abs_path)
if test_folder_abs_path == '' then
return false
else
-- TODO this is a little niave
return string.find(current_file_folder_abs_path, test_folder_abs_path, 1, true) ~= nil
end
end

project.FindProjectTestDirectory = function(git_root, source_file_folder_abs_path)
-- fd added end slashes to paths after 8.4.0
local test_paths = vim.fn.system("cd " .. git_root .. " > /dev/null && fd --type d --hidden --exclude .git | sed 's/\\/$//' | rg '" .. vim.g.deep_test_folder_prefixes .. "'")
if test_paths == '' then
return ''
end
local git_root_rel_source_file_folder_path = path.difference_between_ancestor_folder_and_sub_folder(git_root, source_file_folder_abs_path)
local hops = {}
for _k,v in pairs(str.split(test_paths, '\n')) do
local cmd = 'cd ' .. git_root .. ' > /dev/null && realpath --relative-to="' .. git_root_rel_source_file_folder_path .. '" "' .. v .. '"'
local path = vim.fn.trim(vim.fn.system(cmd))
if path == '.' then
path = git_root_rel_source_file_folder_path
end
local _, count = string.gsub(path, '/', {})
table.insert(hops, {count, path})
end
local idx = matrix.row_with_smallest_first_item(hops)
local path_with_smallest_hops = hops[idx][2]
local rel_path_to_pwd_from_git_root = path.difference_between_ancestor_folder_and_sub_folder(git_root, source_file_folder_abs_path)
local cmd = ''
if rel_path_to_pwd_from_git_root == path_with_smallest_hops then
cmd = 'realpath ' .. git_root .. '/' .. path_with_smallest_hops
else
cmd = 'realpath ' .. git_root .. '/' .. rel_path_to_pwd_from_git_root .. '/' .. path_with_smallest_hops
end
local response = vim.fn.system(cmd)
if vim.v.shell_error ~= 0 then
error(response)
end
return vim.trim(response)
end

return project
10 changes: 10 additions & 0 deletions lua/go_to_test_file/str.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
return {
split = function(str, sep)
local result = {}
local regex = ("([^%s]+)"):format(sep)
for each in str:gmatch(regex) do
table.insert(result, each)
end
return result
end
}
Loading

0 comments on commit dbdc557

Please sign in to comment.