more docs
This commit is contained in:
parent
7eb314b02c
commit
6b8a1e2087
10 changed files with 83 additions and 142 deletions
|
|
@ -56,13 +56,6 @@ COMMANDS *cp-commands*
|
|||
:CP {platform} Platform setup: set platform only.
|
||||
Example: >
|
||||
:CP cses
|
||||
<
|
||||
:CP {problem_id} [--lang={language}]
|
||||
Problem switch: switch to different problem
|
||||
within current contest context.
|
||||
Example: >
|
||||
:CP b
|
||||
:CP b --lang=python
|
||||
<
|
||||
Action Commands ~
|
||||
:CP run [--debug] Toggle run panel for individual test case
|
||||
|
|
@ -82,6 +75,15 @@ COMMANDS *cp-commands*
|
|||
:CP prev Navigate to previous problem in current contest.
|
||||
Stops at first problem (no wrapping).
|
||||
|
||||
Cache Commands ~
|
||||
:CP cache clear [contest]
|
||||
Clear the cache data (contest list, problem
|
||||
data, file states) for the specified contest,
|
||||
or all contests if none specified
|
||||
|
||||
:CP cache read
|
||||
View the cache in a pretty-printed lua buffer.
|
||||
|
||||
Command Flags ~
|
||||
*cp-flags*
|
||||
Flags can be used with setup and action commands:
|
||||
|
|
@ -285,7 +287,6 @@ URL format: https://atcoder.jp/contests/abc123/tasks/abc123_a
|
|||
Usage examples: >
|
||||
:CP atcoder abc324 a " Full setup: problem A from contest ABC324
|
||||
:CP atcoder abc324 " Contest setup: load contest metadata only
|
||||
:CP b " Switch to problem B (if contest loaded)
|
||||
:CP next " Navigate to next problem in contest
|
||||
<
|
||||
Note: AtCoder template includes optimizations
|
||||
|
|
@ -303,7 +304,6 @@ URL format: https://codeforces.com/contest/1234/problem/A
|
|||
Usage examples: >
|
||||
:CP codeforces 1934 a " Full setup: problem A from contest 1934
|
||||
:CP codeforces 1934 " Contest setup: load contest metadata only
|
||||
:CP c " Switch to problem C (if contest loaded)
|
||||
:CP prev " Navigate to previous problem in contest
|
||||
<
|
||||
Note: Problem IDs are automatically converted
|
||||
|
|
|
|||
|
|
@ -111,6 +111,11 @@ function M.set_contest_data(platform, contest_id, problems)
|
|||
cache_data[platform][contest_id] = {
|
||||
problems = problems,
|
||||
}
|
||||
cache_data[platform][contest_id].index_map = {}
|
||||
|
||||
for i, problem in ipairs(problems) do
|
||||
cache_data[platform][contest_id].index_map[problem.id] = i
|
||||
end
|
||||
|
||||
M.save()
|
||||
end
|
||||
|
|
@ -140,16 +145,22 @@ function M.get_test_cases(platform, contest_id, problem_id)
|
|||
problem_id = { problem_id, { 'string', 'nil' }, true },
|
||||
})
|
||||
|
||||
local problem_key = problem_id and (contest_id .. '_' .. problem_id) or contest_id
|
||||
if not cache_data[platform] or not cache_data[platform][problem_key] then
|
||||
if
|
||||
not cache_data[platform]
|
||||
or not cache_data[platform][contest_id]
|
||||
or not cache_data[platform][contest_id].problems
|
||||
or not cache_data[platform][contest_id].index_map
|
||||
then
|
||||
return nil
|
||||
end
|
||||
return cache_data[platform][problem_key].test_cases
|
||||
|
||||
local index = cache_data[platform][contest_id].index_map[problem_id]
|
||||
return cache_data[platform][contest_id].problems[index].test_cases
|
||||
end
|
||||
|
||||
---@param platform string
|
||||
---@param contest_id string
|
||||
---@param problem_id? string
|
||||
---@param problem_id string
|
||||
---@param test_cases CachedTestCase[]
|
||||
---@param timeout_ms? number
|
||||
---@param memory_mb? number
|
||||
|
|
@ -173,21 +184,12 @@ function M.set_test_cases(
|
|||
interactive = { interactive, { 'boolean', 'nil' }, true },
|
||||
})
|
||||
|
||||
local problem_key = problem_id and (contest_id .. '_' .. problem_id) or contest_id
|
||||
if not cache_data[platform] then
|
||||
cache_data[platform] = {}
|
||||
end
|
||||
if not cache_data[platform][problem_key] then
|
||||
cache_data[platform][problem_key] = {}
|
||||
end
|
||||
local index = cache_data[platform][contest_id].index_map[problem_id]
|
||||
|
||||
cache_data[platform][contest_id].problems[index].test_cases = test_cases
|
||||
cache_data[platform][contest_id].problems[index].timeout_ms = timeout_ms or 0
|
||||
cache_data[platform][contest_id].problems[index].memory_mb = memory_mb or 0
|
||||
|
||||
cache_data[platform][problem_key].test_cases = test_cases
|
||||
if timeout_ms then
|
||||
cache_data[platform][problem_key].timeout_ms = timeout_ms
|
||||
end
|
||||
if memory_mb then
|
||||
cache_data[platform][problem_key].memory_mb = memory_mb
|
||||
end
|
||||
M.save()
|
||||
end
|
||||
|
||||
|
|
@ -202,12 +204,9 @@ function M.get_constraints(platform, contest_id, problem_id)
|
|||
problem_id = { problem_id, { 'string', 'nil' }, true },
|
||||
})
|
||||
|
||||
local problem_key = problem_id and (contest_id .. '_' .. problem_id) or contest_id
|
||||
if not cache_data[platform] or not cache_data[platform][problem_key] then
|
||||
return nil, nil
|
||||
end
|
||||
local index = cache_data[platform][contest_id].index_map[problem_id]
|
||||
|
||||
local problem_data = cache_data[platform][problem_key]
|
||||
local problem_data = cache_data[platform][contest_id].problems[index]
|
||||
return problem_data.timeout_ms, problem_data.memory_mb
|
||||
end
|
||||
|
||||
|
|
@ -242,42 +241,34 @@ function M.set_file_state(file_path, platform, contest_id, problem_id, language)
|
|||
end
|
||||
|
||||
---@param platform string
|
||||
---@return table[]?
|
||||
---@return table[]
|
||||
function M.get_contest_list(platform)
|
||||
if not cache_data.contest_lists or not cache_data.contest_lists[platform] then
|
||||
return nil
|
||||
local contest_list = {}
|
||||
for contest_id, contest_data in pairs(cache_data[platform] or {}) do
|
||||
table.insert(contest_list, {
|
||||
id = contest_id,
|
||||
name = contest_data.name,
|
||||
display_name = contest_data.display_name,
|
||||
})
|
||||
end
|
||||
|
||||
return cache_data.contest_lists[platform].contests
|
||||
return contest_list
|
||||
end
|
||||
|
||||
---@param platform string
|
||||
---@param contests table[]
|
||||
function M.set_contest_list(platform, contests)
|
||||
if not cache_data.contest_lists then
|
||||
cache_data.contest_lists = {}
|
||||
cache_data[platform] = cache_data[platform] or {}
|
||||
for _, contest in ipairs(contests) do
|
||||
cache_data[platform][contest.id] = cache_data[platform][contest] or {}
|
||||
cache_data[platform][contest.id].display_name = contest.display_name
|
||||
cache_data[platform][contest.id].name = contest.name
|
||||
end
|
||||
|
||||
cache_data.contest_lists[platform] = {
|
||||
contests = contests,
|
||||
}
|
||||
|
||||
M.save()
|
||||
end
|
||||
|
||||
---@param platform string
|
||||
function M.clear_contest_list(platform)
|
||||
if cache_data.contest_lists and cache_data.contest_lists[platform] then
|
||||
cache_data.contest_lists[platform] = nil
|
||||
M.save()
|
||||
end
|
||||
end
|
||||
|
||||
function M.clear_all()
|
||||
cache_data = {
|
||||
file_states = {},
|
||||
contest_lists = {},
|
||||
}
|
||||
cache_data = {}
|
||||
M.save()
|
||||
end
|
||||
|
||||
|
|
@ -286,10 +277,15 @@ function M.clear_platform(platform)
|
|||
if cache_data[platform] then
|
||||
cache_data[platform] = nil
|
||||
end
|
||||
if cache_data.contest_lists and cache_data.contest_lists[platform] then
|
||||
cache_data.contest_lists[platform] = nil
|
||||
end
|
||||
|
||||
M.save()
|
||||
end
|
||||
|
||||
---@return string
|
||||
function M.get_data_pretty()
|
||||
M.load()
|
||||
|
||||
return vim.inspect(cache_data)
|
||||
end
|
||||
|
||||
return M
|
||||
|
|
|
|||
|
|
@ -7,7 +7,16 @@ local logger = require('cp.log')
|
|||
local platforms = constants.PLATFORMS
|
||||
|
||||
function M.handle_cache_command(cmd)
|
||||
if cmd.subcommand == 'clear' then
|
||||
if cmd.subcommand == 'read' then
|
||||
local data = cache.get_data_pretty()
|
||||
|
||||
local buf = vim.api.nvim_create_buf(true, true)
|
||||
vim.api.nvim_buf_set_name(buf, 'cp.nvim://cache.lua')
|
||||
vim.api.nvim_buf_set_lines(buf, 0, -1, false, vim.split(data, '\n'))
|
||||
vim.bo[buf].filetype = 'lua'
|
||||
|
||||
vim.api.nvim_set_current_buf(buf)
|
||||
elseif cmd.subcommand == 'clear' then
|
||||
cache.load()
|
||||
if cmd.platform then
|
||||
if vim.tbl_contains(platforms, cmd.platform) then
|
||||
|
|
|
|||
|
|
@ -44,11 +44,11 @@ local function parse_command(args)
|
|||
if not subcommand then
|
||||
return { type = 'error', message = 'cache command requires subcommand: clear' }
|
||||
end
|
||||
if subcommand == 'clear' then
|
||||
if vim.tbl_contains({ 'clear', 'read' }, subcommand) then
|
||||
local platform = filtered_args[3]
|
||||
return {
|
||||
type = 'cache',
|
||||
subcommand = 'clear',
|
||||
subcommand = subcommand,
|
||||
platform = platform,
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@ local picker_utils = require('cp.pickers')
|
|||
|
||||
local M = {}
|
||||
|
||||
local function contest_picker(platform)
|
||||
local function contest_picker(platform, refresh)
|
||||
local constants = require('cp.constants')
|
||||
local platform_display_name = constants.PLATFORM_DISPLAY_NAMES[platform] or platform
|
||||
local fzf = require('fzf-lua')
|
||||
local contests = picker_utils.get_contests_for_platform(platform)
|
||||
local contests = picker_utils.get_platform_contests(platform, refresh)
|
||||
|
||||
if vim.tbl_isempty(contests) then
|
||||
vim.notify(
|
||||
|
|
@ -48,7 +48,7 @@ local function contest_picker(platform)
|
|||
['ctrl-r'] = function()
|
||||
local cache = require('cp.cache')
|
||||
cache.clear_contest_list(platform)
|
||||
contest_picker(platform)
|
||||
contest_picker(platform, true)
|
||||
end,
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -38,8 +38,9 @@ end
|
|||
|
||||
---Get list of contests for a specific platform
|
||||
---@param platform string Platform identifier (e.g. "codeforces", "atcoder")
|
||||
---@param refresh? boolean Whether to skip caching and append new contests
|
||||
---@return cp.ContestItem[]
|
||||
function M.get_contests_for_platform(platform)
|
||||
function M.get_platform_contests(platform, refresh)
|
||||
logger.log(
|
||||
('Loading %s contests...'):format(constants.PLATFORM_DISPLAY_NAMES[platform]),
|
||||
vim.log.levels.INFO,
|
||||
|
|
@ -48,21 +49,13 @@ function M.get_contests_for_platform(platform)
|
|||
|
||||
cache.load()
|
||||
|
||||
local picker_contests = cache.get_contest_list(platform) or {}
|
||||
local picker_contests = cache.get_contest_list(platform)
|
||||
|
||||
if vim.tbl_isempty(picker_contests) then
|
||||
if refresh or vim.tbl_isempty(picker_contests) then
|
||||
logger.log(('Cache miss on %s contests'):format(platform))
|
||||
local contests = scraper.scrape_contest_list(platform)
|
||||
|
||||
cache.set_contest_list(platform, contests)
|
||||
|
||||
for _, contest in ipairs(contests or {}) do
|
||||
table.insert(picker_contests, {
|
||||
id = contest.id,
|
||||
name = contest.name,
|
||||
display_name = contest.display_name,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
logger.log(
|
||||
|
|
@ -70,6 +63,9 @@ function M.get_contests_for_platform(platform)
|
|||
vim.log.levels.INFO,
|
||||
true
|
||||
)
|
||||
|
||||
picker_contests = cache.get_contest_list(platform)
|
||||
|
||||
return picker_contests
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@ local picker_utils = require('cp.pickers')
|
|||
|
||||
local M = {}
|
||||
|
||||
local function contest_picker(opts, platform)
|
||||
local function contest_picker(opts, platform, refresh)
|
||||
local constants = require('cp.constants')
|
||||
local platform_display_name = constants.PLATFORM_DISPLAY_NAMES[platform] or platform
|
||||
local contests = picker_utils.get_contests_for_platform(platform)
|
||||
local platform_display_name = constants.PLATFORM_DISPLAY_NAMES[platform]
|
||||
local contests = picker_utils.get_platform_contests(platform, refresh)
|
||||
|
||||
if vim.tbl_isempty(contests) then
|
||||
vim.notify(
|
||||
|
|
@ -48,10 +48,8 @@ local function contest_picker(opts, platform)
|
|||
end)
|
||||
|
||||
map('i', '<c-r>', function()
|
||||
local cache = require('cp.cache')
|
||||
cache.clear_contest_list(platform)
|
||||
actions.close(prompt_bufnr)
|
||||
contest_picker(opts, platform)
|
||||
contest_picker(opts, platform, true)
|
||||
end)
|
||||
|
||||
return true
|
||||
|
|
|
|||
|
|
@ -83,34 +83,6 @@ local function parse_test_cases_from_cache(platform, contest_id, problem_id)
|
|||
return test_cases
|
||||
end
|
||||
|
||||
---@param input_file string
|
||||
---@return TestCase[]
|
||||
local function parse_test_cases_from_files(input_file, _)
|
||||
local base_name = vim.fn.fnamemodify(input_file, ':r')
|
||||
local test_cases = {}
|
||||
|
||||
local i = 1
|
||||
while true do
|
||||
local individual_input_file = base_name .. '.' .. i .. '.cpin'
|
||||
local individual_expected_file = base_name .. '.' .. i .. '.cpout'
|
||||
|
||||
if
|
||||
vim.fn.filereadable(individual_input_file) == 1
|
||||
and vim.fn.filereadable(individual_expected_file) == 1
|
||||
then
|
||||
local input_content = table.concat(vim.fn.readfile(individual_input_file), '\n')
|
||||
local expected_content = table.concat(vim.fn.readfile(individual_expected_file), '\n')
|
||||
|
||||
table.insert(test_cases, create_test_case(i, input_content, expected_content))
|
||||
i = i + 1
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
return test_cases
|
||||
end
|
||||
|
||||
---@param platform string
|
||||
---@param contest_id string
|
||||
---@param problem_id string?
|
||||
|
|
@ -136,28 +108,11 @@ end
|
|||
local function run_single_test_case(contest_config, cp_config, test_case)
|
||||
local state = require('cp.state')
|
||||
local source_file = state.get_source_file()
|
||||
if not source_file then
|
||||
return {
|
||||
status = 'fail',
|
||||
actual = '',
|
||||
error = 'No source file found',
|
||||
time_ms = 0,
|
||||
}
|
||||
end
|
||||
|
||||
local language = vim.fn.fnamemodify(source_file, ':e')
|
||||
local language_name = constants.filetype_to_language[language] or contest_config.default_language
|
||||
local language_config = contest_config[language_name]
|
||||
|
||||
if not language_config then
|
||||
return {
|
||||
status = 'fail',
|
||||
actual = '',
|
||||
error = 'No language configuration',
|
||||
time_ms = 0,
|
||||
}
|
||||
end
|
||||
|
||||
local function substitute_template(cmd_template, substitutions)
|
||||
local result = {}
|
||||
for _, arg in ipairs(cmd_template) do
|
||||
|
|
@ -208,6 +163,7 @@ local function run_single_test_case(contest_config, cp_config, test_case)
|
|||
ok = false,
|
||||
signal = nil,
|
||||
timed_out = false,
|
||||
actual_highlights = {},
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
@ -298,11 +254,7 @@ function M.load_test_cases(state)
|
|||
state.get_problem_id()
|
||||
) or {}
|
||||
|
||||
if vim.tbl_isempty(test_cases) then
|
||||
local input_file = state.get_input_file()
|
||||
local expected_file = state.get_expected_file()
|
||||
test_cases = parse_test_cases_from_files(input_file, expected_file)
|
||||
end
|
||||
-- TODO: re-fetch/cache-populating mechanism to ge the test cases if not in the cache
|
||||
|
||||
run_panel_state.test_cases = test_cases
|
||||
run_panel_state.current_index = 1
|
||||
|
|
|
|||
|
|
@ -7,8 +7,6 @@
|
|||
---@field set_problem_id fun(problem_id: string)
|
||||
---@field get_active_panel fun(): string?
|
||||
---@field set_active_panel fun(): string?
|
||||
---@field get_test_cases fun(): table[]?
|
||||
---@field set_test_cases fun(test_cases: table[])
|
||||
---@field get_saved_session fun(): table?
|
||||
---@field set_saved_session fun(session: table)
|
||||
---@field get_context fun(): {platform: string?, contest_id: string?, problem_id: string?}
|
||||
|
|
@ -56,14 +54,6 @@ function M.set_problem_id(problem_id)
|
|||
state.problem_id = problem_id
|
||||
end
|
||||
|
||||
function M.get_test_cases()
|
||||
return state.test_cases
|
||||
end
|
||||
|
||||
function M.set_test_cases(test_cases)
|
||||
state.test_cases = test_cases
|
||||
end
|
||||
|
||||
function M.get_saved_session()
|
||||
return state.saved_session
|
||||
end
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ end, {
|
|||
if args[2] == 'cache' then
|
||||
return vim.tbl_filter(function(cmd)
|
||||
return cmd:find(ArgLead, 1, true) == 1
|
||||
end, { 'clear' })
|
||||
end, { 'clear', 'read' })
|
||||
end
|
||||
elseif num_args == 4 then
|
||||
if args[2] == 'cache' and args[3] == 'clear' then
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue