@@ 8,11 8,9 @@ const warn = std.debug.warn;
const c = @cImport(@cInclude("time.h"));
-const id_length = "20200106121000".len;
+const id_len = "20200106121000".len;
const extension = ".md";
-const new_template = "---\ntitle: {}\ndate: {}\ntags:\n...\n";
-const backlinks_template = "- [[{}]] {}";
-const backlinks_header = "## Backlinks";
+const bl_header = "## Backlinks";
pub const Zettel = struct {
allocator: *mem.Allocator,
@@ 43,14 41,15 @@ pub const Zettel = struct {
var file = try fs.cwd().createFile(fname, .{});
defer file.close();
- const contents = try fmt.allocPrint(allocator, new_template, .{ title, date });
+ comptime var template = "---\ntitle: {}\ndate: {}\ntags:\n...\n";
+ const contents = try fmt.allocPrint(allocator, template, .{ title, date });
try file.writeAll(contents);
return Zettel{
.allocator = allocator,
.basename = fname[0 .. fname.len - extension.len],
.fname = fname,
- .id = fname[0..id_length],
+ .id = fname[0..id_len],
.tags = &[_][]const u8{},
.title = try mem.dupe(allocator, u8, title),
.mtime = (try file.stat()).mtime,
@@ 91,8 90,8 @@ pub const Zettel = struct {
// Also try id + title
if (mem.startsWith(u8, keyword, self.id) and
- keyword.len > id_length + 1 and keyword[id_length] == ' ' and
- ascii.eqlIgnoreCase(keyword[id_length + 1 ..], self.title))
+ keyword.len > id_len + 1 and keyword[id_len] == ' ' and
+ ascii.eqlIgnoreCase(keyword[id_len + 1 ..], self.title))
{
return true;
}
@@ 106,17 105,19 @@ pub const Zettel = struct {
var links_list = std.ArrayList([]const u8).init(self.allocator);
// Ignore any links beneath a preexisting backlinks header
- const end_index = mem.indexOf(u8, self.contents, backlinks_header) orelse self.contents.len;
+ const end_index = mem.indexOf(u8, self.contents, bl_header) orelse
+ self.contents.len;
var start_index: usize = 0;
- outer: while (mem.indexOfPos(u8, self.contents[0..end_index], start_index, "[[")) |index| : (start_index = index + "[[".len) {
+ outer: while (mem.indexOfPos(u8, self.contents[0..end_index], start_index, "[[")) |index| : (start_index = index + 1) {
// Check for valid link and ID
- const link = self.contents[index .. index + "[[".len + self.id.len + "]]".len];
+ comptime var link_len = "[[".len + id_len + "]]".len;
+ const link = self.contents[index .. index + link_len];
if (!mem.startsWith(u8, link, "[[") or !mem.endsWith(u8, link, "]]")) {
continue;
}
- const id = link["[[".len .. link.len - "]]".len];
+ const id = link["[[".len .. "[[".len + id_len];
for (id) |char| {
if (!ascii.isDigit(char)) {
continue :outer;
@@ 133,41 134,24 @@ pub const Zettel = struct {
pub fn writeBacklinks(self: *Zettel) !void {
if (self.backlinks.items.len == 0) return;
- // Find the index of the end of the YAML metadata block, since that
- // interferes with detection of horizontal rules ("---") in the actual
- // note contents.
- // Note that both "---" and "..." are valid closing delimiters of the
- // metadata block.
- const metadata_start = "---".len + 1;
- const metadata_end = mem.min(usize, &[_]usize{
- mem.indexOf(u8, self.contents[metadata_start..], "...") orelse self.contents.len,
- mem.indexOf(u8, self.contents[metadata_start..], "---") orelse self.contents.len,
- });
-
- const end_index = blk: {
- if (mem.lastIndexOf(u8, self.contents, "---")) |i| {
- if (i <= metadata_end) {
- // If last instance of "---" is in the metadata block, ignore it
- break :blk self.contents.len;
- } else {
- break :blk i - 1;
- }
- } else unreachable;
- };
-
- const start_index = if (mem.indexOf(u8, self.contents, backlinks_header)) |s| s - 1 else end_index;
+ const start_index = if (mem.indexOf(u8, self.contents, bl_header)) |s|
+ s - 1
+ else
+ self.contents.len;
var backlinks = try std.ArrayList([]const u8).initCapacity(self.allocator, self.backlinks.items.len);
- for (self.backlinks.items) |backlink| {
- const item = try fmt.allocPrint(self.allocator, backlinks_template, .{ backlink.id, backlink.title });
+ for (self.backlinks.items) |bl| {
+ comptime var template = "- [[{}]] {}";
+ const item = try fmt.allocPrint(self.allocator, template, .{ bl.id, bl.title });
backlinks.appendAssumeCapacity(item);
}
- const new_contents = try fmt.allocPrint(self.allocator, "{}\n\n{}\n\n{}\n{}", .{
+ const new_contents = try mem.join(self.allocator, "\n", &[_][]const u8{
self.contents[0 .. start_index - 1],
- backlinks_header,
+ "",
+ bl_header,
+ "",
try mem.join(self.allocator, "\n", backlinks.items),
- self.contents[end_index..],
});
if (!mem.eql(u8, new_contents, self.contents)) {
@@ 248,7 232,7 @@ fn fromEntry(allocator: *mem.Allocator, entry: fs.Dir.Entry) !Zettel {
var line_it = mem.split(contents, "\n");
if (line_it.next()) |line| {
- // If first line is not a metadata delimiter, skip this entry
+ // If first line is not a front matter fence, skip this entry
if (!mem.eql(u8, line, "---")) {
return error.InvalidFormat;
}
@@ 293,7 277,7 @@ fn fromEntry(allocator: *mem.Allocator, entry: fs.Dir.Entry) !Zettel {
}
}
} else {
- // Reached EOF before finding closing metadata delimiter
+ // Reached EOF before finding closing front matter fence
return error.InvalidFormat;
}
@@ 306,7 290,7 @@ fn fromEntry(allocator: *mem.Allocator, entry: fs.Dir.Entry) !Zettel {
.allocator = allocator,
.basename = fname[0 .. fname.len - extension.len],
.fname = fname,
- .id = fname[0..id_length],
+ .id = fname[0..id_len],
.tags = tags.toOwnedSlice(),
.title = title.?,
.mtime = (try file.stat()).mtime,
@@ 316,9 300,9 @@ fn fromEntry(allocator: *mem.Allocator, entry: fs.Dir.Entry) !Zettel {
}
fn hasId(name: []const u8) bool {
- if (name.len < id_length) return false;
+ if (name.len < id_len) return false;
- for (name[0..id_length]) |char| {
+ for (name[0..id_len]) |char| {
if (!ascii.isDigit(char)) return false;
}