From 2c25ec616afd3c3017e0bb6e46a667ceb8c4778e Mon Sep 17 00:00:00 2001 From: Barrett Ruth Date: Thu, 26 Feb 2026 22:54:32 -0500 Subject: [PATCH] feat: add per-language template file support Problem: new solution files were always created empty, requiring users to manually paste boilerplate or rely on editor snippets that fire outside cp.nvim's control. Solution: add an optional template field to the language config. When set to a file path, its contents are written into every newly created solution buffer before the setup_code hook runs. Existing files are never overwritten. --- lua/cp/config.lua | 5 +++++ lua/cp/setup.lua | 53 ++++++++++++++++++++++++++++++++++++----------- 2 files changed, 46 insertions(+), 12 deletions(-) diff --git a/lua/cp/config.lua b/lua/cp/config.lua index 4ed762b..6cf43d9 100644 --- a/lua/cp/config.lua +++ b/lua/cp/config.lua @@ -7,6 +7,7 @@ ---@class CpLanguage ---@field extension string ---@field commands CpLangCommands +---@field template? string ---@class CpPlatformOverrides ---@field extension? string @@ -215,6 +216,10 @@ local function validate_language(id, lang) commands = { lang.commands, { 'table' } }, }) + if lang.template ~= nil then + vim.validate({ template = { lang.template, 'string' } }) + end + if not lang.commands.run then error(('[cp.nvim] languages.%s.commands.run is required'):format(id)) end diff --git a/lua/cp/setup.lua b/lua/cp/setup.lua index 0572d55..86fdee1 100644 --- a/lua/cp/setup.lua +++ b/lua/cp/setup.lua @@ -8,6 +8,25 @@ local logger = require('cp.log') local scraper = require('cp.scraper') local state = require('cp.state') +local function apply_template(bufnr, lang_id, platform) + local config = config_module.get_config() + local eff = config.runtime.effective[platform] + and config.runtime.effective[platform][lang_id] + if not eff or not eff.template then + return + end + local path = vim.fn.expand(eff.template) + if vim.fn.filereadable(path) ~= 1 then + logger.log( + ('[cp.nvim] template not readable: %s'):format(path), + vim.log.levels.WARN + ) + return + end + local lines = vim.fn.readfile(path) + vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines) +end + ---Get the language of the current file from cache ---@return string? local function get_current_file_language() @@ -270,14 +289,17 @@ function M.setup_problem(problem_id, language) mods = { silent = true, noautocmd = true, keepalt = true }, }) state.set_solution_win(vim.api.nvim_get_current_win()) - if config.hooks and config.hooks.setup_code and not vim.b[prov.bufnr].cp_setup_done then - local ok = pcall(config.hooks.setup_code, state) - if ok then + if not vim.b[prov.bufnr].cp_setup_done then + apply_template(prov.bufnr, lang, platform) + if config.hooks and config.hooks.setup_code then + local ok = pcall(config.hooks.setup_code, state) + if ok then + vim.b[prov.bufnr].cp_setup_done = true + end + else + helpers.clearcol(prov.bufnr) vim.b[prov.bufnr].cp_setup_done = true end - elseif not vim.b[prov.bufnr].cp_setup_done then - helpers.clearcol(prov.bufnr) - vim.b[prov.bufnr].cp_setup_done = true end cache.set_file_state( vim.fn.fnamemodify(source_file, ':p'), @@ -300,14 +322,21 @@ function M.setup_problem(problem_id, language) local bufnr = vim.api.nvim_get_current_buf() state.set_solution_win(vim.api.nvim_get_current_win()) require('cp.ui.views').ensure_io_view() - if config.hooks and config.hooks.setup_code and not vim.b[bufnr].cp_setup_done then - local ok = pcall(config.hooks.setup_code, state) - if ok then + if not vim.b[bufnr].cp_setup_done then + local is_new = vim.api.nvim_buf_line_count(bufnr) == 1 + and vim.api.nvim_buf_get_lines(bufnr, 0, 1, false)[1] == '' + if is_new then + apply_template(bufnr, lang, platform) + end + if config.hooks and config.hooks.setup_code then + local ok = pcall(config.hooks.setup_code, state) + if ok then + vim.b[bufnr].cp_setup_done = true + end + else + helpers.clearcol(bufnr) vim.b[bufnr].cp_setup_done = true end - elseif not vim.b[bufnr].cp_setup_done then - helpers.clearcol(bufnr) - vim.b[bufnr].cp_setup_done = true end cache.set_file_state( vim.fn.expand('%:p'),