From 1c4a61ce7f39cb5a2c98912862bd91e0b05ea5b0 Mon Sep 17 00:00:00 2001 From: Andrew Haynes Date: Fri, 23 Oct 2026 08:25:21 -0400 Subject: [PATCH] CHORE: Fixed bug #4 by looping dirs and creating directories before other loops in creation. LANG: Added CPP and bugfixed c to use c code and not python --- lua/vern/actions/c.lua | 43 +++++++------ lua/vern/actions/cpp.lua | 102 +++++++++++++++++++++++++++++++ lua/vern/actions/utils.lua | 6 ++ lua/vern/attach/c.lua | 10 +-- lua/vern/attach/cpp.lua | 26 ++++++++ lua/vern/configs/cpp.lua | 67 ++++++++++++++++++++ lua/vern/templates/c.lua | 17 ++++-- lua/vern/templates/cpp.lua | 122 +++++++++++++++++++++++++++++++++++++ lua/vern/types.lua | 7 ++- lua/vern/utils.lua | 15 ++++- 10 files changed, 382 insertions(+), 33 deletions(-) create mode 100644 lua/vern/actions/cpp.lua create mode 100644 lua/vern/attach/cpp.lua create mode 100644 lua/vern/configs/cpp.lua create mode 100644 lua/vern/templates/cpp.lua diff --git a/lua/vern/actions/c.lua b/lua/vern/actions/c.lua index ff6bf3e..c063abd 100644 --- a/lua/vern/actions/c.lua +++ b/lua/vern/actions/c.lua @@ -19,7 +19,9 @@ function M.create_project(opts) ---@type VernCConfig local cfg = vim.tbl_deep_extend("force", {}, configs, configs, opts or {}) local root = join(vim.fn.expand(cfg.workspace_root), cfg.c.root, pname) + local build_cmd = "cd " .. root .. " && cmake -S . -B build" local bin = action_utils.create(cfg.c, pname, root) + local _ = vim.system({ "sh", "-c", build_cmd }):wait() utils.init_git(root, "c") vim.cmd.edit(vim.fn.fnameescape(bin)) @@ -66,31 +68,36 @@ function M.create_file(opts) end) end ---- Goal is to add providers for uv and venv so its available to more users ----@param opts? table Takes a table for the runtime +---@param opts? VernCConfig Takes a table for the runtime function M.run(opts) opts = opts or {} local root = utils.proj_root() or "." local pname = vim.fs.basename(root) - local exe = opts.python.templates.project.bin.target or "src/%s/main.py" - local full_path = join(vim.fn.expand(root), utils.fmt(exe, { project_name = pname })) - utils.term({ "python3 " .. full_path }, opts.terminal or {}) -end + local build_dir = root .. "/build" + local build_cmd = "cd " .. build_dir .. " && cmake --build ." ----@param cmd_args? table Takes a table for the runtime -function M.pip_install(opts, cmd_args) - local args = cmd_args and cmd_args.args and vim.trim(cmd_args.args) or "" - if args ~= "" then - utils.term({ "pip install " .. args }, opts.terminal or {}) - return - end - vim.ui.input({ prompt = "Enter pip module: " }, function(pip_module) - if not pip_module or vim.trim(pip_module) == "" then - vim.notify("Pip Cancelled", vim.log.levels.WARN) - return + vim.system({ "sh", "-c", build_cmd }, { text = true }, function(obj) + if obj.code ~= 0 then + vim.schedule(function() + local error_obj = (obj.stderr ~= "" and obj.stderr) or obj.stdout or "" + local error_lines = vim.list_extend( + { "Error During compilation" }, + vim.split(error_obj, "\n", { trimempty = true }) + ) + local buf, _ = utils.term({ "echo 'compilation failed'" }, opts.terminal, true) + if buf and vim.api.nvim_buf_is_valid(buf) then + vim.api.nvim_set_option_value("modifiable", true, { buf = buf }) + vim.api.nvim_buf_set_lines(buf, 0, -1, false, error_lines) + vim.api.nvim_set_option_value("modifiable", false, { buf = buf }) + end + end) + else + vim.schedule(function() + local run_cmd = "cd '" .. build_dir .. "' && ./" .. pname + utils.term({ run_cmd }, opts.terminal, false) + end) end - utils.term({ "pip install " .. vim.trim(pip_module) }, opts.terminal or {}) end) end diff --git a/lua/vern/actions/cpp.lua b/lua/vern/actions/cpp.lua new file mode 100644 index 0000000..e743000 --- /dev/null +++ b/lua/vern/actions/cpp.lua @@ -0,0 +1,102 @@ +local M = {} + +local utils = require("vern.utils") +local templates = require("vern.templates") +local action_utils = require("vern.actions.utils") + +local join = vim.fs.joinpath + +---@param opts VernCPPConfig +function M.create_project(opts) + vim.ui.input({ prompt = "Enter project name: " }, function(pname) + pname = pname and vim.trim(pname) + if not pname or pname == "" then + return + end + + local configs = require("vern.configs.cpp") + + ---@type VernCPPConfig + local cfg = vim.tbl_deep_extend("force", {}, configs, configs, opts or {}) + local root = join(vim.fn.expand(cfg.workspace_root), cfg.cpp.root, pname) + local bin = action_utils.create(cfg.cpp, pname, root) + utils.init_git(root, "c") + + vim.cmd.edit(vim.fn.fnameescape(bin)) + end) +end + +function M.create_file(opts) + vim.ui.input({ prompt = "Enter type (c/h): " }, function(ft) + ft = ft and vim.trim(ft) + if not ft or ft == "" then + return + end + local configs = require("vern.configs.c") + ---@type VernCPPConfig + local cfg = vim.tbl_deep_extend("force", {}, configs, opts or {}) + vim.ui.input({ prompt = "Enter file name: " }, function(fname) + local root = join(vim.fn.expand(cfg.workspace_root), cfg.cpp.root, utils.proj_name()) + local input_txt = fname + ft = ft and vim.trim(ft) + if not ft or ft == "" then + return + end + local mod + if ft == "c" then + mod = cfg.cpp.templates.file.source or {} + end + if ft == "h" then + mod = cfg.cpp.templates.file.header or {} + input_txt = fname:upper() + end + local raw_path = join(mod.path, mod.name) + local context = { project_name = utils.proj_name(), name = fname } + local path = utils.resolve_path(raw_path, cfg.cpp.dirs or {}, context) + local abs_file_path = vim.fs.normalize(join(root, path)) + if ft == "h" then + local abs_dir_path = vim.fs.dirname(abs_file_path) + if abs_dir_path ~= root and not vim.startswith(root, abs_dir_path) then + vim.fn.mkdir(abs_dir_path, "p") + end + end + utils.dump(abs_file_path, templates.fetch(mod.kind_path, mod.kind, input_txt)) + vim.cmd.edit(vim.fn.expand(abs_file_path)) + end) + end) +end + +---@param opts? VernCPPConfig Takes a table for the runtime +function M.run(opts) + opts = opts or {} + local root = utils.proj_root() or "." + local pname = vim.fs.basename(root) + + local build_dir = root .. "/build" + local build_cmd = "cd " .. build_dir .. " && cmake --build ." + + vim.system({ "sh", "-c", build_cmd }, { text = true }, function(obj) + if obj.code ~= 0 then + vim.schedule(function() + local error_obj = (obj.stderr ~= "" and obj.stderr) or obj.stdout or "" + local error_lines = vim.list_extend( + { "Error During compilation" }, + vim.split(error_obj, "\n", { trimempty = true }) + ) + local buf, _ = utils.term({ "echo 'compilation failed'" }, opts.terminal, true) + if buf and vim.api.nvim_buf_is_valid(buf) then + vim.api.nvim_set_option_value("modifiable", true, { buf = buf }) + vim.api.nvim_buf_set_lines(buf, 0, -1, false, error_lines) + vim.api.nvim_set_option_value("modifiable", false, { buf = buf }) + end + end) + else + vim.schedule(function() + local run_cmd = "cd '" .. build_dir .. "' && ./" .. pname + utils.term({ run_cmd }, opts.terminal, false) + end) + end + end) +end + +return M diff --git a/lua/vern/actions/utils.lua b/lua/vern/actions/utils.lua index 39b24a3..663dbd9 100644 --- a/lua/vern/actions/utils.lua +++ b/lua/vern/actions/utils.lua @@ -14,6 +14,12 @@ function M.create(cfg, pname, root) vim.fn.mkdir(root, "p") local bin + + for _, dir in pairs(cfg.dirs) do + local path = vim.fs.normalize(join(root, utils.fmt(dir, ctx))) + vim.fn.mkdir(path, "p") + end + for tbl_name, tbl in pairs(cfg.templates.project) do local raw_path = join(tbl.path, tbl.name) local path = utils.resolve_path(raw_path, cfg.dirs, ctx) diff --git a/lua/vern/attach/c.lua b/lua/vern/attach/c.lua index 97b3d55..9e57b38 100644 --- a/lua/vern/attach/c.lua +++ b/lua/vern/attach/c.lua @@ -1,10 +1,10 @@ -local actions = require("vern.actions.python") -local config = require("vern.configs.python") +local actions = require("vern.actions.c") +local config = require("vern.configs.c") ---@param bufnr integer Buffer to activate on : defaults to 0 ----@param user_opts? VernPythonConfig +---@param user_opts? VernCConfig return function(bufnr, user_opts) - ---@type VernPythonConfig + ---@type VernCConfig local opts = vim.tbl_deep_extend("force", {}, config, user_opts or {}) vim.api.nvim_buf_create_user_command(bufnr, "CRun", function() if type(actions.run) == "function" then @@ -12,7 +12,7 @@ return function(bufnr, user_opts) end end, { desc = "Run c project" }) - for key, def in pairs(opts.python.keybinds or {}) do + for key, def in pairs(opts.c.keybinds or {}) do if type(def.action) == "function" then vim.keymap.set("n", key, function() def.action(opts) diff --git a/lua/vern/attach/cpp.lua b/lua/vern/attach/cpp.lua new file mode 100644 index 0000000..37453eb --- /dev/null +++ b/lua/vern/attach/cpp.lua @@ -0,0 +1,26 @@ +local actions = require("vern.actions.cpp") +local config = require("vern.configs.cpp") + +---@param bufnr integer Buffer to activate on : defaults to 0 +---@param user_opts? VernCPPConfig +return function(bufnr, user_opts) + ---@type VernCPPConfig + local opts = vim.tbl_deep_extend("force", {}, config, user_opts or {}) + vim.api.nvim_buf_create_user_command(bufnr, "CPPRun", function() + if type(actions.run) == "function" then + actions.run(opts) + end + end, { desc = "Run cpp project" }) + + for key, def in pairs(opts.cpp.keybinds or {}) do + if type(def.action) == "function" then + vim.keymap.set("n", key, function() + def.action(opts) + end, { + buffer = bufnr, + silent = true, + desc = def.desc, + }) + end + end +end diff --git a/lua/vern/configs/cpp.lua b/lua/vern/configs/cpp.lua new file mode 100644 index 0000000..ede23fd --- /dev/null +++ b/lua/vern/configs/cpp.lua @@ -0,0 +1,67 @@ +local M = {} + +---@type VernLanguageConfig +M.cpp = { + root = "CPP", + dirs = { + src = "src", + include = "include/{project_name}", + }, + keybinds = { + ["pr"] = { + desc = "Executes cpp code", + action = require("vern.actions.cpp").run, + }, + ["pf"] = { + desc = "Creates source/header files", + action = require("vern.actions.cpp").create_file, + }, + }, + templates = { + project = { + primary = { + path = "{dirs.src}", + name = "main.cpp", + kind = "main", + kind_path = "vern.templates.cpp", + }, + manifest = { + path = ".", + name = "CMakeLists.txt", + kind = "CmakeLists", + kind_path = "vern.templates.cpp", + }, + style = { + path = ".", + name = ".clang-format", + kind = "format", + kind_path = "vern.templates.cpp", + }, + }, + file = { + header = { + path = "{dirs.include}", + name = "{name}.hpp", + kind = "header", + kind_path = "vern.templates.cpp", + }, + source = { + path = "{dirs.src}", + name = "{name}.c", + kind = "source", + kind_path = "vern.templates.cpp", + }, + }, + extras = { + linter = { + path = ".", + name = ".clang-tidy", + kind = "linter", + kind_path = "vern.templates.c", + enabled = false, + }, + }, + }, +} + +return M diff --git a/lua/vern/templates/c.lua b/lua/vern/templates/c.lua index 27eccb5..cecae62 100644 --- a/lua/vern/templates/c.lua +++ b/lua/vern/templates/c.lua @@ -59,21 +59,26 @@ set(CMAKE_C_STANDARD 23) set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -add_library(__PROJECT_NAME__ STATIC +set(LIB_SOURCES # Add your other types here as you implement them: # src/example1.c # src/example2.c ) -target_include_directories(__PROJECT_NAME__ - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/include -) +if(LIB_SOURCES) + add_library(__PROJECT_NAME__ STATIC ${LIB_SOURCES}) + target_include_directories(%s + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include + ) +endif() add_executable(__PROJECT_NAME___bin src/main.c) set_target_properties(__PROJECT_NAME___bin PROPERTIES OUTPUT_NAME "__PROJECT_NAME__") -target_link_libraries(__PROJECT_NAME___bin PRIVATE __PROJECT_NAME__) +if(TARGET __PROJECT_NAME__) + target_link_libraries(__PROJECT_NAME___bin PRIVATE __PROJECT_NAME__) +endif() ]=]) .. "\n", --[[ ===================================================================== diff --git a/lua/vern/templates/cpp.lua b/lua/vern/templates/cpp.lua new file mode 100644 index 0000000..27eccb5 --- /dev/null +++ b/lua/vern/templates/cpp.lua @@ -0,0 +1,122 @@ +return { + --[[ ===================================================================== + TEMPLATE: MAIN (Entrypoint) + File: main.c + Target: src/{project_name}/main.py + ===================================================================== --]] + main = vim.trim([=[ +#include + +int +main() +{ + char* str = "Hello World"; + printf("%s\n", str); + return 0; +} +]=]) .. "\n", + + --[[ ===================================================================== + TEMPLATE: INIT (Package Anchor) + File: __init__.py + Target: src/{project_name}/__init__.py + ===================================================================== --]] + format = vim.trim([=[ +--- +BasedOnStyle: Google +Standard: Latest +UseTab: Always +TabWidth: 8 +IndentWidth: 8 +BreakBeforeBraces: Allman +SpaceBeforeCpp11BracedList: true +SeparateDefinitionBlocks: Always +Cpp11BracedListStyle: false +BreakAfterReturnType: TopLevel +ReflowComments: Always +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: AllIfsAndElse +AllowShortLoopsOnASingleLine: true +IndentAccessModifiers: false # Indent access specifiers by one level +AccessModifierOffset: -4 # Optional: adjust offset relative to the class indentation level +IndentGotoLabels: InnerIndent +DerivePointerAlignment: false +PointerAlignment: Left +IndentPPDirectives: AfterHash +SortIncludes: CaseSensitive +IncludeBlocks: Preserve +]=]) .. "\n", + + --[[ ===================================================================== + TEMPLATE: PYPROJECT (Spec / Build Manifest) + File: pyproject.toml + Target: pyproject.toml + ===================================================================== --]] + CmakeLists = vim.trim([=[ +cmake_minimum_required(VERSION 3.16) +project(__PROJECT_NAME__ LANGUAGES C) +set(CMAKE_C_STANDARD 23) +set(CMAKE_C_STANDARD_REQUIRED ON) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +add_library(__PROJECT_NAME__ STATIC + # Add your other types here as you implement them: + # src/example1.c + # src/example2.c +) + +target_include_directories(__PROJECT_NAME__ + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include +) + +add_executable(__PROJECT_NAME___bin src/main.c) +set_target_properties(__PROJECT_NAME___bin PROPERTIES OUTPUT_NAME "__PROJECT_NAME__") + +target_link_libraries(__PROJECT_NAME___bin PRIVATE __PROJECT_NAME__) +]=]) .. "\n", + + --[[ ===================================================================== + TEMPLATE: MODULE (Standalone File) + File: {name}.py + Target: src/{project_name}/{name}.py + ===================================================================== --]] + header = vim.trim([=[ +#ifndef __PROJECT_NAME___H +#define __PROJECT_NAME___H + +// Code + +#endif /* __PROJECT_NAME___H */ +]=]) .. "\n", + + source = vim.trim([=[ +]=]) .. "\n", + + linter = vim.trim([=[ +Checks: > + -*, + clang-analyzer-*, + bugprone-*, + -bugprone-easily-swappable-parameters, + -bugprone-reserved-identifier, + performance-*, + security-* + + readability-redundant-declaration + readability-misleading-indentation + readability-implicit-bool-conversion + misc-misplaced-const, + misc-include-cleaner + + -bugprone-narrowing-conversions, + -readability-identifier-length +CheckOptions: + readability-identifier-naming.VariableCase: lower_case + readability-identifier-naming.FunctionCase: lower_case + readability-identifier-naming.StructCase: lower_case + readability-identifier-naming.EnumCase: CamelCase + readability-identifier-naming.EnumConstantCase: UPPER_CASE + readability-identifier-naming.MacroDefinitionCase: UPPER_CASE +]=]) .. "\n", +} diff --git a/lua/vern/types.lua b/lua/vern/types.lua index d62d2ce..9c874b8 100644 --- a/lua/vern/types.lua +++ b/lua/vern/types.lua @@ -52,7 +52,10 @@ ---@field templates VernLanguageTemplates ---@class VernPythonConfig : VernCoreConfig ----@field python VernLanguageConfig +---@field python? VernLanguageConfig ---@class VernCConfig : VernCoreConfig ----@field c VernLanguageConfig +---@field c? VernLanguageConfig +--- +---@class VernCPPConfig : VernCoreConfig +---@field cpp? VernLanguageConfig diff --git a/lua/vern/utils.lua b/lua/vern/utils.lua index 26cd688..4b9c90e 100644 --- a/lua/vern/utils.lua +++ b/lua/vern/utils.lua @@ -8,6 +8,9 @@ function M.dump(path, content) file:close() end +---@param str string String to replace +---@param ctx table Replacment characters +---@return string function M.fmt(str, ctx) return (str:gsub("{([^}]+)}", ctx)) end @@ -70,8 +73,9 @@ end ---@param cmd table ---@param opts VernTerminalConfig +---@param scratch boolean ---@return integer, integer? -function M.term(cmd, opts) +function M.term(cmd, opts, scratch) opts = opts or {} local buf, win if opts.mode.type == "float" then @@ -109,6 +113,9 @@ function M.term(cmd, opts) style = "minimal", border = opts.mode.win.float.border.enabled and opts.mode.win.float.border.type or nil, }) + if scratch then + return buf, win + end end if opts.mode.type == "split" then if opts.mode.win.split.direction == "vertical" then @@ -128,8 +135,12 @@ function M.term(cmd, opts) win = vim.api.nvim_get_current_win() vim.api.nvim_win_set_buf(0, buf) + if scratch then + return buf, win + end end - vim.fn.jobstart(table.concat(cmd, "/"), { term = true }) + local full_cmd = type(cmd) == "table" and table.concat(cmd, " ") or cmd + vim.fn.jobstart(full_cmd, { term = true }) vim.api.nvim_buf_set_keymap( buf, "n",