diff --git a/doc/cp.nvim.txt b/doc/cp.nvim.txt index adc5670..0dfc82d 100644 --- a/doc/cp.nvim.txt +++ b/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) Contest configurations. + {languages} (table) Global language registry. + Each language provides an {extension} and {commands}. + {platforms} (table) 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, 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: "") 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. ============================================================================== diff --git a/lua/cp/cache.lua b/lua/cp/cache.lua index a520a1f..7279b20 100644 --- a/lua/cp/cache.lua +++ b/lua/cp/cache.lua @@ -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() diff --git a/lua/cp/config.lua b/lua/cp/config.lua index 21686f1..738f1a6 100644 --- a/lua/cp/config.lua +++ b/lua/cp/config.lua @@ -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 + +---@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 ----@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 ----@field snippets? any[] ----@field hooks? Hooks ----@field debug? boolean +---@class cp.Config +---@field languages table +---@field platforms table +---@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> } -- 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 diff --git a/lua/cp/runner/run.lua b/lua/cp/runner/run.lua index 03857f6..21d0d55 100644 --- a/lua/cp/runner/run.lua +++ b/lua/cp/runner/run.lua @@ -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 = {} diff --git a/lua/cp/setup.lua b/lua/cp/setup.lua index afab845..fa6b8b0 100644 --- a/lua/cp/setup.lua +++ b/lua/cp/setup.lua @@ -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