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,
|
||||
and language from cached state. Use this after
|
||||
switching files to restore your CP environment.
|
||||
Requires previous setup with full :CP command.
|
||||
|
||||
Setup Commands ~
|
||||
:CP {platform} {contest_id} {problem_id}
|
||||
Full setup: set platform, load contest metadata,
|
||||
and set up specific problem. Scrapes test cases
|
||||
and creates source file.
|
||||
:CP {platform} {contest_id}
|
||||
Full setup: set platform and load contest metadata.
|
||||
Scrapes test cases and creates source file.
|
||||
Example: >
|
||||
:CP codeforces 1933 a
|
||||
:CP codeforces 1933
|
||||
<
|
||||
:CP {platform} {contest_id}
|
||||
Contest setup: set platform, load contest metadata,
|
||||
|
|
@ -51,10 +49,6 @@ COMMANDS *cp-commands*
|
|||
Example: >
|
||||
:CP atcoder abc324
|
||||
:CP codeforces 1951
|
||||
<
|
||||
:CP {platform} Platform setup: set platform only.
|
||||
Example: >
|
||||
:CP cses
|
||||
<
|
||||
Action Commands ~
|
||||
:CP run [--debug] Toggle run panel for individual test case
|
||||
|
|
@ -64,12 +58,13 @@ COMMANDS *cp-commands*
|
|||
Requires contest setup first.
|
||||
|
||||
:CP pick Launch configured picker for interactive
|
||||
platform/contest/problem selection.
|
||||
platform/contest selection.
|
||||
|
||||
Navigation Commands ~
|
||||
:CP next Navigate to next problem in current contest.
|
||||
Stops at last problem (no wrapping).
|
||||
|
||||
|
||||
Navigation Commands ~
|
||||
:CP prev Navigate to previous problem in current contest.
|
||||
Stops at first problem (no wrapping).
|
||||
|
|
@ -78,7 +73,7 @@ COMMANDS *cp-commands*
|
|||
:CP cache clear [contest]
|
||||
Clear the cache data (contest list, problem
|
||||
data, file states) for the specified contest,
|
||||
or all contests if none specified
|
||||
or all contests if none specified.
|
||||
|
||||
:CP cache read
|
||||
View the cache in a pretty-printed lua buffer.
|
||||
|
|
@ -88,13 +83,16 @@ Command Flags ~
|
|||
*cp-flags*
|
||||
Flags can be used with setup and action commands:
|
||||
|
||||
--debug Enable debug compilation with additional flags.
|
||||
Uses the `debug` command template instead of
|
||||
`compile`. Typically includes debug symbols and
|
||||
sanitizers for memory error detection.
|
||||
--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 ~
|
||||
*cp-template-vars*
|
||||
Command templates support variable substitution using `{variable}` syntax:
|
||||
|
|
@ -105,7 +103,7 @@ Template Variables ~
|
|||
• {problem} Problem identifier (e.g. "a", "b")
|
||||
|
||||
Example template: >
|
||||
compile = { 'g++', '{source}', '-o', '{binary}', '-std=c++17' }
|
||||
build = { 'g++', '{source}', '-o', '{binary}', '-std=c++17' }
|
||||
< Would expand to: >
|
||||
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
|
||||
|
||||
{
|
||||
'barrett-ruth/cp.nvim',
|
||||
cmd = 'CP',
|
||||
build = 'uv sync',
|
||||
opts = {
|
||||
platforms = {
|
||||
cses = {
|
||||
cpp = {
|
||||
compile = { 'g++', '{source}', '-o', '{binary}',
|
||||
'-std=c++17', '-fdiagnostic-colors=always' },
|
||||
test = { '{binary}' },
|
||||
debug = { 'g++', '{source}', '-o', '{binary}',
|
||||
'-std=c++17', '-g',
|
||||
'-fdiagnostic-colors=always'
|
||||
'-fsanitize=address,undefined' },
|
||||
},
|
||||
python = {
|
||||
test = { 'python3', '{source}' },
|
||||
},
|
||||
},
|
||||
'barrett-ruth/cp.nvim',
|
||||
cmd = 'CP',
|
||||
build = 'uv sync',
|
||||
opts = {
|
||||
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}' },
|
||||
},
|
||||
snippets = {},
|
||||
debug = false,
|
||||
run_panel = {
|
||||
ansi = true,
|
||||
diff_mode = 'vim',
|
||||
max_output_lines = 50,
|
||||
},
|
||||
python = {
|
||||
extension = 'py',
|
||||
commands = {
|
||||
run = { 'python', '{source}' },
|
||||
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
|
||||
17. Python is also configured with the system executable python.
|
||||
By default, C++ (g++ with ISO C++17) and Python are preconfigured under
|
||||
`languages`. Platforms select which languages are enabled and which one is
|
||||
the default; per-platform overrides can tweak `extension` or `commands`.
|
||||
|
||||
For example, to run CodeForces contests with Python, only the following config
|
||||
is required:
|
||||
For example, to run CodeForces contests with Python by default:
|
||||
|
||||
>lua
|
||||
{
|
||||
platforms = {
|
||||
codeforces = {
|
||||
default_language = 'python'
|
||||
}
|
||||
}
|
||||
platforms = {
|
||||
codeforces = {
|
||||
enabled_languages = { 'cpp', 'python' },
|
||||
default_language = 'python',
|
||||
},
|
||||
},
|
||||
}
|
||||
<
|
||||
|
||||
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 = {
|
||||
codeforces = {
|
||||
rust = {
|
||||
compile = { 'rustc', '{source}', '-o', '{binary}' },
|
||||
test = { 'binary' },
|
||||
extension = 'rs'
|
||||
}
|
||||
}
|
||||
}
|
||||
languages = {
|
||||
rust = {
|
||||
extension = 'rs',
|
||||
commands = {
|
||||
build = { 'rustc', '{source}', '-o', '{binary}' },
|
||||
run = { '{binary}' },
|
||||
},
|
||||
},
|
||||
},
|
||||
platforms = {
|
||||
cses = {
|
||||
enabled_languages = { 'cpp', 'python', 'rust' },
|
||||
default_language = 'rust',
|
||||
},
|
||||
},
|
||||
}
|
||||
<
|
||||
|
||||
*cp.Config*
|
||||
Fields: ~
|
||||
{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.
|
||||
{snippets} (table[]) LuaSnip snippet definitions.
|
||||
{debug} (boolean, default: false) Show info messages
|
||||
during operation.
|
||||
{run_panel} (|RunPanelConfig|) Test panel behavior configuration.
|
||||
{diff} (|DiffConfig|) Diff backend configuration.
|
||||
{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)
|
||||
{debug} (boolean, default: false) Show info messages.
|
||||
{scrapers} (string[]) Supported platform ids.
|
||||
{filename} (function, optional)
|
||||
function(contest, contest_id, problem_id, config, language): string
|
||||
Should return full filename with extension.
|
||||
(default: concatenates contest_id and problem_id, lowercased)
|
||||
{ui} (|CpUI|) UI settings: run panel, diff backend, picker.
|
||||
|
||||
*cp.PlatformConfig*
|
||||
Fields: ~
|
||||
{cpp} (|PlatformLanguageConfig|) C++ language configuration.
|
||||
{python} (|PlatformLanguageConfig|) Python language configuration.
|
||||
{default_language} (string, default: "cpp") Default language for
|
||||
platform contests.
|
||||
Replaced by |CpPlatform|. Platforms no longer inline language tables.
|
||||
|
||||
*cp.LanguageConfig*
|
||||
*CpPlatform*
|
||||
Fields: ~
|
||||
{compile} (string[], optional) Compile command template with
|
||||
{source}, {binary} placeholders.
|
||||
{test} (string[]) Test execution command template.
|
||||
{debug} (string[], optional) Debug compile command template.
|
||||
{executable} (string, optional) Executable name for interpreted languages.
|
||||
{enabled_languages} (string[]) Language ids enabled on this platform.
|
||||
{default_language} (string) One of {enabled_languages}.
|
||||
{overrides} (table<string,|CpPlatformOverrides|>, optional)
|
||||
Per-language overrides of {extension} and/or {commands}.
|
||||
|
||||
*CpLanguage*
|
||||
Fields: ~
|
||||
{extension} (string) File extension without leading dot.
|
||||
{commands} (|CpLangCommands|) Command templates.
|
||||
|
||||
*CpLangCommands*
|
||||
Fields: ~
|
||||
{build} (string[], optional) For compiled languages.
|
||||
Must include {source} and {binary}.
|
||||
{run} (string[], optional) Runtime command.
|
||||
Compiled: must include {binary}.
|
||||
Interpreted: must include {source}.
|
||||
{debug} (string[], optional) Debug variant; same token rules
|
||||
as {build} (compiled) or {run} (interpreted).
|
||||
|
||||
*CpUI*
|
||||
Fields: ~
|
||||
{run_panel} (|RunPanelConfig|) Test panel behavior configuration.
|
||||
{diff} (|DiffConfig|) Diff backend configuration.
|
||||
{picker} (string|nil) 'telescope', 'fzf-lua', or nil.
|
||||
|
||||
*cp.RunPanelConfig*
|
||||
Fields: ~
|
||||
{ansi} (boolean, default: true) Enable ANSI color parsing and
|
||||
highlighting. When true, compiler output and test results
|
||||
display with colored syntax highlighting. When false,
|
||||
ANSI escape codes are stripped for plain text display.
|
||||
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.
|
||||
{ansi} (boolean, default: true) Enable ANSI color parsing
|
||||
and highlighting.
|
||||
{diff_mode} (string, default: "none") Diff backend: "none",
|
||||
"vim", or "git".
|
||||
{max_output_lines} (number, default: 50) Maximum lines of test output.
|
||||
|
||||
*cp.DiffConfig*
|
||||
|
|
@ -247,10 +289,9 @@ run CSES problems with Rust:
|
|||
Fields: ~
|
||||
{before_run} (function, optional) Called before test panel opens.
|
||||
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)
|
||||
{setup_code} (function, optional) Called after source file is opened.
|
||||
Good for configuring buffer settings.
|
||||
function(state: cp.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
|
||||
|
||||
Usage examples: >
|
||||
:CP atcoder abc324 a " Full setup: problem A from contest ABC324
|
||||
: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 ~
|
||||
*cp-codeforces*
|
||||
URL format: https://codeforces.com/contest/1234/problem/A
|
||||
|
||||
Usage examples: >
|
||||
:CP codeforces 1934 a " Full setup: problem A from contest 1934
|
||||
: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 ~
|
||||
*cp-cses*
|
||||
URL format: https://cses.fi/problemset/task/1068
|
||||
|
||||
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
|
||||
<
|
||||
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 contest_id string
|
||||
---@param problem_id? string
|
||||
---@param language? string
|
||||
function M.set_file_state(file_path, platform, contest_id, problem_id, language)
|
||||
function M.set_file_state(file_path, platform, contest_id, problem_id)
|
||||
if not cache_data.file_states then
|
||||
cache_data.file_states = {}
|
||||
end
|
||||
|
|
@ -232,7 +231,6 @@ function M.set_file_state(file_path, platform, contest_id, problem_id, language)
|
|||
platform = platform,
|
||||
contest_id = contest_id,
|
||||
problem_id = problem_id,
|
||||
language = language,
|
||||
}
|
||||
|
||||
M.save()
|
||||
|
|
|
|||
|
|
@ -1,201 +1,279 @@
|
|||
---@class PlatformLanguageConfig
|
||||
---@field compile? string[] Compile command template
|
||||
---@field test string[] Test execution command template
|
||||
---@field debug? string[] Debug command template
|
||||
---@field executable? string Executable name
|
||||
---@field extension? string File extension
|
||||
-- lua/cp/config.lua
|
||||
---@class CpLangCommands
|
||||
---@field build? string[]
|
||||
---@field run? string[]
|
||||
---@field debug? string[]
|
||||
|
||||
---@class PlatformConfig
|
||||
---@field [string] PlatformLanguageConfig
|
||||
---@field default_language? string
|
||||
---@class CpLanguage
|
||||
---@field extension 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
|
||||
---@field before_run? fun(state: cp.State)
|
||||
---@field before_debug? fun(state: cp.State)
|
||||
---@field setup_code? fun(state: cp.State)
|
||||
|
||||
---@class RunPanelConfig
|
||||
---@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
|
||||
---@class CpUI
|
||||
---@field run_panel RunPanelConfig
|
||||
---@field diff DiffConfig
|
||||
---@field picker string|nil
|
||||
|
||||
---@class cp.PartialConfig
|
||||
---@field platforms? table<string, PlatformConfig>
|
||||
---@field snippets? any[]
|
||||
---@field hooks? Hooks
|
||||
---@field debug? boolean
|
||||
---@class cp.Config
|
||||
---@field languages table<string, CpLanguage>
|
||||
---@field platforms table<string, CpPlatform>
|
||||
---@field hooks Hooks
|
||||
---@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 run_panel? RunPanelConfig
|
||||
---@field diff? DiffConfig
|
||||
---@field picker? string|nil
|
||||
---@field ui CpUI
|
||||
---@field runtime { effective: table<string, table<string, CpLanguage>> } -- computed
|
||||
|
||||
---@class cp.PartialConfig: cp.Config
|
||||
|
||||
local M = {}
|
||||
|
||||
local constants = require('cp.constants')
|
||||
local utils = require('cp.utils')
|
||||
|
||||
local default_contest_config = {
|
||||
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',
|
||||
}
|
||||
|
||||
-- defaults per the new single schema
|
||||
---@type cp.Config
|
||||
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 = {
|
||||
codeforces = default_contest_config,
|
||||
atcoder = default_contest_config,
|
||||
cses = default_contest_config,
|
||||
codeforces = {
|
||||
enabled_languages = { 'cpp', 'python' },
|
||||
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 = {},
|
||||
hooks = {
|
||||
before_run = nil,
|
||||
before_debug = nil,
|
||||
setup_code = nil,
|
||||
},
|
||||
hooks = { before_run = nil, before_debug = nil, setup_code = nil },
|
||||
debug = false,
|
||||
scrapers = constants.PLATFORMS,
|
||||
filename = nil,
|
||||
run_panel = {
|
||||
ansi = true,
|
||||
diff_mode = 'none',
|
||||
max_output_lines = 50,
|
||||
},
|
||||
diff = {
|
||||
git = {
|
||||
args = { 'diff', '--no-index', '--word-diff=plain', '--word-diff-regex=.', '--no-prefix' },
|
||||
ui = {
|
||||
run_panel = { ansi = true, diff_mode = 'none', max_output_lines = 50 },
|
||||
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
|
||||
---@return cp.Config
|
||||
function M.setup(user_config)
|
||||
vim.validate({
|
||||
user_config = { user_config, { 'table', 'nil' }, true },
|
||||
})
|
||||
|
||||
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({ user_config = { user_config, { 'table', 'nil' }, true } })
|
||||
local cfg = vim.tbl_deep_extend('force', vim.deepcopy(M.defaults), user_config or {})
|
||||
|
||||
vim.validate({
|
||||
before_run = {
|
||||
config.hooks.before_run,
|
||||
{ 'function', 'nil' },
|
||||
true,
|
||||
},
|
||||
before_debug = {
|
||||
config.hooks.before_debug,
|
||||
{ 'function', 'nil' },
|
||||
true,
|
||||
},
|
||||
setup_code = {
|
||||
config.hooks.setup_code,
|
||||
{ 'function', 'nil' },
|
||||
true,
|
||||
},
|
||||
hooks = { cfg.hooks, { 'table' } },
|
||||
ui = { cfg.ui, { 'table' } },
|
||||
})
|
||||
|
||||
vim.validate({
|
||||
ansi = {
|
||||
config.run_panel.ansi,
|
||||
'boolean',
|
||||
'ansi color parsing must be enabled xor disabled',
|
||||
},
|
||||
before_run = { cfg.hooks.before_run, { 'function', 'nil' }, true },
|
||||
before_debug = { cfg.hooks.before_debug, { 'function', 'nil' }, true },
|
||||
setup_code = { cfg.hooks.setup_code, { 'function', 'nil' }, true },
|
||||
})
|
||||
|
||||
vim.validate({
|
||||
ansi = { cfg.ui.run_panel.ansi, 'boolean' },
|
||||
diff_mode = {
|
||||
config.run_panel.diff_mode,
|
||||
function(value)
|
||||
return vim.tbl_contains({ 'none', 'vim', 'git' }, value)
|
||||
cfg.ui.run_panel.diff_mode,
|
||||
function(v)
|
||||
return vim.tbl_contains({ 'none', 'vim', 'git' }, v)
|
||||
end,
|
||||
"diff_mode must be 'none', 'vim', or 'git'",
|
||||
},
|
||||
max_output_lines = {
|
||||
config.run_panel.max_output_lines,
|
||||
function(value)
|
||||
return type(value) == 'number' and value > 0 and value == math.floor(value)
|
||||
cfg.ui.run_panel.max_output_lines,
|
||||
function(v)
|
||||
return type(v) == 'number' and v > 0 and v == math.floor(v)
|
||||
end,
|
||||
'max_output_lines must be a positive integer',
|
||||
'positive integer',
|
||||
},
|
||||
git = { cfg.ui.diff.git, { 'table' } },
|
||||
})
|
||||
|
||||
vim.validate({
|
||||
git = { config.diff.git, { 'table', 'nil' }, true },
|
||||
})
|
||||
|
||||
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
|
||||
for id, lang in pairs(cfg.languages) do
|
||||
validate_language(id, lang)
|
||||
end
|
||||
|
||||
build_runtime(cfg)
|
||||
|
||||
local ok, err = utils.check_required_runtime()
|
||||
if not ok then
|
||||
error('[cp.nvim] ' .. err)
|
||||
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
|
||||
|
||||
---@param contest_id string
|
||||
|
|
@ -204,25 +282,9 @@ end
|
|||
local function default_filename(contest_id, problem_id)
|
||||
if problem_id then
|
||||
return (contest_id .. problem_id):lower()
|
||||
else
|
||||
return contest_id:lower()
|
||||
end
|
||||
return contest_id:lower()
|
||||
end
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -242,7 +242,6 @@ end
|
|||
---@return nil
|
||||
function M.handle_compilation_failure(output)
|
||||
local ansi = require('cp.ui.ansi')
|
||||
local config = require('cp.config').setup()
|
||||
|
||||
local txt
|
||||
local hl = {}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
-- lua/cp/setup.lua
|
||||
local M = {}
|
||||
|
||||
local cache = require('cp.cache')
|
||||
|
|
@ -17,9 +18,7 @@ function M.set_platform(platform)
|
|||
)
|
||||
return false
|
||||
end
|
||||
|
||||
state.set_platform(platform)
|
||||
|
||||
return true
|
||||
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
|
||||
local has_luasnip, luasnip = pcall(require, 'luasnip')
|
||||
if has_luasnip then
|
||||
local filetype = vim.api.nvim_get_option_value('filetype', { buf = source_buf })
|
||||
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)
|
||||
|
||||
local prefixed_trigger = ('cp.nvim/%s.%s'):format(platform, language)
|
||||
vim.api.nvim_buf_set_lines(0, 0, -1, false, { prefixed_trigger })
|
||||
vim.api.nvim_win_set_cursor(0, { 1, #prefixed_trigger })
|
||||
vim.cmd.startinsert({ bang = true })
|
||||
|
||||
vim.schedule(function()
|
||||
if luasnip.expandable() then
|
||||
luasnip.expand()
|
||||
|
|
@ -141,8 +135,7 @@ function M.setup_problem(problem_id)
|
|||
vim.fn.expand('%:p'),
|
||||
platform,
|
||||
state.get_contest_id() or '',
|
||||
state.get_problem_id(),
|
||||
language
|
||||
state.get_problem_id()
|
||||
)
|
||||
end)
|
||||
end
|
||||
|
|
@ -177,7 +170,6 @@ function M.navigate_problem(direction)
|
|||
|
||||
local problems = contest_data.problems
|
||||
local index = contest_data.index_map[current_problem_id]
|
||||
|
||||
local new_index = index + direction
|
||||
if new_index < 1 or new_index > #problems then
|
||||
return
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue