Skip to content

feat: add macro editor cursor navigation commands#1113

Merged
chhoumann merged 2 commits intomasterfrom
304-feature-request-extend-editor-commands-for-macors
Feb 22, 2026
Merged

feat: add macro editor cursor navigation commands#1113
chhoumann merged 2 commits intomasterfrom
304-feature-request-extend-editor-commands-for-macors

Conversation

@chhoumann
Copy link
Copy Markdown
Owner

@chhoumann chhoumann commented Feb 22, 2026

Summary

  • add four new QuickAdd macro editor commands for cursor navigation:
    • Move cursor to file start
    • Move cursor to file end
    • Move cursor to line start
    • Move cursor to line end
  • wire the new commands into macro execution in MacroChoiceEngine
  • expose the new commands in Macro Builder's Editor commands dropdown
  • add unit tests for command behavior and dispatch coverage

Validation

  • bun run test
  • bun run build-with-lint
  • verified in Obsidian dev vault via CLI eval harness:
    • each new command executed through a temporary QuickAdd macro choice
    • cursor positions matched expected results for all four commands
    • obsidian vault=dev dev:errors reported no captured errors

Closes #304

Summary by CodeRabbit

  • New Features

    • Four new cursor-movement editor commands: move to file start, file end, line start, and line end — available when creating editor command sequences and in the editor command dropdown.
  • Tests

    • Added unit tests covering each new cursor command and error handling when no active editor is present.

Open with Devin

@chhoumann chhoumann linked an issue Feb 22, 2026 that may be closed by this pull request
@vercel
Copy link
Copy Markdown

vercel Bot commented Feb 22, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
quickadd Ready Ready Preview Feb 22, 2026 6:08pm

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 22, 2026

📝 Walkthrough

Walkthrough

Adds four editor cursor-movement commands (file/line start/end), integrates them into the enum, UI, and MacroChoiceEngine dispatch, and includes unit tests for engine dispatch and command behavior (including missing active view error handling).

Changes

Cohort / File(s) Summary
New Cursor Movement Commands
src/types/macros/EditorCommands/MoveCursorToFileStartCommand.ts, src/types/macros/EditorCommands/MoveCursorToFileEndCommand.ts, src/types/macros/EditorCommands/MoveCursorToLineStartCommand.ts, src/types/macros/EditorCommands/MoveCursorToLineEndCommand.ts
Added four EditorCommand subclasses with static run(app: App) implementations that move the active markdown editor cursor to file start/end and line start/end.
Type Definitions
src/types/macros/EditorCommands/EditorCommandType.ts
Extended EditorCommandType enum with MoveCursorToFileStart, MoveCursorToFileEnd, MoveCursorToLineStart, and MoveCursorToLineEnd.
Engine Integration
src/engine/MacroChoiceEngine.ts
Expanded executeEditorCommand switch to dispatch the four new MoveCursor...Command.run(app) cases.
UI Integration
src/gui/MacroGUIs/CommandSequenceEditor.ts
Added imports and runtime switch cases to instantiate the four new MoveCursor commands; added options to the editor-command dropdown.
Unit Tests
src/engine/MacroChoiceEngine.editorCommands.test.ts, src/types/macros/EditorCommands/navigationCommands.test.ts
New tests: MacroChoiceEngine dispatch unit tests (spying on command.run) and navigation command tests validating cursor positions and error when no active markdown view.

Sequence Diagram(s)

sequenceDiagram
  participant UI as "CommandSequenceEditor (UI)"
  participant Engine as "MacroChoiceEngine"
  participant Cmd as "MoveCursor Command"
  participant App as "Obsidian App / Editor"

  UI->>Engine: user selects/saves editor command (EditorCommandType)
  Engine->>Cmd: map type → instantiate/choose command
  Engine->>Cmd: invoke static run(app)
  Cmd->>App: query active markdown view / editor
  Cmd->>App: setCursor(line, ch)
  App-->>Cmd: cursor updated
  Cmd-->>Engine: completed
  Engine-->>UI: update state / confirm
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A hop, a skip, the cursor darts,

To file first line or final parts,
Line start, line end — swift and neat,
Macro paws make movements fleet,
Rabbity cheers for tidy feats. 🥕

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely summarizes the main change: adding four new macro editor cursor navigation commands for file and line navigation.
Linked Issues check ✅ Passed The pull request fully implements the primary coding requirements from issue #304: adds four cursor navigation commands (file start/end, line start/end) that enable non-JavaScript users to perform common editor navigation within macros.
Out of Scope Changes check ✅ Passed All changes are directly scoped to implementing the four cursor navigation commands requested in issue #304; no unrelated modifications or feature creep is present.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 304-feature-request-extend-editor-commands-for-macors

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented Feb 22, 2026

Deploying quickadd with  Cloudflare Pages  Cloudflare Pages

Latest commit: b9de75c
Status: ✅  Deploy successful!
Preview URL: https://eace0f38.quickadd.pages.dev
Branch Preview URL: https://304-feature-request-extend-e.quickadd.pages.dev

View logs

Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

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

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 4 additional findings.

Open in Devin Review

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
src/engine/MacroChoiceEngine.editorCommands.test.ts (1)

42-136: Optional: extract spy setup from each test into beforeEach to reduce duplication.

The same four-spy block (lines 43–54, 67–78, 91–102, 115–126) is copy-pasted into every test case. Since vi.restoreAllMocks() already runs between tests, moving setup to beforeEach eliminates the repetition without any behavioural change.

♻️ Suggested refactor
+	let fileStartSpy: ReturnType<typeof vi.spyOn>;
+	let fileEndSpy: ReturnType<typeof vi.spyOn>;
+	let lineStartSpy: ReturnType<typeof vi.spyOn>;
+	let lineEndSpy: ReturnType<typeof vi.spyOn>;
+
 	beforeEach(() => {
 		vi.restoreAllMocks();
+		fileStartSpy = vi
+			.spyOn(MoveCursorToFileStartCommand, "run")
+			.mockImplementation(() => undefined);
+		fileEndSpy = vi
+			.spyOn(MoveCursorToFileEndCommand, "run")
+			.mockImplementation(() => undefined);
+		lineStartSpy = vi
+			.spyOn(MoveCursorToLineStartCommand, "run")
+			.mockImplementation(() => undefined);
+		lineEndSpy = vi
+			.spyOn(MoveCursorToLineEndCommand, "run")
+			.mockImplementation(() => undefined);
 	});

 	it("dispatches MoveCursorToFileStart", async () => {
-		const fileStartSpy = vi
-			.spyOn(MoveCursorToFileStartCommand, "run")
-			.mockImplementation(() => undefined);
-		const fileEndSpy = vi
-			.spyOn(MoveCursorToFileEndCommand, "run")
-			.mockImplementation(() => undefined);
-		const lineStartSpy = vi
-			.spyOn(MoveCursorToLineStartCommand, "run")
-			.mockImplementation(() => undefined);
-		const lineEndSpy = vi
-			.spyOn(MoveCursorToLineEndCommand, "run")
-			.mockImplementation(() => undefined);
-
 		const app = await callExecuteEditorCommand(
 			EditorCommandType.MoveCursorToFileStart
 		);
 		// ... remaining tests follow the same pattern
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/engine/MacroChoiceEngine.editorCommands.test.ts` around lines 42 - 136,
Tests duplicate the same four spy setups in each it block; move that repeated
setup into a beforeEach to DRY the tests. Add a beforeEach that spies on
MoveCursorToFileStartCommand.run, MoveCursorToFileEndCommand.run,
MoveCursorToLineStartCommand.run and MoveCursorToLineEndCommand.run (using
vi.spyOn(...).mockImplementation(() => undefined)) so each test can call
callExecuteEditorCommand(EditorCommandType...) and assert the appropriate spy
was/was not called; keep vi.restoreAllMocks() between tests as-is.
src/gui/MacroGUIs/CommandSequenceEditor.ts (1)

339-355: Consider human-readable labels for the new multi-word dropdown entries.

The existing short entries (Copy, Cut, Paste) happen to be readable as raw enum strings, but the new compound names render as MoveCursorToFileStart, MoveCursorToFileEnd, etc. — a single PascalCase blob. Passing a separate display string as the second argument to .addOption() would improve clarity for users:

✨ Optional: use readable display labels
-				.addOption(
-					EditorCommandType.MoveCursorToFileStart,
-					EditorCommandType.MoveCursorToFileStart
-				)
-				.addOption(
-					EditorCommandType.MoveCursorToFileEnd,
-					EditorCommandType.MoveCursorToFileEnd
-				)
-				.addOption(
-					EditorCommandType.MoveCursorToLineStart,
-					EditorCommandType.MoveCursorToLineStart
-				)
-				.addOption(
-					EditorCommandType.MoveCursorToLineEnd,
-					EditorCommandType.MoveCursorToLineEnd
-				)
+				.addOption(
+					EditorCommandType.MoveCursorToFileStart,
+					"Move cursor to file start"
+				)
+				.addOption(
+					EditorCommandType.MoveCursorToFileEnd,
+					"Move cursor to file end"
+				)
+				.addOption(
+					EditorCommandType.MoveCursorToLineStart,
+					"Move cursor to line start"
+				)
+				.addOption(
+					EditorCommandType.MoveCursorToLineEnd,
+					"Move cursor to line end"
+				)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/gui/MacroGUIs/CommandSequenceEditor.ts` around lines 339 - 355, The
dropdown options using EditorCommandType enums (e.g.,
EditorCommandType.MoveCursorToFileStart, MoveCursorToFileEnd,
MoveCursorToLineStart, MoveCursorToLineEnd) are displayed as raw PascalCase;
update the addOption calls that add these values (the chain that calls
.addOption(...)) to pass a human-readable label as the second argument (for
example "Move Cursor to File Start", "Move Cursor to File End", "Move Cursor to
Line Start", "Move Cursor to Line End") so the UI shows friendly text while
still storing the enum value.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/engine/MacroChoiceEngine.editorCommands.test.ts`:
- Around line 42-136: Tests duplicate the same four spy setups in each it block;
move that repeated setup into a beforeEach to DRY the tests. Add a beforeEach
that spies on MoveCursorToFileStartCommand.run, MoveCursorToFileEndCommand.run,
MoveCursorToLineStartCommand.run and MoveCursorToLineEndCommand.run (using
vi.spyOn(...).mockImplementation(() => undefined)) so each test can call
callExecuteEditorCommand(EditorCommandType...) and assert the appropriate spy
was/was not called; keep vi.restoreAllMocks() between tests as-is.

In `@src/gui/MacroGUIs/CommandSequenceEditor.ts`:
- Around line 339-355: The dropdown options using EditorCommandType enums (e.g.,
EditorCommandType.MoveCursorToFileStart, MoveCursorToFileEnd,
MoveCursorToLineStart, MoveCursorToLineEnd) are displayed as raw PascalCase;
update the addOption calls that add these values (the chain that calls
.addOption(...)) to pass a human-readable label as the second argument (for
example "Move Cursor to File Start", "Move Cursor to File End", "Move Cursor to
Line Start", "Move Cursor to Line End") so the UI shows friendly text while
still storing the enum value.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
src/engine/MacroChoiceEngine.editorCommands.test.ts (2)

43-60: Consolidate the two beforeEach blocks into one.

Two back-to-back beforeEach hooks at the same describe level are valid but needlessly split. vi.restoreAllMocks() and the spy setup are a single logical unit.

♻️ Proposed consolidation
-	beforeEach(() => {
-		vi.restoreAllMocks();
-	});
-
-	beforeEach(() => {
-		fileStartSpy = vi
+	beforeEach(() => {
+		vi.restoreAllMocks();
+		fileStartSpy = vi
 			.spyOn(MoveCursorToFileStartCommand, "run")
 			.mockImplementation(() => undefined);
 		fileEndSpy = vi
 			.spyOn(MoveCursorToFileEndCommand, "run")
 			.mockImplementation(() => undefined);
 		lineStartSpy = vi
 			.spyOn(MoveCursorToLineStartCommand, "run")
 			.mockImplementation(() => undefined);
 		lineEndSpy = vi
 			.spyOn(MoveCursorToLineEndCommand, "run")
 			.mockImplementation(() => undefined);
 	});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/engine/MacroChoiceEngine.editorCommands.test.ts` around lines 43 - 60,
Combine the two consecutive beforeEach blocks into a single beforeEach that
first calls vi.restoreAllMocks() and then sets up the spies for
MoveCursorToFileStartCommand.run, MoveCursorToFileEndCommand.run,
MoveCursorToLineStartCommand.run, and MoveCursorToLineEndCommand.run (the
fileStartSpy, fileEndSpy, lineStartSpy, lineEndSpy mocks), so mock restoration
and spy setup occur in one logical initialization step.

37-104: Consider adding a test for the default (unhandled type) branch.

executeEditorCommand throws on an unknown EditorCommandType (the exhaustive never check). None of the four tests exercise this path, so a regression (e.g. a new enum member added without a matching case) would go undetected by this suite.

➕ Suggested additional test
+	it("throws for an unhandled editor command type", async () => {
+		await expect(
+			callExecuteEditorCommand("__unknown__" as unknown as EditorCommandType)
+		).rejects.toThrow("Unhandled editor command type");
+	});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/engine/MacroChoiceEngine.editorCommands.test.ts` around lines 37 - 104,
Add a test that exercises the default/unhandled branch by calling
executeEditorCommand (via callExecuteEditorCommand) with an invalid
EditorCommandType value (e.g., a bogus value cast to EditorCommandType) and
assert it throws; specifically, add an async test that awaits
expect(callExecuteEditorCommand(invalidValue as unknown as
EditorCommandType)).rejects.toThrow() to ensure the exhaustive never check in
executeEditorCommand is covered.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/engine/MacroChoiceEngine.editorCommands.test.ts`:
- Around line 43-60: Combine the two consecutive beforeEach blocks into a single
beforeEach that first calls vi.restoreAllMocks() and then sets up the spies for
MoveCursorToFileStartCommand.run, MoveCursorToFileEndCommand.run,
MoveCursorToLineStartCommand.run, and MoveCursorToLineEndCommand.run (the
fileStartSpy, fileEndSpy, lineStartSpy, lineEndSpy mocks), so mock restoration
and spy setup occur in one logical initialization step.
- Around line 37-104: Add a test that exercises the default/unhandled branch by
calling executeEditorCommand (via callExecuteEditorCommand) with an invalid
EditorCommandType value (e.g., a bogus value cast to EditorCommandType) and
assert it throws; specifically, add an async test that awaits
expect(callExecuteEditorCommand(invalidValue as unknown as
EditorCommandType)).rejects.toThrow() to ensure the exhaustive never check in
executeEditorCommand is covered.

@chhoumann chhoumann merged commit c670550 into master Feb 22, 2026
5 checks passed
@chhoumann chhoumann deleted the 304-feature-request-extend-editor-commands-for-macors branch February 22, 2026 20:15
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Mar 5, 2026

🎉 This PR is included in version 2.12.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE REQUEST] Extend editor commands for macors.

1 participant