SPM // Consolidate dependencies.
This commit is contained in:
parent
93256f0095
commit
005116c429
|
@ -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(
|
||||
|
|
|
@ -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, *)
|
||||
|
|
|
@ -7,9 +7,9 @@
|
|||
// requirements defined in MIT License.
|
||||
|
||||
import AppKit
|
||||
import CocoaExtension
|
||||
import Foundation
|
||||
@testable import NSAttributedTextView
|
||||
import OSFrameworkImpl
|
||||
import Shared
|
||||
import XCTest
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
// requirements defined in MIT License.
|
||||
|
||||
import AppKit
|
||||
import CocoaExtension
|
||||
import OSFrameworkImpl
|
||||
import Shared
|
||||
|
||||
private extension NSUserInterfaceLayoutOrientation {
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
// requirements defined in MIT License.
|
||||
|
||||
import AppKit
|
||||
import CocoaExtension
|
||||
import OSFrameworkImpl
|
||||
import Shared
|
||||
|
||||
/// 田所選字窗的 AppKit 简单版本,繪製效率不受 SwiftUI 的限制。
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,8 +8,6 @@
|
|||
|
||||
import Foundation
|
||||
import Megrez
|
||||
import Shared
|
||||
import SQLite3
|
||||
|
||||
public extension LMAssembly {
|
||||
/// 語言模組副本化模組(LMInstantiator,下稱「LMI」)自身為符合天權星組字引擎內
|
||||
|
@ -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)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import Foundation
|
||||
import Megrez
|
||||
import Shared
|
||||
import SwiftExtension
|
||||
|
||||
public extension LMAssembly.LMInstantiator {
|
||||
/// 磁帶模式專用:當前磁帶所規定的花牌鍵。
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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":"ㄓㄨㄤ"}
|
||||
}
|
||||
"""#
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 取得字串形式的資料、生成單元圖陣列。
|
||||
|
|
|
@ -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 = [:]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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: "")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -9,9 +9,9 @@
|
|||
/// 該檔案乃輸入調度模組當中「用來規定在選字窗出現時的按鍵行為」的部分。
|
||||
|
||||
import CandidateWindow
|
||||
import CocoaExtension
|
||||
import InputMethodKit
|
||||
import Megrez
|
||||
import OSFrameworkImpl
|
||||
import Shared
|
||||
|
||||
// MARK: - § 對選字狀態進行調度 (Handle Candidate State).
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import AppKit
|
||||
import Carbon
|
||||
import CocoaExtension
|
||||
import OSFrameworkImpl
|
||||
import Shared
|
||||
|
||||
public class SecurityAgentHelper {
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
// requirements defined in MIT License.
|
||||
|
||||
import AppKit
|
||||
import CocoaExtension
|
||||
import Foundation
|
||||
import OSFrameworkImpl
|
||||
import Shared
|
||||
|
||||
public class VwrServiceMenuEditor: NSViewController {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
// requirements defined in MIT License.
|
||||
|
||||
import AppKit
|
||||
import CocoaExtension
|
||||
import NotifierUI
|
||||
import OSFrameworkImpl
|
||||
import Shared
|
||||
import SwiftExtension
|
||||
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
|
||||
import AppKit
|
||||
import BookmarkManager
|
||||
import CocoaExtension
|
||||
import Foundation
|
||||
import OSFrameworkImpl
|
||||
import Shared
|
||||
|
||||
public extension SettingsPanesCocoa {
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
// requirements defined in MIT License.
|
||||
|
||||
import BookmarkManager
|
||||
import OSFrameworkImpl
|
||||
import Shared
|
||||
import SwiftExtension
|
||||
import SwiftUI
|
||||
|
||||
@available(macOS 13, *)
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
// requirements defined in MIT License.
|
||||
|
||||
import BookmarkManager
|
||||
import CocoaExtension
|
||||
import OSFrameworkImpl
|
||||
import Shared
|
||||
import SwiftExtension
|
||||
import SwiftUI
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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"),
|
||||
]
|
||||
),
|
||||
]
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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"),
|
||||
]
|
|
@ -1,4 +1,4 @@
|
|||
# CocoaExtension
|
||||
# OSFrameworkImpl
|
||||
|
||||
威注音輸入法針對 Cocoa 的一些功能擴充,使程式維護體驗更佳。
|
||||
|
|
@ -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 {
|
|
@ -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.
|
|
@ -10,6 +10,7 @@ import AppKit
|
|||
import Combine
|
||||
import Foundation
|
||||
import LangModelAssembly
|
||||
import OSFrameworkImpl
|
||||
import Shared
|
||||
import SwiftExtension
|
||||
import SwiftUI
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
.DS_Store
|
||||
/.build
|
||||
/Packages
|
||||
/*.xcodeproj
|
||||
xcuserdata/
|
||||
DerivedData/
|
||||
.swiftpm/config/registries.json
|
||||
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
|
||||
.netrc
|
|
@ -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: []
|
||||
),
|
||||
]
|
||||
)
|
|
@ -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.
|
||||
```
|
|
@ -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", "˙"),
|
||||
]
|
|
@ -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"),
|
||||
]
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
// requirements defined in MIT License.
|
||||
|
||||
import AppKit
|
||||
import CocoaExtension
|
||||
import OSFrameworkImpl
|
||||
|
||||
// MARK: - InputSignalProtocol
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
}
|
||||
}
|
|
@ -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"),
|
||||
]
|
||||
),
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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"),
|
||||
]
|
||||
),
|
||||
]
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
// requirements defined in MIT License.
|
||||
|
||||
import AppKit
|
||||
import CocoaExtension
|
||||
import OSFrameworkImpl
|
||||
|
||||
public enum Uninstaller {
|
||||
// MARK: - Uninstall the input method.
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue