diff --git a/src/editor/block/delete-line.js b/src/editor/block/delete-line.js new file mode 100644 index 0000000..9066231 --- /dev/null +++ b/src/editor/block/delete-line.js @@ -0,0 +1,46 @@ +import { EditorSelection } from "@codemirror/state" +import { getActiveNoteBlock } from "./block" + +function updateSel(sel, by) { + return EditorSelection.create(sel.ranges.map(by), sel.mainIndex); +} + +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 +} + +export const deleteLine = (view) => { + if (view.state.readOnly) + return false + + const { state } = view + + const block = getActiveNoteBlock(view.state) + const selectedLines = selectedLineBlocks(state) + + const changes = state.changes(selectedLines.map(({ from, to }) => { + if(from !== block.content.from || to !== block.content.to) { + if (from > 0) from-- + else if (to < state.doc.length) to++ + } + return { from, to } + })) + + const selection = updateSel(state.selection, range => view.moveVertically(range, true)).map(changes) + view.dispatch({ changes, selection, scrollIntoView: true, userEvent: "delete.line" }) + + return true; +} \ No newline at end of file diff --git a/src/editor/keymap.js b/src/editor/keymap.js index 0f74ec7..745aaf3 100644 --- a/src/editor/keymap.js +++ b/src/editor/keymap.js @@ -19,6 +19,7 @@ import { import { pasteCommand, copyCommand, cutCommand } from "./copy-paste.js" import { formatBlockContent } from "./block/format-code.js" +import { deleteLine } from "./block/delete-line.js" export function keymapFromSpec(specs) { @@ -59,6 +60,7 @@ export function heynoteKeymap(editor) { ["Alt-Shift-f", formatBlockContent], ["Mod-Alt-ArrowDown", newCursorBelow], ["Mod-Alt-ArrowUp", newCursorAbove], + ["Mod-Shift-k", deleteLine], {key:"Mod-ArrowUp", run:gotoPreviousBlock, shift:selectPreviousBlock}, {key:"Mod-ArrowDown", run:gotoNextBlock, shift:selectNextBlock}, {key:"Ctrl-ArrowUp", run:gotoPreviousParagraph, shift:selectPreviousParagraph}, diff --git a/tests/delete-line.spec.js b/tests/delete-line.spec.js new file mode 100644 index 0000000..fb38fc5 --- /dev/null +++ b/tests/delete-line.spec.js @@ -0,0 +1,50 @@ +import { expect, test } from "@playwright/test"; +import { HeynotePage } from "./test-utils.js"; + +let heynotePage + +test.beforeEach(async ({ page }) => { + heynotePage = new HeynotePage(page) + await heynotePage.goto() + + expect((await heynotePage.getBlocks()).length).toBe(1) + await heynotePage.setContent(` +∞∞∞text +Block A +text content 1 +text content 2 +text content 3 +text content 4 +∞∞∞text +Block B +∞∞∞text +Block C`) + + // check that blocks are created + expect((await heynotePage.getBlocks()).length).toBe(3) + + // check that visual block layers are created + await expect(page.locator("css=.heynote-blocks-layer > div")).toHaveCount(3) +}); + +test("delete line on single line in Block A", async ({ page }) => { + for (let i = 0; i < 7; i++) { + await page.locator("body").press("ArrowUp") + } + for (let i = 0; i < 4; i++) { + await page.locator("body").press(`${heynotePage.isMac ? "Meta" : "Control"}+Shift+k`) + } + expect(await heynotePage.getBlockContent(0)).toBe("\ntext content 4"); +}); + +test("delete line on selection in Block B", async ({ page }) => { + for (let i = 0; i < 1; i++) { + await page.locator("body").press("ArrowUp") + } + await page.locator("body").press(`${heynotePage.isMac ? "Meta" : "Control"}+A`) + await page.locator("body").press(`${heynotePage.isMac ? "Meta" : "Control"}+Shift+k`) + expect(await heynotePage.getBlockContent(1)).toBe(''); +}); + + +