fix caching
This commit is contained in:
parent
e6c09a4897
commit
7eb314b02c
7 changed files with 79 additions and 85 deletions
|
|
@ -1,7 +1,5 @@
|
|||
# cp.nvim
|
||||
|
||||
> ⚠️ **Warning**: as of 27/09/25, CodeForces upgraded their anti-scraping technology and support is thus (temporarily) broken. I am actively researching a way around this.
|
||||
|
||||
**The definitive competitive programming environment for Neovim**
|
||||
|
||||
Scrape problems, run tests, and debug solutions across multiple platforms with zero configuration.
|
||||
|
|
@ -29,7 +27,7 @@ cp.nvim follows a simple principle: **solve locally, submit remotely**.
|
|||
### Basic Usage
|
||||
|
||||
1. **Find a contest or problem** on the judge website
|
||||
2. **Set up locally** with `:CP <platform> <contest> [<problem>]`
|
||||
2. **Set up locally** with `:CP <platform> <contest> [--{lang=<lang>,debug}]`
|
||||
|
||||
```
|
||||
:CP codeforces 1848
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ function M.get_contest_data(platform, contest_id)
|
|||
end
|
||||
|
||||
local contest_data = cache_data[platform][contest_id]
|
||||
if not contest_data then
|
||||
if not contest_data or vim.tbl_isempty(contest_data) then
|
||||
return nil
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -74,11 +74,8 @@ local function parse_command(args)
|
|||
}
|
||||
elseif #filtered_args == 3 then
|
||||
return {
|
||||
type = 'full_setup',
|
||||
platform = first,
|
||||
contest = filtered_args[2],
|
||||
problem = filtered_args[3],
|
||||
language = language,
|
||||
type = 'error',
|
||||
message = 'Setup contests with :CP <platform> <contest_id> [--{lang=<lang>,debug}]',
|
||||
}
|
||||
else
|
||||
return { type = 'error', message = 'Too many arguments' }
|
||||
|
|
@ -88,16 +85,6 @@ local function parse_command(args)
|
|||
if state.get_platform() and state.get_contest_id() then
|
||||
local cache = require('cp.cache')
|
||||
cache.load()
|
||||
local contest_data =
|
||||
cache.get_contest_data(state.get_platform() or '', state.get_contest_id() or '')
|
||||
if contest_data and contest_data.problems then
|
||||
local problem_ids = vim.tbl_map(function(prob)
|
||||
return prob.id
|
||||
end, contest_data.problems)
|
||||
if vim.tbl_contains(problem_ids, first) then
|
||||
return { type = 'problem_switch', problem = first, language = language }
|
||||
end
|
||||
end
|
||||
return {
|
||||
type = 'error',
|
||||
message = ("invalid subcommand '%s'"):format(first),
|
||||
|
|
@ -149,24 +136,10 @@ function M.handle_command(opts)
|
|||
if cmd.type == 'contest_setup' then
|
||||
local setup = require('cp.setup')
|
||||
if setup.set_platform(cmd.platform) then
|
||||
setup.setup_contest(cmd.platform, cmd.contest, nil, cmd.language)
|
||||
setup.setup_contest(cmd.platform, cmd.contest, cmd.language, nil)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if cmd.type == 'full_setup' then
|
||||
local setup = require('cp.setup')
|
||||
if setup.set_platform(cmd.platform) then
|
||||
setup.setup_contest(cmd.platform, cmd.contest, cmd.problem, cmd.language)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if cmd.type == 'problem_switch' then
|
||||
local setup = require('cp.setup')
|
||||
setup.setup_problem(state.get_contest_id() or '', cmd.problem, cmd.language)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ function M.restore_from_current_file()
|
|||
local file_state = cache.get_file_state(current_file)
|
||||
if not file_state then
|
||||
logger.log(
|
||||
'No cached state found for current file. Use :CP <platform> <contest> <problem> [...] first.',
|
||||
'No cached state found for current file. Use :CP <platform> <contest> [--{lang=<lang>,debug}...] first.',
|
||||
vim.log.levels.ERROR
|
||||
)
|
||||
return false
|
||||
|
|
@ -37,7 +37,12 @@ function M.restore_from_current_file()
|
|||
state.set_contest_id(file_state.contest_id)
|
||||
state.set_problem_id(file_state.problem_id)
|
||||
|
||||
setup.setup_problem(file_state.contest_id, file_state.problem_id, file_state.language)
|
||||
setup.setup_contest(
|
||||
file_state.platform,
|
||||
file_state.contest_id,
|
||||
file_state.language,
|
||||
file_state.problem_id
|
||||
)
|
||||
|
||||
return true
|
||||
end
|
||||
|
|
|
|||
|
|
@ -28,10 +28,12 @@ function M.set_platform(platform)
|
|||
return true
|
||||
end
|
||||
|
||||
local function scrape_contest_problems(platform, contest_id, problems)
|
||||
local function scrape_contest_problems(problems)
|
||||
cache.load()
|
||||
local missing_problems = {}
|
||||
|
||||
local platform, contest_id = state.get_platform() or '', state.get_contest_id() or ''
|
||||
|
||||
for _, prob in ipairs(problems) do
|
||||
local cached_tests = cache.get_test_cases(platform, contest_id, prob.id)
|
||||
if not cached_tests then
|
||||
|
|
@ -40,7 +42,7 @@ local function scrape_contest_problems(platform, contest_id, problems)
|
|||
end
|
||||
|
||||
if vim.tbl_isempty(missing_problems) then
|
||||
logger.log(('All problems already cached for %s contest %s'):format(platform, contest_id))
|
||||
logger.log(('All problems already cached for %s contest %s.'):format(platform, contest_id))
|
||||
return
|
||||
end
|
||||
|
||||
|
|
@ -67,50 +69,60 @@ local function scrape_contest_problems(platform, contest_id, problems)
|
|||
end
|
||||
end
|
||||
|
||||
function M.setup_contest(platform, contest_id, problem_id, language)
|
||||
if not state.get_platform() then
|
||||
logger.log('No platform configured. Use :CP <platform> <contest> [...] first.')
|
||||
function M.setup_contest(platform, contest_id, language, problem_id)
|
||||
if not platform then
|
||||
logger.log('No platform configured. Use :CP <platform> <contest> [--{lang=<lang>,debug} first.')
|
||||
return
|
||||
end
|
||||
|
||||
local config = config_module.get_config()
|
||||
|
||||
if not vim.tbl_contains(config.scrapers, platform) then
|
||||
logger.log(('Scraping disabled for %s - aborting.'):format(platform), vim.log.levels.WARN)
|
||||
logger.log(('Scraping disabled for %s.'):format(platform), vim.log.levels.WARN)
|
||||
return
|
||||
end
|
||||
|
||||
state.set_contest_id(contest_id)
|
||||
-- TODO: should check cache here, & other uses of gt_contest_data validate them
|
||||
logger.log('Fetching contests problems...', vim.log.levels.INFO, true)
|
||||
local contest_data = cache.get_contest_data(platform, contest_id)
|
||||
|
||||
scraper.scrape_contest_metadata(platform, contest_id, function(result)
|
||||
local problems = result.problems
|
||||
if
|
||||
not contest_data
|
||||
or not contest_data.problems
|
||||
or (problem_id and not cache.get_test_cases(platform, contest_id, problem_id))
|
||||
then
|
||||
logger.log('Fetching contests problems...', vim.log.levels.INFO, true)
|
||||
scraper.scrape_contest_metadata(platform, contest_id, function(result)
|
||||
local problems = result.problems
|
||||
|
||||
cache.set_contest_data(platform, contest_id, problems)
|
||||
cache.set_contest_data(platform, contest_id, problems)
|
||||
|
||||
logger.log(('Found %d problems for %s contest %s'):format(#problems, platform, contest_id))
|
||||
logger.log(('Found %d problems for %s contest %s.'):format(#problems, platform, contest_id))
|
||||
|
||||
local target_problem = problem_id or problems[1].id
|
||||
M.setup_problem(problem_id or problems[1].id, language)
|
||||
|
||||
M.setup_problem(contest_id, target_problem, language)
|
||||
|
||||
scrape_contest_problems(platform, contest_id, problems)
|
||||
end)
|
||||
scrape_contest_problems(problems)
|
||||
end)
|
||||
else
|
||||
M.setup_problem(problem_id, language)
|
||||
end
|
||||
end
|
||||
|
||||
function M.setup_problem(contest_id, problem_id, language)
|
||||
if not state.get_platform() then
|
||||
logger.log('No platform set. run :CP <platform> <contest> first', vim.log.levels.ERROR)
|
||||
---@param problem_id string
|
||||
---@param language? string
|
||||
function M.setup_problem(problem_id, language)
|
||||
local platform = state.get_platform()
|
||||
if not platform then
|
||||
logger.log(
|
||||
'No platform set. run :CP <platform> <contest> [--{lang=<lang>,debug}]',
|
||||
vim.log.levels.ERROR
|
||||
)
|
||||
return
|
||||
end
|
||||
|
||||
local config = config_module.get_config()
|
||||
local platform = state.get_platform() or ''
|
||||
|
||||
state.set_contest_id(contest_id)
|
||||
state.set_problem_id(problem_id)
|
||||
|
||||
local config = config_module.get_config()
|
||||
|
||||
vim.schedule(function()
|
||||
vim.cmd.only({ mods = { silent = true } })
|
||||
|
||||
|
|
@ -149,18 +161,30 @@ function M.setup_problem(contest_id, problem_id, language)
|
|||
config.hooks.setup_code(state)
|
||||
end
|
||||
|
||||
cache.set_file_state(vim.fn.expand('%:p'), platform, contest_id, problem_id, language)
|
||||
cache.set_file_state(
|
||||
vim.fn.expand('%:p'),
|
||||
platform,
|
||||
state.get_contest_id() or '',
|
||||
state.get_problem_id(),
|
||||
language
|
||||
)
|
||||
end)
|
||||
end
|
||||
|
||||
function M.navigate_problem(direction, language)
|
||||
if direction == 0 then
|
||||
return
|
||||
end
|
||||
|
||||
direction = direction > 0 and 1 or -1
|
||||
|
||||
local platform = state.get_platform()
|
||||
local contest_id = state.get_contest_id()
|
||||
local current_problem_id = state.get_problem_id()
|
||||
|
||||
if not platform or not contest_id or not current_problem_id then
|
||||
logger.log(
|
||||
'No platform configured. Use :CP <platform> <contest> [...] first.',
|
||||
'No platform configured. Use :CP <platform> <contest> [--{lang=<lang>,debug}] first.',
|
||||
vim.log.levels.ERROR
|
||||
)
|
||||
return
|
||||
|
|
@ -169,7 +193,13 @@ function M.navigate_problem(direction, language)
|
|||
cache.load()
|
||||
local contest_data = cache.get_contest_data(platform, contest_id)
|
||||
if not contest_data or not contest_data.problems then
|
||||
logger.log('No contest data available', vim.log.levels.ERROR)
|
||||
logger.log(
|
||||
('No data available for %s contest %s.'):format(
|
||||
constants.PLATFORM_DISPLAY_NAMES[platform],
|
||||
contest_id
|
||||
),
|
||||
vim.log.levels.ERROR
|
||||
)
|
||||
return
|
||||
end
|
||||
|
||||
|
|
@ -187,12 +217,7 @@ function M.navigate_problem(direction, language)
|
|||
return
|
||||
end
|
||||
|
||||
local cp = require('cp')
|
||||
local args = { platform, contest_id, problems[new_index].id }
|
||||
if language then
|
||||
vim.list_extend(args, { '--lang', language })
|
||||
end
|
||||
cp.handle_command({ fargs = args })
|
||||
M.setup_contest(platform, contest_id, language, problems[new_index].id)
|
||||
end
|
||||
|
||||
return M
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ local logger = require('cp.log')
|
|||
function M.setup(config)
|
||||
local ok, ls = pcall(require, 'luasnip')
|
||||
if not ok then
|
||||
logger.log('LuaSnip not available - snippets disabled', vim.log.levels.INFO)
|
||||
logger.log('LuaSnip not available - snippets are disabled.', vim.log.levels.INFO, true)
|
||||
return
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -23,12 +23,12 @@ function M.toggle_interactive()
|
|||
state.saved_interactive_session = nil
|
||||
end
|
||||
state.set_active_panel(nil)
|
||||
logger.log('interactive closed')
|
||||
logger.log('Interactive panel closed.')
|
||||
return
|
||||
end
|
||||
|
||||
if state.get_active_panel() then
|
||||
logger.log('another panel is already active', vim.log.levels.ERROR)
|
||||
logger.log('Another panel is already active.', vim.log.levels.WARN)
|
||||
return
|
||||
end
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ function M.toggle_interactive()
|
|||
|
||||
if not platform then
|
||||
logger.log(
|
||||
'No platform configured. Use :CP <platform> <contest> [...] first.',
|
||||
'No platform configured. Use :CP <platform> <contest> [--{lang=<lang>,debug}] first.',
|
||||
vim.log.levels.ERROR
|
||||
)
|
||||
return
|
||||
|
|
@ -44,7 +44,7 @@ function M.toggle_interactive()
|
|||
|
||||
if not contest_id then
|
||||
logger.log(
|
||||
('No contest %s configured for platform %s. Use :CP <platform> <contest> <problem> to set up first.'):format(
|
||||
('No contest %s configured for platform %s. Use :CP <platform> <contest> [--{lang=<lang>,debug}] to set up first.'):format(
|
||||
contest_id,
|
||||
platform
|
||||
),
|
||||
|
|
@ -62,12 +62,8 @@ function M.toggle_interactive()
|
|||
local cache = require('cp.cache')
|
||||
cache.load()
|
||||
local contest_data = cache.get_contest_data(platform, contest_id)
|
||||
vim.print('checking cache - contes_data (DLETE ME): ', contest_data)
|
||||
if contest_data and not contest_data.interactive then
|
||||
logger.log(
|
||||
'This is NOT an interactive problem. Use :CP run instead - aborting.',
|
||||
vim.log.levels.WARN
|
||||
)
|
||||
logger.log('This is NOT an interactive problem. Use :CP run instead.', vim.log.levels.WARN)
|
||||
return
|
||||
end
|
||||
|
||||
|
|
@ -140,7 +136,7 @@ function M.toggle_run_panel(is_debug)
|
|||
|
||||
if not contest_id then
|
||||
logger.log(
|
||||
('No contest %s configured for platform %s. Use :CP <platform> <contest> <problem> to set up first.'):format(
|
||||
('No contest %s configured for platform %s. Use :CP <platform> <contest> [--{lang=<lang>,debug}] to set up first.'):format(
|
||||
contest_id,
|
||||
platform
|
||||
),
|
||||
|
|
@ -159,15 +155,12 @@ function M.toggle_run_panel(is_debug)
|
|||
cache.load()
|
||||
local contest_data = cache.get_contest_data(platform, contest_id)
|
||||
if contest_data and contest_data.interactive then
|
||||
logger.log(
|
||||
'This is an interactive problem. Use :CP interact instead - aborting.',
|
||||
vim.log.levels.WARN
|
||||
)
|
||||
logger.log('This is an interactive problem. Use :CP interact instead.', vim.log.levels.WARN)
|
||||
return
|
||||
end
|
||||
|
||||
logger.log(
|
||||
('run panel: platform=%s, contest=%s, problem=%s'):format(
|
||||
('Run panel: platform=%s, contest=%s, problem=%s'):format(
|
||||
tostring(platform),
|
||||
tostring(contest_id),
|
||||
tostring(problem_id)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue