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,114 +94,141 @@ export function changeCurrentBlockLanguage(state, dispatch, language, auto) {
changeLanguageTo(state, dispatch, block, 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 blocks = state.facet(blockState)
const newSelection = EditorSelection.create(state.selection.ranges.map(sel => { const block = getNoteBlockFromPos(state, range.head)
const block = getNoteBlockFromPos(state, sel.head) if (range.head === block.content.from) {
if (sel.head === block.content.from) { const index = blocks.indexOf(block)
const index = blocks.indexOf(block) const previousBlockIndex = index > 0 ? index - 1 : 0
const previousBlockIndex = index > 0 ? index - 1 : 0 return EditorSelection.cursor(blocks[previousBlockIndex].content.from)
return EditorSelection.cursor(blocks[previousBlockIndex].content.from) } else {
} else { return EditorSelection.cursor(block.content.from)
return EditorSelection.cursor(block.content.from) }
} }
}), state.selection.mainIndex)
dispatch(state.update({ function nextBlock(state, range) {
selection: newSelection, const blocks = state.facet(blockState)
scrollIntoView: true, const block = getNoteBlockFromPos(state, range.head)
})) if (range.head === block.content.to) {
return true 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)
}
} }
export function gotoNextBlock({state, dispatch}) { export function gotoNextBlock({state, dispatch}) {
const blocks = state.facet(blockState) return moveSel(state, dispatch, range => nextBlock(state, range))
const newSelection = EditorSelection.create(state.selection.ranges.map(sel => { }
const block = getNoteBlockFromPos(state, sel.head) export function selectNextBlock({state, dispatch}) {
if (sel.head === block.content.to) { return extendSel(state, dispatch, range => nextBlock(state, range))
const index = blocks.indexOf(block) }
const previousBlockIndex = index < blocks.length - 1 ? index + 1 : index export function gotoPreviousBlock({state, dispatch}) {
return EditorSelection.cursor(blocks[previousBlockIndex].content.to) return moveSel(state, dispatch, range => previousBlock(state, range))
} else { }
return EditorSelection.cursor(block.content.to) export function selectPreviousBlock({state, dispatch}) {
} return extendSel(state, dispatch, range => previousBlock(state, range))
}), state.selection.mainIndex)
dispatch(state.update({
selection: newSelection,
scrollIntoView: true,
}))
return true
} }
export function gotoPreviousParagraph({state, dispatch}) {
function previousParagraph(state, range) {
const blocks = state.facet(blockState) const blocks = state.facet(blockState)
const newSelection = EditorSelection.create(state.selection.ranges.map(sel => { let block = getNoteBlockFromPos(state, range.head)
let block = getNoteBlockFromPos(state, sel.head) const blockIndex = blocks.indexOf(block)
const blockIndex = blocks.indexOf(block)
let seenContentLine = false let seenContentLine = false
let pos 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 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] block = blocks[blockIndex - 1]
pos = state.doc.lineAt(block.content.to).from pos = state.doc.lineAt(block.content.to).from
} else { } else {
pos = state.doc.lineAt(sel.head).from pos = state.doc.lineAt(range.head).from
} }
while (pos > block.content.from) { while (pos > block.content.from) {
const line = state.doc.lineAt(pos) const line = state.doc.lineAt(pos)
if (line.text.replace(/\s/g, '').length == 0) { if (line.text.replace(/\s/g, '').length == 0) {
if (seenContentLine) { if (seenContentLine) {
return EditorSelection.cursor(line.from) return EditorSelection.cursor(line.from)
}
} else {
seenContentLine = true
} }
// set position to beginning go previous line } else {
pos = state.doc.lineAt(line.from - 1).from seenContentLine = true
} }
return EditorSelection.cursor(block.content.from) // set position to beginning go previous line
}), state.selection.mainIndex) pos = state.doc.lineAt(line.from - 1).from
dispatch(state.update({ }
selection: newSelection, return EditorSelection.cursor(block.content.from)
scrollIntoView: true, }
}))
return true
function nextParagraph(state, range) {
const blocks = state.facet(blockState)
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(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(range.head).to
}
while (pos < block.content.to) {
const line = state.doc.lineAt(pos)
if (line.text.replace(/\s/g, '').length == 0) {
if (seenContentLine) {
return EditorSelection.cursor(line.from)
}
} else {
seenContentLine = true
}
// set position to beginning go previous line
pos = state.doc.lineAt(line.to + 1).to
}
return EditorSelection.cursor(block.content.to)
} }
export function gotoNextParagraph({state, dispatch}) { export function gotoNextParagraph({state, dispatch}) {
const blocks = state.facet(blockState) return moveSel(state, dispatch, range => nextParagraph(state, range))
const newSelection = EditorSelection.create(state.selection.ranges.map(sel => {
let block = getNoteBlockFromPos(state, sel.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) {
block = blocks[blockIndex + 1]
pos = state.doc.lineAt(block.content.from).to
} else {
pos = state.doc.lineAt(sel.head).to
}
while (pos < block.content.to) {
const line = state.doc.lineAt(pos)
if (line.text.replace(/\s/g, '').length == 0) {
if (seenContentLine) {
return EditorSelection.cursor(line.from)
}
} else {
seenContentLine = true
}
// set position to beginning go previous line
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 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 { EditorView, keymap } from "@codemirror/view"
import { EditorSelection } from "@codemirror/state" import { EditorSelection } from "@codemirror/state"
import { indentWithTab, insertTab, indentLess, indentMore, undo, redo } from "@codemirror/commands" 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) { export function heynoteKeymap(editor) {
return keymap.of([ return keymap.of([
@ -14,8 +23,12 @@ export function heynoteKeymap(editor) {
["Alt-ArrowDown", moveLineDown], ["Alt-ArrowDown", moveLineDown],
["Mod-ArrowUp", gotoPreviousBlock], ["Mod-ArrowUp", gotoPreviousBlock],
["Mod-ArrowDown", gotoNextBlock], ["Mod-ArrowDown", gotoNextBlock],
["Mod-Shift-ArrowUp", selectPreviousBlock],
["Mod-Shift-ArrowDown", selectNextBlock],
["Ctrl-ArrowUp", gotoPreviousParagraph], ["Ctrl-ArrowUp", gotoPreviousParagraph],
["Ctrl-ArrowDown", gotoNextParagraph], ["Ctrl-ArrowDown", gotoNextParagraph],
["Ctrl-Shift-ArrowUp", selectPreviousParagraph],
["Ctrl-Shift-ArrowDown", selectNextParagraph],
["Mod-l", () => editor.openLanguageSelector()], ["Mod-l", () => editor.openLanguageSelector()],
].map(([key, run]) => { ].map(([key, run]) => {
return { return {