Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions src/renderer/editor/codemirror/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,44 @@ export const toggleOrderedListCommand = (
prefixForLine: (lineIndex) => `${lineIndex + 1}. `,
});

export const continueOrderedListCommand = (
view: import("@codemirror/view").EditorView
): boolean => {
const { from, to } = view.state.selection.main;
if (from !== to) {
return false;
}

const line = view.state.doc.lineAt(from);
const match = line.text.match(/^(\s*)(\d+)([.)])\s+(.*)$/);
if (!match) {
return false;
}

const [, indent, numberText, delimiter, content] = match;
if (content.trim() === "") {
view.dispatch({
changes: {
from: line.from,
to: line.to,
insert: indent,
},
selection: { anchor: line.from + indent.length },
scrollIntoView: true,
});
return true;
}

const nextNumber = Number.parseInt(numberText, 10) + 1;
const insert = `\n${indent}${nextNumber}${delimiter} `;
view.dispatch({
changes: { from, to, insert },
selection: { anchor: from + insert.length },
scrollIntoView: true,
});
return true;
};

export const toggleTaskListCommand = (
view: import("@codemirror/view").EditorView
): boolean =>
Expand Down
11 changes: 9 additions & 2 deletions src/renderer/editor/codemirror/extensions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { buildThemeExtension } from "./theme";
import { hybridMarkdown } from "./hybridMarkdown";
import { linkClickHandler } from "./links";
import { createReactSearchPanel } from "./searchPanel";
import { continueOrderedListCommand } from "./commands";

type ExtensionOptions = {
renderMode: RenderMode;
Expand All @@ -37,7 +38,13 @@ export type ExtensionBundle = {
};

export const buildBaseKeymap =
(): import("@codemirror/view").KeyBinding[] => [...searchKeymap];
(): import("@codemirror/view").KeyBinding[] => [
{
key: "Enter",
run: continueOrderedListCommand,
},
...searchKeymap,
Comment on lines +43 to +46

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Move ordered-list Enter binding before default keymaps

This Enter binding is added to buildBaseKeymap, but keymapExtension appends base bindings after bindings, and bindings includes CodeMirror defaultKeymap from app.tsx (which already handles Enter via newline/indent). In practice, pressing Enter in an ordered list will be consumed by the earlier default handler, so continueOrderedListCommand never runs and the new feature is effectively inactive.

Useful? React with 👍 / 👎.

];
Comment on lines 40 to +47

export const renderModeExtension = (mode: RenderMode): Extension => {
if (mode === "hybrid") {
Expand All @@ -49,7 +56,7 @@ export const renderModeExtension = (mode: RenderMode): Extension => {
export const keymapExtension = (
bindings: import("@codemirror/view").KeyBinding[],
base: import("@codemirror/view").KeyBinding[]
): Extension => keymap.of([...bindings, ...base]);
): Extension => keymap.of([...base, ...bindings]);

export const createCmExtensions = (
options: ExtensionOptions
Expand Down
31 changes: 31 additions & 0 deletions tests/controllerShortcuts.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { strict as assert } from "assert";
import {
continueOrderedListCommand,
createSnippetCommand,
toggleBlockquoteCommand,
toggleFencedCodeBlockCommand,
Expand Down Expand Up @@ -74,6 +75,7 @@ class FakeView {
| { from: number; to?: number; insert: string }
| Array<{ from: number; to?: number; insert: string }>;
selection?: { anchor: number; head?: number };
scrollIntoView?: boolean;
}): void {
const changes = Array.isArray(spec.changes)
? spec.changes
Expand Down Expand Up @@ -163,6 +165,35 @@ runTest("ordered list command numbers selected lines", () => {
assert.equal(view.text, "1. one\n2. two");
});

runTest("enter continues ordered list numbers", () => {
const view = new FakeView("7. first", { from: 8, to: 8 });

const handled = continueOrderedListCommand(view as unknown as EditorView);

assert.equal(handled, true);
assert.equal(view.text, "7. first\n8. ");
assert.deepEqual(view.state.selection.main, { from: 12, to: 12 });
});

runTest("enter preserves ordered list delimiter style", () => {
const view = new FakeView("2) first", { from: 8, to: 8 });

const handled = continueOrderedListCommand(view as unknown as EditorView);

assert.equal(handled, true);
assert.equal(view.text, "2) first\n3) ");
});

runTest("enter exits an empty ordered list item", () => {
const view = new FakeView(" 3. ", { from: 7, to: 7 });

const handled = continueOrderedListCommand(view as unknown as EditorView);

assert.equal(handled, true);
assert.equal(view.text, " ");
assert.deepEqual(view.state.selection.main, { from: 2, to: 2 });
});

runTest("task list command toggles checklist markers", () => {
const view = new FakeView("one\ntwo", { from: 0, to: 7 });

Expand Down
Loading