From 1c8b5cda3e6218e847eae50ce708cc9ecf00e7c5 Mon Sep 17 00:00:00 2001 From: Barrett Ruth Date: Fri, 6 Mar 2026 16:08:11 -0500 Subject: [PATCH] feat(config): merge platform config model and add disabled-platform guard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: Supplying any `platforms` table silently dropped all unlisted platforms, making it easy to accidentally disable platforms. Disabled platforms also produced no user-facing error on invocation. Solution: Switch to a merge model — all six platforms are enabled by default and user entries are deep-merged on top. Set a platform key to `false` to disable it explicitly. Add a `check_platform_enabled` guard in `handle_command` for contest fetch, login, logout, and race actions. --- doc/cp.nvim.txt | 53 ++++++++++++++++++++-------------------- lua/cp/commands/init.lua | 40 +++++++++++++++++++++++++++++- lua/cp/config.lua | 10 ++++++-- 3 files changed, 74 insertions(+), 29 deletions(-) diff --git a/doc/cp.nvim.txt b/doc/cp.nvim.txt index c45b11c..1db397b 100644 --- a/doc/cp.nvim.txt +++ b/doc/cp.nvim.txt @@ -92,26 +92,6 @@ Configuration is done via `vim.g.cp`. Set this before using the plugin: cpp = { extension = 'cpp', commands = { build = { ... } } } }, }, - atcoder = { - enabled_languages = { 'cpp', 'python' }, - default_language = 'cpp', - }, - codeforces = { - enabled_languages = { 'cpp', 'python' }, - default_language = 'cpp', - }, - codechef = { - enabled_languages = { 'cpp', 'python' }, - default_language = 'cpp', - }, - usaco = { - enabled_languages = { 'cpp', 'python' }, - default_language = 'cpp', - }, - kattis = { - enabled_languages = { 'cpp', 'python' }, - default_language = 'cpp', - }, }, debug = false, ui = { @@ -137,21 +117,25 @@ Configuration is done via `vim.g.cp`. Set this before using the plugin: < By default, C++ (g++ with ISO C++17) and Python are preconfigured under -'languages'. Platforms select which languages are enabled and which one is -the default; per-platform overrides can tweak 'extension' or 'commands'. +'languages'. All six platforms are enabled by default. User-supplied +platform entries are merged on top of the defaults — you only need to +specify what you want to change. To disable a platform entirely, set it +to `false`. -For example, to run CodeForces contests with Python by default: +For example, to run Codeforces contests with Python by default and +disable CodeChef: >lua vim.g.cp = { platforms = { codeforces = { default_language = 'python', }, + codechef = false, }, } < Any language is supported provided the proper configuration. For example, to -run CSES problems with Rust using the single schema: +add Rust and use it by default on CSES: >lua vim.g.cp = { languages = { @@ -175,8 +159,11 @@ run CSES problems with Rust using the single schema: Fields: ~ {languages} (table) Global language registry. Each language provides an {extension} and {commands}. - {platforms} (table) Per-platform enablement, - default language, and optional overrides. + {platforms} (table) All six platforms + are enabled by default. Each entry is merged on top + of the platform defaults — omitted fields keep their + defaults and unmentioned platforms stay enabled. Set + a platform key to `false` to disable it entirely. {hooks} (|cp.Hooks|) Hook functions called at various stages. {debug} (boolean, default: false) Show info messages. {scrapers} (string[]) Supported platform ids. @@ -476,6 +463,14 @@ COMMANDS *cp-commands* If [platform] is omitted, uses the active platform. Examples: > :CP logout atcoder +< + :CP {platform} signup + Open the platform's registration page in the + browser via |vim.ui.open|. Works even if + {platform} is not enabled in your config. + Examples: > + :CP atcoder signup + :CP codeforces signup < Submit Commands ~ :CP submit [--lang {language}] @@ -1019,6 +1014,12 @@ Credentials are stored under _credentials in the main cache file Remove stored credentials for a platform. Omit [platform] to use the currently active platform. +:CP {platform} signup + Open the platform's account registration page in the browser via + |vim.ui.open|. Works even if {platform} is not enabled in your + config. {platform} is one of: atcoder, codechef, codeforces, cses, + kattis, usaco. + ============================================================================== SUBMIT *cp-submit* diff --git a/lua/cp/commands/init.lua b/lua/cp/commands/init.lua index 6570748..f4368ce 100644 --- a/lua/cp/commands/init.lua +++ b/lua/cp/commands/init.lua @@ -283,7 +283,7 @@ local function parse_command(args) message = 'Too few arguments - specify a contest.', } elseif #args == 2 then - if args[2] == 'login' or args[2] == 'logout' then + if args[2] == 'login' or args[2] == 'logout' or args[2] == 'signup' then return { type = 'action', action = args[2], requires_context = false, platform = first } end local contest = args[2] @@ -330,6 +330,22 @@ local function parse_command(args) return { type = 'error', message = 'Unknown command or no contest context.' } end +---@param platform string +---@return boolean +local function check_platform_enabled(platform) + local cfg = require('cp.config').get_config() + if not cfg.platforms[platform] then + logger.log( + ("Platform '%s' is not enabled. Add it to vim.g.cp.platforms to enable it."):format( + constants.PLATFORM_DISPLAY_NAMES[platform] or platform + ), + { level = vim.log.levels.ERROR } + ) + return false + end + return true +end + --- Core logic for handling `:CP ...` commands ---@return nil function M.handle_command(opts) @@ -378,6 +394,9 @@ function M.handle_command(opts) 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() @@ -396,9 +415,25 @@ function M.handle_command(opts) end vim.ui.open(url) elseif cmd.action == 'login' then + if not check_platform_enabled(cmd.platform) then + return + end require('cp.credentials').login(cmd.platform) elseif cmd.action == 'logout' then + if not check_platform_enabled(cmd.platform) then + return + end require('cp.credentials').logout(cmd.platform) + elseif cmd.action == 'signup' then + local url = constants.SIGNUP_URLS[cmd.platform] + if not url then + logger.log( + ("No signup URL available for '%s'"):format(cmd.platform), + { level = vim.log.levels.WARN } + ) + return + end + vim.ui.open(url) end elseif cmd.type == 'problem_jump' then local platform = state.get_platform() @@ -432,6 +467,9 @@ function M.handle_command(opts) local cache_commands = require('cp.commands.cache') cache_commands.handle_cache_command(cmd) elseif cmd.type == 'contest_setup' then + 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) return diff --git a/lua/cp/config.lua b/lua/cp/config.lua index 0593638..728b85f 100644 --- a/lua/cp/config.lua +++ b/lua/cp/config.lua @@ -107,6 +107,7 @@ ---@field runtime { effective: table> } -- computed ---@class cp.PartialConfig: cp.Config +---@field platforms? table local M = {} @@ -333,13 +334,18 @@ function M.setup(user_config) vim.validate({ user_config = { user_config, { 'table', 'nil' }, true } }) local defaults = vim.deepcopy(M.defaults) if user_config and user_config.platforms then - for plat in pairs(defaults.platforms) do - if not user_config.platforms[plat] then + for plat, v in pairs(user_config.platforms) do + if v == false then defaults.platforms[plat] = nil end end end local cfg = vim.tbl_deep_extend('force', defaults, user_config or {}) + for plat, v in pairs(cfg.platforms) do + if v == false then + cfg.platforms[plat] = nil + end + end if not next(cfg.languages) then error('[cp.nvim] At least one language must be configured')