feat(doc): update for new config
This commit is contained in:
parent
2c0e808c8c
commit
a76d228e3f
5 changed files with 369 additions and 297 deletions
|
|
@ -222,8 +222,7 @@ end
|
|||
---@param platform string
|
||||
---@param contest_id string
|
||||
---@param problem_id? string
|
||||
---@param language? string
|
||||
function M.set_file_state(file_path, platform, contest_id, problem_id, language)
|
||||
function M.set_file_state(file_path, platform, contest_id, problem_id)
|
||||
if not cache_data.file_states then
|
||||
cache_data.file_states = {}
|
||||
end
|
||||
|
|
@ -232,7 +231,6 @@ function M.set_file_state(file_path, platform, contest_id, problem_id, language)
|
|||
platform = platform,
|
||||
contest_id = contest_id,
|
||||
problem_id = problem_id,
|
||||
language = language,
|
||||
}
|
||||
|
||||
M.save()
|
||||
|
|
|
|||
|
|
@ -1,201 +1,279 @@
|
|||
---@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
|
||||
-- lua/cp/config.lua
|
||||
---@class CpLangCommands
|
||||
---@field build? string[]
|
||||
---@field run? string[]
|
||||
---@field debug? string[]
|
||||
|
||||
---@class PlatformConfig
|
||||
---@field [string] PlatformLanguageConfig
|
||||
---@field default_language? string
|
||||
---@class CpLanguage
|
||||
---@field extension string
|
||||
---@field commands CpLangCommands
|
||||
|
||||
---@class CpPlatformOverrides
|
||||
---@field extension? string
|
||||
---@field commands? CpLangCommands
|
||||
|
||||
---@class CpPlatform
|
||||
---@field enabled_languages string[]
|
||||
---@field default_language string
|
||||
---@field overrides? table<string, CpPlatformOverrides>
|
||||
|
||||
---@class RunPanelConfig
|
||||
---@field ansi boolean
|
||||
---@field diff_mode "none"|"vim"|"git"
|
||||
---@field max_output_lines integer
|
||||
|
||||
---@class DiffGitConfig
|
||||
---@field args string[]
|
||||
|
||||
---@class DiffConfig
|
||||
---@field git DiffGitConfig
|
||||
|
||||
---@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
|
||||
---@class CpUI
|
||||
---@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
|
||||
---@class cp.Config
|
||||
---@field languages table<string, CpLanguage>
|
||||
---@field platforms table<string, CpPlatform>
|
||||
---@field hooks Hooks
|
||||
---@field snippets any[]
|
||||
---@field debug boolean
|
||||
---@field scrapers string[]
|
||||
---@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
|
||||
---@field ui CpUI
|
||||
---@field runtime { effective: table<string, table<string, CpLanguage>> } -- computed
|
||||
|
||||
---@class cp.PartialConfig: cp.Config
|
||||
|
||||
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',
|
||||
}
|
||||
|
||||
-- defaults per the new single schema
|
||||
---@type cp.Config
|
||||
M.defaults = {
|
||||
languages = {
|
||||
cpp = {
|
||||
extension = 'cc',
|
||||
commands = {
|
||||
build = { 'g++', '-std=c++17', '{source}', '-o', '{binary}' },
|
||||
run = { '{binary}' },
|
||||
debug = {
|
||||
'g++',
|
||||
'-std=c++17',
|
||||
'-fsanitize=address,undefined',
|
||||
'{source}',
|
||||
'-o',
|
||||
'{binary}',
|
||||
},
|
||||
},
|
||||
},
|
||||
python = {
|
||||
extension = 'py',
|
||||
commands = {
|
||||
run = { 'python', '{source}' },
|
||||
debug = { 'python', '{source}' },
|
||||
},
|
||||
},
|
||||
},
|
||||
platforms = {
|
||||
codeforces = default_contest_config,
|
||||
atcoder = default_contest_config,
|
||||
cses = default_contest_config,
|
||||
codeforces = {
|
||||
enabled_languages = { 'cpp', 'python' },
|
||||
default_language = 'cpp',
|
||||
overrides = {
|
||||
-- example override, safe to keep empty initially
|
||||
},
|
||||
},
|
||||
atcoder = {
|
||||
enabled_languages = { 'cpp', 'python' },
|
||||
default_language = 'cpp',
|
||||
},
|
||||
cses = {
|
||||
enabled_languages = { 'cpp', 'python' },
|
||||
default_language = 'cpp',
|
||||
},
|
||||
},
|
||||
snippets = {},
|
||||
hooks = {
|
||||
before_run = nil,
|
||||
before_debug = nil,
|
||||
setup_code = nil,
|
||||
},
|
||||
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' },
|
||||
ui = {
|
||||
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,
|
||||
},
|
||||
picker = nil,
|
||||
runtime = { effective = {} },
|
||||
}
|
||||
|
||||
local function is_string_list(t)
|
||||
if type(t) ~= 'table' then
|
||||
return false
|
||||
end
|
||||
for i, v in ipairs(t) do
|
||||
if type(v) ~= 'string' then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function has_tokens(cmd, required)
|
||||
if type(cmd) ~= 'table' then
|
||||
return false
|
||||
end
|
||||
local s = table.concat(cmd, ' ')
|
||||
for _, tok in ipairs(required) do
|
||||
if not s:find(vim.pesc(tok), 1, true) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function validate_language(id, lang)
|
||||
vim.validate({
|
||||
extension = { lang.extension, 'string' },
|
||||
commands = { lang.commands, { 'table' } },
|
||||
})
|
||||
if lang.commands.build ~= nil then
|
||||
vim.validate({ build = { lang.commands.build, { 'table' } } })
|
||||
if not has_tokens(lang.commands.build, { '{source}', '{binary}' }) then
|
||||
error(('[cp.nvim] languages.%s.commands.build must include {source} and {binary}'):format(id))
|
||||
end
|
||||
for _, k in ipairs({ 'run', 'debug' }) do
|
||||
if lang.commands[k] then
|
||||
if not has_tokens(lang.commands[k], { '{binary}' }) then
|
||||
error(('[cp.nvim] languages.%s.commands.%s must include {binary}'):format(id, k))
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
for _, k in ipairs({ 'run', 'debug' }) do
|
||||
if lang.commands[k] then
|
||||
if not has_tokens(lang.commands[k], { '{source}' }) then
|
||||
error(('[cp.nvim] languages.%s.commands.%s must include {source}'):format(id, k))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function merge_lang(base, ov)
|
||||
if not ov then
|
||||
return base
|
||||
end
|
||||
local out = vim.deepcopy(base)
|
||||
if ov.extension then
|
||||
out.extension = ov.extension
|
||||
end
|
||||
if ov.commands then
|
||||
out.commands = vim.tbl_deep_extend('force', out.commands or {}, ov.commands or {})
|
||||
end
|
||||
return out
|
||||
end
|
||||
|
||||
---@param cfg cp.Config
|
||||
local function build_runtime(cfg)
|
||||
cfg.runtime = cfg.runtime or { effective = {} }
|
||||
for plat, p in pairs(cfg.platforms) do
|
||||
vim.validate({
|
||||
enabled_languages = { p.enabled_languages, is_string_list, 'string[]' },
|
||||
default_language = { p.default_language, 'string' },
|
||||
})
|
||||
for _, lid in ipairs(p.enabled_languages) do
|
||||
if not cfg.languages[lid] then
|
||||
error(("[cp.nvim] platform %s references unknown language '%s'"):format(plat, lid))
|
||||
end
|
||||
end
|
||||
if not vim.tbl_contains(p.enabled_languages, p.default_language) then
|
||||
error(
|
||||
("[cp.nvim] platform %s default_language '%s' not in enabled_languages"):format(
|
||||
plat,
|
||||
p.default_language
|
||||
)
|
||||
)
|
||||
end
|
||||
cfg.runtime.effective[plat] = {}
|
||||
for _, lid in ipairs(p.enabled_languages) do
|
||||
local base = cfg.languages[lid]
|
||||
validate_language(lid, base)
|
||||
local eff = merge_lang(base, p.overrides and p.overrides[lid] or nil)
|
||||
validate_language(lid, eff)
|
||||
cfg.runtime.effective[plat][lid] = eff
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---@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({ user_config = { user_config, { 'table', 'nil' }, true } })
|
||||
local cfg = vim.tbl_deep_extend('force', vim.deepcopy(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,
|
||||
},
|
||||
hooks = { cfg.hooks, { 'table' } },
|
||||
ui = { cfg.ui, { 'table' } },
|
||||
})
|
||||
|
||||
vim.validate({
|
||||
ansi = {
|
||||
config.run_panel.ansi,
|
||||
'boolean',
|
||||
'ansi color parsing must be enabled xor disabled',
|
||||
},
|
||||
before_run = { cfg.hooks.before_run, { 'function', 'nil' }, true },
|
||||
before_debug = { cfg.hooks.before_debug, { 'function', 'nil' }, true },
|
||||
setup_code = { cfg.hooks.setup_code, { 'function', 'nil' }, true },
|
||||
})
|
||||
|
||||
vim.validate({
|
||||
ansi = { cfg.ui.run_panel.ansi, 'boolean' },
|
||||
diff_mode = {
|
||||
config.run_panel.diff_mode,
|
||||
function(value)
|
||||
return vim.tbl_contains({ 'none', 'vim', 'git' }, value)
|
||||
cfg.ui.run_panel.diff_mode,
|
||||
function(v)
|
||||
return vim.tbl_contains({ 'none', 'vim', 'git' }, v)
|
||||
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)
|
||||
cfg.ui.run_panel.max_output_lines,
|
||||
function(v)
|
||||
return type(v) == 'number' and v > 0 and v == math.floor(v)
|
||||
end,
|
||||
'max_output_lines must be a positive integer',
|
||||
'positive integer',
|
||||
},
|
||||
git = { cfg.ui.diff.git, { 'table' } },
|
||||
})
|
||||
|
||||
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
|
||||
for id, lang in pairs(cfg.languages) do
|
||||
validate_language(id, lang)
|
||||
end
|
||||
|
||||
build_runtime(cfg)
|
||||
|
||||
local ok, err = utils.check_required_runtime()
|
||||
if not ok then
|
||||
error('[cp.nvim] ' .. err)
|
||||
end
|
||||
|
||||
return config
|
||||
current_config = cfg
|
||||
return cfg
|
||||
end
|
||||
|
||||
local current_config = nil
|
||||
|
||||
function M.set_current_config(config)
|
||||
current_config = config
|
||||
end
|
||||
function M.get_config()
|
||||
return current_config or M.defaults
|
||||
end
|
||||
|
||||
---@param contest_id string
|
||||
|
|
@ -204,25 +282,9 @@ end
|
|||
local function default_filename(contest_id, problem_id)
|
||||
if problem_id then
|
||||
return (contest_id .. problem_id):lower()
|
||||
else
|
||||
return contest_id:lower()
|
||||
end
|
||||
return contest_id:lower()
|
||||
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
|
||||
|
|
|
|||
|
|
@ -242,7 +242,6 @@ end
|
|||
---@return nil
|
||||
function M.handle_compilation_failure(output)
|
||||
local ansi = require('cp.ui.ansi')
|
||||
local config = require('cp.config').setup()
|
||||
|
||||
local txt
|
||||
local hl = {}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
-- lua/cp/setup.lua
|
||||
local M = {}
|
||||
|
||||
local cache = require('cp.cache')
|
||||
|
|
@ -17,9 +18,7 @@ function M.set_platform(platform)
|
|||
)
|
||||
return false
|
||||
end
|
||||
|
||||
state.set_platform(platform)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
|
|
@ -112,15 +111,10 @@ function M.setup_problem(problem_id)
|
|||
if vim.api.nvim_buf_get_lines(source_buf, 0, -1, true)[1] == '' then
|
||||
local has_luasnip, luasnip = pcall(require, 'luasnip')
|
||||
if has_luasnip then
|
||||
local filetype = vim.api.nvim_get_option_value('filetype', { buf = source_buf })
|
||||
local language_name = constants.filetype_to_language[filetype]
|
||||
local canonical_language = constants.canonical_filetypes[language_name] or language_name
|
||||
local prefixed_trigger = ('cp.nvim/%s.%s'):format(platform, canonical_language)
|
||||
|
||||
local prefixed_trigger = ('cp.nvim/%s.%s'):format(platform, language)
|
||||
vim.api.nvim_buf_set_lines(0, 0, -1, false, { prefixed_trigger })
|
||||
vim.api.nvim_win_set_cursor(0, { 1, #prefixed_trigger })
|
||||
vim.cmd.startinsert({ bang = true })
|
||||
|
||||
vim.schedule(function()
|
||||
if luasnip.expandable() then
|
||||
luasnip.expand()
|
||||
|
|
@ -141,8 +135,7 @@ function M.setup_problem(problem_id)
|
|||
vim.fn.expand('%:p'),
|
||||
platform,
|
||||
state.get_contest_id() or '',
|
||||
state.get_problem_id(),
|
||||
language
|
||||
state.get_problem_id()
|
||||
)
|
||||
end)
|
||||
end
|
||||
|
|
@ -177,7 +170,6 @@ function M.navigate_problem(direction)
|
|||
|
||||
local problems = contest_data.problems
|
||||
local index = contest_data.index_map[current_problem_id]
|
||||
|
||||
local new_index = index + direction
|
||||
if new_index < 1 or new_index > #problems then
|
||||
return
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue