diff --git a/project_templates/.nvim.lua b/project_templates/.nvim.lua index e0df6ba..06a469f 100644 --- a/project_templates/.nvim.lua +++ b/project_templates/.nvim.lua @@ -1,9 +1,7 @@ --- Load persistent configuration from .workspace.lua -local ws_file = "./.nvim.workspace.lua" -local loaded, workspace = pcall(dofile, ws_file) -if not loaded then - workspace = { args = {}, build_type = "debug", binary = "blog-server" } -end +require("lua/lemon") + +local def_workspace = { args = {}, build_type = "debug", binary = "blog-server" } +local workspace = InitWorkspace(def_workspace) local build_folder local bin_target @@ -47,35 +45,6 @@ vim.api.nvim_create_autocmd("FileType", { end, }) -local function writeWorkspace() - -- A very minimal serializer for workspace configuration - local s = { l = "", ls = {}, i = "" } - local function w(v) s.l = s.l .. v end - local function nl() - s.ls[#s.ls + 1] = s.l; s.l = s.i; - end - local function wv(v) - local t = type(v) - if t == 'table' then - w('{'); local pi = s.i; s.i = s.i .. " " - for k1, v1 in pairs(v) do - nl(); w('['); wv(k1); w('] = '); wv(v1); w(',') - end - s.i = pi; nl(); w('}'); - elseif t == 'number' then - w(tostring(v)) - elseif t == 'string' then - w('"' .. v .. '"') - else - w(tostring(v)) - end - end - - -- Write the workspace file - w("return "); wv(workspace); nl() - vim.fn.writefile(s.ls, ws_file) -end - -- nvim-dap configuration local dap_ok, dap = pcall(require, "dap") local dap_def_cfg @@ -85,22 +54,7 @@ local dap_configs local function updateArgs(args) workspace.args = args if dap_configs ~= nil then dap_configs[1].args = args end - writeWorkspace() -end - --- Find terminal tab or create new one, then run command -local function TermRun(cmd) - local found = false - for i = 1, vim.fn.tabpagenr('$') do - vim.cmd('tabnext ' .. i) - if vim.bo.buftype == 'terminal' then - found = true - break - end - end - if not found then vim.cmd('tabnew | terminal') end - vim.fn.chansend(vim.b.terminal_job_id, '' .. cmd .. '\n') - vim.fn.feedkeys("G", "n") + WriteWorkspace() end -- The Configure command @@ -110,7 +64,7 @@ vim.api.nvim_create_user_command("Configure", function(a) if #a.args > 0 then bt = a.args end workspace.build_type = bt updateBuildEnv() - writeWorkspace() + WriteWorkspace() end, { nargs = '?', desc = "Update run/debug arguments" }) vim.api.nvim_create_user_command("Args", function(a) updateArgs(a.fargs) end, @@ -125,7 +79,7 @@ if dap_ok then } dap_configs = { { - name = 'test all', + name = 'default', type = 'codelldb', request = 'launch', program = bin_name, @@ -147,27 +101,36 @@ if dap_ok then end, { nargs = '*', desc = "Starts debugging with specified arguments" }) end -if MakeAnd then - local r = function() - MakeAnd(function() - TermRun(bin_name .. " " .. table.concat(workspace.args, " ")) - end) - end - -- RunArgs sets the run arguments that F6 uses and reruns immediately - vim.api.nvim_create_user_command("RunArgs", function(a) - updateArgs(a.fargs) - r() - end, { nargs = '*', desc = "Starts debugging with specified arguments" }) - - -- F6 to run the application - vim.keymap.set('n', '', r) - - if dap_ok then - -- Shift-F5 to launch default config - vim.keymap.set('n', '', function() - MakeAnd(function() - dap.run(dap_def_cfg) - end) - end) - end +local r = function() + MakeAnd(function() + TermRun(bin_name .. " " .. table.concat(workspace.args, " ")) + end) +end +-- RunArgs sets the run arguments that F6 uses and reruns immediately +vim.api.nvim_create_user_command("RunArgs", function(a) + updateArgs(a.fargs) + r() +end, { nargs = '*', desc = "Starts debugging with specified arguments" }) + +-- F6 to run the application +vim.keymap.set('n', '', r) +vim.keymap.set('n', '', function() + local info = GetTermInfo() + if info then + -- Send interrupt to terminal without switching + vim.fn.chansend(info.job_id, '\003') + -- Close window if it's open + vim.print("Stopped program") + else + vim.print("No terminal buffer found") + end +end) + +if dap_ok then + -- Shift-F5 to launch default config + vim.keymap.set('n', '', function() + MakeAnd(function() + dap.run(dap_def_cfg) + end) + end) end diff --git a/src/post_manager.rs b/src/post_manager.rs index cd88250..dca7bdb 100644 --- a/src/post_manager.rs +++ b/src/post_manager.rs @@ -1,5 +1,5 @@ use chrono::{DateTime, Utc}; -use pulldown_cmark::{html, Options, Parser}; +use pulldown_cmark::{html, Event, HeadingLevel, Options, Parser, Tag}; use std::collections::HashMap; use std::fs; use std::path::PathBuf; @@ -8,6 +8,7 @@ use std::process::Command; #[derive(Debug, Clone)] pub struct Post { pub name: String, + pub title: String, #[allow(dead_code)] pub filename: String, #[allow(dead_code)] @@ -143,9 +144,10 @@ impl PostManager { } let markdown_content = fs::read_to_string(&entry.file_path) .map_err(|e| format!("Failed to read post file: {}", e))?; - let html_content = markdown_to_html(&markdown_content); + let (html_content, title) = markdown_to_html(&markdown_content); let post = Post { name: name.clone(), + title: title, filename: entry.file_path, markdown_content, html_content, @@ -189,7 +191,28 @@ impl PostManager { } } -fn markdown_to_html(markdown: &str) -> String { +fn markdown_title(markdown: &str) -> Option { + let parser = Parser::new(markdown); + for event in parser { + if let Event::Start(tag) = event { + if let Tag::Heading { + level, + id, + .. + } = tag + { + if level == HeadingLevel::H1 { + if let Some(str) = id { + return Some(str.to_string()); + } + } + } + } + } + None +} + +fn markdown_to_html(markdown: &str) -> (String, String) { let mut options = Options::empty(); options.insert(Options::ENABLE_STRIKETHROUGH); options.insert(Options::ENABLE_TABLES); @@ -197,8 +220,10 @@ fn markdown_to_html(markdown: &str) -> String { options.insert(Options::ENABLE_TASKLISTS); options.insert(Options::ENABLE_SMART_PUNCTUATION); + let title = markdown_title(markdown).unwrap_or("unknown".to_string()); + let parser = Parser::new_ext(markdown, options); let mut html_output = String::new(); html::push_html(&mut html_output, parser); - html_output + (html_output, title) } diff --git a/src/template_engine.rs b/src/template_engine.rs index cddc9e3..3fa4ca8 100644 --- a/src/template_engine.rs +++ b/src/template_engine.rs @@ -171,6 +171,17 @@ fn process_tag( Ok(post.html_content.clone()) } + "title" => { + let post_name = current_post + .ok_or_else(|| "title tag used outside of post context".to_string())?; + + let post = post_manager + .get_post(post_name) + .ok_or_else(|| format!("Post '{}' not found", post_name))?; + + Ok(post.title.to_string()) + } + "updated" => { // Insert the last updated time of the current post (or most recent post) let post_ts = if let Some(p) = current_post {