From 2be79766cf9f997872bb1de5045abc9f7a3493ac Mon Sep 17 00:00:00 2001 From: Ayush7614 Date: Sun, 2 Aug 2026 01:15:11 +0530 Subject: [PATCH] Compile mention autocomplete regex through SafeRegex. try? NSRegularExpression silently disabled @-mentions on compile failure; SafeRegex logs and returns a never-matching pattern instead, matching MessageFormattingEngine. --- bitchat/Services/AutocompleteService.swift | 46 +++++++++++----------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/bitchat/Services/AutocompleteService.swift b/bitchat/Services/AutocompleteService.swift index 6c55f34e..b6483c8d 100644 --- a/bitchat/Services/AutocompleteService.swift +++ b/bitchat/Services/AutocompleteService.swift @@ -2,7 +2,7 @@ // AutocompleteService.swift // bitchat // -// Handles autocomplete suggestions for mentions and commands +// Handles autocomplete suggestions for mentions // This is free and unencumbered software released into the public domain. // @@ -10,49 +10,49 @@ import Foundation /// Manages autocomplete functionality for chat final class AutocompleteService { - private let mentionRegex = try? NSRegularExpression(pattern: "@([\\p{L}0-9_]*)$", options: []) + /// Compiled via `SafeRegex` so a bad pattern logs and disables matching + /// instead of silently returning nil forever (`try?` did the latter). + private let mentionRegex = SafeRegex.compile("@([\\p{L}0-9_]*)$") /// Get autocomplete suggestions for current text func getSuggestions(for text: String, peers: [String], cursorPosition: Int) -> (suggestions: [String], range: NSRange?) { let textToPosition = String(text.prefix(cursorPosition)) - - // Check for mention autocomplete + + // Mentions only — slash-command suggestions are owned by + // `CommandSuggestionsView` / the composer, not this service. if let (mentionSuggestions, mentionRange) = getMentionSuggestions(textToPosition, peers: peers) { return (mentionSuggestions, mentionRange) } - - // Don't handle command autocomplete here - ContentView handles it with better UI - // if let (commandSuggestions, commandRange) = getCommandSuggestions(textToPosition) { - // return (commandSuggestions, commandRange) - // } - + return ([], nil) } - + /// Apply selected suggestion to text func applySuggestion(_ suggestion: String, to text: String, range: NSRange) -> String { guard let textRange = Range(range, in: text) else { return text } - + var replacement = suggestion - + // Add space after command if it takes arguments if suggestion.hasPrefix("/") && needsArgument(command: suggestion) { replacement += " " } - + return text.replacingCharacters(in: textRange, with: replacement) } - + // MARK: - Private Methods - + private func getMentionSuggestions(_ text: String, peers: [String]) -> ([String], NSRange)? { - guard let regex = mentionRegex else { return nil } - let nsText = text as NSString - let matches = regex.matches(in: text, options: [], range: NSRange(location: 0, length: nsText.length)) - + let matches = mentionRegex.matches( + in: text, + options: [], + range: NSRange(location: 0, length: nsText.length) + ) + guard let match = matches.last else { return nil } - + let fullRange = match.range(at: 0) let captureRange = match.range(at: 1) let prefix = nsText.substring(with: captureRange).normalizedNickname.lowercased() @@ -62,10 +62,10 @@ final class AutocompleteService { .sorted() .prefix(5) .map { "@\($0)" } - + return suggestions.isEmpty ? nil : (Array(suggestions), fullRange) } - + private func needsArgument(command: String) -> Bool { switch command { case "/who", "/clear":