Skip to content

Handle completion separators and popup dismissal - #31191

Merged
charliemarsh-oai merged 5 commits into
mainfrom
charlie/autocomplete-separators
Jul 7, 2026
Merged

Handle completion separators and popup dismissal#31191
charliemarsh-oai merged 5 commits into
mainfrom
charlie/autocomplete-separators

Conversation

@charliemarsh-oai

@charliemarsh-oai charliemarsh-oai commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Summary

Autocomplete completion previously inserted a new space even when a separator was already present, which could leave redundant whitespace around neighboring text. Popup dismissal also tracked only the query string, so dismissing one token could suppress a different occurrence with the same text.

This gives completions one horizontal-separator policy across files, images, skills, and mentions. Existing separators are reused when possible, ordinary suffix text remains separated, and line breaks stay intact. Dismissal now identifies the complete whitespace-delimited token occurrence, so offset-only edits preserve dismissal while later identical tokens remain independent. Newly completed values beginning with $ or @ can also remain closed when the next PR's affinity rule recognizes the token to the left of the cursor.

Examples

The examples below use | to represent the cursor.

Reuse existing separators

Starting with an existing two-space gap:

@ma|  next

After accepting src/main.rs and typing foo, completion previously added a third separator and left both original spaces before next:

src/main.rs foo|  next

After this PR, completion reuses the first existing separator as the insertion point and preserves the second between the new text and next:

src/main.rs foo| next

Keep a completed sigil-prefixed value closed

Starting before a line break:

@ma|
next

and accepting a path whose result is itself prefixed with @ produces:

@scope/main.rs |
next

Without this PR's completion dismissal, the affinity rule in #30463 rediscovers the completed token to the left of the cursor and reopens its popup:

@scope/main.rs |
^^^^^^^^^^^^^^^ popup reopens
next

With this PR, the inserted occurrence is dismissed and the popup remains closed:

@scope/main.rs |
^^^^^^^^^^^^^^^ popup remains closed
next

Do not dismiss an identical later occurrence

Given two identical tokens:

@scope/main.rs|  @scope/main.rs

after dismissing the first popup with Escape and moving to the second token, query-only dismissal previously suppressed the second popup too:

@scope/main.rs  @scope/main.rs|
                   ^^^^^^^^^^^^^ popup remains closed

After this PR, dismissal also matches the token's ordinal among complete tokens, so the second occurrence opens normally:

@scope/main.rs  @scope/main.rs|
                   ^^^^^^^^^^^^^ popup opens

Keep dismissal across offset-only edits

After dismissing @ma, moving to its start, and pasting an email-like token:

email@ma.com @ma|

the @ma bytes embedded in email@ma.com do not count as another autocomplete token. The original @ma keeps its dismissal even though its byte range moved.

Stack

This is PR 2 of 3, stacked on #31190. It relies on the explicit replacement ranges introduced there and provides the completion lifecycle used by the targeting fix in #30463.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a8c00448f6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

let separator_precedes_plain_text = self.draft.textarea.text()[after_separator..]
.chars()
.next()
.is_some_and(|c| !c.is_whitespace() && !matches!(c, '@' | '$'));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Badge Preserve a separator before following mention tokens

When a completion is followed by a single existing separator and an @ or $ token (for example completing @ma in @ma @next), this condition treats the suffix as not plain text and moves the cursor after the only space. The next characters the user types are then inserted immediately before the sigil, producing src/main.rs foo@next instead of keeping the existing token separated (src/main.rs foo @next). Preserve an extra separator for any non-whitespace suffix that should remain a distinct token.

Useful? React with 👍 / 👎.

Comment thread codex-rs/tui/src/bottom_pane/chat_composer.rs Outdated
Comment thread codex-rs/tui/src/bottom_pane/chat_composer.rs
@charliemarsh-oai
charliemarsh-oai force-pushed the charlie/autocomplete-separators branch from a8c0044 to b629fce Compare July 6, 2026 21:46
@charliemarsh-oai
charliemarsh-oai requested a review from a team as a code owner July 6, 2026 21:46
charliemarsh-oai added a commit that referenced this pull request Jul 6, 2026
## Summary

Autocomplete popup synchronization already identifies the active token's
range and query, but accepting a result previously discarded that range
and independently recomputed token boundaries around the cursor. Those
calculations could disagree at ambiguous cursor positions. For example,
in `@first| @second` (with `|` marking the cursor at the intervening
space), the popup targets `@second`, while the old acceptance path
replaces `@first`.

This threads the active range through legacy file search, skill
mentions, and mentions-v2 completion. File, image, and mention insertion
now replace the same token that supplied the popup query, and image
completion reuses the shared file insertion path.

## Stack

This is PR 1 of 3. #31191 builds separator and dismissal behavior on
these explicit replacement ranges, and #30463 then fixes token affinity
between adjacent mentions.
Base automatically changed from charlie/autocomplete-target-ranges to main July 6, 2026 21:48
@charliemarsh-oai
charliemarsh-oai marked this pull request as draft July 6, 2026 21:55
@charliemarsh-oai
charliemarsh-oai force-pushed the charlie/autocomplete-separators branch 2 times, most recently from f183e69 to e804544 Compare July 6, 2026 22:01
@charliemarsh-oai
charliemarsh-oai marked this pull request as ready for review July 6, 2026 23:42
@charliemarsh-oai
charliemarsh-oai force-pushed the charlie/autocomplete-separators branch from e44e267 to 304ad36 Compare July 7, 2026 00:13

@fcoury-oai fcoury-oai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ran a couple of smoke tests and they all worked fine.

The only suggestion I have is to extract the completion-separator logic into a dedicated new module since chat_composer is more than 11k lines at the moment.

I'll leave that up to you and approve the PR.

@charliemarsh-oai

Copy link
Copy Markdown
Contributor Author

That's a good call, but I may defer that to the next PR to see if there's a more compelling cut-point.

@charliemarsh-oai
charliemarsh-oai merged commit 2589f7a into main Jul 7, 2026
35 checks passed
@charliemarsh-oai
charliemarsh-oai deleted the charlie/autocomplete-separators branch July 7, 2026 22:02
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 7, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants