Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions webview-ui/src/components/chat/ChatTextArea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -790,6 +790,15 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
let newValue = inputValue.slice(0, cursorPosition)
let totalLength = 0

// Check if we need to add a space before the first mention
const textBefore = inputValue.slice(0, cursorPosition)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This space-checking logic is duplicated in the insertMention function. Since insertMention already handles adding space before @mentions, do we need this logic here as well?

Consider whether both places need this or if it should be centralized in one location to avoid potential inconsistencies.

const needsSpaceBefore =
textBefore.length > 0 && !textBefore.endsWith(" ") && !textBefore.endsWith("\n")
if (needsSpaceBefore) {
newValue += " "
totalLength += 1
}

// Using a standard for loop instead of forEach for potential performance gains.
for (let i = 0; i < lines.length; i++) {
const line = lines[i]
Expand Down
20 changes: 16 additions & 4 deletions webview-ui/src/utils/__tests__/context-mentions.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import {
describe("insertMention", () => {
it("should insert mention at cursor position when no @ symbol exists", () => {
const result = insertMention("Hello world", 5, "test")
expect(result.newValue).toBe("Hello@test world")
expect(result.mentionIndex).toBe(5)
expect(result.newValue).toBe("Hello @test world")
expect(result.mentionIndex).toBe(6)
})

it("should replace text after last @ symbol", () => {
Expand Down Expand Up @@ -46,8 +46,20 @@ describe("insertMention", () => {

it("should handle insertion at the end", () => {
const result = insertMention("Hello", 5, "problems")
expect(result.newValue).toBe("Hello@problems ")
expect(result.mentionIndex).toBe(5)
expect(result.newValue).toBe("Hello @problems ")
expect(result.mentionIndex).toBe(6)
})

it("should insert space before @ when text doesn't end with space", () => {
const result = insertMention("Check", 5, "problems")
expect(result.newValue).toBe("Check @problems ")
expect(result.mentionIndex).toBe(6)
})

it("should not insert extra space when text already ends with space", () => {
const result = insertMention("Check ", 6, "problems")
expect(result.newValue).toBe("Check @problems ")
expect(result.mentionIndex).toBe(6)
})

it("should handle slash command replacement", () => {
Expand Down
19 changes: 14 additions & 5 deletions webview-ui/src/utils/context-mentions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,19 +58,28 @@ export function insertMention(
let mentionIndex: number

if (lastAtIndex !== -1) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Critical issue: This logic replaces everything after the FIRST @ found in the text, which causes data loss when multiple mentions exist.

For example, if the text is "Check @/file1.txt and @" and we're inserting a new mention at the second @, this will replace from the first @ instead.

Consider finding the @ that's actually at or near the cursor position instead of using lastIndexOf on the entire beforeCursor text. You might want to look for the @ closest to the cursor position that doesn't already have a complete mention after it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Active mention detection: this replaces starting at the last "@" before the cursor even if the caret is no longer inside that mention token. If the user already typed a complete mention earlier and the cursor is elsewhere, selecting a new item can overwrite the earlier mention. Replace only when there is no whitespace between the last "@" and the cursor; otherwise insert a new mention.

Suggested change
if (lastAtIndex !== -1) {
const isActiveMention = lastAtIndex !== -1 && !/\s/.test(beforeCursor.slice(lastAtIndex + 1))
if (isActiveMention) {

Fix it with Roo Code or mention @roomote and request a fix.

// If there's an '@' symbol, replace everything after it with the new mention
const beforeMention = text.slice(0, lastAtIndex)
// If there's an '@' symbol, check if we need to add a space before it
const beforeAt = text.slice(0, lastAtIndex)
const needsSpaceBefore = beforeAt.length > 0 && !beforeAt.endsWith(" ") && !beforeAt.endsWith("\n")

// If we need a space before @, add it
const beforeMention = needsSpaceBefore ? beforeAt + " " : beforeAt

// Only replace if afterCursor is all alphanumerical
// This is required to handle languages that don't use space as a word separator (chinese, japanese, korean, etc)
const afterCursorContent = /^[a-zA-Z0-9\s]*$/.test(afterCursor)
? afterCursor.replace(/^[^\s]*/, "")
: afterCursor
newValue = beforeMention + "@" + processedValue + " " + afterCursorContent
mentionIndex = lastAtIndex
mentionIndex = needsSpaceBefore ? lastAtIndex + 1 : lastAtIndex
} else {
// If there's no '@' symbol, insert the mention at the cursor position
newValue = beforeCursor + "@" + processedValue + " " + afterCursor
mentionIndex = position
// Check if we need to add a space before the mention
const needsSpaceBefore = beforeCursor.length > 0 && !beforeCursor.endsWith(" ") && !beforeCursor.endsWith("\n")
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Minor suggestion: The space-checking logic (checking for trailing space or newline) is repeated here and at line 63. Consider extracting this into a small utility function like needsSpaceBeforeMention(text: string): boolean to reduce duplication and make the intent clearer.

const prefix = needsSpaceBefore ? " " : ""

newValue = beforeCursor + prefix + "@" + processedValue + " " + afterCursor
mentionIndex = position + (needsSpaceBefore ? 1 : 0)
}

return { newValue, mentionIndex }
Expand Down
Loading