I used windsurf to write Daily Notes Neovim Plugin for navigating my daily notes in neovim.
For a while now I’ve wanted a way to see my blog drafts… and tonight finally got the massive courage to prompt gpt-5 to build it for me…
Screenshot #
Code #
---@diagnostic disable: undefined-global
-- Blogging plugin: find draft blog posts under `pages/`
-- Draft = YAML front matter contains `published: false` (case-insensitive)
local M = {}
-- Read file and return YAML front matter block as string, or nil
local function read_front_matter(path)
local fh = io.open(path, "r")
if not fh then
return nil
end
local content = fh:read("*a")
fh:close()
if not content then
return nil
end
-- Front matter starts with --- on first line and ends with --- on its own line
-- Support optional CRLF
local start_s, start_e = content:find("^%-%-%-%s*\r?\n")
if not start_e then
return nil
end
local fm_end_s, fm_end_e = content:find("\n%-%-%-%s*\r?\n", start_e + 1)
if not fm_end_s then
return nil
end
-- Extract between the fence lines (exclude the starting and ending --- lines)
local front_matter = content:sub(start_e + 1, fm_end_s)
return front_matter
end
-- Determine if a file is a draft by inspecting front matter for published: false
function M._is_draft(path)
local fm = read_front_matter(path)
if not fm then
return false
end
-- Find a line starting with `published:` and capture the value
-- Trim comments and quotes, compare case-insensitively
local value = fm:match("[Pp][Uu][Bb][Ll][Ii][Ss][Hh][Ee][Dd]%s*:%s*([^\n\r]+)")
if not value then
return false
end
-- strip inline comments starting with # and surrounding quotes/space
value = value:gsub("#.*$", "")
value = value:gsub('"', ""):gsub("'", "")
value = value:gsub("^%s+", ""):gsub("%s+$", "")
local v = value:lower()
if v == "false" or v == "no" or v == "0" then
return true
end
return false
end
-- Telescope picker for draft posts
function M.find_drafts()
local ok_pickers, pickers = pcall(require, "telescope.pickers")
if not ok_pickers then
vim.notify("telescope.nvim not available", vim.log.levels.ERROR)
return
end
local finders = require("telescope.finders")
local previewers = require("telescope.previewers")
local conf = require("telescope.config").values
local actions = require("telescope.actions")
local action_state = require("telescope.actions.state")
-- Collect markdown files under pages/
local files = vim.fn.glob("pages/**/*.md", false, true)
local drafts = {}
for _, f in ipairs(files) do
if M._is_draft(f) then
table.insert(drafts, f)
end
end
if #drafts == 0 then
vim.notify("No draft blog posts found in pages/", vim.log.levels.INFO)
return
end
-- Simple, version-agnostic entry maker
local function entry_maker_fn(line)
return {
value = line,
ordinal = line,
display = vim.fn.fnamemodify(line, ":~:.") or line,
path = line,
}
end
-- Use Telescope builtin find_files with a custom command that lists only drafts.
-- This guarantees previews and matches the style of other file pickers.
local tmpfile = vim.fn.tempname()
local ok_write, err = pcall(function()
local fh = assert(io.open(tmpfile, "w"))
for _, f in ipairs(drafts) do
fh:write(f .. "\n")
end
fh:close()
end)
if not ok_write then
vim.notify("Failed to prepare drafts list: " .. tostring(err), vim.log.levels.ERROR)
return
end
local builtin = require("telescope.builtin")
builtin.find_files({
prompt_title = "Draft Blog Posts",
-- cat the tmp list so Telescope treats it like find output
find_command = { "bash", "-lc", string.format('cat %q', tmpfile) },
attach_mappings = function(prompt_bufnr, map)
local actions = require("telescope.actions")
actions.close:enhance({
post = function()
os.remove(tmpfile)
end,
})
return true
end,
})
end
-- Expose a user command similar to daily plugin style
vim.api.nvim_create_user_command("BlogDrafts", function()
M.find_drafts()
end, {})
return M