Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions lua/competitest/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,8 @@ function M.update_config_table(cfg_tbl, opts)
new_config.run_command[lang].args = cmd.args
end
end
-- Normalize path-related options for global setup configuration
new_config = M.normalize_path_config(new_config)
return new_config
end

Expand All @@ -290,6 +292,43 @@ M.current_setup = nil
---@type table<integer, competitest.Config>
M.buffer_configs = {}

---Normalize path-related configuration options
---Expands ~ to home directory in path configuration options
---@param cfg competitest.Config configuration to normalize
---@return competitest.Config # configuration with normalized paths
function M.normalize_path_config(cfg)
local utils = require("competitest.utils")

-- These options can contain absolute or ~ paths that should be expanded
-- They are NOT relative to current file, so we normalize them during config loading
local absolute_path_options = {
"received_problems_path",
"received_contests_directory",
"received_contests_problems_path",
}

for _, opt in ipairs(absolute_path_options) do
if cfg[opt] and type(cfg[opt]) == "string" then
cfg[opt] = utils.normalize_path(cfg[opt])
end
end

-- Handle template_file which can be false, string, or table
if cfg.template_file then
if type(cfg.template_file) == "string" then
cfg.template_file = utils.normalize_path(cfg.template_file)
elseif type(cfg.template_file) == "table" then
for ext, path in pairs(cfg.template_file) do
if type(path) == "string" then
cfg.template_file[ext] = utils.normalize_path(path)
end
end
end
end

return cfg
end

---Load local configuration for given directory
---@param directory string
---@return competitest.Config? # local configuration, or `nil` when it's absent or incorrect
Expand All @@ -305,6 +344,8 @@ function M.load_local_config(directory)
utils.notify("load_buffer_config: '" .. config_file .. "' doesn't return a table.")
return nil
end
-- Normalize path-related configuration options
local_config = M.normalize_path_config(local_config)
return local_config
end
directory = vim.fn.fnamemodify(directory, ":h")
Expand Down
2 changes: 1 addition & 1 deletion lua/competitest/receive.lua
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,7 @@ function storage_utils.store_received_task_config(filepath, confirm_overwriting,
end

local testcases = require("competitest.testcases")
local tcdir = file_directory .. "/" .. cfg.testcases_directory .. "/"
local tcdir = utils.resolve_config_path(cfg.testcases_directory, file_directory) .. "/"
if cfg.testcases_use_single_file then
local single_file_path = tcdir .. utils.eval_string(filepath, cfg.testcases_single_file_format)
testcases.single_file.write(single_file_path, tctbl)
Expand Down
4 changes: 2 additions & 2 deletions lua/competitest/runner.lua
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,8 @@ function TCRunner:new(bufnr)
bufnr = bufnr,
cc = compile_command,
rc = run_command,
compile_directory = filedir .. buf_cfg.compile_directory .. "/",
running_directory = filedir .. buf_cfg.running_directory .. "/",
compile_directory = utils.resolve_config_path(buf_cfg.compile_directory, filedir) .. "/",
running_directory = utils.resolve_config_path(buf_cfg.running_directory, filedir) .. "/",
tcdata = {},
compile = compile_command ~= nil,
next_tc = 1,
Expand Down
3 changes: 2 additions & 1 deletion lua/competitest/testcases.lua
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,8 @@ end
---@return string # absolute path of testcases directory
local function buf_get_testcases_path(bufnr)
return vim.api.nvim_buf_call(bufnr, function()
return vim.fn.expand("%:p:h") .. "/" .. gbc(bufnr).testcases_directory .. "/"
local base_dir = vim.fn.expand("%:p:h")
return utils.resolve_config_path(gbc(bufnr).testcases_directory, base_dir) .. "/"
end)
end

Expand Down
39 changes: 39 additions & 0 deletions lua/competitest/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,45 @@ function M.buf_eval_string(bufnr, str, tcnum)
return M.eval_string(vim.api.nvim_buf_get_name(bufnr), str)
end

---Expand and normalize a file path
---Expands `~` to home directory and converts to absolute path
---@param path string file path to expand and normalize
---@return string # expanded and normalized absolute path
function M.normalize_path(path)
-- Expand ~ to home directory
local expanded_path = path
if string.sub(path, 1, 1) == "~" then
expanded_path = luv.os_homedir() .. string.sub(path, 2)
end

-- Convert to absolute path
local absolute_path = vim.fn.fnamemodify(expanded_path, ":p")

-- Normalize path (resolve . and .., remove redundant separators)
local normalized = luv.fs_realpath(absolute_path)
return normalized or absolute_path
end

---Resolve a path configuration option that can be relative or absolute
---If path starts with ~ or /, treat it as absolute and expand ~
---Otherwise, return it as-is to be treated as relative to base directory
---@param path string path from configuration
---@param base_dir string base directory for relative paths
---@return string # resolved path
function M.resolve_config_path(path, base_dir)
if not path or path == "" then
return base_dir
end

-- If path starts with ~ or /, it's absolute
if string.sub(path, 1, 1) == "~" or string.sub(path, 1, 1) == "/" then
return M.normalize_path(path)
end

-- Otherwise, it's relative to base_dir
return base_dir .. "/" .. path
end

---Returns `true` if the given file exists, `false` otherwise
---@param filepath string
---@return boolean
Expand Down