右矢印キーによるインラインコードブロックからの脱出バグを修正

basecamp/lexxy

インラインコードブロックの末尾で右矢印キーを押しても脱出できないバグが修正されました。原因は「カーソル後のノード」の取得範囲が意図より広く、後続の段落の存在を誤検出していたことにあります。

背景

inline code を記述後、末尾にスペースを入れずに右矢印キーを押すと、カーソルがコードブロック内に閉じ込められるという問題が #728 で報告されていました。スペースを挿入した場合は正常に脱出できるため、矢印キーによる脱出ロジックに限定したバグでした。特にリストアイテム内のインラインコードで顕著に発生し、コードブロックを抜けられないまま入力が継続されてしまう問題でした。

矢印キーによる脱出は「カーソルがコードテキストノードの末尾にいるとき、後続のノードがなければコードフォーマットを解除する」というロジックで実装されています。問題は「後続のノードがない」という判定に使う関数の返すスコープが広すぎた点にありました。

技術的な変更

src/editor/command_dispatcher.js の1行の変更により、後続ノードの検索スコープが「親要素の次の兄弟要素」から「同一親要素内の次の兄弟ノード」に絞り込まれました。

変更前:

if (this.selection.nodeAfterCursor !== null) return false

変更後:

if (anchorNode.getNextSibling() !== null) return false

変更前の nodeAfterCursor は親要素(段落など)の次の兄弟要素も返していたため、<p>Hello <code>code</code></p><p>next line</p> のような構造では「次の段落」が存在するとして脱出処理が中断されていました。変更後の anchorNode.getNextSibling() はテキストノード自身の直接の兄弟のみを参照するため、同一段落内に後続ノードがなければ正しく脱出処理が実行されます。

あわせて test/browser/tests/formatting/inline_code_escape.test.js が新規追加され、以下の4つのシナリオがPlaywrightによるブラウザテストでカバーされています:

  • 後続段落がある場合のインラインコードからの脱出
  • リストアイテム内のインラインコードからの脱出
  • 段落の唯一のコンテンツがインラインコードの場合の脱出
  • バッククォートショートカットで作成したインラインコードからの脱出

設計判断

nodeAfterCursor から anchorNode.getNextSibling() への置き換えは、参照するスコープを「カーソル周辺のグローバルな隣接ノード」から「テキストノード自身の直接の兄弟」へ明確に限定する変更です。

この脱出ロジックが意図しているのは「コードテキストノードの直後に別のテキストや要素が続く場合は脱出しない(カーソルをその隣接ノードへ自然に移動させる)」という挙動です。段落やリストアイテムといった親要素の境界を越えて後続コンテンツを検索することは、この意図と一致しません。変更はその意図に忠実なスコープをAPI選択によって明示しています。

また、ロジックの修正に合わせてブラウザテストを追加したことで、将来の類似バグの混入を防ぐ安全網が整備されています。

まとめ

1行の変更ながら、ノード参照のスコープ誤りというリッチテキストエディタ実装に特有の落とし穴を突いたバグ修正です。anchorNode.getNextSibling() への置き換えにより、インラインコードの脱出ロジックが同一親要素内という正しいスコープで動作するようになり、段落やリストをまたぐ任意の文書構造でも一貫した挙動が保証されます。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
d5a0f815

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

「リード文(総論)→背景・技術的な変更・設計判断(各論)→まとめ(結論)」という3部構成が明確に適用されており、記事全体の構成が非常に優れています。

カスタムMarkdown構文 ✓ PASS

シンタックスハイライト・GitHubリンク記法の正確性

ファイル名付きシンタックスハイライト(```javascript:ファイルパス)やGitHubのPR・Issue番号のリンク記法([#番号](URL))が正しく使用されています。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

リッチテキストエディタのDOMノード操作に関する内容であり、専門知識を持つエンジニアという対象読者に適した技術レベルと表現で記述されています。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクションが総論→各論の構成になっており、各段落はトピックセンテンスで始まるなど、パラグラフ・ライティングの原則に準拠しています。可読性が非常に高いです。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

記事内で引用されているコードブロックは、提供されたDiffの内容と完全に一致しており、ファイル名も正確です。テストファイル追加の言及もDiff情報と整合しています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

PR Descriptionで使用されている `nodeAfterCursor` や `anchorNode.getNextSibling` などの技術用語を正確に使用し、その意味も適切に解説しています。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

バグの原因(`nodeAfterCursor` のスコープが広すぎた)と修正内容(`anchorNode.getNextSibling()` でスコープを限定)に関する説明は、PR情報と一致しており、技術的に正確です。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

記事内のすべての主張(問題の背景、修正内容、テスト追加など)は、PRのTitle, Description, Diff情報によって裏付けられており、ハルシネーションは検出されませんでした。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR番号(#865)およびIssue番号(#728)が正確に記載・リンクされています。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事のタイトルはPRのタイトル(Fix cursor escape from inline code blocks with arrow keys)の内容を正確に要約しており、主題に一貫性があります。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

バージョン情報やリリース予定など、PR情報に基づかない外部知識の追記は見られませんでした。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

「修正されました」「報告されていました」といった過去形の表現が使われており、完了した変更を報告するPRの内容と時間表現が一致しています。