From 86b8fa5f79725b71c8e717f475265ac79be82f8f Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sat, 27 May 2023 21:18:25 +0800 Subject: [PATCH] Tekkon // v1.5.1 Update, removing Foundation dependency. --- .../Sources/Tekkon/Tekkon_Constants.swift | 2 - .../Sources/Tekkon/Tekkon_Phonabets.swift | 4 +- .../Tekkon/Tekkon_SyllableComposer.swift | 80 ++++++++-------- .../Sources/Tekkon/Tekkon_Utilities.swift | 92 +++++++++++++++++-- 4 files changed, 125 insertions(+), 53 deletions(-) diff --git a/Packages/vChewing_Tekkon/Sources/Tekkon/Tekkon_Constants.swift b/Packages/vChewing_Tekkon/Sources/Tekkon/Tekkon_Constants.swift index 25d2f8e0..42fbee04 100644 --- a/Packages/vChewing_Tekkon/Sources/Tekkon/Tekkon_Constants.swift +++ b/Packages/vChewing_Tekkon/Sources/Tekkon/Tekkon_Constants.swift @@ -6,8 +6,6 @@ // marks, or product names of Contributor, except as required to fulfill notice // requirements defined in MIT License. -import Foundation - /// The namespace for this package. public enum Tekkon { // MARK: - Static Constants diff --git a/Packages/vChewing_Tekkon/Sources/Tekkon/Tekkon_Phonabets.swift b/Packages/vChewing_Tekkon/Sources/Tekkon/Tekkon_Phonabets.swift index 1c97f14d..8d52b902 100644 --- a/Packages/vChewing_Tekkon/Sources/Tekkon/Tekkon_Phonabets.swift +++ b/Packages/vChewing_Tekkon/Sources/Tekkon/Tekkon_Phonabets.swift @@ -6,8 +6,6 @@ // marks, or product names of Contributor, except as required to fulfill notice // requirements defined in MIT License. -import Foundation - public extension Tekkon { // MARK: - Dynamic Constants and Basic Enums @@ -115,7 +113,7 @@ public extension Tekkon { /// - strOf: 要取代的內容。 /// - strWith: 要取代成的內容。 public mutating func selfReplace(_ strOf: String, _ strWith: String = "") { - valueStorage = valueStorage.replacingOccurrences(of: strOf, with: strWith) + if valueStorage == strOf { valueStorage = strWith } ensureType() } diff --git a/Packages/vChewing_Tekkon/Sources/Tekkon/Tekkon_SyllableComposer.swift b/Packages/vChewing_Tekkon/Sources/Tekkon/Tekkon_SyllableComposer.swift index f1d7d0d3..51d059f0 100644 --- a/Packages/vChewing_Tekkon/Sources/Tekkon/Tekkon_SyllableComposer.swift +++ b/Packages/vChewing_Tekkon/Sources/Tekkon/Tekkon_SyllableComposer.swift @@ -6,8 +6,6 @@ // marks, or product names of Contributor, except as required to fulfill notice // requirements defined in MIT License. -import Foundation - public extension Tekkon { // MARK: - Syllable Composer @@ -65,7 +63,7 @@ public extension Tekkon { public func getComposition(isHanyuPinyin: Bool = false, isTextBookStyle: Bool = false) -> String { switch isHanyuPinyin { case false: // 注音輸出的場合 - let valReturnZhuyin = value.replacingOccurrences(of: " ", with: "") + let valReturnZhuyin = value.swapping(" ", with: "") return isTextBookStyle ? cnvPhonaToTextbookReading(target: valReturnZhuyin) : valReturnZhuyin case true: // 拼音輸出的場合 let valReturnPinyin = Tekkon.cnvPhonaToHanyuPinyin(targetJoined: value) @@ -87,7 +85,7 @@ public extension Tekkon { case "˙": toneReturned = "5" default: break } - return romajiBuffer.replacingOccurrences(of: "v", with: "ü") + toneReturned + return romajiBuffer.swapping("v", with: "ü") + toneReturned } /// 注拼槽內容是否為空。 @@ -132,39 +130,45 @@ public extension Tekkon { /// 注意:回傳結果會受到當前注音排列 parser 屬性的影響。 /// - Parameters: /// - key: 傳入的 UniChar 內容。 - public func inputValidityCheck(key inputKey: UniChar = 0) -> Bool { - if let scalar = UnicodeScalar(inputKey) { - let input = String(scalar) - switch parser { - case .ofDachen: - return Tekkon.mapQwertyDachen[input] != nil - case .ofDachen26: - return Tekkon.mapDachenCP26StaticKeys[input] != nil - case .ofETen: - return Tekkon.mapQwertyETenTraditional[input] != nil - case .ofHsu: - return Tekkon.mapHsuStaticKeys[input] != nil - case .ofETen26: - return Tekkon.mapETen26StaticKeys[input] != nil - case .ofIBM: - return Tekkon.mapQwertyIBM[input] != nil - case .ofMiTAC: - return Tekkon.mapQwertyMiTAC[input] != nil - case .ofSeigyou: - return Tekkon.mapSeigyou[input] != nil - case .ofFakeSeigyou: - return Tekkon.mapFakeSeigyou[input] != nil - case .ofStarlight: - return Tekkon.mapStarlightStaticKeys[input] != nil - case .ofAlvinLiu: - return Tekkon.mapAlvinLiuStaticKeys[input] != nil - case .ofWadeGilesPinyin: - return Tekkon.mapWadeGilesPinyinKeys.contains(input) - case .ofHanyuPinyin, .ofSecondaryPinyin, .ofYalePinyin, .ofHualuoPinyin, .ofUniversalPinyin: - return Tekkon.mapArayuruPinyin.contains(input) - } + public func inputValidityCheck(key inputKey: UInt16 = 0) -> Bool { + guard let scalar = UnicodeScalar(inputKey) else { return false } + return inputValidityCheck(charStr: String(scalar)) + } + + /// 用於檢測「某個輸入字符訊號的合規性」的函式。 + /// + /// 注意:回傳結果會受到當前注音排列 parser 屬性的影響。 + /// - Parameters: + /// - charStr: 傳入的字元(String)。 + public func inputValidityCheck(charStr input: String) -> Bool { + switch parser { + case .ofDachen: + return Tekkon.mapQwertyDachen[input] != nil + case .ofDachen26: + return Tekkon.mapDachenCP26StaticKeys[input] != nil + case .ofETen: + return Tekkon.mapQwertyETenTraditional[input] != nil + case .ofHsu: + return Tekkon.mapHsuStaticKeys[input] != nil + case .ofETen26: + return Tekkon.mapETen26StaticKeys[input] != nil + case .ofIBM: + return Tekkon.mapQwertyIBM[input] != nil + case .ofMiTAC: + return Tekkon.mapQwertyMiTAC[input] != nil + case .ofSeigyou: + return Tekkon.mapSeigyou[input] != nil + case .ofFakeSeigyou: + return Tekkon.mapFakeSeigyou[input] != nil + case .ofStarlight: + return Tekkon.mapStarlightStaticKeys[input] != nil + case .ofAlvinLiu: + return Tekkon.mapAlvinLiuStaticKeys[input] != nil + case .ofWadeGilesPinyin: + return Tekkon.mapWadeGilesPinyinKeys.has(string: input) + case .ofHanyuPinyin, .ofSecondaryPinyin, .ofYalePinyin, .ofHualuoPinyin, .ofUniversalPinyin: + return Tekkon.mapArayuruPinyin.has(string: input) } - return false } /// 按需更新拼音組音區的內容顯示。 @@ -222,7 +226,7 @@ public extension Tekkon { /// 如果是諸如複合型注音排列的話,翻譯結果有可能為空,但翻譯過程已經處理好聲介韻調分配了。 /// - Parameters: /// - fromCharCode: 傳入的 UniChar 內容。 - public mutating func receiveKey(fromCharCode inputCharCode: UniChar = 0) { + public mutating func receiveKey(fromCharCode inputCharCode: UInt16 = 0) { if let scalar = UnicodeScalar(inputCharCode) { receiveKey(fromString: String(scalar)) } @@ -781,6 +785,6 @@ public extension Tekkon { private extension String { func doesHave(_ target: String) -> Bool { - target.isEmpty ? isEmpty : contains(target) + has(string: target) } } diff --git a/Packages/vChewing_Tekkon/Sources/Tekkon/Tekkon_Utilities.swift b/Packages/vChewing_Tekkon/Sources/Tekkon/Tekkon_Utilities.swift index 262530a7..d41da463 100644 --- a/Packages/vChewing_Tekkon/Sources/Tekkon/Tekkon_Utilities.swift +++ b/Packages/vChewing_Tekkon/Sources/Tekkon/Tekkon_Utilities.swift @@ -6,8 +6,6 @@ // marks, or product names of Contributor, except as required to fulfill notice // requirements defined in MIT License. -import Foundation - public extension Tekkon { // MARK: - Phonabet to Hanyu-Pinyin Conversion Processing @@ -17,7 +15,7 @@ public extension Tekkon { static func cnvPhonaToHanyuPinyin(targetJoined: String) -> String { var targetConverted = targetJoined for pair in arrPhonaToHanyuPinyin { - targetConverted = targetConverted.replacingOccurrences(of: pair[0], with: pair[1]) + targetConverted = targetConverted.swapping(pair[0], with: pair[1]) } return targetConverted } @@ -28,7 +26,7 @@ public extension Tekkon { static func cnvHanyuPinyinToTextbookStyle(targetJoined: String) -> String { var targetConverted = targetJoined for pair in arrHanyuPinyinTextbookStyleConversionTable { - targetConverted = targetConverted.replacingOccurrences(of: pair[0], with: pair[1]) + targetConverted = targetConverted.swapping(pair[0], with: pair[1]) } return targetConverted } @@ -54,7 +52,7 @@ public extension Tekkon { target: String ) -> String { var newNeta = target - if !"ˊˇˋ˙".contains(String(target.reversed()[0])), !target.contains("_") { newNeta += "1" } + if !"ˊˇˋ˙".has(string: String(target.reversed()[0])), !target.has(string: "_") { newNeta += "1" } return newNeta } @@ -69,11 +67,11 @@ public extension Tekkon { var result = targetJoined for key in Tekkon.mapHanyuPinyin.keys.sorted(by: { $0.count > $1.count }) { guard let value = Tekkon.mapHanyuPinyin[key] else { continue } - result = result.replacingOccurrences(of: key, with: value) + result = result.swapping(key, with: value) } for key in Tekkon.mapArayuruPinyinIntonation.keys.sorted(by: { $0.count > $1.count }) { guard let value = Tekkon.mapArayuruPinyinIntonation[key] else { continue } - result = result.replacingOccurrences(of: key, with: (key == "1") ? newToneOne : value) + result = result.swapping(key, with: (key == "1") ? newToneOne : value) } return result } @@ -82,8 +80,82 @@ public extension Tekkon { /// 檢測字串是否包含半形英數內容 private extension String { var isNotPureAlphanumerical: Bool { - let regex = ".*[^A-Za-z0-9].*" - let testString = NSPredicate(format: "SELF MATCHES %@", regex) - return testString.evaluate(with: self) + let x = unicodeScalars.map(\.value).filter { + if $0 >= 48, $0 <= 57 { return false } + if $0 >= 65, $0 <= 90 { return false } + if $0 >= 97, $0 <= 122 { return false } + return true + } + return !x.isEmpty + } +} + +// This package is trying to deprecate its dependency of Foundation, hence the following contents. + +extension StringProtocol { + func has(string target: any StringProtocol) -> Bool { + let selfArray = Array(unicodeScalars) + let targetArray = Array(target.description.unicodeScalars) + guard !target.isEmpty else { return isEmpty } + guard count >= target.count else { return false } + for index in 0 ..< selfArray.count { + let range = index ..< (Swift.min(index + targetArray.count, selfArray.count)) + let ripped = Array(selfArray[range]) + if ripped == targetArray { return true } + } + return false + } + + func sliced(by separator: any StringProtocol = "") -> [String] { + let selfArray = Array(unicodeScalars) + let arrSeparator = Array(separator.description.unicodeScalars) + var result: [String] = [] + var buffer: [Unicode.Scalar] = [] + var sleepCount = 0 + for index in 0 ..< selfArray.count { + let currentChar = selfArray[index] + let range = index ..< (Swift.min(index + arrSeparator.count, selfArray.count)) + let ripped = Array(selfArray[range]) + if ripped.isEmpty { continue } + if ripped == arrSeparator { + sleepCount = range.count + result.append(buffer.map { String($0) }.joined()) + buffer.removeAll() + } + if sleepCount < 1 { + buffer.append(currentChar) + } + sleepCount -= 1 + } + result.append(buffer.map { String($0) }.joined()) + buffer.removeAll() + return result + } + + func swapping(_ target: String, with newString: String) -> String { + let selfArray = Array(unicodeScalars) + let arrTarget = Array(target.description.unicodeScalars) + var result = "" + var buffer: [Unicode.Scalar] = [] + var sleepCount = 0 + for index in 0 ..< selfArray.count { + let currentChar = selfArray[index] + let range = index ..< (Swift.min(index + arrTarget.count, selfArray.count)) + let ripped = Array(selfArray[range]) + if ripped.isEmpty { continue } + if ripped == arrTarget { + sleepCount = ripped.count + result.append(buffer.map { String($0) }.joined()) + result.append(newString) + buffer.removeAll() + } + if sleepCount < 1 { + buffer.append(currentChar) + } + sleepCount -= 1 + } + result.append(buffer.map { String($0) }.joined()) + buffer.removeAll() + return result } }