Initial commit
This commit is contained in:
87
oAI/Utilities/Extensions/String+Extensions.swift
Normal file
87
oAI/Utilities/Extensions/String+Extensions.swift
Normal file
@@ -0,0 +1,87 @@
|
||||
//
|
||||
// String+Extensions.swift
|
||||
// oAI
|
||||
//
|
||||
// String utility extensions
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension String {
|
||||
// MARK: - Command Parsing
|
||||
|
||||
var isSlashCommand: Bool {
|
||||
hasPrefix("/")
|
||||
}
|
||||
|
||||
func parseCommand() -> (command: String, args: [String])? {
|
||||
guard isSlashCommand else { return nil }
|
||||
|
||||
let parts = self.split(separator: " ", omittingEmptySubsequences: true)
|
||||
.map(String.init)
|
||||
|
||||
guard let command = parts.first else { return nil }
|
||||
let args = Array(parts.dropFirst())
|
||||
|
||||
return (command, args)
|
||||
}
|
||||
|
||||
// MARK: - File Attachment Parsing
|
||||
|
||||
func parseFileAttachments() -> (cleanText: String, filePaths: [String]) {
|
||||
var cleanText = self
|
||||
var filePaths: [String] = []
|
||||
|
||||
// Pattern 1: @<filepath>
|
||||
let anglePattern = #"@<([^>]+)>"#
|
||||
if let regex = try? NSRegularExpression(pattern: anglePattern) {
|
||||
let matches = regex.matches(in: self, range: NSRange(self.startIndex..., in: self))
|
||||
for match in matches.reversed() {
|
||||
if let range = Range(match.range(at: 1), in: self) {
|
||||
let path = String(self[range])
|
||||
filePaths.insert(path, at: 0)
|
||||
}
|
||||
if let fullRange = Range(match.range, in: self) {
|
||||
cleanText.removeSubrange(fullRange)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pattern 2: @filepath (starting with /, ~, ., or drive letter)
|
||||
let directPattern = #"@([~/.][\S]+|[A-Za-z]:[\\\/][\S]+)"#
|
||||
if let regex = try? NSRegularExpression(pattern: directPattern) {
|
||||
let matches = regex.matches(in: cleanText, range: NSRange(cleanText.startIndex..., in: cleanText))
|
||||
for match in matches.reversed() {
|
||||
if let range = Range(match.range(at: 1), in: cleanText) {
|
||||
let path = String(cleanText[range])
|
||||
if !filePaths.contains(path) {
|
||||
filePaths.insert(path, at: 0)
|
||||
}
|
||||
}
|
||||
if let fullRange = Range(match.range, in: cleanText) {
|
||||
cleanText.removeSubrange(fullRange)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (cleanText.trimmingCharacters(in: .whitespaces), filePaths)
|
||||
}
|
||||
|
||||
// MARK: - Token Estimation
|
||||
|
||||
func estimateTokens() -> Int {
|
||||
// Rough estimation: ~4 characters per token
|
||||
// This is approximate; Phase 2 will use proper tokenizer
|
||||
return max(1, count / 4)
|
||||
}
|
||||
|
||||
// MARK: - Truncation
|
||||
|
||||
func truncated(to length: Int, trailing: String = "...") -> String {
|
||||
if count <= length {
|
||||
return self
|
||||
}
|
||||
let endIndex = index(startIndex, offsetBy: length - trailing.count)
|
||||
return String(self[..<endIndex]) + trailing
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user