Live Preview (#119)

Live preview mode is here
This commit is contained in:
Zef Hemel
2022-11-18 16:04:37 +01:00
committed by GitHub
parent c9713bf52b
commit 24c17a793f
42 changed files with 1502 additions and 183 deletions
+46 -10
View File
@@ -12,11 +12,6 @@ syntax:
- "h"
regex: "https?:\\/\\/[-a-zA-Z0-9@:%._\\+~#=]{1,256}([-a-zA-Z0-9()@:%_\\+.~#?&=\\/]*)"
className: sb-naked-url
CommandLink:
firstCharacters:
- "{"
regex: "\\{\\[[^\\]]+\\]\\}"
className: sb-command-link
NamedAnchor:
firstCharacters:
- "$"
@@ -171,6 +166,8 @@ functions:
# Template commands
insertTemplateText:
path: "./template.ts:insertTemplateText"
applyLineReplace:
path: ./template.ts:applyLineReplace
insertFrontMatter:
redirect: insertTemplateText
slashCommand:
@@ -180,18 +177,57 @@ functions:
---
|^|
---
insertTask:
redirect: insertTemplateText
makeH1:
redirect: applyLineReplace
slashCommand:
name: task
description: Insert a task
value: "* [ ] |^|"
name: h1
description: Turn line into h1 header
match: "^#*\\s*"
replace: "# "
makeH2:
redirect: applyLineReplace
slashCommand:
name: h2
description: Turn line into h2 header
match: "^#*\\s*"
replace: "## "
makeH3:
redirect: applyLineReplace
slashCommand:
name: h3
description: Turn line into h3 header
match: "^#*\\s*"
replace: "### "
makeH4:
redirect: applyLineReplace
slashCommand:
name: h4
description: Turn line into h4 header
match: "^#*\\s*"
replace: "#### "
newPage:
path: ./page.ts:newPageCommand
command:
name: "Page: New"
key: "Alt-Shift-n"
insertHRTemplate:
redirect: insertTemplateText
slashCommand:
name: hr
description: Insert a horizontal rule
value: "---"
insertTable:
redirect: insertTemplateText
slashCommand:
name: table
description: Insert a table
boost: -1 # Low boost because it's likely not very commonly used
value: |
| Header A | Header B |
|----------|----------|
| Cell A|^| | Cell B |
quickNoteCommand:
path: ./template.ts:quickNoteCommand
command:
+22 -9
View File
@@ -1,6 +1,11 @@
import type { ClickEvent } from "$sb/app_event.ts";
import { editor, markdown, system } from "$sb/silverbullet-syscall/mod.ts";
import { nodeAtPos, ParseTree } from "$sb/lib/tree.ts";
import {
addParentPointers,
findParentMatching,
nodeAtPos,
ParseTree,
} from "$sb/lib/tree.ts";
// Checks if the URL contains a protocol, if so keeps it, otherwise assumes an attachment
function patchUrl(url: string): string {
@@ -17,10 +22,19 @@ async function actionClickOrActionEnter(
if (!mdTree) {
return;
}
// console.log("Attempting to navigate based on syntax node", mdTree);
const navigationNodeFinder = (t: ParseTree) =>
["WikiLink", "Link", "URL", "NakedURL", "Link", "CommandLink"].includes(
t.type!,
);
if (!navigationNodeFinder(mdTree)) {
mdTree = findParentMatching(mdTree, navigationNodeFinder);
if (!mdTree) {
return;
}
}
switch (mdTree.type) {
case "WikiLinkPage": {
let pageLink = mdTree.children![0].text!;
case "WikiLink": {
let pageLink = mdTree.children![1]!.children![0].text!;
let pos;
if (pageLink.includes("@")) {
[pageLink, pos] = pageLink.split("@");
@@ -47,11 +61,8 @@ async function actionClickOrActionEnter(
break;
}
case "CommandLink": {
const command = mdTree
.children![0].text!.substring(2, mdTree.children![0].text!.length - 2)
.trim();
console.log("Got command link", command);
await system.invokeCommand(command);
const commandName = mdTree.children![1]!.children![0].text!;
await system.invokeCommand(commandName);
break;
}
}
@@ -60,6 +71,7 @@ async function actionClickOrActionEnter(
export async function linkNavigate() {
const mdTree = await markdown.parseMarkdown(await editor.getText());
const newNode = nodeAtPos(mdTree, await editor.getCursor());
addParentPointers(mdTree);
await actionClickOrActionEnter(newNode);
}
@@ -69,6 +81,7 @@ export async function clickNavigate(event: ClickEvent) {
return;
}
const mdTree = await markdown.parseMarkdown(await editor.getText());
addParentPointers(mdTree);
const newNode = nodeAtPos(mdTree, event.pos);
await actionClickOrActionEnter(newNode, event.ctrlKey || event.metaKey);
}
+36 -5
View File
@@ -92,17 +92,24 @@ export async function linkQueryProvider({
export async function deletePage() {
const pageName = await editor.getCurrentPage();
if (
!await editor.confirm(`Are you sure you would like to delete ${pageName}?`)
) {
return;
}
console.log("Navigating to index page");
await editor.navigate("");
console.log("Deleting page from space");
await space.deletePage(pageName);
}
export async function renamePage() {
export async function renamePage(targetName?: string) {
console.log("Got a target name", targetName);
const oldName = await editor.getCurrentPage();
const cursor = await editor.getCursor();
console.log("Old name is", oldName);
const newName = await editor.prompt(`Rename ${oldName} to:`, oldName);
const newName = targetName ||
await editor.prompt(`Rename ${oldName} to:`, oldName);
if (!newName) {
return;
}
@@ -117,17 +124,25 @@ export async function renamePage() {
const text = await editor.getText();
console.log("Writing new page to space");
await space.writePage(newName, text);
const newPageMeta = await space.writePage(newName, text);
console.log("Navigating to new page");
await editor.navigate(newName, cursor, true);
console.log("Deleting page from space");
await space.deletePage(oldName);
// Handling the edge case of a changing page name just in casing on a case insensitive FS
const oldPageMeta = await space.getPageMeta(oldName);
if (oldPageMeta.lastModified !== newPageMeta.lastModified) {
// If they're the same, let's assume it's the same file (case insensitive FS) and not delete, otherwise...
console.log("Deleting page from space");
await space.deletePage(oldName);
}
const pageToUpdateSet = new Set<string>();
for (const pageToUpdate of pagesToUpdate) {
pageToUpdateSet.add(pageToUpdate.page);
}
let updatedReferences = 0;
for (const pageToUpdate of pageToUpdateSet) {
if (pageToUpdate === oldName) {
continue;
@@ -146,12 +161,14 @@ export async function renamePage() {
const pageName = n.children![0].text!;
if (pageName === oldName) {
n.children![0].text = newName;
updatedReferences++;
return n;
}
// page name with @pos position
if (pageName.startsWith(`${oldName}@`)) {
const [, pos] = pageName.split("@");
n.children![0].text = `${newName}@${pos}`;
updatedReferences++;
return n;
}
}
@@ -164,6 +181,20 @@ export async function renamePage() {
await space.writePage(pageToUpdate, newText);
}
}
await editor.flashNotification(
`Renamed page, and updated ${updatedReferences} references`,
);
}
export async function newPageCommand() {
const allPages = await space.listPages();
let pageName = `Untitled`;
let i = 1;
while (allPages.find((p) => p.name === pageName)) {
pageName = `Untitled ${i}`;
i++;
}
await editor.navigate(pageName);
}
type BackLink = {
+29
View File
@@ -3,6 +3,7 @@ import { extractMeta } from "../directive/data.ts";
import { renderToText } from "$sb/lib/tree.ts";
import { niceDate } from "$sb/lib/dates.ts";
import { readSettings } from "$sb/lib/settings_page.ts";
import { regexp } from "https://deno.land/std@0.163.0/encoding/_yaml/type/regexp.ts";
export async function instantiateTemplateCommand() {
const allPages = await space.listPages();
@@ -210,3 +211,31 @@ export async function insertTemplateText(cmdDef: any) {
await editor.moveCursor(cursorPos + carretPos);
}
}
export async function applyLineReplace(cmdDef: any) {
const cursorPos = await editor.getCursor();
const text = await editor.getText();
const matchRegex = new RegExp(cmdDef.match);
let startOfLine = cursorPos;
while (startOfLine > 0 && text[startOfLine - 1] !== "\n") {
startOfLine--;
}
let currentLine = text.slice(startOfLine, cursorPos);
const emptyLine = !currentLine;
currentLine = currentLine.replace(matchRegex, cmdDef.replace);
await editor.dispatch({
changes: {
from: startOfLine,
to: cursorPos,
insert: currentLine,
},
selection: emptyLine
? {
anchor: startOfLine + currentLine.length,
}
: undefined,
});
}
+2 -4
View File
@@ -294,10 +294,8 @@ function render(
body: "",
};
case "CommandLink": {
const commandText = t.children![0].text!.substring(
2,
t.children![0].text!.length - 2,
);
// Child 0 is CommandLinkMark, child 1 is CommandLinkPage
const commandText = t.children![1].children![0].text!;
return {
name: "button",
-5
View File
@@ -91,7 +91,6 @@ export function taskToggle(event: ClickEvent) {
export function previewTaskToggle(eventString: string) {
const [eventName, pos] = JSON.parse(eventString);
if (eventName === "task") {
console.log("Gotta toggle a task at", pos);
return taskToggleAtPos(+pos);
}
}
@@ -107,9 +106,6 @@ async function toggleTaskMarker(node: ParseTree, moveToPos: number) {
to: node.to,
insert: changeTo,
},
selection: {
anchor: moveToPos,
},
});
const parentWikiLinks = collectNodesMatching(
@@ -147,7 +143,6 @@ export async function taskToggleAtPos(pos: number) {
addParentPointers(mdTree);
const node = nodeAtPos(mdTree, pos);
// console.log("Got this node", node?.type);
if (node && node.type === "TaskMarker") {
await toggleTaskMarker(node, pos);
}
+8
View File
@@ -21,6 +21,14 @@ syntax:
styles:
backgroundColor: "rgba(22,22,22,0.07)"
functions:
turnIntoTask:
redirect: core.applyLineReplace
slashCommand:
name: task
description: Turn into task
match: "^(\\s*)[\\-\\*]?\\s*(\\[[ xX]\\])?\\s*"
replace: "$1* [ ] "
indexTasks:
path: "./task.ts:indexTasks"
events: