SPM // Consolidate dependencies.

This commit is contained in:
ShikiSuen 2024-03-05 20:24:49 +08:00
parent 93256f0095
commit 005116c429
65 changed files with 521 additions and 475 deletions

View File

@ -13,13 +13,13 @@ let package = Package(
),
],
dependencies: [
.package(path: "../vChewing_CocoaExtension"),
.package(path: "../vChewing_OSFrameworkImpl"),
],
targets: [
.target(
name: "NSAttributedTextView",
dependencies: [
.product(name: "CocoaExtension", package: "vChewing_CocoaExtension"),
.product(name: "OSFrameworkImpl", package: "vChewing_OSFrameworkImpl"),
]
),
.testTarget(

View File

@ -6,7 +6,7 @@
// Modified by The vChewing Project in order to use it with AppKit.
import AppKit
import CocoaExtension
import OSFrameworkImpl
import SwiftUI
@available(macOS 10.15, *)

View File

@ -7,9 +7,9 @@
// requirements defined in MIT License.
import AppKit
import CocoaExtension
import Foundation
@testable import NSAttributedTextView
import OSFrameworkImpl
import Shared
import XCTest

View File

@ -1,7 +1,6 @@
// (c) 2019 and onwards Robert Muckle-Jones (Apache 2.0 License).
import Foundation
import SwiftExtension
public class LineReader {
let encoding: String.Encoding

View File

@ -7,7 +7,7 @@
// requirements defined in MIT License.
import AppKit
import CocoaExtension
import OSFrameworkImpl
import Shared
private extension NSUserInterfaceLayoutOrientation {

View File

@ -7,7 +7,7 @@
// requirements defined in MIT License.
import AppKit
import CocoaExtension
import OSFrameworkImpl
import Shared
/// AppKit SwiftUI

View File

@ -15,8 +15,7 @@ let package = Package(
dependencies: [
.package(path: "../RMJay_LineReader"),
.package(path: "../vChewing_Megrez"),
.package(path: "../vChewing_PinyinPhonaConverter"),
.package(path: "../vChewing_Shared"),
.package(path: "../vChewing_SwiftExtension"),
],
targets: [
.target(
@ -24,8 +23,7 @@ let package = Package(
dependencies: [
.product(name: "LineReader", package: "RMJay_LineReader"),
.product(name: "Megrez", package: "vChewing_Megrez"),
.product(name: "Shared", package: "vChewing_Shared"),
.product(name: "PinyinPhonaConverter", package: "vChewing_PinyinPhonaConverter"),
.product(name: "SwiftExtension", package: "vChewing_SwiftExtension"),
]
),
.testTarget(

View File

@ -8,7 +8,6 @@
import Foundation
import LineReader
import Shared
public extension LMAssembly {
enum LMConsolidator {
@ -26,19 +25,19 @@ public extension LMAssembly {
let lineReader = try LineReader(file: fileHandle)
for strLine in lineReader { // i=0
if strLine != kPragmaHeader {
vCLog("Header Mismatch, Starting In-Place Consolidation.")
vCLMLog("Header Mismatch, Starting In-Place Consolidation.")
return false
} else {
vCLog("Header Verification Succeeded: \(strLine).")
vCLMLog("Header Verification Succeeded: \(strLine).")
return true
}
}
} catch {
vCLog("Header Verification Failed: File Access Error.")
vCLMLog("Header Verification Failed: File Access Error.")
return false
}
}
vCLog("Header Verification Failed: File Missing.")
vCLMLog("Header Verification Failed: File Missing.")
return false
}
@ -51,12 +50,12 @@ public extension LMAssembly {
let dict = try FileManager.default.attributesOfItem(atPath: path)
if let value = dict[FileAttributeKey.size] as? UInt64 { fileSize = value }
} catch {
vCLog("EOF Fix Failed: File Missing at \(path).")
vCLMLog("EOF Fix Failed: File Missing at \(path).")
return false
}
guard let fileSize = fileSize else { return false }
guard let writeFile = FileHandle(forUpdatingAtPath: path) else {
vCLog("EOF Fix Failed: File Not Writable at \(path).")
vCLMLog("EOF Fix Failed: File Not Writable at \(path).")
return false
}
defer { writeFile.closeFile() }
@ -64,11 +63,11 @@ public extension LMAssembly {
/// consolidate()
writeFile.seek(toFileOffset: fileSize - 1)
if writeFile.readDataToEndOfFile().first != 0x0A {
vCLog("EOF Missing Confirmed, Start Fixing.")
vCLMLog("EOF Missing Confirmed, Start Fixing.")
var newData = Data()
newData.append(0x0A)
writeFile.write(newData)
vCLog("EOF Successfully Assured.")
vCLMLog("EOF Successfully Assured.")
}
return false
}
@ -142,14 +141,29 @@ public extension LMAssembly {
// Write consolidated file contents.
try strProcessed.write(to: urlPath, atomically: false, encoding: .utf8)
} catch {
vCLog("Consolidation Failed w/ File: \(path), error: \(error)")
vCLMLog("Consolidation Failed w/ File: \(path), error: \(error)")
return false
}
vCLog("Either Consolidation Successful Or No-Need-To-Consolidate.")
vCLMLog("Either Consolidation Successful Or No-Need-To-Consolidate.")
return true
}
vCLog("Consolidation Failed: File Missing at \(path).")
vCLMLog("Consolidation Failed: File Missing at \(path).")
return false
}
}
}
private extension String {
mutating func regReplace(pattern: String, replaceWith: String = "") {
// Ref: https://stackoverflow.com/a/40993403/4162914 && https://stackoverflow.com/a/71291137/4162914
do {
let regex = try NSRegularExpression(
pattern: pattern, options: [.caseInsensitive, .anchorsMatchLines]
)
let range = NSRange(startIndex..., in: self)
self = regex.stringByReplacingMatches(
in: self, options: [], range: range, withTemplate: replaceWith
)
} catch { return }
}
}

View File

@ -8,8 +8,6 @@
import Foundation
import Megrez
import Shared
import SQLite3
public extension LMAssembly {
/// LMInstantiatorLMI
@ -47,13 +45,13 @@ public extension LMAssembly {
static var ptrSQL: OpaquePointer?
// SQLite
public private(set) static var isSQLDBConnected: Bool = false
public internal(set) static var isSQLDBConnected: Bool = false
//
public let isCHS: Bool
//
public var config = Config()
public private(set) var config = Config()
// package
public init(
@ -69,21 +67,8 @@ public extension LMAssembly {
return self
}
@discardableResult public static func connectSQLDB(dbPath: String, dropPreviousConnection: Bool = true) -> Bool {
if dropPreviousConnection { disconnectSQLDB() }
vCLog("Establishing SQLite connection to: \(dbPath)")
guard sqlite3_open(dbPath, &Self.ptrSQL) == SQLITE_OK else { return false }
guard "PRAGMA journal_mode = OFF;".runAsSQLExec(dbPointer: &ptrSQL) else { return false }
isSQLDBConnected = true
return true
}
public static func disconnectSQLDB() {
if Self.ptrSQL != nil {
sqlite3_close_v2(Self.ptrSQL)
Self.ptrSQL = nil
}
isSQLDBConnected = false
public static func setCassetCandidateKeyValidator(_ validator: @escaping (String) -> Bool) {
Self.lmCassette.candidateKeysValidator = validator
}
///
@ -126,9 +111,9 @@ public extension LMAssembly {
if FileManager.default.isReadableFile(atPath: path) {
self.lmUserPhrases.clear()
self.lmUserPhrases.open(path)
vCLog("lmUserPhrases: \(self.lmUserPhrases.count) entries of data loaded from: \(path)")
vCLMLog("lmUserPhrases: \(self.lmUserPhrases.count) entries of data loaded from: \(path)")
} else {
vCLog("lmUserPhrases: File access failure: \(path)")
vCLMLog("lmUserPhrases: File access failure: \(path)")
}
}
guard let filterPath = filterPath else { return }
@ -136,9 +121,9 @@ public extension LMAssembly {
if FileManager.default.isReadableFile(atPath: filterPath) {
self.lmFiltered.clear()
self.lmFiltered.open(filterPath)
vCLog("lmFiltered: \(self.lmFiltered.count) entries of data loaded from: \(path)")
vCLMLog("lmFiltered: \(self.lmFiltered.count) entries of data loaded from: \(path)")
} else {
vCLog("lmFiltered: File access failure: \(path)")
vCLMLog("lmFiltered: File access failure: \(path)")
}
}
}
@ -148,9 +133,9 @@ public extension LMAssembly {
if FileManager.default.isReadableFile(atPath: path) {
lmFiltered.clear()
lmFiltered.open(path)
vCLog("lmFiltered: \(lmFiltered.count) entries of data loaded from: \(path)")
vCLMLog("lmFiltered: \(lmFiltered.count) entries of data loaded from: \(path)")
} else {
vCLog("lmFiltered: File access failure: \(path)")
vCLMLog("lmFiltered: File access failure: \(path)")
}
}
@ -159,9 +144,9 @@ public extension LMAssembly {
if FileManager.default.isReadableFile(atPath: path) {
self.lmUserSymbols.clear()
self.lmUserSymbols.open(path)
vCLog("lmUserSymbol: \(self.lmUserSymbols.count) entries of data loaded from: \(path)")
vCLMLog("lmUserSymbol: \(self.lmUserSymbols.count) entries of data loaded from: \(path)")
} else {
vCLog("lmUserSymbol: File access failure: \(path)")
vCLMLog("lmUserSymbol: File access failure: \(path)")
}
}
}
@ -171,9 +156,9 @@ public extension LMAssembly {
if FileManager.default.isReadableFile(atPath: path) {
self.lmAssociates.clear()
self.lmAssociates.open(path)
vCLog("lmAssociates: \(self.lmAssociates.count) entries of data loaded from: \(path)")
vCLMLog("lmAssociates: \(self.lmAssociates.count) entries of data loaded from: \(path)")
} else {
vCLog("lmAssociates: File access failure: \(path)")
vCLMLog("lmAssociates: File access failure: \(path)")
}
}
}
@ -183,9 +168,9 @@ public extension LMAssembly {
if FileManager.default.isReadableFile(atPath: path) {
self.lmReplacements.clear()
self.lmReplacements.open(path)
vCLog("lmReplacements: \(self.lmReplacements.count) entries of data loaded from: \(path)")
vCLMLog("lmReplacements: \(self.lmReplacements.count) entries of data loaded from: \(path)")
} else {
vCLog("lmReplacements: File access failure: \(path)")
vCLMLog("lmReplacements: File access failure: \(path)")
}
}
}
@ -196,9 +181,9 @@ public extension LMAssembly {
if FileManager.default.isReadableFile(atPath: path) {
Self.lmCassette.clear()
Self.lmCassette.open(path)
vCLog("lmCassette: \(Self.lmCassette.count) entries of data loaded from: \(path)")
vCLMLog("lmCassette: \(Self.lmCassette.count) entries of data loaded from: \(path)")
} else {
vCLog("lmCassette: File access failure: \(path)")
vCLMLog("lmCassette: File access failure: \(path)")
}
}
}

View File

@ -8,7 +8,7 @@
import Foundation
import Megrez
import Shared
import SwiftExtension
public extension LMAssembly.LMInstantiator {
///

View File

@ -8,7 +8,6 @@
import Foundation
import Megrez
import Shared
import SQLite3
/* ==============
@ -57,6 +56,23 @@ extension LMAssembly.LMInstantiator {
}
extension LMAssembly.LMInstantiator {
@discardableResult public static func connectSQLDB(dbPath: String, dropPreviousConnection: Bool = true) -> Bool {
if dropPreviousConnection { disconnectSQLDB() }
vCLMLog("Establishing SQLite connection to: \(dbPath)")
guard sqlite3_open(dbPath, &Self.ptrSQL) == SQLITE_OK else { return false }
guard "PRAGMA journal_mode = OFF;".runAsSQLExec(dbPointer: &ptrSQL) else { return false }
isSQLDBConnected = true
return true
}
public static func disconnectSQLDB() {
if Self.ptrSQL != nil {
sqlite3_close_v2(Self.ptrSQL)
Self.ptrSQL = nil
}
isSQLDBConnected = false
}
fileprivate static func querySQL(strStmt sqlQuery: String, coreColumn column: CoreColumn, handler: (String) -> Void) {
guard Self.ptrSQL != nil else { return }
performStatementSansResult { ptrStatement in

View File

@ -0,0 +1,123 @@
// (c) 2021 and onwards The vChewing Project (MIT-NTL License).
// ====================
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
// ... with NTL restriction stating that:
// No trademark license is granted to use the trade names, trademarks, service
// marks, or product names of Contributor, except as required to fulfill notice
// requirements defined in MIT License.
import Foundation
// 使 LMAssembly Tekkon
private typealias LengthSortedDictionary = [Int: [String: String]]
private let mapHanyuPinyinToPhonabets: LengthSortedDictionary = {
let parsed = try? JSONDecoder().decode(LengthSortedDictionary.self, from: jsnHanyuPinyinToMPS.data(using: .utf8) ?? Data([]))
return parsed ?? [:]
}()
extension String {
mutating func convertToPhonabets(newToneOne: String = "") {
if isEmpty || contains("_") || !isNotPureAlphanumerical { return }
let lengths = mapHanyuPinyinToPhonabets.keys.sorted().reversed()
lengths.forEach { length in
mapHanyuPinyinToPhonabets[length]?.forEach { key, value in
self = replacingOccurrences(of: key, with: value)
}
}
self = replacingOccurrences(of: " ", with: newToneOne)
}
}
///
private extension String {
var isNotPureAlphanumerical: Bool {
let regex = ".*[^A-Za-z0-9].*"
let testString = NSPredicate(format: "SELF MATCHES %@", regex)
return testString.evaluate(with: self)
}
}
private let jsnHanyuPinyinToMPS = #"""
{
"1":{"1":" ","2":"ˊ","3":"ˇ","4":"ˋ","5":"˙","a":"","e":"","o":"","q":""},
"2":{"ai":"","an":"","ao":"","ba":"ㄅㄚ","bi":"ㄅㄧ","bo":"ㄅㄛ","bu":"ㄅㄨ",
"ca":"ㄘㄚ","ce":"ㄘㄜ","ci":"","cu":"ㄘㄨ","da":"ㄉㄚ","de":"ㄉㄜ","di":"ㄉㄧ",
"du":"ㄉㄨ","eh":"","ei":"","en":"","er":"","fa":"ㄈㄚ","fo":"ㄈㄛ",
"fu":"ㄈㄨ","ga":"ㄍㄚ","ge":"ㄍㄜ","gi":"ㄍㄧ","gu":"ㄍㄨ","ha":"ㄏㄚ","he":"ㄏㄜ",
"hu":"ㄏㄨ","ji":"ㄐㄧ","ju":"ㄐㄩ","ka":"ㄎㄚ","ke":"ㄎㄜ","ku":"ㄎㄨ","la":"ㄌㄚ",
"le":"ㄌㄜ","li":"ㄌㄧ","lo":"ㄌㄛ","lu":"ㄌㄨ","lv":"ㄌㄩ","ma":"ㄇㄚ","me":"ㄇㄜ",
"mi":"ㄇㄧ","mo":"ㄇㄛ","mu":"ㄇㄨ","na":"ㄋㄚ","ne":"ㄋㄜ","ni":"ㄋㄧ","nu":"ㄋㄨ",
"nv":"ㄋㄩ","ou":"","pa":"ㄆㄚ","pi":"ㄆㄧ","po":"ㄆㄛ","pu":"ㄆㄨ","qi":"ㄑㄧ",
"qu":"ㄑㄩ","re":"ㄖㄜ","ri":"","ru":"ㄖㄨ","sa":"ㄙㄚ","se":"ㄙㄜ","si":"",
"su":"ㄙㄨ","ta":"ㄊㄚ","te":"ㄊㄜ","ti":"ㄊㄧ","tu":"ㄊㄨ","wa":"ㄨㄚ","wo":"ㄨㄛ",
"wu":"","xi":"ㄒㄧ","xu":"ㄒㄩ","ya":"ㄧㄚ","ye":"ㄧㄝ","yi":"","yo":"ㄧㄛ",
"yu":"","za":"ㄗㄚ","ze":"ㄗㄜ","zi":"","zu":"ㄗㄨ"},
"3":{"ang":"","bai":"ㄅㄞ","ban":"ㄅㄢ","bao":"ㄅㄠ","bei":"ㄅㄟ","ben":"ㄅㄣ",
"bie":"ㄅㄧㄝ","bin":"ㄅㄧㄣ","cai":"ㄘㄞ","can":"ㄘㄢ","cao":"ㄘㄠ","cei":"ㄘㄟ",
"cen":"ㄘㄣ","cha":"ㄔㄚ","che":"ㄔㄜ","chi":"","chu":"ㄔㄨ","cou":"ㄘㄡ",
"cui":"ㄘㄨㄟ","cun":"ㄘㄨㄣ","cuo":"ㄘㄨㄛ","dai":"ㄉㄞ","dan":"ㄉㄢ","dao":"ㄉㄠ",
"dei":"ㄉㄟ","den":"ㄉㄣ","dia":"ㄉㄧㄚ","die":"ㄉㄧㄝ","diu":"ㄉㄧㄡ","dou":"ㄉㄡ",
"dui":"ㄉㄨㄟ","dun":"ㄉㄨㄣ","duo":"ㄉㄨㄛ","eng":"","fan":"ㄈㄢ","fei":"ㄈㄟ",
"fen":"ㄈㄣ","fou":"ㄈㄡ","gai":"ㄍㄞ","gan":"ㄍㄢ","gao":"ㄍㄠ","gei":"ㄍㄟ",
"gen":"ㄍㄣ","gin":"ㄍㄧㄣ","gou":"ㄍㄡ","gua":"ㄍㄨㄚ","gue":"ㄍㄨㄜ","gui":"ㄍㄨㄟ",
"gun":"ㄍㄨㄣ","guo":"ㄍㄨㄛ","hai":"ㄏㄞ","han":"ㄏㄢ","hao":"ㄏㄠ","hei":"ㄏㄟ",
"hen":"ㄏㄣ","hou":"ㄏㄡ","hua":"ㄏㄨㄚ","hui":"ㄏㄨㄟ","hun":"ㄏㄨㄣ","huo":"ㄏㄨㄛ",
"jia":"ㄐㄧㄚ","jie":"ㄐㄧㄝ","jin":"ㄐㄧㄣ","jiu":"ㄐㄧㄡ","jue":"ㄐㄩㄝ",
"jun":"ㄐㄩㄣ","kai":"ㄎㄞ","kan":"ㄎㄢ","kao":"ㄎㄠ","ken":"ㄎㄣ","kiu":"ㄎㄧㄡ",
"kou":"ㄎㄡ","kua":"ㄎㄨㄚ","kui":"ㄎㄨㄟ","kun":"ㄎㄨㄣ","kuo":"ㄎㄨㄛ","lai":"ㄌㄞ",
"lan":"ㄌㄢ","lao":"ㄌㄠ","lei":"ㄌㄟ","lia":"ㄌㄧㄚ","lie":"ㄌㄧㄝ","lin":"ㄌㄧㄣ",
"liu":"ㄌㄧㄡ","lou":"ㄌㄡ","lun":"ㄌㄨㄣ","luo":"ㄌㄨㄛ","lve":"ㄌㄩㄝ","mai":"ㄇㄞ",
"man":"ㄇㄢ","mao":"ㄇㄠ","mei":"ㄇㄟ","men":"ㄇㄣ","mie":"ㄇㄧㄝ","min":"ㄇㄧㄣ",
"miu":"ㄇㄧㄡ","mou":"ㄇㄡ","nai":"ㄋㄞ","nan":"ㄋㄢ","nao":"ㄋㄠ","nei":"ㄋㄟ",
"nen":"ㄋㄣ","nie":"ㄋㄧㄝ","nin":"ㄋㄧㄣ","niu":"ㄋㄧㄡ","nou":"ㄋㄡ","nui":"ㄋㄨㄟ",
"nun":"ㄋㄨㄣ","nuo":"ㄋㄨㄛ","nve":"ㄋㄩㄝ","pai":"ㄆㄞ","pan":"ㄆㄢ","pao":"ㄆㄠ",
"pei":"ㄆㄟ","pen":"ㄆㄣ","pia":"ㄆㄧㄚ","pie":"ㄆㄧㄝ","pin":"ㄆㄧㄣ","pou":"ㄆㄡ",
"qia":"ㄑㄧㄚ","qie":"ㄑㄧㄝ","qin":"ㄑㄧㄣ","qiu":"ㄑㄧㄡ","que":"ㄑㄩㄝ",
"qun":"ㄑㄩㄣ","ran":"ㄖㄢ","rao":"ㄖㄠ","ren":"ㄖㄣ","rou":"ㄖㄡ","rui":"ㄖㄨㄟ",
"run":"ㄖㄨㄣ","ruo":"ㄖㄨㄛ","sai":"ㄙㄞ","san":"ㄙㄢ","sao":"ㄙㄠ","sei":"ㄙㄟ",
"sen":"ㄙㄣ","sha":"ㄕㄚ","she":"ㄕㄜ","shi":"","shu":"ㄕㄨ","sou":"ㄙㄡ",
"sui":"ㄙㄨㄟ","sun":"ㄙㄨㄣ","suo":"ㄙㄨㄛ","tai":"ㄊㄞ","tan":"ㄊㄢ","tao":"ㄊㄠ",
"tie":"ㄊㄧㄝ","tou":"ㄊㄡ","tui":"ㄊㄨㄟ","tun":"ㄊㄨㄣ","tuo":"ㄊㄨㄛ",
"wai":"ㄨㄞ","wan":"ㄨㄢ","wei":"ㄨㄟ","wen":"ㄨㄣ","xia":"ㄒㄧㄚ","xie":"ㄒㄧㄝ",
"xin":"ㄒㄧㄣ","xiu":"ㄒㄧㄡ","xue":"ㄒㄩㄝ","xun":"ㄒㄩㄣ","yai":"ㄧㄞ",
"yan":"ㄧㄢ","yao":"ㄧㄠ","yin":"ㄧㄣ","you":"ㄧㄡ","yue":"ㄩㄝ","yun":"ㄩㄣ",
"zai":"ㄗㄞ","zan":"ㄗㄢ","zao":"ㄗㄠ","zei":"ㄗㄟ","zen":"ㄗㄣ","zha":"ㄓㄚ",
"zhe":"ㄓㄜ","zhi":"","zhu":"ㄓㄨ","zou":"ㄗㄡ","zui":"ㄗㄨㄟ","zun":"ㄗㄨㄣ",
"zuo":"ㄗㄨㄛ"},
"4":{"bang":"ㄅㄤ","beng":"ㄅㄥ","bian":"ㄅㄧㄢ","biao":"ㄅㄧㄠ","bing":"ㄅㄧㄥ",
"cang":"ㄘㄤ","ceng":"ㄘㄥ","chai":"ㄔㄞ","chan":"ㄔㄢ","chao":"ㄔㄠ","chen":"ㄔㄣ",
"chou":"ㄔㄡ","chua":"ㄔㄨㄚ","chui":"ㄔㄨㄟ","chun":"ㄔㄨㄣ","chuo":"ㄔㄨㄛ",
"cong":"ㄘㄨㄥ","cuan":"ㄘㄨㄢ","dang":"ㄉㄤ","deng":"ㄉㄥ","dian":"ㄉㄧㄢ",
"diao":"ㄉㄧㄠ","ding":"ㄉㄧㄥ","dong":"ㄉㄨㄥ","duan":"ㄉㄨㄢ","fang":"ㄈㄤ",
"feng":"ㄈㄥ","fiao":"ㄈㄧㄠ","fong":"ㄈㄨㄥ","gang":"ㄍㄤ","geng":"ㄍㄥ",
"giao":"ㄍㄧㄠ","gong":"ㄍㄨㄥ","guai":"ㄍㄨㄞ","guan":"ㄍㄨㄢ","hang":"ㄏㄤ",
"heng":"ㄏㄥ","hong":"ㄏㄨㄥ","huai":"ㄏㄨㄞ","huan":"ㄏㄨㄢ","jian":"ㄐㄧㄢ",
"jiao":"ㄐㄧㄠ","jing":"ㄐㄧㄥ","juan":"ㄐㄩㄢ","kang":"ㄎㄤ","keng":"ㄎㄥ",
"kong":"ㄎㄨㄥ","kuai":"ㄎㄨㄞ","kuan":"ㄎㄨㄢ","lang":"ㄌㄤ","leng":"ㄌㄥ",
"lian":"ㄌㄧㄢ","liao":"ㄌㄧㄠ","ling":"ㄌㄧㄥ","long":"ㄌㄨㄥ","luan":"ㄌㄨㄢ",
"lvan":"ㄌㄩㄢ","mang":"ㄇㄤ","meng":"ㄇㄥ","mian":"ㄇㄧㄢ","miao":"ㄇㄧㄠ",
"ming":"ㄇㄧㄥ","nang":"ㄋㄤ","neng":"ㄋㄥ","nian":"ㄋㄧㄢ","niao":"ㄋㄧㄠ",
"ning":"ㄋㄧㄥ","nong":"ㄋㄨㄥ","nuan":"ㄋㄨㄢ","pang":"ㄆㄤ","peng":"ㄆㄥ",
"pian":"ㄆㄧㄢ","piao":"ㄆㄧㄠ","ping":"ㄆㄧㄥ","qian":"ㄑㄧㄢ","qiao":"ㄑㄧㄠ",
"qing":"ㄑㄧㄥ","quan":"ㄑㄩㄢ","rang":"ㄖㄤ","reng":"ㄖㄥ","rong":"ㄖㄨㄥ",
"ruan":"ㄖㄨㄢ","sang":"ㄙㄤ","seng":"ㄙㄥ","shai":"ㄕㄞ","shan":"ㄕㄢ",
"shao":"ㄕㄠ","shei":"ㄕㄟ","shen":"ㄕㄣ","shou":"ㄕㄡ","shua":"ㄕㄨㄚ",
"shui":"ㄕㄨㄟ","shun":"ㄕㄨㄣ","shuo":"ㄕㄨㄛ","song":"ㄙㄨㄥ","suan":"ㄙㄨㄢ",
"tang":"ㄊㄤ","teng":"ㄊㄥ","tian":"ㄊㄧㄢ","tiao":"ㄊㄧㄠ","ting":"ㄊㄧㄥ",
"tong":"ㄊㄨㄥ","tuan":"ㄊㄨㄢ","wang":"ㄨㄤ","weng":"ㄨㄥ","xian":"ㄒㄧㄢ",
"xiao":"ㄒㄧㄠ","xing":"ㄒㄧㄥ","xuan":"ㄒㄩㄢ","yang":"ㄧㄤ","ying":"ㄧㄥ",
"yong":"ㄩㄥ","yuan":"ㄩㄢ","zang":"ㄗㄤ","zeng":"ㄗㄥ","zhai":"ㄓㄞ",
"zhan":"ㄓㄢ","zhao":"ㄓㄠ","zhei":"ㄓㄟ","zhen":"ㄓㄣ","zhou":"ㄓㄡ",
"zhua":"ㄓㄨㄚ","zhui":"ㄓㄨㄟ","zhun":"ㄓㄨㄣ","zhuo":"ㄓㄨㄛ",
"zong":"ㄗㄨㄥ","zuan":"ㄗㄨㄢ"},
"5":{"biang":"ㄅㄧㄤ","chang":"ㄔㄤ","cheng":"ㄔㄥ","chong":"ㄔㄨㄥ","chuai":"ㄔㄨㄞ",
"chuan":"ㄔㄨㄢ","duang":"ㄉㄨㄤ","guang":"ㄍㄨㄤ","huang":"ㄏㄨㄤ","jiang":"ㄐㄧㄤ",
"jiong":"ㄐㄩㄥ","kiang":"ㄎㄧㄤ","kuang":"ㄎㄨㄤ","liang":"ㄌㄧㄤ","niang":"ㄋㄧㄤ",
"qiang":"ㄑㄧㄤ","qiong":"ㄑㄩㄥ","shang":"ㄕㄤ","sheng":"ㄕㄥ","shuai":"ㄕㄨㄞ",
"shuan":"ㄕㄨㄢ","xiang":"ㄒㄧㄤ","xiong":"ㄒㄩㄥ","zhang":"ㄓㄤ","zheng":"ㄓㄥ",
"zhong":"ㄓㄨㄥ","zhuai":"ㄓㄨㄞ","zhuan":"ㄓㄨㄢ"},
"6":{"chuang":"ㄔㄨㄤ","shuang":"ㄕㄨㄤ","zhuang":"ㄓㄨㄤ"}
}
"""#

View File

@ -7,8 +7,6 @@
// requirements defined in MIT License.
import Megrez
import PinyinPhonaConverter
import Shared
extension LMAssembly {
struct LMAssociates {
@ -48,8 +46,8 @@ extension LMAssembly {
replaceData(textData: rawStrData)
} catch {
filePath = oldPath
vCLog("\(error)")
vCLog("↑ Exception happened when reading data at: \(path).")
vCLMLog("\(error)")
vCLMLog("↑ Exception happened when reading data at: \(path).")
return false
}
@ -93,7 +91,7 @@ extension LMAssembly {
do {
try strData.write(toFile: filePath, atomically: true, encoding: .utf8)
} catch {
vCLog("Failed to save current database to: \(filePath)")
vCLMLog("Failed to save current database to: \(filePath)")
}
}
@ -113,8 +111,7 @@ extension LMAssembly {
pairs.append(theValue)
}
}
var set = Set<String>()
return pairs.filter { set.insert($0).inserted }
return pairs.deduplicated
}
public func hasValuesFor(pair: Megrez.KeyValuePaired) -> Bool {

View File

@ -10,7 +10,6 @@
import Foundation
import LineReader
import Megrez
import Shared
extension LMAssembly {
/// 便使
@ -40,6 +39,7 @@ extension LMAssembly {
public private(set) var areCandidateKeysShiftHeld: Bool = false
public private(set) var supplyQuickResults: Bool = false
public private(set) var supplyPartiallyMatchedResults: Bool = false
public var candidateKeysValidator: (String) -> Bool = { _ in false }
/// 西 - NORM
private var norm = 0.0
}
@ -195,7 +195,7 @@ extension LMAssembly.LMCassette {
// Post process.
// Package 便 J / K
//
if CandidateKey.validate(keys: selectionKeys) != nil { selectionKeys = "1234567890" }
if !candidateKeysValidator(selectionKeys) { selectionKeys = "1234567890" }
if !keysUsedInCharDef.intersection(selectionKeys.map(\.description)).isEmpty {
areCandidateKeysShiftHeld = true
}
@ -204,10 +204,10 @@ extension LMAssembly.LMCassette {
filePath = path
return true
} catch {
vCLog("CIN Loading Failed: File Access Error.")
vCLMLog("CIN Loading Failed: File Access Error.")
}
} else {
vCLog("CIN Loading Failed: File Missing.")
vCLMLog("CIN Loading Failed: File Missing.")
}
filePath = oldPath
return false

View File

@ -7,8 +7,6 @@
// requirements defined in MIT License.
import Megrez
import PinyinPhonaConverter
import Shared
extension LMAssembly {
/// LMCore LMCoreEX range
@ -81,8 +79,8 @@ extension LMAssembly {
replaceData(textData: rawStrData)
} catch {
filePath = oldPath
vCLog("\(error)")
vCLog("↑ Exception happened when reading data at: \(path).")
vCLMLog("\(error)")
vCLMLog("↑ Exception happened when reading data at: \(path).")
return false
}
@ -133,7 +131,7 @@ extension LMAssembly {
}
try dataToWrite.write(toFile: filePath, atomically: true, encoding: .utf8)
} catch {
vCLog("Failed to save current database to: \(filePath)")
vCLMLog("Failed to save current database to: \(filePath)")
}
}
@ -150,7 +148,7 @@ extension LMAssembly {
strDump += addline
}
}
vCLog(strDump)
vCLMLog(strDump)
}
/// strData

View File

@ -7,7 +7,6 @@
// requirements defined in MIT License.
import Foundation
import Shared
extension LMAssembly {
struct LMPlainBopomofo {
@ -22,8 +21,8 @@ extension LMAssembly {
let rawJSON = try JSONDecoder().decode([String: [String: String]].self, from: rawData)
dataMap = rawJSON
} catch {
vCLog("\(error)")
vCLog("↑ Exception happened when parsing raw JSON sequence data from vChewing LMAssembly.")
vCLMLog("\(error)")
vCLMLog("↑ Exception happened when parsing raw JSON sequence data from vChewing LMAssembly.")
dataMap = [:]
}
}

View File

@ -6,8 +6,6 @@
// marks, or product names of Contributor, except as required to fulfill notice
// requirements defined in MIT License.
import Shared
extension LMAssembly {
struct LMReplacements {
public private(set) var filePath: String?
@ -35,8 +33,8 @@ extension LMAssembly {
replaceData(textData: rawStrData)
} catch {
filePath = oldPath
vCLog("\(error)")
vCLog("↑ Exception happened when reading data at: \(path).")
vCLMLog("\(error)")
vCLMLog("↑ Exception happened when reading data at: \(path).")
return false
}
@ -72,7 +70,7 @@ extension LMAssembly {
do {
try strData.write(toFile: filePath, atomically: true, encoding: .utf8)
} catch {
vCLog("Failed to save current database to: \(filePath)")
vCLMLog("Failed to save current database to: \(filePath)")
}
}
@ -81,7 +79,7 @@ extension LMAssembly {
for entry in rangeMap {
strDump += strData[entry.value] + "\n"
}
vCLog(strDump)
vCLMLog(strDump)
}
public func valuesFor(key: String) -> String {

View File

@ -9,7 +9,6 @@
import Foundation
import Megrez
import Shared
// MARK: - Public Types.
@ -202,18 +201,18 @@ extension LMAssembly.LMUserOverride {
do {
let nullData = "{}"
guard let fileURL = fileURL ?? fileSaveLocationURL else {
throw "given fileURL is invalid or nil."
throw UOMError(rawValue: "given fileURL is invalid or nil.")
}
try nullData.write(to: fileURL, atomically: false, encoding: .utf8)
} catch {
vCLog("UOM Error: Unable to clear the data in the UOM file. Details: \(error)")
vCLMLog("UOM Error: Unable to clear the data in the UOM file. Details: \(error)")
return
}
}
func saveData(toURL fileURL: URL? = nil) {
guard let fileURL: URL = fileURL ?? fileSaveLocationURL else {
vCLog("UOM saveData() failed. At least the file Save URL is not set for the current UOM.")
vCLMLog("UOM saveData() failed. At least the file Save URL is not set for the current UOM.")
return
}
// 使 JSONSerialization
@ -222,14 +221,14 @@ extension LMAssembly.LMUserOverride {
guard let jsonData = try? encoder.encode(mutLRUMap) else { return }
try jsonData.write(to: fileURL, options: .atomic)
} catch {
vCLog("UOM Error: Unable to save data, abort saving. Details: \(error)")
vCLMLog("UOM Error: Unable to save data, abort saving. Details: \(error)")
return
}
}
func loadData(fromURL fileURL: URL? = nil) {
guard let fileURL: URL = fileURL ?? fileSaveLocationURL else {
vCLog("UOM loadData() failed. At least the file Load URL is not set for the current UOM.")
vCLMLog("UOM loadData() failed. At least the file Load URL is not set for the current UOM.")
return
}
// 使 JSONSerialization
@ -238,13 +237,13 @@ extension LMAssembly.LMUserOverride {
let data = try Data(contentsOf: fileURL, options: .mappedIfSafe)
if ["", "{}"].contains(String(data: data, encoding: .utf8)) { return }
guard let jsonResult = try? decoder.decode([String: KeyObservationPair].self, from: data) else {
vCLog("UOM Error: Read file content type invalid, abort loading.")
vCLMLog("UOM Error: Read file content type invalid, abort loading.")
return
}
mutLRUMap = jsonResult
resetMRUList()
} catch {
vCLog("UOM Error: Unable to read file or parse the data, abort loading. Details: \(error)")
vCLMLog("UOM Error: Unable to read file or parse the data, abort loading. Details: \(error)")
return
}
}
@ -271,7 +270,7 @@ extension LMAssembly.LMUserOverride {
mutLRUMap.removeValue(forKey: mutLRUList[mutLRUList.endIndex - 1].key)
mutLRUList.removeLast()
}
vCLog("UOM: Observation finished with new observation: \(key)")
vCLMLog("UOM: Observation finished with new observation: \(key)")
saveCallback?() ?? saveData()
return
}
@ -282,7 +281,7 @@ extension LMAssembly.LMUserOverride {
)
mutLRUList.insert(theNeta, at: 0)
mutLRUMap[key] = theNeta
vCLog("UOM: Observation finished with existing observation: \(key)")
vCLMLog("UOM: Observation finished with existing observation: \(key)")
saveCallback?() ?? saveData()
}
}
@ -400,3 +399,10 @@ extension LMAssembly.LMUserOverride {
return result
}
}
struct UOMError: LocalizedError {
var rawValue: String
var errorDescription: String? {
NSLocalizedString("rawValue", comment: "")
}
}

View File

@ -7,7 +7,6 @@
// requirements defined in MIT License.
import Foundation
import Shared
import SQLite3
public enum LMAssembly {
@ -56,7 +55,7 @@ extension Array where Element == String {
sqlite3_prepare_v2(ptrDB, strStmt, -1, &ptrStmt, nil) == SQLITE_OK && sqlite3_step(ptrStmt) == SQLITE_DONE
}
guard thisResult else {
vCLog("SQL Query Error. Statement: \(strStmt)")
vCLMLog("SQL Query Error. Statement: \(strStmt)")
return false
}
}
@ -83,3 +82,13 @@ func performStatementSansResult(_ handler: (inout OpaquePointer?) -> Void) {
}
handler(&ptrStmt)
}
func vCLMLog(_ strPrint: StringLiteralType) {
guard let toLog = UserDefaults.standard.object(forKey: "_DebugMode") as? Bool else {
NSLog("vChewingDebug: %@", strPrint)
return
}
if toLog {
NSLog("vChewingDebug: %@", strPrint)
}
}

View File

@ -19,7 +19,7 @@ let package = Package(
.package(path: "../Qwertyyb_ShiftKeyUpChecker"),
.package(path: "../vChewing_BrailleSputnik"),
.package(path: "../vChewing_CandidateWindow"),
.package(path: "../vChewing_CocoaExtension"),
.package(path: "../vChewing_OSFrameworkImpl"),
.package(path: "../vChewing_Hotenka"),
.package(path: "../vChewing_IMKUtils"),
.package(path: "../vChewing_KimoDataReader"),
@ -42,7 +42,7 @@ let package = Package(
.product(name: "BrailleSputnik", package: "vChewing_BrailleSputnik"),
.product(name: "BookmarkManager", package: "Jad_BookmarkManager"),
.product(name: "CandidateWindow", package: "vChewing_CandidateWindow"),
.product(name: "CocoaExtension", package: "vChewing_CocoaExtension"),
.product(name: "OSFrameworkImpl", package: "vChewing_OSFrameworkImpl"),
.product(name: "FolderMonitor", package: "DanielGalasko_FolderMonitor"),
.product(name: "Hotenka", package: "vChewing_Hotenka"),
.product(name: "IMKUtils", package: "vChewing_IMKUtils"),

View File

@ -9,9 +9,9 @@
/// 調
import CandidateWindow
import CocoaExtension
import InputMethodKit
import Megrez
import OSFrameworkImpl
import Shared
// MARK: - § 調 (Handle Candidate State).

View File

@ -1041,11 +1041,15 @@ extension InputHandler {
let fullWidthResult = behaviorValue % 2 != 0 //
triagePrefs: switch (behaviorValue, isConsideredEmptyForNow) {
case (2, _), (3, _), (4, false), (5, false):
currentLM.config.numPadFWHWStatus = fullWidthResult
currentLM.setOptions { config in
config.numPadFWHWStatus = fullWidthResult
}
if handlePunctuation("_NumPad_\(inputText)") { return true }
default: break triagePrefs // case 0 & 1
}
currentLM.config.numPadFWHWStatus = nil
currentLM.setOptions { config in
config.numPadFWHWStatus = nil
}
delegate.switchState(IMEState.ofEmpty())
let charToCommit = inputText.applyingTransformFW2HW(reverse: fullWidthResult)
delegate.switchState(IMEState.ofCommitting(textToCommit: charToCommit))

View File

@ -9,11 +9,11 @@
/// 調 IMK 調
/// 調
import CocoaExtension
import IMKUtils
import InputMethodKit
import LangModelAssembly
import Megrez
import OSFrameworkImpl
import Shared
// MARK: - § 調 (Handle Input with States) * Triage

View File

@ -65,6 +65,11 @@ public class LMMgr {
///
/// - Remark: cassettePath()
public static func loadCassetteData() {
func validateCassetteCandidateKey(_ target: String) -> Bool {
CandidateKey.validate(keys: target) == nil
}
LMAssembly.LMInstantiator.setCassetCandidateKeyValidator(validateCassetteCandidateKey)
LMAssembly.LMInstantiator.loadCassetteData(path: cassettePath())
}

View File

@ -8,7 +8,7 @@
import AppKit
import Carbon
import CocoaExtension
import OSFrameworkImpl
import Shared
public class SecurityAgentHelper {

View File

@ -7,8 +7,8 @@
// requirements defined in MIT License.
import AppKit
import CocoaExtension
import Foundation
import OSFrameworkImpl
import Shared
public class VwrServiceMenuEditor: NSViewController {

View File

@ -7,10 +7,10 @@
// requirements defined in MIT License.
import CandidateWindow
import CocoaExtension
import IMKUtils
import InputMethodKit
import NotifierUI
import OSFrameworkImpl
import PopupCompositionBuffer
import Shared
import ShiftKeyUpChecker

View File

@ -6,10 +6,10 @@
// marks, or product names of Contributor, except as required to fulfill notice
// requirements defined in MIT License.
import CocoaExtension
import IMKUtils
import InputMethodKit
import NotifierUI
import OSFrameworkImpl
import Shared
import SwiftyCapsLockToggler

View File

@ -7,8 +7,8 @@
// requirements defined in MIT License.
import AppKit
import CocoaExtension
import NotifierUI
import OSFrameworkImpl
import Shared
import SwiftExtension

View File

@ -8,8 +8,8 @@
import AppKit
import BookmarkManager
import CocoaExtension
import Foundation
import OSFrameworkImpl
import Shared
public extension SettingsPanesCocoa {

View File

@ -7,8 +7,8 @@
// requirements defined in MIT License.
import BookmarkManager
import OSFrameworkImpl
import Shared
import SwiftExtension
import SwiftUI
@available(macOS 13, *)

View File

@ -7,7 +7,7 @@
// requirements defined in MIT License.
import BookmarkManager
import CocoaExtension
import OSFrameworkImpl
import Shared
import SwiftExtension
import SwiftUI

View File

@ -6,10 +6,10 @@
// marks, or product names of Contributor, except as required to fulfill notice
// requirements defined in MIT License.
import CocoaExtension
import InputMethodKit
import LangModelAssembly
@testable import MainAssembly
import OSFrameworkImpl
import Shared
import XCTest

View File

@ -6,10 +6,10 @@
// marks, or product names of Contributor, except as required to fulfill notice
// requirements defined in MIT License.
import CocoaExtension
import InputMethodKit
import LangModelAssembly
@testable import MainAssembly
import OSFrameworkImpl
import Shared
import XCTest

View File

@ -6,10 +6,10 @@
// marks, or product names of Contributor, except as required to fulfill notice
// requirements defined in MIT License.
import CocoaExtension
import InputMethodKit
import LangModelAssembly
@testable import MainAssembly
import OSFrameworkImpl
import Shared
import XCTest

View File

@ -13,13 +13,13 @@ let package = Package(
),
],
dependencies: [
.package(path: "../vChewing_CocoaExtension"),
.package(path: "../vChewing_OSFrameworkImpl"),
],
targets: [
.target(
name: "NotifierUI",
dependencies: [
.product(name: "CocoaExtension", package: "vChewing_CocoaExtension"),
.product(name: "OSFrameworkImpl", package: "vChewing_OSFrameworkImpl"),
]
),
]

View File

@ -7,7 +7,7 @@
// requirements defined in MIT License.
import AppKit
import CocoaExtension
import OSFrameworkImpl
public class Notifier: NSWindowController {
public static func notify(message: String) {

View File

@ -2,14 +2,14 @@
import PackageDescription
let package = Package(
name: "CocoaExtension",
name: "OSFrameworkImpl",
platforms: [
.macOS(.v11),
],
products: [
.library(
name: "CocoaExtension",
targets: ["CocoaExtension"]
name: "OSFrameworkImpl",
targets: ["OSFrameworkImpl"]
),
],
dependencies: [
@ -17,7 +17,7 @@ let package = Package(
],
targets: [
.target(
name: "CocoaExtension",
name: "OSFrameworkImpl",
dependencies: [
.product(name: "SwiftExtension", package: "vChewing_SwiftExtension"),
]

View File

@ -1,4 +1,4 @@
# CocoaExtension
# OSFrameworkImpl
威注音輸入法針對 Cocoa 的一些功能擴充,使程式維護體驗更佳。

View File

@ -210,19 +210,6 @@ public extension NSApplication {
}
}
// MARK: - String.applyingTransform
public extension String {
func applyingTransformFW2HW(reverse: Bool) -> String {
if #available(macOS 10.11, *) {
return applyingTransform(.fullwidthToHalfwidth, reverse: reverse) ?? self
}
let theString = NSMutableString(string: self)
CFStringTransform(theString, nil, kCFStringTransformFullwidthHalfwidth, reverse)
return theString as String
}
}
// MARK: - Check whether current date is the given date.
public extension Date {

View File

@ -6,6 +6,7 @@
// marks, or product names of Contributor, except as required to fulfill notice
// requirements defined in MIT License.
import SwiftExtension
import SwiftUI
// MARK: - Add "didChange" support to bindings.

View File

@ -10,6 +10,7 @@ import AppKit
import Combine
import Foundation
import LangModelAssembly
import OSFrameworkImpl
import Shared
import SwiftExtension
import SwiftUI

View File

@ -1,9 +0,0 @@
.DS_Store
/.build
/Packages
/*.xcodeproj
xcuserdata/
DerivedData/
.swiftpm/config/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc

View File

@ -1,22 +0,0 @@
// swift-tools-version:5.3
import PackageDescription
let package = Package(
name: "PinyinPhonaConverter",
platforms: [
.macOS(.v11),
],
products: [
.library(
name: "PinyinPhonaConverter",
targets: ["PinyinPhonaConverter"]
),
],
dependencies: [],
targets: [
.target(
name: "PinyinPhonaConverter",
dependencies: []
),
]
)

View File

@ -1,13 +0,0 @@
# PinyinPhonaConverter
拼音轉注音模組。因為太耽誤編譯時間,所以單獨拿出來寫了一個給 LangModelAssembly 專用的版本。
```
// (c) 2021 and onwards The vChewing Project (MIT-NTL License).
// ====================
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
// ... with NTL restriction stating that:
// No trademark license is granted to use the trade names, trademarks, service
// marks, or product names of Contributor, except as required to fulfill notice
// requirements defined in MIT License.
```

View File

@ -1,92 +0,0 @@
// (c) 2021 and onwards The vChewing Project (MIT-NTL License).
// ====================
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
// ... with NTL restriction stating that:
// No trademark license is granted to use the trade names, trademarks, service
// marks, or product names of Contributor, except as required to fulfill notice
// requirements defined in MIT License.
import AppKit
public extension String {
mutating func convertToPhonabets(newToneOne: String = "") {
if isEmpty || contains("_") || !isNotPureAlphanumerical { return }
for key in arrHanyuPinyinToPhonabets {
self = replacingOccurrences(of: key.0, with: key.1)
}
self = replacingOccurrences(of: " ", with: newToneOne)
}
}
///
private extension String {
var isNotPureAlphanumerical: Bool {
let regex = ".*[^A-Za-z0-9].*"
let testString = NSPredicate(format: "SELF MATCHES %@", regex)
return testString.evaluate(with: self)
}
}
private let arrHanyuPinyinToPhonabets: [(String, String)] = [
("chuang", "ㄔㄨㄤ"), ("shuang", "ㄕㄨㄤ"), ("zhuang", "ㄓㄨㄤ"), ("chang", "ㄔㄤ"), ("cheng", "ㄔㄥ"), ("chong", "ㄔㄨㄥ"),
("chuai", "ㄔㄨㄞ"), ("chuan", "ㄔㄨㄢ"), ("guang", "ㄍㄨㄤ"), ("huang", "ㄏㄨㄤ"), ("jiang", "ㄐㄧㄤ"), ("jiong", "ㄐㄩㄥ"),
("kiang", "ㄎㄧㄤ"), ("kuang", "ㄎㄨㄤ"), ("biang", "ㄅㄧㄤ"), ("duang", "ㄉㄨㄤ"), ("liang", "ㄌㄧㄤ"), ("niang", "ㄋㄧㄤ"),
("qiang", "ㄑㄧㄤ"), ("qiong", "ㄑㄩㄥ"), ("shang", "ㄕㄤ"), ("sheng", "ㄕㄥ"), ("shuai", "ㄕㄨㄞ"), ("shuan", "ㄕㄨㄢ"),
("xiang", "ㄒㄧㄤ"), ("xiong", "ㄒㄩㄥ"), ("zhang", "ㄓㄤ"), ("zheng", "ㄓㄥ"), ("zhong", "ㄓㄨㄥ"), ("zhuai", "ㄓㄨㄞ"),
("zhuan", "ㄓㄨㄢ"), ("bang", "ㄅㄤ"), ("beng", "ㄅㄥ"), ("bian", "ㄅㄧㄢ"), ("biao", "ㄅㄧㄠ"), ("bing", "ㄅㄧㄥ"), ("cang", "ㄘㄤ"),
("ceng", "ㄘㄥ"), ("chai", "ㄔㄞ"), ("chan", "ㄔㄢ"), ("chao", "ㄔㄠ"), ("chen", "ㄔㄣ"), ("chou", "ㄔㄡ"), ("chua", "ㄔㄨㄚ"),
("chui", "ㄔㄨㄟ"), ("chun", "ㄔㄨㄣ"), ("chuo", "ㄔㄨㄛ"), ("cong", "ㄘㄨㄥ"), ("cuan", "ㄘㄨㄢ"), ("dang", "ㄉㄤ"), ("deng", "ㄉㄥ"),
("dian", "ㄉㄧㄢ"), ("diao", "ㄉㄧㄠ"), ("ding", "ㄉㄧㄥ"), ("dong", "ㄉㄨㄥ"), ("duan", "ㄉㄨㄢ"), ("fang", "ㄈㄤ"), ("feng", "ㄈㄥ"),
("fiao", "ㄈㄧㄠ"), ("fong", "ㄈㄨㄥ"), ("gang", "ㄍㄤ"), ("geng", "ㄍㄥ"), ("giao", "ㄍㄧㄠ"), ("gong", "ㄍㄨㄥ"), ("guai", "ㄍㄨㄞ"),
("guan", "ㄍㄨㄢ"), ("hang", "ㄏㄤ"), ("heng", "ㄏㄥ"), ("hong", "ㄏㄨㄥ"), ("huai", "ㄏㄨㄞ"), ("huan", "ㄏㄨㄢ"), ("jian", "ㄐㄧㄢ"),
("jiao", "ㄐㄧㄠ"), ("jing", "ㄐㄧㄥ"), ("juan", "ㄐㄩㄢ"), ("kang", "ㄎㄤ"), ("keng", "ㄎㄥ"), ("kong", "ㄎㄨㄥ"), ("kuai", "ㄎㄨㄞ"),
("kuan", "ㄎㄨㄢ"), ("lang", "ㄌㄤ"), ("leng", "ㄌㄥ"), ("lian", "ㄌㄧㄢ"), ("liao", "ㄌㄧㄠ"), ("ling", "ㄌㄧㄥ"), ("long", "ㄌㄨㄥ"),
("luan", "ㄌㄨㄢ"), ("lvan", "ㄌㄩㄢ"), ("mang", "ㄇㄤ"), ("meng", "ㄇㄥ"), ("mian", "ㄇㄧㄢ"), ("miao", "ㄇㄧㄠ"), ("ming", "ㄇㄧㄥ"),
("nang", "ㄋㄤ"), ("neng", "ㄋㄥ"), ("nian", "ㄋㄧㄢ"), ("niao", "ㄋㄧㄠ"), ("ning", "ㄋㄧㄥ"), ("nong", "ㄋㄨㄥ"), ("nuan", "ㄋㄨㄢ"),
("pang", "ㄆㄤ"), ("peng", "ㄆㄥ"), ("pian", "ㄆㄧㄢ"), ("piao", "ㄆㄧㄠ"), ("ping", "ㄆㄧㄥ"), ("qian", "ㄑㄧㄢ"), ("qiao", "ㄑㄧㄠ"),
("qing", "ㄑㄧㄥ"), ("quan", "ㄑㄩㄢ"), ("rang", "ㄖㄤ"), ("reng", "ㄖㄥ"), ("rong", "ㄖㄨㄥ"), ("ruan", "ㄖㄨㄢ"), ("sang", "ㄙㄤ"),
("seng", "ㄙㄥ"), ("shai", "ㄕㄞ"), ("shan", "ㄕㄢ"), ("shao", "ㄕㄠ"), ("shei", "ㄕㄟ"), ("shen", "ㄕㄣ"), ("shou", "ㄕㄡ"),
("shua", "ㄕㄨㄚ"), ("shui", "ㄕㄨㄟ"), ("shun", "ㄕㄨㄣ"), ("shuo", "ㄕㄨㄛ"), ("song", "ㄙㄨㄥ"), ("suan", "ㄙㄨㄢ"), ("tang", "ㄊㄤ"),
("teng", "ㄊㄥ"), ("tian", "ㄊㄧㄢ"), ("tiao", "ㄊㄧㄠ"), ("ting", "ㄊㄧㄥ"), ("tong", "ㄊㄨㄥ"), ("tuan", "ㄊㄨㄢ"), ("wang", "ㄨㄤ"),
("weng", "ㄨㄥ"), ("xian", "ㄒㄧㄢ"), ("xiao", "ㄒㄧㄠ"), ("xing", "ㄒㄧㄥ"), ("xuan", "ㄒㄩㄢ"), ("yang", "ㄧㄤ"), ("ying", "ㄧㄥ"),
("yong", "ㄩㄥ"), ("yuan", "ㄩㄢ"), ("zang", "ㄗㄤ"), ("zeng", "ㄗㄥ"), ("zhai", "ㄓㄞ"), ("zhan", "ㄓㄢ"), ("zhao", "ㄓㄠ"),
("zhei", "ㄓㄟ"), ("zhen", "ㄓㄣ"), ("zhou", "ㄓㄡ"), ("zhua", "ㄓㄨㄚ"), ("zhui", "ㄓㄨㄟ"), ("zhun", "ㄓㄨㄣ"), ("zhuo", "ㄓㄨㄛ"),
("zong", "ㄗㄨㄥ"), ("zuan", "ㄗㄨㄢ"), ("jun", "ㄐㄩㄣ"), ("ang", ""), ("bai", "ㄅㄞ"), ("ban", "ㄅㄢ"), ("bao", "ㄅㄠ"),
("bei", "ㄅㄟ"), ("ben", "ㄅㄣ"), ("bie", "ㄅㄧㄝ"), ("bin", "ㄅㄧㄣ"), ("cai", "ㄘㄞ"), ("can", "ㄘㄢ"), ("cao", "ㄘㄠ"),
("cei", "ㄘㄟ"), ("cen", "ㄘㄣ"), ("cha", "ㄔㄚ"), ("che", "ㄔㄜ"), ("chi", ""), ("chu", "ㄔㄨ"), ("cou", "ㄘㄡ"),
("cui", "ㄘㄨㄟ"), ("cun", "ㄘㄨㄣ"), ("cuo", "ㄘㄨㄛ"), ("dai", "ㄉㄞ"), ("dan", "ㄉㄢ"), ("dao", "ㄉㄠ"), ("dei", "ㄉㄟ"),
("den", "ㄉㄣ"), ("dia", "ㄉㄧㄚ"), ("die", "ㄉㄧㄝ"), ("diu", "ㄉㄧㄡ"), ("dou", "ㄉㄡ"), ("dui", "ㄉㄨㄟ"), ("dun", "ㄉㄨㄣ"),
("duo", "ㄉㄨㄛ"), ("eng", ""), ("fan", "ㄈㄢ"), ("fei", "ㄈㄟ"), ("fen", "ㄈㄣ"), ("fou", "ㄈㄡ"), ("gai", "ㄍㄞ"),
("gan", "ㄍㄢ"), ("gao", "ㄍㄠ"), ("gei", "ㄍㄟ"), ("gin", "ㄍㄧㄣ"), ("gen", "ㄍㄣ"), ("gou", "ㄍㄡ"), ("gua", "ㄍㄨㄚ"),
("gue", "ㄍㄨㄜ"), ("gui", "ㄍㄨㄟ"), ("gun", "ㄍㄨㄣ"), ("guo", "ㄍㄨㄛ"), ("hai", "ㄏㄞ"), ("han", "ㄏㄢ"), ("hao", "ㄏㄠ"),
("hei", "ㄏㄟ"), ("hen", "ㄏㄣ"), ("hou", "ㄏㄡ"), ("hua", "ㄏㄨㄚ"), ("hui", "ㄏㄨㄟ"), ("hun", "ㄏㄨㄣ"), ("huo", "ㄏㄨㄛ"),
("jia", "ㄐㄧㄚ"), ("jie", "ㄐㄧㄝ"), ("jin", "ㄐㄧㄣ"), ("jiu", "ㄐㄧㄡ"), ("jue", "ㄐㄩㄝ"), ("kai", "ㄎㄞ"), ("kan", "ㄎㄢ"),
("kao", "ㄎㄠ"), ("ken", "ㄎㄣ"), ("kiu", "ㄎㄧㄡ"), ("kou", "ㄎㄡ"), ("kua", "ㄎㄨㄚ"), ("kui", "ㄎㄨㄟ"), ("kun", "ㄎㄨㄣ"),
("kuo", "ㄎㄨㄛ"), ("lai", "ㄌㄞ"), ("lan", "ㄌㄢ"), ("lao", "ㄌㄠ"), ("lei", "ㄌㄟ"), ("lia", "ㄌㄧㄚ"), ("lie", "ㄌㄧㄝ"),
("lin", "ㄌㄧㄣ"), ("liu", "ㄌㄧㄡ"), ("lou", "ㄌㄡ"), ("lun", "ㄌㄨㄣ"), ("luo", "ㄌㄨㄛ"), ("lve", "ㄌㄩㄝ"), ("mai", "ㄇㄞ"),
("man", "ㄇㄢ"), ("mao", "ㄇㄠ"), ("mei", "ㄇㄟ"), ("men", "ㄇㄣ"), ("mie", "ㄇㄧㄝ"), ("min", "ㄇㄧㄣ"), ("miu", "ㄇㄧㄡ"),
("mou", "ㄇㄡ"), ("nai", "ㄋㄞ"), ("nan", "ㄋㄢ"), ("nao", "ㄋㄠ"), ("nei", "ㄋㄟ"), ("nen", "ㄋㄣ"), ("nie", "ㄋㄧㄝ"),
("nin", "ㄋㄧㄣ"), ("niu", "ㄋㄧㄡ"), ("nou", "ㄋㄡ"), ("nui", "ㄋㄨㄟ"), ("nun", "ㄋㄨㄣ"), ("nuo", "ㄋㄨㄛ"), ("nve", "ㄋㄩㄝ"),
("pai", "ㄆㄞ"), ("pan", "ㄆㄢ"), ("pao", "ㄆㄠ"), ("pei", "ㄆㄟ"), ("pen", "ㄆㄣ"), ("pia", "ㄆㄧㄚ"), ("pie", "ㄆㄧㄝ"),
("pin", "ㄆㄧㄣ"), ("pou", "ㄆㄡ"), ("qia", "ㄑㄧㄚ"), ("qie", "ㄑㄧㄝ"), ("qin", "ㄑㄧㄣ"), ("qiu", "ㄑㄧㄡ"), ("que", "ㄑㄩㄝ"),
("qun", "ㄑㄩㄣ"), ("ran", "ㄖㄢ"), ("rao", "ㄖㄠ"), ("ren", "ㄖㄣ"), ("rou", "ㄖㄡ"), ("rui", "ㄖㄨㄟ"), ("run", "ㄖㄨㄣ"),
("ruo", "ㄖㄨㄛ"), ("sai", "ㄙㄞ"), ("san", "ㄙㄢ"), ("sao", "ㄙㄠ"), ("sei", "ㄙㄟ"), ("sen", "ㄙㄣ"), ("sha", "ㄕㄚ"),
("she", "ㄕㄜ"), ("shi", ""), ("shu", "ㄕㄨ"), ("sou", "ㄙㄡ"), ("sui", "ㄙㄨㄟ"), ("sun", "ㄙㄨㄣ"), ("suo", "ㄙㄨㄛ"),
("tai", "ㄊㄞ"), ("tan", "ㄊㄢ"), ("tao", "ㄊㄠ"), ("tie", "ㄊㄧㄝ"), ("tou", "ㄊㄡ"), ("tui", "ㄊㄨㄟ"), ("tun", "ㄊㄨㄣ"),
("tuo", "ㄊㄨㄛ"), ("wai", "ㄨㄞ"), ("wan", "ㄨㄢ"), ("wei", "ㄨㄟ"), ("wen", "ㄨㄣ"), ("xia", "ㄒㄧㄚ"), ("xie", "ㄒㄧㄝ"),
("xin", "ㄒㄧㄣ"), ("xiu", "ㄒㄧㄡ"), ("xue", "ㄒㄩㄝ"), ("xun", "ㄒㄩㄣ"), ("yai", "ㄧㄞ"), ("yan", "ㄧㄢ"), ("yao", "ㄧㄠ"),
("yin", "ㄧㄣ"), ("you", "ㄧㄡ"), ("yue", "ㄩㄝ"), ("yun", "ㄩㄣ"), ("zai", "ㄗㄞ"), ("zan", "ㄗㄢ"), ("zao", "ㄗㄠ"),
("zei", "ㄗㄟ"), ("zen", "ㄗㄣ"), ("zha", "ㄓㄚ"), ("zhe", "ㄓㄜ"), ("zhi", ""), ("zhu", "ㄓㄨ"), ("zou", "ㄗㄡ"),
("zui", "ㄗㄨㄟ"), ("zun", "ㄗㄨㄣ"), ("zuo", "ㄗㄨㄛ"), ("ai", ""), ("an", ""), ("ao", ""), ("ba", "ㄅㄚ"), ("bi", "ㄅㄧ"),
("bo", "ㄅㄛ"), ("bu", "ㄅㄨ"), ("ca", "ㄘㄚ"), ("ce", "ㄘㄜ"), ("ci", ""), ("cu", "ㄘㄨ"), ("da", "ㄉㄚ"), ("de", "ㄉㄜ"),
("di", "ㄉㄧ"), ("du", "ㄉㄨ"), ("eh", ""), ("ei", ""), ("en", ""), ("er", ""), ("fa", "ㄈㄚ"), ("fo", "ㄈㄛ"),
("fu", "ㄈㄨ"), ("ga", "ㄍㄚ"), ("ge", "ㄍㄜ"), ("gi", "ㄍㄧ"), ("gu", "ㄍㄨ"), ("ha", "ㄏㄚ"), ("he", "ㄏㄜ"), ("hu", "ㄏㄨ"),
("ji", "ㄐㄧ"), ("ju", "ㄐㄩ"), ("ka", "ㄎㄚ"), ("ke", "ㄎㄜ"), ("ku", "ㄎㄨ"), ("la", "ㄌㄚ"), ("le", "ㄌㄜ"), ("li", "ㄌㄧ"),
("lo", "ㄌㄛ"), ("lu", "ㄌㄨ"), ("lv", "ㄌㄩ"), ("ma", "ㄇㄚ"), ("me", "ㄇㄜ"), ("mi", "ㄇㄧ"), ("mo", "ㄇㄛ"), ("mu", "ㄇㄨ"),
("na", "ㄋㄚ"), ("ne", "ㄋㄜ"), ("ni", "ㄋㄧ"), ("nu", "ㄋㄨ"), ("nv", "ㄋㄩ"), ("ou", ""), ("pa", "ㄆㄚ"), ("pi", "ㄆㄧ"),
("po", "ㄆㄛ"), ("pu", "ㄆㄨ"), ("qi", "ㄑㄧ"), ("qu", "ㄑㄩ"), ("re", "ㄖㄜ"), ("ri", ""), ("ru", "ㄖㄨ"), ("sa", "ㄙㄚ"),
("se", "ㄙㄜ"), ("si", ""), ("su", "ㄙㄨ"), ("ta", "ㄊㄚ"), ("te", "ㄊㄜ"), ("ti", "ㄊㄧ"), ("tu", "ㄊㄨ"), ("wa", "ㄨㄚ"),
("wo", "ㄨㄛ"), ("wu", ""), ("xi", "ㄒㄧ"), ("xu", "ㄒㄩ"), ("ya", "ㄧㄚ"), ("ye", "ㄧㄝ"), ("yi", ""), ("yo", "ㄧㄛ"),
("yu", ""), ("za", "ㄗㄚ"), ("ze", "ㄗㄜ"), ("zi", ""), ("zu", "ㄗㄨ"), ("a", ""), ("e", ""), ("o", ""), ("q", ""),
("1", " "), ("2", "ˊ"), ("3", "ˇ"), ("4", "ˋ"), ("5", "˙"),
]

View File

@ -13,7 +13,7 @@ let package = Package(
),
],
dependencies: [
.package(path: "../vChewing_CocoaExtension"),
.package(path: "../vChewing_OSFrameworkImpl"),
.package(path: "../vChewing_IMKUtils"),
.package(path: "../vChewing_SwiftExtension"),
],
@ -21,7 +21,7 @@ let package = Package(
.target(
name: "Shared",
dependencies: [
.product(name: "CocoaExtension", package: "vChewing_CocoaExtension"),
.product(name: "OSFrameworkImpl", package: "vChewing_OSFrameworkImpl"),
.product(name: "IMKUtils", package: "vChewing_IMKUtils"),
.product(name: "SwiftExtension", package: "vChewing_SwiftExtension"),
]

View File

@ -8,7 +8,7 @@
// marks, or product names of Contributor, except as required to fulfill notice
// requirements defined in MIT License.
import AppKit
import Foundation
@objcMembers public class Broadcaster: NSObject {
public static var shared = Broadcaster()

View File

@ -6,8 +6,6 @@
// marks, or product names of Contributor, except as required to fulfill notice
// requirements defined in MIT License.
import Foundation
open class CandidateNode {
public var name: String
public var members: [CandidateNode]

View File

@ -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 CandidateNode {
convenience init(
name: String, services: [CandidateTextService], previous: CandidateNode? = nil

View File

@ -7,7 +7,7 @@
// requirements defined in MIT License.
import AppKit
import CocoaExtension
import OSFrameworkImpl
// MARK: - InputSignalProtocol

View File

@ -9,9 +9,9 @@
// UserDefRenderable AppKit SwiftUI
import AppKit
import CocoaExtension
import Foundation
import IMKUtils
import OSFrameworkImpl
public class UserDefRenderableCocoa: NSObject, Identifiable {
public let def: UserDef

View File

@ -6,8 +6,6 @@
// marks, or product names of Contributor, except as required to fulfill notice
// requirements defined in MIT License.
import Foundation
// MARK: - Bool Operators
public func |= (lhs: inout Bool, rhs: Bool) {
@ -22,25 +20,11 @@ public func ^= (lhs: inout Bool, rhs: Bool) {
lhs = lhs != rhs
}
// MARK: - String.localized extension
public extension StringLiteralType {
var localized: String { NSLocalizedString(description, comment: "") }
}
// MARK: - Root Extensions
// MARK: - Root Extensions (deduplicated)
// Extend the RangeReplaceableCollection to allow it clean duplicated characters.
// Ref: https://stackoverflow.com/questions/25738817/
public extension RangeReplaceableCollection where Element: Hashable {
/// 使 NSOrderedSet class
var classDeduplicated: Self {
NSOrderedSet(array: Array(self)).compactMap { $0 as? Element.Type } as? Self ?? self
// Bug KeyValuePaired
// var set = Set<Element>()
// return filter { set.insert($0).inserted }
}
///
/// - Remark: class class Identifiable
var deduplicated: Self {
@ -49,22 +33,6 @@ public extension RangeReplaceableCollection where Element: Hashable {
}
}
// MARK: - String Tildes Expansion Extension
public extension String {
var expandingTildeInPath: String {
(self as NSString).expandingTildeInPath
}
}
// MARK: - String Localized Error Extension
extension String: LocalizedError {
public var errorDescription: String? {
self
}
}
// MARK: - Ensuring trailing slash of a string
public extension String {
@ -77,21 +45,6 @@ public extension String {
// MARK: - CharCode printability check
// Ref: https://forums.swift.org/t/57085/5
public extension UniChar {
var isPrintable: Bool {
guard Unicode.Scalar(UInt32(self)) != nil else {
struct NotAWholeScalar: Error {}
return false
}
return true
}
var isPrintableASCII: Bool {
(32 ... 126).contains(self)
}
}
public extension Unicode.Scalar {
var isPrintableASCII: Bool {
(32 ... 126).contains(value)
@ -133,70 +86,23 @@ public extension Bool {
}
}
// MARK: - User Defaults Storage
public extension UserDefaults {
//
static var pendingUnitTests = false
static var unitTests = UserDefaults(suiteName: "UnitTests")
static var current: UserDefaults {
pendingUnitTests ? .unitTests ?? .standard : .standard
}
}
// MARK: - Property Wrapper
// Ref: https://www.avanderlee.com/swift/property-wrappers/
@propertyWrapper
public struct AppProperty<Value> {
public let key: String
public let defaultValue: Value
public var container: UserDefaults { .current }
public init(key: String, defaultValue: Value) {
self.key = key
self.defaultValue = defaultValue
if container.object(forKey: key) == nil {
container.set(defaultValue, forKey: key)
}
}
public var wrappedValue: Value {
get {
container.object(forKey: key) as? Value ?? defaultValue
}
set {
container.set(newValue, forKey: key)
}
}
}
// MARK: -
// Ref: https://stackoverflow.com/a/32581409/4162914
public extension Double {
func rounded(toPlaces places: Int) -> Double {
let divisor = pow(10.0, Double(places))
let divisor = 10.0.mathPowered(by: places)
return (self * divisor).rounded() / divisor
}
}
// MARK: - String RegReplace Extension
// Ref: https://stackoverflow.com/a/40993403/4162914 && https://stackoverflow.com/a/71291137/4162914
public extension String {
mutating func regReplace(pattern: String, replaceWith: String = "") {
do {
let regex = try NSRegularExpression(
pattern: pattern, options: [.caseInsensitive, .anchorsMatchLines]
)
let range = NSRange(startIndex..., in: self)
self = regex.stringByReplacingMatches(
in: self, options: [], range: range, withTemplate: replaceWith
)
} catch { return }
public extension Double {
func mathPowered(by operand: Int) -> Double {
var target = self
for _ in 0 ..< operand {
target = target * target
}
return target
}
}
@ -236,26 +142,6 @@ public extension String {
var withEllipsis: String { self + "" }
}
// MARK: - Localized String Extension for Integers and Floats
public extension BinaryFloatingPoint {
func i18n(loc: String) -> String {
let formatter = NumberFormatter()
formatter.locale = Locale(identifier: loc)
formatter.numberStyle = .spellOut
return formatter.string(from: NSDecimalNumber(string: "\(self)")) ?? ""
}
}
public extension BinaryInteger {
func i18n(loc: String) -> String {
let formatter = NumberFormatter()
formatter.locale = Locale(identifier: loc)
formatter.numberStyle = .spellOut
return formatter.string(from: NSDecimalNumber(string: "\(self)")) ?? ""
}
}
// MARK: - Index Revolver (only for Array)
// Further discussion: https://forums.swift.org/t/62847
@ -283,31 +169,6 @@ public extension Int {
}
}
// MARK: - Parse String As Hex Literal
// Original author: Shiki Suen
// Refactored by: Isaac Xen
public extension String {
func parsedAsHexLiteral(encoding: CFStringEncodings? = nil) -> String? {
guard !isEmpty else { return nil }
var charBytes = [Int8]()
var buffer: Int?
compactMap(\.hexDigitValue).forEach { neta in
if let validBuffer = buffer {
charBytes.append(.init(bitPattern: UInt8(validBuffer << 4 + neta)))
buffer = nil
} else {
buffer = neta
}
}
let encodingUBE = CFStringBuiltInEncodings.UTF16BE.rawValue
let encodingRAW = encoding.map { UInt32($0.rawValue) } ?? encodingUBE
let result = CFStringCreateWithCString(nil, &charBytes, encodingRAW) as String?
return result?.isEmpty ?? true ? nil : result
}
}
// MARK: - Overlap Checker (for two sets)
public extension Set where Element: Hashable {
@ -340,32 +201,6 @@ public extension Array where Element: Hashable {
}
}
// MARK: - Version Comparer.
public extension String {
/// ref: https://sarunw.com/posts/how-to-compare-two-app-version-strings-in-swift/
func versionCompare(_ otherVersion: String) -> ComparisonResult {
let versionDelimiter = "."
var versionComponents = components(separatedBy: versionDelimiter) // <1>
var otherVersionComponents = otherVersion.components(separatedBy: versionDelimiter)
let zeroDiff = versionComponents.count - otherVersionComponents.count // <2>
// <3> Compare normally if the formats are the same.
guard zeroDiff != 0 else { return compare(otherVersion, options: .numeric) }
let zeros = Array(repeating: "0", count: abs(zeroDiff)) // <4>
if zeroDiff > 0 {
otherVersionComponents.append(contentsOf: zeros) // <5>
} else {
versionComponents.append(contentsOf: zeros)
}
return versionComponents.joined(separator: versionDelimiter)
.compare(otherVersionComponents.joined(separator: versionDelimiter), options: .numeric) // <6>
}
}
// MARK: - Array Builder.
@resultBuilder
@ -419,3 +254,33 @@ public extension Comparable {
return givenMap[returnableID]
}
}
// MARK: - String.applyingTransform
public extension String {
/// This only works with ASCII chars for now.
func applyingTransformFW2HW(reverse: Bool) -> String {
var arr: [Character] = map { $0 }
for i in 0 ..< arr.count {
let oldChar = arr[i]
guard oldChar.unicodeScalars.count == 1 else { continue }
guard let oldCodePoint = oldChar.unicodeScalars.first?.value else { continue }
if reverse {
guard oldChar.isASCII else { continue }
} else {
guard oldCodePoint > 0xFEE0 || oldCodePoint == 0x3000 else { continue }
}
var newCodePoint: Int32 = reverse ? (Int32(oldCodePoint) + 0xFEE0) : (Int32(oldCodePoint) - 0xFEE0)
checkSpace: switch (oldCodePoint, reverse) {
case (0x3000, false): newCodePoint = 0x20
case (0x20, true): newCodePoint = 0x3000
default: break checkSpace
}
guard newCodePoint > 0 else { continue }
guard let newScalar = Unicode.Scalar(UInt16(newCodePoint)) else { continue }
let newChar = Character(newScalar)
arr[i] = newChar
}
return String(arr)
}
}

View File

@ -0,0 +1,190 @@
// (c) 2021 and onwards The vChewing Project (MIT-NTL License).
// ====================
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
// ... with NTL restriction stating that:
// No trademark license is granted to use the trade names, trademarks, service
// marks, or product names of Contributor, except as required to fulfill notice
// requirements defined in MIT License.
import Foundation
// MARK: - String.localized extension
public extension StringLiteralType {
var localized: String { NSLocalizedString(description, comment: "") }
}
// MARK: - Root Extensions (classDeduplicated)
// Extend the RangeReplaceableCollection to allow it clean duplicated characters.
// Ref: https://stackoverflow.com/questions/25738817/
public extension RangeReplaceableCollection where Element: Hashable {
/// 使 NSOrderedSet class
var classDeduplicated: Self {
NSOrderedSet(array: Array(self)).compactMap { $0 as? Element.Type } as? Self ?? self
// Bug KeyValuePaired
// var set = Set<Element>()
// return filter { set.insert($0).inserted }
}
}
// MARK: - String Tildes Expansion Extension
public extension String {
var expandingTildeInPath: String {
(self as NSString).expandingTildeInPath
}
}
// MARK: - String Localized Error Extension
extension String: LocalizedError {
public var errorDescription: String? {
self
}
}
// MARK: - CharCode printability check for UniChar (CoreFoundation)
// Ref: https://forums.swift.org/t/57085/5
public extension UniChar {
var isPrintable: Bool {
guard Unicode.Scalar(UInt32(self)) != nil else {
struct NotAWholeScalar: Error {}
return false
}
return true
}
var isPrintableASCII: Bool {
(32 ... 126).contains(self)
}
}
// MARK: - User Defaults Storage
public extension UserDefaults {
//
static var pendingUnitTests = false
static var unitTests = UserDefaults(suiteName: "UnitTests")
static var current: UserDefaults {
pendingUnitTests ? .unitTests ?? .standard : .standard
}
}
// MARK: - Property Wrapper
// Ref: https://www.avanderlee.com/swift/property-wrappers/
@propertyWrapper
public struct AppProperty<Value> {
public let key: String
public let defaultValue: Value
public var container: UserDefaults { .current }
public init(key: String, defaultValue: Value) {
self.key = key
self.defaultValue = defaultValue
if container.object(forKey: key) == nil {
container.set(defaultValue, forKey: key)
}
}
public var wrappedValue: Value {
get {
container.object(forKey: key) as? Value ?? defaultValue
}
set {
container.set(newValue, forKey: key)
}
}
}
// MARK: - String RegReplace Extension
// Ref: https://stackoverflow.com/a/40993403/4162914 && https://stackoverflow.com/a/71291137/4162914
public extension String {
mutating func regReplace(pattern: String, replaceWith: String = "") {
do {
let regex = try NSRegularExpression(
pattern: pattern, options: [.caseInsensitive, .anchorsMatchLines]
)
let range = NSRange(startIndex..., in: self)
self = regex.stringByReplacingMatches(
in: self, options: [], range: range, withTemplate: replaceWith
)
} catch { return }
}
}
// MARK: - Localized String Extension for Integers and Floats
public extension BinaryFloatingPoint {
func i18n(loc: String) -> String {
let formatter = NumberFormatter()
formatter.locale = Locale(identifier: loc)
formatter.numberStyle = .spellOut
return formatter.string(from: NSDecimalNumber(string: "\(self)")) ?? ""
}
}
public extension BinaryInteger {
func i18n(loc: String) -> String {
let formatter = NumberFormatter()
formatter.locale = Locale(identifier: loc)
formatter.numberStyle = .spellOut
return formatter.string(from: NSDecimalNumber(string: "\(self)")) ?? ""
}
}
// MARK: - Parse String As Hex Literal
// Original author: Shiki Suen
// Refactored by: Isaac Xen
public extension String {
func parsedAsHexLiteral(encoding: CFStringEncodings? = nil) -> String? {
guard !isEmpty else { return nil }
var charBytes = [Int8]()
var buffer: Int?
compactMap(\.hexDigitValue).forEach { neta in
if let validBuffer = buffer {
charBytes.append(.init(bitPattern: UInt8(validBuffer << 4 + neta)))
buffer = nil
} else {
buffer = neta
}
}
let encodingUBE = CFStringBuiltInEncodings.UTF16BE.rawValue
let encodingRAW = encoding.map { UInt32($0.rawValue) } ?? encodingUBE
let result = CFStringCreateWithCString(nil, &charBytes, encodingRAW) as String?
return result?.isEmpty ?? true ? nil : result
}
}
// MARK: - Version Comparer.
public extension String {
/// ref: https://sarunw.com/posts/how-to-compare-two-app-version-strings-in-swift/
func versionCompare(_ otherVersion: String) -> ComparisonResult {
let versionDelimiter = "."
var versionComponents = components(separatedBy: versionDelimiter) // <1>
var otherVersionComponents = otherVersion.components(separatedBy: versionDelimiter)
let zeroDiff = versionComponents.count - otherVersionComponents.count // <2>
// <3> Compare normally if the formats are the same.
guard zeroDiff != 0 else { return compare(otherVersion, options: .numeric) }
let zeros = Array(repeating: "0", count: abs(zeroDiff)) // <4>
if zeroDiff > 0 {
otherVersionComponents.append(contentsOf: zeros) // <5>
} else {
versionComponents.append(contentsOf: zeros)
}
return versionComponents.joined(separator: versionDelimiter)
.compare(otherVersionComponents.joined(separator: versionDelimiter), options: .numeric) // <6>
}
}

View File

@ -14,7 +14,7 @@ let package = Package(
],
dependencies: [
.package(path: "../Fuziki_NSAttributedTextView"),
.package(path: "../vChewing_CocoaExtension"),
.package(path: "../vChewing_OSFrameworkImpl"),
.package(path: "../vChewing_Shared"),
],
targets: [
@ -22,7 +22,7 @@ let package = Package(
name: "TooltipUI",
dependencies: [
.product(name: "NSAttributedTextView", package: "Fuziki_NSAttributedTextView"),
.product(name: "CocoaExtension", package: "vChewing_CocoaExtension"),
.product(name: "OSFrameworkImpl", package: "vChewing_OSFrameworkImpl"),
.product(name: "Shared", package: "vChewing_Shared"),
]
),

View File

@ -7,8 +7,8 @@
// requirements defined in MIT License.
import AppKit
import CocoaExtension
import NSAttributedTextView
import OSFrameworkImpl
import Shared
public class TooltipUI_LateCocoa: NSWindowController, TooltipUIProtocol {

View File

@ -13,13 +13,13 @@ let package = Package(
),
],
dependencies: [
.package(path: "../vChewing_CocoaExtension"),
.package(path: "../vChewing_OSFrameworkImpl"),
],
targets: [
.target(
name: "Uninstaller",
dependencies: [
.product(name: "CocoaExtension", package: "vChewing_CocoaExtension"),
.product(name: "OSFrameworkImpl", package: "vChewing_OSFrameworkImpl"),
]
),
]

View File

@ -7,7 +7,7 @@
// requirements defined in MIT License.
import AppKit
import CocoaExtension
import OSFrameworkImpl
public enum Uninstaller {
// MARK: - Uninstall the input method.

View File

@ -26,9 +26,8 @@
5B70F4EC2A0BE900005EA8C4 /* MenuIcon-TCVIM@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 5B70F4E82A0BE900005EA8C4 /* MenuIcon-TCVIM@2x.png */; };
5B765F09293A253C00122315 /* PhraseEditorUI in Frameworks */ = {isa = PBXBuildFile; productRef = 5B765F08293A253C00122315 /* PhraseEditorUI */; };
5B7DA80328BF6BC600D7B2AD /* fixinstall.sh in Resources */ = {isa = PBXBuildFile; fileRef = 5B7DA80228BF6BBA00D7B2AD /* fixinstall.sh */; };
5B963C9D28D5BFB800DCEE88 /* CocoaExtension in Frameworks */ = {isa = PBXBuildFile; productRef = 5B963C9C28D5BFB800DCEE88 /* CocoaExtension */; };
5B963C9D28D5BFB800DCEE88 /* OSFrameworkImpl in Frameworks */ = {isa = PBXBuildFile; productRef = 5B963C9C28D5BFB800DCEE88 /* OSFrameworkImpl */; };
5B963CA328D5C23600DCEE88 /* SwiftExtension in Frameworks */ = {isa = PBXBuildFile; productRef = 5B963CA228D5C23600DCEE88 /* SwiftExtension */; };
5B98114828D6198700CBC605 /* PinyinPhonaConverter in Frameworks */ = {isa = PBXBuildFile; productRef = 5B98114728D6198700CBC605 /* PinyinPhonaConverter */; };
5BA8C30328DF0360004C5CC4 /* CandidateWindow in Frameworks */ = {isa = PBXBuildFile; productRef = 5BA8C30228DF0360004C5CC4 /* CandidateWindow */; };
5BAD0CD527D701F6003D127F /* vChewingKeyLayout.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 5B30F11227BA568800484E24 /* vChewingKeyLayout.bundle */; };
5BBBB75F27AED54C0023B93A /* Beep.m4a in Resources */ = {isa = PBXBuildFile; fileRef = 5BBBB75D27AED54C0023B93A /* Beep.m4a */; };
@ -144,10 +143,8 @@
5B70F4E82A0BE900005EA8C4 /* MenuIcon-TCVIM@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "MenuIcon-TCVIM@2x.png"; sourceTree = "<group>"; };
5B765F07293A250000122315 /* vChewing_PhraseEditorUI */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_PhraseEditorUI; path = Packages/vChewing_PhraseEditorUI; sourceTree = "<group>"; };
5B7DA80228BF6BBA00D7B2AD /* fixinstall.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; lineEnding = 0; path = fixinstall.sh; sourceTree = "<group>"; };
5B963C9B28D5BE4100DCEE88 /* vChewing_CocoaExtension */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_CocoaExtension; path = Packages/vChewing_CocoaExtension; sourceTree = "<group>"; };
5B963C9E28D5C14600DCEE88 /* vChewing_Shared */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_Shared; path = Packages/vChewing_Shared; sourceTree = "<group>"; };
5B963CA128D5C22D00DCEE88 /* vChewing_SwiftExtension */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_SwiftExtension; path = Packages/vChewing_SwiftExtension; sourceTree = "<group>"; };
5B98114628D6198000CBC605 /* vChewing_PinyinPhonaConverter */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_PinyinPhonaConverter; path = Packages/vChewing_PinyinPhonaConverter; sourceTree = "<group>"; };
5BA8C30128DEFE4F004C5CC4 /* vChewing_CandidateWindow */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_CandidateWindow; path = Packages/vChewing_CandidateWindow; sourceTree = "<group>"; };
5BAD29D12B7E1A1B0013D695 /* vChewing_KimoDataReader */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_KimoDataReader; path = Packages/vChewing_KimoDataReader; sourceTree = "<group>"; };
5BAD67E12ADAB62D005A4842 /* HangarRash_SwiftyCapsLockToggler */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = HangarRash_SwiftyCapsLockToggler; path = Packages/HangarRash_SwiftyCapsLockToggler; sourceTree = "<group>"; };
@ -163,6 +160,7 @@
5BC5E01C28DDE4270094E427 /* vChewing_NotifierUI */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_NotifierUI; path = Packages/vChewing_NotifierUI; sourceTree = "<group>"; };
5BC5E01F28DDEFD80094E427 /* vChewing_TooltipUI */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_TooltipUI; path = Packages/vChewing_TooltipUI; sourceTree = "<group>"; };
5BC5E02228DE07250094E427 /* vChewing_PopupCompositionBuffer */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_PopupCompositionBuffer; path = Packages/vChewing_PopupCompositionBuffer; sourceTree = "<group>"; };
5BDA45492B97588B00DFA179 /* vChewing_OSFrameworkImpl */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_OSFrameworkImpl; path = Packages/vChewing_OSFrameworkImpl; sourceTree = "<group>"; };
5BDB7A3028D47587001AC277 /* Jad_BookmarkManager */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = Jad_BookmarkManager; path = Packages/Jad_BookmarkManager; sourceTree = "<group>"; };
5BDB7A3128D47587001AC277 /* DanielGalasko_FolderMonitor */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = DanielGalasko_FolderMonitor; path = Packages/DanielGalasko_FolderMonitor; sourceTree = "<group>"; };
5BDB7A3228D47587001AC277 /* vChewing_Hotenka */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_Hotenka; path = Packages/vChewing_Hotenka; sourceTree = "<group>"; };
@ -229,8 +227,7 @@
5BDB7A4728D4824A001AC277 /* Tekkon in Frameworks */,
5BDB7A3D28D4824A001AC277 /* Hotenka in Frameworks */,
5BDB7A3F28D4824A001AC277 /* LineReader in Frameworks */,
5B963C9D28D5BFB800DCEE88 /* CocoaExtension in Frameworks */,
5B98114828D6198700CBC605 /* PinyinPhonaConverter in Frameworks */,
5B963C9D28D5BFB800DCEE88 /* OSFrameworkImpl in Frameworks */,
5BFC63CF28D4ACA3004A77B7 /* LangModelAssembly in Frameworks */,
5BC5E02128DDEFE00094E427 /* TooltipUI in Frameworks */,
5B765F09293A253C00122315 /* PhraseEditorUI in Frameworks */,
@ -389,7 +386,6 @@
5BDB7A3528D47587001AC277 /* RMJay_LineReader */,
5B6CA6272B8A1C9200A85050 /* vChewing_BrailleSputnik */,
5BA8C30128DEFE4F004C5CC4 /* vChewing_CandidateWindow */,
5B963C9B28D5BE4100DCEE88 /* vChewing_CocoaExtension */,
5BDB7A3228D47587001AC277 /* vChewing_Hotenka */,
5BFC63C728D49511004A77B7 /* vChewing_IMKUtils */,
5BAD29D12B7E1A1B0013D695 /* vChewing_KimoDataReader */,
@ -397,8 +393,8 @@
5B65FB322A9518C9007EEFB0 /* vChewing_MainAssembly */,
5BDB7A3328D47587001AC277 /* vChewing_Megrez */,
5BC5E01C28DDE4270094E427 /* vChewing_NotifierUI */,
5BDA45492B97588B00DFA179 /* vChewing_OSFrameworkImpl */,
5B765F07293A250000122315 /* vChewing_PhraseEditorUI */,
5B98114628D6198000CBC605 /* vChewing_PinyinPhonaConverter */,
5BC5E02228DE07250094E427 /* vChewing_PopupCompositionBuffer */,
5B963C9E28D5C14600DCEE88 /* vChewing_Shared */,
5B963CA128D5C22D00DCEE88 /* vChewing_SwiftExtension */,
@ -558,9 +554,8 @@
5BFC63C828D49754004A77B7 /* IMKUtils */,
5BFC63CB28D49BBC004A77B7 /* UpdateSputnik */,
5BFC63CE28D4ACA3004A77B7 /* LangModelAssembly */,
5B963C9C28D5BFB800DCEE88 /* CocoaExtension */,
5B963C9C28D5BFB800DCEE88 /* OSFrameworkImpl */,
5B963CA228D5C23600DCEE88 /* SwiftExtension */,
5B98114728D6198700CBC605 /* PinyinPhonaConverter */,
5B40113828D7050D00A9D4CB /* Shared */,
5B40113B28D71C0100A9D4CB /* Uninstaller */,
5BC5E01D28DDE4770094E427 /* NotifierUI */,
@ -1377,18 +1372,14 @@
isa = XCSwiftPackageProductDependency;
productName = PhraseEditorUI;
};
5B963C9C28D5BFB800DCEE88 /* CocoaExtension */ = {
5B963C9C28D5BFB800DCEE88 /* OSFrameworkImpl */ = {
isa = XCSwiftPackageProductDependency;
productName = CocoaExtension;
productName = OSFrameworkImpl;
};
5B963CA228D5C23600DCEE88 /* SwiftExtension */ = {
isa = XCSwiftPackageProductDependency;
productName = SwiftExtension;
};
5B98114728D6198700CBC605 /* PinyinPhonaConverter */ = {
isa = XCSwiftPackageProductDependency;
productName = PinyinPhonaConverter;
};
5BA8C30228DF0360004C5CC4 /* CandidateWindow */ = {
isa = XCSwiftPackageProductDependency;
productName = CandidateWindow;