mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-25 16:05:19 +00:00
Periphery 3.7.4 audit of both schemes (macOS + iOS, intersected so platform-specific code is never touched), with test targets indexed and the share extension built. 277 dead declarations removed or demoted: dead forwarding wrappers (ChatViewModel+Nostr/+PrivateChat), removed- feature remnants (autocomplete command suggestions, back-swipe tuning, MediaSendError, GeohashParticipantTracker), unused Tor dormancy bindings, assign-only properties, unused parameters (renamed to _), and redundant public accessibility. 13 orphaned localization keys deleted across all 29 locales (old pre-#1392 location-notes UI, app_info warnings). Two real tests were flagged as unused because they never ran: Swift Testing methods missing @Test (NostrProtocolTests. testAckRoundTripNIP44V2_Delivered, NotificationStreamAssemblerTests. testAssemblesCompressedLargeFrame). Re-armed both; they pass. Deliberately kept, now recorded in .periphery.baseline.json: iOS-only code invisible to the CI macOS scan, C FFI signatures, keep-alive NWPathMonitor reference, InboundEventKey.eventID (dedup semantics), wifiBulk capability bit (reserved for Wi-Fi bulk work, used by BitFoundation package tests), and the String secureClear cluster (exercised by package tests). New: .periphery.yml config and an advisory Dead Code CI job (mirrors the SwiftLint precedent from #1361) that fails on findings not in the committed baseline. Verified: full macOS app suite, BitFoundation (119) and BitLogger (13) package tests green; periphery scan --strict exits clean. Co-authored-by: jack <jackjackbits@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
78 lines
2.7 KiB
Swift
78 lines
2.7 KiB
Swift
//
|
|
// AutocompleteService.swift
|
|
// bitchat
|
|
//
|
|
// Handles autocomplete suggestions for mentions and commands
|
|
// This is free and unencumbered software released into the public domain.
|
|
//
|
|
|
|
import Foundation
|
|
|
|
/// Manages autocomplete functionality for chat
|
|
final class AutocompleteService {
|
|
private let mentionRegex = try? NSRegularExpression(pattern: "@([\\p{L}0-9_]*)$", options: [])
|
|
|
|
/// 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
|
|
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))
|
|
|
|
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).lowercased()
|
|
|
|
let suggestions = peers
|
|
.filter { $0.lowercased().hasPrefix(prefix) }
|
|
.sorted()
|
|
.prefix(5)
|
|
.map { "@\($0)" }
|
|
|
|
return suggestions.isEmpty ? nil : (Array(suggestions), fullRange)
|
|
}
|
|
|
|
private func needsArgument(command: String) -> Bool {
|
|
switch command {
|
|
case "/who", "/clear":
|
|
return false
|
|
default:
|
|
return true
|
|
}
|
|
}
|
|
}
|