548 lines
23 KiB
Text
548 lines
23 KiB
Text
*cp.nvim.txt* Competitive programming plugin for Neovim
|
|
|
|
Author: Barrett Ruth <br.barrettruth@gmail.com>
|
|
License: Same terms as Vim itself (see |license|)
|
|
|
|
==============================================================================
|
|
INTRODUCTION *cp.nvim*
|
|
|
|
cp.nvim is a competitive programming plugin that automates problem setup,
|
|
compilation, and testing workflow for online judges.
|
|
|
|
Supported platforms (for now!): AtCoder, Codeforces, CSES
|
|
|
|
==============================================================================
|
|
REQUIREMENTS *cp-requirements*
|
|
|
|
- Neovim 0.10.0+
|
|
- Unix-like operating system
|
|
- uv package manager (https://docs.astral.sh/uv/)
|
|
|
|
==============================================================================
|
|
COMMANDS *cp-commands*
|
|
|
|
:CP *:CP*
|
|
cp.nvim uses a single :CP command with intelligent argument parsing:
|
|
|
|
Setup Commands ~
|
|
:CP {platform} {contest_id}
|
|
Full setup: set platform and load contest metadata.
|
|
Scrapes test cases and creates source file.
|
|
Example: >
|
|
:CP codeforces 1933
|
|
<
|
|
:CP {platform} {contest_id}
|
|
Contest setup: set platform, load contest metadata,
|
|
and scrape all test cases in the contest.
|
|
Opens the first problem after completion.
|
|
Example: >
|
|
:CP atcoder abc324
|
|
<
|
|
Action Commands ~
|
|
:CP run Toggle run panel for individual test cases.
|
|
Shows per-test results with redesigned
|
|
layout for efficient comparison.
|
|
|
|
:CP debug
|
|
Same as above but with the debug mode configured
|
|
settings.
|
|
|
|
:CP pick Launch configured picker for interactive
|
|
platform/contest selection.
|
|
|
|
:CP interact [script]
|
|
Open an interactive terminal for the current problem.
|
|
If an executable interactor is provided, runs the compiled
|
|
binary against the source file (see
|
|
*cp-interact*). Otherwise, runs the source
|
|
file. Only valid for interactive problems.
|
|
|
|
Navigation Commands ~
|
|
:CP next Navigate to next problem in current contest.
|
|
Stops at last problem (no wrapping).
|
|
|
|
|
|
:CP prev Navigate to previous problem in current contest.
|
|
Stops at first problem (no wrapping).
|
|
|
|
:CP {problem_id} Jump to problem {problem_id} in a contest.
|
|
Requires that a contest has already been set up.
|
|
|
|
State Restoration ~
|
|
:CP Restore state from current file.
|
|
Automatically detects platform, contest, problem,
|
|
and language from cached state. Use this after
|
|
switching files to restore your CP environment.
|
|
|
|
Cache Commands ~
|
|
:CP cache clear [contest]
|
|
Clear the cache data for the specified contest,
|
|
or all contests if none specified.
|
|
|
|
:CP cache read
|
|
View the cache in a pretty-printed lua buffer.
|
|
Exit with q.
|
|
|
|
Template Variables ~
|
|
*cp-template-vars*
|
|
Command templates support variable substitution using {variable} syntax:
|
|
|
|
• {source} Source file path (e.g. "abc324a.cpp")
|
|
• {binary} Output binary path (e.g. "build/abc324a.run")
|
|
|
|
Example template: >
|
|
build = { 'g++', '{source}', '-o', '{binary}', '-std=c++17' }
|
|
< Would expand to: >
|
|
g++ abc324a.cpp -o build/abc324a.run -std=c++17
|
|
<
|
|
|
|
==============================================================================
|
|
CONFIGURATION *cp-config*
|
|
|
|
Here's an example configuration with lazy.nvim:
|
|
>lua
|
|
{
|
|
'barrett-ruth/cp.nvim',
|
|
cmd = 'CP',
|
|
build = 'uv sync',
|
|
opts = {
|
|
languages = {
|
|
cpp = {
|
|
extension = 'cc',
|
|
commands = {
|
|
build = { 'g++', '-std=c++17', '{source}', '-o', '{binary}',
|
|
'-fdiagnostics-color=always' },
|
|
run = { '{binary}' },
|
|
debug = { 'g++', '-std=c++17', '-fsanitize=address,undefined',
|
|
'{source}', '-o', '{binary}' },
|
|
},
|
|
},
|
|
python = {
|
|
extension = 'py',
|
|
commands = {
|
|
run = { 'python', '{source}' },
|
|
debug = { 'python', '{source}' },
|
|
},
|
|
},
|
|
},
|
|
platforms = {
|
|
cses = {
|
|
enabled_languages = { 'cpp', 'python' },
|
|
default_language = 'cpp',
|
|
overrides = {
|
|
cpp = { extension = 'cpp', commands = { build = { ... } } }
|
|
},
|
|
},
|
|
atcoder = {
|
|
enabled_languages = { 'cpp', 'python' },
|
|
default_language = 'cpp',
|
|
},
|
|
codeforces = {
|
|
enabled_languages = { 'cpp', 'python' },
|
|
default_language = 'cpp',
|
|
},
|
|
},
|
|
open_url = true,
|
|
debug = false,
|
|
ui = {
|
|
run_panel = {
|
|
ansi = true,
|
|
diff_mode = 'vim',
|
|
max_output_lines = 50,
|
|
},
|
|
diff = {
|
|
git = {
|
|
args = { 'diff', '--no-index', '--word-diff=plain',
|
|
'--word-diff-regex=.', '--no-prefix' },
|
|
},
|
|
},
|
|
picker = 'telescope',
|
|
},
|
|
}
|
|
}
|
|
<
|
|
|
|
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'.
|
|
|
|
For example, to run CodeForces contests with Python by default:
|
|
>lua
|
|
{
|
|
platforms = {
|
|
codeforces = {
|
|
default_language = 'python',
|
|
},
|
|
},
|
|
}
|
|
<
|
|
Any language is supported provided the proper configuration. For example, to
|
|
run CSES problems with Rust using the single schema:
|
|
>lua
|
|
{
|
|
languages = {
|
|
rust = {
|
|
extension = 'rs',
|
|
commands = {
|
|
build = { 'rustc', '{source}', '-o', '{binary}' },
|
|
run = { '{binary}' },
|
|
},
|
|
},
|
|
},
|
|
platforms = {
|
|
cses = {
|
|
enabled_languages = { 'cpp', 'python', 'rust' },
|
|
default_language = 'rust',
|
|
},
|
|
},
|
|
}
|
|
<
|
|
*cp.Config*
|
|
Fields: ~
|
|
{languages} (table<string,|CpLanguage|>) Global language registry.
|
|
Each language provides an {extension} and {commands}.
|
|
{platforms} (table<string,|CpPlatform|>) Per-platform enablement,
|
|
default language, and optional overrides.
|
|
{hooks} (|cp.Hooks|) Hook functions called at various stages.
|
|
{debug} (boolean, default: false) Show info messages.
|
|
{scrapers} (string[]) Supported platform ids.
|
|
{filename} (function, optional)
|
|
function(contest, contest_id, problem_id, config, language): string
|
|
Should return full filename with extension.
|
|
(default: concatenates contest_id and problem_id, lowercased)
|
|
{ui} (|CpUI|) UI settings: run panel, diff backend, picker.
|
|
{open_url} (boolean) Open the contest & problem url in the browser
|
|
when the contest is first opened.
|
|
|
|
*CpPlatform*
|
|
Fields: ~
|
|
{enabled_languages} (string[]) Language ids enabled on this platform.
|
|
{default_language} (string) One of {enabled_languages}.
|
|
{overrides} (table<string,|CpPlatformOverrides|>, optional)
|
|
Per-language overrides of {extension} and/or {commands}.
|
|
|
|
*CpLanguage*
|
|
Fields: ~
|
|
{extension} (string) File extension without leading dot.
|
|
{commands} (|CpLangCommands|) Command templates.
|
|
|
|
*CpLangCommands*
|
|
Fields: ~
|
|
{build} (string[], optional) For compiled languages.
|
|
Must include {source} and {binary}.
|
|
{run} (string[], optional) Runtime command.
|
|
Compiled: must include {binary}.
|
|
Interpreted: must include {source}.
|
|
{debug} (string[], optional) Debug variant; same token rules
|
|
as {build} (compiled) or {run} (interpreted).
|
|
|
|
*CpUI*
|
|
Fields: ~
|
|
{run_panel} (|RunPanelConfig|) Test panel behavior configuration.
|
|
{diff} (|DiffConfig|) Diff backend configuration.
|
|
{picker} (string|nil) 'telescope', 'fzf-lua', or nil.
|
|
|
|
*cp.RunPanelConfig*
|
|
Fields: ~
|
|
{ansi} (boolean, default: true) Enable ANSI color parsing
|
|
and highlighting.
|
|
{diff_mode} (string, default: "none") Diff backend: "none",
|
|
"vim", or "git".
|
|
{max_output_lines} (number, default: 50) Maximum lines of test output.
|
|
|
|
*cp.DiffConfig*
|
|
Fields: ~
|
|
{git} (|cp.DiffGitConfig|) Git diff backend configuration.
|
|
|
|
*cp.DiffGitConfig*
|
|
Fields: ~
|
|
{args} (string[]) Command-line arguments for git diff.
|
|
Default: { 'diff', '--no-index', '--word-diff=plain',
|
|
'--word-diff-regex=.', '--no-prefix' }
|
|
• --no-index: Compare files outside git repository
|
|
• --word-diff=plain: Character-level diff markers
|
|
• --word-diff-regex=.: Split on every character
|
|
• --no-prefix: Remove a/ b/ prefixes from output
|
|
|
|
*cp.Hooks*
|
|
Fields: ~
|
|
{before_run} (function, optional) Called before test panel opens.
|
|
function(state: cp.State)
|
|
{before_debug} (function, optional) Called before debug build/run.
|
|
function(state: cp.State)
|
|
{setup_code} (function, optional) Called after source file is opened.
|
|
function(state: cp.State)
|
|
|
|
Hook functions receive the cp.nvim state object (cp.State). See the state
|
|
module documentation (lua/cp/state.lua) for available methods and fields.
|
|
|
|
Example usage in hook:
|
|
>lua
|
|
hooks = {
|
|
setup_code = function(state)
|
|
print("Setting up " .. state.get_base_name())
|
|
print("Source file: " .. state.get_source_file())
|
|
end
|
|
}
|
|
<
|
|
|
|
==============================================================================
|
|
WORKFLOW *cp-workflow*
|
|
|
|
For the sake of consistency and simplicity, cp.nvim extracts contest/problem
|
|
identifiers from URLs. This means that, for example, CodeForces/AtCoder
|
|
contests are configured by their round id rather than round number. See below.
|
|
|
|
==============================================================================
|
|
PLATFORM-SPECIFIC USAGE *cp-platforms*
|
|
|
|
AtCoder ~
|
|
*cp-atcoder*
|
|
URL format:
|
|
https://atcoder.jp/contests/{contest_id}/tasks/{contest_id}_{problem_id}
|
|
|
|
Usage examples: >
|
|
:CP atcoder abc324 " Set up atcoder.jp/contests/abc324
|
|
|
|
Codeforces ~
|
|
*cp-codeforces*
|
|
URL format: https://codeforces.com/contest/{contest_id}/problem/{problem_id}
|
|
|
|
Usage examples: >
|
|
:CP codeforces 1934 " Set up codeforces.com/contest/1934
|
|
|
|
CSES ~
|
|
*cp-cses*
|
|
URL format: https://cses.fi/problemset/task/{problem_id}
|
|
|
|
Usage examples: >
|
|
:CP cses dynamic_programming " Set up all problems in dp category
|
|
|
|
==============================================================================
|
|
|
|
COMPLETE WORKFLOW EXAMPLE *cp-example*
|
|
|
|
Example: Setting up and solving AtCoder contest ABC324
|
|
|
|
1. Browse to https://atcoder.jp/contests/abc324
|
|
|
|
2. Set up entire contest (bulk setup): >
|
|
:CP atcoder abc324
|
|
< This scrapes all test case data, downloads all test cases,
|
|
and opens the first problem.
|
|
|
|
3. Code your solution, then test: >
|
|
:CP run
|
|
< Navigate with j/k, run specific tests with <enter>
|
|
Exit test panel with q or :CP run when done
|
|
|
|
4. Move to next problem: >
|
|
:CP next
|
|
< This automatically sets up the next problem (likely problem B)
|
|
|
|
5. Continue solving problems with :CP next/:CP prev navigation
|
|
|
|
6. Switch to another file (e.g. previous contest): >
|
|
:e ~/contests/abc323/a.cpp
|
|
:CP
|
|
< Automatically restores abc323 contest context
|
|
|
|
7. Submit solutions on AtCoder website
|
|
|
|
==============================================================================
|
|
PICKER INTEGRATION *cp-picker*
|
|
|
|
When picker integration is enabled in configuration, cp.nvim provides interactive
|
|
platform and contest selection using telescope.nvim or fzf-lua.
|
|
|
|
:CP pick *:CP-pick*
|
|
Launch configured picker for interactive problem selection.
|
|
Control Flow: Select Platform → Contest → Code!
|
|
|
|
Requires picker = 'telescope' or picker = 'fzf-lua' in configuration.
|
|
Requires corresponding plugin (telescope.nvim or fzf-lua) to be installed.
|
|
|
|
PICKER KEYMAPS *cp-picker-keys*
|
|
<c-r> Force refresh/update contest list.
|
|
Useful when contest lists are outdated or incomplete
|
|
|
|
==============================================================================
|
|
RUN PANEL *cp-run*
|
|
|
|
The run panel provides individual test case debugging. Problem time/memory
|
|
limit constraints are in columns Time/Mem respectively. Used time/memory are
|
|
in columns Runtime/RSS respectively.
|
|
|
|
Interface ~
|
|
|
|
The run panel uses the following table layout: >
|
|
|
|
┌─────┬────────┬──────────────┬───────────┬──────────┬──────────┬─────────────┐
|
|
│ # │ Status │ Runtime (ms) │ Time (ms) │ RSS (MB) │ Mem (MB) │ Exit Code │
|
|
├─────┼────────┼──────────────┼───────────┼──────────┼──────────┼─────────────┤
|
|
│ 1 │ AC │ 12.0 │ 2000 │ 123 │ 256 │ 0 │
|
|
│ >2 │ WA │ 45.70 │ 2000 │ 100 │ 256 │ 1 │
|
|
├─────┴────────┴──────────────┴───────────┴──────────┴──────────┴─────────────┤
|
|
│ Input: │
|
|
│ 5 3 │
|
|
├─────┬────────┬──────────────┬───────────┬──────────┬──────────┬─────────────┤
|
|
│ 3 │ TLE │ 9.0 │ 2000 │ 256 │ 256 │ 136 (SIGBUS)│
|
|
│ 4 │ RTE │ 0.0 │ 2000 │ 256 │ 256 │139 (SIGUSR2)│
|
|
└─────┴────────┴──────────────┴───────────┴──────────┴──────────┴─────────────┘
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
│ Expected vs Actual │
|
|
│ 423 │
|
|
│ 100 │
|
|
│ hello world │
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
|
|
Status Indicators ~
|
|
|
|
Test cases use competitive programming terminology with color highlighting:
|
|
|
|
AC Accepted (passed)
|
|
WA Wrong Answer (output mismatch)
|
|
TLE Time Limit Exceeded (timeout)
|
|
MLE Memory Limit Exceeded Error (heuristic)
|
|
RTE Runtime Error (other non-zero exit code)
|
|
NA Any other state
|
|
<
|
|
|
|
==============================================================================
|
|
INTERACTIVE MODE *cp-interact*
|
|
|
|
Run interactive problems manually or with an orchestrator. :CP interact is
|
|
available for interactive problems. Test cases are ignored in interactive mode
|
|
(no run panel, no diffs).
|
|
|
|
When using :CP interact {interactor}, the interactor must be executable
|
|
(chmod +x). Completion after :CP interact suggests executables in CWD.
|
|
|
|
1) Terminal-only ~
|
|
:CP interact
|
|
Execute the current program and open an interactive terminal running
|
|
it directly. Use this for manual testing.
|
|
|
|
2) Orchestrated ~
|
|
:CP interact {interactor}
|
|
Execute the current program and open an interactive terminal that runs
|
|
your interactor script against it.
|
|
{interactor} is an executable file relative to the CWD.
|
|
Example:
|
|
:CP interact my-executable-interactor.py
|
|
|
|
|
|
Keymaps ~
|
|
<c-q> Close the terminal and restore the previous layout.
|
|
|
|
==============================================================================
|
|
ANSI COLORS AND HIGHLIGHTING *cp-ansi*
|
|
|
|
cp.nvim provides comprehensive ANSI color support and highlighting for
|
|
compiler output, program stderr, and diff visualization.
|
|
|
|
If you cannot see color highlighting in your config, it is likely due to an
|
|
erroneous config. Most tools (GCC, Python, Clang, Rustc) color stdout based on
|
|
whether stdout is connected to a terminal. One can usually get aorund this by
|
|
leveraging flags to force colored output. For example, to force colors with GCC,
|
|
alter your config as follows:
|
|
>lua
|
|
{
|
|
commands = {
|
|
build = {
|
|
'g++',
|
|
'-fdiagnostics-color=always',
|
|
...
|
|
}
|
|
}
|
|
}
|
|
<
|
|
|
|
==============================================================================
|
|
HIGHLIGHT GROUPS *cp-highlights*
|
|
|
|
Test Status Groups ~
|
|
|
|
CpTestAC Green foreground for AC status
|
|
CpTestWA Red foreground for WA status
|
|
CpTestTLE Orange foreground for TLE status
|
|
CpTestMLE Orange foreground for MLE status
|
|
CpTestRTE Purple foreground for RTE status
|
|
CpTestNA Gray foreground for remaining state
|
|
|
|
ANSI Color Groups ~
|
|
|
|
cp.nvim preserves ANSI colors from compiler output and program stderr using
|
|
a sophisticated parsing system. Colors are automatically mapped to your
|
|
terminal colorscheme via vim.g.terminal_color_* variables.
|
|
|
|
Diff Highlighting ~
|
|
|
|
The git diff backend uses Neovim's built-in highlight groups that automatically
|
|
adapt to your colorscheme:
|
|
|
|
DiffAdd Highlights added text in git diffs
|
|
DiffDelete Highlights removed text in git diffs
|
|
|
|
==============================================================================
|
|
TERMINAL COLOR INTEGRATION *cp-terminal-colors*
|
|
|
|
ANSI colors automatically use the terminal's color palette through Neovim's
|
|
vim.g.terminal_color_* variables.
|
|
|
|
==============================================================================
|
|
HIGHLIGHT CUSTOMIZATION *cp-highlight-custom*
|
|
|
|
Customize highlight groups after your colorscheme loads:
|
|
>lua
|
|
vim.api.nvim_create_autocmd('ColorScheme', {
|
|
callback = function()
|
|
vim.api.nvim_set_hl(0, 'CpTestAC', { link = 'String' })
|
|
end
|
|
})
|
|
|
|
==============================================================================
|
|
RUN PANEL KEYMAPS *cp-test-keys*
|
|
|
|
<c-n> Navigate to next test case
|
|
<c-p> Navigate to previous test case
|
|
t Cycle through diff modes: none → git → vim
|
|
q Exit run panel and restore layout
|
|
<c-q> Exit interactive terminal and restore layout
|
|
|
|
Diff Modes ~
|
|
|
|
Three diff backends are available:
|
|
|
|
none Nothing
|
|
vim Built-in vim diff (default, always available)
|
|
git Character-level git word-diff (requires git, more precise)
|
|
|
|
The git backend shows character-level changes with [-removed-] and {+added+}
|
|
markers.
|
|
|
|
Execution Details ~
|
|
|
|
Test cases are executed individually using the same compilation and
|
|
execution pipeline, but with isolated input/output for
|
|
precise failure analysis.
|
|
|
|
==============================================================================
|
|
FILE STRUCTURE *cp-files*
|
|
|
|
cp.nvim creates the following file structure upon problem setup: >
|
|
|
|
{problem_id}.{ext} " Source file
|
|
build/
|
|
{problem_id}.run " Compiled binary
|
|
io/
|
|
{problem_id}.n.cpin " nth test input
|
|
{problem_id}.n.cpout " nth program output
|
|
{problem_id}.expected " Expected output
|
|
<
|
|
==============================================================================
|
|
HEALTH CHECK *cp-health*
|
|
|
|
Run |:checkhealth| cp to verify your setup.
|
|
|
|
vim:tw=78:ts=8:ft=help:norl:
|