Skip to content

Commit

Permalink
Fix some YAML parser issues
Browse files Browse the repository at this point in the history
Fixes some of the issues brought up in #209.
  • Loading branch information
epwalsh committed Oct 20, 2023
1 parent 1387498 commit 96ac0d9
Show file tree
Hide file tree
Showing 8 changed files with 72 additions and 20 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

- Eliminated silent runtime errors on validation errors in note.from_lines
- Eliminated silent runtime errors on validation errors in `note.from_lines`.
- Fixed parsing YAML boolean values in frontmatter.
- Fixed parsing implicit null values in YAML frontmatter.

## [v1.14.2](https://github.com/epwalsh/obsidian.nvim/releases/tag/v1.14.2) - 2023-09-25

Expand Down
6 changes: 3 additions & 3 deletions lua/cmp_obsidian.lua
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ source.complete = function(self, request, callback)
if can_complete and search ~= nil and #search >= opts.completion.min_chars then
local items = {}
for note in client:search(search, "--ignore-case") do
local aliases = util.unique { note.id, note:display_name(), unpack(note.aliases) }
local aliases = util.unique { tostring(note.id), note:display_name(), unpack(note.aliases) }
for _, alias in pairs(aliases) do
local options = {}

Expand All @@ -37,8 +37,8 @@ source.complete = function(self, request, callback)
table.insert(options, alias)

for _, option in pairs(options) do
local label = "[[" .. note.id
if option ~= note.id then
local label = "[[" .. tostring(note.id)
if option ~= tostring(note.id) then
label = label .. "|" .. option .. "]]"
else
label = label .. "]]"
Expand Down
4 changes: 2 additions & 2 deletions lua/obsidian/backlinks.lua
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ backlinks.gather = function(self)
local backlink_matches = {}
local last_path = nil
local last_note = nil
for match in util.search(self.client.dir, "[[" .. self.note.id) do
for match in util.search(self.client.dir, "[[" .. tostring(self.note.id)) do
if match == nil then
break
elseif is_valid_backlink(match) then
Expand Down Expand Up @@ -189,7 +189,7 @@ backlinks.view = function(self)
-- Add highlights for all refs in the text.
for i, ref_idx in ipairs(ref_indices) do
local ref_str = ref_strs[i]
if string.find(ref_str, self.note.id, 1, true) ~= nil then
if string.find(ref_str, tostring(self.note.id), 1, true) ~= nil then
table.insert(highlights, {
group = "Search",
line = #view_lines - 1,
Expand Down
6 changes: 3 additions & 3 deletions lua/obsidian/command.lua
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ command.backlinks = function(client, _)
end)
if ok then
echo.info(
("Showing backlinks '%s'. Hit ENTER on a line to follow the backlink."):format(backlinks.note.id),
("Showing backlinks '%s'. Hit ENTER on a line to follow the backlink."):format(tostring(backlinks.note.id)),
client.opts.log_level
)
backlinks:view()
Expand Down Expand Up @@ -440,7 +440,7 @@ command.link_new = function(client, data)

line = string.sub(line, 1, cscol - 1)
.. "[["
.. note.id
.. tostring(note.id)
.. "|"
.. string.sub(line, cscol, cecol)
.. "]]"
Expand Down Expand Up @@ -480,7 +480,7 @@ command.link = function(client, data)

line = string.sub(line, 1, cscol - 1)
.. "[["
.. note.id
.. tostring(note.id)
.. "|"
.. string.sub(line, cscol, cecol)
.. "]]"
Expand Down
6 changes: 1 addition & 5 deletions lua/obsidian/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -272,12 +272,8 @@ client.parse_title_id_path = function(self, title, id, dir)
-- Trim whitespace.
title = title:match "^%s*(.-)%s*$"

if title == "" then
title = nil
end

-- Remove suffix.
if title:match "%.md" then
-- Remove suffix.
title = title:sub(1, title:len() - 3)
title_is_path = true
end
Expand Down
8 changes: 4 additions & 4 deletions lua/obsidian/note.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ local echo = require "obsidian.echo"
local SKIP_UPDATING_FRONTMATTER = { "README.md", "CONTRIBUTING.md", "CHANGELOG.md" }

---@class obsidian.Note
---@field id string
---@field id string|integer
---@field aliases string[]
---@field tags string[]
---@field path Path|?
Expand All @@ -17,7 +17,7 @@ local note = {}

---Create new note.
---
---@param id string
---@param id string|number
---@param aliases string[]
---@param tags string[]
---@param path string|Path|?
Expand Down Expand Up @@ -147,7 +147,7 @@ note.display_name = function(self)
if #self.aliases > 0 then
return self.aliases[#self.aliases]
end
return self.id
return tostring(self.id)
end

---Initialize a note from an iterator of lines.
Expand Down Expand Up @@ -215,7 +215,7 @@ note.from_lines = function(lines, path, root)
---@diagnostic disable-next-line: param-type-mismatch
for k, v in pairs(data) do
if k == "id" then
if type(v) == "string" then
if type(v) == "string" or type(v) == "number" then
id = v
else
echo.warn("Invalid 'id' in frontmatter for " .. tostring(path))
Expand Down
47 changes: 45 additions & 2 deletions lua/obsidian/yaml.lua
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ local context = function(str)
end

local word = function(w)
return "^(" .. w .. ")([%s$%c])"
return "^(" .. w .. ")([%s%c])"
end

local tokens = {
Expand Down Expand Up @@ -542,9 +542,52 @@ end

local yaml = {}

local count_indent = function(str)
local indent = 0
for i = 1, #str do
if string.sub(i, i) == " " then
indent = indent + 1
else
break
end
end
return indent
end

local preprocess = function(str)
local lines = {}
local current_indent = 0
for line in str:gmatch "[^\r\n]+" do
line = string.gsub(line, "%s+$", "")
local indent = count_indent(line)

-- HACK: If the previous line is something like 'foo:' and the current line has the same indent
-- but is not a list item, then we change the previous line to 'foo: nil' since otherwise the parser
-- would not parse that correctly.
if
indent == current_indent
and lines[#lines] ~= nil
and lines[#lines]:sub(#lines[#lines]) == ":"
and not line:match "^%s*-"
then
lines[#lines] = lines[#lines] .. " null"
end

table.insert(lines, line)
current_indent = indent
end
str = table.concat(lines, "\n")
if str:sub(#str) ~= "\n" then
-- Need a new line at the end for some parsing to work.
str = str .. "\n"
end
return str
end

---Deserialize a YAML string.
yaml.loads = function(str)
return Parser:new(tokenize(str)):parse()
local parser = Parser:new(tokenize(preprocess(str)))
return parser:parse()
end

---@return string[]
Expand Down
11 changes: 11 additions & 0 deletions test/obsidian/yaml_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,15 @@ describe("obsidian.yaml", function()
assert.equals(#data.aliases, 1)
assert.equals(data.aliases[1], "Foo Baz")
end)
it("should parse boolean field values", function()
local data = yaml.loads "complete: false"
assert.equals(type(data), "table")
assert.equals(type(data.complete), "boolean")
end)
it("should parse implicit null values", function()
local data = yaml.loads "tags: \ncomplete: false"
assert.equals(type(data), "table")
assert.equals(data.tags, nil)
assert.equals(data.complete, false)
end)
end)

0 comments on commit 96ac0d9

Please sign in to comment.