From 99a6d0fcc7204ba19a84b39deea608d69df34e3e Mon Sep 17 00:00:00 2001 From: Jonatan Heyman Date: Sat, 21 Jan 2023 16:01:11 +0100 Subject: [PATCH] Copy moveLineUp/moveLineDown command from Codemirror source into move-lines.js. Plan is to modify them to fix issue when moving the only line from a a single line block. When that happens it results in the following text data in the buffer: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ∞∞∞text ∞∞∞text And the separator needs a newline on both sides to be valid --- src/editor/block/commands.js | 18 ++------- src/editor/block/move-lines.js | 73 ++++++++++++++++++++++++++++++++++ src/editor/keymap.js | 3 +- 3 files changed, 78 insertions(+), 16 deletions(-) create mode 100644 src/editor/block/move-lines.js diff --git a/src/editor/block/commands.js b/src/editor/block/commands.js index 23748a7..b934bed 100644 --- a/src/editor/block/commands.js +++ b/src/editor/block/commands.js @@ -7,6 +7,9 @@ import { import { heynoteEvent, LANGUAGE_CHANGE } from "../annotation.js"; import { blockState, getActiveNoteBlock, getNoteBlockFromPos } from "./block" import { levenshtein_distance } from "../language-detection/levenshtein" +import { moveLineDown, moveLineUp } from "./move-lines.js"; + +export { moveLineDown, moveLineUp } export const insertNewBlockAtCursor = ({ state, dispatch }) => { @@ -60,21 +63,6 @@ export const selectAll = ({ state, dispatch }) => { return true } -/** - * Prevent moveLineUp from executing if any cursor is on the first line of the first note - */ -export function moveLineUp({ state, dispatch }) { - if (state.readOnly) - return false - if (state.selection.ranges.some(range => { - let startLine = state.doc.lineAt(range.from) - return startLine.from <= state.facet(blockState)[0].content.from - })) { - return true; - } - return defaultMoveLineUp({state, dispatch}) -} - export function changeLanguageTo(state, dispatch, block, language, auto) { if (state.readOnly) diff --git a/src/editor/block/move-lines.js b/src/editor/block/move-lines.js new file mode 100644 index 0000000..d4d3ab6 --- /dev/null +++ b/src/editor/block/move-lines.js @@ -0,0 +1,73 @@ +import { EditorSelection } from "@codemirror/state" +import { blockState } from "./block" + + +function selectedLineBlocks(state) { + let blocks = [], upto = -1; + for (let range of state.selection.ranges) { + let startLine = state.doc.lineAt(range.from), endLine = state.doc.lineAt(range.to); + if (!range.empty && range.to == endLine.from) + endLine = state.doc.lineAt(range.to - 1); + if (upto >= startLine.number) { + let prev = blocks[blocks.length - 1]; + prev.to = endLine.to; + prev.ranges.push(range); + } + else { + blocks.push({ from: startLine.from, to: endLine.to, ranges: [range] }); + } + upto = endLine.number + 1; + } + return blocks; +} + +function moveLine(state, dispatch, forward) { + if (state.readOnly) + return false; + let changes = [], ranges = []; + for (let block of selectedLineBlocks(state)) { + if (forward ? block.to == state.doc.length : block.from == 0) + continue; + let nextLine = state.doc.lineAt(forward ? block.to + 1 : block.from - 1); + let size = nextLine.length + 1; + if (forward) { + changes.push({ from: block.to, to: nextLine.to }, { from: block.from, insert: nextLine.text + state.lineBreak }); + for (let r of block.ranges) + ranges.push(EditorSelection.range(Math.min(state.doc.length, r.anchor + size), Math.min(state.doc.length, r.head + size))); + } + else { + changes.push({ from: nextLine.from, to: block.from }, { from: block.to, insert: state.lineBreak + nextLine.text }); + for (let r of block.ranges) + ranges.push(EditorSelection.range(r.anchor - size, r.head - size)); + } + } + if (!changes.length) + return false; + dispatch(state.update({ + changes, + scrollIntoView: true, + selection: EditorSelection.create(ranges, state.selection.mainIndex), + userEvent: "move.line" + })); + return true; +} + +/** +Move the selected lines up one line. +*/ +export const moveLineUp = ({ state, dispatch }) => { + // prevent moving lines up before the first block separator + if (state.selection.ranges.some(range => { + let startLine = state.doc.lineAt(range.from) + return startLine.from <= state.facet(blockState)[0].content.from + })) { + return true; + } + + return moveLine(state, dispatch, false) +} + +/** +Move the selected lines down one line. +*/ +export const moveLineDown = ({ state, dispatch }) => moveLine(state, dispatch, true); diff --git a/src/editor/keymap.js b/src/editor/keymap.js index 5d57940..bd197b7 100644 --- a/src/editor/keymap.js +++ b/src/editor/keymap.js @@ -1,7 +1,7 @@ import { EditorView, keymap } from "@codemirror/view" import { EditorSelection } from "@codemirror/state" import { indentWithTab, insertTab, indentLess, indentMore, undo, redo } from "@codemirror/commands" -import { insertNewBlockAtCursor, addNewBlockAfterCurrent, moveLineUp, selectAll, gotoPreviousBlock, gotoNextBlock, gotoPreviousParagraph, gotoNextParagraph } from "./block/commands.js"; +import { insertNewBlockAtCursor, addNewBlockAfterCurrent, moveLineUp, moveLineDown, selectAll, gotoPreviousBlock, gotoNextBlock, gotoPreviousParagraph, gotoNextParagraph } from "./block/commands.js"; export function heynoteKeymap(editor) { return keymap.of([ @@ -11,6 +11,7 @@ export function heynoteKeymap(editor) { ["Mod-Shift-Enter", insertNewBlockAtCursor], ["Mod-a", selectAll], ["Alt-ArrowUp", moveLineUp], + ["Alt-ArrowDown", moveLineDown], ["Mod-ArrowUp", gotoPreviousBlock], ["Mod-ArrowDown", gotoNextBlock], ["Ctrl-ArrowUp", gotoPreviousParagraph],