feat(doc): update for new config

This commit is contained in:
Barrett Ruth 2025-10-04 19:04:49 -04:00
parent 2c0e808c8c
commit a76d228e3f
5 changed files with 369 additions and 297 deletions

View file

@ -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.
==============================================================================

View file

@ -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()

View file

@ -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

View file

@ -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 = {}

View file

@ -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