Fixes #330
parent
63083d1e05
commit
43adb13fb2
|
@ -15,6 +15,14 @@ export function getText(): Promise<string> {
|
|||
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> {
|
||||
return syscall("editor.getCursor");
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import { editor, markdown, space, sync } from "$sb/syscalls.ts";
|
|||
import {
|
||||
addParentPointers,
|
||||
collectNodesMatching,
|
||||
findNodeMatching,
|
||||
findNodeOfType,
|
||||
findParentMatching,
|
||||
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));
|
||||
}
|
||||
|
|
|
@ -32,4 +32,9 @@ functions:
|
|||
taskComplete:
|
||||
path: ./complete.ts:completeTaskState
|
||||
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.checked = this.checked;
|
||||
checkbox.addEventListener("click", (e) => {
|
||||
// Let the click handler handle this
|
||||
e.stopPropagation();
|
||||
|
||||
e.preventDefault();
|
||||
});
|
||||
checkbox.addEventListener("mouseup", (e) => {
|
||||
e.stopPropagation();
|
||||
this.clickCallback(this.pos);
|
||||
});
|
||||
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 { PageRef } from "$sb/lib/page.ts";
|
||||
import { openSearchPanel } from "../deps.ts";
|
||||
import { diffAndPrepareChanges } from "./cm_util.ts";
|
||||
|
||||
export function editorSyscalls(client: Client): SysCallMapping {
|
||||
const syscalls: SysCallMapping = {
|
||||
|
@ -24,6 +25,13 @@ export function editorSyscalls(client: Client): SysCallMapping {
|
|||
"editor.getText": () => {
|
||||
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 => {
|
||||
return client.editorView.state.selection.main.from;
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue