Skip to content

fix: composer @file autocomplete cursor and spacing bugs#936

Open
chuks-qua wants to merge 2 commits intopingdotgg:mainfrom
chuks-qua:fix/922-mid-text-autocomplete
Open

fix: composer @file autocomplete cursor and spacing bugs#936
chuks-qua wants to merge 2 commits intopingdotgg:mainfrom
chuks-qua:fix/922-mid-text-autocomplete

Conversation

@chuks-qua
Copy link
Contributor

@chuks-qua chuks-qua commented Mar 11, 2026

Closes #291
Closes #922

What Changed

Three fixes for the composer's @file autocomplete.

Mid-text @ trigger detection:
ComposerPromptEditor now computes an expanded cursor from the Lexical node tree and passes it through onChange and readSnapshot. ChatView uses this expanded cursor in detectComposerTrigger instead of the regex-based expandCollapsedComposerCursor.

Double space after completion:
When the replacement (@AGENTS.md ) already ends with a space and a space exists at the cursor, the insertion now consumes the existing space instead of leaving two.

Caret lands at end of line after completion:
applyPromptReplacement returned an expanded cursor from replaceTextRange but stored it as a collapsed cursor. All setComposerCursor call sites now use the correct coordinate system.

Code changes:

  • Add getExpandedAbsoluteOffsetForPoint and $readExpandedSelectionOffsetFromEditorState to ComposerPromptEditor.tsx
  • Pass expandedCursor through the onChange callback and readSnapshot interface
  • Add collapseExpandedComposerCursor and export clampCollapsedComposerCursor in composer-logic.ts
  • Add extendReplacementRangeForTrailingSpace to consume trailing spaces during completion
  • Fix all setComposerCursor call sites to store collapsed offsets
  • Add unit tests for collapsed/expanded cursor conversion and trailing space consumption

Why

expandCollapsedComposerCursor uses MENTION_TOKEN_REGEX to locate mentions in raw text. When a user types @ before an existing word (e.g., cursor between inspect and in this sentence), the regex matches @in as a mention and shifts the cursor past it. detectComposerTrigger then receives the wrong offset and returns null, so the autocomplete popup never appears.

The Lexical editor already tracks which nodes are ComposerMentionNode instances. Reading the expanded cursor directly from the node tree eliminates the regex false-match problem.

The other two bugs come from mixing collapsed and expanded cursor offsets in ChatView and ComposerPromptEditor. Collapsed offsets count a mention pill as 1 character; expanded offsets count by full text length (@AGENTS.md = 11). Passing an expanded offset where collapsed is expected inflates the cursor position, and failing to consume an existing trailing space doubles the gap.

UI Changes

Before

composer-before-fix.mp4

After

compose-after-fix.mp4

In the After video, also notice: after selecting a completion mid-text, the cursor lands immediately after the inserted mention (not at the end of the line), and there is exactly one space between the mention and the next word.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes

Note

Fix @file autocomplete trigger detection when cursor is mid-prompt with trailing text

  • The composer now tracks both a collapsed cursor (where mentions count as 1 character) and an expanded cursor (full mention text length), using the expanded cursor for trigger detection and the collapsed cursor for focus/selection operations.
  • Adds collapseExpandedComposerCursor and clampCollapsedComposerCursor exports to composer-logic.ts to convert between cursor representations.
  • ComposerPromptEditor.tsx now computes and reports both cursor forms to callers via an extended onChange signature and readSnapshot return value.
  • Adds extendReplacementRangeForTrailingSpace in ChatView.tsx so inserting a path or /model token that ends with a space consumes any existing trailing space at the insertion point, preventing double spaces.
  • Behavioral Change: trigger detection now uses the expanded cursor offset, which fixes false-positive matches around mentions but changes how cursor position is computed during autocomplete.

Macroscope summarized 28a40d7.

@coderabbitai
Copy link

coderabbitai bot commented Mar 11, 2026

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 22d6bf01-9f0f-40b7-a214-8c132929ac25

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

@github-actions github-actions bot added the vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. label Mar 11, 2026
@chuks-qua chuks-qua force-pushed the fix/922-mid-text-autocomplete branch 2 times, most recently from 7e6ae59 to a47a5fe Compare March 11, 2026 22:46
@chuks-qua
Copy link
Contributor Author

authorization is needed for redeployment

@chuks-qua chuks-qua force-pushed the fix/922-mid-text-autocomplete branch from a47a5fe to 5605fa1 Compare March 11, 2026 23:07
@chuks-qua chuks-qua marked this pull request as ready for review March 11, 2026 23:14
@chuks-qua chuks-qua force-pushed the fix/922-mid-text-autocomplete branch 2 times, most recently from fdbe241 to 8f3b32a Compare March 11, 2026 23:24
@mwolson
Copy link

mwolson commented Mar 12, 2026

Hi, thanks for looking into the issue I filed for #291 . I took a brief look as of 8f3b32a the likely remaining items I have are:

  • applyPromptReplacement() still doesn’t consume an existing trailing space under the cursor, and it still feeds the replacement cursor back through the composer as if expanded/raw-text offsets were mention-aware. That seems likely to leave the “double space after insertion” / “caret lands too far right” part unresolved.
  • A focused regression test for the exact whitespace-started selection flow from Composer @ completion started in whitespace can dismiss early and insert with wrong spacing #291 might be nice to have

@chuks-qua
Copy link
Contributor Author

chuks-qua commented Mar 12, 2026

Hi, thanks for looking into the issue I filed for #291 . I took a brief look as of 8f3b32a the likely remaining items I have are:

  • applyPromptReplacement() still doesn’t consume an existing trailing space under the cursor, and it still feeds the replacement cursor back through the composer as if expanded/raw-text offsets were mention-aware. That seems likely to leave the “double space after insertion” / “caret lands too far right” part unresolved.
  • A focused regression test for the exact whitespace-started selection flow from Composer @ completion started in whitespace can dismiss early and insert with wrong spacing #291 might be nice to have

Hey, thanks for pointing this out, i am looking into it now.


Edit

Looked into it and now i follow what you are saying.
Trailing Space
When you select a completion in a double-space gap:
and_then_|@AG|__summarize
The replacement @AGENTS.md already has a trailing space, but the existing space after the cursor stays too:
and_then_@AGENTS.md__surmmarize

Note

used _ for spaces and | to group the suggestion trigger

Cursor after insertion
When a mention pill like @AGENTS.md already exist earlier in the prompt, the code stores the wrong cursor position after inerting a new completion. the pill counts as 1 char internally but 11 chars in the raw text. applyPromptReplacement stores the raw text offset without converting it, so the caret jumps to the end of the line instead of landing right after the insertion.

I am implementing the fixes.

@chuks-qua chuks-qua marked this pull request as draft March 12, 2026 01:26
@chuks-qua chuks-qua force-pushed the fix/922-mid-text-autocomplete branch from 9918194 to eee6cf7 Compare March 12, 2026 02:15
…utocomplete

expandCollapsedComposerCursor uses a regex to find mentions in raw text,
which false-matches plain text like @in when the user types @ before an
existing word. This corrupts the cursor mapping and prevents trigger
detection from firing.

Compute the expanded cursor directly from the Lexical editor's node tree,
which knows which nodes are actual ComposerMentionNode instances. Pass this
expanded cursor through onChange and readSnapshot so detectComposerTrigger
receives the correct text offset.

Closes pingdotgg#291
Related to pingdotgg#922
@chuks-qua chuks-qua force-pushed the fix/922-mid-text-autocomplete branch 2 times, most recently from 2cd327f to 28a40d7 Compare March 12, 2026 02:41
@chuks-qua chuks-qua marked this pull request as ready for review March 12, 2026 02:44
…ailing space

applyPromptReplacement stored the expanded cursor from replaceTextRange
as a collapsed cursor, causing the caret to jump to the end of the line
after selecting a completion when a mention pill existed earlier in the
prompt. Convert the cursor with collapseExpandedComposerCursor before
storing and restoring focus.

When a replacement already ends with a space and the character at the
cursor is also a space, extend the replacement range to consume the
existing space so the result has exactly one.

Fix all other setComposerCursor call sites that passed expanded offsets
where collapsed offsets were expected.
@chuks-qua chuks-qua changed the title fix: @file autocomplete works mid-prompt with trailing text fix: composer @file autocomplete cursor and spacing bugs Mar 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

2 participants