## 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:
```text
@ma| next
```
After accepting `src/main.rs` and typing `foo`, completion previously
added a third separator and left both original spaces before `next`:
```text
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`:
```text
src/main.rs foo| next
```
### Keep a completed sigil-prefixed value closed
Starting before a line break:
```text
@ma|
next
```
and accepting a path whose result is itself prefixed with `@` produces:
```text
@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:
```text
@scope/main.rs |
^^^^^^^^^^^^^^^ popup reopens
next
```
With this PR, the inserted occurrence is dismissed and the popup remains
closed:
```text
@scope/main.rs |
^^^^^^^^^^^^^^^ popup remains closed
next
```
### Do not dismiss an identical later occurrence
Given two identical tokens:
```text
@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:
```text
@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:
```text
@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:
```text
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.