diff --git a/README.md b/README.md index 7f68591..76e764a 100644 --- a/README.md +++ b/README.md @@ -37,9 +37,6 @@ ### Installation -> [!IMPORTANT] -> This plugin requires the Lua [lyaml](https://luarocks.org/modules/gvvaughan/lyaml) library for YAML parsing. - Use your favorite Neovim plugin manager.
diff --git a/docs/project-description.md b/docs/project-description.md index 54166d2..90d7f67 100644 --- a/docs/project-description.md +++ b/docs/project-description.md @@ -24,7 +24,7 @@ Each extension is independently configurable and can be enabled or disabled as n - The extension can be enabled or disabled via the `enabled` config field. - **Frontmatter Parsing:** - - The extension parses YAML frontmatter from markdown files using a Lua YAML parser (`lyaml` or `yaml.nvim`). + - The extension parses YAML frontmatter from markdown files using a built-in lightweight YAML parser. - **Slash Command Patching:** - The `/buffer` slash command is patched so that after a buffer is added, the extension injects the appropriate instruction files. @@ -36,7 +36,7 @@ Each extension is independently configurable and can be enabled or disabled as n ## Technical Notes - The extension caches the mapping between globs and custom instruction files in memory for performance. -- If no YAML parser is available, the extension notifies the user and skips conditional custom instructions. +- The extension includes a lightweight YAML parser optimized for frontmatter parsing. - The extension is designed to be robust and extensible, following CodeCompanion's extension guidelines. ## Goals diff --git a/lua/codecompanion/filewise/frontmatter.lua b/lua/codecompanion/filewise/frontmatter.lua index 7f15323..d9c63bb 100644 --- a/lua/codecompanion/filewise/frontmatter.lua +++ b/lua/codecompanion/filewise/frontmatter.lua @@ -2,7 +2,7 @@ -- filewise/frontmatter – Utilities for parsing YAML frontmatter from markdown --============================================================================= -local lyaml = require 'lyaml' +local yaml_parser = require 'codecompanion.filewise.yaml_parser' local M = {} @@ -31,7 +31,7 @@ function M.parse(path, rest) if rest then table.insert(body, l) else break end end end - local ok, fm = pcall(lyaml.load, table.concat(frontmatter, '\n')) + local ok, fm = pcall(yaml_parser.parse, table.concat(frontmatter, '\n')) if ok and type(fm) == 'table' then return fm, body end diff --git a/lua/codecompanion/filewise/yaml_parser.lua b/lua/codecompanion/filewise/yaml_parser.lua new file mode 100644 index 0000000..7ef0795 --- /dev/null +++ b/lua/codecompanion/filewise/yaml_parser.lua @@ -0,0 +1,145 @@ +--============================================================================= +-- filewise/yaml_parser – Simple YAML parser for frontmatter +--============================================================================= + +local M = {} + +--- Trim leading and trailing whitespace from a string. +---@param s string|nil +---@return string|nil +local function trim(s) + if not s then return s end + return s:match('^%s*(.-)%s*$') +end + +--- Remove a single layer of surrounding quotes from a string if present. +---@param s string|nil +---@return string|nil +local function unquote(s) + if not s then return s end + return s:match('^"(.-)"$') or s:match("^'(.-)'$") or s +end + +--- Strip only the leading indentation from a string (keep trailing spaces intact). +---@param s string +---@return string +local function ltrim(s) + return (s:gsub('^%s+', '')) +end + +--- Simple YAML parser for basic key-value frontmatter +--- Supports: +--- - simple key: value pairs +--- - inline lists: key: [a, b, c] +--- - dash lists: +--- key: +--- - a +--- - b +--- - block scalars using | or > +--- Notes/limitations (by design for frontmatter use-cases): +--- - keys are limited to [A-Za-z0-9_-] +--- - inline lists don't support commas inside quoted strings +--- - scalar values are returned as strings (no auto-typing) +---@param yaml_text string|nil The YAML content to parse +---@return table|nil Parsed YAML as a table, or nil if invalid/empty +function M.parse(yaml_text) + if not yaml_text or yaml_text == "" then + return nil + end + + local result = {} + local lines = {} + for l in yaml_text:gmatch('[^\r\n]+') do + table.insert(lines, l) + end + + local i = 1 + while i <= #lines do + local raw = lines[i] + local line = trim(raw) + -- skip empty and comment lines + if line == '' or line:match('^#') then + i = i + 1 + else + local key, value = line:match('^([%w_%-]+)%s*:%s*(.*)$') + if key then + -- empty value -> might be a dash list or block following + if value == '' then + -- check for dash list on following lines + local items = {} + local j = i + 1 + while j <= #lines do + local nxt = lines[j] + if nxt:match('^%s*%-%s+') then + local item = nxt:match('^%s*%-%s+(.*)$') + item = trim(item) + item = unquote(item) + table.insert(items, item) + j = j + 1 + elseif nxt:match('^%s*$') then + j = j + 1 + else + break + end + end + if #items > 0 then + result[key] = items + i = j + else + -- nothing special; store empty string + result[key] = '' + i = i + 1 + end + else + -- inline list? e.g. [a, b, c] + local inline = value:match('^%[(.*)%]$') + if inline then + local items = {} + for part in inline:gmatch('[^,]+') do + local v = trim(part) + v = unquote(v) + table.insert(items, v) + end + result[key] = items + i = i + 1 + elseif value == '|' or value == '>' then + -- block scalar: collect following indented lines (start with whitespace) + local j = i + 1 + local buf = {} + while j <= #lines do + local nxt = lines[j] + if nxt:match('^%s+$') then + -- preserve blank line inside block scalar + table.insert(buf, '') + j = j + 1 + elseif nxt:match('^%s+') then + -- strip only the leading indentation for readability + table.insert(buf, ltrim(nxt)) + j = j + 1 + else + break + end + end + result[key] = table.concat(buf, '\n') + i = j + else + local v = trim(value) + v = unquote(v) + result[key] = v + i = i + 1 + end + end + else + -- not a key line; ignore + i = i + 1 + end + end + end + + if next(result) == nil then + return nil + end + return result +end + +return M \ No newline at end of file