Fixes #330
parent
63083d1e05
commit
43adb13fb2
|
@ -15,6 +15,14 @@ export function getText(): Promise<string> {
|
||||||
return syscall("editor.getText");
|
return syscall("editor.getText");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This updates the editor text, but in a minimal-diff way:
|
||||||
|
* it compares the current editor text with the new text, and only sends the changes to the editor, thereby preserving cursor location
|
||||||
|
*/
|
||||||
|
export function setText(newText: string) {
|
||||||
|
return syscall("editor.setText", newText);
|
||||||
|
}
|
||||||
|
|
||||||
export function getCursor(): Promise<number> {
|
export function getCursor(): Promise<number> {
|
||||||
return syscall("editor.getCursor");
|
return syscall("editor.getCursor");
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { editor, markdown, space, sync } from "$sb/syscalls.ts";
|
||||||
import {
|
import {
|
||||||
addParentPointers,
|
addParentPointers,
|
||||||
collectNodesMatching,
|
collectNodesMatching,
|
||||||
|
findNodeMatching,
|
||||||
findNodeOfType,
|
findNodeOfType,
|
||||||
findParentMatching,
|
findParentMatching,
|
||||||
nodeAtPos,
|
nodeAtPos,
|
||||||
|
@ -337,3 +338,34 @@ export async function postponeCommand() {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function removeCompletedTasksCommand() {
|
||||||
|
const tree = await markdown.parseMarkdown(await editor.getText());
|
||||||
|
addParentPointers(tree);
|
||||||
|
|
||||||
|
// Taking this ugly approach because the tree is modified in place
|
||||||
|
// Just finding and removing one task at a time and then repeating until nothing changes
|
||||||
|
while (true) {
|
||||||
|
const completedTaskNode = findNodeMatching(tree, (node) => {
|
||||||
|
return node.type === "Task" &&
|
||||||
|
["x", "X"].includes(node.children![0].children![1].text!);
|
||||||
|
});
|
||||||
|
if (completedTaskNode) {
|
||||||
|
// Ok got one, let's remove it
|
||||||
|
const listItemNode = completedTaskNode.parent!;
|
||||||
|
const bulletListNode = listItemNode.parent!;
|
||||||
|
// Remove the list item
|
||||||
|
const listItemIdx = bulletListNode.children!.indexOf(listItemNode);
|
||||||
|
let removeItems = 1;
|
||||||
|
if (bulletListNode.children![listItemIdx + 1]?.text === "\n") {
|
||||||
|
removeItems++;
|
||||||
|
}
|
||||||
|
bulletListNode.children!.splice(listItemIdx, removeItems);
|
||||||
|
} else {
|
||||||
|
// No completed tasks left, we're done
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await editor.setText(renderToText(tree));
|
||||||
|
}
|
||||||
|
|
|
@ -33,3 +33,8 @@ functions:
|
||||||
path: ./complete.ts:completeTaskState
|
path: ./complete.ts:completeTaskState
|
||||||
events:
|
events:
|
||||||
- editor:complete
|
- editor:complete
|
||||||
|
|
||||||
|
removeCompletedTasksCommand:
|
||||||
|
path: task.ts:removeCompletedTasksCommand
|
||||||
|
command:
|
||||||
|
name: "Task: Remove Completed"
|
|
@ -19,9 +19,11 @@ class CheckboxWidget extends WidgetType {
|
||||||
checkbox.type = "checkbox";
|
checkbox.type = "checkbox";
|
||||||
checkbox.checked = this.checked;
|
checkbox.checked = this.checked;
|
||||||
checkbox.addEventListener("click", (e) => {
|
checkbox.addEventListener("click", (e) => {
|
||||||
// Let the click handler handle this
|
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
});
|
||||||
|
checkbox.addEventListener("mouseup", (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
this.clickCallback(this.pos);
|
this.clickCallback(this.pos);
|
||||||
});
|
});
|
||||||
wrap.appendChild(checkbox);
|
wrap.appendChild(checkbox);
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
import diff, { DELETE, INSERT } from "https://esm.sh/fast-diff@1.3.0";
|
||||||
|
import type { ChangeSpec } from "@codemirror/state";
|
||||||
|
|
||||||
|
export function diffAndPrepareChanges(
|
||||||
|
oldString: string,
|
||||||
|
newString: string,
|
||||||
|
): ChangeSpec[] {
|
||||||
|
// Use the fast-diff library to compute the changes
|
||||||
|
const diffs = diff(oldString, newString);
|
||||||
|
|
||||||
|
// Convert the diffs to CodeMirror transactions
|
||||||
|
let startIndex = 0;
|
||||||
|
const changes: ChangeSpec[] = [];
|
||||||
|
for (const part of diffs) {
|
||||||
|
if (part[0] === INSERT) {
|
||||||
|
changes.push({ from: startIndex, insert: part[1] });
|
||||||
|
} else if (part[0] === DELETE) {
|
||||||
|
changes.push({ from: startIndex, to: startIndex + part[1].length });
|
||||||
|
}
|
||||||
|
startIndex += part[1].length;
|
||||||
|
}
|
||||||
|
return changes;
|
||||||
|
}
|
|
@ -15,6 +15,7 @@ import type { FilterOption } from "../types.ts";
|
||||||
import { UploadFile } from "../../plug-api/types.ts";
|
import { UploadFile } from "../../plug-api/types.ts";
|
||||||
import { PageRef } from "$sb/lib/page.ts";
|
import { PageRef } from "$sb/lib/page.ts";
|
||||||
import { openSearchPanel } from "../deps.ts";
|
import { openSearchPanel } from "../deps.ts";
|
||||||
|
import { diffAndPrepareChanges } from "./cm_util.ts";
|
||||||
|
|
||||||
export function editorSyscalls(client: Client): SysCallMapping {
|
export function editorSyscalls(client: Client): SysCallMapping {
|
||||||
const syscalls: SysCallMapping = {
|
const syscalls: SysCallMapping = {
|
||||||
|
@ -24,6 +25,13 @@ export function editorSyscalls(client: Client): SysCallMapping {
|
||||||
"editor.getText": () => {
|
"editor.getText": () => {
|
||||||
return client.editorView.state.sliceDoc();
|
return client.editorView.state.sliceDoc();
|
||||||
},
|
},
|
||||||
|
"editor.setText": (_ctx, newText: string) => {
|
||||||
|
const currentText = client.editorView.state.sliceDoc();
|
||||||
|
const allChanges = diffAndPrepareChanges(currentText, newText);
|
||||||
|
client.editorView.dispatch({
|
||||||
|
changes: allChanges,
|
||||||
|
});
|
||||||
|
},
|
||||||
"editor.getCursor": (): number => {
|
"editor.getCursor": (): number => {
|
||||||
return client.editorView.state.selection.main.from;
|
return client.editorView.state.selection.main.from;
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue