feat(doc): update for new config
This commit is contained in:
parent
2c0e808c8c
commit
a76d228e3f
5 changed files with 369 additions and 297 deletions
263
doc/cp.nvim.txt
263
doc/cp.nvim.txt
|
|
@ -32,15 +32,13 @@ COMMANDS *cp-commands*
|
||||||
Automatically detects platform, contest, problem,
|
Automatically detects platform, contest, problem,
|
||||||
and language from cached state. Use this after
|
and language from cached state. Use this after
|
||||||
switching files to restore your CP environment.
|
switching files to restore your CP environment.
|
||||||
Requires previous setup with full :CP command.
|
|
||||||
|
|
||||||
Setup Commands ~
|
Setup Commands ~
|
||||||
:CP {platform} {contest_id} {problem_id}
|
:CP {platform} {contest_id}
|
||||||
Full setup: set platform, load contest metadata,
|
Full setup: set platform and load contest metadata.
|
||||||
and set up specific problem. Scrapes test cases
|
Scrapes test cases and creates source file.
|
||||||
and creates source file.
|
|
||||||
Example: >
|
Example: >
|
||||||
:CP codeforces 1933 a
|
:CP codeforces 1933
|
||||||
<
|
<
|
||||||
:CP {platform} {contest_id}
|
:CP {platform} {contest_id}
|
||||||
Contest setup: set platform, load contest metadata,
|
Contest setup: set platform, load contest metadata,
|
||||||
|
|
@ -51,10 +49,6 @@ COMMANDS *cp-commands*
|
||||||
Example: >
|
Example: >
|
||||||
:CP atcoder abc324
|
:CP atcoder abc324
|
||||||
:CP codeforces 1951
|
:CP codeforces 1951
|
||||||
<
|
|
||||||
:CP {platform} Platform setup: set platform only.
|
|
||||||
Example: >
|
|
||||||
:CP cses
|
|
||||||
<
|
<
|
||||||
Action Commands ~
|
Action Commands ~
|
||||||
:CP run [--debug] Toggle run panel for individual test case
|
:CP run [--debug] Toggle run panel for individual test case
|
||||||
|
|
@ -64,12 +58,13 @@ COMMANDS *cp-commands*
|
||||||
Requires contest setup first.
|
Requires contest setup first.
|
||||||
|
|
||||||
:CP pick Launch configured picker for interactive
|
:CP pick Launch configured picker for interactive
|
||||||
platform/contest/problem selection.
|
platform/contest selection.
|
||||||
|
|
||||||
Navigation Commands ~
|
Navigation Commands ~
|
||||||
:CP next Navigate to next problem in current contest.
|
:CP next Navigate to next problem in current contest.
|
||||||
Stops at last problem (no wrapping).
|
Stops at last problem (no wrapping).
|
||||||
|
|
||||||
|
|
||||||
Navigation Commands ~
|
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).
|
||||||
|
|
@ -78,7 +73,7 @@ COMMANDS *cp-commands*
|
||||||
:CP cache clear [contest]
|
:CP cache clear [contest]
|
||||||
Clear the cache data (contest list, problem
|
Clear the cache data (contest list, problem
|
||||||
data, file states) for the specified contest,
|
data, file states) for the specified contest,
|
||||||
or all contests if none specified
|
or all contests if none specified.
|
||||||
|
|
||||||
:CP cache read
|
:CP cache read
|
||||||
View the cache in a pretty-printed lua buffer.
|
View the cache in a pretty-printed lua buffer.
|
||||||
|
|
@ -88,13 +83,16 @@ Command Flags ~
|
||||||
*cp-flags*
|
*cp-flags*
|
||||||
Flags can be used with setup and action commands:
|
Flags can be used with setup and action commands:
|
||||||
|
|
||||||
--debug Enable debug compilation with additional flags.
|
--debug Use the debug command template.
|
||||||
Uses the `debug` command template instead of
|
For compiled languages, this selects
|
||||||
`compile`. Typically includes debug symbols and
|
`commands.debug` (a debug *build*) instead of
|
||||||
sanitizers for memory error detection.
|
`commands.build`. For interpreted languages,
|
||||||
|
this selects `commands.debug` in place of
|
||||||
|
`commands.run`.
|
||||||
Example: >
|
Example: >
|
||||||
:CP run --debug
|
: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:
|
||||||
|
|
@ -105,7 +103,7 @@ Template Variables ~
|
||||||
• {problem} Problem identifier (e.g. "a", "b")
|
• {problem} Problem identifier (e.g. "a", "b")
|
||||||
|
|
||||||
Example template: >
|
Example template: >
|
||||||
compile = { 'g++', '{source}', '-o', '{binary}', '-std=c++17' }
|
build = { 'g++', '{source}', '-o', '{binary}', '-std=c++17' }
|
||||||
< Would expand to: >
|
< Would expand to: >
|
||||||
g++ abc324a.cpp -o build/abc324a.run -std=c++17
|
g++ abc324a.cpp -o build/abc324a.run -std=c++17
|
||||||
<
|
<
|
||||||
|
|
@ -116,117 +114,161 @@ CONFIGURATION *cp-config*
|
||||||
Here's an example configuration with lazy.nvim: >lua
|
Here's an example configuration with lazy.nvim: >lua
|
||||||
|
|
||||||
{
|
{
|
||||||
'barrett-ruth/cp.nvim',
|
'barrett-ruth/cp.nvim',
|
||||||
cmd = 'CP',
|
cmd = 'CP',
|
||||||
build = 'uv sync',
|
build = 'uv sync',
|
||||||
opts = {
|
opts = {
|
||||||
platforms = {
|
languages = {
|
||||||
cses = {
|
cpp = {
|
||||||
cpp = {
|
extension = 'cc',
|
||||||
compile = { 'g++', '{source}', '-o', '{binary}',
|
commands = {
|
||||||
'-std=c++17', '-fdiagnostic-colors=always' },
|
build = { 'g++', '-std=c++17', '{source}', '-o', '{binary}' },
|
||||||
test = { '{binary}' },
|
run = { '{binary}' },
|
||||||
debug = { 'g++', '{source}', '-o', '{binary}',
|
debug = { 'g++', '-std=c++17', '-fsanitize=address,undefined',
|
||||||
'-std=c++17', '-g',
|
'{source}', '-o', '{binary}' },
|
||||||
'-fdiagnostic-colors=always'
|
|
||||||
'-fsanitize=address,undefined' },
|
|
||||||
},
|
|
||||||
python = {
|
|
||||||
test = { 'python3', '{source}' },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
snippets = {},
|
},
|
||||||
debug = false,
|
python = {
|
||||||
run_panel = {
|
extension = 'py',
|
||||||
ansi = true,
|
commands = {
|
||||||
diff_mode = 'vim',
|
run = { 'python', '{source}' },
|
||||||
max_output_lines = 50,
|
debug = { 'python', '{source}' },
|
||||||
},
|
},
|
||||||
diff = {
|
},
|
||||||
git = {
|
},
|
||||||
args = { 'diff', '--no-index', '--word-diff=plain',
|
|
||||||
'--word-diff-regex=.', '--no-prefix' },
|
platforms = {
|
||||||
},
|
cses = {
|
||||||
|
enabled_languages = { 'cpp', 'python' },
|
||||||
|
default_language = 'cpp',
|
||||||
|
overrides = {
|
||||||
|
cpp = { extension = 'cpp', commands = { build = { ... } } }
|
||||||
},
|
},
|
||||||
picker = 'telescope',
|
},
|
||||||
}
|
atcoder = {
|
||||||
|
enabled_languages = { 'cpp', 'python' },
|
||||||
|
default_language = 'cpp',
|
||||||
|
},
|
||||||
|
codeforces = {
|
||||||
|
enabled_languages = { 'cpp', 'python' },
|
||||||
|
default_language = 'cpp',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
snippets = {},
|
||||||
|
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, all contests are configured to use C++ with the g++ compiler and ISO standard
|
By default, C++ (g++ with ISO C++17) and Python are preconfigured under
|
||||||
17. Python is also configured with the system executable python.
|
`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, only the following config
|
For example, to run CodeForces contests with Python by default:
|
||||||
is required:
|
|
||||||
|
|
||||||
|
>lua
|
||||||
{
|
{
|
||||||
platforms = {
|
platforms = {
|
||||||
codeforces = {
|
codeforces = {
|
||||||
default_language = 'python'
|
enabled_languages = { 'cpp', 'python' },
|
||||||
}
|
default_language = 'python',
|
||||||
}
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
<
|
||||||
|
|
||||||
Any language is supported provided the proper configuration. For example, to
|
Any language is supported provided the proper configuration. For example, to
|
||||||
run CSES problems with Rust:
|
run CSES problems with Rust using the single schema:
|
||||||
|
|
||||||
|
>lua
|
||||||
{
|
{
|
||||||
platforms = {
|
languages = {
|
||||||
codeforces = {
|
rust = {
|
||||||
rust = {
|
extension = 'rs',
|
||||||
compile = { 'rustc', '{source}', '-o', '{binary}' },
|
commands = {
|
||||||
test = { 'binary' },
|
build = { 'rustc', '{source}', '-o', '{binary}' },
|
||||||
extension = 'rs'
|
run = { '{binary}' },
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
|
platforms = {
|
||||||
|
cses = {
|
||||||
|
enabled_languages = { 'cpp', 'python', 'rust' },
|
||||||
|
default_language = 'rust',
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
<
|
||||||
|
|
||||||
*cp.Config*
|
*cp.Config*
|
||||||
Fields: ~
|
Fields: ~
|
||||||
{platforms} (table<string,PlatformContestConfig>) Contest configurations.
|
{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.
|
{hooks} (|cp.Hooks|) Hook functions called at various stages.
|
||||||
{snippets} (table[]) LuaSnip snippet definitions.
|
{snippets} (table[]) LuaSnip snippet definitions.
|
||||||
{debug} (boolean, default: false) Show info messages
|
{debug} (boolean, default: false) Show info messages.
|
||||||
during operation.
|
{scrapers} (string[]) Supported platform ids.
|
||||||
{run_panel} (|RunPanelConfig|) Test panel behavior configuration.
|
{filename} (function, optional)
|
||||||
{diff} (|DiffConfig|) Diff backend configuration.
|
function(contest, contest_id, problem_id, config, language): string
|
||||||
{picker} (string, optional) Picker integration: "telescope",
|
|
||||||
"fzf-lua", or nil to disable. When enabled, provides
|
|
||||||
:CP pick for interactive platform/contest/problem selection.
|
|
||||||
{filename} (function, optional) Custom filename generation.
|
|
||||||
function(contest, contest_id, problem_id, config, language)
|
|
||||||
Should return full filename with extension.
|
Should return full filename with extension.
|
||||||
(default: concatenates contest_id and problem_id, lowercased)
|
(default: concatenates contest_id and problem_id, lowercased)
|
||||||
|
{ui} (|CpUI|) UI settings: run panel, diff backend, picker.
|
||||||
|
|
||||||
*cp.PlatformConfig*
|
*cp.PlatformConfig*
|
||||||
Fields: ~
|
Replaced by |CpPlatform|. Platforms no longer inline language tables.
|
||||||
{cpp} (|PlatformLanguageConfig|) C++ language configuration.
|
|
||||||
{python} (|PlatformLanguageConfig|) Python language configuration.
|
|
||||||
{default_language} (string, default: "cpp") Default language for
|
|
||||||
platform contests.
|
|
||||||
|
|
||||||
*cp.LanguageConfig*
|
*CpPlatform*
|
||||||
Fields: ~
|
Fields: ~
|
||||||
{compile} (string[], optional) Compile command template with
|
{enabled_languages} (string[]) Language ids enabled on this platform.
|
||||||
{source}, {binary} placeholders.
|
{default_language} (string) One of {enabled_languages}.
|
||||||
{test} (string[]) Test execution command template.
|
{overrides} (table<string,|CpPlatformOverrides|>, optional)
|
||||||
{debug} (string[], optional) Debug compile command template.
|
Per-language overrides of {extension} and/or {commands}.
|
||||||
{executable} (string, optional) Executable name for interpreted languages.
|
|
||||||
|
*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*
|
*cp.RunPanelConfig*
|
||||||
Fields: ~
|
Fields: ~
|
||||||
{ansi} (boolean, default: true) Enable ANSI color parsing and
|
{ansi} (boolean, default: true) Enable ANSI color parsing
|
||||||
highlighting. When true, compiler output and test results
|
and highlighting.
|
||||||
display with colored syntax highlighting. When false,
|
{diff_mode} (string, default: "none") Diff backend: "none",
|
||||||
ANSI escape codes are stripped for plain text display.
|
"vim", or "git".
|
||||||
Requires vim.g.terminal_color_* to be configured for
|
|
||||||
proper color display.
|
|
||||||
{diff_mode} (string, default: "none") Diff backend: "none", "vim", or "git".
|
|
||||||
"none" displays plain buffers without highlighting,
|
|
||||||
"vim" uses built-in diff, "git" provides character-level precision.
|
|
||||||
{toggle_diff_key} (string, default: "<c-t>") Key to cycle through diff modes.
|
|
||||||
{max_output_lines} (number, default: 50) Maximum lines of test output.
|
{max_output_lines} (number, default: 50) Maximum lines of test output.
|
||||||
|
|
||||||
*cp.DiffConfig*
|
*cp.DiffConfig*
|
||||||
|
|
@ -247,10 +289,9 @@ run CSES problems with Rust:
|
||||||
Fields: ~
|
Fields: ~
|
||||||
{before_run} (function, optional) Called before test panel opens.
|
{before_run} (function, optional) Called before test panel opens.
|
||||||
function(state: cp.State)
|
function(state: cp.State)
|
||||||
{before_debug} (function, optional) Called before debug compilation.
|
{before_debug} (function, optional) Called before debug build/run.
|
||||||
function(state: cp.State)
|
function(state: cp.State)
|
||||||
{setup_code} (function, optional) Called after source file is opened.
|
{setup_code} (function, optional) Called after source file is opened.
|
||||||
Good for configuring buffer settings.
|
|
||||||
function(state: cp.State)
|
function(state: cp.State)
|
||||||
|
|
||||||
Hook functions receive the cp.nvim state object (cp.State). See the state
|
Hook functions receive the cp.nvim state object (cp.State). See the state
|
||||||
|
|
@ -280,41 +321,21 @@ AtCoder ~
|
||||||
URL format: https://atcoder.jp/contests/abc123/tasks/abc123_a
|
URL format: https://atcoder.jp/contests/abc123/tasks/abc123_a
|
||||||
|
|
||||||
Usage examples: >
|
Usage examples: >
|
||||||
:CP atcoder abc324 a " Full setup: problem A from contest ABC324
|
|
||||||
:CP atcoder abc324 " Contest setup: load contest metadata only
|
:CP atcoder abc324 " Contest setup: load contest metadata only
|
||||||
:CP next " Navigate to next problem in contest
|
|
||||||
<
|
|
||||||
Note: AtCoder template includes optimizations
|
|
||||||
for multi-test case problems commonly found
|
|
||||||
in contests.
|
|
||||||
|
|
||||||
AtCoder Heuristic Contests (AHC) are excluded
|
|
||||||
from the contest list as they don't have
|
|
||||||
standard sample test cases.
|
|
||||||
|
|
||||||
Codeforces ~
|
Codeforces ~
|
||||||
*cp-codeforces*
|
*cp-codeforces*
|
||||||
URL format: https://codeforces.com/contest/1234/problem/A
|
URL format: https://codeforces.com/contest/1234/problem/A
|
||||||
|
|
||||||
Usage examples: >
|
Usage examples: >
|
||||||
:CP codeforces 1934 a " Full setup: problem A from contest 1934
|
|
||||||
:CP codeforces 1934 " Contest setup: load contest metadata only
|
:CP codeforces 1934 " Contest setup: load contest metadata only
|
||||||
:CP prev " Navigate to previous problem in contest
|
|
||||||
<
|
|
||||||
Note: Problem IDs are automatically converted
|
|
||||||
to lowercase for consistency.
|
|
||||||
|
|
||||||
CSES ~
|
CSES ~
|
||||||
*cp-cses*
|
*cp-cses*
|
||||||
URL format: https://cses.fi/problemset/task/1068
|
URL format: https://cses.fi/problemset/task/1068
|
||||||
|
|
||||||
Usage examples: >
|
Usage examples: >
|
||||||
:CP cses dynamic_programming 1633 " Set up problem 1633 from DP category
|
|
||||||
:CP cses dynamic_programming " Set up ALL problems from DP category
|
:CP cses dynamic_programming " Set up ALL problems from DP category
|
||||||
<
|
|
||||||
Note: Category name is always required. For bulk
|
|
||||||
setup, omit the problem ID to scrape all problems
|
|
||||||
in the category.
|
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -222,8 +222,7 @@ end
|
||||||
---@param platform string
|
---@param platform string
|
||||||
---@param contest_id string
|
---@param contest_id string
|
||||||
---@param problem_id? string
|
---@param problem_id? string
|
||||||
---@param language? string
|
function M.set_file_state(file_path, platform, contest_id, problem_id)
|
||||||
function M.set_file_state(file_path, platform, contest_id, problem_id, language)
|
|
||||||
if not cache_data.file_states then
|
if not cache_data.file_states then
|
||||||
cache_data.file_states = {}
|
cache_data.file_states = {}
|
||||||
end
|
end
|
||||||
|
|
@ -232,7 +231,6 @@ function M.set_file_state(file_path, platform, contest_id, problem_id, language)
|
||||||
platform = platform,
|
platform = platform,
|
||||||
contest_id = contest_id,
|
contest_id = contest_id,
|
||||||
problem_id = problem_id,
|
problem_id = problem_id,
|
||||||
language = language,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
M.save()
|
M.save()
|
||||||
|
|
|
||||||
|
|
@ -1,201 +1,279 @@
|
||||||
---@class PlatformLanguageConfig
|
-- lua/cp/config.lua
|
||||||
---@field compile? string[] Compile command template
|
---@class CpLangCommands
|
||||||
---@field test string[] Test execution command template
|
---@field build? string[]
|
||||||
---@field debug? string[] Debug command template
|
---@field run? string[]
|
||||||
---@field executable? string Executable name
|
---@field debug? string[]
|
||||||
---@field extension? string File extension
|
|
||||||
|
|
||||||
---@class PlatformConfig
|
---@class CpLanguage
|
||||||
---@field [string] PlatformLanguageConfig
|
---@field extension string
|
||||||
---@field default_language? string
|
---@field commands CpLangCommands
|
||||||
|
|
||||||
|
---@class CpPlatformOverrides
|
||||||
|
---@field extension? string
|
||||||
|
---@field commands? CpLangCommands
|
||||||
|
|
||||||
|
---@class CpPlatform
|
||||||
|
---@field enabled_languages string[]
|
||||||
|
---@field default_language string
|
||||||
|
---@field overrides? table<string, CpPlatformOverrides>
|
||||||
|
|
||||||
|
---@class RunPanelConfig
|
||||||
|
---@field ansi boolean
|
||||||
|
---@field diff_mode "none"|"vim"|"git"
|
||||||
|
---@field max_output_lines integer
|
||||||
|
|
||||||
|
---@class DiffGitConfig
|
||||||
|
---@field args string[]
|
||||||
|
|
||||||
|
---@class DiffConfig
|
||||||
|
---@field git DiffGitConfig
|
||||||
|
|
||||||
---@class Hooks
|
---@class Hooks
|
||||||
---@field before_run? fun(state: cp.State)
|
---@field before_run? fun(state: cp.State)
|
||||||
---@field before_debug? fun(state: cp.State)
|
---@field before_debug? fun(state: cp.State)
|
||||||
---@field setup_code? fun(state: cp.State)
|
---@field setup_code? fun(state: cp.State)
|
||||||
|
|
||||||
---@class RunPanelConfig
|
---@class CpUI
|
||||||
---@field ansi boolean Enable ANSI color parsing and highlighting
|
|
||||||
---@field diff_mode "none"|"vim"|"git" Diff backend to use
|
|
||||||
---@field max_output_lines number Maximum lines of test output to display
|
|
||||||
|
|
||||||
---@class DiffGitConfig
|
|
||||||
---@field args string[] Git diff arguments
|
|
||||||
|
|
||||||
---@class DiffConfig
|
|
||||||
---@field git DiffGitConfig
|
|
||||||
|
|
||||||
---@class cp.Config
|
|
||||||
---@field platforms table<string, PlatformConfig>
|
|
||||||
---@field snippets any[]
|
|
||||||
---@field hooks Hooks
|
|
||||||
---@field debug boolean
|
|
||||||
---@field filename? fun(contest: string, contest_id: string, problem_id?: string, config: cp.Config, language?: string): string
|
|
||||||
---@field run_panel RunPanelConfig
|
---@field run_panel RunPanelConfig
|
||||||
---@field diff DiffConfig
|
---@field diff DiffConfig
|
||||||
---@field picker string|nil
|
---@field picker string|nil
|
||||||
|
|
||||||
---@class cp.PartialConfig
|
---@class cp.Config
|
||||||
---@field platforms? table<string, PlatformConfig>
|
---@field languages table<string, CpLanguage>
|
||||||
---@field snippets? any[]
|
---@field platforms table<string, CpPlatform>
|
||||||
---@field hooks? Hooks
|
---@field hooks Hooks
|
||||||
---@field debug? boolean
|
---@field snippets any[]
|
||||||
|
---@field debug boolean
|
||||||
|
---@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
|
||||||
---@field run_panel? RunPanelConfig
|
---@field ui CpUI
|
||||||
---@field diff? DiffConfig
|
---@field runtime { effective: table<string, table<string, CpLanguage>> } -- computed
|
||||||
---@field picker? string|nil
|
|
||||||
|
---@class cp.PartialConfig: cp.Config
|
||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
local constants = require('cp.constants')
|
local constants = require('cp.constants')
|
||||||
local utils = require('cp.utils')
|
local utils = require('cp.utils')
|
||||||
|
|
||||||
local default_contest_config = {
|
-- defaults per the new single schema
|
||||||
cpp = {
|
|
||||||
compile = { 'g++', '-std=c++17', '{source}', '-o', '{binary}' },
|
|
||||||
debug = { 'g++', '-std=c++17', '-fsanitize=address,undefined', '{source}', '-o', '{binary}' },
|
|
||||||
test = { '{binary}' },
|
|
||||||
},
|
|
||||||
python = {
|
|
||||||
test = { '{source}' },
|
|
||||||
debug = { '{source}' },
|
|
||||||
executable = 'python',
|
|
||||||
},
|
|
||||||
default_language = 'cpp',
|
|
||||||
}
|
|
||||||
|
|
||||||
---@type cp.Config
|
---@type cp.Config
|
||||||
M.defaults = {
|
M.defaults = {
|
||||||
|
languages = {
|
||||||
|
cpp = {
|
||||||
|
extension = 'cc',
|
||||||
|
commands = {
|
||||||
|
build = { 'g++', '-std=c++17', '{source}', '-o', '{binary}' },
|
||||||
|
run = { '{binary}' },
|
||||||
|
debug = {
|
||||||
|
'g++',
|
||||||
|
'-std=c++17',
|
||||||
|
'-fsanitize=address,undefined',
|
||||||
|
'{source}',
|
||||||
|
'-o',
|
||||||
|
'{binary}',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
python = {
|
||||||
|
extension = 'py',
|
||||||
|
commands = {
|
||||||
|
run = { 'python', '{source}' },
|
||||||
|
debug = { 'python', '{source}' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
platforms = {
|
platforms = {
|
||||||
codeforces = default_contest_config,
|
codeforces = {
|
||||||
atcoder = default_contest_config,
|
enabled_languages = { 'cpp', 'python' },
|
||||||
cses = default_contest_config,
|
default_language = 'cpp',
|
||||||
|
overrides = {
|
||||||
|
-- example override, safe to keep empty initially
|
||||||
|
},
|
||||||
|
},
|
||||||
|
atcoder = {
|
||||||
|
enabled_languages = { 'cpp', 'python' },
|
||||||
|
default_language = 'cpp',
|
||||||
|
},
|
||||||
|
cses = {
|
||||||
|
enabled_languages = { 'cpp', 'python' },
|
||||||
|
default_language = 'cpp',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
snippets = {},
|
snippets = {},
|
||||||
hooks = {
|
hooks = { before_run = nil, before_debug = nil, setup_code = nil },
|
||||||
before_run = nil,
|
|
||||||
before_debug = nil,
|
|
||||||
setup_code = nil,
|
|
||||||
},
|
|
||||||
debug = false,
|
debug = false,
|
||||||
scrapers = constants.PLATFORMS,
|
scrapers = constants.PLATFORMS,
|
||||||
filename = nil,
|
filename = nil,
|
||||||
run_panel = {
|
ui = {
|
||||||
ansi = true,
|
run_panel = { ansi = true, diff_mode = 'none', max_output_lines = 50 },
|
||||||
diff_mode = 'none',
|
diff = {
|
||||||
max_output_lines = 50,
|
git = {
|
||||||
},
|
args = { 'diff', '--no-index', '--word-diff=plain', '--word-diff-regex=.', '--no-prefix' },
|
||||||
diff = {
|
},
|
||||||
git = {
|
|
||||||
args = { 'diff', '--no-index', '--word-diff=plain', '--word-diff-regex=.', '--no-prefix' },
|
|
||||||
},
|
},
|
||||||
|
picker = nil,
|
||||||
},
|
},
|
||||||
picker = nil,
|
runtime = { effective = {} },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
local function is_string_list(t)
|
||||||
|
if type(t) ~= 'table' then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
for i, v in ipairs(t) do
|
||||||
|
if type(v) ~= 'string' then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
local function has_tokens(cmd, required)
|
||||||
|
if type(cmd) ~= 'table' then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
local s = table.concat(cmd, ' ')
|
||||||
|
for _, tok in ipairs(required) do
|
||||||
|
if not s:find(vim.pesc(tok), 1, true) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
local function validate_language(id, lang)
|
||||||
|
vim.validate({
|
||||||
|
extension = { lang.extension, 'string' },
|
||||||
|
commands = { lang.commands, { 'table' } },
|
||||||
|
})
|
||||||
|
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
|
||||||
|
end
|
||||||
|
else
|
||||||
|
for _, k in ipairs({ 'run', 'debug' }) do
|
||||||
|
if lang.commands[k] then
|
||||||
|
if not has_tokens(lang.commands[k], { '{source}' }) then
|
||||||
|
error(('[cp.nvim] languages.%s.commands.%s must include {source}'):format(id, k))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function merge_lang(base, ov)
|
||||||
|
if not ov then
|
||||||
|
return base
|
||||||
|
end
|
||||||
|
local out = vim.deepcopy(base)
|
||||||
|
if ov.extension then
|
||||||
|
out.extension = ov.extension
|
||||||
|
end
|
||||||
|
if ov.commands then
|
||||||
|
out.commands = vim.tbl_deep_extend('force', out.commands or {}, ov.commands or {})
|
||||||
|
end
|
||||||
|
return out
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param cfg cp.Config
|
||||||
|
local function build_runtime(cfg)
|
||||||
|
cfg.runtime = cfg.runtime or { effective = {} }
|
||||||
|
for plat, p in pairs(cfg.platforms) do
|
||||||
|
vim.validate({
|
||||||
|
enabled_languages = { p.enabled_languages, is_string_list, 'string[]' },
|
||||||
|
default_language = { p.default_language, 'string' },
|
||||||
|
})
|
||||||
|
for _, lid in ipairs(p.enabled_languages) do
|
||||||
|
if not cfg.languages[lid] then
|
||||||
|
error(("[cp.nvim] platform %s references unknown language '%s'"):format(plat, lid))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not vim.tbl_contains(p.enabled_languages, p.default_language) then
|
||||||
|
error(
|
||||||
|
("[cp.nvim] platform %s default_language '%s' not in enabled_languages"):format(
|
||||||
|
plat,
|
||||||
|
p.default_language
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
cfg.runtime.effective[plat] = {}
|
||||||
|
for _, lid in ipairs(p.enabled_languages) do
|
||||||
|
local base = cfg.languages[lid]
|
||||||
|
validate_language(lid, base)
|
||||||
|
local eff = merge_lang(base, p.overrides and p.overrides[lid] or nil)
|
||||||
|
validate_language(lid, eff)
|
||||||
|
cfg.runtime.effective[plat][lid] = eff
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
---@param user_config cp.PartialConfig|nil
|
---@param user_config cp.PartialConfig|nil
|
||||||
---@return cp.Config
|
---@return cp.Config
|
||||||
function M.setup(user_config)
|
function M.setup(user_config)
|
||||||
vim.validate({
|
vim.validate({ user_config = { user_config, { 'table', 'nil' }, true } })
|
||||||
user_config = { user_config, { 'table', 'nil' }, true },
|
local cfg = vim.tbl_deep_extend('force', vim.deepcopy(M.defaults), user_config or {})
|
||||||
})
|
|
||||||
|
|
||||||
if user_config then
|
|
||||||
vim.validate({
|
|
||||||
platforms = { user_config.platforms, { 'table', 'nil' }, true },
|
|
||||||
snippets = { user_config.snippets, { 'table', 'nil' }, true },
|
|
||||||
hooks = { user_config.hooks, { 'table', 'nil' }, true },
|
|
||||||
debug = { user_config.debug, { 'boolean', 'nil' }, true },
|
|
||||||
filename = { user_config.filename, { 'function', 'nil' }, true },
|
|
||||||
run_panel = { user_config.run_panel, { 'table', 'nil' }, true },
|
|
||||||
diff = { user_config.diff, { 'table', 'nil' }, true },
|
|
||||||
picker = { user_config.picker, { 'string', 'nil' }, true },
|
|
||||||
})
|
|
||||||
|
|
||||||
if user_config.platforms then
|
|
||||||
for platform_name, platform_config in pairs(user_config.platforms) do
|
|
||||||
vim.validate({
|
|
||||||
[platform_name] = {
|
|
||||||
platform_config,
|
|
||||||
function(config)
|
|
||||||
if type(config) ~= 'table' then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
return true
|
|
||||||
end,
|
|
||||||
'contest configuration',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if user_config.picker then
|
|
||||||
if not vim.tbl_contains({ 'telescope', 'fzf-lua' }, user_config.picker) then
|
|
||||||
error(("Invalid picker '%s'. Must be 'telescope' or 'fzf-lua'"):format(user_config.picker))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local config = vim.tbl_deep_extend('force', M.defaults, user_config or {})
|
|
||||||
|
|
||||||
vim.validate({
|
vim.validate({
|
||||||
before_run = {
|
hooks = { cfg.hooks, { 'table' } },
|
||||||
config.hooks.before_run,
|
ui = { cfg.ui, { 'table' } },
|
||||||
{ 'function', 'nil' },
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
before_debug = {
|
|
||||||
config.hooks.before_debug,
|
|
||||||
{ 'function', 'nil' },
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
setup_code = {
|
|
||||||
config.hooks.setup_code,
|
|
||||||
{ 'function', 'nil' },
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
vim.validate({
|
vim.validate({
|
||||||
ansi = {
|
before_run = { cfg.hooks.before_run, { 'function', 'nil' }, true },
|
||||||
config.run_panel.ansi,
|
before_debug = { cfg.hooks.before_debug, { 'function', 'nil' }, true },
|
||||||
'boolean',
|
setup_code = { cfg.hooks.setup_code, { 'function', 'nil' }, true },
|
||||||
'ansi color parsing must be enabled xor disabled',
|
})
|
||||||
},
|
|
||||||
|
vim.validate({
|
||||||
|
ansi = { cfg.ui.run_panel.ansi, 'boolean' },
|
||||||
diff_mode = {
|
diff_mode = {
|
||||||
config.run_panel.diff_mode,
|
cfg.ui.run_panel.diff_mode,
|
||||||
function(value)
|
function(v)
|
||||||
return vim.tbl_contains({ 'none', 'vim', 'git' }, value)
|
return vim.tbl_contains({ 'none', 'vim', 'git' }, v)
|
||||||
end,
|
end,
|
||||||
"diff_mode must be 'none', 'vim', or 'git'",
|
"diff_mode must be 'none', 'vim', or 'git'",
|
||||||
},
|
},
|
||||||
max_output_lines = {
|
max_output_lines = {
|
||||||
config.run_panel.max_output_lines,
|
cfg.ui.run_panel.max_output_lines,
|
||||||
function(value)
|
function(v)
|
||||||
return type(value) == 'number' and value > 0 and value == math.floor(value)
|
return type(v) == 'number' and v > 0 and v == math.floor(v)
|
||||||
end,
|
end,
|
||||||
'max_output_lines must be a positive integer',
|
'positive integer',
|
||||||
},
|
},
|
||||||
|
git = { cfg.ui.diff.git, { 'table' } },
|
||||||
})
|
})
|
||||||
|
|
||||||
vim.validate({
|
for id, lang in pairs(cfg.languages) do
|
||||||
git = { config.diff.git, { 'table', 'nil' }, true },
|
validate_language(id, lang)
|
||||||
})
|
|
||||||
|
|
||||||
for _, platform_config in pairs(config.platforms) do
|
|
||||||
if not platform_config.default_language then
|
|
||||||
-- arbitrarily choose a language
|
|
||||||
platform_config.default_language = vim.tbl_keys(platform_config)[1]
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
build_runtime(cfg)
|
||||||
|
|
||||||
local ok, err = utils.check_required_runtime()
|
local ok, err = utils.check_required_runtime()
|
||||||
if not ok then
|
if not ok then
|
||||||
error('[cp.nvim] ' .. err)
|
error('[cp.nvim] ' .. err)
|
||||||
end
|
end
|
||||||
|
|
||||||
return config
|
current_config = cfg
|
||||||
|
return cfg
|
||||||
|
end
|
||||||
|
|
||||||
|
local current_config = nil
|
||||||
|
|
||||||
|
function M.set_current_config(config)
|
||||||
|
current_config = config
|
||||||
|
end
|
||||||
|
function M.get_config()
|
||||||
|
return current_config or M.defaults
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param contest_id string
|
---@param contest_id string
|
||||||
|
|
@ -204,25 +282,9 @@ end
|
||||||
local function default_filename(contest_id, problem_id)
|
local function default_filename(contest_id, problem_id)
|
||||||
if problem_id then
|
if problem_id then
|
||||||
return (contest_id .. problem_id):lower()
|
return (contest_id .. problem_id):lower()
|
||||||
else
|
|
||||||
return contest_id:lower()
|
|
||||||
end
|
end
|
||||||
|
return contest_id:lower()
|
||||||
end
|
end
|
||||||
|
|
||||||
M.default_filename = default_filename
|
M.default_filename = default_filename
|
||||||
|
|
||||||
local current_config = nil
|
|
||||||
|
|
||||||
--- Set the config
|
|
||||||
---@return nil
|
|
||||||
function M.set_current_config(config)
|
|
||||||
current_config = config
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Get the config
|
|
||||||
---@return cp.Config
|
|
||||||
function M.get_config()
|
|
||||||
return current_config or M.defaults
|
|
||||||
end
|
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
|
|
||||||
|
|
@ -242,7 +242,6 @@ end
|
||||||
---@return nil
|
---@return nil
|
||||||
function M.handle_compilation_failure(output)
|
function M.handle_compilation_failure(output)
|
||||||
local ansi = require('cp.ui.ansi')
|
local ansi = require('cp.ui.ansi')
|
||||||
local config = require('cp.config').setup()
|
|
||||||
|
|
||||||
local txt
|
local txt
|
||||||
local hl = {}
|
local hl = {}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
-- lua/cp/setup.lua
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
local cache = require('cp.cache')
|
local cache = require('cp.cache')
|
||||||
|
|
@ -17,9 +18,7 @@ function M.set_platform(platform)
|
||||||
)
|
)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
state.set_platform(platform)
|
state.set_platform(platform)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -112,15 +111,10 @@ function M.setup_problem(problem_id)
|
||||||
if vim.api.nvim_buf_get_lines(source_buf, 0, -1, true)[1] == '' then
|
if vim.api.nvim_buf_get_lines(source_buf, 0, -1, true)[1] == '' then
|
||||||
local has_luasnip, luasnip = pcall(require, 'luasnip')
|
local has_luasnip, luasnip = pcall(require, 'luasnip')
|
||||||
if has_luasnip then
|
if has_luasnip then
|
||||||
local filetype = vim.api.nvim_get_option_value('filetype', { buf = source_buf })
|
local prefixed_trigger = ('cp.nvim/%s.%s'):format(platform, language)
|
||||||
local language_name = constants.filetype_to_language[filetype]
|
|
||||||
local canonical_language = constants.canonical_filetypes[language_name] or language_name
|
|
||||||
local prefixed_trigger = ('cp.nvim/%s.%s'):format(platform, canonical_language)
|
|
||||||
|
|
||||||
vim.api.nvim_buf_set_lines(0, 0, -1, false, { prefixed_trigger })
|
vim.api.nvim_buf_set_lines(0, 0, -1, false, { prefixed_trigger })
|
||||||
vim.api.nvim_win_set_cursor(0, { 1, #prefixed_trigger })
|
vim.api.nvim_win_set_cursor(0, { 1, #prefixed_trigger })
|
||||||
vim.cmd.startinsert({ bang = true })
|
vim.cmd.startinsert({ bang = true })
|
||||||
|
|
||||||
vim.schedule(function()
|
vim.schedule(function()
|
||||||
if luasnip.expandable() then
|
if luasnip.expandable() then
|
||||||
luasnip.expand()
|
luasnip.expand()
|
||||||
|
|
@ -141,8 +135,7 @@ function M.setup_problem(problem_id)
|
||||||
vim.fn.expand('%:p'),
|
vim.fn.expand('%:p'),
|
||||||
platform,
|
platform,
|
||||||
state.get_contest_id() or '',
|
state.get_contest_id() or '',
|
||||||
state.get_problem_id(),
|
state.get_problem_id()
|
||||||
language
|
|
||||||
)
|
)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
@ -177,7 +170,6 @@ function M.navigate_problem(direction)
|
||||||
|
|
||||||
local problems = contest_data.problems
|
local problems = contest_data.problems
|
||||||
local index = contest_data.index_map[current_problem_id]
|
local index = contest_data.index_map[current_problem_id]
|
||||||
|
|
||||||
local new_index = index + direction
|
local new_index = index + direction
|
||||||
if new_index < 1 or new_index > #problems then
|
if new_index < 1 or new_index > #problems then
|
||||||
return
|
return
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue