Add commands for selecting next/previous block/paragraph

This commit is contained in:
Jonatan Heyman 2023-01-24 16:16:54 +01:00
parent ed13baf4e4
commit a5d47d81f6
2 changed files with 137 additions and 97 deletions

View File

@ -94,58 +94,81 @@ export function changeCurrentBlockLanguage(state, dispatch, language, auto) {
changeLanguageTo(state, dispatch, block, language, auto)
}
export function gotoPreviousBlock({state, dispatch}) {
function updateSel(sel, by) {
return EditorSelection.create(sel.ranges.map(by), sel.mainIndex);
}
function setSel(state, selection) {
return state.update({ selection, scrollIntoView: true, userEvent: "select" });
}
function extendSel(state, dispatch, how) {
let selection = updateSel(state.selection, range => {
let head = how(range);
return EditorSelection.range(range.anchor, head.head, head.goalColumn, head.bidiLevel || undefined);
});
if (selection.eq(state.selection))
return false;
dispatch(setSel(state, selection));
return true;
}
function moveSel(state, dispatch, how) {
let selection = updateSel(state.selection, how);
if (selection.eq(state.selection))
return false;
dispatch(setSel(state, selection));
return true;
}
function previousBlock(state, range) {
const blocks = state.facet(blockState)
const newSelection = EditorSelection.create(state.selection.ranges.map(sel => {
const block = getNoteBlockFromPos(state, sel.head)
if (sel.head === block.content.from) {
const block = getNoteBlockFromPos(state, range.head)
if (range.head === block.content.from) {
const index = blocks.indexOf(block)
const previousBlockIndex = index > 0 ? index - 1 : 0
return EditorSelection.cursor(blocks[previousBlockIndex].content.from)
} else {
return EditorSelection.cursor(block.content.from)
}
}), state.selection.mainIndex)
dispatch(state.update({
selection: newSelection,
scrollIntoView: true,
}))
return true
}
export function gotoNextBlock({state, dispatch}) {
function nextBlock(state, range) {
const blocks = state.facet(blockState)
const newSelection = EditorSelection.create(state.selection.ranges.map(sel => {
const block = getNoteBlockFromPos(state, sel.head)
if (sel.head === block.content.to) {
const block = getNoteBlockFromPos(state, range.head)
if (range.head === block.content.to) {
const index = blocks.indexOf(block)
const previousBlockIndex = index < blocks.length - 1 ? index + 1 : index
return EditorSelection.cursor(blocks[previousBlockIndex].content.to)
} else {
return EditorSelection.cursor(block.content.to)
}
}), state.selection.mainIndex)
dispatch(state.update({
selection: newSelection,
scrollIntoView: true,
}))
return true
}
export function gotoPreviousParagraph({state, dispatch}) {
export function gotoNextBlock({state, dispatch}) {
return moveSel(state, dispatch, range => nextBlock(state, range))
}
export function selectNextBlock({state, dispatch}) {
return extendSel(state, dispatch, range => nextBlock(state, range))
}
export function gotoPreviousBlock({state, dispatch}) {
return moveSel(state, dispatch, range => previousBlock(state, range))
}
export function selectPreviousBlock({state, dispatch}) {
return extendSel(state, dispatch, range => previousBlock(state, range))
}
function previousParagraph(state, range) {
const blocks = state.facet(blockState)
const newSelection = EditorSelection.create(state.selection.ranges.map(sel => {
let block = getNoteBlockFromPos(state, sel.head)
let block = getNoteBlockFromPos(state, range.head)
const blockIndex = blocks.indexOf(block)
let seenContentLine = false
let pos
// if we're on the first row of a block, and it's not the first block, we start from the end of the previous block
if (state.doc.lineAt(sel.head).from === block.content.from && blockIndex > 0) {
if (state.doc.lineAt(range.head).from === block.content.from && blockIndex > 0) {
block = blocks[blockIndex - 1]
pos = state.doc.lineAt(block.content.to).from
} else {
pos = state.doc.lineAt(sel.head).from
pos = state.doc.lineAt(range.head).from
}
while (pos > block.content.from) {
@ -161,28 +184,22 @@ export function gotoPreviousParagraph({state, dispatch}) {
pos = state.doc.lineAt(line.from - 1).from
}
return EditorSelection.cursor(block.content.from)
}), state.selection.mainIndex)
dispatch(state.update({
selection: newSelection,
scrollIntoView: true,
}))
return true
}
export function gotoNextParagraph({state, dispatch}) {
function nextParagraph(state, range) {
const blocks = state.facet(blockState)
const newSelection = EditorSelection.create(state.selection.ranges.map(sel => {
let block = getNoteBlockFromPos(state, sel.head)
let block = getNoteBlockFromPos(state, range.head)
const blockIndex = blocks.indexOf(block)
let seenContentLine = false
let pos
// if we're at the last line of a block, and it's not the last block, we start from the beginning of the next block
if (state.doc.lineAt(sel.head).to === block.content.to && blockIndex < blocks.length - 1) {
if (state.doc.lineAt(range.head).to === block.content.to && blockIndex < blocks.length - 1) {
block = blocks[blockIndex + 1]
pos = state.doc.lineAt(block.content.from).to
} else {
pos = state.doc.lineAt(sel.head).to
pos = state.doc.lineAt(range.head).to
}
while (pos < block.content.to) {
@ -198,10 +215,20 @@ export function gotoNextParagraph({state, dispatch}) {
pos = state.doc.lineAt(line.to + 1).to
}
return EditorSelection.cursor(block.content.to)
}), state.selection.mainIndex)
dispatch(state.update({
selection: newSelection,
scrollIntoView: true,
}))
return true
}
export function gotoNextParagraph({state, dispatch}) {
return moveSel(state, dispatch, range => nextParagraph(state, range))
}
export function selectNextParagraph({state, dispatch}) {
return extendSel(state, dispatch, range => nextParagraph(state, range))
}
export function gotoPreviousParagraph({state, dispatch}) {
return moveSel(state, dispatch, range => previousParagraph(state, range))
}
export function selectPreviousParagraph({state, dispatch}) {
return extendSel(state, dispatch, range => previousParagraph(state, range))
}

View File

@ -1,7 +1,16 @@
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, moveLineDown, selectAll, gotoPreviousBlock, gotoNextBlock, gotoPreviousParagraph, gotoNextParagraph } from "./block/commands.js";
import {
insertNewBlockAtCursor,
addNewBlockAfterCurrent,
moveLineUp, moveLineDown,
selectAll,
gotoPreviousBlock, gotoNextBlock,
selectNextBlock, selectPreviousBlock,
gotoPreviousParagraph, gotoNextParagraph,
selectNextParagraph, selectPreviousParagraph,
} from "./block/commands.js";
export function heynoteKeymap(editor) {
return keymap.of([
@ -14,8 +23,12 @@ export function heynoteKeymap(editor) {
["Alt-ArrowDown", moveLineDown],
["Mod-ArrowUp", gotoPreviousBlock],
["Mod-ArrowDown", gotoNextBlock],
["Mod-Shift-ArrowUp", selectPreviousBlock],
["Mod-Shift-ArrowDown", selectNextBlock],
["Ctrl-ArrowUp", gotoPreviousParagraph],
["Ctrl-ArrowDown", gotoNextParagraph],
["Ctrl-Shift-ArrowUp", selectPreviousParagraph],
["Ctrl-Shift-ArrowDown", selectNextParagraph],
["Mod-l", () => editor.openLanguageSelector()],
].map(([key, run]) => {
return {