Files
cmux/Sources/TextBoxMentionCompletionDetector.swift
Lawrence Chen d35cffe8da Anchor textbox autocomplete to the cursor (#5021)
* Anchor textbox completions to cursor

* Reposition textbox completions on window changes

* Improve textbox mention search indexing

* Show root mentions while file index warms

* Open textbox mention popover while loading

* Address textbox mention review feedback

* Ignore textbox completion panel in close shortcut lint

* Address textbox mention review feedback

* Move textbox mention tests to Swift Testing

* Fix textbox mention completion races

* Use shared ripgrep resolver for textbox mentions

* Fix textbox mention stale refresh paths

* Address textbox mention review edge cases

* Fix textbox mention markdown and scanner waiters

* Normalize textbox mention control navigation

* Prioritize local textbox mention skills

* Fix textbox mention key routing edge cases

* Stamp textbox mention cache on completion

* Update textbox mention stale return test

* Fix mention index warning

* Address textbox mention review feedback
2026-06-03 15:43:38 -07:00

55 lines
1.7 KiB
Swift

import Foundation
struct TextBoxMentionCompletionDetector {
static func query(in text: String, selectedRange: NSRange) -> TextBoxMentionQuery? {
guard selectedRange.length == 0,
selectedRange.location != NSNotFound else {
return nil
}
let nsText = text as NSString
let cursor = min(max(0, selectedRange.location), nsText.length)
guard cursor > 0 else { return nil }
var tokenStart = cursor
while tokenStart > 0 {
let previous = nsText.substring(with: NSRange(location: tokenStart - 1, length: 1))
if previous.rangeOfCharacter(from: .whitespacesAndNewlines) != nil {
break
}
tokenStart -= 1
}
guard tokenStart < cursor else { return nil }
let tokenRange = NSRange(location: tokenStart, length: cursor - tokenStart)
let token = nsText.substring(with: tokenRange)
guard let trigger = token.first else { return nil }
let kind: TextBoxMentionKind
switch trigger {
case "@":
kind = .file
case "/":
kind = .skill
case "$":
kind = .skill
default:
return nil
}
guard token.allSatisfy({ character in
character != "[" &&
character != "]" &&
character != "(" &&
character != ")" &&
character != "<" &&
character != ">"
}) else {
return nil
}
let query = String(token.dropFirst())
return TextBoxMentionQuery(kind: kind, range: tokenRange, query: query, trigger: trigger)
}
}