fix: expand language IDs, fix AtCoder submit, normalize logging (#353)

## Problem

Language version coverage was incomplete across all platforms, AtCoder
submit used a stale cookie fast-path that caused silent failures, and
raw
`vim.notify` calls throughout the codebase produced inconsistent or
missing `[cp.nvim]:` prefixes.

## Solution

Remove cookie persistence from AtCoder login/submit (always fresh
login),
increase the submit nav timeout to 40s, and switch to in-memory buffer
upload with the correct per-language extension from a full
`_LANGUAGE_ID_EXTENSION`
map covering all 116 AtCoder languages. Expand `LANGUAGE_VERSIONS` in
`constants.lua` with all AtCoder languages, 15 new CF languages with
full
version variants, and 50+ Kattis languages. Fix AtCoder `prolog` ID
(`6079`→`6081`, was Pony) and remove the non-existent `racket` entry.
Replace all raw `vim.notify` calls with `logger.log`. Simplify the
submit
language doc to point at `constants.lua` rather than maintaining a
static table.
This commit is contained in:
Barrett Ruth 2026-03-06 21:35:13 -05:00 committed by GitHub
parent 1ac521a126
commit 291de4e137
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 356 additions and 114 deletions

View file

@ -262,15 +262,8 @@ local function validate_language(id, lang)
if lang.commands.build ~= nil then
vim.validate({ build = { lang.commands.build, { 'table' } } })
if not has_tokens(lang.commands.build, { '{source}', '{binary}' }) then
error(('[cp.nvim] languages.%s.commands.build must include {source} and {binary}'):format(id))
end
for _, k in ipairs({ 'run', 'debug' }) do
if lang.commands[k] then
if not has_tokens(lang.commands[k], { '{binary}' }) then
error(('[cp.nvim] languages.%s.commands.%s must include {binary}'):format(id, k))
end
end
if not has_tokens(lang.commands.build, { '{source}' }) then
error(('[cp.nvim] languages.%s.commands.build must include {source}'):format(id))
end
else
for _, k in ipairs({ 'run', 'debug' }) do

View file

@ -75,14 +75,67 @@ M.signal_codes = {
M.LANGUAGE_VERSIONS = {
atcoder = {
cpp = { ['c++20'] = '6054', ['c++23'] = '6017' },
python = { python3 = '6082', pypy3 = '6083' },
cpp = { ['c++20'] = '6054', ['c++23'] = '6017', ['c++23-clang'] = '6116' },
python = { python3 = '6082', pypy3 = '6083', codon = '6115' },
java = { java = '6056' },
rust = { rust = '6088' },
c = { c23clang = '6013', c23gcc = '6014' },
go = { go = '6051', gccgo = '6050' },
haskell = { haskell = '6052' },
csharp = { csharp = '6015', ['csharp-aot'] = '6016' },
kotlin = { kotlin = '6062' },
ruby = { ruby = '6087', truffleruby = '6086' },
javascript = { bun = '6057', deno = '6058', nodejs = '6059' },
typescript = { deno = '6100', bun = '6101', nodejs = '6102' },
scala = { scala = '6090', ['scala-native'] = '6091' },
ocaml = { ocaml = '6073' },
dart = { dart = '6033' },
elixir = { elixir = '6038' },
erlang = { erlang = '6041' },
fsharp = { fsharp = '6042' },
swift = { swift = '6095' },
zig = { zig = '6111' },
nim = { nim = '6072', ['nim-old'] = '6071' },
lua = { lua = '6067', luajit = '6068' },
perl = { perl = '6076' },
php = { php = '6077' },
pascal = { pascal = '6075' },
crystal = { crystal = '6028' },
d = { dmd = '6030', gdc = '6031', ldc = '6032' },
julia = { julia = '6114' },
r = { r = '6084' },
commonlisp = { commonlisp = '6027' },
scheme = { chezscheme = '6092', gauche = '6093' },
clojure = { clojure = '6022', ['clojure-aot'] = '6023', babashka = '6021' },
ada = { ada = '6002' },
bash = { bash = '6008' },
fortran = { fortran2023 = '6047', fortran2018 = '6046', fortran77 = '6048' },
gleam = { gleam = '6049' },
lean = { lean = '6065' },
pony = { pony = '6079' },
prolog = { prolog = '6081' },
vala = { vala = '6106' },
v = { v = '6105' },
sql = { duckdb = '6118' },
},
codeforces = {
cpp = { ['c++17'] = '54', ['c++20'] = '89', ['c++23'] = '91' },
python = { python3 = '31', pypy3 = '70' },
cpp = { ['c++17'] = '54', ['c++20'] = '89', ['c++23'] = '91', c11 = '43' },
python = { python3 = '31', pypy3 = '70', python2 = '7', pypy2 = '40', ['pypy3-old'] = '41' },
java = { java8 = '36', java21 = '87' },
kotlin = { ['1.7'] = '83', ['1.9'] = '88', ['2.2'] = '99' },
rust = { ['2021'] = '75', ['2024'] = '98' },
go = { go = '32' },
csharp = { mono = '9', dotnet3 = '65', dotnet6 = '79', dotnet9 = '96' },
haskell = { haskell = '12' },
javascript = { v8 = '34', nodejs = '55' },
ruby = { ruby = '67' },
scala = { scala = '20' },
ocaml = { ocaml = '19' },
d = { d = '28' },
perl = { perl = '13' },
php = { php = '6' },
pascal = { freepascal = '4', pascalabc = '51' },
fsharp = { fsharp = '97' },
},
cses = {
cpp = { ['c++17'] = 'C++17' },
@ -92,9 +145,58 @@ M.LANGUAGE_VERSIONS = {
},
kattis = {
cpp = { ['c++17'] = 'C++', ['c++20'] = 'C++', ['c++23'] = 'C++' },
python = { python3 = 'Python 3' },
python = { python3 = 'Python 3', python2 = 'Python 2' },
java = { java = 'Java' },
rust = { rust = 'Rust' },
ada = { ada = 'Ada' },
algol60 = { algol60 = 'Algol 60' },
algol68 = { algol68 = 'Algol 68' },
apl = { apl = 'APL' },
bash = { bash = 'Bash' },
bcpl = { bcpl = 'BCPL' },
bqn = { bqn = 'BQN' },
c = { c = 'C' },
cobol = { cobol = 'COBOL' },
commonlisp = { commonlisp = 'Common Lisp' },
crystal = { crystal = 'Crystal' },
csharp = { csharp = 'C#' },
d = { d = 'D' },
dart = { dart = 'Dart' },
elixir = { elixir = 'Elixir' },
erlang = { erlang = 'Erlang' },
forth = { forth = 'Forth' },
fortran = { fortran = 'Fortran' },
fortran77 = { fortran77 = 'Fortran 77' },
fsharp = { fsharp = 'F#' },
gerbil = { gerbil = 'Gerbil' },
go = { go = 'Go' },
haskell = { haskell = 'Haskell' },
icon = { icon = 'Icon' },
javascript = { javascript = 'JavaScript (Node.js)', spidermonkey = 'JavaScript (SpiderMonkey)' },
julia = { julia = 'Julia' },
kotlin = { kotlin = 'Kotlin' },
lua = { lua = 'Lua' },
modula2 = { modula2 = 'Modula-2' },
nim = { nim = 'Nim' },
objectivec = { objectivec = 'Objective-C' },
ocaml = { ocaml = 'OCaml' },
octave = { octave = 'Octave' },
odin = { odin = 'Odin' },
pascal = { pascal = 'Pascal' },
perl = { perl = 'Perl' },
php = { php = 'PHP' },
pli = { pli = 'PL/I' },
prolog = { prolog = 'Prolog' },
racket = { racket = 'Racket' },
ruby = { ruby = 'Ruby' },
scala = { scala = 'Scala' },
simula = { simula = 'Simula 67' },
smalltalk = { smalltalk = 'Smalltalk' },
snobol = { snobol = 'SNOBOL' },
swift = { swift = 'Swift' },
typescript = { typescript = 'TypeScript' },
visualbasic = { visualbasic = 'Visual Basic' },
zig = { zig = 'Zig' },
},
usaco = {
cpp = { ['c++11'] = 'cpp', ['c++17'] = 'cpp' },

View file

@ -21,7 +21,7 @@ local function ensure_initialized()
local ok, result = pcall(config_module.setup, user_config)
if not ok then
local msg = tostring(result):gsub('^.+:%d+: ', '')
vim.notify(msg, vim.log.levels.ERROR)
logger.log(msg, { level = vim.log.levels.ERROR, override = true, sync = true })
return false
end
config_module.set_current_config(result)

View file

@ -1,3 +1,4 @@
local logger = require('cp.log')
local picker_utils = require('cp.pickers')
local M = {}
@ -9,9 +10,9 @@ local function contest_picker(platform, refresh, language)
local contests = picker_utils.get_platform_contests(platform, refresh)
if vim.tbl_isempty(contests) then
vim.notify(
logger.log(
("No contests found for platform '%s'"):format(platform_display_name),
vim.log.levels.WARN
{ level = vim.log.levels.WARN }
)
return
end

View file

@ -4,6 +4,7 @@ local conf = require('telescope.config').values
local action_state = require('telescope.actions.state')
local actions = require('telescope.actions')
local logger = require('cp.log')
local picker_utils = require('cp.pickers')
local M = {}
@ -14,9 +15,9 @@ local function contest_picker(opts, platform, refresh, language)
local contests = picker_utils.get_platform_contests(platform, refresh)
if vim.tbl_isempty(contests) then
vim.notify(
logger.log(
('No contests found for platform: %s'):format(platform_display_name),
vim.log.levels.WARN
{ level = vim.log.levels.WARN }
)
return
end

View file

@ -239,13 +239,13 @@ function M.start(platform, contest_id, language)
logger.log('Contest started!', { level = vim.log.levels.INFO, override = true })
race_try_setup(p, c, l, 1, token)
elseif should_notify(r) then
vim.notify(
('[cp.nvim]: %s race "%s" starts in %s'):format(
logger.log(
('%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
{ level = vim.log.levels.INFO, override = true }
)
end
end)

View file

@ -78,7 +78,7 @@ function M.submit(opts)
prompt_credentials(platform, function(creds)
vim.cmd.update()
vim.notify('[cp.nvim] Submitting...', vim.log.levels.INFO)
logger.log('Submitting...', { level = vim.log.levels.INFO, override = true })
require('cp.scraper').submit(
platform,
@ -89,7 +89,10 @@ function M.submit(opts)
creds,
function(ev)
vim.schedule(function()
vim.notify('[cp.nvim] ' .. (STATUS_MSGS[ev.status] or ev.status), vim.log.levels.INFO)
logger.log(
STATUS_MSGS[ev.status] or ev.status,
{ level = vim.log.levels.INFO, override = true }
)
end)
end,
function(result)

View file

@ -142,7 +142,10 @@ local function discover_nix_submit_cmd()
local plugin_path = M.get_plugin_path()
vim.cmd.redraw()
vim.notify('Building submit environment...', vim.log.levels.INFO)
logger.log(
'Building submit environment...',
{ level = vim.log.levels.INFO, override = true, sync = true }
)
vim.cmd.redraw()
local result = vim
.system(
@ -209,7 +212,10 @@ local function discover_nix_python()
end
local plugin_path = M.get_plugin_path()
vim.notify('[cp.nvim] Building Python environment with nix...', vim.log.levels.INFO)
logger.log(
'Building Python environment with nix...',
{ level = vim.log.levels.INFO, override = true, sync = true }
)
vim.cmd.redraw()
local result = vim
.system(
@ -263,7 +269,10 @@ function M.setup_python_env()
if not on_nixos and vim.fn.executable('uv') == 1 then
local plugin_path = M.get_plugin_path()
logger.log('Python env: uv sync (dir=' .. plugin_path .. ')')
vim.notify('[cp.nvim] Setting up Python environment...', vim.log.levels.INFO)
logger.log(
'Setting up Python environment...',
{ level = vim.log.levels.INFO, override = true, sync = true }
)
vim.cmd.redraw()
local env = vim.fn.environ()