diff --git a/doc/cp.nvim.txt b/doc/cp.nvim.txt index add0bcb..e6c4c61 100644 --- a/doc/cp.nvim.txt +++ b/doc/cp.nvim.txt @@ -438,17 +438,14 @@ COMMANDS *cp-commands* < Race Commands ~ - :CP race {platform} {contest_id} [--lang {language}] + :CP {platform} {contest_id} --race [--lang {language}] Start a countdown to the contest's scheduled start time. At T=0, automatically runs: :CP {platform} {contest_id} [--lang ...] Examples: > - :CP race atcoder abc400 - :CP race codeforces 2100 --lang python + :CP atcoder abc400 --race + :CP codeforces 2100 --race --lang python < - :CP race stop - Cancel an active race countdown. - Credential Commands ~ :CP login [platform] Set or update stored credentials for a platform. @@ -588,9 +585,6 @@ through the same code path as |:CP|. *(cp-open)* (cp-open) Open current problem URL in browser. Equivalent to :CP open. - *(cp-race-stop)* -(cp-race-stop) Cancel active race countdown. Equivalent to :CP race stop. - Example configuration: >lua vim.keymap.set('n', 'cr', '(cp-run)') vim.keymap.set('n', 'cp', '(cp-panel)') @@ -601,7 +595,7 @@ Example configuration: >lua vim.keymap.set('n', 'ci', '(cp-interact)') vim.keymap.set('n', 'cs', '(cp-stress)') vim.keymap.set('n', 'cu', '(cp-submit)') - vim.keymap.set('n', 'cR', '(cp-race-stop)') + < ============================================================================== @@ -986,15 +980,14 @@ RACE *cp-race* Count down to a contest's start time and automatically run setup at T=0. -:CP race {platform} {contest_id} [--lang {language}] +:CP {platform} {contest_id} --race [--lang {language}] Start a countdown timer. At T=0, automatically runs: :CP {platform} {contest_id} [--lang {language}] Examples: > - :CP race atcoder abc400 - :CP race codeforces 2100 --lang python + :CP atcoder abc400 --race + :CP codeforces 2100 --race --lang python < -:CP race stop - Cancel an active race countdown. +Starting a new race while one is active automatically cancels the previous one. Statusline integration: see |cp-race-status|. diff --git a/lua/cp/commands/init.lua b/lua/cp/commands/init.lua index f4368ce..d28de00 100644 --- a/lua/cp/commands/init.lua +++ b/lua/cp/commands/init.lua @@ -24,6 +24,7 @@ local actions = constants.ACTIONS ---@field mode? string ---@field debug? boolean ---@field language? string +---@field race? boolean ---@field subcommand? string ---@param str string @@ -70,28 +71,6 @@ local function parse_command(args) else return { type = 'error', message = 'unknown cache subcommand: ' .. subcommand } end - elseif first == 'race' then - if args[2] == 'stop' then - return { type = 'action', action = 'race_stop', requires_context = false } - end - if not args[2] or not args[3] then - return { - type = 'error', - message = 'Usage: :CP race [--lang ]', - } - end - local language = nil - if args[4] == '--lang' and args[5] then - language = args[5] - end - return { - type = 'action', - action = 'race', - requires_context = false, - platform = args[2], - contest = args[3], - language = language, - } elseif first == 'interact' then local inter = args[2] if inter and inter ~= '' then @@ -295,6 +274,17 @@ local function parse_command(args) platform = first, contest = contest, } + elseif #args == 3 and args[3] == '--race' then + local contest = args[2] + if first == 'codeforces' then + contest = canonicalize_cf_contest(contest) + end + return { + type = 'contest_setup', + platform = first, + contest = contest, + race = true, + } elseif #args == 4 and args[3] == '--lang' then local contest = args[2] if first == 'codeforces' then @@ -306,10 +296,35 @@ local function parse_command(args) contest = contest, language = args[4], } + elseif #args == 5 then + local contest = args[2] + if first == 'codeforces' then + contest = canonicalize_cf_contest(contest) + end + local language, race = nil, false + if args[3] == '--race' and args[4] == '--lang' then + language = args[5] + race = true + elseif args[3] == '--lang' and args[5] == '--race' then + language = args[4] + race = true + else + return { + type = 'error', + message = 'Invalid arguments. Usage: :CP [--race] [--lang ]', + } + end + return { + type = 'contest_setup', + platform = first, + contest = contest, + language = language, + race = race, + } else return { type = 'error', - message = 'Invalid arguments. Usage: :CP [--lang ]', + message = 'Invalid arguments. Usage: :CP [--race] [--lang ]', } end end @@ -393,13 +408,6 @@ function M.handle_command(opts) require('cp.stress').toggle(cmd.generator_cmd, cmd.brute_cmd) elseif cmd.action == 'submit' then require('cp.submit').submit({ language = cmd.language }) - elseif cmd.action == 'race' then - if not check_platform_enabled(cmd.platform) then - return - end - require('cp.race').start(cmd.platform, cmd.contest, cmd.language) - elseif cmd.action == 'race_stop' then - require('cp.race').stop() elseif cmd.action == 'open' then local cache = require('cp.cache') cache.load() @@ -470,8 +478,12 @@ function M.handle_command(opts) if not check_platform_enabled(cmd.platform) then return end - local setup = require('cp.setup') - setup.setup_contest(cmd.platform, cmd.contest, nil, cmd.language) + if cmd.race then + require('cp.race').start(cmd.platform, cmd.contest, cmd.language) + else + local setup = require('cp.setup') + setup.setup_contest(cmd.platform, cmd.contest, nil, cmd.language) + end return end end diff --git a/lua/cp/constants.lua b/lua/cp/constants.lua index d2d306e..b227d53 100644 --- a/lua/cp/constants.lua +++ b/lua/cp/constants.lua @@ -10,7 +10,6 @@ M.ACTIONS = { 'cache', 'interact', 'edit', - 'race', 'stress', 'submit', 'open', diff --git a/lua/cp/race.lua b/lua/cp/race.lua index 5b49fc1..8f06db7 100644 --- a/lua/cp/race.lua +++ b/lua/cp/race.lua @@ -20,11 +20,11 @@ local function format_countdown(seconds) local m = math.floor((seconds % 3600) / 60) local s = seconds % 60 if d > 0 then - return string.format('%dd %dh %dm %ds', d, h, m, s) + return string.format('%dd%dh%dm%ds', d, h, m, s) elseif h > 0 then - return string.format('%dh %dm %ds', h, m, s) + return string.format('%dh%dm%ds', h, m, s) elseif m > 0 then - return string.format('%dm %ds', m, s) + return string.format('%dm%ds', m, s) end return string.format('%ds', s) end @@ -39,8 +39,7 @@ function M.start(platform, contest_id, language) return end if race_state.timer then - logger.log('Race already active. Use :CP race stop first.', { level = vim.log.levels.WARN }) - return + M.stop() end cache.load() @@ -48,7 +47,7 @@ function M.start(platform, contest_id, language) local display = constants.PLATFORM_DISPLAY_NAMES[platform] or platform local cached_countdown = cache.get_supports_countdown(platform) if cached_countdown == false then - logger.log(('%s does not support :CP race'):format(display), { level = vim.log.levels.ERROR }) + logger.log(('%s does not support --race'):format(display), { level = vim.log.levels.ERROR }) return end @@ -64,10 +63,7 @@ function M.start(platform, contest_id, language) local sc = result.supports_countdown if sc == false then cache.set_contest_summaries(platform, result.contests or {}, { supports_countdown = false }) - logger.log( - ('%s does not support :CP race'):format(display), - { level = vim.log.levels.ERROR } - ) + logger.log(('%s does not support --race'):format(display), { level = vim.log.levels.ERROR }) return end if result.contests and #result.contests > 0 then @@ -127,7 +123,11 @@ function M.start(platform, contest_id, language) require('cp.setup').setup_contest(p, c, nil, l) else vim.notify( - ('[cp.nvim] %s starts in %s'):format(race_state.contest_name, format_countdown(r)), + ('[cp.nvim]: %s race "%s" starts in %s'):format( + constants.PLATFORM_DISPLAY_NAMES[race_state.platform] or race_state.platform, + race_state.contest_name, + format_countdown(r) + ), vim.log.levels.INFO ) end @@ -141,6 +141,8 @@ function M.stop() logger.log('No active race', { level = vim.log.levels.WARN }) return end + local display = constants.PLATFORM_DISPLAY_NAMES[race_state.platform] or race_state.platform + local name = race_state.contest_name or race_state.contest_id timer:stop() timer:close() race_state.timer = nil @@ -149,7 +151,10 @@ function M.stop() race_state.contest_name = nil race_state.language = nil race_state.start_time = nil - logger.log('Race cancelled', { level = vim.log.levels.INFO, override = true }) + logger.log( + ('Cancelled %s race "%s"'):format(display, name), + { level = vim.log.levels.INFO, override = true } + ) end function M.status() diff --git a/plugin/cp.lua b/plugin/cp.lua index cc6a367..531a5f3 100644 --- a/plugin/cp.lua +++ b/plugin/cp.lua @@ -108,13 +108,6 @@ end, { end end return filter_candidates(candidates) - elseif args[2] == 'race' then - if require('cp.race').status().active then - return filter_candidates({ 'stop' }) - end - local candidates = { 'stop' } - vim.list_extend(candidates, platforms) - return filter_candidates(candidates) elseif args[2] == 'open' then return filter_candidates({ 'problem', 'contest', 'standings' }) elseif args[2] == 'next' or args[2] == 'prev' or args[2] == 'pick' then @@ -129,15 +122,6 @@ end, { if args[2] == 'stress' then local utils = require('cp.utils') return filter_candidates(utils.cwd_executables()) - elseif - args[2] == 'race' - and not require('cp.race').status().active - and vim.tbl_contains(platforms, args[3]) - then - local cache = require('cp.cache') - cache.load() - local contests = cache.get_cached_contest_ids(args[3]) - return filter_candidates(contests) elseif args[2] == 'cache' and args[3] == 'clear' then local candidates = vim.list_extend({}, platforms) table.insert(candidates, '') @@ -152,6 +136,9 @@ end, { cache.load() local contest_data = cache.get_contest_data(args[2], args[3]) local candidates = { '--lang' } + if not require('cp.race').status().active then + table.insert(candidates, '--race') + end if contest_data and contest_data.problems then for _, problem in ipairs(contest_data.problems) do table.insert(candidates, problem.id) @@ -160,34 +147,35 @@ end, { return filter_candidates(candidates) end elseif num_args == 5 then - if - args[2] == 'race' - and not require('cp.race').status().active - and vim.tbl_contains(platforms, args[3]) - then - return filter_candidates({ '--lang' }) - elseif args[2] == 'cache' and args[3] == 'clear' and vim.tbl_contains(platforms, args[4]) then + if args[2] == 'cache' and args[3] == 'clear' and vim.tbl_contains(platforms, args[4]) then local cache = require('cp.cache') cache.load() local contests = cache.get_cached_contest_ids(args[4]) return filter_candidates(contests) elseif vim.tbl_contains(platforms, args[2]) then - if args[4] == '--lang' then + if args[3] == '--race' then + return filter_candidates({ '--lang' }) + elseif args[4] == '--lang' then return filter_candidates(get_enabled_languages(args[2])) + elseif args[3] == '--lang' then + local candidates = {} + if not require('cp.race').status().active then + table.insert(candidates, '--race') + end + return filter_candidates(candidates) else return filter_candidates({ '--lang' }) end end elseif num_args == 6 then - if - args[2] == 'race' - and not require('cp.race').status().active - and vim.tbl_contains(platforms, args[3]) - and args[5] == '--lang' - then - return filter_candidates(get_enabled_languages(args[3])) - elseif vim.tbl_contains(platforms, args[2]) and args[5] == '--lang' then - return filter_candidates(get_enabled_languages(args[2])) + if vim.tbl_contains(platforms, args[2]) then + if args[3] == '--race' and args[4] == '--lang' then + return filter_candidates(get_enabled_languages(args[2])) + elseif args[3] == '--lang' and args[5] == '--race' then + return {} + elseif args[5] == '--lang' then + return filter_candidates(get_enabled_languages(args[2])) + end end end return {} @@ -209,6 +197,3 @@ vim.keymap.set('n', '(cp-pick)', cp_action('pick'), { desc = 'CP pick cont vim.keymap.set('n', '(cp-interact)', cp_action('interact'), { desc = 'CP interactive mode' }) vim.keymap.set('n', '(cp-stress)', cp_action('stress'), { desc = 'CP stress test' }) vim.keymap.set('n', '(cp-submit)', cp_action('submit'), { desc = 'CP submit solution' }) -vim.keymap.set('n', '(cp-race-stop)', function() - require('cp.race').stop() -end, { desc = 'CP stop race countdown' })