fix(log): improve logging

This commit is contained in:
Barrett Ruth 2025-10-01 16:41:24 -04:00
parent 62af1965f8
commit a925686a17
10 changed files with 144 additions and 225 deletions

View file

@ -7,15 +7,20 @@ local logger = require('cp.log')
local platforms = constants.PLATFORMS local platforms = constants.PLATFORMS
function M.handle_cache_command(cmd) function M.handle_cache_command(cmd)
cmd.platform = cmd.platform:lower()
if cmd.subcommand == 'clear' then if cmd.subcommand == 'clear' then
cache.load() cache.load()
if cmd.platform then if cmd.platform then
if vim.tbl_contains(platforms, cmd.platform) then if vim.tbl_contains(platforms, cmd.platform) then
cache.clear_platform(cmd.platform) cache.clear_platform(cmd.platform)
logger.log(('cleared cache for %s'):format(cmd.platform), vim.log.levels.INFO, true) logger.log(
('Cache cleared for platform %s'):format(cmd.platform),
vim.log.levels.INFO,
true
)
else else
logger.log( logger.log(
('unknown platform: %s. Available: %s'):format( ("Unknown platform: '%s'. Available: %s"):format(
cmd.platform, cmd.platform,
table.concat(platforms, ', ') table.concat(platforms, ', ')
), ),
@ -24,7 +29,7 @@ function M.handle_cache_command(cmd)
end end
else else
cache.clear_all() cache.clear_all()
logger.log('cleared all cache', vim.log.levels.INFO, true) logger.log('Cache cleared', vim.log.levels.INFO, true)
end end
end end
end end

View file

@ -8,7 +8,7 @@ function M.handle_pick_action()
if not config.picker then if not config.picker then
logger.log( logger.log(
'No picker configured. Set picker = "telescope" or picker = "fzf-lua" in config', 'No picker configured. Set picker = "{telescope,fzf-lua}" in your config.',
vim.log.levels.ERROR vim.log.levels.ERROR
) )
return return
@ -20,14 +20,14 @@ function M.handle_pick_action()
local ok = pcall(require, 'telescope') local ok = pcall(require, 'telescope')
if not ok then if not ok then
logger.log( logger.log(
'Telescope not available. Install telescope.nvim or change picker config', 'telescope.nvim is not available. Install telescope.nvim xor change your picker config.',
vim.log.levels.ERROR vim.log.levels.ERROR
) )
return return
end end
local ok_cp, telescope_picker = pcall(require, 'cp.pickers.telescope') local ok_cp, telescope_picker = pcall(require, 'cp.pickers.telescope')
if not ok_cp then if not ok_cp then
logger.log('Failed to load telescope integration', vim.log.levels.ERROR) logger.log('Failed to load telescope integration.', vim.log.levels.ERROR)
return return
end end
@ -36,14 +36,14 @@ function M.handle_pick_action()
local ok, _ = pcall(require, 'fzf-lua') local ok, _ = pcall(require, 'fzf-lua')
if not ok then if not ok then
logger.log( logger.log(
'fzf-lua not available. Install fzf-lua or change picker config', 'fzf-lua is not available. Install fzf-lua xor change your picker config',
vim.log.levels.ERROR vim.log.levels.ERROR
) )
return return
end end
local ok_cp, fzf_picker = pcall(require, 'cp.pickers.fzf_lua') local ok_cp, fzf_picker = pcall(require, 'cp.pickers.fzf_lua')
if not ok_cp then if not ok_cp then
logger.log('Failed to load fzf-lua integration', vim.log.levels.ERROR) logger.log('Failed to load fzf-lua integration.', vim.log.levels.ERROR)
return return
end end

View file

@ -5,7 +5,7 @@ local logger = require('cp.log')
local snippets = require('cp.snippets') local snippets = require('cp.snippets')
if not vim.fn.has('nvim-0.10.0') then if not vim.fn.has('nvim-0.10.0') then
logger.log('[cp.nvim]: requires nvim-0.10.0+', vim.log.levels.ERROR) logger.log('Requires nvim-0.10.0+', vim.log.levels.ERROR)
return {} return {}
end end

View file

@ -2,6 +2,7 @@ local M = {}
local cache = require('cp.cache') local cache = require('cp.cache')
local config = require('cp.config').get_config() local config = require('cp.config').get_config()
local constants = require('cp.constants')
local logger = require('cp.log') local logger = require('cp.log')
local scraper = require('cp.scraper') local scraper = require('cp.scraper')
@ -21,7 +22,6 @@ local scraper = require('cp.scraper')
---@return cp.PlatformItem[] ---@return cp.PlatformItem[]
function M.get_platforms() function M.get_platforms()
local constants = require('cp.constants')
local result = {} local result = {}
for _, platform in ipairs(constants.PLATFORMS) do for _, platform in ipairs(constants.PLATFORMS) do
@ -40,7 +40,11 @@ end
---@param platform string Platform identifier (e.g. "codeforces", "atcoder") ---@param platform string Platform identifier (e.g. "codeforces", "atcoder")
---@return cp.ContestItem[] ---@return cp.ContestItem[]
function M.get_contests_for_platform(platform) function M.get_contests_for_platform(platform)
logger.log(('Loading %s contests..'):format(platform), vim.log.levels.INFO, true) logger.log(
('Loading %s contests...'):format(constants.PLATFORM_DISPLAY_NAMES[platform]),
vim.log.levels.INFO,
true
)
cache.load() cache.load()
@ -62,21 +66,11 @@ function M.get_contests_for_platform(platform)
end end
logger.log( logger.log(
('Loaded %d %s contests.'):format(#picker_contests, platform), ('Loaded %s %s contests.'):format(#picker_contests, constants.PLATFORM_DISPLAY_NAMES[platform]),
vim.log.levels.INFO, vim.log.levels.INFO,
true true
) )
return picker_contests return picker_contests
end end
---@param platform string Platform identifier
---@param contest_id string Contest identifier
---@param problem_id string Problem identifier
function M.setup_problem(platform, contest_id, problem_id)
vim.schedule(function()
local cp = require('cp')
cp.handle_command({ fargs = { platform, contest_id, problem_id } })
end)
end
return M return M

View file

@ -7,7 +7,7 @@ local state = require('cp.state')
function M.restore_from_current_file() function M.restore_from_current_file()
local current_file = vim.fn.expand('%:p') local current_file = vim.fn.expand('%:p')
if current_file == '' then if current_file == '' then
logger.log('No file is currently open', vim.log.levels.ERROR) logger.log('No file is currently open.', vim.log.levels.ERROR)
return false return false
end end
@ -15,7 +15,7 @@ function M.restore_from_current_file()
local file_state = cache.get_file_state(current_file) local file_state = cache.get_file_state(current_file)
if not file_state then if not file_state then
logger.log( 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> <problem> [...] first.',
vim.log.levels.ERROR vim.log.levels.ERROR
) )
return false return false
@ -25,7 +25,7 @@ function M.restore_from_current_file()
('Restoring from cached state: %s %s %s'):format( ('Restoring from cached state: %s %s %s'):format(
file_state.platform, file_state.platform,
file_state.contest_id, file_state.contest_id,
file_state.problem_id or 'N/A' file_state.problem_id
) )
) )

View file

@ -52,7 +52,7 @@ end
---@return {code: integer, stdout: string, stderr: string} ---@return {code: integer, stdout: string, stderr: string}
function M.compile_generic(language_config, substitutions) function M.compile_generic(language_config, substitutions)
if not language_config.compile then if not language_config.compile then
logger.log('no compilation step required') logger.log('No compilation step required for language - skipping.')
return { code = 0, stderr = '' } return { code = 0, stderr = '' }
end end
@ -73,9 +73,9 @@ function M.compile_generic(language_config, substitutions)
result.stderr = ansi.bytes_to_string(result.stderr or '') result.stderr = ansi.bytes_to_string(result.stderr or '')
if result.code == 0 then if result.code == 0 then
logger.log(('compilation successful (%.1fms)'):format(compile_time), vim.log.levels.INFO) logger.log(('Compilation successful in %.1fms.'):format(compile_time), vim.log.levels.INFO)
else else
logger.log(('compilation failed (%.1fms)'):format(compile_time)) logger.log(('Compilation failed in %.1fms.'):format(compile_time))
end end
return result return result
@ -107,14 +107,14 @@ local function execute_command(cmd, input_data, timeout_ms)
local actual_code = result.code or 0 local actual_code = result.code or 0
if result.code == 124 then if result.code == 124 then
logger.log(('execution timed out after %.1fms'):format(execution_time), vim.log.levels.WARN) logger.log(('Execution timed out in %.1fms.'):format(execution_time), vim.log.levels.WARN)
elseif actual_code ~= 0 then elseif actual_code ~= 0 then
logger.log( logger.log(
('execution failed (exit code %d, %.1fms)'):format(actual_code, execution_time), ('Execution failed in %.1fms (exit code %d).'):format(execution_time, actual_code),
vim.log.levels.WARN vim.log.levels.WARN
) )
else else
logger.log(('execution successful (%.1fms)'):format(execution_time)) logger.log(('Execution successful in %.1fms.'):format(execution_time))
end end
return { return {
@ -177,8 +177,8 @@ function M.compile_problem(contest_config, is_debug)
local state = require('cp.state') local state = require('cp.state')
local source_file = state.get_source_file() local source_file = state.get_source_file()
if not source_file then if not source_file then
logger.log('No source file found', vim.log.levels.ERROR) logger.log('No source file found.', vim.log.levels.ERROR)
return { success = false, output = 'No source file found' } return { success = false, output = 'No source file found.' }
end end
local language = get_language_from_file(source_file, contest_config) local language = get_language_from_file(source_file, contest_config)
@ -186,7 +186,7 @@ function M.compile_problem(contest_config, is_debug)
if not language_config then if not language_config then
logger.log('No configuration for language: ' .. language, vim.log.levels.ERROR) logger.log('No configuration for language: ' .. language, vim.log.levels.ERROR)
return { success = false, output = 'No configuration for language: ' .. language } return { success = false, output = ('No configuration for language %s.'):format(language) }
end end
local binary_file = state.get_binary_file() local binary_file = state.get_binary_file()
@ -203,10 +203,6 @@ function M.compile_problem(contest_config, is_debug)
if compile_result.code ~= 0 then if compile_result.code ~= 0 then
return { success = false, output = compile_result.stdout or 'unknown error' } return { success = false, output = compile_result.stdout or 'unknown error' }
end end
logger.log(
('compilation successful (%s)'):format(is_debug and 'debug mode' or 'test mode'),
vim.log.levels.INFO
)
end end
return { success = true, output = nil } return { success = true, output = nil }
@ -220,7 +216,10 @@ function M.run_problem(contest_config, is_debug)
local output_file = state.get_output_file() local output_file = state.get_output_file()
if not source_file or not output_file then if not source_file or not output_file then
logger.log('Missing required file paths', vim.log.levels.ERROR) logger.log(
('Missing required file paths %s and %s'):format(source_file, output_file),
vim.log.levels.ERROR
)
return return
end end
@ -257,13 +256,14 @@ function M.run_problem(contest_config, is_debug)
local cache = require('cp.cache') local cache = require('cp.cache')
cache.load() cache.load()
local platform = state.get_platform() local platform = state.get_platform()
local contest_id = state.get_contest_id() local contest_id = state.get_contest_id()
local problem_id = state.get_problem_id() local problem_id = state.get_problem_id()
local expected_file = state.get_expected_file() local expected_file = state.get_expected_file()
if not platform or not contest_id or not expected_file then if not platform or not contest_id or not expected_file then
logger.log('configure a contest before running a problem', vim.log.levels.ERROR) logger.log('Configure a contest before running a problem', vim.log.levels.ERROR)
return return
end end
local timeout_ms, _ = cache.get_constraints(platform, contest_id, problem_id) local timeout_ms, _ = cache.get_constraints(platform, contest_id, problem_id)

View file

@ -185,7 +185,7 @@ local function run_single_test_case(contest_config, cp_config, test_case)
} }
if language_config.compile and binary_file and vim.fn.filereadable(binary_file) == 0 then if language_config.compile and binary_file and vim.fn.filereadable(binary_file) == 0 then
logger.log('binary not found, compiling first...') logger.log('Binary not found - compiling first.')
local compile_cmd = substitute_template(language_config.compile, substitutions) local compile_cmd = substitute_template(language_config.compile, substitutions)
local redirected_cmd = vim.deepcopy(compile_cmd) local redirected_cmd = vim.deepcopy(compile_cmd)
redirected_cmd[#redirected_cmd] = redirected_cmd[#redirected_cmd] .. ' 2>&1' redirected_cmd[#redirected_cmd] = redirected_cmd[#redirected_cmd] .. ' 2>&1'
@ -219,9 +219,6 @@ local function run_single_test_case(contest_config, cp_config, test_case)
local start_time = vim.uv.hrtime() local start_time = vim.uv.hrtime()
local timeout_ms = run_panel_state.constraints and run_panel_state.constraints.timeout_ms or 2000 local timeout_ms = run_panel_state.constraints and run_panel_state.constraints.timeout_ms or 2000
if not run_panel_state.constraints then
logger.log('no problem constraints available, using default 2000ms timeout')
end
local redirected_run_cmd = vim.deepcopy(run_cmd) local redirected_run_cmd = vim.deepcopy(run_cmd)
redirected_run_cmd[#redirected_run_cmd] = redirected_run_cmd[#redirected_run_cmd] .. ' 2>&1' redirected_run_cmd[#redirected_run_cmd] = redirected_run_cmd[#redirected_run_cmd] .. ' 2>&1'
local result = vim local result = vim
@ -315,14 +312,7 @@ function M.load_test_cases(state)
state.get_problem_id() state.get_problem_id()
) )
local constraint_info = run_panel_state.constraints logger.log(('Loaded %d test case(s)'):format(#test_cases), vim.log.levels.INFO)
and string.format(
' with %dms/%dMB limits',
run_panel_state.constraints.timeout_ms,
run_panel_state.constraints.memory_mb
)
or ''
logger.log(('loaded %d test case(s)%s'):format(#test_cases, constraint_info), vim.log.levels.INFO)
return #test_cases > 0 return #test_cases > 0
end end

View file

@ -5,7 +5,7 @@ local logger = require('cp.log')
local function syshandle(result) local function syshandle(result)
if result.code ~= 0 then if result.code ~= 0 then
local msg = 'Scraper failed: ' .. (result.error or result.stderr or 'Unknown error') local msg = 'Scraper failed: ' .. (result.stderr or 'Unknown error')
logger.log(msg, vim.log.levels.ERROR) logger.log(msg, vim.log.levels.ERROR)
return { return {
success = false, success = false,
@ -69,13 +69,17 @@ end
function M.scrape_contest_metadata(platform, contest_id, callback) function M.scrape_contest_metadata(platform, contest_id, callback)
run_scraper(platform, 'metadata', { contest_id }, { run_scraper(platform, 'metadata', { contest_id }, {
on_exit = function(result) on_exit = function(result)
if not result.success then if not result.success or vim.tbl_isempty(result.data.problems) then
logger.log( logger.log(
('Failed to scrape metadata for %s contest %s - aborting.'):format(platform, contest_id) ('Failed to scrape metadata for %s contest %s - aborting.'):format(platform, contest_id),
vim.log.levels.ERROR
) )
return return
end end
callback(result.data)
if type(callback) == 'function' then
callback(result.data)
end
end, end,
}) })
end end
@ -83,7 +87,10 @@ end
function M.scrape_contest_list(platform) function M.scrape_contest_list(platform)
local result = run_scraper(platform, 'contests', {}, { sync = true }) local result = run_scraper(platform, 'contests', {}, { sync = true })
if not result.success or not result.data.contests then if not result.success or not result.data.contests then
logger.log(('Could not scrape contests list for platform %s: %s'):format(platform, result.msg)) logger.log(
('Could not scrape contests list for platform %s: %s'):format(platform, result.msg),
vim.log.levels.ERROR
)
return {} return {}
end end
@ -123,7 +130,9 @@ function M.scrape_problem_tests(platform, contest_id, problem_id, callback)
end end
end) end)
callback(result.data) if type(callback) == 'function' then
callback(result.data)
end
end, end,
}) })
end end

View file

@ -28,155 +28,7 @@ function M.set_platform(platform)
return true return true
end end
-- NOTE: this is backwards local function scrape_contest_problems(platform, contest_id, problems)
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.')
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)
return
end
state.set_contest_id(contest_id)
logger.log('fetching contests problems...', vim.log.levels.INFO, true)
scraper.scrape_contest_metadata(platform, contest_id, function(result)
local problems = result.problems
if vim.tbl_isempty(problems) then
logger.log('no problems found in contest', vim.log.levels.ERROR)
return
end
logger.log(('found %d problems'):format(#problems))
local target_problem = problem_id or problems[1].id
if problem_id then
local problem_exists = false
for _, prob in ipairs(problems) do
if prob.id == problem_id then
problem_exists = true
break
end
end
if not problem_exists then
logger.log(
('invalid problem %s for contest %s'):format(problem_id, contest_id),
vim.log.levels.ERROR
)
return
end
end
-- NOTE: should setup buffer without a name, then save it with proper name later for immediate editing
M.setup_problem(contest_id, target_problem, language)
M.scrape_remaining_problems(platform, contest_id, problems)
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)
return
end
local config = config_module.get_config()
local platform = state.get_platform() or ''
logger.log(('setting up problem %s%s...'):format(contest_id, problem_id or ''))
state.set_contest_id(contest_id)
state.set_problem_id(problem_id)
vim.schedule(function()
local ok, err = pcall(function()
vim.cmd.only({ mods = { silent = true } })
local source_file = state.get_source_file(language)
if not source_file then
return
end
vim.cmd.e(source_file)
local source_buf = vim.api.nvim_get_current_buf()
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)
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()
else
vim.api.nvim_buf_set_lines(0, 0, 1, false, { '' })
vim.api.nvim_win_set_cursor(0, { 1, 0 })
end
vim.cmd.stopinsert()
end)
else
vim.api.nvim_input(('i%s<c-space><esc>'):format(platform))
end
end
if config.hooks and config.hooks.setup_code then
config.hooks.setup_code(state)
end
cache.set_file_state(vim.fn.expand('%:p'), platform, contest_id, problem_id, language)
logger.log(('ready - problem %s'):format(state.get_base_name()))
end)
if not ok then
logger.log(('setup error: %s'):format(err), vim.log.levels.ERROR)
end
end)
local cached_tests = cache.get_test_cases(platform, contest_id, problem_id)
if cached_tests then
state.set_test_cases(cached_tests)
logger.log(('using cached test cases (%d)'):format(#cached_tests))
else
logger.log('loading test cases...')
scraper.scrape_problem_tests(platform, contest_id, problem_id, function(result)
state.set_test_cases(result.tests or {})
cached_tests = {}
for i, test_case in ipairs(result.tests or {}) do
table.insert(cached_tests, {
index = i,
input = test_case.input,
expected = test_case.expected,
})
end
cache.set_test_cases(
platform,
contest_id,
problem_id,
cached_tests,
result.timeout_ms,
result.memory_mb
)
end)
end
end
function M.scrape_remaining_problems(platform, contest_id, problems)
cache.load() cache.load()
local missing_problems = {} local missing_problems = {}
@ -188,21 +40,98 @@ function M.scrape_remaining_problems(platform, contest_id, problems)
end end
if vim.tbl_isempty(missing_problems) then if vim.tbl_isempty(missing_problems) then
logger.log('all problems already cached') logger.log(('All problems already cached for %s contest %s'):format(platform, contest_id))
return return
end end
logger.log(('caching %d remaining problems...'):format(#missing_problems))
for _, prob in ipairs(missing_problems) do for _, prob in ipairs(missing_problems) do
scraper.scrape_problem_tests(platform, contest_id, prob.id, function(result) scraper.scrape_problem_tests(platform, contest_id, prob.id)
if result.success then
logger.log(('background: scraped problem %s'):format(prob.id))
end
end)
end end
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.')
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)
return
end
state.set_contest_id(contest_id)
logger.log('Fetching contests problems...', vim.log.levels.INFO, true)
scraper.scrape_contest_metadata(platform, contest_id, function(result)
local problems = result.problems
logger.log(('found %d problems'):format(#problems))
local target_problem = problem_id or problems[1].id
M.setup_problem(contest_id, target_problem, language)
scrape_contest_problems(platform, contest_id, problems)
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)
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)
vim.schedule(function()
vim.cmd.only({ mods = { silent = true } })
local source_file = state.get_source_file(language)
if not source_file then
return
end
vim.cmd.e(source_file)
local source_buf = vim.api.nvim_get_current_buf()
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)
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()
else
vim.api.nvim_buf_set_lines(0, 0, 1, false, { '' })
vim.api.nvim_win_set_cursor(0, { 1, 0 })
end
vim.cmd.stopinsert()
end)
end
end
if config.hooks and config.hooks.setup_code then
config.hooks.setup_code(state)
end
cache.set_file_state(vim.fn.expand('%:p'), platform, contest_id, problem_id, language)
end)
end
function M.navigate_problem(direction, language) function M.navigate_problem(direction, language)
local platform = state.get_platform() local platform = state.get_platform()
local contest_id = state.get_contest_id() local contest_id = state.get_contest_id()
@ -219,7 +148,7 @@ function M.navigate_problem(direction, language)
cache.load() cache.load()
local contest_data = cache.get_contest_data(platform, contest_id) local contest_data = cache.get_contest_data(platform, contest_id)
if not contest_data or not contest_data.problems then if not contest_data or not contest_data.problems then
logger.log('no contest data available', vim.log.levels.ERROR) logger.log('No contest data available', vim.log.levels.ERROR)
return return
end end
@ -232,19 +161,12 @@ function M.navigate_problem(direction, language)
end end
end end
if not current_index then
logger.log('current problem not found in contest', vim.log.levels.ERROR)
return
end
local new_index = current_index + direction local new_index = current_index + direction
if new_index < 1 or new_index > #problems then if new_index < 1 or new_index > #problems then
logger.log('no more problems in that direction', vim.log.levels.WARN)
return return
end end
local new_problem = problems[new_index] M.setup_problem(contest_id, problems[new_index].id, language)
M.setup_problem(contest_id, new_problem.id, language)
end end
return M return M

View file

@ -19,7 +19,6 @@ describe('cp command parsing', function()
setup_contest = function() end, setup_contest = function() end,
navigate_problem = function() end, navigate_problem = function() end,
setup_problem = function() end, setup_problem = function() end,
scrape_remaining_problems = function() end,
} }
package.loaded['cp.setup'] = mock_setup package.loaded['cp.setup'] = mock_setup