cp.nvim/lua/cp/config.lua

228 lines
5.9 KiB
Lua

---@class PlatformLanguageConfig
---@field compile? string[] Compile command template
---@field test string[] Test execution command template
---@field debug? string[] Debug command template
---@field executable? string Executable name
---@field extension? string File extension
---@class PlatformConfig
---@field [string] PlatformLanguageConfig
---@field default_language? string
---@class Hooks
---@field before_run? fun(state: cp.State)
---@field before_debug? fun(state: cp.State)
---@field setup_code? fun(state: cp.State)
---@class RunPanelConfig
---@field ansi boolean Enable ANSI color parsing and highlighting
---@field diff_mode "none"|"vim"|"git" Diff backend to use
---@field max_output_lines number Maximum lines of test output to display
---@class DiffGitConfig
---@field args string[] Git diff arguments
---@class DiffConfig
---@field git DiffGitConfig
---@class cp.Config
---@field platforms table<string, PlatformConfig>
---@field snippets any[]
---@field hooks Hooks
---@field debug boolean
---@field filename? fun(contest: string, contest_id: string, problem_id?: string, config: cp.Config, language?: string): string
---@field run_panel RunPanelConfig
---@field diff DiffConfig
---@field picker string|nil
---@class cp.PartialConfig
---@field platforms? table<string, PlatformConfig>
---@field snippets? any[]
---@field hooks? Hooks
---@field debug? boolean
---@field filename? fun(contest: string, contest_id: string, problem_id?: string, config: cp.Config, language?: string): string
---@field run_panel? RunPanelConfig
---@field diff? DiffConfig
---@field picker? string|nil
local M = {}
local constants = require('cp.constants')
local utils = require('cp.utils')
local default_contest_config = {
cpp = {
compile = { 'g++', '-std=c++17', '{source}', '-o', '{binary}' },
debug = { 'g++', '-std=c++17', '-fsanitize=address,undefined', '{source}', '-o', '{binary}' },
test = { '{binary}' },
},
python = {
test = { '{source}' },
debug = { '{source}' },
executable = 'python',
},
default_language = 'cpp',
}
---@type cp.Config
M.defaults = {
platforms = {
codeforces = default_contest_config,
atcoder = default_contest_config,
cses = default_contest_config,
},
snippets = {},
hooks = {
before_run = nil,
before_debug = nil,
setup_code = nil,
},
debug = false,
scrapers = constants.PLATFORMS,
filename = nil,
run_panel = {
ansi = true,
diff_mode = 'none',
max_output_lines = 50,
},
diff = {
git = {
args = { 'diff', '--no-index', '--word-diff=plain', '--word-diff-regex=.', '--no-prefix' },
},
},
picker = nil,
}
---@param user_config cp.PartialConfig|nil
---@return cp.Config
function M.setup(user_config)
vim.validate({
user_config = { user_config, { 'table', 'nil' }, true },
})
if user_config then
vim.validate({
platforms = { user_config.platforms, { 'table', 'nil' }, true },
snippets = { user_config.snippets, { 'table', 'nil' }, true },
hooks = { user_config.hooks, { 'table', 'nil' }, true },
debug = { user_config.debug, { 'boolean', 'nil' }, true },
filename = { user_config.filename, { 'function', 'nil' }, true },
run_panel = { user_config.run_panel, { 'table', 'nil' }, true },
diff = { user_config.diff, { 'table', 'nil' }, true },
picker = { user_config.picker, { 'string', 'nil' }, true },
})
if user_config.platforms then
for platform_name, platform_config in pairs(user_config.platforms) do
vim.validate({
[platform_name] = {
platform_config,
function(config)
if type(config) ~= 'table' then
return false
end
return true
end,
'contest configuration',
},
})
end
end
if user_config.picker then
if not vim.tbl_contains({ 'telescope', 'fzf-lua' }, user_config.picker) then
error(("Invalid picker '%s'. Must be 'telescope' or 'fzf-lua'"):format(user_config.picker))
end
end
end
local config = vim.tbl_deep_extend('force', M.defaults, user_config or {})
vim.validate({
before_run = {
config.hooks.before_run,
{ 'function', 'nil' },
true,
},
before_debug = {
config.hooks.before_debug,
{ 'function', 'nil' },
true,
},
setup_code = {
config.hooks.setup_code,
{ 'function', 'nil' },
true,
},
})
vim.validate({
ansi = {
config.run_panel.ansi,
'boolean',
'ansi color parsing must be enabled xor disabled',
},
diff_mode = {
config.run_panel.diff_mode,
function(value)
return vim.tbl_contains({ 'none', 'vim', 'git' }, value)
end,
"diff_mode must be 'none', 'vim', or 'git'",
},
max_output_lines = {
config.run_panel.max_output_lines,
function(value)
return type(value) == 'number' and value > 0 and value == math.floor(value)
end,
'max_output_lines must be a positive integer',
},
})
vim.validate({
git = { config.diff.git, { 'table', 'nil' }, true },
})
for _, platform_config in pairs(config.platforms) do
if not platform_config.default_language then
-- arbitrarily choose a language
platform_config.default_language = vim.tbl_keys(platform_config)[1]
end
end
local ok, err = utils.check_required_runtime()
if not ok then
error('[cp.nvim] ' .. err)
end
return config
end
---@param contest_id string
---@param problem_id? string
---@return string
local function default_filename(contest_id, problem_id)
if problem_id then
return (contest_id .. problem_id):lower()
else
return contest_id:lower()
end
end
M.default_filename = default_filename
local current_config = nil
--- Set the config
---@return nil
function M.set_current_config(config)
current_config = config
end
--- Get the config
---@return cp.Config
function M.get_config()
return current_config or M.defaults
end
return M