More ways to define tags in frontmatter
parent
df83c62dec
commit
c709f4e4be
|
@ -7,4 +7,10 @@ Deno.test("cheap yaml", () => {
|
||||||
assertEquals(["template"], determineTags("tags: template"));
|
assertEquals(["template"], determineTags("tags: template"));
|
||||||
assertEquals(["bla", "template"], determineTags("tags: bla,template"));
|
assertEquals(["bla", "template"], determineTags("tags: bla,template"));
|
||||||
assertEquals(["bla", "template"], determineTags("tags:\n- bla\n- template"));
|
assertEquals(["bla", "template"], determineTags("tags:\n- bla\n- template"));
|
||||||
|
assertEquals(["bla", "template"], determineTags(`tags: "#bla,#template"`));
|
||||||
|
assertEquals(["bla", "template"], determineTags(`tags: '#bla, #template'`));
|
||||||
|
assertEquals(
|
||||||
|
["bla", "template"],
|
||||||
|
determineTags(`tags:\n- "#bla"\n- template`),
|
||||||
|
);
|
||||||
});
|
});
|
|
@ -1,5 +1,5 @@
|
||||||
const yamlKvRegex = /^\s*(\w+):\s*(.*)/;
|
const yamlKvRegex = /^\s*(\w+):\s*["']?([^'"]*)["']?$/;
|
||||||
const yamlListItemRegex = /^\s*-\s+(.+)/;
|
const yamlListItemRegex = /^\s*-\s+["']?([^'"]+)["']?$/;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cheap YAML parser to determine tags (ugly, regex based but fast)
|
* Cheap YAML parser to determine tags (ugly, regex based but fast)
|
||||||
|
@ -19,7 +19,9 @@ export function determineTags(yamlText: string): string[] {
|
||||||
inTagsSection = true;
|
inTagsSection = true;
|
||||||
// 'template' there? Yay!
|
// 'template' there? Yay!
|
||||||
if (value) {
|
if (value) {
|
||||||
tags.push(...value.split(/,\s*/));
|
tags.push(
|
||||||
|
...value.split(/,\s*|\s+/).map((t) => t.replace(/^#/, "")),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
inTagsSection = false;
|
inTagsSection = false;
|
||||||
|
@ -27,7 +29,7 @@ export function determineTags(yamlText: string): string[] {
|
||||||
}
|
}
|
||||||
const yamlListem = yamlListItemRegex.exec(line);
|
const yamlListem = yamlListItemRegex.exec(line);
|
||||||
if (yamlListem && inTagsSection) {
|
if (yamlListem && inTagsSection) {
|
||||||
tags.push(yamlListem[1]);
|
tags.push(yamlListem[1].replace(/^#/, ""));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return tags;
|
return tags;
|
|
@ -62,10 +62,14 @@ export async function extractFrontmatter(
|
||||||
if (!data.tags) {
|
if (!data.tags) {
|
||||||
data.tags = [];
|
data.tags = [];
|
||||||
}
|
}
|
||||||
// Normalize tags to an array and support a "tag1, tag2" notation
|
// Normalize tags to an array
|
||||||
|
// support "tag1, tag2" as well as "tag1 tag2" as well as "#tag1 #tag2" notations
|
||||||
if (typeof data.tags === "string") {
|
if (typeof data.tags === "string") {
|
||||||
data.tags = (data.tags as string).split(/,\s*/);
|
data.tags = (data.tags as string).split(/,\s*|\s+/);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Strip # from tags
|
||||||
|
data.tags = data.tags.map((t) => t.replace(/^#/, ""));
|
||||||
if (options.removeKeys && options.removeKeys.length > 0) {
|
if (options.removeKeys && options.removeKeys.length > 0) {
|
||||||
let removedOne = false;
|
let removedOne = false;
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import type { CompleteEvent } from "$sb/app_event.ts";
|
||||||
import { events } from "$sb/syscalls.ts";
|
import { events } from "$sb/syscalls.ts";
|
||||||
import { queryObjects } from "./api.ts";
|
import { queryObjects } from "./api.ts";
|
||||||
import { ObjectValue, QueryExpression } from "$sb/types.ts";
|
import { ObjectValue, QueryExpression } from "$sb/types.ts";
|
||||||
import { determineTags } from "./cheap_yaml.ts";
|
import { determineTags } from "../../plug-api/lib/cheap_yaml.ts";
|
||||||
|
|
||||||
export type AttributeObject = ObjectValue<{
|
export type AttributeObject = ObjectValue<{
|
||||||
name: string;
|
name: string;
|
||||||
|
|
|
@ -62,17 +62,28 @@ export async function tagComplete(completeEvent: CompleteEvent) {
|
||||||
}
|
}
|
||||||
const tagPrefix = match[0].substring(1);
|
const tagPrefix = match[0].substring(1);
|
||||||
let parent = "page";
|
let parent = "page";
|
||||||
if (taskPrefixRegex.test(completeEvent.linePrefix)) {
|
if (!completeEvent.parentNodes.find((n) => n.startsWith("FrontMatter:"))) {
|
||||||
parent = "task";
|
if (taskPrefixRegex.test(completeEvent.linePrefix)) {
|
||||||
} else if (itemPrefixRegex.test(completeEvent.linePrefix)) {
|
parent = "task";
|
||||||
parent = "item";
|
} else if (itemPrefixRegex.test(completeEvent.linePrefix)) {
|
||||||
|
parent = "item";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query all tags
|
// Query all tags with a matching parent
|
||||||
const allTags = await queryObjects<TagObject>("tag", {
|
const allTags: any[] = await queryObjects<TagObject>("tag", {
|
||||||
filter: ["=", ["attr", "parent"], ["string", parent]],
|
filter: ["=", ["attr", "parent"], ["string", parent]],
|
||||||
|
select: [{ name: "name" }],
|
||||||
|
distinct: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (parent === "page") {
|
||||||
|
// Also add template, even though that would otherwise not appear because has "builtin" as a parent
|
||||||
|
allTags.push({
|
||||||
|
name: "template",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
from: completeEvent.pos - tagPrefix.length,
|
from: completeEvent.pos - tagPrefix.length,
|
||||||
options: allTags.map((tag) => ({
|
options: allTags.map((tag) => ({
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
import { determineTags } from "$sb/lib/cheap_yaml.ts";
|
||||||
|
|
||||||
const frontMatterRegex = /^---\n(([^\n]|\n)*?)---\n/;
|
const frontMatterRegex = /^---\n(([^\n]|\n)*?)---\n/;
|
||||||
const yamlKvRegex = /^\s*(\w+):\s*(.*)/;
|
|
||||||
const yamlListItemRegex = /^\s*-\s+(.+)/;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Quick and dirty way to check if a page is a template or not
|
* Quick and dirty way to check if a page is a template or not
|
||||||
|
@ -13,30 +13,9 @@ export function isTemplate(pageText: string): boolean {
|
||||||
if (frontmatter) {
|
if (frontmatter) {
|
||||||
pageText = pageText.slice(frontmatter[0].length);
|
pageText = pageText.slice(frontmatter[0].length);
|
||||||
const frontmatterText = frontmatter[1];
|
const frontmatterText = frontmatter[1];
|
||||||
const lines = frontmatterText.split("\n");
|
const tags = determineTags(frontmatterText);
|
||||||
let inTagsSection = false;
|
if (tags.includes("template")) {
|
||||||
for (const line of lines) {
|
return true;
|
||||||
const yamlKv = yamlKvRegex.exec(line);
|
|
||||||
if (yamlKv) {
|
|
||||||
const [key, value] = yamlKv.slice(1);
|
|
||||||
// Looking for a 'tags' key
|
|
||||||
if (key === "tags") {
|
|
||||||
inTagsSection = true;
|
|
||||||
// 'template' there? Yay!
|
|
||||||
if (value.split(/,\s*/).includes("template")) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
inTagsSection = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const yamlListem = yamlListItemRegex.exec(line);
|
|
||||||
if (yamlListem && inTagsSection) {
|
|
||||||
// List item is 'template'? Yay!
|
|
||||||
if (yamlListem[1] === "template") {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Or if the page text starts with a #template tag
|
// Or if the page text starts with a #template tag
|
||||||
|
|
|
@ -21,6 +21,17 @@ While SilverBullet allows arbitrary metadata to be added to pages, there are a f
|
||||||
* `name` (==DISALLOWED==): is an attribute used for page names, _you should not set it_.
|
* `name` (==DISALLOWED==): is an attribute used for page names, _you should not set it_.
|
||||||
* `displayName` (`string`): very similar in effect as `aliases` but will use this name for the page in certain contexts.
|
* `displayName` (`string`): very similar in effect as `aliases` but will use this name for the page in certain contexts.
|
||||||
* `aliases` (`array of strings`): allow you to specify a list of alternative names for this page, which can be used to navigate or link to this page
|
* `aliases` (`array of strings`): allow you to specify a list of alternative names for this page, which can be used to navigate or link to this page
|
||||||
* `tags` (`array of strings` or `string`): an alternative (and perhaps preferred) way to assign [[Tags]] to a page. In principle you specify them as a list of strings, but for convenience you can also specify them as (possibly comma-separated) string, e.g. `tags: tag1, tag2, tag3`
|
* `tags` (`array of strings` or `string`): an alternative (and perhaps preferred) way to assign [[Tags]] to a page. There are various ways to define these, take your pick:
|
||||||
|
```yaml
|
||||||
|
tags: tag1, tag2 # with commas
|
||||||
|
tags: tag1 tag2 # with spaces
|
||||||
|
tags: "#tag1 #tag2" # with pound signs and quotes (you get completion)
|
||||||
|
tags: # as a list
|
||||||
|
- tag1
|
||||||
|
- tag2
|
||||||
|
tags: # as a list with pound signs and quotes
|
||||||
|
- "#tag1"
|
||||||
|
- "#tag2"
|
||||||
|
```
|
||||||
|
|
||||||
In addition, in the context of [[Templates]] frontmatter has a very specific interpretation.
|
In addition, in the context of [[Templates]] frontmatter has a very specific interpretation.
|
Loading…
Reference in New Issue