commit
20712dd427
4 changed files with 173 additions and 45 deletions
156
doc/cp.txt
Normal file
156
doc/cp.txt
Normal file
|
|
@ -0,0 +1,156 @@
|
||||||
|
*cp.txt* Competitive programming plugin for Neovim
|
||||||
|
|
||||||
|
Author: Barrett Ruth <br.barrettruth@gmail.com>
|
||||||
|
License: Same terms as Vim itself (see |license|)
|
||||||
|
|
||||||
|
INTRODUCTION *cp* *cp.nvim*
|
||||||
|
|
||||||
|
cp.nvim is a competitive programming plugin that automates problem setup,
|
||||||
|
compilation, and testing workflow for online judges.
|
||||||
|
|
||||||
|
Supported platforms: AtCoder, Codeforces, CSES
|
||||||
|
|
||||||
|
REQUIREMENTS *cp-requirements*
|
||||||
|
|
||||||
|
- Neovim 0.10.0+
|
||||||
|
- uv package manager (https://docs.astral.sh/uv/)
|
||||||
|
- C++ compiler (g++/clang++)
|
||||||
|
|
||||||
|
Optional:
|
||||||
|
- LuaSnip for template expansion (https://github.com/L3MON4D3/LuaSnip)
|
||||||
|
|
||||||
|
COMMANDS *cp-commands*
|
||||||
|
|
||||||
|
*:CP*
|
||||||
|
:CP {contest} Set up contest environment for {contest}.
|
||||||
|
Available contests: atcoder, codeforces, cses
|
||||||
|
|
||||||
|
:CP {contest} {problem} Set up problem from {contest}. Scrapes test
|
||||||
|
cases and creates source file.
|
||||||
|
|
||||||
|
:CP {contest} {problem} {letter}
|
||||||
|
For AtCoder/Codeforces: set up problem with
|
||||||
|
specific letter (e.g. a, b, c)
|
||||||
|
|
||||||
|
:CP {problem} Set up {problem} in current contest mode.
|
||||||
|
Requires contest to be set first.
|
||||||
|
|
||||||
|
:CP run Compile and run current problem with test input.
|
||||||
|
Shows execution time and output comparison.
|
||||||
|
|
||||||
|
:CP debug Compile with debug flags and run current problem.
|
||||||
|
Includes sanitizers and debug symbols.
|
||||||
|
|
||||||
|
:CP diff Enter diff mode to compare actual vs expected
|
||||||
|
output. Run again to exit diff mode.
|
||||||
|
|
||||||
|
CONFIGURATION *cp-config*
|
||||||
|
|
||||||
|
cp.nvim is automatically lazy-loaded - no config/setup is required.
|
||||||
|
|
||||||
|
Provide extra options via a setup() function with your package manager. For
|
||||||
|
example, with lazy.nvim (https://github.com/folke/lazy.nvim):
|
||||||
|
|
||||||
|
{
|
||||||
|
'barrett-ruth/cp.nvim',
|
||||||
|
config = function()
|
||||||
|
local ls = require('luasnip')
|
||||||
|
local s = ls.snippet
|
||||||
|
|
||||||
|
require('cp').setup({
|
||||||
|
contests = {
|
||||||
|
default = {
|
||||||
|
cpp_version = 20,
|
||||||
|
compile_flags = { "-O2", "-DLOCAL", "-Wall", "-Wextra" },
|
||||||
|
debug_flags = { "-g3", "-fsanitize=address,undefined", "-DLOCAL" },
|
||||||
|
timeout_ms = 2000,
|
||||||
|
},
|
||||||
|
atcoder = {
|
||||||
|
cpp_version = 23,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
snippets = {
|
||||||
|
cses = {
|
||||||
|
s("cses", "#include <iostream>\nusing namespace std;\n\nint main() {\n\t$0\n}")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
hooks = {
|
||||||
|
before_run = function(problem_id)
|
||||||
|
vim.cmd.w()
|
||||||
|
vim.lsp.buf.format()
|
||||||
|
end,
|
||||||
|
before_debug = function(problem_id)
|
||||||
|
...
|
||||||
|
end
|
||||||
|
},
|
||||||
|
})
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
Configuration options:
|
||||||
|
|
||||||
|
contests Dictionary of contest configurations - each contest inherits from 'default'.
|
||||||
|
|
||||||
|
cpp_version c++ standard version (e.g. 20, 23)
|
||||||
|
compile_flags compiler flags for run builds
|
||||||
|
debug_flags compiler flags for debug builds
|
||||||
|
timeout_ms duration (ms) to run/debug before timeout
|
||||||
|
|
||||||
|
snippets LuaSnip snippets by contest type
|
||||||
|
|
||||||
|
hooks Functions called at specific events
|
||||||
|
before_run Called before :CP run
|
||||||
|
before_debug Called before :CP debug
|
||||||
|
|
||||||
|
WORKFLOW *cp-workflow*
|
||||||
|
|
||||||
|
1. Set up contest environment: >
|
||||||
|
:CP atcoder
|
||||||
|
<
|
||||||
|
2. Set up specific problem: >
|
||||||
|
:CP abc123 a
|
||||||
|
<
|
||||||
|
This creates abc123a.cc and scrapes test cases to io/abc123a.in and
|
||||||
|
io/abc123a.expected. Alternatively, run :CP atcoder abc123 a
|
||||||
|
|
||||||
|
3. Write, test, and view your solution output:
|
||||||
|
|
||||||
|
:CP run
|
||||||
|
<
|
||||||
|
Output appears in vertical split showing execution time, output, and
|
||||||
|
whether it matches expected output.
|
||||||
|
|
||||||
|
4. Debug if needed:
|
||||||
|
|
||||||
|
:CP debug
|
||||||
|
<
|
||||||
|
5. Compare actual vs. expected output visually: >
|
||||||
|
|
||||||
|
:CP diff
|
||||||
|
<
|
||||||
|
Enters 3-way diff mode with actual, expected, and input files.
|
||||||
|
|
||||||
|
FILE STRUCTURE *cp-files*
|
||||||
|
|
||||||
|
cp.nvim creates the following file structure upon setup:
|
||||||
|
|
||||||
|
problem.cc
|
||||||
|
build/*.{run,debug}
|
||||||
|
io/
|
||||||
|
problem.in
|
||||||
|
problem.out
|
||||||
|
problem.expected
|
||||||
|
|
||||||
|
SNIPPETS *cp-snippets*
|
||||||
|
|
||||||
|
cp.nvim integrates with LuaSnip for automatic template expansion. When you
|
||||||
|
open a new problem file, type the contest name and press <Tab> to expand.
|
||||||
|
|
||||||
|
Built-in snippets include basic C++ templates for each contest type.
|
||||||
|
Custom snippets can be added via configuration.
|
||||||
|
|
||||||
|
HEALTH CHECK *cp-health*
|
||||||
|
|
||||||
|
Run |:checkhealth| cp to verify your setup.
|
||||||
|
|
||||||
|
vim:tw=78:ts=8:ft=help:norl:
|
||||||
|
|
@ -23,7 +23,7 @@ end
|
||||||
|
|
||||||
local function check_python_env()
|
local function check_python_env()
|
||||||
local plugin_path = debug.getinfo(1, "S").source:sub(2)
|
local plugin_path = debug.getinfo(1, "S").source:sub(2)
|
||||||
plugin_path = vim.fn.fnamemodify(plugin_path, ":h:h:h:h")
|
plugin_path = vim.fn.fnamemodify(plugin_path, ":h:h:h")
|
||||||
local venv_dir = plugin_path .. "/.venv"
|
local venv_dir = plugin_path .. "/.venv"
|
||||||
|
|
||||||
if vim.fn.isdirectory(venv_dir) == 1 then
|
if vim.fn.isdirectory(venv_dir) == 1 then
|
||||||
|
|
@ -35,7 +35,7 @@ end
|
||||||
|
|
||||||
local function check_scrapers()
|
local function check_scrapers()
|
||||||
local plugin_path = debug.getinfo(1, "S").source:sub(2)
|
local plugin_path = debug.getinfo(1, "S").source:sub(2)
|
||||||
plugin_path = vim.fn.fnamemodify(plugin_path, ":h:h:h:h")
|
plugin_path = vim.fn.fnamemodify(plugin_path, ":h:h:h")
|
||||||
|
|
||||||
local scrapers = { "atcoder.py", "codeforces.py", "cses.py" }
|
local scrapers = { "atcoder.py", "codeforces.py", "cses.py" }
|
||||||
for _, scraper in ipairs(scrapers) do
|
for _, scraper in ipairs(scrapers) do
|
||||||
|
|
@ -59,24 +59,6 @@ local function check_luasnip()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function check_directories()
|
|
||||||
local cwd = vim.fn.getcwd()
|
|
||||||
local build_dir = cwd .. "/build"
|
|
||||||
local io_dir = cwd .. "/io"
|
|
||||||
|
|
||||||
if vim.fn.isdirectory(build_dir) == 1 then
|
|
||||||
vim.health.ok("Build directory exists: " .. build_dir)
|
|
||||||
else
|
|
||||||
vim.health.info("Build directory will be created when needed")
|
|
||||||
end
|
|
||||||
|
|
||||||
if vim.fn.isdirectory(io_dir) == 1 then
|
|
||||||
vim.health.ok("IO directory exists: " .. io_dir)
|
|
||||||
else
|
|
||||||
vim.health.info("IO directory will be created when needed")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function check_config()
|
local function check_config()
|
||||||
local cp = require("cp")
|
local cp = require("cp")
|
||||||
if cp.is_initialized() then
|
if cp.is_initialized() then
|
||||||
|
|
@ -100,7 +82,6 @@ function M.check()
|
||||||
check_python_env()
|
check_python_env()
|
||||||
check_scrapers()
|
check_scrapers()
|
||||||
check_luasnip()
|
check_luasnip()
|
||||||
check_directories()
|
|
||||||
check_config()
|
check_config()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -250,14 +250,20 @@ function M.handle_command(opts)
|
||||||
elseif cmd == "diff" then
|
elseif cmd == "diff" then
|
||||||
diff_problem()
|
diff_problem()
|
||||||
else
|
else
|
||||||
if vim.g.cp_contest then
|
local similar_contests = vim.tbl_filter(function(contest)
|
||||||
if (vim.g.cp_contest == "atcoder" or vim.g.cp_contest == "codeforces") and args[2] then
|
return contest:find(cmd:sub(1, 3), 1, true) == 1
|
||||||
setup_problem(cmd, args[2])
|
end, competition_types)
|
||||||
else
|
|
||||||
setup_problem(cmd)
|
if #similar_contests > 0 then
|
||||||
end
|
log(
|
||||||
|
("unknown contest type '%s'. Did you mean: %s"):format(cmd, table.concat(similar_contests, ", ")),
|
||||||
|
vim.log.levels.ERROR
|
||||||
|
)
|
||||||
else
|
else
|
||||||
log("no contest mode set. run :CP <contest> first or use full command", vim.log.levels.ERROR)
|
log(
|
||||||
|
("unknown contest type '%s'. Available: [%s]"):format(cmd, table.concat(competition_types, ", ")),
|
||||||
|
vim.log.levels.ERROR
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
19
readme.md
19
readme.md
|
|
@ -2,12 +2,11 @@
|
||||||
|
|
||||||
neovim plugin for competitive programming.
|
neovim plugin for competitive programming.
|
||||||
|
|
||||||
> NOTE: sample test data from [codeforces](https://codeforces.com) is scraped via [cloudscraper](https://github.com/VeNoMouS/cloudscraper).
|
> Sample test data from [codeforces](https://codeforces.com) is scraped via [cloudscraper](https://github.com/VeNoMouS/cloudscraper). Use at your own risk.
|
||||||
> Use at your own risk.
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Support for multiple online judges (AtCoder, Codeforces, CSES)
|
- Support for multiple online judges ([AtCoder](https://atcoder.jp/), [Codeforces](https://codeforces.com/), [CSES](https://cses.fi))
|
||||||
- Automatic problem scraping and test case management
|
- Automatic problem scraping and test case management
|
||||||
- Integrated build, run, and debug commands
|
- Integrated build, run, and debug commands
|
||||||
- Diff mode for comparing output with expected results
|
- Diff mode for comparing output with expected results
|
||||||
|
|
@ -19,20 +18,6 @@ neovim plugin for competitive programming.
|
||||||
- [uv](https://docs.astral.sh/uv/): problem scraping (optional)
|
- [uv](https://docs.astral.sh/uv/): problem scraping (optional)
|
||||||
- [LuaSnip](https://github.com/L3MON4D3/LuaSnip): contest-specific snippets (optional)
|
- [LuaSnip](https://github.com/L3MON4D3/LuaSnip): contest-specific snippets (optional)
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
Using [lazy.nvim](https://github.com/folke/lazy.nvim):
|
|
||||||
|
|
||||||
```lua
|
|
||||||
{
|
|
||||||
"barrett-ruth/cp.nvim",
|
|
||||||
cmd = "CP",
|
|
||||||
dependencies = {
|
|
||||||
"L3MON4D3/LuaSnip",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
```vim
|
```vim
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue