stash
This commit is contained in:
parent
97036272dd
commit
b1e617e74a
11 changed files with 476 additions and 186 deletions
|
@ -1,8 +1,7 @@
|
|||
import { createEffect, createMemo, createSignal } from "solid-js";
|
||||
import { createEffect, createMemo, createSignal, untrack } from "solid-js";
|
||||
import { debounce } from "@solid-primitives/scheduled";
|
||||
import { Editor, splitAt, useEditor } from "~/features/editor";
|
||||
import { Editor, Index_Range, splitBy, createElement, useEditor, mergeNodes } from "~/features/editor";
|
||||
import { visitParents } from "unist-util-visit-parents";
|
||||
import findAncestor from 'unist-util-ancestor';
|
||||
import type * as hast from 'hast';
|
||||
import css from './editor.module.css';
|
||||
|
||||
|
@ -41,7 +40,7 @@ export default function Formatter(props: {}) {
|
|||
<textarea oninput={onInput} title="markdown">{value()}</textarea>
|
||||
|
||||
<div class={css.editor}>
|
||||
<Editor value={value()} oninput={setValue}>
|
||||
<Editor value={untrack(value)} oninput={setValue}>
|
||||
<Toolbar />
|
||||
<SearchAndReplace />
|
||||
</Editor>
|
||||
|
@ -52,82 +51,41 @@ export default function Formatter(props: {}) {
|
|||
function Toolbar() {
|
||||
const { mutate, selection } = useEditor();
|
||||
|
||||
const matchesAncestor = (tree: hast.Node, node: hast.Text, predicate: (node: hast.Node) => boolean) => {
|
||||
let matches = false;
|
||||
const trimWhitespaceOn = ({ startNode: startContainer, endNode: endContainer, startOffset, endOffset, ...rest }: Index_Range): Index_Range => {
|
||||
const matchStart = startContainer.value.slice(startOffset).match(/^(\s+).*?$/);
|
||||
const matchEnd = endContainer.value.slice(0, endOffset).match(/^.*?(\s+)$/);
|
||||
|
||||
visitParents(tree, n => n === node, (_, ancestors) => {
|
||||
matches = ancestors.some(predicate);
|
||||
});
|
||||
|
||||
return matches;
|
||||
}
|
||||
return {
|
||||
startNode: startContainer,
|
||||
startOffset: startOffset + (matchStart?.[1].length ?? 0),
|
||||
endNode: endContainer,
|
||||
endOffset: endOffset - (matchEnd?.[1].length ?? 0),
|
||||
...rest
|
||||
};
|
||||
};
|
||||
|
||||
const bold = () => {
|
||||
const [start, end] = selection();
|
||||
const range = selection();
|
||||
|
||||
if (!start || !end) {
|
||||
return
|
||||
if (!range) {
|
||||
return;
|
||||
}
|
||||
|
||||
mutate((ast) => {
|
||||
console.log(end.node.value.slice(0, end.offset));
|
||||
const { startNode, endNode, startOffset, endOffset, commonAncestor } = trimWhitespaceOn(range);
|
||||
|
||||
// Trim whitespace from selection
|
||||
const matchStart = start.node.value.slice(start.offset).match(/^(\s+).*?$/);
|
||||
if (matchStart !== null) {
|
||||
start.offset += matchStart[1].length;
|
||||
}
|
||||
const [left, toBold, right] = splitBy(commonAncestor(), [
|
||||
{ node: startNode, offset: startOffset },
|
||||
{ node: endNode, offset: endOffset },
|
||||
]);
|
||||
|
||||
const matchEnd = end.node.value.slice(0, end.offset).match(/^.*?(\s+)$/);
|
||||
if (matchEnd !== null) {
|
||||
end.offset -= matchEnd[1].length;
|
||||
}
|
||||
console.log(left, toBold, right);
|
||||
const boldedElement = createElement('strong', toBold.flatMap(child => child.tagName === 'strong' ? mergeNodes(child.children) : child)) as hast.RootContent;
|
||||
|
||||
// Edge case Unbold the selected characters
|
||||
if (start.node === end.node) {
|
||||
visitParents(ast, (n): n is hast.Text => n === start.node, (n, ancestors) => {
|
||||
const [strong, parent] = ancestors.toReversed();
|
||||
// THIS IS WHERE I LEFT OFF
|
||||
// AST needs to be clean!!!!
|
||||
|
||||
if (strong.type === 'element' && strong.tagName === 'strong') {
|
||||
parent.children.splice(parent.children.indexOf(strong as hast.ElementContent), 1,
|
||||
{ type: 'element', tagName: 'strong', properties: {}, children: [{ type: 'text', value: n.value.slice(0, start.offset) }] },
|
||||
{ type: 'text', value: n.value.slice(start.offset, end.offset) },
|
||||
{ type: 'element', tagName: 'strong', properties: {}, children: [{ type: 'text', value: n.value.slice(end.offset) }] },
|
||||
);
|
||||
}
|
||||
else {
|
||||
strong.children.splice(strong.children.indexOf(n), 1,
|
||||
{ type: 'text', value: n.value.slice(0, start.offset) },
|
||||
{ type: 'element', tagName: 'strong', properties: {}, children: [{ type: 'text', value: n.value.slice(start.offset, end.offset) }] },
|
||||
{ type: 'text', value: n.value.slice(end.offset) },
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return ast;
|
||||
}
|
||||
|
||||
const common = findAncestor(ast, [start.node, end.node] as const) as hast.Element;
|
||||
const startIsBold = matchesAncestor(common, start.node, (node) => node.type === 'element' && node.tagName === 'strong');
|
||||
const endIsBold = matchesAncestor(common, end.node, (node) => node.type === 'element' && node.tagName === 'strong');
|
||||
|
||||
// Extend to left
|
||||
if (startIsBold) {
|
||||
start.offset = 0;
|
||||
}
|
||||
|
||||
// Extend to right
|
||||
if (endIsBold) {
|
||||
end.offset = end.node.value.length;
|
||||
}
|
||||
|
||||
const [a, b] = splitAt(common, start.node, start.offset);
|
||||
const [c, d] = splitAt({ type: 'root', children: b }, end.node, end.offset);
|
||||
const boldedElement = { type: 'element', tagName: 'strong', children: c } as hast.RootContent;
|
||||
|
||||
common.children = [...a, boldedElement, ...d] as hast.ElementContent[];
|
||||
|
||||
console.log(c, d, common.children);
|
||||
commonAncestor().children = [...left, boldedElement, ...right];
|
||||
|
||||
return ast;
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue