feat: add :CP login command for explicit credential management

Problem: credentials were only set implicitly on first :CP submit.
There was no way to update wrong credentials, log out, or set
credentials ahead of time without editing the cache JSON manually.

Solution: add :CP login [platform] which always prompts for username
and password and overwrites any saved credentials for that platform.
Omitting the platform falls back to the active platform. Wire the
command through constants, parse_command, handle_command, and add
tab-completion (suggests platform names). Document in vimdoc under
the SUBMIT section and in the commands reference.
This commit is contained in:
Barrett Ruth 2026-03-03 16:26:48 -05:00 committed by Barrett Ruth
parent a08d1f0c5e
commit 3e0b7beabf
5 changed files with 66 additions and 4 deletions

View file

@ -433,10 +433,20 @@ COMMANDS *cp-commands*
Cancel an active race countdown. Cancel an active race countdown.
Submit Commands ~ Submit Commands ~
:CP login [platform]
Set or update stored credentials for a platform.
Always prompts for username and password,
overwriting any previously saved credentials.
If [platform] is omitted, uses the active platform.
Examples: >
:CP login atcoder
:CP login codeforces
<
:CP submit [--lang {language}] :CP submit [--lang {language}]
Submit the current solution to the online Submit the current solution to the online
judge. Prompts for credentials on first use judge. Uses stored credentials (set via
and stores them for subsequent submissions. :CP login). Prompts on first use if no
credentials are saved.
--lang: Submit solution for a specific language. --lang: Submit solution for a specific language.
State Restoration ~ State Restoration ~
@ -947,9 +957,18 @@ SUBMIT *cp-submit*
Submit the current solution to the online judge. Submit the current solution to the online judge.
:CP login [platform] *cp-login*
Set or update stored credentials for a platform. Always prompts for
username and password, overwriting any previously saved credentials.
Omit [platform] to use the currently active platform.
Credentials are stored in the main cache file under _credentials.
Use this command before submitting for the first time, or any time
you need to update stored credentials.
:CP submit [--lang {language}] :CP submit [--lang {language}]
Submit the current solution. Prompts for credentials on first use Submit the current solution. Uses stored credentials (set via
and stores them for subsequent submissions. :CP login). Prompts on first use if no credentials are saved.
--lang: Override the language to submit. --lang: Override the language to submit.
Platform support: Platform support:

View file

@ -83,6 +83,12 @@ local function parse_command(args)
else else
return { type = 'action', action = 'interact' } return { type = 'action', action = 'interact' }
end end
elseif first == 'login' then
return {
type = 'action',
action = 'login',
platform = args[2],
}
elseif first == 'stress' then elseif first == 'stress' then
return { return {
type = 'action', type = 'action',
@ -317,6 +323,8 @@ function M.handle_command(opts)
edit.toggle_edit(cmd.test_index) edit.toggle_edit(cmd.test_index)
elseif cmd.action == 'stress' then elseif cmd.action == 'stress' then
require('cp.stress').toggle(cmd.generator_cmd, cmd.brute_cmd) require('cp.stress').toggle(cmd.generator_cmd, cmd.brute_cmd)
elseif cmd.action == 'login' then
require('cp.login').login(cmd.platform)
elseif cmd.action == 'submit' then elseif cmd.action == 'submit' then
require('cp.submit').submit({ language = cmd.language }) require('cp.submit').submit({ language = cmd.language })
elseif cmd.action == 'race' then elseif cmd.action == 'race' then

View file

@ -13,6 +13,7 @@ M.ACTIONS = {
'race', 'race',
'stress', 'stress',
'submit', 'submit',
'login',
} }
M.PLATFORM_DISPLAY_NAMES = { M.PLATFORM_DISPLAY_NAMES = {

32
lua/cp/login.lua Normal file
View file

@ -0,0 +1,32 @@
local M = {}
local cache = require('cp.cache')
local logger = require('cp.log')
local state = require('cp.state')
function M.login(platform)
platform = platform or state.get_platform()
if not platform then
logger.log('No platform specified. Usage: :CP login <platform>', vim.log.levels.ERROR)
return
end
vim.ui.input({ prompt = platform .. ' username: ' }, function(username)
if not username or username == '' then
logger.log('Login cancelled', vim.log.levels.WARN)
return
end
vim.fn.inputsave()
local password = vim.fn.inputsecret(platform .. ' password: ')
vim.fn.inputrestore()
if not password or password == '' then
logger.log('Login cancelled', vim.log.levels.WARN)
return
end
cache.load()
cache.set_credentials(platform, { username = username, password = password })
logger.log(platform .. ' credentials saved', vim.log.levels.INFO)
end)
end
return M

View file

@ -103,6 +103,8 @@ end, {
end end
end end
return filter_candidates(candidates) return filter_candidates(candidates)
elseif args[2] == 'login' then
return filter_candidates(platforms)
elseif args[2] == 'race' then elseif args[2] == 'race' then
local candidates = { 'stop' } local candidates = { 'stop' }
vim.list_extend(candidates, platforms) vim.list_extend(candidates, platforms)