Outline: enable moving items across boundaries

pull/612/head
Zef Hemel 2023-12-18 16:44:04 +01:00
parent 8d85031a14
commit 8b860194d7
1 changed files with 63 additions and 18 deletions

View File

@ -6,27 +6,52 @@ export async function moveItemUp() {
try { try {
const currentItemBounds = determineItemBounds(text, cursorPos); const currentItemBounds = determineItemBounds(text, cursorPos);
const previousItemBounds = determineItemBounds( let previousItemBounds: ReturnType<typeof determineItemBounds> | undefined;
text,
currentItemBounds.from - 1,
currentItemBounds.indentLevel,
);
if (currentItemBounds.from === previousItemBounds.from) { try {
throw new Error("Already at the top"); previousItemBounds = determineItemBounds(
text,
currentItemBounds.from - 1,
currentItemBounds.indentLevel,
);
if (currentItemBounds.from === previousItemBounds.from) {
throw new Error("Already at the top");
}
} catch {
// Ok, top of the list, let's find the previous item at any other indent level and adapt
previousItemBounds = determineItemBounds(
text,
currentItemBounds.from - 1,
);
}
let newPreviousText = text.slice(
previousItemBounds.from,
previousItemBounds.to,
);
// If the current item is embedded inside the previous item, we need to strip it out
if (
currentItemBounds.from >= previousItemBounds.from &&
currentItemBounds.to <= previousItemBounds.to
) {
newPreviousText =
text.slice(previousItemBounds.from, currentItemBounds.from) +
text.slice(currentItemBounds.to, previousItemBounds.to);
} }
const newText = const newText =
ensureNewLine(text.slice(currentItemBounds.from, currentItemBounds.to)) + ensureNewLine(text.slice(currentItemBounds.from, currentItemBounds.to)) +
text.slice(previousItemBounds.from, previousItemBounds.to); newPreviousText;
const newCursorPos = (cursorPos - currentItemBounds.from) + const newCursorPos = (cursorPos - currentItemBounds.from) +
previousItemBounds.from; previousItemBounds.from;
// console.log("New replacement text", newText);
await editor.dispatch({ await editor.dispatch({
changes: [ changes: [
{ {
from: previousItemBounds.from, from: Math.min(previousItemBounds.from, currentItemBounds.from),
to: currentItemBounds.to, to: Math.max(currentItemBounds.to, previousItemBounds.to),
insert: newText, insert: newText,
}, },
], ],
@ -45,19 +70,34 @@ export async function moveItemDown() {
try { try {
const currentItemBounds = determineItemBounds(text, cursorPos); const currentItemBounds = determineItemBounds(text, cursorPos);
const nextItemBounds = determineItemBounds( let nextItemBounds: ReturnType<typeof determineItemBounds> | undefined;
text, try {
currentItemBounds.to + 1, nextItemBounds = determineItemBounds(
currentItemBounds.indentLevel, text,
); currentItemBounds.to + 1,
currentItemBounds.indentLevel,
);
if (currentItemBounds.from === nextItemBounds.from) { if (currentItemBounds.from === nextItemBounds.from) {
throw new Error("Already at the bottom");
}
} catch {
nextItemBounds = determineItemBounds(
text,
currentItemBounds.to + 1,
undefined,
false,
);
}
if (currentItemBounds.to === nextItemBounds.to) {
throw new Error("Already at the bottom"); throw new Error("Already at the bottom");
} }
const nextItemText = ensureNewLine( const nextItemText = ensureNewLine(
text.slice(nextItemBounds.from, nextItemBounds.to), text.slice(nextItemBounds.from, nextItemBounds.to),
); );
// console.log("Next item text", nextItemText);
const newText = nextItemText + const newText = nextItemText +
text.slice(currentItemBounds.from, currentItemBounds.to); text.slice(currentItemBounds.from, currentItemBounds.to);
const newCursorPos = (cursorPos - currentItemBounds.from) + const newCursorPos = (cursorPos - currentItemBounds.from) +
@ -65,8 +105,8 @@ export async function moveItemDown() {
await editor.dispatch({ await editor.dispatch({
changes: [ changes: [
{ {
from: currentItemBounds.from, from: Math.min(nextItemBounds.from, currentItemBounds.from),
to: nextItemBounds.to, to: Math.max(nextItemBounds.to, currentItemBounds.to),
insert: newText, insert: newText,
}, },
], ],
@ -152,6 +192,7 @@ function determineItemBounds(
text: string, text: string,
pos: number, pos: number,
minIndentLevel?: number, minIndentLevel?: number,
withChildren = true,
): { from: number; to: number; indentLevel: number } { ): { from: number; to: number; indentLevel: number } {
// Find the start of the item marked with a bullet // Find the start of the item marked with a bullet
let currentItemStart = pos; let currentItemStart = pos;
@ -199,6 +240,10 @@ function determineItemBounds(
while (currentItemEnd < text.length && text[currentItemEnd - 1] !== "\n") { while (currentItemEnd < text.length && text[currentItemEnd - 1] !== "\n") {
currentItemEnd++; currentItemEnd++;
} }
if (!withChildren) {
// We're not interested in the children, so let's stop here
break;
}
// Check the indent level of the next line // Check the indent level of the next line
let nextIndentLevel = 0; let nextIndentLevel = 0;
while (text[currentItemEnd + nextIndentLevel] === " ") { while (text[currentItemEnd + nextIndentLevel] === " ") {