commit
5e9c00014d
19 changed files with 185 additions and 438 deletions
|
|
@ -10,15 +10,13 @@ https://github.com/user-attachments/assets/50b19481-8e6d-47b4-bebc-15e16c61a9c9
|
||||||
|
|
||||||
- **Multi-platform support**: AtCoder, Codeforces, CSES with consistent interface
|
- **Multi-platform support**: AtCoder, Codeforces, CSES with consistent interface
|
||||||
- **Automatic problem setup**: Scrape test cases and metadata in seconds
|
- **Automatic problem setup**: Scrape test cases and metadata in seconds
|
||||||
- **Rich test output**: ANSI color support for compiler errors and program output
|
- **Rich test output**: 256 color ANSI support for compiler errors and program output
|
||||||
- **Language agnostic**: Works with any compiled language
|
- **Language agnostic**: Works with any language
|
||||||
- **Template integration**: Contest-specific snippets via LuaSnip
|
- **Diff viewer**: Compare expected vs actual output with 3 diff modes
|
||||||
- **Diff viewer**: Compare expected vs actual output with precision
|
|
||||||
|
|
||||||
## Optional Dependencies
|
## Optional Dependencies
|
||||||
|
|
||||||
- [uv](https://docs.astral.sh/uv/) for problem scraping
|
- [uv](https://docs.astral.sh/uv/) for problem scraping
|
||||||
- [LuaSnip](https://github.com/L3MON4D3/LuaSnip) for templates
|
|
||||||
- GNU [time](https://www.gnu.org/software/time/) and [timeout](https://www.gnu.org/software/coreutils/manual/html_node/timeout-invocation.html)
|
- GNU [time](https://www.gnu.org/software/time/) and [timeout](https://www.gnu.org/software/coreutils/manual/html_node/timeout-invocation.html)
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
|
||||||
|
|
@ -4,3 +4,4 @@ vim.opt_local.statuscolumn = ''
|
||||||
vim.opt_local.signcolumn = 'no'
|
vim.opt_local.signcolumn = 'no'
|
||||||
vim.opt_local.wrap = true
|
vim.opt_local.wrap = true
|
||||||
vim.opt_local.linebreak = true
|
vim.opt_local.linebreak = true
|
||||||
|
vim.opt_local.foldcolumn = '0'
|
||||||
|
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
vim.opt_local.number = false
|
|
||||||
vim.opt_local.relativenumber = false
|
|
||||||
vim.opt_local.statuscolumn = ''
|
|
||||||
vim.opt_local.signcolumn = 'no'
|
|
||||||
vim.opt_local.wrap = true
|
|
||||||
vim.opt_local.linebreak = true
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
vim.opt_local.number = false
|
|
||||||
vim.opt_local.relativenumber = false
|
|
||||||
vim.opt_local.statuscolumn = ''
|
|
||||||
vim.opt_local.signcolumn = 'no'
|
|
||||||
vim.opt_local.wrap = true
|
|
||||||
vim.opt_local.linebreak = true
|
|
||||||
vim.opt_local.modifiable = true
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
vim.opt_local.number = false
|
|
||||||
vim.opt_local.relativenumber = false
|
|
||||||
vim.opt_local.statuscolumn = ''
|
|
||||||
vim.opt_local.signcolumn = 'no'
|
|
||||||
vim.opt_local.wrap = true
|
|
||||||
vim.opt_local.linebreak = true
|
|
||||||
vim.opt_local.foldcolumn = '0'
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
if exists("b:current_syntax")
|
|
||||||
finish
|
|
||||||
endif
|
|
||||||
|
|
||||||
syntax match cpOutputCode /^\[code\]:/
|
|
||||||
syntax match cpOutputTime /^\[time\]:/
|
|
||||||
syntax match cpOutputDebug /^\[debug\]:/
|
|
||||||
syntax match cpOutputOkTrue /^\[ok\]:\ze true$/
|
|
||||||
syntax match cpOutputOkFalse /^\[ok\]:\ze false$/
|
|
||||||
|
|
||||||
highlight default link cpOutputCode DiagnosticInfo
|
|
||||||
highlight default link cpOutputTime Comment
|
|
||||||
highlight default link cpOutputDebug Comment
|
|
||||||
highlight default link cpOutputOkTrue DiffAdd
|
|
||||||
highlight default link cpOutputOkFalse DiffDelete
|
|
||||||
|
|
||||||
let b:current_syntax = "cp"
|
|
||||||
159
doc/cp.nvim.txt
159
doc/cp.nvim.txt
|
|
@ -16,10 +16,7 @@ REQUIREMENTS *cp-requirements*
|
||||||
|
|
||||||
- Neovim 0.10.0+
|
- Neovim 0.10.0+
|
||||||
- Unix-like operating system
|
- Unix-like operating system
|
||||||
|
|
||||||
Optional:
|
|
||||||
- uv package manager (https://docs.astral.sh/uv/)
|
- uv package manager (https://docs.astral.sh/uv/)
|
||||||
- LuaSnip for template expansion (https://github.com/L3MON4D3/LuaSnip)
|
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
COMMANDS *cp-commands*
|
COMMANDS *cp-commands*
|
||||||
|
|
@ -51,11 +48,13 @@ COMMANDS *cp-commands*
|
||||||
:CP codeforces 1951
|
:CP codeforces 1951
|
||||||
<
|
<
|
||||||
Action Commands ~
|
Action Commands ~
|
||||||
:CP run [--debug] Toggle run panel for individual test case
|
:CP run Toggle run panel for individual test cases.
|
||||||
debugging. Shows per-test results with redesigned
|
Shows per-test results with redesigned
|
||||||
layout for efficient comparison.
|
layout for efficient comparison.
|
||||||
Use --debug flag to compile with debug flags.
|
|
||||||
Requires contest setup first.
|
:CP debug
|
||||||
|
Same as above but with the debug mode configured
|
||||||
|
settings.
|
||||||
|
|
||||||
:CP pick Launch configured picker for interactive
|
:CP pick Launch configured picker for interactive
|
||||||
platform/contest selection.
|
platform/contest selection.
|
||||||
|
|
@ -65,10 +64,12 @@ COMMANDS *cp-commands*
|
||||||
Stops at last problem (no wrapping).
|
Stops at last problem (no wrapping).
|
||||||
|
|
||||||
|
|
||||||
Navigation Commands ~
|
|
||||||
:CP prev Navigate to previous problem in current contest.
|
:CP prev Navigate to previous problem in current contest.
|
||||||
Stops at first problem (no wrapping).
|
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.
|
||||||
|
|
||||||
Cache Commands ~
|
Cache Commands ~
|
||||||
:CP cache clear [contest]
|
:CP cache clear [contest]
|
||||||
Clear the cache data (contest list, problem
|
Clear the cache data (contest list, problem
|
||||||
|
|
@ -79,20 +80,6 @@ COMMANDS *cp-commands*
|
||||||
View the cache in a pretty-printed lua buffer.
|
View the cache in a pretty-printed lua buffer.
|
||||||
Exit with q.
|
Exit with q.
|
||||||
|
|
||||||
Command Flags ~
|
|
||||||
*cp-flags*
|
|
||||||
Flags can be used with setup and action commands:
|
|
||||||
|
|
||||||
--debug Use the debug command template.
|
|
||||||
For compiled languages, this selects
|
|
||||||
`commands.debug` (a debug *build*) instead of
|
|
||||||
`commands.build`. For interpreted languages,
|
|
||||||
this selects `commands.debug` in place of
|
|
||||||
`commands.run`.
|
|
||||||
Example: >
|
|
||||||
:CP run --debug
|
|
||||||
<
|
|
||||||
|
|
||||||
Template Variables ~
|
Template Variables ~
|
||||||
*cp-template-vars*
|
*cp-template-vars*
|
||||||
Command templates support variable substitution using `{variable}` syntax:
|
Command templates support variable substitution using `{variable}` syntax:
|
||||||
|
|
@ -136,7 +123,6 @@ Here's an example configuration with lazy.nvim: >lua
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
platforms = {
|
platforms = {
|
||||||
cses = {
|
cses = {
|
||||||
enabled_languages = { 'cpp', 'python' },
|
enabled_languages = { 'cpp', 'python' },
|
||||||
|
|
@ -154,10 +140,7 @@ Here's an example configuration with lazy.nvim: >lua
|
||||||
default_language = 'cpp',
|
default_language = 'cpp',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
snippets = {},
|
|
||||||
debug = false,
|
debug = false,
|
||||||
|
|
||||||
ui = {
|
ui = {
|
||||||
run_panel = {
|
run_panel = {
|
||||||
ansi = true,
|
ansi = true,
|
||||||
|
|
@ -223,7 +206,6 @@ run CSES problems with Rust using the single schema:
|
||||||
{platforms} (table<string,|CpPlatform|>) Per-platform enablement,
|
{platforms} (table<string,|CpPlatform|>) Per-platform enablement,
|
||||||
default language, and optional overrides.
|
default language, and optional overrides.
|
||||||
{hooks} (|cp.Hooks|) Hook functions called at various stages.
|
{hooks} (|cp.Hooks|) Hook functions called at various stages.
|
||||||
{snippets} (table[]) LuaSnip snippet definitions.
|
|
||||||
{debug} (boolean, default: false) Show info messages.
|
{debug} (boolean, default: false) Show info messages.
|
||||||
{scrapers} (string[]) Supported platform ids.
|
{scrapers} (string[]) Supported platform ids.
|
||||||
{filename} (function, optional)
|
{filename} (function, optional)
|
||||||
|
|
@ -359,22 +341,18 @@ Example: Setting up and solving AtCoder contest ABC324
|
||||||
< Navigate with j/k, run specific tests with <enter>
|
< Navigate with j/k, run specific tests with <enter>
|
||||||
Exit test panel with q or :CP run when done
|
Exit test panel with q or :CP run when done
|
||||||
|
|
||||||
5. If needed, debug with sanitizers: >
|
5. Move to next problem: >
|
||||||
:CP run --debug
|
|
||||||
<
|
|
||||||
|
|
||||||
6. Move to next problem: >
|
|
||||||
:CP next
|
:CP next
|
||||||
< This automatically sets up problem B
|
< This automatically sets up problem B
|
||||||
|
|
||||||
7. Continue solving problems with :CP next/:CP prev navigation
|
6. Continue solving problems with :CP next/:CP prev navigation
|
||||||
|
|
||||||
8. Switch to another file (e.g. previous contest): >
|
7. Switch to another file (e.g. previous contest): >
|
||||||
:e ~/contests/abc323/a.cpp
|
:e ~/contests/abc323/a.cpp
|
||||||
:CP
|
:CP
|
||||||
< Automatically restores abc323 contest context
|
< Automatically restores abc323 contest context
|
||||||
|
|
||||||
9. Submit solutions on AtCoder website
|
8. Submit solutions on AtCoder website
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
PICKER INTEGRATION *cp-picker*
|
PICKER INTEGRATION *cp-picker*
|
||||||
|
|
@ -396,18 +374,9 @@ PICKER KEYMAPS *cp-picker-keys*
|
||||||
==============================================================================
|
==============================================================================
|
||||||
RUN PANEL *cp-run*
|
RUN PANEL *cp-run*
|
||||||
|
|
||||||
The run panel provides individual test case debugging with a streamlined
|
The run panel provides individual test case debugging. Problem time/memory
|
||||||
layout optimized for modern screens. Shows test status with competitive
|
limit constraints are in columns Time/Mem respectively. Used time/memory are
|
||||||
programming terminology and efficient space usage.
|
in columns Runtime/RSS respectively.
|
||||||
|
|
||||||
Activation ~
|
|
||||||
*:CP-run*
|
|
||||||
:CP run [--debug] Toggle run panel on/off. When activated,
|
|
||||||
replaces current layout with test interface.
|
|
||||||
Automatically compiles and runs all tests.
|
|
||||||
Use --debug flag to compile with debug symbols
|
|
||||||
and sanitizers. Toggle again to restore original
|
|
||||||
layout.
|
|
||||||
|
|
||||||
Interface ~
|
Interface ~
|
||||||
|
|
||||||
|
|
@ -441,7 +410,7 @@ Test cases use competitive programming terminology with color highlighting:
|
||||||
TLE Time Limit Exceeded (timeout)
|
TLE Time Limit Exceeded (timeout)
|
||||||
MLE Memory Limit Exceeded Error (heuristic)
|
MLE Memory Limit Exceeded Error (heuristic)
|
||||||
RTE Runtime Error (other non-zero exit code)
|
RTE Runtime Error (other non-zero exit code)
|
||||||
NA Any other state (undecipherable, error, running)
|
NA Any other state
|
||||||
<
|
<
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
|
|
@ -450,13 +419,28 @@ ANSI COLORS AND HIGHLIGHTING *cp-ansi*
|
||||||
cp.nvim provides comprehensive ANSI color support and highlighting for
|
cp.nvim provides comprehensive ANSI color support and highlighting for
|
||||||
compiler output, program stderr, and diff visualization.
|
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:
|
||||||
|
|
||||||
|
{
|
||||||
|
commands = {
|
||||||
|
build = {
|
||||||
|
'g++',
|
||||||
|
'-fdiagnostics-color=always',
|
||||||
|
...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
HIGHLIGHT GROUPS *cp-highlights*
|
HIGHLIGHT GROUPS *cp-highlights*
|
||||||
|
|
||||||
Test Status Groups ~
|
Test Status Groups ~
|
||||||
|
|
||||||
Test cases use competitive programming terminology with color highlighting:
|
|
||||||
|
|
||||||
CpTestAC Green foreground for AC status
|
CpTestAC Green foreground for AC status
|
||||||
CpTestWA Red foreground for WA status
|
CpTestWA Red foreground for WA status
|
||||||
CpTestTLE Orange foreground for TLE status
|
CpTestTLE Orange foreground for TLE status
|
||||||
|
|
@ -470,56 +454,21 @@ cp.nvim preserves ANSI colors from compiler output and program stderr using
|
||||||
a sophisticated parsing system. Colors are automatically mapped to your
|
a sophisticated parsing system. Colors are automatically mapped to your
|
||||||
terminal colorscheme via vim.g.terminal_color_* variables.
|
terminal colorscheme via vim.g.terminal_color_* variables.
|
||||||
|
|
||||||
Basic formatting groups:
|
|
||||||
CpAnsiBold Bold text formatting
|
|
||||||
CpAnsiItalic Italic text formatting
|
|
||||||
CpAnsiBoldItalic Combined bold and italic formatting
|
|
||||||
|
|
||||||
Standard terminal colors (each supports Bold, Italic, BoldItalic variants):
|
|
||||||
CpAnsiRed Standard red (terminal_color_1)
|
|
||||||
CpAnsiGreen Standard green (terminal_color_2)
|
|
||||||
CpAnsiYellow Standard yellow (terminal_color_3)
|
|
||||||
CpAnsiBlue Standard blue (terminal_color_4)
|
|
||||||
CpAnsiMagenta Standard magenta (terminal_color_5)
|
|
||||||
CpAnsiCyan Standard cyan (terminal_color_6)
|
|
||||||
CpAnsiWhite Standard white (terminal_color_7)
|
|
||||||
CpAnsiBlack Standard black (terminal_color_0)
|
|
||||||
|
|
||||||
Bright color variants:
|
|
||||||
CpAnsiBrightRed Bright red (terminal_color_9)
|
|
||||||
CpAnsiBrightGreen Bright green (terminal_color_10)
|
|
||||||
CpAnsiBrightYellow Bright yellow (terminal_color_11)
|
|
||||||
CpAnsiBrightBlue Bright blue (terminal_color_12)
|
|
||||||
CpAnsiBrightMagenta Bright magenta (terminal_color_13)
|
|
||||||
CpAnsiBrightCyan Bright cyan (terminal_color_14)
|
|
||||||
CpAnsiBrightWhite Bright white (terminal_color_15)
|
|
||||||
CpAnsiBrightBlack Bright black (terminal_color_8)
|
|
||||||
|
|
||||||
Example combinations:
|
|
||||||
CpAnsiBoldRed Bold red combination
|
|
||||||
CpAnsiItalicGreen Italic green combination
|
|
||||||
CpAnsiBoldItalicYellow Bold italic yellow combination
|
|
||||||
|
|
||||||
Diff Highlighting ~
|
Diff Highlighting ~
|
||||||
|
|
||||||
Diff visualization uses Neovim's built-in highlight groups that automatically
|
The git diff backend uses Neovim's built-in highlight groups that automatically
|
||||||
adapt to your colorscheme:
|
adapt to your colorscheme:
|
||||||
|
|
||||||
DiffAdd Highlights added text in git diffs
|
DiffAdd Highlights added text in git diffs
|
||||||
DiffDelete Highlights removed text in git diffs
|
DiffDelete Highlights removed text in git diffs
|
||||||
|
|
||||||
These groups are automatically used by the git diff backend for character-level
|
|
||||||
difference visualization with optimal colorscheme integration.
|
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
TERMINAL COLOR INTEGRATION *cp-terminal-colors*
|
TERMINAL COLOR INTEGRATION *cp-terminal-colors*
|
||||||
|
|
||||||
ANSI colors automatically use your terminal's color palette through Neovim's
|
ANSI colors automatically use the terminal's color palette through Neovim's
|
||||||
vim.g.terminal_color_* variables. This ensures compiler colors match your
|
vim.g.terminal_color_* variables.
|
||||||
colorscheme without manual configuration.
|
|
||||||
|
|
||||||
If your colorscheme doesn't set terminal colors, cp.nvim will warn you and
|
If your colorscheme doesn't set terminal colors, set them like so: >vim
|
||||||
ANSI colors won't display properly - set them like so: >vim
|
|
||||||
let g:terminal_color_1 = '#ff6b6b'
|
let g:terminal_color_1 = '#ff6b6b'
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
@ -550,6 +499,7 @@ prevent them from being overridden: >lua
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
RUN PANEL KEYMAPS *cp-test-keys*
|
RUN PANEL KEYMAPS *cp-test-keys*
|
||||||
|
|
||||||
<c-n> Navigate to next test case
|
<c-n> Navigate to next test case
|
||||||
<c-p> Navigate to previous test case
|
<c-p> Navigate to previous test case
|
||||||
t Cycle through diff modes: none → git → vim
|
t Cycle through diff modes: none → git → vim
|
||||||
|
|
@ -558,27 +508,27 @@ q Exit run panel and restore layout
|
||||||
|
|
||||||
Diff Modes ~
|
Diff Modes ~
|
||||||
|
|
||||||
Two diff backends are available:
|
Three diff backends are available:
|
||||||
|
|
||||||
|
none Nothing
|
||||||
vim Built-in vim diff (default, always available)
|
vim Built-in vim diff (default, always available)
|
||||||
git Character-level git word-diff (requires git, more precise)
|
git Character-level git word-diff (requires git, more precise)
|
||||||
|
|
||||||
The git backend shows character-level changes with [-removed-] and {+added+}
|
The git backend shows character-level changes with [-removed-] and {+added+}
|
||||||
markers for precise difference analysis.
|
markers.
|
||||||
|
|
||||||
Execution Details ~
|
Execution Details ~
|
||||||
|
|
||||||
Test cases are executed individually using the same compilation and
|
Test cases are executed individually using the same compilation and
|
||||||
execution pipeline, but with isolated input/output for
|
execution pipeline, but with isolated input/output for
|
||||||
precise failure analysis. All tests are automatically run when the
|
precise failure analysis.
|
||||||
panel opens.
|
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
FILE STRUCTURE *cp-files*
|
FILE STRUCTURE *cp-files*
|
||||||
|
|
||||||
cp.nvim creates the following file structure upon problem setup: >
|
cp.nvim creates the following file structure upon problem setup: >
|
||||||
|
|
||||||
{problem_id}.{ext} " Source file (e.g. a.cc, b.py)
|
{problem_id}.{ext} " Source file
|
||||||
build/
|
build/
|
||||||
{problem_id}.run " Compiled binary
|
{problem_id}.run " Compiled binary
|
||||||
io/
|
io/
|
||||||
|
|
@ -586,27 +536,6 @@ cp.nvim creates the following file structure upon problem setup: >
|
||||||
{problem_id}.n.cpout " nth program output
|
{problem_id}.n.cpout " nth program output
|
||||||
{problem_id}.expected " Expected output
|
{problem_id}.expected " Expected output
|
||||||
<
|
<
|
||||||
==============================================================================
|
|
||||||
SNIPPETS *cp-snippets*
|
|
||||||
|
|
||||||
cp.nvim integrates with LuaSnip for automatic template expansion. Built-in
|
|
||||||
snippets include basic C++ and Python templates for each contest type.
|
|
||||||
|
|
||||||
Snippet trigger names must match the following format exactly: >
|
|
||||||
|
|
||||||
cp.nvim/{platform}.{language}
|
|
||||||
<
|
|
||||||
Where {platform} is the contest platform (atcoder, codeforces, cses) and
|
|
||||||
{language} is the programming language (cpp, python).
|
|
||||||
|
|
||||||
Examples: >
|
|
||||||
cp.nvim/atcoder.cpp
|
|
||||||
cp.nvim/codeforces.python
|
|
||||||
cp.nvim/cses.cpp
|
|
||||||
<
|
|
||||||
|
|
||||||
Custom snippets can be added via the `snippets` configuration field.
|
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
HEALTH CHECK *cp-health*
|
HEALTH CHECK *cp-health*
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
vim.filetype.add({
|
|
||||||
extension = {
|
|
||||||
cpin = 'cpin',
|
|
||||||
cpout = 'cpout',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
@ -10,11 +10,11 @@ local actions = constants.ACTIONS
|
||||||
---@class ParsedCommand
|
---@class ParsedCommand
|
||||||
---@field type string
|
---@field type string
|
||||||
---@field error string?
|
---@field error string?
|
||||||
---@field debug? boolean
|
|
||||||
---@field action? string
|
---@field action? string
|
||||||
---@field message? string
|
---@field message? string
|
||||||
---@field contest? string
|
---@field contest? string
|
||||||
---@field platform? string
|
---@field platform? string
|
||||||
|
---@field problem_id? string
|
||||||
|
|
||||||
--- Turn raw args into normalized structure to later dispatch
|
--- Turn raw args into normalized structure to later dispatch
|
||||||
---@param args string[] The raw command-line mode args
|
---@param args string[] The raw command-line mode args
|
||||||
|
|
@ -26,22 +26,16 @@ local function parse_command(args)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
local debug = vim.tbl_contains(args, '--debug')
|
local first = args[1]
|
||||||
|
|
||||||
local filtered_args = vim.tbl_filter(function(arg)
|
|
||||||
return arg ~= '--debug'
|
|
||||||
end, args)
|
|
||||||
|
|
||||||
local first = filtered_args[1]
|
|
||||||
|
|
||||||
if vim.tbl_contains(actions, first) then
|
if vim.tbl_contains(actions, first) then
|
||||||
if first == 'cache' then
|
if first == 'cache' then
|
||||||
local subcommand = filtered_args[2]
|
local subcommand = args[2]
|
||||||
if not subcommand then
|
if not subcommand then
|
||||||
return { type = 'error', message = 'cache command requires subcommand: clear' }
|
return { type = 'error', message = 'cache command requires subcommand: clear' }
|
||||||
end
|
end
|
||||||
if vim.tbl_contains({ 'clear', 'read' }, subcommand) then
|
if vim.tbl_contains({ 'clear', 'read' }, subcommand) then
|
||||||
local platform = filtered_args[3]
|
local platform = args[3]
|
||||||
return {
|
return {
|
||||||
type = 'cache',
|
type = 'cache',
|
||||||
subcommand = subcommand,
|
subcommand = subcommand,
|
||||||
|
|
@ -51,42 +45,40 @@ local function parse_command(args)
|
||||||
return { type = 'error', message = 'unknown cache subcommand: ' .. subcommand }
|
return { type = 'error', message = 'unknown cache subcommand: ' .. subcommand }
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
return { type = 'action', action = first, debug = debug }
|
return { type = 'action', action = first }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if vim.tbl_contains(platforms, first) then
|
if vim.tbl_contains(platforms, first) then
|
||||||
if #filtered_args == 1 then
|
if #args == 1 then
|
||||||
return {
|
return {
|
||||||
type = 'error',
|
type = 'error',
|
||||||
message = 'Too few arguments - specify a contest.',
|
message = 'Too few arguments - specify a contest.',
|
||||||
}
|
}
|
||||||
elseif #filtered_args == 2 then
|
elseif #args == 2 then
|
||||||
return {
|
return {
|
||||||
type = 'contest_setup',
|
type = 'contest_setup',
|
||||||
platform = first,
|
platform = first,
|
||||||
contest = filtered_args[2],
|
contest = args[2],
|
||||||
}
|
}
|
||||||
elseif #filtered_args == 3 then
|
elseif #args == 3 then
|
||||||
return {
|
return {
|
||||||
type = 'error',
|
type = 'error',
|
||||||
message = 'Setup contests with :CP <platform> <contest_id>',
|
message = 'Setup contests with :CP <platform> <contest_id>.',
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return { type = 'error', message = 'Too many arguments' }
|
return { type = 'error', message = 'Too many arguments' }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if state.get_platform() and state.get_contest_id() then
|
if #args == 1 then
|
||||||
local cache = require('cp.cache')
|
|
||||||
cache.load()
|
|
||||||
return {
|
return {
|
||||||
type = 'error',
|
type = 'problem_jump',
|
||||||
message = ("invalid subcommand '%s'"):format(first),
|
problem_id = first,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
return { type = 'error', message = 'Unknown command or no contest context' }
|
return { type = 'error', message = 'Unknown command or no contest context.' }
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Core logic for handling `:CP ...` commands
|
--- Core logic for handling `:CP ...` commands
|
||||||
|
|
@ -109,7 +101,9 @@ function M.handle_command(opts)
|
||||||
if cmd.action == 'interact' then
|
if cmd.action == 'interact' then
|
||||||
ui.toggle_interactive()
|
ui.toggle_interactive()
|
||||||
elseif cmd.action == 'run' then
|
elseif cmd.action == 'run' then
|
||||||
ui.toggle_run_panel(cmd.debug)
|
ui.toggle_run_panel()
|
||||||
|
elseif cmd.action == 'debug' then
|
||||||
|
ui.toggle_run_panel({ debug = true })
|
||||||
elseif cmd.action == 'next' then
|
elseif cmd.action == 'next' then
|
||||||
setup.navigate_problem(1)
|
setup.navigate_problem(1)
|
||||||
elseif cmd.action == 'prev' then
|
elseif cmd.action == 'prev' then
|
||||||
|
|
@ -118,6 +112,30 @@ function M.handle_command(opts)
|
||||||
local picker = require('cp.commands.picker')
|
local picker = require('cp.commands.picker')
|
||||||
picker.handle_pick_action()
|
picker.handle_pick_action()
|
||||||
end
|
end
|
||||||
|
elseif cmd.type == 'problem_jump' then
|
||||||
|
local platform = state.get_platform()
|
||||||
|
local contest_id = state.get_contest_id()
|
||||||
|
local problem_id = cmd.problem_id
|
||||||
|
|
||||||
|
if not (platform and contest_id) then
|
||||||
|
logger.log('No contest is currently active.', vim.log.levels.ERROR)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local cache = require('cp.cache')
|
||||||
|
cache.load()
|
||||||
|
local contest_data = cache.get_contest_data(platform, contest_id)
|
||||||
|
|
||||||
|
if not (contest_data and contest_data.index_map and contest_data.index_map[problem_id]) then
|
||||||
|
logger.log(
|
||||||
|
("%s contest '%s' has no problem '%s'."):format(platform, contest_id, problem_id),
|
||||||
|
vim.log.levels.ERROR
|
||||||
|
)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local setup = require('cp.setup')
|
||||||
|
setup.setup_contest(platform, contest_id, problem_id)
|
||||||
elseif cmd.type == 'cache' then
|
elseif cmd.type == 'cache' then
|
||||||
local cache_commands = require('cp.commands.cache')
|
local cache_commands = require('cp.commands.cache')
|
||||||
cache_commands.handle_cache_command(cmd)
|
cache_commands.handle_cache_command(cmd)
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,6 @@
|
||||||
---@field languages table<string, CpLanguage>
|
---@field languages table<string, CpLanguage>
|
||||||
---@field platforms table<string, CpPlatform>
|
---@field platforms table<string, CpPlatform>
|
||||||
---@field hooks Hooks
|
---@field hooks Hooks
|
||||||
---@field snippets any[]
|
|
||||||
---@field debug boolean
|
---@field debug boolean
|
||||||
---@field scrapers string[]
|
---@field scrapers string[]
|
||||||
---@field filename? fun(contest: string, contest_id: string, problem_id?: string, config: cp.Config, language?: string): string
|
---@field filename? fun(contest: string, contest_id: string, problem_id?: string, config: cp.Config, language?: string): string
|
||||||
|
|
@ -100,7 +99,6 @@ M.defaults = {
|
||||||
default_language = 'cpp',
|
default_language = 'cpp',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
snippets = {},
|
|
||||||
hooks = { before_run = nil, before_debug = nil, setup_code = nil },
|
hooks = { before_run = nil, before_debug = nil, setup_code = nil },
|
||||||
debug = false,
|
debug = false,
|
||||||
scrapers = constants.PLATFORMS,
|
scrapers = constants.PLATFORMS,
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ local M = {}
|
||||||
|
|
||||||
local utils = require('cp.utils')
|
local utils = require('cp.utils')
|
||||||
|
|
||||||
local function check_required()
|
local function check()
|
||||||
vim.health.start('cp.nvim [required] ~')
|
vim.health.start('cp.nvim [required] ~')
|
||||||
|
|
||||||
if vim.fn.has('nvim-0.10.0') == 1 then
|
if vim.fn.has('nvim-0.10.0') == 1 then
|
||||||
|
|
@ -49,24 +49,12 @@ local function check_required()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function check_optional()
|
|
||||||
vim.health.start('cp.nvim [optional] ~')
|
|
||||||
|
|
||||||
local has_luasnip = pcall(require, 'luasnip')
|
|
||||||
if has_luasnip then
|
|
||||||
vim.health.ok('LuaSnip integration available')
|
|
||||||
else
|
|
||||||
vim.health.info('LuaSnip not available (templates optional)')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.check()
|
function M.check()
|
||||||
local version = require('cp.version')
|
local version = require('cp.version')
|
||||||
vim.health.start('cp.nvim health check ~')
|
vim.health.start('cp.nvim health check ~')
|
||||||
vim.health.info('Version: ' .. version.version)
|
vim.health.info('Version: ' .. version.version)
|
||||||
|
|
||||||
check_required()
|
check()
|
||||||
check_optional()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ local M = {}
|
||||||
|
|
||||||
local config_module = require('cp.config')
|
local config_module = require('cp.config')
|
||||||
local logger = require('cp.log')
|
local logger = require('cp.log')
|
||||||
local snippets = require('cp.snippets')
|
|
||||||
|
|
||||||
if vim.fn.has('nvim-0.10.0') == 0 then
|
if vim.fn.has('nvim-0.10.0') == 0 then
|
||||||
logger.log('Requires nvim-0.10.0+', vim.log.levels.ERROR)
|
logger.log('Requires nvim-0.10.0+', vim.log.levels.ERROR)
|
||||||
|
|
@ -11,7 +10,6 @@ end
|
||||||
|
|
||||||
local user_config = {}
|
local user_config = {}
|
||||||
local config = nil
|
local config = nil
|
||||||
local snippets_initialized = false
|
|
||||||
local initialized = false
|
local initialized = false
|
||||||
|
|
||||||
--- Root handler for all `:CP ...` commands
|
--- Root handler for all `:CP ...` commands
|
||||||
|
|
@ -27,10 +25,6 @@ function M.setup(opts)
|
||||||
config = config_module.setup(user_config)
|
config = config_module.setup(user_config)
|
||||||
config_module.set_current_config(config)
|
config_module.set_current_config(config)
|
||||||
|
|
||||||
if not snippets_initialized then
|
|
||||||
snippets.setup(config)
|
|
||||||
snippets_initialized = true
|
|
||||||
end
|
|
||||||
initialized = true
|
initialized = true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -107,26 +107,6 @@ function M.setup_problem(problem_id, language)
|
||||||
local lang = language or config.platforms[platform].default_language
|
local lang = language or config.platforms[platform].default_language
|
||||||
local source_file = state.get_source_file(lang)
|
local source_file = state.get_source_file(lang)
|
||||||
vim.cmd.e(source_file)
|
vim.cmd.e(source_file)
|
||||||
local source_buf = vim.api.nvim_get_current_buf()
|
|
||||||
|
|
||||||
if vim.api.nvim_buf_get_lines(source_buf, 0, -1, true)[1] == '' then
|
|
||||||
local ok, luasnip = pcall(require, 'luasnip')
|
|
||||||
if ok then
|
|
||||||
local trigger = ('cp.nvim/%s.%s'):format(platform, lang)
|
|
||||||
vim.api.nvim_buf_set_lines(0, 0, -1, false, { trigger })
|
|
||||||
vim.api.nvim_win_set_cursor(0, { 1, #trigger })
|
|
||||||
vim.cmd.startinsert({ bang = true })
|
|
||||||
vim.schedule(function()
|
|
||||||
if luasnip.expandable() then
|
|
||||||
luasnip.expand()
|
|
||||||
else
|
|
||||||
vim.api.nvim_buf_set_lines(0, 0, 1, false, { '' })
|
|
||||||
vim.api.nvim_win_set_cursor(0, { 1, 0 })
|
|
||||||
end
|
|
||||||
vim.cmd.stopinsert()
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if config.hooks and config.hooks.setup_code then
|
if config.hooks and config.hooks.setup_code then
|
||||||
config.hooks.setup_code(state)
|
config.hooks.setup_code(state)
|
||||||
|
|
|
||||||
|
|
@ -1,134 +0,0 @@
|
||||||
local M = {}
|
|
||||||
local logger = require('cp.log')
|
|
||||||
|
|
||||||
function M.setup(config)
|
|
||||||
local ok, ls = pcall(require, 'luasnip')
|
|
||||||
if not ok then
|
|
||||||
logger.log('LuaSnip not available - snippets are disabled.', vim.log.levels.INFO, true)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local s, i, fmt = ls.snippet, ls.insert_node, require('luasnip.extras.fmt').fmt
|
|
||||||
|
|
||||||
local constants = require('cp.constants')
|
|
||||||
local filetype_to_language = constants.filetype_to_language
|
|
||||||
|
|
||||||
local language_to_filetype = {}
|
|
||||||
for ext, lang in pairs(filetype_to_language) do
|
|
||||||
if not language_to_filetype[lang] then
|
|
||||||
language_to_filetype[lang] = ext
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local template_definitions = {
|
|
||||||
cpp = {
|
|
||||||
codeforces = [[#include <bits/stdc++.h>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
void solve() {{
|
|
||||||
{}
|
|
||||||
}}
|
|
||||||
|
|
||||||
int main() {{
|
|
||||||
std::cin.tie(nullptr)->sync_with_stdio(false);
|
|
||||||
|
|
||||||
int tc = 1;
|
|
||||||
std::cin >> tc;
|
|
||||||
|
|
||||||
for (int t = 0; t < tc; ++t) {{
|
|
||||||
solve();
|
|
||||||
}}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}}]],
|
|
||||||
|
|
||||||
atcoder = [[#include <bits/stdc++.h>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
void solve() {{
|
|
||||||
{}
|
|
||||||
}}
|
|
||||||
|
|
||||||
int main() {{
|
|
||||||
std::cin.tie(nullptr)->sync_with_stdio(false);
|
|
||||||
|
|
||||||
#ifdef LOCAL
|
|
||||||
int tc;
|
|
||||||
std::cin >> tc;
|
|
||||||
|
|
||||||
for (int t = 0; t < tc; ++t) {{
|
|
||||||
solve();
|
|
||||||
}}
|
|
||||||
#else
|
|
||||||
solve();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}}]],
|
|
||||||
|
|
||||||
cses = [[#include <bits/stdc++.h>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
int main() {{
|
|
||||||
std::cin.tie(nullptr)->sync_with_stdio(false);
|
|
||||||
|
|
||||||
{}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}}]],
|
|
||||||
},
|
|
||||||
|
|
||||||
python = {
|
|
||||||
codeforces = [[def solve():
|
|
||||||
{}
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
tc = int(input())
|
|
||||||
for _ in range(tc):
|
|
||||||
solve()]],
|
|
||||||
|
|
||||||
atcoder = [[def solve():
|
|
||||||
{}
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
solve()]],
|
|
||||||
|
|
||||||
cses = [[def solve():
|
|
||||||
{}
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
solve()]],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
local user_overrides = {}
|
|
||||||
for _, snippet in ipairs(config.snippets or {}) do
|
|
||||||
user_overrides[snippet.trigger:lower()] = snippet
|
|
||||||
end
|
|
||||||
|
|
||||||
for language, template_set in pairs(template_definitions) do
|
|
||||||
local snippets = {}
|
|
||||||
local filetype = constants.canonical_filetypes[language]
|
|
||||||
|
|
||||||
for contest, template in pairs(template_set) do
|
|
||||||
local prefixed_trigger = ('cp.nvim/%s.%s'):format(contest:lower(), language)
|
|
||||||
if not user_overrides[prefixed_trigger:lower()] then
|
|
||||||
table.insert(snippets, s(prefixed_trigger, fmt(template, { i(1) })))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
for trigger, snippet in pairs(user_overrides) do
|
|
||||||
local prefix_match = trigger:lower():match('^cp%.nvim/[^.]+%.(.+)$')
|
|
||||||
if prefix_match == language then
|
|
||||||
table.insert(snippets, snippet)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
ls.add_snippets(filetype, snippets)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return M
|
|
||||||
|
|
@ -65,6 +65,10 @@ function M.get_base_name()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function M.get_language()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
function M.get_source_file(language)
|
function M.get_source_file(language)
|
||||||
local base_name = M.get_base_name()
|
local base_name = M.get_base_name()
|
||||||
if not base_name or not M.get_platform() then
|
if not base_name or not M.get_platform() then
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,46 @@
|
||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
local logger = require('cp.log')
|
|
||||||
|
|
||||||
local dyn_hl_cache = {}
|
local dyn_hl_cache = {}
|
||||||
|
|
||||||
|
local ANSI_TERMINAL_COLOR_CODE_FALLBACK = {
|
||||||
|
[0] = '#000000',
|
||||||
|
[1] = '#800000',
|
||||||
|
[2] = '#008000',
|
||||||
|
[3] = '#808000',
|
||||||
|
[4] = '#000080',
|
||||||
|
[5] = '#800080',
|
||||||
|
[6] = '#008080',
|
||||||
|
[7] = '#c0c0c0',
|
||||||
|
[8] = '#808080',
|
||||||
|
[9] = '#ff0000',
|
||||||
|
[10] = '#00ff00',
|
||||||
|
[11] = '#ffff00',
|
||||||
|
[12] = '#0000ff',
|
||||||
|
[13] = '#ff00ff',
|
||||||
|
[14] = '#00ffff',
|
||||||
|
[15] = '#ffffff',
|
||||||
|
}
|
||||||
|
|
||||||
|
local function xterm_to_hex(n)
|
||||||
|
if n >= 0 and n <= 15 then
|
||||||
|
local key = 'terminal_color_' .. n
|
||||||
|
return vim.g[key] or ANSI_TERMINAL_COLOR_CODE_FALLBACK[n]
|
||||||
|
end
|
||||||
|
if n >= 16 and n <= 231 then
|
||||||
|
local c = n - 16
|
||||||
|
local r = math.floor(c / 36) % 6
|
||||||
|
local g = math.floor(c / 6) % 6
|
||||||
|
local b = c % 6
|
||||||
|
local function level(x)
|
||||||
|
return x == 0 and 0 or 55 + 40 * x
|
||||||
|
end
|
||||||
|
return ('#%02x%02x%02x'):format(level(r), level(g), level(b))
|
||||||
|
end
|
||||||
|
local l = 8 + 10 * (n - 232)
|
||||||
|
return ('#%02x%02x%02x'):format(l, l, l)
|
||||||
|
end
|
||||||
|
|
||||||
---@param s string|table
|
---@param s string|table
|
||||||
---@return string
|
---@return string
|
||||||
function M.bytes_to_string(s)
|
function M.bytes_to_string(s)
|
||||||
|
|
@ -40,24 +76,7 @@ local function ensure_hl_for(fg, bold, italic)
|
||||||
suffix = fg.name
|
suffix = fg.name
|
||||||
elseif fg and fg.kind == 'xterm' then
|
elseif fg and fg.kind == 'xterm' then
|
||||||
suffix = ('X%03d'):format(fg.idx)
|
suffix = ('X%03d'):format(fg.idx)
|
||||||
local function xterm_to_hex(n)
|
|
||||||
if n >= 0 and n <= 15 then
|
|
||||||
local key = 'terminal_color_' .. n
|
|
||||||
return vim.g[key]
|
|
||||||
end
|
|
||||||
if n >= 16 and n <= 231 then
|
|
||||||
local c = n - 16
|
|
||||||
local r = math.floor(c / 36) % 6
|
|
||||||
local g = math.floor(c / 6) % 6
|
|
||||||
local b = c % 6
|
|
||||||
local function level(x)
|
|
||||||
return x == 0 and 0 or 55 + 40 * x
|
|
||||||
end
|
|
||||||
return ('#%02x%02x%02x'):format(level(r), level(g), level(b))
|
|
||||||
end
|
|
||||||
local l = 8 + 10 * (n - 232)
|
|
||||||
return ('#%02x%02x%02x'):format(l, l, l)
|
|
||||||
end
|
|
||||||
opts.fg = xterm_to_hex(fg.idx) or 'NONE'
|
opts.fg = xterm_to_hex(fg.idx) or 'NONE'
|
||||||
elseif fg and fg.kind == 'rgb' then
|
elseif fg and fg.kind == 'rgb' then
|
||||||
suffix = ('Rgb%02x%02x%02x'):format(fg.r, fg.g, fg.b)
|
suffix = ('Rgb%02x%02x%02x'):format(fg.r, fg.g, fg.b)
|
||||||
|
|
@ -256,31 +275,24 @@ end
|
||||||
---@return nil
|
---@return nil
|
||||||
function M.setup_highlight_groups()
|
function M.setup_highlight_groups()
|
||||||
local color_map = {
|
local color_map = {
|
||||||
Black = vim.g.terminal_color_0,
|
Black = vim.g.terminal_color_0 or ANSI_TERMINAL_COLOR_CODE_FALLBACK[0],
|
||||||
Red = vim.g.terminal_color_1,
|
Red = vim.g.terminal_color_1 or ANSI_TERMINAL_COLOR_CODE_FALLBACK[1],
|
||||||
Green = vim.g.terminal_color_2,
|
Green = vim.g.terminal_color_2 or ANSI_TERMINAL_COLOR_CODE_FALLBACK[2],
|
||||||
Yellow = vim.g.terminal_color_3,
|
Yellow = vim.g.terminal_color_3 or ANSI_TERMINAL_COLOR_CODE_FALLBACK[3],
|
||||||
Blue = vim.g.terminal_color_4,
|
Blue = vim.g.terminal_color_4 or ANSI_TERMINAL_COLOR_CODE_FALLBACK[4],
|
||||||
Magenta = vim.g.terminal_color_5,
|
Magenta = vim.g.terminal_color_5 or ANSI_TERMINAL_COLOR_CODE_FALLBACK[5],
|
||||||
Cyan = vim.g.terminal_color_6,
|
Cyan = vim.g.terminal_color_6 or ANSI_TERMINAL_COLOR_CODE_FALLBACK[6],
|
||||||
White = vim.g.terminal_color_7,
|
White = vim.g.terminal_color_7 or ANSI_TERMINAL_COLOR_CODE_FALLBACK[7],
|
||||||
BrightBlack = vim.g.terminal_color_8,
|
BrightBlack = vim.g.terminal_color_8 or ANSI_TERMINAL_COLOR_CODE_FALLBACK[8],
|
||||||
BrightRed = vim.g.terminal_color_9,
|
BrightRed = vim.g.terminal_color_9 or ANSI_TERMINAL_COLOR_CODE_FALLBACK[9],
|
||||||
BrightGreen = vim.g.terminal_color_10,
|
BrightGreen = vim.g.terminal_color_10 or ANSI_TERMINAL_COLOR_CODE_FALLBACK[10],
|
||||||
BrightYellow = vim.g.terminal_color_11,
|
BrightYellow = vim.g.terminal_color_11 or ANSI_TERMINAL_COLOR_CODE_FALLBACK[11],
|
||||||
BrightBlue = vim.g.terminal_color_12,
|
BrightBlue = vim.g.terminal_color_12 or ANSI_TERMINAL_COLOR_CODE_FALLBACK[12],
|
||||||
BrightMagenta = vim.g.terminal_color_13,
|
BrightMagenta = vim.g.terminal_color_13 or ANSI_TERMINAL_COLOR_CODE_FALLBACK[13],
|
||||||
BrightCyan = vim.g.terminal_color_14,
|
BrightCyan = vim.g.terminal_color_14 or ANSI_TERMINAL_COLOR_CODE_FALLBACK[14],
|
||||||
BrightWhite = vim.g.terminal_color_15,
|
BrightWhite = vim.g.terminal_color_15 or ANSI_TERMINAL_COLOR_CODE_FALLBACK[15],
|
||||||
}
|
}
|
||||||
|
|
||||||
if vim.tbl_count(color_map) < 16 then
|
|
||||||
logger.log(
|
|
||||||
'ansi terminal colors (vim.g.terminal_color_*) not configured. ANSI colors will not display properly.',
|
|
||||||
vim.log.levels.WARN
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
local combinations = {
|
local combinations = {
|
||||||
{ bold = false, italic = false },
|
{ bold = false, italic = false },
|
||||||
{ bold = true, italic = false },
|
{ bold = true, italic = false },
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,8 @@ local function create_none_diff_layout(parent_win, expected_content, actual_cont
|
||||||
local expected_win = vim.api.nvim_get_current_win()
|
local expected_win = vim.api.nvim_get_current_win()
|
||||||
vim.api.nvim_win_set_buf(expected_win, expected_buf)
|
vim.api.nvim_win_set_buf(expected_win, expected_buf)
|
||||||
|
|
||||||
vim.api.nvim_set_option_value('filetype', 'cptest', { buf = expected_buf })
|
vim.api.nvim_set_option_value('filetype', 'cp', { buf = expected_buf })
|
||||||
vim.api.nvim_set_option_value('filetype', 'cptest', { buf = actual_buf })
|
vim.api.nvim_set_option_value('filetype', 'cp', { buf = actual_buf })
|
||||||
vim.api.nvim_set_option_value('winbar', 'Expected', { win = expected_win })
|
vim.api.nvim_set_option_value('winbar', 'Expected', { win = expected_win })
|
||||||
vim.api.nvim_set_option_value('winbar', 'Actual', { win = actual_win })
|
vim.api.nvim_set_option_value('winbar', 'Actual', { win = actual_win })
|
||||||
|
|
||||||
|
|
@ -53,8 +53,8 @@ local function create_vim_diff_layout(parent_win, expected_content, actual_conte
|
||||||
local expected_win = vim.api.nvim_get_current_win()
|
local expected_win = vim.api.nvim_get_current_win()
|
||||||
vim.api.nvim_win_set_buf(expected_win, expected_buf)
|
vim.api.nvim_win_set_buf(expected_win, expected_buf)
|
||||||
|
|
||||||
vim.api.nvim_set_option_value('filetype', 'cptest', { buf = expected_buf })
|
vim.api.nvim_set_option_value('filetype', 'cp', { buf = expected_buf })
|
||||||
vim.api.nvim_set_option_value('filetype', 'cptest', { buf = actual_buf })
|
vim.api.nvim_set_option_value('filetype', 'cp', { buf = actual_buf })
|
||||||
vim.api.nvim_set_option_value('winbar', 'Expected', { win = expected_win })
|
vim.api.nvim_set_option_value('winbar', 'Expected', { win = expected_win })
|
||||||
vim.api.nvim_set_option_value('winbar', 'Actual', { win = actual_win })
|
vim.api.nvim_set_option_value('winbar', 'Actual', { win = actual_win })
|
||||||
|
|
||||||
|
|
@ -96,7 +96,7 @@ local function create_git_diff_layout(parent_win, expected_content, actual_conte
|
||||||
local diff_win = vim.api.nvim_get_current_win()
|
local diff_win = vim.api.nvim_get_current_win()
|
||||||
vim.api.nvim_win_set_buf(diff_win, diff_buf)
|
vim.api.nvim_win_set_buf(diff_win, diff_buf)
|
||||||
|
|
||||||
vim.api.nvim_set_option_value('filetype', 'cptest', { buf = diff_buf })
|
vim.api.nvim_set_option_value('filetype', 'cp', { buf = diff_buf })
|
||||||
vim.api.nvim_set_option_value('winbar', 'Expected vs Actual', { win = diff_win })
|
vim.api.nvim_set_option_value('winbar', 'Expected vs Actual', { win = diff_win })
|
||||||
|
|
||||||
local diff_backend = require('cp.ui.diff')
|
local diff_backend = require('cp.ui.diff')
|
||||||
|
|
@ -132,7 +132,7 @@ local function create_single_layout(parent_win, content)
|
||||||
vim.cmd('resize ' .. math.floor(vim.o.lines * 0.35))
|
vim.cmd('resize ' .. math.floor(vim.o.lines * 0.35))
|
||||||
local win = vim.api.nvim_get_current_win()
|
local win = vim.api.nvim_get_current_win()
|
||||||
vim.api.nvim_win_set_buf(win, buf)
|
vim.api.nvim_win_set_buf(win, buf)
|
||||||
vim.api.nvim_set_option_value('filetype', 'cptest', { buf = buf })
|
vim.api.nvim_set_option_value('filetype', 'cp', { buf = buf })
|
||||||
|
|
||||||
return {
|
return {
|
||||||
buffers = { buf },
|
buffers = { buf },
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
|
---@class RunOpts
|
||||||
|
---@field debug? boolean
|
||||||
|
|
||||||
local config_module = require('cp.config')
|
local config_module = require('cp.config')
|
||||||
local layouts = require('cp.ui.layouts')
|
local layouts = require('cp.ui.layouts')
|
||||||
local logger = require('cp.log')
|
local logger = require('cp.log')
|
||||||
|
|
@ -51,19 +54,13 @@ function M.toggle_interactive()
|
||||||
local platform, contest_id = state.get_platform(), state.get_contest_id()
|
local platform, contest_id = state.get_platform(), state.get_contest_id()
|
||||||
|
|
||||||
if not platform then
|
if not platform then
|
||||||
logger.log(
|
logger.log('No platform configured.', vim.log.levels.ERROR)
|
||||||
'No platform configured. Use :CP <platform> <contest> [--{lang=<lang>,debug}] first.',
|
|
||||||
vim.log.levels.ERROR
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if not contest_id then
|
if not contest_id then
|
||||||
logger.log(
|
logger.log(
|
||||||
('No contest %s configured for platform %s. Use :CP <platform> <contest> [--{lang=<lang>,debug}] to set up first.'):format(
|
('No contest %s configured for platform %s.'):format(contest_id, platform),
|
||||||
contest_id,
|
|
||||||
platform
|
|
||||||
),
|
|
||||||
vim.log.levels.ERROR
|
vim.log.levels.ERROR
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
@ -118,8 +115,8 @@ function M.toggle_interactive()
|
||||||
state.set_active_panel('interactive')
|
state.set_active_panel('interactive')
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param debug? boolean
|
---@param run_opts? RunOpts
|
||||||
function M.toggle_run_panel(debug)
|
function M.toggle_run_panel(run_opts)
|
||||||
if state.get_active_panel() == 'run' then
|
if state.get_active_panel() == 'run' then
|
||||||
if current_diff_layout then
|
if current_diff_layout then
|
||||||
current_diff_layout.cleanup()
|
current_diff_layout.cleanup()
|
||||||
|
|
@ -152,10 +149,7 @@ function M.toggle_run_panel(debug)
|
||||||
|
|
||||||
if not contest_id then
|
if not contest_id then
|
||||||
logger.log(
|
logger.log(
|
||||||
('No contest %s configured for platform %s. Use :CP <platform> <contest> [--{lang=<lang>,debug}] to set up first.'):format(
|
('No contest %s configured for platform %s.'):format(contest_id, platform),
|
||||||
contest_id,
|
|
||||||
platform
|
|
||||||
),
|
|
||||||
vim.log.levels.ERROR
|
vim.log.levels.ERROR
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
@ -187,13 +181,6 @@ function M.toggle_run_panel(debug)
|
||||||
)
|
)
|
||||||
|
|
||||||
local config = config_module.get_config()
|
local config = config_module.get_config()
|
||||||
if config.hooks and config.hooks.before_run then
|
|
||||||
config.hooks.before_run(state)
|
|
||||||
end
|
|
||||||
if debug and config.hooks and config.hooks.before_debug then
|
|
||||||
config.hooks.before_debug(state)
|
|
||||||
end
|
|
||||||
|
|
||||||
local run = require('cp.runner.run')
|
local run = require('cp.runner.run')
|
||||||
local input_file = state.get_input_file()
|
local input_file = state.get_input_file()
|
||||||
logger.log(('run panel: checking test cases for %s'):format(input_file or 'none'))
|
logger.log(('run panel: checking test cases for %s'):format(input_file or 'none'))
|
||||||
|
|
@ -210,7 +197,7 @@ function M.toggle_run_panel(debug)
|
||||||
local tab_buf = utils.create_buffer_with_options()
|
local tab_buf = utils.create_buffer_with_options()
|
||||||
local main_win = vim.api.nvim_get_current_win()
|
local main_win = vim.api.nvim_get_current_win()
|
||||||
vim.api.nvim_win_set_buf(main_win, tab_buf)
|
vim.api.nvim_win_set_buf(main_win, tab_buf)
|
||||||
vim.api.nvim_set_option_value('filetype', 'cptest', { buf = tab_buf })
|
vim.api.nvim_set_option_value('filetype', 'cp', { buf = tab_buf })
|
||||||
|
|
||||||
local test_windows = { tab_win = main_win }
|
local test_windows = { tab_win = main_win }
|
||||||
local test_buffers = { tab_buf = tab_buf }
|
local test_buffers = { tab_buf = tab_buf }
|
||||||
|
|
@ -282,6 +269,17 @@ function M.toggle_run_panel(debug)
|
||||||
|
|
||||||
setup_keybindings_for_buffer(test_buffers.tab_buf)
|
setup_keybindings_for_buffer(test_buffers.tab_buf)
|
||||||
|
|
||||||
|
if config.hooks and config.hooks.before_run then
|
||||||
|
vim.schedule_wrap(function()
|
||||||
|
config.hooks.before_run(state)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
if run_opts and run_opts.debug and config.hooks and config.hooks.before_debug then
|
||||||
|
vim.schedule_wrap(function()
|
||||||
|
config.hooks.before_debug(state)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
local execute = require('cp.runner.execute')
|
local execute = require('cp.runner.execute')
|
||||||
local compile_result = execute.compile_problem()
|
local compile_result = execute.compile_problem()
|
||||||
if compile_result.success then
|
if compile_result.success then
|
||||||
|
|
|
||||||
|
|
@ -23,22 +23,26 @@ end, {
|
||||||
if num_args == 2 then
|
if num_args == 2 then
|
||||||
local candidates = {}
|
local candidates = {}
|
||||||
local state = require('cp.state')
|
local state = require('cp.state')
|
||||||
local platform, contest_id = state.get_platform(), state.get_contest_id()
|
local platform = state.get_platform()
|
||||||
|
local contest_id = state.get_contest_id()
|
||||||
|
|
||||||
if platform and contest_id then
|
if platform and contest_id then
|
||||||
vim.list_extend(candidates, actions)
|
vim.list_extend(candidates, actions)
|
||||||
local cache = require('cp.cache')
|
local cache = require('cp.cache')
|
||||||
cache.load()
|
cache.load()
|
||||||
local contest_data = cache.get_contest_data(platform, contest_id)
|
local contest_data = cache.get_contest_data(platform, contest_id)
|
||||||
if contest_data and contest_data.problems then
|
|
||||||
for _, problem in ipairs(contest_data.problems) do
|
if contest_data and contest_data.index_map then
|
||||||
table.insert(candidates, problem.id)
|
local ids = vim.tbl_keys(contest_data.index_map)
|
||||||
end
|
table.sort(ids)
|
||||||
|
vim.list_extend(candidates, ids)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
vim.list_extend(candidates, platforms)
|
vim.list_extend(candidates, platforms)
|
||||||
table.insert(candidates, 'cache')
|
table.insert(candidates, 'cache')
|
||||||
table.insert(candidates, 'pick')
|
table.insert(candidates, 'pick')
|
||||||
end
|
end
|
||||||
|
|
||||||
return vim.tbl_filter(function(cmd)
|
return vim.tbl_filter(function(cmd)
|
||||||
return cmd:find(ArgLead, 1, true) == 1
|
return cmd:find(ArgLead, 1, true) == 1
|
||||||
end, candidates)
|
end, candidates)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue