Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
jtzero committed Jun 10, 2024
0 parents commit a9c3bb6
Show file tree
Hide file tree
Showing 13 changed files with 384 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
24 changes: 24 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,24 @@
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"
}
build = {
type = "builtin",
modules = {
["go-to-test-file"] = "lua/go-to-test-file.lua",
["go-to-test-file.dir"] = "lua/go-to-test-file/dir.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"
}
}
243 changes: 243 additions & 0 deletions lua/go_to_test_file.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
local str = require('go_to_test_file.str')
local matrix = require('go_to_test_file.matrix')
local dir = require('go_to_test_file.dir')

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


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')
vim.api.nvim_set_var('test_folder_prefixes', '^(test/|tests/|spec/)(unit/|integration/)?')
vim.api.nvim_set_var('deep_test_folder_prefixes', '(test|tests|spec)(/unit|/integration)?$')
-- TODO add second level
vim.api.nvim_set_var('lua_test_folder_prefixes', '^test/|^tests/|^spec/')

local 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

GoToTestFile.FindProjectTestDirectory = function(git_root, source_file_folder_abs_path)
local test_dirs = vim.fn.system("cd " .. git_root .. " > /dev/null && fd --type d --hidden --exclude .git | rg '" .. vim.g.deep_test_folder_prefixes .. "'")
if test_dirs == '' then
return ''
end
local git_root_rel_source_file_folder_path = dir.difference_between_ancestor_folder_and_sub_folder(git_root, source_file_folder_abs_path)
local hops = {}
for _k,v in pairs(str.split(test_dirs, '\n')) do
local path = vim.fn.trim(vim.fn.system('cd ' .. git_root .. ' > /dev/null && realpath --relative-to="' .. git_root_rel_source_file_folder_path .. '" "' .. v .. '"'))
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 = dir.difference_between_ancestor_folder_and_sub_folder(git_root, source_file_folder_abs_path)
local full_path = vim.fn.trim(vim.fn.system('realpath ' .. git_root .. '/' .. rel_path_to_pwd_from_git_root .. '/' .. path_with_smallest_hops))
return full_path
end

GoToTestFile.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

GoToTestFile.DomainlessSourceFolderGoToTestFileatch = function(prefixes, source_folder)
local identifiers = str.split(prefixes, '|')
return 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.GitRootOfFile = function(file_abs_path)
local file_folder_abs_path = vim.fn.fnamemodify(file_abs_path, ":h")
return vim.fn.trim(vim.fn.system('cd ' .. file_folder_abs_path .. ' > /dev/null && git rev-parse --show-toplevel'))
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 = GoToTestFile.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=dir.difference_between_ancestor_folder_and_sub_folder(project_abs_path_root, source_file_folder_abs_path)
local project_source_folder = GoToTestFile.ProjectSourceFolder(project_abs_path_root, source_file_folder_path_rel_to_project_root)
local domainless_source_folder_match = GoToTestFile.DomainlessSourceFolderGoToTestFileatch(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 command = "cd " .. test_folder_abs_path .. " > /dev/null && rg --files | fzf --filter '" .. prefixless_source_file_path .. "' | head -1"
local file_relative_to_root = vim.fn.trim(vim.fn.system(command))
if file_relative_to_root == '' then
return test_folder_abs_path
else
return test_folder_abs_path .. '/' .. file_relative_to_root
end
end

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

GoToTestFile.RemoveTestFileNameIdentifiers = function(test_filename_rel_path_from_project)
local identifiers = str.split(vim.g.lua_test_file_identifiers, '|')
local with_period = match_one(test_filename_rel_path_from_project, identifiers, '%.', '')
local with_underscore = 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 = 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 = GoToTestFile.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=dir.difference_between_ancestor_folder_and_sub_folder(project_abs_path_root, test_file_folder_abs_path)

local path_without_prefixes = GoToTestFile.RemoveTestFolderPrefixes(vim.g.lua_test_folder_prefixes, test_file_folder_path_rel_to_project_root)
local current_filename_no_ext = vim.fn.fnamemodify(test_file_with_abs_path, ':t:r')
local test_filepath_without_test_identifiers = GoToTestFile.RemoveTestFileNameIdentifiers(path_without_prefixes .. '/' .. current_filename_no_ext)

-- 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"
return vim.fn.trim(vim.fn.system(command))
end
vim.cmd('command! FindSrcFolderCodeFile :lua print(GoToTestFile.FindSrcFolderCodeFile(GoToTestFile.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 list = str.split(vim.g.lua_test_file_identifiers, '|')
local matched = match_one(test_filename, list, '%.', '%.')
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
GoToTestFile.HasPeerSourceCodeFile = function(file_with_path)
local current_file_name = vim.fn.fnamemodify(file_with_path, ':t')
local output = vim.fn.trim(vim.fn.system('printf "' .. current_file_name .. '" | rg "\\.' .. vim.g.test_file_identifiers .. '\\."'))
return output ~= ''
end

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

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

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

GoToTestFile.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 = GoToTestFile.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

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

GoToTestFile.FindTestOrSrcCodeFile = function(git_root, current_file_abs_path)
if GoToTestFile.HasPeerSourceCodeFile(current_file_abs_path) then
return GoToTestFile.FindPeerSourceCodeFile(current_file_abs_path)
elseif GoToTestFile.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.GitRootOfFile(vim.fn.expand("%:p")), vim.fn.expand("%:p")))')

GoToTestFile.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

vim.cmd('command! RepoTestFolder :lua print(GoToTestFile.RepoTestFolder())')

-- 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.GitRootOfFile(current_file_abs_path)
local path = GoToTestFile.FindTestOrSrcCodeFile(git_root, current_file_abs_path)
if path == './' then
local test_path = GoToTestFile.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
25 changes: 25 additions & 0 deletions lua/go_to_test_file/dir.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
-- 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,
-- 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)
return vim.fn.trim(vim.fn.system('realpath --relative-to="' .. ancestor .. '" "' .. sub_folder .. '"'))
end
}
18 changes: 18 additions & 0 deletions lua/go_to_test_file/matrix.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
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
return idx
else
if new_min < min then
min = new_min
idx = i
end
end
end
return idx
end
}
1 change: 1 addition & 0 deletions lua/go_to_test_file/project.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
return {}
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
}
18 changes: 18 additions & 0 deletions lua/go_to_test_file/system.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
local windows_name = 'WIN'
local linux_name = 'NIX'
local function is_win()
return package.config:sub(1, 1) == '\\'
end

return {
windows_name = windows_name,
linux_name = linux_name,
is_win = is_win,
name = function()
if is_win() then
return windows_name
else
return linux_name
end
end
}
23 changes: 23 additions & 0 deletions spec/go_to_test_file/dir_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
local assert = require('luassert')


local dir = require('go_to_test_file.dir')
local system = require('go_to_test_file.system')

describe('dir', function()
describe('script_path', function()
it('will print the relative path of the current script directory', function()
local actual = dir.script_path(system.name)
local ps = dir.path_separator(system.name())
local expected = vim.fn.getcwd() .. ps .. 'spec/go_to_test_file/'
assert.are.equal(expected, actual)
end)
end)
describe('difference_between_ancestor_folder_and_sub_folder', function()
it('will print the relative path from subfolder to ancestor', function()
local ps = dir.path_separator(system.name())
local this_path = vim.fn.getcwd() .. ps .. 'spec/go_to_test_file'
assert.are.equal(dir.difference_between_ancestor_folder_and_sub_folder(this_path, 'tst'), '../../tst')
end)
end)
end)
10 changes: 10 additions & 0 deletions spec/go_to_test_file/matrix_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
local matrix = require('go_to_test_file.matrix')

describe('table', function()
describe('row_with_smallest_first_item', function()
it('finds the index of the row with the smallest first item', function()
local test_table = { {9, '/lib'}, {1, '/etc'}, {2, '/root'} }
assert.are.equal(2, matrix.row_with_smallest_first_item(test_table))
end)
end)
end)
8 changes: 8 additions & 0 deletions spec/go_to_test_file/str_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
local str = require('go_to_test_file.str')

describe('str', function()
it('splits a string', function()

assert(str.split("asdf,zxcv", ','))
end)
end)
Empty file added spec/go_to_test_file/tst/.keep
Empty file.

0 comments on commit a9c3bb6

Please sign in to comment.