1.7.2 // UserOverrideModel, etc. Merge Gitee PR!49 from upd/1.7.2

This commit is contained in:
ShikiSuen 2022-06-23 13:34:59 +00:00 committed by Gitee
commit ee7cdc5a2d
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
50 changed files with 1051 additions and 575 deletions

@ -1 +1 @@
Subproject commit 1b67116c77dd654f156c43754694ac3a17a19a3a
Subproject commit 97d518cac19c96c5cd397bbdbcf8f95d2e967e73

View File

@ -134,17 +134,15 @@ struct InputSignal: CustomStringConvertible {
private var extraChooseCandidateKeyReverse: KeyCode = .kNone
private var absorbedArrowKey: KeyCode = .kNone
private var verticalTypingOnlyChooseCandidateKey: KeyCode = .kNone
private(set) var emacsKey: vChewingEmacsKey
private(set) var emacsKey: EmacsKey
public init(
inputText: String?, keyCode: UInt16, charCode: UInt16, flags: NSEvent.ModifierFlags,
isVerticalTyping: Bool, inputTextIgnoringModifiers: String? = nil
) {
let inputText = AppleKeyboardConverter.cnvStringApple2ABC(inputText ?? "")
let inputTextIgnoringModifiers = AppleKeyboardConverter.cnvStringApple2ABC(
inputTextIgnoringModifiers ?? inputText)
self.inputText = inputText
self.inputTextIgnoringModifiers = inputTextIgnoringModifiers
self.inputText = AppleKeyboardConverter.cnvStringApple2ABC(inputText ?? "")
self.inputTextIgnoringModifiers = AppleKeyboardConverter.cnvStringApple2ABC(
inputTextIgnoringModifiers ?? inputText ?? "")
self.flags = flags
isFlagChanged = false
isTypingVertical = isVerticalTyping
@ -163,7 +161,7 @@ struct InputSignal: CustomStringConvertible {
event.charactersIgnoringModifiers ?? "")
keyCode = event.keyCode
flags = event.modifierFlags
isFlagChanged = (event.type == .flagsChanged) ? true : false
isFlagChanged = (event.type == .flagsChanged)
isTypingVertical = isVerticalTyping
let charCode: UInt16 = {
// count > 0!isEmpty滿
@ -230,14 +228,14 @@ struct InputSignal: CustomStringConvertible {
flags.contains([.control]) && inputText?.first?.isLetter ?? false
}
var isOptionHotKey: Bool {
flags.contains([.option]) && inputText?.first?.isLetter ?? false
}
var isOptionHold: Bool {
flags.contains([.option])
}
var isOptionHotKey: Bool {
flags.contains([.option]) && inputText?.first?.isLetter ?? false
}
var isCapsLockOn: Bool {
flags.contains([.capsLock])
}
@ -334,7 +332,7 @@ struct InputSignal: CustomStringConvertible {
KeyCode(rawValue: keyCode) == extraChooseCandidateKeyReverse
}
var isverticalTypingOnlyChooseCandidateKey: Bool {
var isVerticalTypingOnlyChooseCandidateKey: Bool {
KeyCode(rawValue: keyCode) == verticalTypingOnlyChooseCandidateKey
}
@ -350,7 +348,7 @@ struct InputSignal: CustomStringConvertible {
}
}
enum vChewingEmacsKey: UInt16 {
enum EmacsKey: UInt16 {
case none = 0
case forward = 6 // F
case backward = 2 // B
@ -361,10 +359,10 @@ enum vChewingEmacsKey: UInt16 {
}
enum EmacsKeyHelper {
static func detect(charCode: UniChar, flags: NSEvent.ModifierFlags) -> vChewingEmacsKey {
static func detect(charCode: UniChar, flags: NSEvent.ModifierFlags) -> EmacsKey {
let charCode = AppleKeyboardConverter.cnvApple2ABC(charCode)
if flags.contains(.control) {
return vChewingEmacsKey(rawValue: charCode) ?? .none
return EmacsKey(rawValue: charCode) ?? .none
}
return .none
}

View File

@ -137,7 +137,7 @@ class InputState {
class NotEmpty: InputState {
private(set) var composingBuffer: String
private(set) var cursorIndex: Int = 0 { didSet { cursorIndex = max(cursorIndex, 0) } }
var composingBufferConverted: String {
public var composingBufferConverted: String {
let converted = IME.kanjiConversionIfRequired(composingBuffer)
if converted.utf16.count != composingBuffer.utf16.count
|| converted.count != composingBuffer.count
@ -153,10 +153,10 @@ class InputState {
defer { self.cursorIndex = cursorIndex }
}
var attributedString: NSAttributedString {
var attributedString: NSMutableAttributedString {
///
/// JIS
let attributedString = NSAttributedString(
let attributedString = NSMutableAttributedString(
string: composingBufferConverted,
attributes: [
.underlineStyle: NSUnderlineStyle.single.rawValue,
@ -270,7 +270,7 @@ class InputState {
defer { self.markerIndex = markerIndex }
}
override var attributedString: NSAttributedString {
override var attributedString: NSMutableAttributedString {
///
/// JIS
let attributedString = NSMutableAttributedString(string: composingBufferConverted)
@ -392,8 +392,8 @@ class InputState {
//
//
// Crediting Qwertyyb: https://github.com/qwertyyb/Fire/issues/55#issuecomment-1133497700
override var attributedString: NSAttributedString {
let attributedString = NSAttributedString(
override var attributedString: NSMutableAttributedString {
let attributedString = NSMutableAttributedString(
string: " ",
attributes: [
.underlineStyle: NSUnderlineStyle.single.rawValue,

View File

@ -156,8 +156,8 @@ class KeyHandler {
/// nil
func buildAssociatePhraseArray(withKey key: String) -> [String] {
var arrResult: [String] = []
if currentLM.hasAssociatedPhrasesForKey(key) {
arrResult.append(contentsOf: currentLM.associatedPhrasesForKey(key))
if currentLM.hasAssociatedPhrasesFor(key: key) {
arrResult.append(contentsOf: currentLM.associatedPhrasesFor(key: key))
}
return arrResult
}
@ -170,37 +170,39 @@ class KeyHandler {
func fixNode(value: String, respectCursorPushing: Bool = true) {
let cursorIndex = min(actualCandidateCursorIndex + (mgrPrefs.useRearCursorMode ? 1 : 0), compositorLength)
compositor.grid.fixNodeSelectedCandidate(location: cursorIndex, value: value)
// //
// let selectedNode: Megrez.NodeAnchor = compositor.grid.fixNodeSelectedCandidate(
// location: cursorIndex, value: value
// )
// //
// if !mgrPrefs.useSCPCTypingMode {
// //
// var addToUserOverrideModel = true
// if selectedNode.spanningLength != value.count {
// IME.prtDebugIntel("UOM: SpanningLength != value.count, dismissing.")
// addToUserOverrideModel = false
// }
// if addToUserOverrideModel {
// if let theNode = selectedNode.node {
// // SymbolLM Score -12
// if theNode.scoreFor(candidate: value) <= -12 {
// IME.prtDebugIntel("UOM: Score <= -12, dismissing.")
// addToUserOverrideModel = false
// }
// }
// }
// if addToUserOverrideModel {
// IME.prtDebugIntel("UOM: Start Observation.")
// // trigram
// // trigram
// currentUOM.observe(
// walkedNodes: walkedAnchors, cursorIndex: cursorIndex, candidate: value,
// timestamp: NSDate().timeIntervalSince1970
// )
// }
// }
//
let selectedNode: Megrez.NodeAnchor = compositor.grid.fixNodeSelectedCandidate(
location: cursorIndex, value: value
)
//
if !mgrPrefs.useSCPCTypingMode {
//
var addToUserOverrideModel = true
if selectedNode.spanningLength != value.count {
IME.prtDebugIntel("UOM: SpanningLength != value.count, dismissing.")
addToUserOverrideModel = false
}
if addToUserOverrideModel {
if let theNode = selectedNode.node {
// SymbolLM Score -12
if theNode.scoreFor(candidate: value) <= -12 {
IME.prtDebugIntel("UOM: Score <= -12, dismissing.")
addToUserOverrideModel = false
}
}
}
if addToUserOverrideModel {
IME.prtDebugIntel("UOM: Start Observation.")
// trigram
// trigram
currentUOM.observe(
walkedAnchors: walkedAnchors, cursorIndex: cursorIndex, candidate: value,
timestamp: NSDate().timeIntervalSince1970
)
}
}
//
walk()
///
@ -237,40 +239,54 @@ class KeyHandler {
///
var candidatesArray: [String] {
var arrNodes: [Megrez.NodeAnchor] = rawNodes
var arrCandidates: [String] = []
var arrNodes: [Megrez.NodeAnchor] = []
arrNodes.append(contentsOf: rawNodes)
/// nodes
///
///
if !arrNodes.isEmpty {
// sort the nodes, so that longer nodes (representing longer phrases)
// are placed at the top of the candidate list
arrNodes.sort { $0.keyLength > $1.keyLength }
if arrNodes.isEmpty { return arrCandidates }
// then use the Swift trick to retrieve the candidates for each node at/crossing the cursor
for currentNodeAnchor in arrNodes {
if let currentNode = currentNodeAnchor.node {
for currentCandidate in currentNode.candidates {
arrCandidates.append(currentCandidate.value)
}
// sort the nodes, so that longer nodes (representing longer phrases)
// are placed at the top of the candidate list
arrNodes = arrNodes.stableSort { $0.keyLength > $1.keyLength }
// then use the Swift trick to retrieve the candidates for each node at/crossing the cursor
for currentNodeAnchor in arrNodes {
if let currentNode = currentNodeAnchor.node {
for currentCandidate in currentNode.candidates {
// / JIS
//
//
arrCandidates.append(currentCandidate.value)
}
}
}
if mgrPrefs.fetchSuggestionsFromUserOverrideModel, !mgrPrefs.useSCPCTypingMode {
let arrSuggestedUnigrams: [Megrez.Unigram] = fetchSuggestedCandidates().stableSort { $0.score > $1.score }
let arrSuggestedCandidates: [String] = arrSuggestedUnigrams.map { $0.keyValue.value }
arrCandidates = arrSuggestedCandidates.filter { arrCandidates.contains($0) } + arrCandidates
arrCandidates = arrCandidates.deduplicate
arrCandidates = arrCandidates.stableSort { $0.count > $1.count }
}
return arrCandidates
}
///
func dealWithOverrideModelSuggestions() {
///
func fetchSuggestedCandidates() -> [Megrez.Unigram] {
currentUOM.suggest(
walkedAnchors: walkedAnchors, cursorIndex: compositorCursorIndex,
timestamp: NSDate().timeIntervalSince1970)
}
///
func fetchAndApplySuggestionsFromUserOverrideModel() {
///
if mgrPrefs.useSCPCTypingMode { return }
///
if !mgrPrefs.fetchSuggestionsFromUserOverrideModel { return }
/// trigram
let overrideValue =
mgrPrefs.useSCPCTypingMode
? ""
: currentUOM.suggest(
walkedNodes: walkedAnchors, cursorIndex: compositorCursorIndex,
timestamp: NSDate().timeIntervalSince1970
)
let overrideValue = fetchSuggestedCandidates().first?.keyValue.value ?? ""
///
if !overrideValue.isEmpty {

View File

@ -122,7 +122,7 @@ extension KeyHandler {
// MARK: PgDn
if input.isPageDown || input.emacsKey == vChewingEmacsKey.nextPage {
if input.isPageDown || input.emacsKey == EmacsKey.nextPage {
let updated: Bool = ctlCandidateCurrent.showNextPage()
if !updated {
IME.prtDebugIntel("9B691919")
@ -166,7 +166,7 @@ extension KeyHandler {
// MARK: EmacsKey Backward
if input.emacsKey == vChewingEmacsKey.backward {
if input.emacsKey == EmacsKey.backward {
let updated: Bool = ctlCandidateCurrent.highlightPreviousCandidate()
if !updated {
IME.prtDebugIntel("9B89308D")
@ -199,7 +199,7 @@ extension KeyHandler {
// MARK: EmacsKey Forward
if input.emacsKey == vChewingEmacsKey.forward {
if input.emacsKey == EmacsKey.forward {
let updated: Bool = ctlCandidateCurrent.highlightNextCandidate()
if !updated {
IME.prtDebugIntel("9B2428D")
@ -254,7 +254,7 @@ extension KeyHandler {
// MARK: Home Key
if input.isHome || input.emacsKey == vChewingEmacsKey.home {
if input.isHome || input.emacsKey == EmacsKey.home {
if ctlCandidateCurrent.selectedCandidateIndex == 0 {
IME.prtDebugIntel("9B6EDE8D")
errorCallback()
@ -278,7 +278,7 @@ extension KeyHandler {
if candidates.isEmpty {
return false
} else { // count > 0!isEmpty滿
if input.isEnd || input.emacsKey == vChewingEmacsKey.end {
if input.isEnd || input.emacsKey == EmacsKey.end {
if ctlCandidateCurrent.selectedCandidateIndex == candidates.count - 1 {
IME.prtDebugIntel("9B69AAAD")
errorCallback()

View File

@ -83,8 +83,8 @@ extension KeyHandler {
/// ASCII 使insertText:replacementRange:
/// ASCII
/// Objective-C isPrintable()
/// CTools.h Swift
/// Objective-C isPrintable()
/// CTools.h Swift
if charCode < 0x80, !CTools.isPrintable(charCode) {
return false
}
@ -175,27 +175,27 @@ extension KeyHandler {
// 使 OVMandarin調
composer.receiveKey(fromString: " ")
}
let reading = composer.getComposition() //
let reading = composer.getComposition() //
//
//
//
if !ifLangModelHasUnigrams(forKey: reading) {
IME.prtDebugIntel("B49C0979語彙庫內無「\(reading)」的匹配記錄。")
errorCallback()
composer.clear()
//
//
stateCallback((compositorLength == 0) ? InputState.EmptyIgnoringPreviousState() : buildInputtingState)
return true // IMK
return true // IMK
}
//
//
insertToCompositorAtCursor(reading: reading)
//
//
let textToCommit = popOverflowComposingTextAndWalk
//
// dealWithOverrideModelSuggestions() // 使
//
fetchAndApplySuggestionsFromUserOverrideModel()
//
markNodesFixedIfNecessary()
@ -257,7 +257,7 @@ extension KeyHandler {
if let currentState = state as? InputState.NotEmpty, composer.isEmpty,
input.isExtraChooseCandidateKey || input.isExtraChooseCandidateKeyReverse || input.isSpace
|| input.isPageDown || input.isPageUp || (input.isTab && mgrPrefs.specifyShiftTabKeyBehavior)
|| (input.isTypingVertical && (input.isverticalTypingOnlyChooseCandidateKey))
|| (input.isTypingVertical && (input.isVerticalTypingOnlyChooseCandidateKey))
{
if input.isSpace {
/// Space
@ -305,7 +305,7 @@ extension KeyHandler {
// MARK: Cursor backward
if input.isCursorBackward || input.emacsKey == vChewingEmacsKey.backward {
if input.isCursorBackward || input.emacsKey == EmacsKey.backward {
return handleBackward(
state: state,
input: input,
@ -316,7 +316,7 @@ extension KeyHandler {
// MARK: Cursor forward
if input.isCursorForward || input.emacsKey == vChewingEmacsKey.forward {
if input.isCursorForward || input.emacsKey == EmacsKey.forward {
return handleForward(
state: state, input: input, stateCallback: stateCallback, errorCallback: errorCallback
)
@ -324,13 +324,13 @@ extension KeyHandler {
// MARK: Home
if input.isHome || input.emacsKey == vChewingEmacsKey.home {
if input.isHome || input.emacsKey == EmacsKey.home {
return handleHome(state: state, stateCallback: stateCallback, errorCallback: errorCallback)
}
// MARK: End
if input.isEnd || input.emacsKey == vChewingEmacsKey.end {
if input.isEnd || input.emacsKey == EmacsKey.end {
return handleEnd(state: state, stateCallback: stateCallback, errorCallback: errorCallback)
}
@ -360,7 +360,7 @@ extension KeyHandler {
// MARK: Delete
if input.isDelete || input.emacsKey == vChewingEmacsKey.delete {
if input.isDelete || input.emacsKey == EmacsKey.delete {
return handleDelete(state: state, stateCallback: stateCallback, errorCallback: errorCallback)
}

View File

@ -233,7 +233,7 @@ extension KeyHandler {
}
// Shift + Left
if input.isCursorBackward || input.emacsKey == vChewingEmacsKey.backward, input.isShiftHold {
if input.isCursorBackward || input.emacsKey == EmacsKey.backward, input.isShiftHold {
var index = state.markerIndex
if index > 0 {
index = state.composingBuffer.utf16PreviousPosition(for: index)
@ -254,7 +254,7 @@ extension KeyHandler {
}
// Shift + Right
if input.isCursorForward || input.emacsKey == vChewingEmacsKey.forward, input.isShiftHold {
if input.isCursorForward || input.emacsKey == EmacsKey.forward, input.isShiftHold {
var index = state.markerIndex
if index < (state.composingBuffer.utf16.count) {
index = state.composingBuffer.utf16NextPosition(for: index)
@ -755,7 +755,8 @@ extension KeyHandler {
stateCallback: @escaping (InputState) -> Void,
errorCallback: @escaping () -> Void
) -> Bool {
guard let state = state as? InputState.Inputting else {
if composer.isEmpty && (compositor.isEmpty || walkedAnchors.isEmpty) { return false }
guard state is InputState.Inputting else {
guard state is InputState.Empty else {
IME.prtDebugIntel("6044F081")
errorCallback()
@ -772,7 +773,7 @@ extension KeyHandler {
}
//
let candidates = buildCandidate(state: state).candidates
let candidates = candidatesArray
guard !candidates.isEmpty else {
IME.prtDebugIntel("3378A6DF")
errorCallback()

View File

@ -1296,7 +1296,7 @@ public struct Tekkon {
///
///
/// 便 validity check
/// ////
///
static let mapETen26StaticKeys: [String: String] = [
"a": "", "b": "", "c": "", "d": "", "e": "", "f": "", "g": "", "h": "", "i": "", "j": "", "k": "",
"l": "", "m": "", "n": "", "o": "", "p": "", "q": "", "r": "", "s": "", "t": "", "u": "", "v": "",

View File

@ -63,8 +63,12 @@ class ctlInputMethod: IMKInputController {
client().overrideKeyboard(withKeyboardNamed: mgrPrefs.basicKeyboardLayout)
}
/// 調
/// 調
func resetKeyHandler() {
if let state = state as? InputState.NotEmpty {
/// 調
handle(state: InputState.Committing(textToCommit: state.composingBufferConverted))
}
keyHandler.clear()
handle(state: InputState.Empty())
}
@ -101,7 +105,7 @@ class ctlInputMethod: IMKInputController {
///
/// macOS
if client().bundleIdentifier() != Bundle.main.bundleIdentifier {
// Override the keyboard layout to the basic one.
// 使
setKeyLayout()
handle(state: .Empty())
} //
@ -143,7 +147,7 @@ class ctlInputMethod: IMKInputController {
///
/// macOS
if client().bundleIdentifier() != Bundle.main.bundleIdentifier {
// Remember to override the keyboard layout again -- treat this as an activate event.
// 使
setKeyLayout()
handle(state: .Empty())
} //
@ -229,10 +233,6 @@ class ctlInputMethod: IMKInputController {
/// - Parameter sender: 使
override func commitComposition(_ sender: Any!) {
_ = sender //
if let state = state as? InputState.NotEmpty {
/// 調
handle(state: InputState.Committing(textToCommit: state.composingBuffer))
}
resetKeyHandler()
}
}
@ -278,6 +278,38 @@ extension ctlInputMethod {
clearInlineDisplay()
return
}
var identifier: AnyObject {
switch IME.currentInputMode {
case InputMode.imeModeCHS:
if #available(macOS 12.0, *) {
return "zh-Hans" as AnyObject
}
case InputMode.imeModeCHT:
if #available(macOS 12.0, *) {
return (mgrPrefs.shiftJISShinjitaiOutputEnabled || mgrPrefs.chineseConversionEnabled)
? "ja" as AnyObject : "zh-Hant" as AnyObject
}
default:
break
}
return "" as AnyObject
}
// [Shiki's Note] This might needs to be bug-reported to Apple:
// The LanguageIdentifier attribute of an NSAttributeString designated to
// IMK Client().SetMarkedText won't let the actual font respect your languageIdentifier
// settings. Still, this might behaves as Apple's current expectation, I'm afraid.
if #available(macOS 12.0, *) {
state.attributedString.setAttributes(
[.languageIdentifier: identifier],
range: NSRange(
location: 0,
length: state.composingBuffer.utf16.count
)
)
}
/// selectionRange
/// 0 replacementRangeNSNotFound
///
@ -297,6 +329,7 @@ extension ctlInputMethod {
}
///
/// IMK commitComposition
private func commit(text: String) {
let buffer = IME.kanjiConversionIfRequired(text)
if buffer.isEmpty {

View File

@ -139,6 +139,11 @@ extension ctlInputMethod {
)
}
menu.addItem(
withTitle: NSLocalizedString("Optimize Memorized Phrases", comment: ""),
action: #selector(removeUnigramsFromUOM(_:)), keyEquivalent: ""
)
menu.addItem(NSMenuItem.separator()) // ---------------------
if optionKeyPressed {
@ -199,6 +204,7 @@ extension ctlInputMethod {
}
@objc func toggleSCPCTypingMode(_: Any?) {
resetKeyHandler()
NotifierController.notify(
message: String(
format: "%@%@%@", NSLocalizedString("Per-Char Select Mode", comment: ""), "\n",
@ -206,10 +212,10 @@ extension ctlInputMethod {
? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "")
))
resetKeyHandler()
}
@objc func toggleChineseConverter(_: Any?) {
resetKeyHandler()
NotifierController.notify(
message: String(
format: "%@%@%@", NSLocalizedString("Force KangXi Writing", comment: ""), "\n",
@ -217,10 +223,10 @@ extension ctlInputMethod {
? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "")
))
resetKeyHandler()
}
@objc func toggleShiftJISShinjitaiOutput(_: Any?) {
resetKeyHandler()
NotifierController.notify(
message: String(
format: "%@%@%@", NSLocalizedString("JIS Shinjitai Output", comment: ""), "\n",
@ -228,10 +234,10 @@ extension ctlInputMethod {
? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "")
))
resetKeyHandler()
}
@objc func toggleHalfWidthPunctuation(_: Any?) {
resetKeyHandler()
NotifierController.notify(
message: String(
format: "%@%@%@", NSLocalizedString("Half-Width Punctuation Mode", comment: ""),
@ -240,10 +246,10 @@ extension ctlInputMethod {
? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "")
))
resetKeyHandler()
}
@objc func toggleCNS11643Enabled(_: Any?) {
resetKeyHandler()
NotifierController.notify(
message: String(
format: "%@%@%@", NSLocalizedString("CNS11643 Mode", comment: ""), "\n",
@ -251,10 +257,10 @@ extension ctlInputMethod {
? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "")
))
resetKeyHandler()
}
@objc func toggleSymbolEnabled(_: Any?) {
resetKeyHandler()
NotifierController.notify(
message: String(
format: "%@%@%@", NSLocalizedString("Symbol & Emoji Input", comment: ""), "\n",
@ -262,10 +268,10 @@ extension ctlInputMethod {
? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "")
))
resetKeyHandler()
}
@objc func toggleAssociatedPhrasesEnabled(_: Any?) {
resetKeyHandler()
NotifierController.notify(
message: String(
format: "%@%@%@", NSLocalizedString("Per-Char Associated Phrases", comment: ""),
@ -274,10 +280,10 @@ extension ctlInputMethod {
? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "")
))
resetKeyHandler()
}
@objc func togglePhraseReplacement(_: Any?) {
resetKeyHandler()
NotifierController.notify(
message: String(
format: "%@%@%@", NSLocalizedString("Use Phrase Replacement", comment: ""), "\n",
@ -285,7 +291,6 @@ extension ctlInputMethod {
? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "")
))
resetKeyHandler()
}
@objc func selfUninstall(_: Any?) {
@ -311,38 +316,38 @@ extension ctlInputMethod {
}
@objc func openUserPhrases(_: Any?) {
IME.openPhraseFile(userFileAt: mgrLangModel.userPhrasesDataPath(IME.getInputMode()))
if NSEvent.modifierFlags.contains(.option), mgrPrefs.isDebugModeEnabled {
IME.openPhraseFile(userFileAt: mgrLangModel.userPhrasesDataPath(IME.getInputMode(isReversed: true)))
IME.openPhraseFile(fromURL: mgrLangModel.userPhrasesDataURL(IME.getInputMode()))
if NSEvent.modifierFlags.contains(.option) {
IME.openPhraseFile(fromURL: mgrLangModel.userPhrasesDataURL(IME.getInputMode(isReversed: true)))
}
}
@objc func openExcludedPhrases(_: Any?) {
IME.openPhraseFile(userFileAt: mgrLangModel.excludedPhrasesDataPath(IME.getInputMode()))
if NSEvent.modifierFlags.contains(.option), mgrPrefs.isDebugModeEnabled {
IME.openPhraseFile(userFileAt: mgrLangModel.excludedPhrasesDataPath(IME.getInputMode(isReversed: true)))
IME.openPhraseFile(fromURL: mgrLangModel.userFilteredDataURL(IME.getInputMode()))
if NSEvent.modifierFlags.contains(.option) {
IME.openPhraseFile(fromURL: mgrLangModel.userFilteredDataURL(IME.getInputMode(isReversed: true)))
}
}
@objc func openUserSymbols(_: Any?) {
IME.openPhraseFile(userFileAt: mgrLangModel.userSymbolDataPath(IME.getInputMode()))
if NSEvent.modifierFlags.contains(.option), mgrPrefs.isDebugModeEnabled {
IME.openPhraseFile(userFileAt: mgrLangModel.userSymbolDataPath(IME.getInputMode(isReversed: true)))
IME.openPhraseFile(fromURL: mgrLangModel.userSymbolDataURL(IME.getInputMode()))
if NSEvent.modifierFlags.contains(.option) {
IME.openPhraseFile(fromURL: mgrLangModel.userSymbolDataURL(IME.getInputMode(isReversed: true)))
}
}
@objc func openPhraseReplacement(_: Any?) {
IME.openPhraseFile(userFileAt: mgrLangModel.phraseReplacementDataPath(IME.getInputMode()))
if NSEvent.modifierFlags.contains(.option), mgrPrefs.isDebugModeEnabled {
IME.openPhraseFile(userFileAt: mgrLangModel.phraseReplacementDataPath(IME.getInputMode(isReversed: true)))
IME.openPhraseFile(fromURL: mgrLangModel.userReplacementsDataURL(IME.getInputMode()))
if NSEvent.modifierFlags.contains(.option) {
IME.openPhraseFile(fromURL: mgrLangModel.userReplacementsDataURL(IME.getInputMode(isReversed: true)))
}
}
@objc func openAssociatedPhrases(_: Any?) {
IME.openPhraseFile(userFileAt: mgrLangModel.userAssociatedPhrasesDataPath(IME.getInputMode()))
if NSEvent.modifierFlags.contains(.option), mgrPrefs.isDebugModeEnabled {
IME.openPhraseFile(fromURL: mgrLangModel.userAssociatesDataURL(IME.getInputMode()))
if NSEvent.modifierFlags.contains(.option) {
IME.openPhraseFile(
userFileAt: mgrLangModel.userAssociatedPhrasesDataPath(IME.getInputMode(isReversed: true)))
fromURL: mgrLangModel.userAssociatesDataURL(IME.getInputMode(isReversed: true)))
}
}
@ -350,6 +355,13 @@ extension ctlInputMethod {
IME.initLangModels(userOnly: true)
}
@objc func removeUnigramsFromUOM(_: Any?) {
mgrLangModel.removeUnigramsFromUserOverrideModel(IME.getInputMode())
if NSEvent.modifierFlags.contains(.option) {
mgrLangModel.removeUnigramsFromUserOverrideModel(IME.getInputMode(isReversed: true))
}
}
@objc func showAbout(_: Any?) {
(NSApp.delegate as? AppDelegate)?.showAbout()
NSApp.activate(ignoringOtherApps: true)

View File

@ -48,7 +48,7 @@ public enum IME {
switch (mgrPrefs.chineseConversionEnabled, mgrPrefs.shiftJISShinjitaiOutputEnabled) {
case (false, true): return vChewingKanjiConverter.cnvTradToJIS(text)
case (true, false): return vChewingKanjiConverter.cnvTradToKangXi(text)
//
//
case (true, true): return vChewingKanjiConverter.cnvTradToJIS(text)
case (false, false): return text
}
@ -122,6 +122,10 @@ public enum IME {
// MARK: - Open a phrase data file.
static func openPhraseFile(fromURL url: URL) {
openPhraseFile(userFileAt: url.path)
}
static func openPhraseFile(userFileAt path: String) {
func checkIfUserFilesExist() -> Bool {
if !mgrLangModel.chkUserLMFilesExist(InputMode.imeModeCHS)
@ -373,7 +377,7 @@ public enum IME {
// Extend the RangeReplaceableCollection to allow it clean duplicated characters.
// Ref: https://stackoverflow.com/questions/25738817/
extension RangeReplaceableCollection where Element: Hashable {
var charDeDuplicate: Self {
var deduplicate: Self {
var set = Set<Element>()
return filter { set.insert($0).inserted }
}
@ -417,3 +421,25 @@ extension UniChar {
return true
}
}
// MARK: - Stable Sort Extension
// Ref: https://stackoverflow.com/a/50545761/4162914
extension Sequence {
/// Return a stable-sorted collection.
///
/// - Parameter areInIncreasingOrder: Return nil when two element are equal.
/// - Returns: The sorted collection.
public func stableSort(
by areInIncreasingOrder: (Element, Element) throws -> Bool
)
rethrows -> [Element]
{
try enumerated()
.sorted { a, b -> Bool in
try areInIncreasingOrder(a.element, b.element)
|| (a.offset < b.offset && !areInIncreasingOrder(b.element, a.element))
}
.map(\.element)
}
}

View File

@ -56,6 +56,7 @@ struct UserDef {
static let kShouldNotFartInLieuOfBeep = "ShouldNotFartInLieuOfBeep"
static let kShowHanyuPinyinInCompositionBuffer = "ShowHanyuPinyinInCompositionBuffer"
static let kInlineDumpPinyinInLieuOfZhuyin = "InlineDumpPinyinInLieuOfZhuyin"
static let kFetchSuggestionsFromUserOverrideModel = "FetchSuggestionsFromUserOverrideModel"
static let kCandidateTextFontName = "CandidateTextFontName"
static let kCandidateKeyLabelFontName = "CandidateKeyLabelFontName"
@ -270,6 +271,9 @@ public enum mgrPrefs {
UserDefaults.standard.setDefault(
mgrPrefs.allowBoostingSingleKanjiAsUserPhrase, forKey: UserDef.kAllowBoostingSingleKanjiAsUserPhrase
)
UserDefaults.standard.setDefault(
mgrPrefs.fetchSuggestionsFromUserOverrideModel, forKey: UserDef.kFetchSuggestionsFromUserOverrideModel
)
UserDefaults.standard.setDefault(mgrPrefs.usingHotKeySCPC, forKey: UserDef.kUsingHotKeySCPC)
UserDefaults.standard.setDefault(mgrPrefs.usingHotKeyAssociates, forKey: UserDef.kUsingHotKeyAssociates)
@ -344,6 +348,9 @@ public enum mgrPrefs {
@UserDefault(key: UserDef.kAllowBoostingSingleKanjiAsUserPhrase, defaultValue: false)
static var allowBoostingSingleKanjiAsUserPhrase: Bool
@UserDefault(key: UserDef.kFetchSuggestionsFromUserOverrideModel, defaultValue: true)
static var fetchSuggestionsFromUserOverrideModel: Bool
static var minCandidateLength: Int {
mgrPrefs.allowBoostingSingleKanjiAsUserPhrase ? 1 : 2
}

View File

@ -28,6 +28,9 @@ extension vChewing {
public enum LMConsolidator {
public static let kPragmaHeader = "# 𝙵𝙾𝚁𝙼𝙰𝚃 𝚘𝚛𝚐.𝚊𝚝𝚎𝚕𝚒𝚎𝚛𝙸𝚗𝚖𝚞.𝚟𝚌𝚑𝚎𝚠𝚒𝚗𝚐.𝚞𝚜𝚎𝚛𝙻𝚊𝚗𝚐𝚞𝚊𝚐𝚎𝙼𝚘𝚍𝚎𝚕𝙳𝚊𝚝𝚊.𝚏𝚘𝚛𝚖𝚊𝚝𝚝𝚎𝚍"
///
/// - Parameter path:
/// - Returns:
public static func checkPragma(path: String) -> Bool {
if FileManager.default.fileExists(atPath: path) {
let fileHandle = FileHandle(forReadingAtPath: path)!
@ -51,12 +54,17 @@ extension vChewing {
return false
}
///
/// - Parameter path:
/// - Returns:
@discardableResult public static func fixEOF(path: String) -> Bool {
let urlPath = URL(fileURLWithPath: path)
if FileManager.default.fileExists(atPath: path) {
var strIncoming = ""
do {
strIncoming += try String(contentsOf: urlPath, encoding: .utf8)
/// Swift LMConsolidator EOF
/// consolidate()
if !strIncoming.hasSuffix("\n") {
IME.prtDebugIntel("EOF Fix Necessity Confirmed, Start Fixing.")
if let writeFile = FileHandle(forUpdatingAtPath: path),
@ -81,10 +89,14 @@ extension vChewing {
return false
}
///
/// - Parameters:
/// - path:
/// - shouldCheckPragma:
/// - Returns:
@discardableResult public static func consolidate(path: String, pragma shouldCheckPragma: Bool) -> Bool {
var pragmaResult = false
let pragmaResult = checkPragma(path: path)
if shouldCheckPragma {
pragmaResult = checkPragma(path: path)
if pragmaResult {
return true
}
@ -105,6 +117,7 @@ extension vChewing {
strProcessed.regReplace(pattern: #"( +| +| +|\t+)+"#, replaceWith: " ")
//
strProcessed.regReplace(pattern: #"(^ | $)"#, replaceWith: "")
strProcessed.regReplace(pattern: #"(\n | \n)"#, replaceWith: "\n")
// CR & FF to LF,
strProcessed.regReplace(pattern: #"(\f+|\r+|\n+)+"#, replaceWith: "\n")
if strProcessed.prefix(1) == " " { //
@ -114,21 +127,21 @@ extension vChewing {
strProcessed.removeLast()
}
// Step 3: Add Formatted Pragma, the Sorted Header:
// Step 2: Add Formatted Pragma, the Sorted Header:
if !pragmaResult {
strProcessed = kPragmaHeader + "\n" + strProcessed // Add Sorted Header
}
// Step 4: Deduplication.
// Step 3: Deduplication.
let arrData = strProcessed.split(separator: "\n")
// reversed override
let arrDataDeduplicated = Array(NSOrderedSet(array: arrData.reversed()).array as! [String])
strProcessed = arrDataDeduplicated.reversed().joined(separator: "\n") + "\n"
// Step 5: Remove duplicated newlines at the end of the file.
strProcessed.regReplace(pattern: "\\n+", replaceWith: "\n")
// Step 4: Remove duplicated newlines at the end of the file.
strProcessed.regReplace(pattern: #"\n+"#, replaceWith: "\n")
// Step 6: Write consolidated file contents.
// Step 5: Write consolidated file contents.
try strProcessed.write(to: urlPath, atomically: false, encoding: .utf8)
} catch {

View File

@ -26,19 +26,10 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import Foundation
//
// LMInstantiator 100MB
private var lmCNS = vChewing.LMCoreNS(
reverse: true, consolidate: false, defaultScore: -11.0, forceDefaultScore: false
)
private var lmSymbols = vChewing.LMCoreNS(
reverse: true, consolidate: false, defaultScore: -13.0, forceDefaultScore: false
)
extension vChewing {
/// LMInstantiatorLMI
/// LanguageModel 使
///
/// LanguageModelProtocol 使
///
///
/// LMI 調
/// LMI
@ -53,7 +44,7 @@ extension vChewing {
///
/// LMI LMI
///
public class LMInstantiator: Megrez.LanguageModel {
public class LMInstantiator: LanguageModelProtocol {
//
public var isPhraseReplacementEnabled = false
public var isCNSEnabled = false
@ -79,6 +70,15 @@ extension vChewing {
reverse: true, consolidate: false, defaultScore: -1.0, forceDefaultScore: false
)
//
// 100MB
static var lmCNS = vChewing.LMCoreNS(
reverse: true, consolidate: false, defaultScore: -11.0, forceDefaultScore: false
)
static var lmSymbols = vChewing.LMCoreNS(
reverse: true, consolidate: false, defaultScore: -13.0, forceDefaultScore: false
)
// 使
// 使使
var lmUserPhrases = LMCoreEX(
@ -93,10 +93,7 @@ extension vChewing {
var lmReplacements = LMReplacments()
var lmAssociates = LMAssociates()
//
override init() {}
// 調
// MARK: -
public var isLanguageModelLoaded: Bool { lmCore.isLoaded() }
public func loadLanguageModel(path: String) {
@ -108,11 +105,11 @@ extension vChewing {
}
}
public var isCNSDataLoaded: Bool { lmCNS.isLoaded() }
public var isCNSDataLoaded: Bool { vChewing.LMInstantiator.lmCNS.isLoaded() }
public func loadCNSData(path: String) {
if FileManager.default.isReadableFile(atPath: path) {
lmCNS.open(path)
IME.prtDebugIntel("lmCNS: \(lmCNS.count) entries of data loaded from: \(path)")
vChewing.LMInstantiator.lmCNS.open(path)
IME.prtDebugIntel("lmCNS: \(vChewing.LMInstantiator.lmCNS.count) entries of data loaded from: \(path)")
} else {
IME.prtDebugIntel("lmCNS: File access failure: \(path)")
}
@ -128,11 +125,11 @@ extension vChewing {
}
}
public var isSymbolDataLoaded: Bool { lmSymbols.isLoaded() }
public var isSymbolDataLoaded: Bool { vChewing.LMInstantiator.lmSymbols.isLoaded() }
public func loadSymbolData(path: String) {
if FileManager.default.isReadableFile(atPath: path) {
lmSymbols.open(path)
IME.prtDebugIntel("lmSymbol: \(lmSymbols.count) entries of data loaded from: \(path)")
vChewing.LMInstantiator.lmSymbols.open(path)
IME.prtDebugIntel("lmSymbol: \(vChewing.LMInstantiator.lmSymbols.count) entries of data loaded from: \(path)")
} else {
IME.prtDebugIntel("lmSymbols: File access failure: \(path)")
}
@ -185,7 +182,7 @@ extension vChewing {
}
}
// MARK: - Core Functions (Public)
// MARK: -
///
// public func bigramsForKeys(preceedingKey: String, key: String) -> [Megrez.Bigram] { }
@ -193,11 +190,11 @@ extension vChewing {
/// LMI
/// - Parameter key:
/// - Returns:
override open func unigramsFor(key: String) -> [Megrez.Unigram] {
public func unigramsFor(key: String) -> [Megrez.Unigram] {
if key == " " {
///
let spaceUnigram = Megrez.Unigram(
keyValue: Megrez.KeyValuePair(key: " ", value: " "),
keyValue: Megrez.KeyValuePaired(key: " ", value: " "),
score: 0
)
return [spaceUnigram]
@ -216,16 +213,16 @@ extension vChewing {
rawAllUnigrams += lmCore.unigramsFor(key: key)
if isCNSEnabled {
rawAllUnigrams += lmCNS.unigramsFor(key: key)
rawAllUnigrams += vChewing.LMInstantiator.lmCNS.unigramsFor(key: key)
}
if isSymbolEnabled {
rawAllUnigrams += lmUserSymbols.unigramsFor(key: key)
rawAllUnigrams += lmSymbols.unigramsFor(key: key)
rawAllUnigrams += vChewing.LMInstantiator.lmSymbols.unigramsFor(key: key)
}
// Swift 使 NSOrderedSet
var filteredPairs: Set<Megrez.KeyValuePair> = []
var filteredPairs: Set<Megrez.KeyValuePaired> = []
// KeyValuePair
for unigram in lmFiltered.unigramsFor(key: key) {
@ -238,9 +235,10 @@ extension vChewing {
)
}
/// If the model has unigrams for the given key.
/// @param key The key.
override open func hasUnigramsFor(key: String) -> Bool {
///
/// - Parameter key:
/// - Returns:
public func hasUnigramsFor(key: String) -> Bool {
if key == " " { return true }
if !lmFiltered.hasUnigramsFor(key: key) {
@ -250,46 +248,40 @@ extension vChewing {
return !unigramsFor(key: key).isEmpty
}
public func associatedPhrasesForKey(_ key: String) -> [String] {
public func associatedPhrasesFor(key: String) -> [String] {
lmAssociates.valuesFor(key: key) ?? []
}
public func hasAssociatedPhrasesForKey(_ key: String) -> Bool {
public func hasAssociatedPhrasesFor(key: String) -> Bool {
lmAssociates.hasValuesFor(key: key)
}
// MARK: - Core Functions (Private)
/// 滿 LanguageModelProtocol
public func bigramsForKeys(precedingKey _: String, key _: String) -> [Megrez.Bigram] { .init() }
// MARK: -
///
/// - Parameters:
/// - unigrams:
/// - filteredPairs:
/// - Returns:
/// - unigrams:
/// - filteredPairs:
/// - Returns:
func filterAndTransform(
unigrams: [Megrez.Unigram],
filter filteredPairs: Set<Megrez.KeyValuePair>
filter filteredPairs: Set<Megrez.KeyValuePaired>
) -> [Megrez.Unigram] {
var results: [Megrez.Unigram] = []
var insertedPairs: Set<Megrez.KeyValuePair> = []
var insertedPairs: Set<Megrez.KeyValuePaired> = []
for unigram in unigrams {
var pair: Megrez.KeyValuePair = unigram.keyValue
if filteredPairs.contains(pair) {
continue
}
var pair: Megrez.KeyValuePaired = unigram.keyValue
if filteredPairs.contains(pair) { continue }
if isPhraseReplacementEnabled {
let replacement = lmReplacements.valuesFor(key: pair.value)
if !replacement.isEmpty {
IME.prtDebugIntel("\(pair.value) -> \(replacement)")
pair.value = replacement
}
}
if !insertedPairs.contains(pair) {
results.append(Megrez.Unigram(keyValue: pair, score: unigram.score))
insertedPairs.insert(pair)
if !replacement.isEmpty { pair.value = replacement }
}
if insertedPairs.contains(pair) { continue }
results.append(Megrez.Unigram(keyValue: pair, score: unigram.score))
insertedPairs.insert(pair)
}
return results
}

View File

@ -40,6 +40,34 @@ class SymbolNode {
children = Array(symbols).map { SymbolNode(String($0), nil) }
}
static func parseUserSymbolNodeData() {
let url = mgrLangModel.userSymbolNodeDataURL()
//
var arrLines = [String.SubSequence]()
var fieldSlice = [Substring.SubSequence]()
var arrChildren = [SymbolNode]()
do {
arrLines = try String(contentsOfFile: url.path, encoding: .utf8).split(separator: "\n")
for strLine in arrLines.lazy.filter({ !$0.isEmpty }) {
fieldSlice = strLine.split(separator: "=")
switch fieldSlice.count {
case 1: arrChildren.append(.init(String(fieldSlice[0])))
case 2: arrChildren.append(.init(String(fieldSlice[0]), symbols: .init(fieldSlice[1])))
default: break
}
}
if arrChildren.isEmpty {
root = defaultSymbolRoot
} else {
root = .init("/", arrChildren)
}
} catch {
root = defaultSymbolRoot
}
}
// MARK: - Static data.
static let catCommonSymbols = String(
format: NSLocalizedString("catCommonSymbols", comment: ""))
static let catHoriBrackets = String(
@ -71,7 +99,9 @@ class SymbolNode {
static let catLineSegments = String(
format: NSLocalizedString("catLineSegments", comment: ""))
static let root: SymbolNode = .init(
private(set) static var root: SymbolNode = .init("/")
private static let defaultSymbolRoot: SymbolNode = .init(
"/",
[
SymbolNode(""),

View File

@ -31,7 +31,7 @@ extension vChewing {
/// C++ ParselessLM Swift
/// For
@frozen public struct LMCoreEX {
/// 便 strData
/// 便 strData
var rangeMap: [String: [Range<String.Index>]] = [:]
///
var strData: String = ""
@ -66,12 +66,12 @@ extension vChewing {
shouldForceDefaultScore = forceDefaultScore
}
///
///
public func isLoaded() -> Bool {
!rangeMap.isEmpty
}
///
///
/// - parameters:
/// - path:
@discardableResult public mutating func open(_ path: String) -> Bool {
@ -88,9 +88,9 @@ extension vChewing {
strData = try String(contentsOfFile: path, encoding: .utf8).replacingOccurrences(of: "\t", with: " ")
strData.ranges(splitBy: "\n").forEach {
let neta = strData[$0].split(separator: " ")
if neta.count >= 2 {
let theKey = shouldReverse ? String(neta[1]) : String(neta[0])
if !neta[0].isEmpty, !neta[1].isEmpty, theKey.first != "#" {
if neta.count >= 2, String(neta[0]).first != "#" {
if !neta[0].isEmpty, !neta[1].isEmpty {
let theKey = shouldReverse ? String(neta[1]) : String(neta[0])
let theValue = $0
rangeMap[theKey, default: []].append(theValue)
}
@ -105,7 +105,7 @@ extension vChewing {
return true
}
///
///
public mutating func close() {
if isLoaded() {
rangeMap.removeAll()
@ -114,7 +114,7 @@ extension vChewing {
// MARK: - Advanced features
/// macOS Console.app
/// macOS Console.app
///
///
public func dump() {
@ -130,7 +130,7 @@ extension vChewing {
IME.prtDebugIntel(strDump)
}
/// 使 strData
/// 使 strData
///
///
/// - parameters:
@ -142,7 +142,7 @@ extension vChewing {
precedingKey == key ? [Megrez.Bigram]() : [Megrez.Bigram]()
}
/// strData
/// strData
/// - parameters:
/// - key:
public func unigramsFor(key: String) -> [Megrez.Unigram] {
@ -151,7 +151,7 @@ extension vChewing {
for netaRange in arrRangeRecords {
let neta = strData[netaRange].split(separator: " ")
let theValue: String = shouldReverse ? String(neta[0]) : String(neta[1])
let kvPair = Megrez.KeyValuePair(key: key, value: theValue)
let kvPair = Megrez.KeyValuePaired(key: key, value: theValue)
var theScore = defaultScore
if neta.count >= 3, !shouldForceDefaultScore {
theScore = .init(String(neta[2])) ?? defaultScore
@ -165,7 +165,7 @@ extension vChewing {
return grams
}
///
///
/// - parameters:
/// - key:
public func hasUnigramsFor(key: String) -> Bool {

View File

@ -29,7 +29,7 @@ extension vChewing {
/// mac
/// 使 plist
@frozen public struct LMCoreNS {
/// UTF8
/// UTF8
var rangeMap: [String: [Data]] = [:]
/// LMCoreNS
var strData: String = ""
@ -67,12 +67,12 @@ extension vChewing {
shouldForceDefaultScore = forceDefaultScore
}
///
///
public func isLoaded() -> Bool {
!rangeMap.isEmpty
}
///
///
/// - parameters:
/// - path:
@discardableResult public mutating func open(_ path: String) -> Bool {
@ -93,7 +93,7 @@ extension vChewing {
return true
}
///
///
public mutating func close() {
if isLoaded() {
rangeMap.removeAll()
@ -102,7 +102,7 @@ extension vChewing {
// MARK: - Advanced features
/// macOS Console.app
/// macOS Console.app
///
///
public func dump() {
@ -124,7 +124,7 @@ extension vChewing {
IME.prtDebugIntel(strDump)
}
/// 使 UTF8
/// 使 UTF8
///
///
/// - parameters:
@ -136,7 +136,7 @@ extension vChewing {
precedingKey == key ? [Megrez.Bigram]() : [Megrez.Bigram]()
}
/// UTF8
/// UTF8
/// - parameters:
/// - key:
public func unigramsFor(key: String) -> [Megrez.Unigram] {
@ -146,7 +146,7 @@ extension vChewing {
let strNetaSet = String(decoding: netaSet, as: UTF8.self)
let neta = Array(strNetaSet.split(separator: " ").reversed())
let theValue: String = .init(neta[0])
let kvPair = Megrez.KeyValuePair(key: key, value: theValue)
let kvPair = Megrez.KeyValuePaired(key: key, value: theValue)
var theScore = defaultScore
if neta.count >= 2, !shouldForceDefaultScore {
theScore = .init(String(neta[1])) ?? defaultScore
@ -160,7 +160,7 @@ extension vChewing {
return grams
}
///
///
/// - parameters:
/// - key:
public func hasUnigramsFor(key: String) -> Bool {

View File

@ -1,5 +1,5 @@
// Copyright (c) 2021 and onwards The vChewing Project (MIT-NTL License).
// Refactored from the ObjCpp-version of this class by Mengjuei Hsieh (MIT License).
// Refactored from the Cpp version of this class by Mengjuei Hsieh (MIT License).
/*
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
@ -27,38 +27,6 @@ import Foundation
extension vChewing {
public class LMUserOverride {
// MARK: - Private Structures
// class
class Override {
var count: Int = 0
var timestamp: Double = 0.0
}
class Observation {
var count: Int = 0
var overrides: [String: Override] = [:]
func update(candidate: String, timestamp: Double) {
count += 1
if let neta = overrides[candidate] {
neta.timestamp = timestamp
neta.count += 1
overrides[candidate] = neta
}
}
}
class KeyObservationPair {
var key: String
var observation: Observation
init(key: String, observation: Observation) {
self.key = key
self.observation = observation
}
}
// MARK: - Main
var mutCapacity: Int
@ -73,50 +41,57 @@ extension vChewing {
}
public func observe(
walkedNodes: [Megrez.NodeAnchor],
walkedAnchors: [Megrez.NodeAnchor],
cursorIndex: Int,
candidate: String,
timestamp: Double
) {
let key = convertKeyFrom(walkedNodes: walkedNodes, cursorIndex: cursorIndex)
let key = convertKeyFrom(walkedAnchors: walkedAnchors, cursorIndex: cursorIndex)
guard !key.isEmpty else { return }
guard mutLRUMap[key] != nil else {
let observation: Observation = .init()
var observation: Observation = .init()
observation.update(candidate: candidate, timestamp: timestamp)
let koPair = KeyObservationPair(key: key, observation: observation)
// key key key
// Swift
mutLRUMap.removeValue(forKey: key)
mutLRUMap[key] = koPair
mutLRUList.insert(koPair, at: 0)
if mutLRUList.count > mutCapacity {
mutLRUMap[mutLRUList[mutLRUList.endIndex].key] = nil
mutLRUMap.removeValue(forKey: mutLRUList[mutLRUList.endIndex].key)
mutLRUList.removeLast()
}
IME.prtDebugIntel("UOM: Observation finished with new observation: \(key)")
mgrLangModel.saveUserOverrideModelData()
return
}
if let theNeta = mutLRUMap[key] {
if var theNeta = mutLRUMap[key] {
theNeta.observation.update(candidate: candidate, timestamp: timestamp)
mutLRUList.insert(theNeta, at: 0)
mutLRUMap[key] = theNeta
IME.prtDebugIntel("UOM: Observation finished with existing observation: \(key)")
mgrLangModel.saveUserOverrideModelData()
}
}
public func suggest(
walkedNodes: [Megrez.NodeAnchor],
walkedAnchors: [Megrez.NodeAnchor],
cursorIndex: Int,
timestamp: Double
) -> String {
let key = convertKeyFrom(walkedNodes: walkedNodes, cursorIndex: cursorIndex)
) -> [Megrez.Unigram] {
let key = convertKeyFrom(walkedAnchors: walkedAnchors, cursorIndex: cursorIndex)
let currentReadingKey = convertKeyFrom(walkedAnchors: walkedAnchors, cursorIndex: cursorIndex, readingOnly: true)
guard let koPair = mutLRUMap[key] else {
IME.prtDebugIntel("UOM: mutLRUMap[key] is nil, throwing blank suggestion for key: \(key).")
return ""
return .init()
}
let observation = koPair.observation
var candidate = ""
var score = 0.0
var arrResults = [Megrez.Unigram]()
var currentHighScore = 0.0
for overrideNeta in Array(observation.overrides) {
let override: Override = overrideNeta.value
let overrideScore: Double = getScore(
@ -126,23 +101,20 @@ extension vChewing {
timestamp: timestamp,
lambda: mutDecayExponent
)
if overrideScore == 0.0 {
continue
}
if overrideScore > score {
candidate = overrideNeta.key
score = overrideScore
}
if (0...currentHighScore).contains(overrideScore) { continue }
let newUnigram = Megrez.Unigram(
keyValue: .init(key: currentReadingKey, value: overrideNeta.key), score: overrideScore
)
arrResults.insert(newUnigram, at: 0)
currentHighScore = overrideScore
}
if candidate.isEmpty {
if arrResults.isEmpty {
IME.prtDebugIntel("UOM: No usable suggestions in the result for key: \(key).")
}
return candidate
return arrResults
}
public func getScore(
private func getScore(
eventCount: Int,
totalCount: Int,
eventTimestamp: Double,
@ -156,50 +128,193 @@ extension vChewing {
}
func convertKeyFrom(
walkedNodes: [Megrez.NodeAnchor], cursorIndex: Int
walkedAnchors: [Megrez.NodeAnchor], cursorIndex: Int, readingOnly: Bool = false
) -> String {
let arrEndingPunctuation = ["", "", "", "", "", "", "", ""]
var arrNodesReversed: [Megrez.NodeAnchor] = []
let whiteList = "你他妳她祢她它牠再在"
var arrNodes: [Megrez.NodeAnchor] = []
var intLength = 0
for theNodeAnchor in walkedNodes {
// .reverse()
arrNodesReversed = [theNodeAnchor] + arrNodesReversed
for theNodeAnchor in walkedAnchors {
arrNodes.append(theNodeAnchor)
intLength += theNodeAnchor.spanningLength
if intLength >= cursorIndex {
break
}
}
if arrNodesReversed.isEmpty { return "" }
if arrNodes.isEmpty { return "" }
var strCurrent = "()"
var strPrevious = "()"
var strAnterior = "()"
arrNodes = Array(arrNodes.reversed())
guard let kvCurrent = arrNodesReversed[0].node?.currentKeyValue,
guard let kvCurrent = arrNodes[0].node?.currentKeyValue,
!arrEndingPunctuation.contains(kvCurrent.value)
else {
return ""
}
//
if kvCurrent.key.split(separator: "-").count != kvCurrent.value.count { return "" }
//
strCurrent = kvCurrent.key
if arrNodesReversed.count >= 2,
let kvPrevious = arrNodesReversed[1].node?.currentKeyValue,
!arrEndingPunctuation.contains(kvPrevious.value)
{
strPrevious = "(\(kvPrevious.key),\(kvPrevious.value))"
let strCurrent = kvCurrent.key
var kvPrevious = Megrez.KeyValuePaired()
var kvAnterior = Megrez.KeyValuePaired()
var readingStack = ""
var trigramKey: String { "(\(kvAnterior.toNGramKey),\(kvPrevious.toNGramKey),\(strCurrent))" }
var result: String {
// kvCurrent
if readingStack.contains("_")
|| (!kvPrevious.isValid && kvCurrent.value.count == 1 && !whiteList.contains(kvCurrent.value))
{
return ""
} else {
return (readingOnly ? strCurrent : trigramKey)
}
}
if arrNodesReversed.count >= 3,
let kvAnterior = arrNodesReversed[2].node?.currentKeyValue,
!arrEndingPunctuation.contains(kvAnterior.value)
if arrNodes.count >= 2,
let kvPreviousThisOne = arrNodes[1].node?.currentKeyValue,
!arrEndingPunctuation.contains(kvPrevious.value),
kvPrevious.key.split(separator: "-").count == kvPrevious.value.count
{
strAnterior = "(\(kvAnterior.key),\(kvAnterior.value))"
kvPrevious = kvPreviousThisOne
readingStack = kvPrevious.key + readingStack
}
return "(\(strAnterior),\(strPrevious),\(strCurrent))"
if arrNodes.count >= 3,
let kvAnteriorThisOne = arrNodes[2].node?.currentKeyValue,
!arrEndingPunctuation.contains(kvAnterior.value),
kvAnterior.key.split(separator: "-").count == kvAnterior.value.count
{
kvAnterior = kvAnteriorThisOne
readingStack = kvAnterior.key + readingStack
}
return result
}
}
}
// MARK: - Private Structures
extension vChewing.LMUserOverride {
enum OverrideUnit: CodingKey { case count, timestamp }
enum ObservationUnit: CodingKey { case count, overrides }
enum KeyObservationPairUnit: CodingKey { case key, observation }
struct Override: Hashable, Encodable, Decodable {
var count: Int = 0
var timestamp: Double = 0.0
static func == (lhs: Override, rhs: Override) -> Bool {
lhs.count == rhs.count && lhs.timestamp == rhs.timestamp
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: OverrideUnit.self)
try container.encode(timestamp, forKey: .timestamp)
try container.encode(count, forKey: .count)
}
func hash(into hasher: inout Hasher) {
hasher.combine(count)
hasher.combine(timestamp)
}
}
struct Observation: Hashable, Encodable, Decodable {
var count: Int = 0
var overrides: [String: Override] = [:]
static func == (lhs: Observation, rhs: Observation) -> Bool {
lhs.count == rhs.count && lhs.overrides == rhs.overrides
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: ObservationUnit.self)
try container.encode(count, forKey: .count)
try container.encode(overrides, forKey: .overrides)
}
func hash(into hasher: inout Hasher) {
hasher.combine(count)
hasher.combine(overrides)
}
mutating func update(candidate: String, timestamp: Double) {
count += 1
if overrides.keys.contains(candidate) {
overrides[candidate]?.timestamp = timestamp
overrides[candidate]?.count += 1
} else {
overrides[candidate] = .init(count: 1, timestamp: timestamp)
}
}
}
struct KeyObservationPair: Hashable, Encodable, Decodable {
var key: String
var observation: Observation
static func == (lhs: KeyObservationPair, rhs: KeyObservationPair) -> Bool {
lhs.key == rhs.key && lhs.observation == rhs.observation
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: KeyObservationPairUnit.self)
try container.encode(key, forKey: .key)
try container.encode(observation, forKey: .observation)
}
func hash(into hasher: inout Hasher) {
hasher.combine(key)
hasher.combine(observation)
}
}
}
// MARK: - Hash and Dehash the entire UOM data
extension vChewing.LMUserOverride {
/// LRU
public func bleachUnigrams() {
for key in mutLRUMap.keys {
if !key.contains("(),()") { continue }
mutLRUMap.removeValue(forKey: key)
}
resetMRUList()
mgrLangModel.saveUserOverrideModelData()
}
internal func resetMRUList() {
mutLRUList.removeAll()
for neta in mutLRUMap.reversed() {
mutLRUList.append(neta.value)
}
}
public func saveData(toURL fileURL: URL) {
let encoder = JSONEncoder()
do {
if let jsonData = try? encoder.encode(mutLRUMap) {
try jsonData.write(to: fileURL, options: .atomic)
}
} catch {
IME.prtDebugIntel("UOM Error: Unable to save data, abort saving. Details: \(error)")
return
}
}
public func loadData(fromURL fileURL: URL) {
let decoder = JSONDecoder()
do {
let data = try Data(contentsOf: fileURL, options: .mappedIfSafe)
guard let jsonResult = try? decoder.decode([String: KeyObservationPair].self, from: data) else {
IME.prtDebugIntel("UOM Error: Read file content type invalid, abort loading.")
return
}
mutLRUMap = jsonResult
resetMRUList()
} catch {
IME.prtDebugIntel("UOM Error: Unable to read file or parse the data, abort loading. Details: \(error)")
return
}
}
}

View File

@ -149,32 +149,37 @@ enum mgrLangModel {
public static func loadUserPhrasesData() {
gLangModelCHT.loadUserPhrasesData(
path: userPhrasesDataPath(InputMode.imeModeCHT),
filterPath: excludedPhrasesDataPath(InputMode.imeModeCHT)
path: userPhrasesDataURL(InputMode.imeModeCHT).path,
filterPath: userFilteredDataURL(InputMode.imeModeCHT).path
)
gLangModelCHS.loadUserPhrasesData(
path: userPhrasesDataPath(InputMode.imeModeCHS),
filterPath: excludedPhrasesDataPath(InputMode.imeModeCHS)
path: userPhrasesDataURL(InputMode.imeModeCHS).path,
filterPath: userFilteredDataURL(InputMode.imeModeCHS).path
)
gLangModelCHT.loadUserSymbolData(path: userSymbolDataPath(InputMode.imeModeCHT))
gLangModelCHS.loadUserSymbolData(path: userSymbolDataPath(InputMode.imeModeCHS))
gLangModelCHT.loadUserSymbolData(path: userSymbolDataURL(InputMode.imeModeCHT).path)
gLangModelCHS.loadUserSymbolData(path: userSymbolDataURL(InputMode.imeModeCHS).path)
gUserOverrideModelCHT.loadData(fromURL: userOverrideModelDataURL(InputMode.imeModeCHT))
gUserOverrideModelCHS.loadData(fromURL: userOverrideModelDataURL(InputMode.imeModeCHS))
SymbolNode.parseUserSymbolNodeData()
}
public static func loadUserAssociatesData() {
gLangModelCHT.loadUserAssociatesData(
path: mgrLangModel.userAssociatedPhrasesDataPath(InputMode.imeModeCHT)
path: mgrLangModel.userAssociatesDataURL(InputMode.imeModeCHT).path
)
gLangModelCHS.loadUserAssociatesData(
path: mgrLangModel.userAssociatedPhrasesDataPath(InputMode.imeModeCHS)
path: mgrLangModel.userAssociatesDataURL(InputMode.imeModeCHS).path
)
}
public static func loadUserPhraseReplacement() {
gLangModelCHT.loadReplacementsData(
path: mgrLangModel.phraseReplacementDataPath(InputMode.imeModeCHT)
path: mgrLangModel.userReplacementsDataURL(InputMode.imeModeCHT).path
)
gLangModelCHS.loadReplacementsData(
path: mgrLangModel.phraseReplacementDataPath(InputMode.imeModeCHS)
path: mgrLangModel.userReplacementsDataURL(InputMode.imeModeCHS).path
)
}
@ -219,37 +224,48 @@ enum mgrLangModel {
// Swift appendingPathComponent URL .path
static func userPhrasesDataPath(_ mode: InputMode) -> String {
static func userPhrasesDataURL(_ mode: InputMode) -> URL {
let fileName = (mode == InputMode.imeModeCHT) ? "userdata-cht.txt" : "userdata-chs.txt"
return URL(fileURLWithPath: dataFolderPath(isDefaultFolder: false)).appendingPathComponent(fileName).path
return URL(fileURLWithPath: dataFolderPath(isDefaultFolder: false)).appendingPathComponent(fileName)
}
static func userSymbolDataPath(_ mode: InputMode) -> String {
static func userSymbolDataURL(_ mode: InputMode) -> URL {
let fileName = (mode == InputMode.imeModeCHT) ? "usersymbolphrases-cht.txt" : "usersymbolphrases-chs.txt"
return URL(fileURLWithPath: dataFolderPath(isDefaultFolder: false)).appendingPathComponent(fileName).path
return URL(fileURLWithPath: dataFolderPath(isDefaultFolder: false)).appendingPathComponent(fileName)
}
static func userAssociatedPhrasesDataPath(_ mode: InputMode) -> String {
static func userAssociatesDataURL(_ mode: InputMode) -> URL {
let fileName = (mode == InputMode.imeModeCHT) ? "associatedPhrases-cht.txt" : "associatedPhrases-chs.txt"
return URL(fileURLWithPath: dataFolderPath(isDefaultFolder: false)).appendingPathComponent(fileName).path
return URL(fileURLWithPath: dataFolderPath(isDefaultFolder: false)).appendingPathComponent(fileName)
}
static func excludedPhrasesDataPath(_ mode: InputMode) -> String {
static func userFilteredDataURL(_ mode: InputMode) -> URL {
let fileName = (mode == InputMode.imeModeCHT) ? "exclude-phrases-cht.txt" : "exclude-phrases-chs.txt"
return URL(fileURLWithPath: dataFolderPath(isDefaultFolder: false)).appendingPathComponent(fileName).path
return URL(fileURLWithPath: dataFolderPath(isDefaultFolder: false)).appendingPathComponent(fileName)
}
static func phraseReplacementDataPath(_ mode: InputMode) -> String {
static func userReplacementsDataURL(_ mode: InputMode) -> URL {
let fileName = (mode == InputMode.imeModeCHT) ? "phrases-replacement-cht.txt" : "phrases-replacement-chs.txt"
return URL(fileURLWithPath: dataFolderPath(isDefaultFolder: false)).appendingPathComponent(fileName).path
return URL(fileURLWithPath: dataFolderPath(isDefaultFolder: false)).appendingPathComponent(fileName)
}
static func userSymbolNodeDataURL() -> URL {
let fileName = "symbols.dat"
return URL(fileURLWithPath: dataFolderPath(isDefaultFolder: false)).appendingPathComponent(fileName)
}
static func userOverrideModelDataURL(_ mode: InputMode) -> URL {
let fileName = (mode == InputMode.imeModeCHT) ? "override-model-data-cht.dat" : "override-model-data-chs.dat"
return URL(fileURLWithPath: dataFolderPath(isDefaultFolder: false)).appendingPathComponent(fileName)
}
// MARK: - 使
static func ensureFileExists(
_ filePath: String, populateWithTemplate templateBasename: String = "1145141919810",
_ fileURL: URL, populateWithTemplate templateBasename: String = "1145141919810",
extension ext: String = "txt"
) -> Bool {
let filePath = fileURL.path
if !FileManager.default.fileExists(atPath: filePath) {
let templateURL = Bundle.main.url(forResource: templateBasename, withExtension: ext)
var templateData = Data("".utf8)
@ -274,11 +290,14 @@ enum mgrLangModel {
if !userDataFolderExists {
return false
}
if !ensureFileExists(userPhrasesDataPath(mode))
|| !ensureFileExists(userAssociatedPhrasesDataPath(mode))
|| !ensureFileExists(excludedPhrasesDataPath(mode))
|| !ensureFileExists(phraseReplacementDataPath(mode))
|| !ensureFileExists(userSymbolDataPath(mode))
/// SymbolNode UserOverrideModel
///
///
if !ensureFileExists(userPhrasesDataURL(mode))
|| !ensureFileExists(userAssociatesDataURL(mode))
|| !ensureFileExists(userFilteredDataURL(mode))
|| !ensureFileExists(userReplacementsDataURL(mode))
|| !ensureFileExists(userSymbolDataURL(mode))
{
return false
}
@ -391,7 +410,7 @@ enum mgrLangModel {
return false
}
let path = areWeDeleting ? excludedPhrasesDataPath(mode) : userPhrasesDataPath(mode)
let theURL = areWeDeleting ? userFilteredDataURL(mode) : userPhrasesDataURL(mode)
if areWeDuplicating, !areWeDeleting {
// Do not use ASCII characters to comment here.
@ -400,7 +419,7 @@ enum mgrLangModel {
currentMarkedPhrase += "\t#𝙾𝚟𝚎𝚛𝚛𝚒𝚍𝚎"
}
if let writeFile = FileHandle(forUpdatingAtPath: path),
if let writeFile = FileHandle(forUpdatingAtPath: theURL.path),
let data = currentMarkedPhrase.data(using: .utf8),
let endl = "\n".data(using: .utf8)
{
@ -415,7 +434,7 @@ enum mgrLangModel {
// We enforce the format consolidation here, since the pragma header
// will let the UserPhraseLM bypasses the consolidating process on load.
if !vChewing.LMConsolidator.consolidate(path: path, pragma: false) {
if !vChewing.LMConsolidator.consolidate(path: theURL.path, pragma: false) {
return false
}
@ -428,4 +447,20 @@ enum mgrLangModel {
}
return false
}
static func saveUserOverrideModelData() {
gUserOverrideModelCHT.saveData(toURL: userOverrideModelDataURL(InputMode.imeModeCHT))
gUserOverrideModelCHS.saveData(toURL: userOverrideModelDataURL(InputMode.imeModeCHS))
}
static func removeUnigramsFromUserOverrideModel(_ mode: InputMode) {
switch mode {
case .imeModeCHS:
gUserOverrideModelCHT.bleachUnigrams()
case .imeModeCHT:
gUserOverrideModelCHS.bleachUnigrams()
case .imeModeNULL:
break
}
}
}

View File

@ -35,9 +35,9 @@ extension Megrez {
///
private var mutGrid: Grid = .init()
/// 使
private var mutLM: LanguageModel
private var mutLM: LanguageModelProtocol
///
///
public var maxBuildSpanLength: Int { mutGrid.maxBuildSpanLength }
///
public var joinSeparator: String = ""
@ -48,7 +48,7 @@ extension Megrez {
}
///
public var isEmpty: Bool { grid.isEmpty }
public var isEmpty: Bool { mutGrid.isEmpty }
///
public var grid: Grid { mutGrid }
@ -62,7 +62,7 @@ extension Megrez {
/// - lm: Megrez.LanguageModel
/// - length: 10
/// - separator:
public init(lm: LanguageModel, length: Int = 10, separator: String = "") {
public init(lm: LanguageModelProtocol, length: Int = 10, separator: String = "") {
mutLM = lm
mutGrid = .init(spanLength: abs(length)) //
joinSeparator = separator
@ -112,7 +112,7 @@ extension Megrez {
return true
}
///
/// X
///
///
///
@ -140,8 +140,8 @@ extension Megrez {
///
/// - Parameters:
/// - at:
/// - score: 0
/// - location:
/// - accumulatedScore: 0
/// - joinedPhrase: 使
/// - longPhrases: 使
public func walk(
@ -160,8 +160,8 @@ extension Megrez {
///
/// - Parameters:
/// - at:
/// - score: 0
/// - location:
/// - accumulatedScore: 0
/// - joinedPhrase: 使
/// - longPhrases: 使
public func reverseWalk(
@ -219,11 +219,9 @@ extension Megrez {
} else {
//
var longPhrases = [String]()
for theAnchor in nodes {
for theAnchor in nodes.lazy.filter({ $0.spanningLength > 1 }) {
guard let theNode = theAnchor.node else { continue }
if theAnchor.spanningLength > 1 {
longPhrases.append(theNode.currentKeyValue.value)
}
longPhrases.append(theNode.currentKeyValue.value)
}
longPhrases = longPhrases.stableSorted {
@ -249,10 +247,10 @@ extension Megrez {
}
var result: [NodeAnchor] = paths[0]
for neta in paths {
if neta.last!.accumulatedScore > result.last!.accumulatedScore {
result = neta
}
for neta in paths.lazy.filter({
$0.last!.accumulatedScore > result.last!.accumulatedScore
}) {
result = neta
}
return result
@ -267,29 +265,20 @@ extension Megrez {
for p in itrBegin..<itrEnd {
for q in 1..<maxBuildSpanLength {
if p + q > itrEnd {
break
}
if p + q > itrEnd { break }
let arrSlice = mutReadings[p..<(p + q)]
let combinedReading: String = join(slice: arrSlice, separator: joinSeparator)
if !mutGrid.hasMatchedNode(location: p, spanningLength: q, key: combinedReading) {
let unigrams: [Unigram] = mutLM.unigramsFor(key: combinedReading)
if !unigrams.isEmpty {
let n = Node(key: combinedReading, unigrams: unigrams)
mutGrid.insertNode(node: n, location: p, spanningLength: q)
}
}
if mutGrid.hasMatchedNode(location: p, spanningLength: q, key: combinedReading) { continue }
let unigrams: [Unigram] = mutLM.unigramsFor(key: combinedReading)
if unigrams.isEmpty { continue }
let n = Node(key: combinedReading, unigrams: unigrams)
mutGrid.insertNode(node: n, location: p, spanningLength: q)
}
}
}
private func join(slice arrSlice: ArraySlice<String>, separator: String) -> String {
var arrResult: [String] = []
for value in arrSlice {
arrResult.append(value)
}
return arrResult.joined(separator: separator)
arrSlice.joined(separator: separator)
}
}
}
@ -303,7 +292,7 @@ extension Sequence {
///
/// - Parameter areInIncreasingOrder: Return nil when two element are equal.
/// - Returns: The sorted collection.
func stableSorted(
fileprivate func stableSorted(
by areInIncreasingOrder: (Element, Element) throws -> Bool
)
rethrows -> [Element]

View File

@ -32,15 +32,16 @@ extension Megrez {
///
private var mutMaxBuildSpanLength = 10
///
///
public var maxBuildSpanLength: Int { mutMaxBuildSpanLength }
///
var width: Int { mutSpans.count }
///
public var width: Int { mutSpans.count }
///
var isEmpty: Bool { mutSpans.isEmpty }
///
public var isEmpty: Bool { mutSpans.isEmpty }
///
public init(spanLength: Int = 10) {
mutMaxBuildSpanLength = spanLength
mutSpans = [Megrez.Span]()
@ -90,11 +91,10 @@ extension Megrez {
public func expandGridByOneAt(location: Int) {
let location = abs(location) //
mutSpans.insert(Span(), at: location)
if location != 0, location != mutSpans.count {
for i in 0..<location {
// zaps overlapping spans
mutSpans[i].removeNodeOfLengthGreaterThan(location - i)
}
if location == 0 || location == mutSpans.count { return }
for i in 0..<location {
// zaps overlapping spans
mutSpans[i].removeNodeOfLengthGreaterThan(location - i)
}
}
@ -120,18 +120,18 @@ extension Megrez {
public func nodesBeginningAt(location: Int) -> [NodeAnchor] {
let location = abs(location) //
var results = [NodeAnchor]()
if location < mutSpans.count { // mutSpans
let span = mutSpans[location]
for i in 1...maxBuildSpanLength {
if let np = span.node(length: i) {
results.append(
NodeAnchor(
node: np,
location: location,
spanningLength: i
)
if location >= mutSpans.count { return results }
// mutSpans location 0
let span = mutSpans[location]
for i in 1...maxBuildSpanLength {
if let np = span.node(length: i) {
results.append(
.init(
node: np,
location: location,
spanningLength: i
)
}
)
}
}
return results
@ -143,20 +143,18 @@ extension Megrez {
public func nodesEndingAt(location: Int) -> [NodeAnchor] {
let location = abs(location) //
var results = [NodeAnchor]()
if !mutSpans.isEmpty, location <= mutSpans.count {
for i in 0..<location {
let span = mutSpans[i]
if i + span.maximumLength >= location {
if let np = span.node(length: location - i) {
results.append(
NodeAnchor(
node: np,
location: i,
spanningLength: location - i
)
)
}
}
if mutSpans.isEmpty || location > mutSpans.count { return results }
for i in 0..<location {
let span = mutSpans[i]
if i + span.maximumLength < location { continue }
if let np = span.node(length: location - i) {
results.append(
.init(
node: np,
location: i,
spanningLength: location - i
)
)
}
}
return results
@ -168,24 +166,20 @@ extension Megrez {
public func nodesCrossingOrEndingAt(location: Int) -> [NodeAnchor] {
let location = abs(location) //
var results = [NodeAnchor]()
if !mutSpans.isEmpty, location <= mutSpans.count {
for i in 0..<location {
let span = mutSpans[i]
if i + span.maximumLength >= location {
for j in 1...span.maximumLength {
if i + j < location {
continue
}
if let np = span.node(length: j) {
results.append(
NodeAnchor(
node: np,
location: i,
spanningLength: location - i
)
)
}
}
if mutSpans.isEmpty || location > mutSpans.count { return results }
for i in 0..<location {
let span = mutSpans[i]
if i + span.maximumLength < location { continue }
for j in 1...span.maximumLength {
if i + j < location { continue }
if let np = span.node(length: j) {
results.append(
.init(
node: np,
location: i,
spanningLength: location - i
)
)
}
}
}
@ -193,6 +187,8 @@ extension Megrez {
}
///
///
///
/// - Parameters:
/// - location:
/// - value:
@ -245,6 +241,7 @@ extension Megrez {
// MARK: - DumpDOT-related functions.
extension Megrez.Grid {
/// GraphViz
public var dumpDOT: String {
var strOutput = "digraph {\ngraph [ rankdir=LR ];\nBOS;\n"
for (p, span) in mutSpans.enumerated() {

View File

@ -32,7 +32,7 @@ extension Megrez {
private var mutMaximumLength: Int = 0
///
var maximumLength: Int {
public var maximumLength: Int {
mutMaximumLength
}
@ -77,7 +77,8 @@ extension Megrez {
/// - Parameters:
/// - length:
public func node(length: Int) -> Node? {
mutLengthNodeMap[abs(length)] //
// Abs()
mutLengthNodeMap.keys.contains(abs(length)) ? mutLengthNodeMap[abs(length)] : nil
}
}
}

View File

@ -26,8 +26,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
extension Megrez {
///
public class Node {
///
private let mutLM: LanguageModel = .init()
///
private var mutKey: String = ""
///
@ -37,11 +35,11 @@ extension Megrez {
///
private var mutBigrams: [Bigram]
///
private var mutCandidates: [KeyValuePair] = []
private var mutCandidates: [KeyValuePaired] = []
/// 調
private var mutValueUnigramIndexMap: [String: Int] = [:]
///
private var mutPrecedingBigramMap: [KeyValuePair: [Megrez.Bigram]] = [:]
private var mutPrecedingBigramMap: [KeyValuePaired: [Megrez.Bigram]] = [:]
///
private var mutCandidateFixed: Bool = false
///
@ -54,21 +52,21 @@ extension Megrez {
}
///
var candidates: [KeyValuePair] { mutCandidates }
public var candidates: [KeyValuePaired] { mutCandidates }
///
var isCandidateFixed: Bool { mutCandidateFixed }
public var isCandidateFixed: Bool { mutCandidateFixed }
///
var key: String { mutKey }
public var key: String { mutKey }
///
var score: Double { mutScore }
public var score: Double { mutScore }
///
var currentKeyValue: KeyValuePair {
mutSelectedUnigramIndex >= mutUnigrams.count ? KeyValuePair() : mutCandidates[mutSelectedUnigramIndex]
public var currentKeyValue: KeyValuePaired {
mutSelectedUnigramIndex >= mutUnigrams.count ? KeyValuePaired() : mutCandidates[mutSelectedUnigramIndex]
}
///
var highestUnigramScore: Double { mutUnigrams.isEmpty ? 0.0 : mutUnigrams[0].score }
public var highestUnigramScore: Double { mutUnigrams.isEmpty ? 0.0 : mutUnigrams[0].score }
///
/// - Parameters:
@ -93,7 +91,9 @@ extension Megrez {
mutCandidates.append(gram.keyValue)
}
for gram in bigrams {
for gram in bigrams.lazy.filter({ [self] in
mutPrecedingBigramMap.keys.contains($0.precedingKeyValue)
}) {
mutPrecedingBigramMap[gram.precedingKeyValue]?.append(gram)
}
}
@ -101,19 +101,18 @@ extension Megrez {
///
/// - Parameters:
/// - precedingKeyValues:
public func primeNodeWith(precedingKeyValues: [KeyValuePair]) {
public func primeNodeWith(precedingKeyValues: [KeyValuePaired]) {
var newIndex = mutSelectedUnigramIndex
var max = mutScore
if !isCandidateFixed {
for neta in precedingKeyValues {
let bigrams = mutPrecedingBigramMap[neta] ?? []
for bigram in bigrams {
guard bigram.score > max else { continue }
if let valRetrieved = mutValueUnigramIndexMap[bigram.keyValue.value] {
newIndex = valRetrieved as Int
max = bigram.score
}
for bigram in bigrams.lazy.filter({ [self] in
$0.score > max && mutValueUnigramIndexMap.keys.contains($0.keyValue.value)
}) {
newIndex = mutValueUnigramIndexMap[bigram.keyValue.value] ?? newIndex
max = bigram.score
}
}
}
@ -156,10 +155,8 @@ extension Megrez {
/// - Parameters:
/// - candidate:
public func scoreFor(candidate: String) -> Double {
for unigram in mutUnigrams {
if unigram.keyValue.value == candidate {
return unigram.score
}
for unigram in mutUnigrams.lazy.filter({ $0.keyValue.value == candidate }) {
return unigram.score
}
return 0.0
}

View File

@ -23,24 +23,35 @@ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
public protocol LanguageModelProtocol {
///
func unigramsFor(key: String) -> [Megrez.Unigram]
///
func bigramsForKeys(precedingKey: String, key: String) -> [Megrez.Bigram]
///
func hasUnigramsFor(key: String) -> Bool
}
extension Megrez {
/// 使
open class LanguageModel {
open class LanguageModel: LanguageModelProtocol {
public init() {}
// Swift
///
///
open func unigramsFor(key: String) -> [Megrez.Unigram] {
key.isEmpty ? [Megrez.Unigram]() : [Megrez.Unigram]()
}
///
///
open func bigramsForKeys(precedingKey: String, key: String) -> [Megrez.Bigram] {
precedingKey == key ? [Megrez.Bigram]() : [Megrez.Bigram]()
}
///
///
open func hasUnigramsFor(key: String) -> Bool {
key.count != 0
}

View File

@ -27,9 +27,9 @@ extension Megrez {
///
@frozen public struct Bigram: Equatable, CustomStringConvertible {
///
public var keyValue: KeyValuePair
public var keyValue: KeyValuePaired
///
public var precedingKeyValue: KeyValuePair
public var precedingKeyValue: KeyValuePaired
///
public var score: Double
///
@ -42,7 +42,7 @@ extension Megrez {
/// - precedingKeyValue:
/// - keyValue:
/// - score:
public init(precedingKeyValue: KeyValuePair, keyValue: KeyValuePair, score: Double) {
public init(precedingKeyValue: KeyValuePaired, keyValue: KeyValuePaired, score: Double) {
self.keyValue = keyValue
self.precedingKeyValue = precedingKeyValue
self.score = score

View File

@ -27,7 +27,7 @@ extension Megrez {
///
@frozen public struct Unigram: Equatable, CustomStringConvertible {
///
public var keyValue: KeyValuePair
public var keyValue: KeyValuePaired
///
public var score: Double
///
@ -39,7 +39,7 @@ extension Megrez {
/// - Parameters:
/// - keyValue:
/// - score:
public init(keyValue: KeyValuePair, score: Double) {
public init(keyValue: KeyValuePaired, score: Double) {
self.keyValue = keyValue
self.score = score
}
@ -49,11 +49,6 @@ extension Megrez {
hasher.combine(score)
}
//
public static func compareScore(a: Unigram, b: Unigram) -> Bool {
a.score > b.score
}
public static func == (lhs: Unigram, rhs: Unigram) -> Bool {
lhs.keyValue == rhs.keyValue && lhs.score == rhs.score
}

View File

@ -25,17 +25,19 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
extension Megrez {
///
@frozen public struct KeyValuePair: Equatable, Hashable, Comparable, CustomStringConvertible {
@frozen public struct KeyValuePaired: Equatable, Hashable, Comparable, CustomStringConvertible {
///
public var key: String
///
public var value: String
///
public var description: String {
"(" + key + "," + value + ")"
}
public var description: String { "(" + key + "," + value + ")" }
/// false
public var isValid: Bool { !key.isEmpty && !value.isEmpty }
/// ()
public var toNGramKey: String { !isValid ? "()" : "(" + key + "," + value + ")" }
///
///
/// - Parameters:
/// - key:
/// - value:
@ -49,23 +51,23 @@ extension Megrez {
hasher.combine(value)
}
public static func == (lhs: KeyValuePair, rhs: KeyValuePair) -> Bool {
public static func == (lhs: KeyValuePaired, rhs: KeyValuePaired) -> Bool {
lhs.key.count == rhs.key.count && lhs.value == rhs.value
}
public static func < (lhs: KeyValuePair, rhs: KeyValuePair) -> Bool {
public static func < (lhs: KeyValuePaired, rhs: KeyValuePaired) -> Bool {
(lhs.key.count < rhs.key.count) || (lhs.key.count == rhs.key.count && lhs.value < rhs.value)
}
public static func > (lhs: KeyValuePair, rhs: KeyValuePair) -> Bool {
public static func > (lhs: KeyValuePaired, rhs: KeyValuePaired) -> Bool {
(lhs.key.count > rhs.key.count) || (lhs.key.count == rhs.key.count && lhs.value > rhs.value)
}
public static func <= (lhs: KeyValuePair, rhs: KeyValuePair) -> Bool {
public static func <= (lhs: KeyValuePaired, rhs: KeyValuePaired) -> Bool {
(lhs.key.count <= rhs.key.count) || (lhs.key.count == rhs.key.count && lhs.value <= rhs.value)
}
public static func >= (lhs: KeyValuePair, rhs: KeyValuePair) -> Bool {
public static func >= (lhs: KeyValuePaired, rhs: KeyValuePaired) -> Bool {
(lhs.key.count >= rhs.key.count) || (lhs.key.count == rhs.key.count && lhs.value >= rhs.value)
}
}

View File

@ -25,7 +25,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import AVFoundation
import Foundation
public class clsSFX {
public enum clsSFX {
static func beep() {
let filePath = Bundle.main.path(forResource: mgrPrefs.shouldNotFartInLieuOfBeep ? "Beep" : "Fart", ofType: "m4a")!
let fileURL = URL(fileURLWithPath: filePath)
@ -33,4 +33,15 @@ public class clsSFX {
AudioServicesCreateSystemSoundID(fileURL as CFURL, &soundID)
AudioServicesPlaySystemSound(soundID)
}
static func beep(count: Int = 1) {
if count <= 1 {
clsSFX.beep()
return
}
for _ in 0...count {
clsSFX.beep()
usleep(500_000)
}
}
}

View File

@ -62,6 +62,7 @@
"Loading CHS Core Dict..." = "Loading CHS Core Dict...";
"Loading CHT Core Dict..." = "Loading CHT Core Dict...";
"Core Dict loading complete." = "Core Dict loading complete.";
"Optimize Memorized Phrases" = "Optimize Memorized Phrases";
// The followings are the category names used in the Symbol menu.
"catCommonSymbols" = "CommonSymbols";
@ -88,6 +89,7 @@
"Apple Chewing - Dachen" = "Apple Chewing - Dachen";
"Apple Chewing - Eten Traditional" = "Apple Chewing - Eten Traditional";
"Apple Dynamic Bopomofo Basic Keyboard Layouts (Dachen & Eten Traditional) must match the Dachen parser in order to be functional." = "Apple Dynamic Bopomofo Basic Keyboard Layouts (Dachen & Eten Traditional) must match the Dachen parser in order to be functional.";
"Applying typing suggestions from half-life user override model" = "Applying typing suggestions from half-life user override model";
"at the rear of the phrase (like Microsoft New Phonetic)" = "at the rear of the phrase (like Microsoft New Phonetic)";
"Auto-convert traditional Chinese glyphs to JIS Shinjitai characters" = "Auto-convert traditional Chinese glyphs to JIS Shinjitai characters";
"Auto-convert traditional Chinese glyphs to KangXi characters" = "Auto-convert traditional Chinese glyphs to KangXi characters";
@ -113,7 +115,7 @@
"Debug Mode" = "Debug Mode";
"Dictionary" = "Dictionary";
"Emulating select-candidate-per-character mode" = "Emulating select-candidate-per-character mode";
"Enable CNS11643 Support (2022-04-27)" = "Enable CNS11643 Support (2022-04-27)";
"Enable CNS11643 Support (2022-06-15)" = "Enable CNS11643 Support (2022-06-15)";
"Enable Space key for calling candidate window" = "Enable Space key for calling candidate window";
"Enable symbol input support (incl. certain emoji symbols)" = "Enable symbol input support (incl. certain emoji symbols)";
"English" = "English";

View File

@ -62,6 +62,7 @@
"Loading CHS Core Dict..." = "Loading CHS Core Dict...";
"Loading CHT Core Dict..." = "Loading CHT Core Dict...";
"Core Dict loading complete." = "Core Dict loading complete.";
"Optimize Memorized Phrases" = "Optimize Memorized Phrases";
// The followings are the category names used in the Symbol menu.
"catCommonSymbols" = "CommonSymbols";
@ -88,6 +89,7 @@
"Apple Chewing - Dachen" = "Apple Chewing - Dachen";
"Apple Chewing - Eten Traditional" = "Apple Chewing - Eten Traditional";
"Apple Dynamic Bopomofo Basic Keyboard Layouts (Dachen & Eten Traditional) must match the Dachen parser in order to be functional." = "Apple Dynamic Bopomofo Basic Keyboard Layouts (Dachen & Eten Traditional) must match the Dachen parser in order to be functional.";
"Applying typing suggestions from half-life user override model" = "Applying typing suggestions from half-life user override model";
"at the rear of the phrase (like Microsoft New Phonetic)" = "at the rear of the phrase (like Microsoft New Phonetic)";
"Auto-convert traditional Chinese glyphs to JIS Shinjitai characters" = "Auto-convert traditional Chinese glyphs to JIS Shinjitai characters";
"Auto-convert traditional Chinese glyphs to KangXi characters" = "Auto-convert traditional Chinese glyphs to KangXi characters";
@ -113,7 +115,7 @@
"Debug Mode" = "Debug Mode";
"Dictionary" = "Dictionary";
"Emulating select-candidate-per-character mode" = "Emulating select-candidate-per-character mode";
"Enable CNS11643 Support (2022-04-27)" = "Enable CNS11643 Support (2022-04-27)";
"Enable CNS11643 Support (2022-06-15)" = "Enable CNS11643 Support (2022-06-15)";
"Enable Space key for calling candidate window" = "Enable Space key for calling candidate window";
"Enable symbol input support (incl. certain emoji symbols)" = "Enable symbol input support (incl. certain emoji symbols)";
"English" = "English";

View File

@ -62,6 +62,7 @@
"Loading CHS Core Dict..." = "簡体中国語核心辞書読込中…";
"Loading CHT Core Dict..." = "繁体中国語核心辞書読込中…";
"Core Dict loading complete." = "核心辞書読込完了";
"Optimize Memorized Phrases" = "臨時記憶資料を整う";
// The followings are the category names used in the Symbol menu.
"catCommonSymbols" = "常用";
@ -88,6 +89,7 @@
"Apple Chewing - Dachen" = "Apple 大千注音キーボード";
"Apple Chewing - Eten Traditional" = "Apple 倚天傳統キーボード";
"Apple Dynamic Bopomofo Basic Keyboard Layouts (Dachen & Eten Traditional) must match the Dachen parser in order to be functional." = "Apple 動態注音キーボード (大千と倚天伝統) を使うには、共通語分析器の配列を大千と設定すべきである。";
"Applying typing suggestions from half-life user override model" = "入力中で臨時記憶モジュールからお薦めの候補を自動的に選ぶ";
"at the rear of the phrase (like Microsoft New Phonetic)" = "単語の後で // Microsoft 新注音入力のやり方";
"Auto-convert traditional Chinese glyphs to JIS Shinjitai characters" = "入力した繁体字を日文 JIS 新字体と自動変換";
"Auto-convert traditional Chinese glyphs to KangXi characters" = "入力した繁体字を康熙字体と自動変換";
@ -113,7 +115,7 @@
"Debug Mode" = "欠陥辿着モード";
"Dictionary" = "辞書設定";
"Emulating select-candidate-per-character mode" = "漢字1つづつ全候補選択入力モード";
"Enable CNS11643 Support (2022-04-27)" = "全字庫モード // 入力可能な漢字数を倍増す (2022-04-27)";
"Enable CNS11643 Support (2022-06-15)" = "全字庫モード // 入力可能な漢字数を倍増す (2022-06-15)";
"Enable Space key for calling candidate window" = "Space キーで入力候補を呼び出す";
"Enable symbol input support (incl. certain emoji symbols)" = "僅かなる絵文字も含む符号入力サポートを起用";
"English" = "英語";

View File

@ -62,6 +62,7 @@
"Loading CHS Core Dict..." = "载入简体中文核心辞典…";
"Loading CHT Core Dict..." = "载入繁体中文核心辞典…";
"Core Dict loading complete." = "核心辞典载入完毕";
"Optimize Memorized Phrases" = "精简临时记忆语汇资料";
// The followings are the category names used in the Symbol menu.
"catCommonSymbols" = "常用";
@ -88,6 +89,7 @@
"Apple Chewing - Dachen" = "Apple 大千注音键盘排列";
"Apple Chewing - Eten Traditional" = "Apple 倚天传统键盘排列";
"Apple Dynamic Bopomofo Basic Keyboard Layouts (Dachen & Eten Traditional) must match the Dachen parser in order to be functional." = "Apple 动态注音键盘布局(大千与倚天)要求普通话/国音分析器得配置为大千排列。";
"Applying typing suggestions from half-life user override model" = "在敲字时自动套用来自半衰记忆模组的建议";
"at the rear of the phrase (like Microsoft New Phonetic)" = "将游标置于词语后方 // Windows 微软新注音风格";
"Auto-convert traditional Chinese glyphs to JIS Shinjitai characters" = "自动将繁体中文字转为日文 JIS 新字体";
"Auto-convert traditional Chinese glyphs to KangXi characters" = "自动将繁体中文字转为康熙正体字";
@ -113,7 +115,7 @@
"Debug Mode" = "侦错模式";
"Dictionary" = "辞典";
"Emulating select-candidate-per-character mode" = "模拟 90 年代前期注音逐字选字输入风格";
"Enable CNS11643 Support (2022-04-27)" = "启用 CNS11643 全字库支援 (2022-04-27)";
"Enable CNS11643 Support (2022-06-15)" = "启用 CNS11643 全字库支援 (2022-06-15)";
"Enable Space key for calling candidate window" = "敲空格键以呼出候选字窗";
"Enable symbol input support (incl. certain emoji symbols)" = "启用包括少许绘文字在内的符号输入支援";
"English" = "英语";

View File

@ -62,6 +62,7 @@
"Loading CHS Core Dict..." = "載入簡體中文核心辭典…";
"Loading CHT Core Dict..." = "載入繁體中文核心辭典…";
"Core Dict loading complete." = "核心辭典載入完畢";
"Optimize Memorized Phrases" = "精簡臨時記憶語彙資料";
// The followings are the category names used in the Symbol menu.
"catCommonSymbols" = "常用";
@ -88,6 +89,7 @@
"Apple Chewing - Dachen" = "Apple 大千注音鍵盤佈局";
"Apple Chewing - Eten Traditional" = "Apple 倚天傳統鍵盤佈局";
"Apple Dynamic Bopomofo Basic Keyboard Layouts (Dachen & Eten Traditional) must match the Dachen parser in order to be functional." = "Apple 動態注音鍵盤佈局(大千與倚天)要求普通話/國音分析器得配置為大千排列。";
"Applying typing suggestions from half-life user override model" = "在敲字時自動套用來自半衰記憶模組的建議";
"at the rear of the phrase (like Microsoft New Phonetic)" = "將游標置於詞語後方 // Windows 微軟新注音風格";
"Auto-convert traditional Chinese glyphs to JIS Shinjitai characters" = "自動將繁體中文字轉為日文 JIS 新字體";
"Auto-convert traditional Chinese glyphs to KangXi characters" = "自動將繁體中文字轉為康熙正體字";
@ -113,7 +115,7 @@
"Debug Mode" = "偵錯模式";
"Dictionary" = "辭典";
"Emulating select-candidate-per-character mode" = "模擬 90 年代前期注音逐字選字輸入風格";
"Enable CNS11643 Support (2022-04-27)" = "啟用 CNS11643 全字庫支援 (2022-04-27)";
"Enable CNS11643 Support (2022-06-15)" = "啟用 CNS11643 全字庫支援 (2022-06-15)";
"Enable Space key for calling candidate window" = "敲空格鍵以呼出候選字窗";
"Enable symbol input support (incl. certain emoji symbols)" = "啟用包括少許繪文字在內的符號輸入支援";
"English" = "英語";

View File

@ -74,6 +74,11 @@ private class vwrCandidateUniversal: NSView {
@objc(setKeyLabels:displayedCandidates:)
func set(keyLabels labels: [String], displayedCandidates candidates: [String]) {
let candidates = candidates.map { theCandidate -> String in
let theConverted = IME.kanjiConversionIfRequired(theCandidate)
return (theCandidate == theConverted) ? theCandidate : "\(theConverted)(\(theCandidate))"
}
let count = min(labels.count, candidates.count)
keyLabels = Array(labels[0..<count])
displayedCandidates = Array(candidates[0..<count])
@ -209,7 +214,9 @@ private class vwrCandidateUniversal: NSView {
}
case InputMode.imeModeCHT:
if #available(macOS 12.0, *) {
activeCandidateAttr[.languageIdentifier] = "zh-Hant" as AnyObject
activeCandidateAttr[.languageIdentifier] =
(mgrPrefs.shiftJISShinjitaiOutputEnabled || mgrPrefs.chineseConversionEnabled)
? "ja" as AnyObject : "zh-Hant" as AnyObject
}
default:
break
@ -279,7 +286,9 @@ private class vwrCandidateUniversal: NSView {
}
case InputMode.imeModeCHT:
if #available(macOS 12.0, *) {
activeCandidateAttr[.languageIdentifier] = "zh-Hant" as AnyObject
activeCandidateAttr[.languageIdentifier] =
(mgrPrefs.shiftJISShinjitaiOutputEnabled || mgrPrefs.chineseConversionEnabled)
? "ja" as AnyObject : "zh-Hant" as AnyObject
}
default:
break

View File

@ -37,6 +37,8 @@ struct suiPrefPaneDictionary: View {
forKey: UserDef.kSymbolInputEnabled)
@State private var selAllowBoostingSingleKanjiAsUserPhrase: Bool = UserDefaults.standard.bool(
forKey: UserDef.kAllowBoostingSingleKanjiAsUserPhrase)
@State private var selFetchSuggestionsFromUserOverrideModel: Bool = UserDefaults.standard.bool(
forKey: UserDef.kFetchSuggestionsFromUserOverrideModel)
private let contentWidth: Double = {
switch mgrPrefs.appleLanguages[0] {
case "ja":
@ -116,7 +118,7 @@ struct suiPrefPaneDictionary: View {
}
Divider()
Toggle(
LocalizedStringKey("Enable CNS11643 Support (2022-04-27)"),
LocalizedStringKey("Enable CNS11643 Support (2022-06-15)"),
isOn: $selEnableCNS11643
)
.onChange(of: selEnableCNS11643) { value in
@ -138,6 +140,13 @@ struct suiPrefPaneDictionary: View {
.onChange(of: selAllowBoostingSingleKanjiAsUserPhrase) { value in
mgrPrefs.allowBoostingSingleKanjiAsUserPhrase = value
}
Toggle(
LocalizedStringKey("Applying typing suggestions from half-life user override model"),
isOn: $selFetchSuggestionsFromUserOverrideModel
)
.onChange(of: selFetchSuggestionsFromUserOverrideModel) { value in
mgrPrefs.fetchSuggestionsFromUserOverrideModel = value
}
}
}
}

View File

@ -65,7 +65,7 @@ struct suiPrefPaneExperience: View {
ComboBox(items: mgrPrefs.suggestedCandidateKeys, text: $selSelectionKeys).frame(width: 180).onChange(
of: selSelectionKeys
) { value in
let keys: String = (value.trimmingCharacters(in: .whitespacesAndNewlines) as String).charDeDuplicate
let keys: String = value.trimmingCharacters(in: .whitespacesAndNewlines).deduplicate
do {
try mgrPrefs.validate(candidateKeys: keys)
mgrPrefs.candidateKeys = keys

View File

@ -228,7 +228,7 @@ class ctlPrefWindow: NSWindowController {
let keys = (sender as AnyObject).stringValue?.trimmingCharacters(
in: .whitespacesAndNewlines
)
.charDeDuplicate
.deduplicate
else {
return
}

View File

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="20037" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="20037"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
@ -681,9 +680,9 @@
</connections>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="p7V-IN-OTr">
<rect key="frame" x="19" y="296.5" width="406" height="17"/>
<rect key="frame" x="19" y="297.5" width="406" height="17"/>
<constraints>
<constraint firstAttribute="height" constant="16" id="Ieq-hl-01l"/>
<constraint firstAttribute="height" constant="16" id="reU-bw-htf"/>
</constraints>
<buttonCell key="cell" type="check" title="Automatically reload user data files if changes detected" bezelStyle="regularSquare" imagePosition="left" controlSize="small" state="on" inset="2" id="f8i-69-zxm">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
@ -694,9 +693,9 @@
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="O4B-Z2-XYi">
<rect key="frame" x="19" y="274.5" width="406" height="17"/>
<rect key="frame" x="19" y="275.5" width="406" height="17"/>
<constraints>
<constraint firstAttribute="height" constant="16" id="2gi-B5-vjz"/>
<constraint firstAttribute="height" constant="16" id="UxA-rj-D5M"/>
</constraints>
<buttonCell key="cell" type="check" title="Enable symbol input support (incl. certain emoji symbols)" bezelStyle="regularSquare" imagePosition="left" controlSize="small" state="on" inset="2" id="hSv-LJ-Cq3" userLabel="chkSymbolEnabled">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
@ -736,11 +735,11 @@
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Yaj-QY-7xV" userLabel="chkCNSSupport">
<rect key="frame" x="19" y="252.5" width="406" height="17"/>
<rect key="frame" x="19" y="253.5" width="406" height="17"/>
<constraints>
<constraint firstAttribute="height" constant="16" id="vin-Cj-dDq"/>
<constraint firstAttribute="height" constant="16" id="guU-8E-HAX"/>
</constraints>
<buttonCell key="cell" type="check" title="Enable CNS11643 Support (2022-04-27)" bezelStyle="regularSquare" imagePosition="left" controlSize="small" inset="2" id="W24-T4-cg0">
<buttonCell key="cell" type="check" title="Enable CNS11643 Support (2022-06-15)" bezelStyle="regularSquare" imagePosition="left" controlSize="small" inset="2" id="W24-T4-cg0">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="cellTitle"/>
</buttonCell>
@ -750,9 +749,9 @@
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Hyc-Nw-dET">
<rect key="frame" x="19" y="230.5" width="406" height="17"/>
<rect key="frame" x="19" y="231.5" width="406" height="17"/>
<constraints>
<constraint firstAttribute="height" constant="16" id="drz-HW-cX1"/>
<constraint firstAttribute="height" constant="16" id="B9v-pe-shJ"/>
</constraints>
<buttonCell key="cell" type="check" title="Allow boosting / excluding a candidate of single kanji" bezelStyle="regularSquare" imagePosition="left" controlSize="small" state="on" inset="2" id="chkAllowBoostingSingleKanjiAsUserPhrase" userLabel="chkAllowBoostingSingleKanjiAsUserPhrase">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
@ -762,29 +761,45 @@
<binding destination="32" name="value" keyPath="values.AllowBoostingSingleKanjiAsUserPhrase" id="jiw-1V-C7x"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="D46-A7D-E0E">
<rect key="frame" x="19" y="211.5" width="406" height="17"/>
<constraints>
<constraint firstAttribute="height" constant="16" id="zkb-Rd-IZG"/>
</constraints>
<buttonCell key="cell" type="check" title="Applying typing suggestions from half-life user override model" bezelStyle="regularSquare" imagePosition="left" controlSize="small" state="on" inset="2" id="chkFetchSuggestionsFromUserOverrideModel" userLabel="chkFetchSuggestionsFromUserOverrideModel">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="cellTitle"/>
</buttonCell>
<connections>
<binding destination="32" name="value" keyPath="values.FetchSuggestionsFromUserOverrideModel" id="F53-CE5-A2E"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="s7t-Kk-EPu" firstAttribute="bottom" secondItem="MPN-np-SbT" secondAttribute="bottom" id="0Fo-ya-hQ9"/>
<constraint firstItem="p7V-IN-OTr" firstAttribute="leading" secondItem="O4B-Z2-XYi" secondAttribute="leading" id="6GG-yw-MYi"/>
<constraint firstItem="O4B-Z2-XYi" firstAttribute="top" secondItem="p7V-IN-OTr" secondAttribute="bottom" constant="6" symbolic="YES" id="6H0-cT-DDa"/>
<constraint firstItem="O4B-Z2-XYi" firstAttribute="leading" secondItem="Yaj-QY-7xV" secondAttribute="leading" id="0Rn-L2-gTb"/>
<constraint firstItem="Yaj-QY-7xV" firstAttribute="trailing" secondItem="Hyc-Nw-dET" secondAttribute="trailing" id="4S6-WJ-PoV"/>
<constraint firstItem="D46-A7D-E0E" firstAttribute="top" secondItem="Hyc-Nw-dET" secondAttribute="bottom" constant="4" id="4TT-pB-2Xi"/>
<constraint firstAttribute="trailing" secondItem="FUV-qx-xkC" secondAttribute="trailing" constant="20" symbolic="YES" id="6QR-tj-5cH"/>
<constraint firstItem="Yaj-QY-7xV" firstAttribute="trailing" secondItem="Hyc-Nw-dET" secondAttribute="trailing" id="6ty-e2-2yl"/>
<constraint firstItem="p7V-IN-OTr" firstAttribute="leading" secondItem="s7t-Kk-EPu" secondAttribute="leading" id="7KG-BW-e6E"/>
<constraint firstItem="s7t-Kk-EPu" firstAttribute="leading" secondItem="MPN-np-SbT" secondAttribute="trailing" constant="-376" id="9at-E8-Bt1"/>
<constraint firstItem="O4B-Z2-XYi" firstAttribute="trailing" secondItem="Yaj-QY-7xV" secondAttribute="trailing" id="BP1-pw-lUK"/>
<constraint firstItem="FUV-qx-xkC" firstAttribute="top" secondItem="cy4-aV-hhk" secondAttribute="top" constant="15" id="BZE-dD-V2R"/>
<constraint firstItem="MPN-np-SbT" firstAttribute="top" secondItem="FUV-qx-xkC" secondAttribute="bottom" constant="14" id="Bp9-2u-f9E"/>
<constraint firstItem="Yaj-QY-7xV" firstAttribute="leading" secondItem="Hyc-Nw-dET" secondAttribute="leading" id="DJV-E7-Wgv"/>
<constraint firstItem="Yaj-QY-7xV" firstAttribute="top" secondItem="O4B-Z2-XYi" secondAttribute="bottom" constant="6" symbolic="YES" id="F6l-lB-OT5"/>
<constraint firstItem="FUV-qx-xkC" firstAttribute="leading" secondItem="cy4-aV-hhk" secondAttribute="leading" constant="20" symbolic="YES" id="Hy2-ZC-cvb"/>
<constraint firstItem="Hyc-Nw-dET" firstAttribute="top" secondItem="Yaj-QY-7xV" secondAttribute="bottom" constant="6" symbolic="YES" id="OCz-SW-av6"/>
<constraint firstItem="Hyc-Nw-dET" firstAttribute="top" secondItem="Yaj-QY-7xV" secondAttribute="bottom" constant="6" symbolic="YES" id="KH4-XR-ok1"/>
<constraint firstItem="Hyc-Nw-dET" firstAttribute="leading" secondItem="D46-A7D-E0E" secondAttribute="leading" id="LER-hN-AwW"/>
<constraint firstItem="s7t-Kk-EPu" firstAttribute="firstBaseline" secondItem="MPN-np-SbT" secondAttribute="baseline" id="OYH-gA-WcA"/>
<constraint firstItem="p7V-IN-OTr" firstAttribute="trailing" secondItem="jXe-xz-9Sd" secondAttribute="trailing" id="QQv-cA-dO9"/>
<constraint firstItem="p7V-IN-OTr" firstAttribute="leading" secondItem="O4B-Z2-XYi" secondAttribute="leading" id="W70-cE-S0L"/>
<constraint firstItem="FUV-qx-xkC" firstAttribute="leading" secondItem="MPN-np-SbT" secondAttribute="leading" constant="-346" id="Zet-wH-kmC"/>
<constraint firstItem="O4B-Z2-XYi" firstAttribute="leading" secondItem="Yaj-QY-7xV" secondAttribute="leading" id="aI8-ll-ybr"/>
<constraint firstItem="Hyc-Nw-dET" firstAttribute="trailing" secondItem="D46-A7D-E0E" secondAttribute="trailing" id="byp-fz-9tg"/>
<constraint firstItem="p7V-IN-OTr" firstAttribute="trailing" secondItem="O4B-Z2-XYi" secondAttribute="trailing" id="cWy-Ks-WmK"/>
<constraint firstItem="jXe-xz-9Sd" firstAttribute="leading" secondItem="MPN-np-SbT" secondAttribute="trailing" constant="-1" id="cYQ-Rx-tuG"/>
<constraint firstItem="p7V-IN-OTr" firstAttribute="top" secondItem="s7t-Kk-EPu" secondAttribute="bottom" constant="14.5" id="hJ9-p8-clP"/>
<constraint firstItem="O4B-Z2-XYi" firstAttribute="trailing" secondItem="Yaj-QY-7xV" secondAttribute="trailing" id="pZC-Vr-DTa"/>
<constraint firstItem="p7V-IN-OTr" firstAttribute="trailing" secondItem="O4B-Z2-XYi" secondAttribute="trailing" id="s0C-8F-wWy"/>
<constraint firstItem="Yaj-QY-7xV" firstAttribute="leading" secondItem="Hyc-Nw-dET" secondAttribute="leading" id="ehf-lN-Jqs"/>
<constraint firstItem="p7V-IN-OTr" firstAttribute="top" secondItem="s7t-Kk-EPu" secondAttribute="bottom" constant="13.5" id="ggR-mS-i0Q"/>
<constraint firstItem="Yaj-QY-7xV" firstAttribute="top" secondItem="O4B-Z2-XYi" secondAttribute="bottom" constant="6" symbolic="YES" id="ixY-hj-v4X"/>
<constraint firstItem="p7V-IN-OTr" firstAttribute="trailing" secondItem="jXe-xz-9Sd" secondAttribute="trailing" id="oB7-NT-vJZ"/>
<constraint firstItem="O4B-Z2-XYi" firstAttribute="top" secondItem="p7V-IN-OTr" secondAttribute="bottom" constant="6" symbolic="YES" id="oZz-am-afB"/>
<constraint firstItem="p7V-IN-OTr" firstAttribute="leading" secondItem="s7t-Kk-EPu" secondAttribute="leading" id="rUo-Bv-0Ey"/>
<constraint firstItem="s7t-Kk-EPu" firstAttribute="trailing" secondItem="FUV-qx-xkC" secondAttribute="trailing" constant="-60" id="vIO-x1-7Q2"/>
<constraint firstItem="jXe-xz-9Sd" firstAttribute="baseline" secondItem="MPN-np-SbT" secondAttribute="baseline" id="wys-ML-2Q2"/>
</constraints>

View File

@ -41,6 +41,7 @@
"BSK-bH-Gct.title" = "Auto-convert traditional Chinese glyphs to KangXi characters";
"cf2-se-PDO.title" = "Dictionary and Language Models";
"chkAllowBoostingSingleKanjiAsUserPhrase.title" = "Allow boosting / excluding a candidate of single kanji";
"chkFetchSuggestionsFromUserOverrideModel.title" = "Applying typing suggestions from half-life user override model";
"dIN-TZ-67g.title" = "Space to +cycle candidates, Shift+Space to +cycle pages";
"E1l-m8-xgb.title" = "Advanced Settings";
"eia-1F-Do0.title" = "Auto-convert traditional Chinese glyphs to JIS Shinjitai characters";
@ -71,7 +72,7 @@
"TXr-FF-ehw.title" = "Traditional Chinese";
"ueU-Rz-a1C.title" = "Choose the behavior of (Shift+)Tab key in the candidate window.";
"Uyz-xL-TVN.title" = "Output Settings";
"W24-T4-cg0.title" = "Enable CNS11643 Support (2022-04-27)";
"W24-T4-cg0.title" = "Enable CNS11643 Support (2022-06-15)";
"wFR-zX-M8H.title" = "Show Hanyu-Pinyin in the inline composition buffer";
"wN3-k3-b2a.title" = "Choose your desired user data folder path. Will be omitted if invalid.";
"wQ9-px-b07.title" = "Apple Dynamic Bopomofo Basic Keyboard Layouts (Dachen & Eten Traditional) must match the Dachen parser in order to be functional.";

View File

@ -41,6 +41,7 @@
"BSK-bH-Gct.title" = "入力した繁体字を康熙字体と自動変換";
"cf2-se-PDO.title" = "辞書と言語モデル";
"chkAllowBoostingSingleKanjiAsUserPhrase.title" = "即排除/即最優先にできる候補の文字数の最低限は1字とする";
"chkFetchSuggestionsFromUserOverrideModel.title" = "入力中で臨時記憶モジュールからお薦めの候補を自動的に選ぶ";
"dIN-TZ-67g.title" = "Shift+Space で次のページ、Space で次の候補文字を";
"E1l-m8-xgb.title" = "詳細設定";
"eia-1F-Do0.title" = "入力した繁体字を日文 JIS 新字体と自動変換";
@ -71,7 +72,7 @@
"TXr-FF-ehw.title" = "繁体中国語";
"ueU-Rz-a1C.title" = "入力候補陳列での (Shift+)Tab キーの輪番切替対象をご指定ください。";
"Uyz-xL-TVN.title" = "出力設定";
"W24-T4-cg0.title" = "全字庫モード // 入力可能の漢字数倍増 (2022-04-27)";
"W24-T4-cg0.title" = "全字庫モード // 入力可能の漢字数倍増 (2022-06-15)";
"wFR-zX-M8H.title" = "弁音合併入力(入力緩衝列で代わりに漢語弁音の音読み)";
"wN3-k3-b2a.title" = "欲しがるユーザー辞書保存先をご指定ください。無効の保存先設定は効かぬ。";
"wQ9-px-b07.title" = "Apple 動態注音キーボード (大千と倚天伝統) を使うには、共通語分析器の注音配列を大千と設定すべきである。";

View File

@ -41,7 +41,7 @@
"BSK-bH-Gct.title" = "自动将繁体中文字转换为康熙正体字";
"cf2-se-PDO.title" = "辞典&語言模型";
"chkAllowBoostingSingleKanjiAsUserPhrase.title" = "将可以就地升权/排除的候选字词的最短词长设为单个汉字";
"chkAllowBoostingSingleKanjiAsUserPhrase.title" = "Allow boosting / excluding a candidate of single kanji";
"chkFetchSuggestionsFromUserOverrideModel.title" = "在敲字时自动套用来自半衰记忆模组的建议";
"dIN-TZ-67g.title" = "Shift+Space 换下一页Space 换选下一个候选字。";
"E1l-m8-xgb.title" = "进阶设定";
"eia-1F-Do0.title" = "自动将繁体中文字转换为日本简化字JIS 新字体)";
@ -72,7 +72,7 @@
"TXr-FF-ehw.title" = "繁体中文";
"ueU-Rz-a1C.title" = "指定 (Shift+)Tab 热键在选字窗内的轮替操作对象。";
"Uyz-xL-TVN.title" = "输出设定";
"W24-T4-cg0.title" = "启用 CNS11643 全字库支援 (2022-04-27)";
"W24-T4-cg0.title" = "启用 CNS11643 全字库支援 (2022-06-15)";
"wFR-zX-M8H.title" = "拼音并击模式(组字区内看到的是汉语拼音)";
"wN3-k3-b2a.title" = "请在此指定您想指定的使用者语汇档案目录。无效值会被忽略。";
"wQ9-px-b07.title" = "Apple 动态注音键盘布局(大千与倚天)要求普通话/国音分析器的注音排列得配置为大千排列。";

View File

@ -41,6 +41,7 @@
"BSK-bH-Gct.title" = "自動將繁體中文字轉換為康熙正體字";
"cf2-se-PDO.title" = "辭典&語言模型";
"chkAllowBoostingSingleKanjiAsUserPhrase.title" = "將可以就地升權/排除的候選字詞的最短詞長設為單個漢字";
"chkFetchSuggestionsFromUserOverrideModel.title" = "在敲字時自動套用來自半衰記憶模組的建議";
"dIN-TZ-67g.title" = "Shift+Space 換下一頁Space 換選下一個候選字";
"E1l-m8-xgb.title" = "進階設定";
"eia-1F-Do0.title" = "自動將繁體中文字轉換為日本簡化字JIS 新字體)";
@ -71,7 +72,7 @@
"TXr-FF-ehw.title" = "繁體中文";
"ueU-Rz-a1C.title" = "指定 (Shift+)Tab 熱鍵在選字窗內的輪替操作對象。";
"Uyz-xL-TVN.title" = "輸出設定";
"W24-T4-cg0.title" = "啟用 CNS11643 全字庫支援 (2022-04-27)";
"W24-T4-cg0.title" = "啟用 CNS11643 全字庫支援 (2022-06-15)";
"wFR-zX-M8H.title" = "拼音並擊模式(組字區內看到的是漢語拼音)";
"wN3-k3-b2a.title" = "請在此指定您想指定的使用者語彙檔案目錄。無效值會被忽略。";
"wQ9-px-b07.title" = "Apple 動態注音鍵盤佈局(大千與倚天)要求普通話/國音分析器的注音排列得配置為大千排列。";

View File

@ -3,9 +3,9 @@
<plist version="1.0">
<dict>
<key>CFBundleShortVersionString</key>
<string>1.7.1</string>
<string>1.7.2</string>
<key>CFBundleVersion</key>
<string>1971</string>
<string>1972</string>
<key>UpdateInfoEndpoint</key>
<string>https://gitee.com/vchewing/vChewing-macOS/raw/main/Update-Info.plist</string>
<key>UpdateInfoSite</key>

View File

@ -52,14 +52,18 @@ extension String {
// Tab to ASCII Space
// ASCII
strProcessed.regReplace(pattern: #"( +| +| +|\t+)+"#, replaceWith: " ")
strProcessed.regReplace(pattern: #"(^ | $)"#, replaceWith: "") //
strProcessed.regReplace(pattern: #"(\f+|\r+|\n+)+"#, replaceWith: "\n") // CR & FF to LF,
//
strProcessed.regReplace(pattern: #"(^ | $)"#, replaceWith: "")
strProcessed.regReplace(pattern: #"(\n | \n)"#, replaceWith: "\n")
// CR & FF to LF,
strProcessed.regReplace(pattern: #"(\f+|\r+|\n+)+"#, replaceWith: "\n")
if strProcessed.prefix(1) == " " { //
strProcessed.removeFirst()
}
if strProcessed.suffix(1) == " " { //
strProcessed.removeLast()
}
if cnvHYPYtoBPMF {
// Step 2: Convert HanyuPinyin to Bopomofo.
//

View File

@ -726,7 +726,7 @@
<key>USE_HFS+_COMPRESSION</key>
<false/>
<key>VERSION</key>
<string>1.7.1</string>
<string>1.7.2</string>
</dict>
<key>TYPE</key>
<integer>0</integer>

View File

@ -10,9 +10,10 @@
5B0AF8B527B2C8290096FE54 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B0AF8B427B2C8290096FE54 /* StringExtension.swift */; };
5B11328927B94CFB00E58451 /* AppleKeyboardConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B11328827B94CFB00E58451 /* AppleKeyboardConverter.swift */; };
5B242403284B0D6500520FE4 /* ctlCandidateUniversal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B242402284B0D6500520FE4 /* ctlCandidateUniversal.swift */; };
5B2F2BB6286216A500B8557B /* vChewingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B2F2BB5286216A500B8557B /* vChewingTests.swift */; };
5B3133BF280B229700A4A505 /* KeyHandler_States.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B3133BE280B229700A4A505 /* KeyHandler_States.swift */; };
5B38F59A281E2E49007D5F5D /* 6_Unigram.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1D15FC0EB100ABF4B3 /* 6_Unigram.swift */; };
5B38F59B281E2E49007D5F5D /* 7_KeyValuePair.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1815FC0EB100ABF4B3 /* 7_KeyValuePair.swift */; };
5B38F59B281E2E49007D5F5D /* 7_KeyValuePaired.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1815FC0EB100ABF4B3 /* 7_KeyValuePaired.swift */; };
5B38F59C281E2E49007D5F5D /* 2_Grid.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1715FC0EB100ABF4B3 /* 2_Grid.swift */; };
5B38F59D281E2E49007D5F5D /* 4_Node.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1A15FC0EB100ABF4B3 /* 4_Node.swift */; };
5B38F59E281E2E49007D5F5D /* 6_Bigram.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1415FC0EB100ABF4B3 /* 6_Bigram.swift */; };
@ -21,7 +22,7 @@
5B38F5A2281E2E49007D5F5D /* 0_Megrez.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1615FC0EB100ABF4B3 /* 0_Megrez.swift */; };
5B38F5A3281E2E49007D5F5D /* 3_Span.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1C15FC0EB100ABF4B3 /* 3_Span.swift */; };
5B38F5A4281E2E49007D5F5D /* 5_LanguageModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1915FC0EB100ABF4B3 /* 5_LanguageModel.swift */; };
5B3A87BC28597CDB0090E163 /* SymbolNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B3A87BB28597CDB0090E163 /* SymbolNode.swift */; };
5B3A87BC28597CDB0090E163 /* LMSymbolNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B3A87BB28597CDB0090E163 /* LMSymbolNode.swift */; };
5B40730C281672610023DFFF /* lmAssociates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B407309281672610023DFFF /* lmAssociates.swift */; };
5B40730D281672610023DFFF /* lmReplacements.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B40730A281672610023DFFF /* lmReplacements.swift */; };
5B54E743283A7D89001ECBDC /* lmCoreNS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B54E742283A7D89001ECBDC /* lmCoreNS.swift */; };
@ -128,6 +129,13 @@
remoteGlobalIDString = 5BD05BB727B2A429004C4F1D;
remoteInfo = vChewingPhraseEditor;
};
5B2F2BB7286216A500B8557B /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 6A0D4E9415FC0CFA00ABF4B3 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 6A0D4EA115FC0D2D00ABF4B3;
remoteInfo = vChewing;
};
6ACA420015FC1DCC00935EF6 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 6A0D4E9415FC0CFA00ABF4B3 /* Project object */;
@ -185,9 +193,11 @@
5B18BA7427C7BD8C0056EB19 /* LICENSE-CHT.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = "LICENSE-CHT.txt"; sourceTree = "<group>"; };
5B242402284B0D6500520FE4 /* ctlCandidateUniversal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ctlCandidateUniversal.swift; sourceTree = "<group>"; };
5B2DB17127AF8771006D874E /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; name = Makefile; path = Data/Makefile; sourceTree = "<group>"; };
5B2F2BB3286216A500B8557B /* vChewingTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = vChewingTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
5B2F2BB5286216A500B8557B /* vChewingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = vChewingTests.swift; sourceTree = "<group>"; };
5B30F11227BA568800484E24 /* vChewingKeyLayout.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = vChewingKeyLayout.bundle; sourceTree = "<group>"; };
5B3133BE280B229700A4A505 /* KeyHandler_States.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = KeyHandler_States.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5B3A87BB28597CDB0090E163 /* SymbolNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SymbolNode.swift; sourceTree = "<group>"; };
5B3A87BB28597CDB0090E163 /* LMSymbolNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LMSymbolNode.swift; sourceTree = "<group>"; };
5B407309281672610023DFFF /* lmAssociates.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; lineEnding = 0; path = lmAssociates.swift; sourceTree = "<group>"; usesTabs = 0; };
5B40730A281672610023DFFF /* lmReplacements.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; lineEnding = 0; path = lmReplacements.swift; sourceTree = "<group>"; usesTabs = 0; };
5B54E742283A7D89001ECBDC /* lmCoreNS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = lmCoreNS.swift; sourceTree = "<group>"; };
@ -287,7 +297,7 @@
6A0D4F1515FC0EB100ABF4B3 /* 1_Compositor.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 1_Compositor.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
6A0D4F1615FC0EB100ABF4B3 /* 0_Megrez.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = 0_Megrez.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
6A0D4F1715FC0EB100ABF4B3 /* 2_Grid.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 2_Grid.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
6A0D4F1815FC0EB100ABF4B3 /* 7_KeyValuePair.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 7_KeyValuePair.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
6A0D4F1815FC0EB100ABF4B3 /* 7_KeyValuePaired.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 7_KeyValuePaired.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
6A0D4F1915FC0EB100ABF4B3 /* 5_LanguageModel.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 5_LanguageModel.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
6A0D4F1A15FC0EB100ABF4B3 /* 4_Node.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 4_Node.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
6A0D4F1B15FC0EB100ABF4B3 /* 3_NodeAnchor.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 3_NodeAnchor.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
@ -322,6 +332,13 @@
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
5B2F2BB0286216A500B8557B /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
5BD05BB527B2A429004C4F1D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@ -370,6 +387,14 @@
name = MiscRootFiles;
sourceTree = "<group>";
};
5B2F2BB4286216A500B8557B /* vChewingTests */ = {
isa = PBXGroup;
children = (
5B2F2BB5286216A500B8557B /* vChewingTests.swift */,
);
path = vChewingTests;
sourceTree = "<group>";
};
5B407308281672610023DFFF /* SubLMs */ = {
isa = PBXGroup;
children = (
@ -464,8 +489,8 @@
5B407308281672610023DFFF /* SubLMs */,
5B949BDA2816DDBC00D87B5D /* LMConsolidator.swift */,
5BD0113A28180D6100609769 /* LMInstantiator.swift */,
5B3A87BB28597CDB0090E163 /* LMSymbolNode.swift */,
5BAEFACF28012565001F42C9 /* mgrLangModel.swift */,
5B3A87BB28597CDB0090E163 /* SymbolNode.swift */,
);
path = LangModelRelated;
sourceTree = "<group>";
@ -702,6 +727,7 @@
6ACA41E715FC1D9000935EF6 /* Installer */,
6A0D4EC215FC0D3C00ABF4B3 /* Source */,
5BD05BB927B2A429004C4F1D /* UserPhraseEditor */,
5B2F2BB4286216A500B8557B /* vChewingTests */,
6A0D4EA315FC0D2D00ABF4B3 /* Products */,
D47D73C127A7200500255A50 /* Frameworks */,
5BDC5CB127C28E8B00E1CCE2 /* KeyboardExtension */,
@ -715,6 +741,7 @@
6A0D4EA215FC0D2D00ABF4B3 /* vChewing.app */,
6ACA41CB15FC1D7500935EF6 /* vChewingInstaller.app */,
5BD05BB827B2A429004C4F1D /* vChewingPhraseEditor.app */,
5B2F2BB3286216A500B8557B /* vChewingTests.xctest */,
);
name = Products;
sourceTree = "<group>";
@ -769,7 +796,7 @@
6A0D4F1915FC0EB100ABF4B3 /* 5_LanguageModel.swift */,
6A0D4F1415FC0EB100ABF4B3 /* 6_Bigram.swift */,
6A0D4F1D15FC0EB100ABF4B3 /* 6_Unigram.swift */,
6A0D4F1815FC0EB100ABF4B3 /* 7_KeyValuePair.swift */,
6A0D4F1815FC0EB100ABF4B3 /* 7_KeyValuePaired.swift */,
);
path = Megrez;
sourceTree = "<group>";
@ -802,6 +829,24 @@
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
5B2F2BB2286216A500B8557B /* vChewingTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 5B2F2BBB286216A500B8557B /* Build configuration list for PBXNativeTarget "vChewingTests" */;
buildPhases = (
5B2F2BAF286216A500B8557B /* Sources */,
5B2F2BB0286216A500B8557B /* Frameworks */,
5B2F2BB1286216A500B8557B /* Resources */,
);
buildRules = (
);
dependencies = (
5B2F2BB8286216A500B8557B /* PBXTargetDependency */,
);
name = vChewingTests;
productName = vChewingTests;
productReference = 5B2F2BB3286216A500B8557B /* vChewingTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
5BD05BB727B2A429004C4F1D /* vChewingPhraseEditor */ = {
isa = PBXNativeTarget;
buildConfigurationList = 5BD05BC927B2A42A004C4F1D /* Build configuration list for PBXNativeTarget "vChewingPhraseEditor" */;
@ -870,9 +915,13 @@
6A0D4E9415FC0CFA00ABF4B3 /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 1320;
LastSwiftUpdateCheck = 1340;
LastUpgradeCheck = 1400;
TargetAttributes = {
5B2F2BB2286216A500B8557B = {
CreatedOnToolsVersion = 13.4.1;
TestTargetID = 6A0D4EA115FC0D2D00ABF4B3;
};
5BD05BB727B2A429004C4F1D = {
CreatedOnToolsVersion = 13.2;
LastSwiftMigration = 1320;
@ -909,11 +958,19 @@
6A0D4EA115FC0D2D00ABF4B3 /* vChewing */,
6ACA41CA15FC1D7500935EF6 /* vChewingInstaller */,
5BD05BB727B2A429004C4F1D /* vChewingPhraseEditor */,
5B2F2BB2286216A500B8557B /* vChewingTests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
5B2F2BB1286216A500B8557B /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
5BD05BB627B2A429004C4F1D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@ -1029,6 +1086,14 @@
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
5B2F2BAF286216A500B8557B /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
5B2F2BB6286216A500B8557B /* vChewingTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
5BD05BB427B2A429004C4F1D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@ -1071,7 +1136,7 @@
D456576E279E4F7B00DF6BC9 /* InputSignal.swift in Sources */,
5BA9FD1027FEDB6B002DE248 /* suiPrefPaneKeyboard.swift in Sources */,
5B3133BF280B229700A4A505 /* KeyHandler_States.swift in Sources */,
5B3A87BC28597CDB0090E163 /* SymbolNode.swift in Sources */,
5B3A87BC28597CDB0090E163 /* LMSymbolNode.swift in Sources */,
5BA9FD4327FEF3C8002DE248 /* Preferences.swift in Sources */,
5BA9FD4427FEF3C8002DE248 /* SegmentedControlStyleViewController.swift in Sources */,
D47F7DCE278BFB57002F9DD7 /* ctlPrefWindow.swift in Sources */,
@ -1084,7 +1149,7 @@
5B11328927B94CFB00E58451 /* AppleKeyboardConverter.swift in Sources */,
5B54E743283A7D89001ECBDC /* lmCoreNS.swift in Sources */,
5B62A32927AE77D100A19448 /* FSEventStreamHelper.swift in Sources */,
5B38F59B281E2E49007D5F5D /* 7_KeyValuePair.swift in Sources */,
5B38F59B281E2E49007D5F5D /* 7_KeyValuePaired.swift in Sources */,
5B62A33627AE795800A19448 /* mgrPrefs.swift in Sources */,
5B38F5A4281E2E49007D5F5D /* 5_LanguageModel.swift in Sources */,
5BAEFAD028012565001F42C9 /* mgrLangModel.swift in Sources */,
@ -1137,6 +1202,11 @@
target = 5BD05BB727B2A429004C4F1D /* vChewingPhraseEditor */;
targetProxy = 5B0AF8B227B2C4E20096FE54 /* PBXContainerItemProxy */;
};
5B2F2BB8286216A500B8557B /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 6A0D4EA115FC0D2D00ABF4B3 /* vChewing */;
targetProxy = 5B2F2BB7286216A500B8557B /* PBXContainerItemProxy */;
};
5B707CEA27D9F47A0099EF99 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
productRef = 5B707CE927D9F47A0099EF99 /* OpenCC */;
@ -1284,6 +1354,84 @@
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
5B2F2BB9286216A500B8557B /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
BUNDLE_LOADER = "$(TEST_HOST)";
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1972;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GENERATE_INFOPLIST_FILE = YES;
MACOSX_DEPLOYMENT_TARGET = 12.3;
MARKETING_VERSION = 1.7.2;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewingTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/vChewing.app/Contents/MacOS/vChewing";
};
name = Debug;
};
5B2F2BBA286216A500B8557B /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
BUNDLE_LOADER = "$(TEST_HOST)";
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1972;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GENERATE_INFOPLIST_FILE = YES;
MACOSX_DEPLOYMENT_TARGET = 12.3;
MARKETING_VERSION = 1.7.2;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewingTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/vChewing.app/Contents/MacOS/vChewing";
};
name = Release;
};
5BD05BC727B2A42A004C4F1D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@ -1304,7 +1452,7 @@
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1971;
CURRENT_PROJECT_VERSION = 1972;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
GCC_C_LANGUAGE_STANDARD = gnu11;
@ -1328,7 +1476,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.11.5;
MARKETING_VERSION = 1.7.1;
MARKETING_VERSION = 1.7.2;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewing.vChewingPhraseEditor;
@ -1361,7 +1509,7 @@
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1971;
CURRENT_PROJECT_VERSION = 1972;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
@ -1381,7 +1529,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.11.5;
MARKETING_VERSION = 1.7.1;
MARKETING_VERSION = 1.7.2;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewing.vChewingPhraseEditor;
@ -1499,7 +1647,7 @@
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1971;
CURRENT_PROJECT_VERSION = 1972;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = "";
@ -1535,7 +1683,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.11.5;
MARKETING_VERSION = 1.7.1;
MARKETING_VERSION = 1.7.2;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.inputmethod.vChewing;
PRODUCT_NAME = "$(TARGET_NAME)";
@ -1567,7 +1715,7 @@
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1971;
CURRENT_PROJECT_VERSION = 1972;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_ASSET_PATHS = "";
@ -1598,7 +1746,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.11.5;
MARKETING_VERSION = 1.7.1;
MARKETING_VERSION = 1.7.2;
PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.inputmethod.vChewing;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@ -1625,7 +1773,7 @@
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1971;
CURRENT_PROJECT_VERSION = 1972;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
GCC_C_LANGUAGE_STANDARD = gnu99;
@ -1651,7 +1799,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.11.5;
MARKETING_VERSION = 1.7.1;
MARKETING_VERSION = 1.7.2;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "org.atelierInmu.vChewing.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
@ -1678,7 +1826,7 @@
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1971;
CURRENT_PROJECT_VERSION = 1972;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = "";
@ -1699,7 +1847,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.11.5;
MARKETING_VERSION = 1.7.1;
MARKETING_VERSION = 1.7.2;
PRODUCT_BUNDLE_IDENTIFIER = "org.atelierInmu.vChewing.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@ -1713,6 +1861,15 @@
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
5B2F2BBB286216A500B8557B /* Build configuration list for PBXNativeTarget "vChewingTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
5B2F2BB9286216A500B8557B /* Debug */,
5B2F2BBA286216A500B8557B /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
5BD05BC927B2A42A004C4F1D /* Build configuration list for PBXNativeTarget "vChewingPhraseEditor" */ = {
isa = XCConfigurationList;
buildConfigurations = (

View File

@ -28,6 +28,16 @@
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "5B2F2BB2286216A500B8557B"
BuildableName = "vChewingTests.xctest"
BlueprintName = "vChewingTests"
ReferencedContainer = "container:vChewing.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction

View File

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
</Workspace>

View File

@ -1,47 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1320"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<PathRunnable
runnableDebuggingMode = "0"
BundleIdentifier = "org.atelierInmu.inputmethod.vChewing"
FilePath = "~/Library/Input Methods/vChewing.app">
</PathRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,29 @@
// Copyright (c) 2021 and onwards The vChewing Project (MIT-NTL License).
/*
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
1. The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
2. 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 above.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import XCTest
class vChewingTests: XCTestCase {
}