Pre Merge pull request !49 from ShikiSuen/upd/1.7.2

This commit is contained in:
ShikiSuen 2022-06-23 11:37:44 +00:00 committed by Gitee
commit dee058a19f
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 extraChooseCandidateKeyReverse: KeyCode = .kNone
private var absorbedArrowKey: KeyCode = .kNone private var absorbedArrowKey: KeyCode = .kNone
private var verticalTypingOnlyChooseCandidateKey: KeyCode = .kNone private var verticalTypingOnlyChooseCandidateKey: KeyCode = .kNone
private(set) var emacsKey: vChewingEmacsKey private(set) var emacsKey: EmacsKey
public init( public init(
inputText: String?, keyCode: UInt16, charCode: UInt16, flags: NSEvent.ModifierFlags, inputText: String?, keyCode: UInt16, charCode: UInt16, flags: NSEvent.ModifierFlags,
isVerticalTyping: Bool, inputTextIgnoringModifiers: String? = nil isVerticalTyping: Bool, inputTextIgnoringModifiers: String? = nil
) { ) {
let inputText = AppleKeyboardConverter.cnvStringApple2ABC(inputText ?? "") self.inputText = AppleKeyboardConverter.cnvStringApple2ABC(inputText ?? "")
let inputTextIgnoringModifiers = AppleKeyboardConverter.cnvStringApple2ABC( self.inputTextIgnoringModifiers = AppleKeyboardConverter.cnvStringApple2ABC(
inputTextIgnoringModifiers ?? inputText) inputTextIgnoringModifiers ?? inputText ?? "")
self.inputText = inputText
self.inputTextIgnoringModifiers = inputTextIgnoringModifiers
self.flags = flags self.flags = flags
isFlagChanged = false isFlagChanged = false
isTypingVertical = isVerticalTyping isTypingVertical = isVerticalTyping
@ -163,7 +161,7 @@ struct InputSignal: CustomStringConvertible {
event.charactersIgnoringModifiers ?? "") event.charactersIgnoringModifiers ?? "")
keyCode = event.keyCode keyCode = event.keyCode
flags = event.modifierFlags flags = event.modifierFlags
isFlagChanged = (event.type == .flagsChanged) ? true : false isFlagChanged = (event.type == .flagsChanged)
isTypingVertical = isVerticalTyping isTypingVertical = isVerticalTyping
let charCode: UInt16 = { let charCode: UInt16 = {
// count > 0!isEmpty滿 // count > 0!isEmpty滿
@ -230,14 +228,14 @@ struct InputSignal: CustomStringConvertible {
flags.contains([.control]) && inputText?.first?.isLetter ?? false flags.contains([.control]) && inputText?.first?.isLetter ?? false
} }
var isOptionHotKey: Bool {
flags.contains([.option]) && inputText?.first?.isLetter ?? false
}
var isOptionHold: Bool { var isOptionHold: Bool {
flags.contains([.option]) flags.contains([.option])
} }
var isOptionHotKey: Bool {
flags.contains([.option]) && inputText?.first?.isLetter ?? false
}
var isCapsLockOn: Bool { var isCapsLockOn: Bool {
flags.contains([.capsLock]) flags.contains([.capsLock])
} }
@ -334,7 +332,7 @@ struct InputSignal: CustomStringConvertible {
KeyCode(rawValue: keyCode) == extraChooseCandidateKeyReverse KeyCode(rawValue: keyCode) == extraChooseCandidateKeyReverse
} }
var isverticalTypingOnlyChooseCandidateKey: Bool { var isVerticalTypingOnlyChooseCandidateKey: Bool {
KeyCode(rawValue: keyCode) == verticalTypingOnlyChooseCandidateKey KeyCode(rawValue: keyCode) == verticalTypingOnlyChooseCandidateKey
} }
@ -350,7 +348,7 @@ struct InputSignal: CustomStringConvertible {
} }
} }
enum vChewingEmacsKey: UInt16 { enum EmacsKey: UInt16 {
case none = 0 case none = 0
case forward = 6 // F case forward = 6 // F
case backward = 2 // B case backward = 2 // B
@ -361,10 +359,10 @@ enum vChewingEmacsKey: UInt16 {
} }
enum EmacsKeyHelper { 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) let charCode = AppleKeyboardConverter.cnvApple2ABC(charCode)
if flags.contains(.control) { if flags.contains(.control) {
return vChewingEmacsKey(rawValue: charCode) ?? .none return EmacsKey(rawValue: charCode) ?? .none
} }
return .none return .none
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1296,7 +1296,7 @@ public struct Tekkon {
/// ///
/// ///
/// 便 validity check /// 便 validity check
/// //// ///
static let mapETen26StaticKeys: [String: String] = [ static let mapETen26StaticKeys: [String: String] = [
"a": "", "b": "", "c": "", "d": "", "e": "", "f": "", "g": "", "h": "", "i": "", "j": "", "k": "", "a": "", "b": "", "c": "", "d": "", "e": "", "f": "", "g": "", "h": "", "i": "", "j": "", "k": "",
"l": "", "m": "", "n": "", "o": "", "p": "", "q": "", "r": "", "s": "", "t": "", "u": "", "v": "", "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) client().overrideKeyboard(withKeyboardNamed: mgrPrefs.basicKeyboardLayout)
} }
/// 調 /// 調
func resetKeyHandler() { func resetKeyHandler() {
if let state = state as? InputState.NotEmpty {
/// 調
handle(state: InputState.Committing(textToCommit: state.composingBufferConverted))
}
keyHandler.clear() keyHandler.clear()
handle(state: InputState.Empty()) handle(state: InputState.Empty())
} }
@ -101,7 +105,7 @@ class ctlInputMethod: IMKInputController {
/// ///
/// macOS /// macOS
if client().bundleIdentifier() != Bundle.main.bundleIdentifier { if client().bundleIdentifier() != Bundle.main.bundleIdentifier {
// Override the keyboard layout to the basic one. // 使
setKeyLayout() setKeyLayout()
handle(state: .Empty()) handle(state: .Empty())
} // } //
@ -143,7 +147,7 @@ class ctlInputMethod: IMKInputController {
/// ///
/// macOS /// macOS
if client().bundleIdentifier() != Bundle.main.bundleIdentifier { if client().bundleIdentifier() != Bundle.main.bundleIdentifier {
// Remember to override the keyboard layout again -- treat this as an activate event. // 使
setKeyLayout() setKeyLayout()
handle(state: .Empty()) handle(state: .Empty())
} // } //
@ -229,10 +233,6 @@ class ctlInputMethod: IMKInputController {
/// - Parameter sender: 使 /// - Parameter sender: 使
override func commitComposition(_ sender: Any!) { override func commitComposition(_ sender: Any!) {
_ = sender // _ = sender //
if let state = state as? InputState.NotEmpty {
/// 調
handle(state: InputState.Committing(textToCommit: state.composingBuffer))
}
resetKeyHandler() resetKeyHandler()
} }
} }
@ -278,6 +278,38 @@ extension ctlInputMethod {
clearInlineDisplay() clearInlineDisplay()
return 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 /// selectionRange
/// 0 replacementRangeNSNotFound /// 0 replacementRangeNSNotFound
/// ///
@ -297,6 +329,7 @@ extension ctlInputMethod {
} }
/// ///
/// IMK commitComposition
private func commit(text: String) { private func commit(text: String) {
let buffer = IME.kanjiConversionIfRequired(text) let buffer = IME.kanjiConversionIfRequired(text)
if buffer.isEmpty { 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()) // --------------------- menu.addItem(NSMenuItem.separator()) // ---------------------
if optionKeyPressed { if optionKeyPressed {
@ -199,6 +204,7 @@ extension ctlInputMethod {
} }
@objc func toggleSCPCTypingMode(_: Any?) { @objc func toggleSCPCTypingMode(_: Any?) {
resetKeyHandler()
NotifierController.notify( NotifierController.notify(
message: String( message: String(
format: "%@%@%@", NSLocalizedString("Per-Char Select Mode", comment: ""), "\n", format: "%@%@%@", NSLocalizedString("Per-Char Select Mode", comment: ""), "\n",
@ -206,10 +212,10 @@ extension ctlInputMethod {
? NSLocalizedString("NotificationSwitchON", comment: "") ? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "") : NSLocalizedString("NotificationSwitchOFF", comment: "")
)) ))
resetKeyHandler()
} }
@objc func toggleChineseConverter(_: Any?) { @objc func toggleChineseConverter(_: Any?) {
resetKeyHandler()
NotifierController.notify( NotifierController.notify(
message: String( message: String(
format: "%@%@%@", NSLocalizedString("Force KangXi Writing", comment: ""), "\n", format: "%@%@%@", NSLocalizedString("Force KangXi Writing", comment: ""), "\n",
@ -217,10 +223,10 @@ extension ctlInputMethod {
? NSLocalizedString("NotificationSwitchON", comment: "") ? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "") : NSLocalizedString("NotificationSwitchOFF", comment: "")
)) ))
resetKeyHandler()
} }
@objc func toggleShiftJISShinjitaiOutput(_: Any?) { @objc func toggleShiftJISShinjitaiOutput(_: Any?) {
resetKeyHandler()
NotifierController.notify( NotifierController.notify(
message: String( message: String(
format: "%@%@%@", NSLocalizedString("JIS Shinjitai Output", comment: ""), "\n", format: "%@%@%@", NSLocalizedString("JIS Shinjitai Output", comment: ""), "\n",
@ -228,10 +234,10 @@ extension ctlInputMethod {
? NSLocalizedString("NotificationSwitchON", comment: "") ? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "") : NSLocalizedString("NotificationSwitchOFF", comment: "")
)) ))
resetKeyHandler()
} }
@objc func toggleHalfWidthPunctuation(_: Any?) { @objc func toggleHalfWidthPunctuation(_: Any?) {
resetKeyHandler()
NotifierController.notify( NotifierController.notify(
message: String( message: String(
format: "%@%@%@", NSLocalizedString("Half-Width Punctuation Mode", comment: ""), format: "%@%@%@", NSLocalizedString("Half-Width Punctuation Mode", comment: ""),
@ -240,10 +246,10 @@ extension ctlInputMethod {
? NSLocalizedString("NotificationSwitchON", comment: "") ? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "") : NSLocalizedString("NotificationSwitchOFF", comment: "")
)) ))
resetKeyHandler()
} }
@objc func toggleCNS11643Enabled(_: Any?) { @objc func toggleCNS11643Enabled(_: Any?) {
resetKeyHandler()
NotifierController.notify( NotifierController.notify(
message: String( message: String(
format: "%@%@%@", NSLocalizedString("CNS11643 Mode", comment: ""), "\n", format: "%@%@%@", NSLocalizedString("CNS11643 Mode", comment: ""), "\n",
@ -251,10 +257,10 @@ extension ctlInputMethod {
? NSLocalizedString("NotificationSwitchON", comment: "") ? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "") : NSLocalizedString("NotificationSwitchOFF", comment: "")
)) ))
resetKeyHandler()
} }
@objc func toggleSymbolEnabled(_: Any?) { @objc func toggleSymbolEnabled(_: Any?) {
resetKeyHandler()
NotifierController.notify( NotifierController.notify(
message: String( message: String(
format: "%@%@%@", NSLocalizedString("Symbol & Emoji Input", comment: ""), "\n", format: "%@%@%@", NSLocalizedString("Symbol & Emoji Input", comment: ""), "\n",
@ -262,10 +268,10 @@ extension ctlInputMethod {
? NSLocalizedString("NotificationSwitchON", comment: "") ? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "") : NSLocalizedString("NotificationSwitchOFF", comment: "")
)) ))
resetKeyHandler()
} }
@objc func toggleAssociatedPhrasesEnabled(_: Any?) { @objc func toggleAssociatedPhrasesEnabled(_: Any?) {
resetKeyHandler()
NotifierController.notify( NotifierController.notify(
message: String( message: String(
format: "%@%@%@", NSLocalizedString("Per-Char Associated Phrases", comment: ""), format: "%@%@%@", NSLocalizedString("Per-Char Associated Phrases", comment: ""),
@ -274,10 +280,10 @@ extension ctlInputMethod {
? NSLocalizedString("NotificationSwitchON", comment: "") ? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "") : NSLocalizedString("NotificationSwitchOFF", comment: "")
)) ))
resetKeyHandler()
} }
@objc func togglePhraseReplacement(_: Any?) { @objc func togglePhraseReplacement(_: Any?) {
resetKeyHandler()
NotifierController.notify( NotifierController.notify(
message: String( message: String(
format: "%@%@%@", NSLocalizedString("Use Phrase Replacement", comment: ""), "\n", format: "%@%@%@", NSLocalizedString("Use Phrase Replacement", comment: ""), "\n",
@ -285,7 +291,6 @@ extension ctlInputMethod {
? NSLocalizedString("NotificationSwitchON", comment: "") ? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "") : NSLocalizedString("NotificationSwitchOFF", comment: "")
)) ))
resetKeyHandler()
} }
@objc func selfUninstall(_: Any?) { @objc func selfUninstall(_: Any?) {
@ -311,38 +316,38 @@ extension ctlInputMethod {
} }
@objc func openUserPhrases(_: Any?) { @objc func openUserPhrases(_: Any?) {
IME.openPhraseFile(userFileAt: mgrLangModel.userPhrasesDataPath(IME.getInputMode())) IME.openPhraseFile(fromURL: mgrLangModel.userPhrasesDataURL(IME.getInputMode()))
if NSEvent.modifierFlags.contains(.option), mgrPrefs.isDebugModeEnabled { if NSEvent.modifierFlags.contains(.option) {
IME.openPhraseFile(userFileAt: mgrLangModel.userPhrasesDataPath(IME.getInputMode(isReversed: true))) IME.openPhraseFile(fromURL: mgrLangModel.userPhrasesDataURL(IME.getInputMode(isReversed: true)))
} }
} }
@objc func openExcludedPhrases(_: Any?) { @objc func openExcludedPhrases(_: Any?) {
IME.openPhraseFile(userFileAt: mgrLangModel.excludedPhrasesDataPath(IME.getInputMode())) IME.openPhraseFile(fromURL: mgrLangModel.userFilteredDataURL(IME.getInputMode()))
if NSEvent.modifierFlags.contains(.option), mgrPrefs.isDebugModeEnabled { if NSEvent.modifierFlags.contains(.option) {
IME.openPhraseFile(userFileAt: mgrLangModel.excludedPhrasesDataPath(IME.getInputMode(isReversed: true))) IME.openPhraseFile(fromURL: mgrLangModel.userFilteredDataURL(IME.getInputMode(isReversed: true)))
} }
} }
@objc func openUserSymbols(_: Any?) { @objc func openUserSymbols(_: Any?) {
IME.openPhraseFile(userFileAt: mgrLangModel.userSymbolDataPath(IME.getInputMode())) IME.openPhraseFile(fromURL: mgrLangModel.userSymbolDataURL(IME.getInputMode()))
if NSEvent.modifierFlags.contains(.option), mgrPrefs.isDebugModeEnabled { if NSEvent.modifierFlags.contains(.option) {
IME.openPhraseFile(userFileAt: mgrLangModel.userSymbolDataPath(IME.getInputMode(isReversed: true))) IME.openPhraseFile(fromURL: mgrLangModel.userSymbolDataURL(IME.getInputMode(isReversed: true)))
} }
} }
@objc func openPhraseReplacement(_: Any?) { @objc func openPhraseReplacement(_: Any?) {
IME.openPhraseFile(userFileAt: mgrLangModel.phraseReplacementDataPath(IME.getInputMode())) IME.openPhraseFile(fromURL: mgrLangModel.userReplacementsDataURL(IME.getInputMode()))
if NSEvent.modifierFlags.contains(.option), mgrPrefs.isDebugModeEnabled { if NSEvent.modifierFlags.contains(.option) {
IME.openPhraseFile(userFileAt: mgrLangModel.phraseReplacementDataPath(IME.getInputMode(isReversed: true))) IME.openPhraseFile(fromURL: mgrLangModel.userReplacementsDataURL(IME.getInputMode(isReversed: true)))
} }
} }
@objc func openAssociatedPhrases(_: Any?) { @objc func openAssociatedPhrases(_: Any?) {
IME.openPhraseFile(userFileAt: mgrLangModel.userAssociatedPhrasesDataPath(IME.getInputMode())) IME.openPhraseFile(fromURL: mgrLangModel.userAssociatesDataURL(IME.getInputMode()))
if NSEvent.modifierFlags.contains(.option), mgrPrefs.isDebugModeEnabled { if NSEvent.modifierFlags.contains(.option) {
IME.openPhraseFile( 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) 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?) { @objc func showAbout(_: Any?) {
(NSApp.delegate as? AppDelegate)?.showAbout() (NSApp.delegate as? AppDelegate)?.showAbout()
NSApp.activate(ignoringOtherApps: true) NSApp.activate(ignoringOtherApps: true)

View File

@ -48,7 +48,7 @@ public enum IME {
switch (mgrPrefs.chineseConversionEnabled, mgrPrefs.shiftJISShinjitaiOutputEnabled) { switch (mgrPrefs.chineseConversionEnabled, mgrPrefs.shiftJISShinjitaiOutputEnabled) {
case (false, true): return vChewingKanjiConverter.cnvTradToJIS(text) case (false, true): return vChewingKanjiConverter.cnvTradToJIS(text)
case (true, false): return vChewingKanjiConverter.cnvTradToKangXi(text) case (true, false): return vChewingKanjiConverter.cnvTradToKangXi(text)
// //
case (true, true): return vChewingKanjiConverter.cnvTradToJIS(text) case (true, true): return vChewingKanjiConverter.cnvTradToJIS(text)
case (false, false): return text case (false, false): return text
} }
@ -122,6 +122,10 @@ public enum IME {
// MARK: - Open a phrase data file. // MARK: - Open a phrase data file.
static func openPhraseFile(fromURL url: URL) {
openPhraseFile(userFileAt: url.path)
}
static func openPhraseFile(userFileAt path: String) { static func openPhraseFile(userFileAt path: String) {
func checkIfUserFilesExist() -> Bool { func checkIfUserFilesExist() -> Bool {
if !mgrLangModel.chkUserLMFilesExist(InputMode.imeModeCHS) if !mgrLangModel.chkUserLMFilesExist(InputMode.imeModeCHS)
@ -373,7 +377,7 @@ public enum IME {
// Extend the RangeReplaceableCollection to allow it clean duplicated characters. // Extend the RangeReplaceableCollection to allow it clean duplicated characters.
// Ref: https://stackoverflow.com/questions/25738817/ // Ref: https://stackoverflow.com/questions/25738817/
extension RangeReplaceableCollection where Element: Hashable { extension RangeReplaceableCollection where Element: Hashable {
var charDeDuplicate: Self { var deduplicate: Self {
var set = Set<Element>() var set = Set<Element>()
return filter { set.insert($0).inserted } return filter { set.insert($0).inserted }
} }
@ -417,3 +421,25 @@ extension UniChar {
return true 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 kShouldNotFartInLieuOfBeep = "ShouldNotFartInLieuOfBeep"
static let kShowHanyuPinyinInCompositionBuffer = "ShowHanyuPinyinInCompositionBuffer" static let kShowHanyuPinyinInCompositionBuffer = "ShowHanyuPinyinInCompositionBuffer"
static let kInlineDumpPinyinInLieuOfZhuyin = "InlineDumpPinyinInLieuOfZhuyin" static let kInlineDumpPinyinInLieuOfZhuyin = "InlineDumpPinyinInLieuOfZhuyin"
static let kFetchSuggestionsFromUserOverrideModel = "FetchSuggestionsFromUserOverrideModel"
static let kCandidateTextFontName = "CandidateTextFontName" static let kCandidateTextFontName = "CandidateTextFontName"
static let kCandidateKeyLabelFontName = "CandidateKeyLabelFontName" static let kCandidateKeyLabelFontName = "CandidateKeyLabelFontName"
@ -270,6 +271,9 @@ public enum mgrPrefs {
UserDefaults.standard.setDefault( UserDefaults.standard.setDefault(
mgrPrefs.allowBoostingSingleKanjiAsUserPhrase, forKey: UserDef.kAllowBoostingSingleKanjiAsUserPhrase 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.usingHotKeySCPC, forKey: UserDef.kUsingHotKeySCPC)
UserDefaults.standard.setDefault(mgrPrefs.usingHotKeyAssociates, forKey: UserDef.kUsingHotKeyAssociates) UserDefaults.standard.setDefault(mgrPrefs.usingHotKeyAssociates, forKey: UserDef.kUsingHotKeyAssociates)
@ -344,6 +348,9 @@ public enum mgrPrefs {
@UserDefault(key: UserDef.kAllowBoostingSingleKanjiAsUserPhrase, defaultValue: false) @UserDefault(key: UserDef.kAllowBoostingSingleKanjiAsUserPhrase, defaultValue: false)
static var allowBoostingSingleKanjiAsUserPhrase: Bool static var allowBoostingSingleKanjiAsUserPhrase: Bool
@UserDefault(key: UserDef.kFetchSuggestionsFromUserOverrideModel, defaultValue: true)
static var fetchSuggestionsFromUserOverrideModel: Bool
static var minCandidateLength: Int { static var minCandidateLength: Int {
mgrPrefs.allowBoostingSingleKanjiAsUserPhrase ? 1 : 2 mgrPrefs.allowBoostingSingleKanjiAsUserPhrase ? 1 : 2
} }

View File

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

View File

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

View File

@ -40,6 +40,34 @@ class SymbolNode {
children = Array(symbols).map { SymbolNode(String($0), nil) } 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( static let catCommonSymbols = String(
format: NSLocalizedString("catCommonSymbols", comment: "")) format: NSLocalizedString("catCommonSymbols", comment: ""))
static let catHoriBrackets = String( static let catHoriBrackets = String(
@ -71,7 +99,9 @@ class SymbolNode {
static let catLineSegments = String( static let catLineSegments = String(
format: NSLocalizedString("catLineSegments", comment: "")) format: NSLocalizedString("catLineSegments", comment: ""))
static let root: SymbolNode = .init( private(set) static var root: SymbolNode = .init("/")
private static let defaultSymbolRoot: SymbolNode = .init(
"/", "/",
[ [
SymbolNode(""), SymbolNode(""),

View File

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

View File

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

View File

@ -1,5 +1,5 @@
// Copyright (c) 2021 and onwards The vChewing Project (MIT-NTL License). // 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 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 this software and associated documentation files (the "Software"), to deal in
@ -27,38 +27,6 @@ import Foundation
extension vChewing { extension vChewing {
public class LMUserOverride { 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 // MARK: - Main
var mutCapacity: Int var mutCapacity: Int
@ -73,50 +41,57 @@ extension vChewing {
} }
public func observe( public func observe(
walkedNodes: [Megrez.NodeAnchor], walkedAnchors: [Megrez.NodeAnchor],
cursorIndex: Int, cursorIndex: Int,
candidate: String, candidate: String,
timestamp: Double 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 { guard mutLRUMap[key] != nil else {
let observation: Observation = .init() var observation: Observation = .init()
observation.update(candidate: candidate, timestamp: timestamp) observation.update(candidate: candidate, timestamp: timestamp)
let koPair = KeyObservationPair(key: key, observation: observation) let koPair = KeyObservationPair(key: key, observation: observation)
// key key key
// Swift
mutLRUMap.removeValue(forKey: key)
mutLRUMap[key] = koPair mutLRUMap[key] = koPair
mutLRUList.insert(koPair, at: 0) mutLRUList.insert(koPair, at: 0)
if mutLRUList.count > mutCapacity { if mutLRUList.count > mutCapacity {
mutLRUMap[mutLRUList[mutLRUList.endIndex].key] = nil mutLRUMap.removeValue(forKey: mutLRUList[mutLRUList.endIndex].key)
mutLRUList.removeLast() mutLRUList.removeLast()
} }
IME.prtDebugIntel("UOM: Observation finished with new observation: \(key)") IME.prtDebugIntel("UOM: Observation finished with new observation: \(key)")
mgrLangModel.saveUserOverrideModelData()
return return
} }
if let theNeta = mutLRUMap[key] { if var theNeta = mutLRUMap[key] {
theNeta.observation.update(candidate: candidate, timestamp: timestamp) theNeta.observation.update(candidate: candidate, timestamp: timestamp)
mutLRUList.insert(theNeta, at: 0) mutLRUList.insert(theNeta, at: 0)
mutLRUMap[key] = theNeta mutLRUMap[key] = theNeta
IME.prtDebugIntel("UOM: Observation finished with existing observation: \(key)") IME.prtDebugIntel("UOM: Observation finished with existing observation: \(key)")
mgrLangModel.saveUserOverrideModelData()
} }
} }
public func suggest( public func suggest(
walkedNodes: [Megrez.NodeAnchor], walkedAnchors: [Megrez.NodeAnchor],
cursorIndex: Int, cursorIndex: Int,
timestamp: Double timestamp: Double
) -> String { ) -> [Megrez.Unigram] {
let key = convertKeyFrom(walkedNodes: walkedNodes, cursorIndex: cursorIndex) let key = convertKeyFrom(walkedAnchors: walkedAnchors, cursorIndex: cursorIndex)
let currentReadingKey = convertKeyFrom(walkedAnchors: walkedAnchors, cursorIndex: cursorIndex, readingOnly: true)
guard let koPair = mutLRUMap[key] else { guard let koPair = mutLRUMap[key] else {
IME.prtDebugIntel("UOM: mutLRUMap[key] is nil, throwing blank suggestion for key: \(key).") IME.prtDebugIntel("UOM: mutLRUMap[key] is nil, throwing blank suggestion for key: \(key).")
return "" return .init()
} }
let observation = koPair.observation let observation = koPair.observation
var candidate = "" var arrResults = [Megrez.Unigram]()
var score = 0.0 var currentHighScore = 0.0
for overrideNeta in Array(observation.overrides) { for overrideNeta in Array(observation.overrides) {
let override: Override = overrideNeta.value let override: Override = overrideNeta.value
let overrideScore: Double = getScore( let overrideScore: Double = getScore(
@ -126,23 +101,20 @@ extension vChewing {
timestamp: timestamp, timestamp: timestamp,
lambda: mutDecayExponent lambda: mutDecayExponent
) )
if (0...currentHighScore).contains(overrideScore) { continue }
if overrideScore == 0.0 { let newUnigram = Megrez.Unigram(
continue keyValue: .init(key: currentReadingKey, value: overrideNeta.key), score: overrideScore
)
arrResults.insert(newUnigram, at: 0)
currentHighScore = overrideScore
} }
if arrResults.isEmpty {
if overrideScore > score {
candidate = overrideNeta.key
score = overrideScore
}
}
if candidate.isEmpty {
IME.prtDebugIntel("UOM: No usable suggestions in the result for key: \(key).") IME.prtDebugIntel("UOM: No usable suggestions in the result for key: \(key).")
} }
return candidate return arrResults
} }
public func getScore( private func getScore(
eventCount: Int, eventCount: Int,
totalCount: Int, totalCount: Int,
eventTimestamp: Double, eventTimestamp: Double,
@ -156,50 +128,193 @@ extension vChewing {
} }
func convertKeyFrom( func convertKeyFrom(
walkedNodes: [Megrez.NodeAnchor], cursorIndex: Int walkedAnchors: [Megrez.NodeAnchor], cursorIndex: Int, readingOnly: Bool = false
) -> String { ) -> String {
let arrEndingPunctuation = ["", "", "", "", "", "", "", ""] let arrEndingPunctuation = ["", "", "", "", "", "", "", ""]
var arrNodesReversed: [Megrez.NodeAnchor] = [] let whiteList = "你他妳她祢她它牠再在"
var arrNodes: [Megrez.NodeAnchor] = []
var intLength = 0 var intLength = 0
for theNodeAnchor in walkedNodes { for theNodeAnchor in walkedAnchors {
// .reverse() arrNodes.append(theNodeAnchor)
arrNodesReversed = [theNodeAnchor] + arrNodesReversed
intLength += theNodeAnchor.spanningLength intLength += theNodeAnchor.spanningLength
if intLength >= cursorIndex { if intLength >= cursorIndex {
break break
} }
} }
if arrNodesReversed.isEmpty { return "" } if arrNodes.isEmpty { return "" }
var strCurrent = "()" arrNodes = Array(arrNodes.reversed())
var strPrevious = "()"
var strAnterior = "()"
guard let kvCurrent = arrNodesReversed[0].node?.currentKeyValue, guard let kvCurrent = arrNodes[0].node?.currentKeyValue,
!arrEndingPunctuation.contains(kvCurrent.value) !arrEndingPunctuation.contains(kvCurrent.value)
else { else {
return "" return ""
} }
//
if kvCurrent.key.split(separator: "-").count != kvCurrent.value.count { return "" }
// //
strCurrent = kvCurrent.key let strCurrent = kvCurrent.key
var kvPrevious = Megrez.KeyValuePaired()
if arrNodesReversed.count >= 2, var kvAnterior = Megrez.KeyValuePaired()
let kvPrevious = arrNodesReversed[1].node?.currentKeyValue, var readingStack = ""
!arrEndingPunctuation.contains(kvPrevious.value) 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))
{ {
strPrevious = "(\(kvPrevious.key),\(kvPrevious.value))" return ""
} else {
return (readingOnly ? strCurrent : trigramKey)
}
} }
if arrNodesReversed.count >= 3, if arrNodes.count >= 2,
let kvAnterior = arrNodesReversed[2].node?.currentKeyValue, let kvPreviousThisOne = arrNodes[1].node?.currentKeyValue,
!arrEndingPunctuation.contains(kvAnterior.value) !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() { public static func loadUserPhrasesData() {
gLangModelCHT.loadUserPhrasesData( gLangModelCHT.loadUserPhrasesData(
path: userPhrasesDataPath(InputMode.imeModeCHT), path: userPhrasesDataURL(InputMode.imeModeCHT).path,
filterPath: excludedPhrasesDataPath(InputMode.imeModeCHT) filterPath: userFilteredDataURL(InputMode.imeModeCHT).path
) )
gLangModelCHS.loadUserPhrasesData( gLangModelCHS.loadUserPhrasesData(
path: userPhrasesDataPath(InputMode.imeModeCHS), path: userPhrasesDataURL(InputMode.imeModeCHS).path,
filterPath: excludedPhrasesDataPath(InputMode.imeModeCHS) filterPath: userFilteredDataURL(InputMode.imeModeCHS).path
) )
gLangModelCHT.loadUserSymbolData(path: userSymbolDataPath(InputMode.imeModeCHT)) gLangModelCHT.loadUserSymbolData(path: userSymbolDataURL(InputMode.imeModeCHT).path)
gLangModelCHS.loadUserSymbolData(path: userSymbolDataPath(InputMode.imeModeCHS)) 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() { public static func loadUserAssociatesData() {
gLangModelCHT.loadUserAssociatesData( gLangModelCHT.loadUserAssociatesData(
path: mgrLangModel.userAssociatedPhrasesDataPath(InputMode.imeModeCHT) path: mgrLangModel.userAssociatesDataURL(InputMode.imeModeCHT).path
) )
gLangModelCHS.loadUserAssociatesData( gLangModelCHS.loadUserAssociatesData(
path: mgrLangModel.userAssociatedPhrasesDataPath(InputMode.imeModeCHS) path: mgrLangModel.userAssociatesDataURL(InputMode.imeModeCHS).path
) )
} }
public static func loadUserPhraseReplacement() { public static func loadUserPhraseReplacement() {
gLangModelCHT.loadReplacementsData( gLangModelCHT.loadReplacementsData(
path: mgrLangModel.phraseReplacementDataPath(InputMode.imeModeCHT) path: mgrLangModel.userReplacementsDataURL(InputMode.imeModeCHT).path
) )
gLangModelCHS.loadReplacementsData( gLangModelCHS.loadReplacementsData(
path: mgrLangModel.phraseReplacementDataPath(InputMode.imeModeCHS) path: mgrLangModel.userReplacementsDataURL(InputMode.imeModeCHS).path
) )
} }
@ -219,37 +224,48 @@ enum mgrLangModel {
// Swift appendingPathComponent URL .path // 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" 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" 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" 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" 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" 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: - 使 // MARK: - 使
static func ensureFileExists( static func ensureFileExists(
_ filePath: String, populateWithTemplate templateBasename: String = "1145141919810", _ fileURL: URL, populateWithTemplate templateBasename: String = "1145141919810",
extension ext: String = "txt" extension ext: String = "txt"
) -> Bool { ) -> Bool {
let filePath = fileURL.path
if !FileManager.default.fileExists(atPath: filePath) { if !FileManager.default.fileExists(atPath: filePath) {
let templateURL = Bundle.main.url(forResource: templateBasename, withExtension: ext) let templateURL = Bundle.main.url(forResource: templateBasename, withExtension: ext)
var templateData = Data("".utf8) var templateData = Data("".utf8)
@ -274,11 +290,14 @@ enum mgrLangModel {
if !userDataFolderExists { if !userDataFolderExists {
return false return false
} }
if !ensureFileExists(userPhrasesDataPath(mode)) /// SymbolNode UserOverrideModel
|| !ensureFileExists(userAssociatedPhrasesDataPath(mode)) ///
|| !ensureFileExists(excludedPhrasesDataPath(mode)) ///
|| !ensureFileExists(phraseReplacementDataPath(mode)) if !ensureFileExists(userPhrasesDataURL(mode))
|| !ensureFileExists(userSymbolDataPath(mode)) || !ensureFileExists(userAssociatesDataURL(mode))
|| !ensureFileExists(userFilteredDataURL(mode))
|| !ensureFileExists(userReplacementsDataURL(mode))
|| !ensureFileExists(userSymbolDataURL(mode))
{ {
return false return false
} }
@ -391,7 +410,7 @@ enum mgrLangModel {
return false return false
} }
let path = areWeDeleting ? excludedPhrasesDataPath(mode) : userPhrasesDataPath(mode) let theURL = areWeDeleting ? userFilteredDataURL(mode) : userPhrasesDataURL(mode)
if areWeDuplicating, !areWeDeleting { if areWeDuplicating, !areWeDeleting {
// Do not use ASCII characters to comment here. // Do not use ASCII characters to comment here.
@ -400,7 +419,7 @@ enum mgrLangModel {
currentMarkedPhrase += "\t#𝙾𝚟𝚎𝚛𝚛𝚒𝚍𝚎" currentMarkedPhrase += "\t#𝙾𝚟𝚎𝚛𝚛𝚒𝚍𝚎"
} }
if let writeFile = FileHandle(forUpdatingAtPath: path), if let writeFile = FileHandle(forUpdatingAtPath: theURL.path),
let data = currentMarkedPhrase.data(using: .utf8), let data = currentMarkedPhrase.data(using: .utf8),
let endl = "\n".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 // We enforce the format consolidation here, since the pragma header
// will let the UserPhraseLM bypasses the consolidating process on load. // 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 return false
} }
@ -428,4 +447,20 @@ enum mgrLangModel {
} }
return false 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 mutGrid: Grid = .init()
/// 使 /// 使
private var mutLM: LanguageModel private var mutLM: LanguageModelProtocol
/// ///
public var maxBuildSpanLength: Int { mutGrid.maxBuildSpanLength } public var maxBuildSpanLength: Int { mutGrid.maxBuildSpanLength }
/// ///
public var joinSeparator: String = "" 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 } public var grid: Grid { mutGrid }
@ -62,7 +62,7 @@ extension Megrez {
/// - lm: Megrez.LanguageModel /// - lm: Megrez.LanguageModel
/// - length: 10 /// - length: 10
/// - separator: /// - separator:
public init(lm: LanguageModel, length: Int = 10, separator: String = "") { public init(lm: LanguageModelProtocol, length: Int = 10, separator: String = "") {
mutLM = lm mutLM = lm
mutGrid = .init(spanLength: abs(length)) // mutGrid = .init(spanLength: abs(length)) //
joinSeparator = separator joinSeparator = separator
@ -112,7 +112,7 @@ extension Megrez {
return true return true
} }
/// /// X
/// ///
/// ///
/// ///
@ -140,8 +140,8 @@ extension Megrez {
/// ///
/// - Parameters: /// - Parameters:
/// - at: /// - location:
/// - score: 0 /// - accumulatedScore: 0
/// - joinedPhrase: 使 /// - joinedPhrase: 使
/// - longPhrases: 使 /// - longPhrases: 使
public func walk( public func walk(
@ -160,8 +160,8 @@ extension Megrez {
/// ///
/// - Parameters: /// - Parameters:
/// - at: /// - location:
/// - score: 0 /// - accumulatedScore: 0
/// - joinedPhrase: 使 /// - joinedPhrase: 使
/// - longPhrases: 使 /// - longPhrases: 使
public func reverseWalk( public func reverseWalk(
@ -219,12 +219,10 @@ extension Megrez {
} else { } else {
// //
var longPhrases = [String]() var longPhrases = [String]()
for theAnchor in nodes { for theAnchor in nodes.lazy.filter({ $0.spanningLength > 1 }) {
guard let theNode = theAnchor.node else { continue } guard let theNode = theAnchor.node else { continue }
if theAnchor.spanningLength > 1 {
longPhrases.append(theNode.currentKeyValue.value) longPhrases.append(theNode.currentKeyValue.value)
} }
}
longPhrases = longPhrases.stableSorted { longPhrases = longPhrases.stableSorted {
$0.count > $1.count $0.count > $1.count
@ -249,11 +247,11 @@ extension Megrez {
} }
var result: [NodeAnchor] = paths[0] var result: [NodeAnchor] = paths[0]
for neta in paths { for neta in paths.lazy.filter({
if neta.last!.accumulatedScore > result.last!.accumulatedScore { $0.last!.accumulatedScore > result.last!.accumulatedScore
}) {
result = neta result = neta
} }
}
return result return result
} }
@ -267,29 +265,20 @@ extension Megrez {
for p in itrBegin..<itrEnd { for p in itrBegin..<itrEnd {
for q in 1..<maxBuildSpanLength { for q in 1..<maxBuildSpanLength {
if p + q > itrEnd { if p + q > itrEnd { break }
break
}
let arrSlice = mutReadings[p..<(p + q)] let arrSlice = mutReadings[p..<(p + q)]
let combinedReading: String = join(slice: arrSlice, separator: joinSeparator) let combinedReading: String = join(slice: arrSlice, separator: joinSeparator)
if mutGrid.hasMatchedNode(location: p, spanningLength: q, key: combinedReading) { continue }
if !mutGrid.hasMatchedNode(location: p, spanningLength: q, key: combinedReading) {
let unigrams: [Unigram] = mutLM.unigramsFor(key: combinedReading) let unigrams: [Unigram] = mutLM.unigramsFor(key: combinedReading)
if !unigrams.isEmpty { if unigrams.isEmpty { continue }
let n = Node(key: combinedReading, unigrams: unigrams) let n = Node(key: combinedReading, unigrams: unigrams)
mutGrid.insertNode(node: n, location: p, spanningLength: q) mutGrid.insertNode(node: n, location: p, spanningLength: q)
} }
} }
} }
}
}
private func join(slice arrSlice: ArraySlice<String>, separator: String) -> String { private func join(slice arrSlice: ArraySlice<String>, separator: String) -> String {
var arrResult: [String] = [] arrSlice.joined(separator: separator)
for value in arrSlice {
arrResult.append(value)
}
return arrResult.joined(separator: separator)
} }
} }
} }
@ -303,7 +292,7 @@ extension Sequence {
/// ///
/// - Parameter areInIncreasingOrder: Return nil when two element are equal. /// - Parameter areInIncreasingOrder: Return nil when two element are equal.
/// - Returns: The sorted collection. /// - Returns: The sorted collection.
func stableSorted( fileprivate func stableSorted(
by areInIncreasingOrder: (Element, Element) throws -> Bool by areInIncreasingOrder: (Element, Element) throws -> Bool
) )
rethrows -> [Element] rethrows -> [Element]

View File

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

View File

@ -32,7 +32,7 @@ extension Megrez {
private var mutMaximumLength: Int = 0 private var mutMaximumLength: Int = 0
/// ///
var maximumLength: Int { public var maximumLength: Int {
mutMaximumLength mutMaximumLength
} }
@ -77,7 +77,8 @@ extension Megrez {
/// - Parameters: /// - Parameters:
/// - length: /// - length:
public func node(length: Int) -> Node? { 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 { extension Megrez {
/// ///
public class Node { public class Node {
///
private let mutLM: LanguageModel = .init()
/// ///
private var mutKey: String = "" private var mutKey: String = ""
/// ///
@ -37,11 +35,11 @@ extension Megrez {
/// ///
private var mutBigrams: [Bigram] private var mutBigrams: [Bigram]
/// ///
private var mutCandidates: [KeyValuePair] = [] private var mutCandidates: [KeyValuePaired] = []
/// 調 /// 調
private var mutValueUnigramIndexMap: [String: Int] = [:] private var mutValueUnigramIndexMap: [String: Int] = [:]
/// ///
private var mutPrecedingBigramMap: [KeyValuePair: [Megrez.Bigram]] = [:] private var mutPrecedingBigramMap: [KeyValuePaired: [Megrez.Bigram]] = [:]
/// ///
private var mutCandidateFixed: Bool = false 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 { public var currentKeyValue: KeyValuePaired {
mutSelectedUnigramIndex >= mutUnigrams.count ? KeyValuePair() : mutCandidates[mutSelectedUnigramIndex] 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: /// - Parameters:
@ -93,7 +91,9 @@ extension Megrez {
mutCandidates.append(gram.keyValue) 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) mutPrecedingBigramMap[gram.precedingKeyValue]?.append(gram)
} }
} }
@ -101,22 +101,21 @@ extension Megrez {
/// ///
/// - Parameters: /// - Parameters:
/// - precedingKeyValues: /// - precedingKeyValues:
public func primeNodeWith(precedingKeyValues: [KeyValuePair]) { public func primeNodeWith(precedingKeyValues: [KeyValuePaired]) {
var newIndex = mutSelectedUnigramIndex var newIndex = mutSelectedUnigramIndex
var max = mutScore var max = mutScore
if !isCandidateFixed { if !isCandidateFixed {
for neta in precedingKeyValues { for neta in precedingKeyValues {
let bigrams = mutPrecedingBigramMap[neta] ?? [] let bigrams = mutPrecedingBigramMap[neta] ?? []
for bigram in bigrams { for bigram in bigrams.lazy.filter({ [self] in
guard bigram.score > max else { continue } $0.score > max && mutValueUnigramIndexMap.keys.contains($0.keyValue.value)
if let valRetrieved = mutValueUnigramIndexMap[bigram.keyValue.value] { }) {
newIndex = valRetrieved as Int newIndex = mutValueUnigramIndexMap[bigram.keyValue.value] ?? newIndex
max = bigram.score max = bigram.score
} }
} }
} }
}
mutScore = max mutScore = max
mutSelectedUnigramIndex = newIndex mutSelectedUnigramIndex = newIndex
} }
@ -156,11 +155,9 @@ extension Megrez {
/// - Parameters: /// - Parameters:
/// - candidate: /// - candidate:
public func scoreFor(candidate: String) -> Double { public func scoreFor(candidate: String) -> Double {
for unigram in mutUnigrams { for unigram in mutUnigrams.lazy.filter({ $0.keyValue.value == candidate }) {
if unigram.keyValue.value == candidate {
return unigram.score return unigram.score
} }
}
return 0.0 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. 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 { extension Megrez {
/// 使 /// 使
open class LanguageModel { open class LanguageModel: LanguageModelProtocol {
public init() {} public init() {}
// Swift // Swift
/// ///
open func unigramsFor(key: String) -> [Megrez.Unigram] { open func unigramsFor(key: String) -> [Megrez.Unigram] {
key.isEmpty ? [Megrez.Unigram]() : [Megrez.Unigram]() key.isEmpty ? [Megrez.Unigram]() : [Megrez.Unigram]()
} }
/// ///
open func bigramsForKeys(precedingKey: String, key: String) -> [Megrez.Bigram] { open func bigramsForKeys(precedingKey: String, key: String) -> [Megrez.Bigram] {
precedingKey == key ? [Megrez.Bigram]() : [Megrez.Bigram]() precedingKey == key ? [Megrez.Bigram]() : [Megrez.Bigram]()
} }
/// ///
open func hasUnigramsFor(key: String) -> Bool { open func hasUnigramsFor(key: String) -> Bool {
key.count != 0 key.count != 0
} }

View File

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

View File

@ -27,7 +27,7 @@ extension Megrez {
/// ///
@frozen public struct Unigram: Equatable, CustomStringConvertible { @frozen public struct Unigram: Equatable, CustomStringConvertible {
/// ///
public var keyValue: KeyValuePair public var keyValue: KeyValuePaired
/// ///
public var score: Double public var score: Double
/// ///
@ -39,7 +39,7 @@ extension Megrez {
/// - Parameters: /// - Parameters:
/// - keyValue: /// - keyValue:
/// - score: /// - score:
public init(keyValue: KeyValuePair, score: Double) { public init(keyValue: KeyValuePaired, score: Double) {
self.keyValue = keyValue self.keyValue = keyValue
self.score = score self.score = score
} }
@ -49,11 +49,6 @@ extension Megrez {
hasher.combine(score) hasher.combine(score)
} }
//
public static func compareScore(a: Unigram, b: Unigram) -> Bool {
a.score > b.score
}
public static func == (lhs: Unigram, rhs: Unigram) -> Bool { public static func == (lhs: Unigram, rhs: Unigram) -> Bool {
lhs.keyValue == rhs.keyValue && lhs.score == rhs.score 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 { extension Megrez {
/// ///
@frozen public struct KeyValuePair: Equatable, Hashable, Comparable, CustomStringConvertible { @frozen public struct KeyValuePaired: Equatable, Hashable, Comparable, CustomStringConvertible {
/// ///
public var key: String public var key: String
/// ///
public var value: String public var value: String
/// ///
public var description: String { public var description: String { "(" + key + "," + value + ")" }
"(" + key + "," + value + ")" /// false
} public var isValid: Bool { !key.isEmpty && !value.isEmpty }
/// ()
public var toNGramKey: String { !isValid ? "()" : "(" + key + "," + value + ")" }
/// ///
/// - Parameters: /// - Parameters:
/// - key: /// - key:
/// - value: /// - value:
@ -49,23 +51,23 @@ extension Megrez {
hasher.combine(value) 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 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) (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) (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) (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) (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 AVFoundation
import Foundation import Foundation
public class clsSFX { public enum clsSFX {
static func beep() { static func beep() {
let filePath = Bundle.main.path(forResource: mgrPrefs.shouldNotFartInLieuOfBeep ? "Beep" : "Fart", ofType: "m4a")! let filePath = Bundle.main.path(forResource: mgrPrefs.shouldNotFartInLieuOfBeep ? "Beep" : "Fart", ofType: "m4a")!
let fileURL = URL(fileURLWithPath: filePath) let fileURL = URL(fileURLWithPath: filePath)
@ -33,4 +33,15 @@ public class clsSFX {
AudioServicesCreateSystemSoundID(fileURL as CFURL, &soundID) AudioServicesCreateSystemSoundID(fileURL as CFURL, &soundID)
AudioServicesPlaySystemSound(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 CHS Core Dict..." = "Loading CHS Core Dict...";
"Loading CHT Core Dict..." = "Loading CHT Core Dict..."; "Loading CHT Core Dict..." = "Loading CHT Core Dict...";
"Core Dict loading complete." = "Core Dict loading complete."; "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. // The followings are the category names used in the Symbol menu.
"catCommonSymbols" = "CommonSymbols"; "catCommonSymbols" = "CommonSymbols";
@ -88,6 +89,7 @@
"Apple Chewing - Dachen" = "Apple Chewing - Dachen"; "Apple Chewing - Dachen" = "Apple Chewing - Dachen";
"Apple Chewing - Eten Traditional" = "Apple Chewing - Eten Traditional"; "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."; "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)"; "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 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"; "Auto-convert traditional Chinese glyphs to KangXi characters" = "Auto-convert traditional Chinese glyphs to KangXi characters";
@ -113,7 +115,7 @@
"Debug Mode" = "Debug Mode"; "Debug Mode" = "Debug Mode";
"Dictionary" = "Dictionary"; "Dictionary" = "Dictionary";
"Emulating select-candidate-per-character mode" = "Emulating select-candidate-per-character mode"; "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 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)"; "Enable symbol input support (incl. certain emoji symbols)" = "Enable symbol input support (incl. certain emoji symbols)";
"English" = "English"; "English" = "English";

View File

@ -62,6 +62,7 @@
"Loading CHS Core Dict..." = "Loading CHS Core Dict..."; "Loading CHS Core Dict..." = "Loading CHS Core Dict...";
"Loading CHT Core Dict..." = "Loading CHT Core Dict..."; "Loading CHT Core Dict..." = "Loading CHT Core Dict...";
"Core Dict loading complete." = "Core Dict loading complete."; "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. // The followings are the category names used in the Symbol menu.
"catCommonSymbols" = "CommonSymbols"; "catCommonSymbols" = "CommonSymbols";
@ -88,6 +89,7 @@
"Apple Chewing - Dachen" = "Apple Chewing - Dachen"; "Apple Chewing - Dachen" = "Apple Chewing - Dachen";
"Apple Chewing - Eten Traditional" = "Apple Chewing - Eten Traditional"; "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."; "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)"; "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 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"; "Auto-convert traditional Chinese glyphs to KangXi characters" = "Auto-convert traditional Chinese glyphs to KangXi characters";
@ -113,7 +115,7 @@
"Debug Mode" = "Debug Mode"; "Debug Mode" = "Debug Mode";
"Dictionary" = "Dictionary"; "Dictionary" = "Dictionary";
"Emulating select-candidate-per-character mode" = "Emulating select-candidate-per-character mode"; "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 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)"; "Enable symbol input support (incl. certain emoji symbols)" = "Enable symbol input support (incl. certain emoji symbols)";
"English" = "English"; "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" = "臨時記憶資料を整う";
// The followings are the category names used in the Symbol menu. // The followings are the category names used in the Symbol menu.
"catCommonSymbols" = "常用"; "catCommonSymbols" = "常用";
@ -88,6 +89,7 @@
"Apple Chewing - Dachen" = "Apple 大千注音キーボード"; "Apple Chewing - Dachen" = "Apple 大千注音キーボード";
"Apple Chewing - Eten Traditional" = "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 動態注音キーボード (大千と倚天伝統) を使うには、共通語分析器の配列を大千と設定すべきである。"; "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 新注音入力のやり方"; "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 JIS Shinjitai characters" = "入力した繁体字を日文 JIS 新字体と自動変換";
"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" = "漢字1つづつ全候補選択入力モード"; "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 Space key for calling candidate window" = "Space キーで入力候補を呼び出す";
"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" = "精简临时记忆语汇资料";
// The followings are the category names used in the Symbol menu. // The followings are the category names used in the Symbol menu.
"catCommonSymbols" = "常用"; "catCommonSymbols" = "常用";
@ -88,6 +89,7 @@
"Apple Chewing - Dachen" = "Apple 大千注音键盘排列"; "Apple Chewing - Dachen" = "Apple 大千注音键盘排列";
"Apple Chewing - Eten Traditional" = "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 动态注音键盘布局(大千与倚天)要求普通话/国音分析器得配置为大千排列。"; "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 微软新注音风格"; "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 JIS Shinjitai characters" = "自动将繁体中文字转为日文 JIS 新字体";
"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" = "模拟 90 年代前期注音逐字选字输入风格"; "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 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" = "精簡臨時記憶語彙資料";
// The followings are the category names used in the Symbol menu. // The followings are the category names used in the Symbol menu.
"catCommonSymbols" = "常用"; "catCommonSymbols" = "常用";
@ -88,6 +89,7 @@
"Apple Chewing - Dachen" = "Apple 大千注音鍵盤佈局"; "Apple Chewing - Dachen" = "Apple 大千注音鍵盤佈局";
"Apple Chewing - Eten Traditional" = "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 動態注音鍵盤佈局(大千與倚天)要求普通話/國音分析器得配置為大千排列。"; "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 微軟新注音風格"; "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 JIS Shinjitai characters" = "自動將繁體中文字轉為日文 JIS 新字體";
"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" = "模擬 90 年代前期注音逐字選字輸入風格"; "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 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

@ -74,6 +74,11 @@ private class vwrCandidateUniversal: NSView {
@objc(setKeyLabels:displayedCandidates:) @objc(setKeyLabels:displayedCandidates:)
func set(keyLabels labels: [String], displayedCandidates candidates: [String]) { 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) let count = min(labels.count, candidates.count)
keyLabels = Array(labels[0..<count]) keyLabels = Array(labels[0..<count])
displayedCandidates = Array(candidates[0..<count]) displayedCandidates = Array(candidates[0..<count])
@ -209,7 +214,9 @@ private class vwrCandidateUniversal: NSView {
} }
case InputMode.imeModeCHT: case InputMode.imeModeCHT:
if #available(macOS 12.0, *) { 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: default:
break break
@ -279,7 +286,9 @@ private class vwrCandidateUniversal: NSView {
} }
case InputMode.imeModeCHT: case InputMode.imeModeCHT:
if #available(macOS 12.0, *) { 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: default:
break break

View File

@ -37,6 +37,8 @@ struct suiPrefPaneDictionary: View {
forKey: UserDef.kSymbolInputEnabled) forKey: UserDef.kSymbolInputEnabled)
@State private var selAllowBoostingSingleKanjiAsUserPhrase: Bool = UserDefaults.standard.bool( @State private var selAllowBoostingSingleKanjiAsUserPhrase: Bool = UserDefaults.standard.bool(
forKey: UserDef.kAllowBoostingSingleKanjiAsUserPhrase) forKey: UserDef.kAllowBoostingSingleKanjiAsUserPhrase)
@State private var selFetchSuggestionsFromUserOverrideModel: Bool = UserDefaults.standard.bool(
forKey: UserDef.kFetchSuggestionsFromUserOverrideModel)
private let contentWidth: Double = { private let contentWidth: Double = {
switch mgrPrefs.appleLanguages[0] { switch mgrPrefs.appleLanguages[0] {
case "ja": case "ja":
@ -116,7 +118,7 @@ struct suiPrefPaneDictionary: View {
} }
Divider() Divider()
Toggle( Toggle(
LocalizedStringKey("Enable CNS11643 Support (2022-04-27)"), LocalizedStringKey("Enable CNS11643 Support (2022-06-15)"),
isOn: $selEnableCNS11643 isOn: $selEnableCNS11643
) )
.onChange(of: selEnableCNS11643) { value in .onChange(of: selEnableCNS11643) { value in
@ -138,6 +140,13 @@ struct suiPrefPaneDictionary: View {
.onChange(of: selAllowBoostingSingleKanjiAsUserPhrase) { value in .onChange(of: selAllowBoostingSingleKanjiAsUserPhrase) { value in
mgrPrefs.allowBoostingSingleKanjiAsUserPhrase = value 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( ComboBox(items: mgrPrefs.suggestedCandidateKeys, text: $selSelectionKeys).frame(width: 180).onChange(
of: selSelectionKeys of: selSelectionKeys
) { value in ) { value in
let keys: String = (value.trimmingCharacters(in: .whitespacesAndNewlines) as String).charDeDuplicate let keys: String = value.trimmingCharacters(in: .whitespacesAndNewlines).deduplicate
do { do {
try mgrPrefs.validate(candidateKeys: keys) try mgrPrefs.validate(candidateKeys: keys)
mgrPrefs.candidateKeys = keys mgrPrefs.candidateKeys = keys

View File

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

View File

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?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"> <document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="20037" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies> <dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="20037"/> <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="20037"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
@ -681,9 +680,9 @@
</connections> </connections>
</textField> </textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="p7V-IN-OTr"> <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> <constraints>
<constraint firstAttribute="height" constant="16" id="Ieq-hl-01l"/> <constraint firstAttribute="height" constant="16" id="reU-bw-htf"/>
</constraints> </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"> <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"/> <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
@ -694,9 +693,9 @@
</connections> </connections>
</button> </button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="O4B-Z2-XYi"> <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> <constraints>
<constraint firstAttribute="height" constant="16" id="2gi-B5-vjz"/> <constraint firstAttribute="height" constant="16" id="UxA-rj-D5M"/>
</constraints> </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"> <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"/> <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
@ -736,11 +735,11 @@
</connections> </connections>
</button> </button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Yaj-QY-7xV" userLabel="chkCNSSupport"> <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> <constraints>
<constraint firstAttribute="height" constant="16" id="vin-Cj-dDq"/> <constraint firstAttribute="height" constant="16" id="guU-8E-HAX"/>
</constraints> </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"/> <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="cellTitle"/> <font key="font" metaFont="cellTitle"/>
</buttonCell> </buttonCell>
@ -750,9 +749,9 @@
</connections> </connections>
</button> </button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Hyc-Nw-dET"> <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> <constraints>
<constraint firstAttribute="height" constant="16" id="drz-HW-cX1"/> <constraint firstAttribute="height" constant="16" id="B9v-pe-shJ"/>
</constraints> </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"> <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"/> <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"/> <binding destination="32" name="value" keyPath="values.AllowBoostingSingleKanjiAsUserPhrase" id="jiw-1V-C7x"/>
</connections> </connections>
</button> </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> </subviews>
<constraints> <constraints>
<constraint firstItem="s7t-Kk-EPu" firstAttribute="bottom" secondItem="MPN-np-SbT" secondAttribute="bottom" id="0Fo-ya-hQ9"/> <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="leading" secondItem="Yaj-QY-7xV" secondAttribute="leading" id="0Rn-L2-gTb"/>
<constraint firstItem="O4B-Z2-XYi" firstAttribute="top" secondItem="p7V-IN-OTr" secondAttribute="bottom" constant="6" symbolic="YES" id="6H0-cT-DDa"/> <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 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="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="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="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="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="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="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="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="Yaj-QY-7xV" firstAttribute="leading" secondItem="Hyc-Nw-dET" secondAttribute="leading" id="ehf-lN-Jqs"/>
<constraint firstItem="O4B-Z2-XYi" firstAttribute="trailing" secondItem="Yaj-QY-7xV" secondAttribute="trailing" id="pZC-Vr-DTa"/> <constraint firstItem="p7V-IN-OTr" firstAttribute="top" secondItem="s7t-Kk-EPu" secondAttribute="bottom" constant="13.5" id="ggR-mS-i0Q"/>
<constraint firstItem="p7V-IN-OTr" firstAttribute="trailing" secondItem="O4B-Z2-XYi" secondAttribute="trailing" id="s0C-8F-wWy"/> <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="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"/> <constraint firstItem="jXe-xz-9Sd" firstAttribute="baseline" secondItem="MPN-np-SbT" secondAttribute="baseline" id="wys-ML-2Q2"/>
</constraints> </constraints>

View File

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

View File

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

View File

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

View File

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

View File

@ -52,14 +52,18 @@ extension String {
// Tab to ASCII Space // Tab to ASCII Space
// ASCII // ASCII
strProcessed.regReplace(pattern: #"( +| +| +|\t+)+"#, replaceWith: " ") 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) == " " { // if strProcessed.prefix(1) == " " { //
strProcessed.removeFirst() strProcessed.removeFirst()
} }
if strProcessed.suffix(1) == " " { // if strProcessed.suffix(1) == " " { //
strProcessed.removeLast() strProcessed.removeLast()
} }
if cnvHYPYtoBPMF { if cnvHYPYtoBPMF {
// Step 2: Convert HanyuPinyin to Bopomofo. // Step 2: Convert HanyuPinyin to Bopomofo.
// //

View File

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

View File

@ -10,9 +10,10 @@
5B0AF8B527B2C8290096FE54 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B0AF8B427B2C8290096FE54 /* StringExtension.swift */; }; 5B0AF8B527B2C8290096FE54 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B0AF8B427B2C8290096FE54 /* StringExtension.swift */; };
5B11328927B94CFB00E58451 /* AppleKeyboardConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B11328827B94CFB00E58451 /* AppleKeyboardConverter.swift */; }; 5B11328927B94CFB00E58451 /* AppleKeyboardConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B11328827B94CFB00E58451 /* AppleKeyboardConverter.swift */; };
5B242403284B0D6500520FE4 /* ctlCandidateUniversal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B242402284B0D6500520FE4 /* ctlCandidateUniversal.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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 5B40730C281672610023DFFF /* lmAssociates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B407309281672610023DFFF /* lmAssociates.swift */; };
5B40730D281672610023DFFF /* lmReplacements.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B40730A281672610023DFFF /* lmReplacements.swift */; }; 5B40730D281672610023DFFF /* lmReplacements.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B40730A281672610023DFFF /* lmReplacements.swift */; };
5B54E743283A7D89001ECBDC /* lmCoreNS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B54E742283A7D89001ECBDC /* lmCoreNS.swift */; }; 5B54E743283A7D89001ECBDC /* lmCoreNS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B54E742283A7D89001ECBDC /* lmCoreNS.swift */; };
@ -128,6 +129,13 @@
remoteGlobalIDString = 5BD05BB727B2A429004C4F1D; remoteGlobalIDString = 5BD05BB727B2A429004C4F1D;
remoteInfo = vChewingPhraseEditor; remoteInfo = vChewingPhraseEditor;
}; };
5B2F2BB7286216A500B8557B /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 6A0D4E9415FC0CFA00ABF4B3 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 6A0D4EA115FC0D2D00ABF4B3;
remoteInfo = vChewing;
};
6ACA420015FC1DCC00935EF6 /* PBXContainerItemProxy */ = { 6ACA420015FC1DCC00935EF6 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = 6A0D4E9415FC0CFA00ABF4B3 /* Project object */; containerPortal = 6A0D4E9415FC0CFA00ABF4B3 /* Project object */;
@ -185,9 +193,11 @@
5B18BA7427C7BD8C0056EB19 /* LICENSE-CHT.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = "LICENSE-CHT.txt"; sourceTree = "<group>"; }; 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>"; }; 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>"; }; 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>"; }; 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; }; 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; }; 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; }; 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>"; }; 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; }; 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; }; 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; }; 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; }; 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; }; 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; }; 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 */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
5B2F2BB0286216A500B8557B /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
5BD05BB527B2A429004C4F1D /* Frameworks */ = { 5BD05BB527B2A429004C4F1D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@ -370,6 +387,14 @@
name = MiscRootFiles; name = MiscRootFiles;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
5B2F2BB4286216A500B8557B /* vChewingTests */ = {
isa = PBXGroup;
children = (
5B2F2BB5286216A500B8557B /* vChewingTests.swift */,
);
path = vChewingTests;
sourceTree = "<group>";
};
5B407308281672610023DFFF /* SubLMs */ = { 5B407308281672610023DFFF /* SubLMs */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -464,8 +489,8 @@
5B407308281672610023DFFF /* SubLMs */, 5B407308281672610023DFFF /* SubLMs */,
5B949BDA2816DDBC00D87B5D /* LMConsolidator.swift */, 5B949BDA2816DDBC00D87B5D /* LMConsolidator.swift */,
5BD0113A28180D6100609769 /* LMInstantiator.swift */, 5BD0113A28180D6100609769 /* LMInstantiator.swift */,
5B3A87BB28597CDB0090E163 /* LMSymbolNode.swift */,
5BAEFACF28012565001F42C9 /* mgrLangModel.swift */, 5BAEFACF28012565001F42C9 /* mgrLangModel.swift */,
5B3A87BB28597CDB0090E163 /* SymbolNode.swift */,
); );
path = LangModelRelated; path = LangModelRelated;
sourceTree = "<group>"; sourceTree = "<group>";
@ -702,6 +727,7 @@
6ACA41E715FC1D9000935EF6 /* Installer */, 6ACA41E715FC1D9000935EF6 /* Installer */,
6A0D4EC215FC0D3C00ABF4B3 /* Source */, 6A0D4EC215FC0D3C00ABF4B3 /* Source */,
5BD05BB927B2A429004C4F1D /* UserPhraseEditor */, 5BD05BB927B2A429004C4F1D /* UserPhraseEditor */,
5B2F2BB4286216A500B8557B /* vChewingTests */,
6A0D4EA315FC0D2D00ABF4B3 /* Products */, 6A0D4EA315FC0D2D00ABF4B3 /* Products */,
D47D73C127A7200500255A50 /* Frameworks */, D47D73C127A7200500255A50 /* Frameworks */,
5BDC5CB127C28E8B00E1CCE2 /* KeyboardExtension */, 5BDC5CB127C28E8B00E1CCE2 /* KeyboardExtension */,
@ -715,6 +741,7 @@
6A0D4EA215FC0D2D00ABF4B3 /* vChewing.app */, 6A0D4EA215FC0D2D00ABF4B3 /* vChewing.app */,
6ACA41CB15FC1D7500935EF6 /* vChewingInstaller.app */, 6ACA41CB15FC1D7500935EF6 /* vChewingInstaller.app */,
5BD05BB827B2A429004C4F1D /* vChewingPhraseEditor.app */, 5BD05BB827B2A429004C4F1D /* vChewingPhraseEditor.app */,
5B2F2BB3286216A500B8557B /* vChewingTests.xctest */,
); );
name = Products; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
@ -769,7 +796,7 @@
6A0D4F1915FC0EB100ABF4B3 /* 5_LanguageModel.swift */, 6A0D4F1915FC0EB100ABF4B3 /* 5_LanguageModel.swift */,
6A0D4F1415FC0EB100ABF4B3 /* 6_Bigram.swift */, 6A0D4F1415FC0EB100ABF4B3 /* 6_Bigram.swift */,
6A0D4F1D15FC0EB100ABF4B3 /* 6_Unigram.swift */, 6A0D4F1D15FC0EB100ABF4B3 /* 6_Unigram.swift */,
6A0D4F1815FC0EB100ABF4B3 /* 7_KeyValuePair.swift */, 6A0D4F1815FC0EB100ABF4B3 /* 7_KeyValuePaired.swift */,
); );
path = Megrez; path = Megrez;
sourceTree = "<group>"; sourceTree = "<group>";
@ -802,6 +829,24 @@
/* End PBXGroup section */ /* End PBXGroup section */
/* Begin PBXNativeTarget 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 */ = { 5BD05BB727B2A429004C4F1D /* vChewingPhraseEditor */ = {
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = 5BD05BC927B2A42A004C4F1D /* Build configuration list for PBXNativeTarget "vChewingPhraseEditor" */; buildConfigurationList = 5BD05BC927B2A42A004C4F1D /* Build configuration list for PBXNativeTarget "vChewingPhraseEditor" */;
@ -870,9 +915,13 @@
6A0D4E9415FC0CFA00ABF4B3 /* Project object */ = { 6A0D4E9415FC0CFA00ABF4B3 /* Project object */ = {
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
LastSwiftUpdateCheck = 1320; LastSwiftUpdateCheck = 1340;
LastUpgradeCheck = 1400; LastUpgradeCheck = 1400;
TargetAttributes = { TargetAttributes = {
5B2F2BB2286216A500B8557B = {
CreatedOnToolsVersion = 13.4.1;
TestTargetID = 6A0D4EA115FC0D2D00ABF4B3;
};
5BD05BB727B2A429004C4F1D = { 5BD05BB727B2A429004C4F1D = {
CreatedOnToolsVersion = 13.2; CreatedOnToolsVersion = 13.2;
LastSwiftMigration = 1320; LastSwiftMigration = 1320;
@ -909,11 +958,19 @@
6A0D4EA115FC0D2D00ABF4B3 /* vChewing */, 6A0D4EA115FC0D2D00ABF4B3 /* vChewing */,
6ACA41CA15FC1D7500935EF6 /* vChewingInstaller */, 6ACA41CA15FC1D7500935EF6 /* vChewingInstaller */,
5BD05BB727B2A429004C4F1D /* vChewingPhraseEditor */, 5BD05BB727B2A429004C4F1D /* vChewingPhraseEditor */,
5B2F2BB2286216A500B8557B /* vChewingTests */,
); );
}; };
/* End PBXProject section */ /* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */ /* Begin PBXResourcesBuildPhase section */
5B2F2BB1286216A500B8557B /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
5BD05BB627B2A429004C4F1D /* Resources */ = { 5BD05BB627B2A429004C4F1D /* Resources */ = {
isa = PBXResourcesBuildPhase; isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@ -1029,6 +1086,14 @@
/* End PBXShellScriptBuildPhase section */ /* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */
5B2F2BAF286216A500B8557B /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
5B2F2BB6286216A500B8557B /* vChewingTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
5BD05BB427B2A429004C4F1D /* Sources */ = { 5BD05BB427B2A429004C4F1D /* Sources */ = {
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@ -1071,7 +1136,7 @@
D456576E279E4F7B00DF6BC9 /* InputSignal.swift in Sources */, D456576E279E4F7B00DF6BC9 /* InputSignal.swift in Sources */,
5BA9FD1027FEDB6B002DE248 /* suiPrefPaneKeyboard.swift in Sources */, 5BA9FD1027FEDB6B002DE248 /* suiPrefPaneKeyboard.swift in Sources */,
5B3133BF280B229700A4A505 /* KeyHandler_States.swift in Sources */, 5B3133BF280B229700A4A505 /* KeyHandler_States.swift in Sources */,
5B3A87BC28597CDB0090E163 /* SymbolNode.swift in Sources */, 5B3A87BC28597CDB0090E163 /* LMSymbolNode.swift in Sources */,
5BA9FD4327FEF3C8002DE248 /* Preferences.swift in Sources */, 5BA9FD4327FEF3C8002DE248 /* Preferences.swift in Sources */,
5BA9FD4427FEF3C8002DE248 /* SegmentedControlStyleViewController.swift in Sources */, 5BA9FD4427FEF3C8002DE248 /* SegmentedControlStyleViewController.swift in Sources */,
D47F7DCE278BFB57002F9DD7 /* ctlPrefWindow.swift in Sources */, D47F7DCE278BFB57002F9DD7 /* ctlPrefWindow.swift in Sources */,
@ -1084,7 +1149,7 @@
5B11328927B94CFB00E58451 /* AppleKeyboardConverter.swift in Sources */, 5B11328927B94CFB00E58451 /* AppleKeyboardConverter.swift in Sources */,
5B54E743283A7D89001ECBDC /* lmCoreNS.swift in Sources */, 5B54E743283A7D89001ECBDC /* lmCoreNS.swift in Sources */,
5B62A32927AE77D100A19448 /* FSEventStreamHelper.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 */, 5B62A33627AE795800A19448 /* mgrPrefs.swift in Sources */,
5B38F5A4281E2E49007D5F5D /* 5_LanguageModel.swift in Sources */, 5B38F5A4281E2E49007D5F5D /* 5_LanguageModel.swift in Sources */,
5BAEFAD028012565001F42C9 /* mgrLangModel.swift in Sources */, 5BAEFAD028012565001F42C9 /* mgrLangModel.swift in Sources */,
@ -1137,6 +1202,11 @@
target = 5BD05BB727B2A429004C4F1D /* vChewingPhraseEditor */; target = 5BD05BB727B2A429004C4F1D /* vChewingPhraseEditor */;
targetProxy = 5B0AF8B227B2C4E20096FE54 /* PBXContainerItemProxy */; targetProxy = 5B0AF8B227B2C4E20096FE54 /* PBXContainerItemProxy */;
}; };
5B2F2BB8286216A500B8557B /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 6A0D4EA115FC0D2D00ABF4B3 /* vChewing */;
targetProxy = 5B2F2BB7286216A500B8557B /* PBXContainerItemProxy */;
};
5B707CEA27D9F47A0099EF99 /* PBXTargetDependency */ = { 5B707CEA27D9F47A0099EF99 /* PBXTargetDependency */ = {
isa = PBXTargetDependency; isa = PBXTargetDependency;
productRef = 5B707CE927D9F47A0099EF99 /* OpenCC */; productRef = 5B707CE927D9F47A0099EF99 /* OpenCC */;
@ -1284,6 +1354,84 @@
/* End PBXVariantGroup section */ /* End PBXVariantGroup section */
/* Begin XCBuildConfiguration 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 */ = { 5BD05BC727B2A42A004C4F1D /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
@ -1304,7 +1452,7 @@
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1971; CURRENT_PROJECT_VERSION = 1972;
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = dwarf; DEBUG_INFORMATION_FORMAT = dwarf;
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
@ -1328,7 +1476,7 @@
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 10.11.5; MACOSX_DEPLOYMENT_TARGET = 10.11.5;
MARKETING_VERSION = 1.7.1; MARKETING_VERSION = 1.7.2;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewing.vChewingPhraseEditor; PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewing.vChewingPhraseEditor;
@ -1361,7 +1509,7 @@
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1971; CURRENT_PROJECT_VERSION = 1972;
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO; ENABLE_NS_ASSERTIONS = NO;
@ -1381,7 +1529,7 @@
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 10.11.5; MACOSX_DEPLOYMENT_TARGET = 10.11.5;
MARKETING_VERSION = 1.7.1; MARKETING_VERSION = 1.7.2;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewing.vChewingPhraseEditor; PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewing.vChewingPhraseEditor;
@ -1499,7 +1647,7 @@
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1971; CURRENT_PROJECT_VERSION = 1972;
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = ""; DEVELOPMENT_TEAM = "";
@ -1535,7 +1683,7 @@
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 10.11.5; MACOSX_DEPLOYMENT_TARGET = 10.11.5;
MARKETING_VERSION = 1.7.1; MARKETING_VERSION = 1.7.2;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.inputmethod.vChewing; PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.inputmethod.vChewing;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
@ -1567,7 +1715,7 @@
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1971; CURRENT_PROJECT_VERSION = 1972;
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_ASSET_PATHS = "";
@ -1598,7 +1746,7 @@
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 10.11.5; MACOSX_DEPLOYMENT_TARGET = 10.11.5;
MARKETING_VERSION = 1.7.1; MARKETING_VERSION = 1.7.2;
PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.inputmethod.vChewing; PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.inputmethod.vChewing;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
@ -1625,7 +1773,7 @@
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1971; CURRENT_PROJECT_VERSION = 1972;
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = ""; DEVELOPMENT_TEAM = "";
GCC_C_LANGUAGE_STANDARD = gnu99; GCC_C_LANGUAGE_STANDARD = gnu99;
@ -1651,7 +1799,7 @@
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 10.11.5; MACOSX_DEPLOYMENT_TARGET = 10.11.5;
MARKETING_VERSION = 1.7.1; MARKETING_VERSION = 1.7.2;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "org.atelierInmu.vChewing.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_BUNDLE_IDENTIFIER = "org.atelierInmu.vChewing.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
@ -1678,7 +1826,7 @@
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1971; CURRENT_PROJECT_VERSION = 1972;
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = ""; DEVELOPMENT_TEAM = "";
@ -1699,7 +1847,7 @@
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 10.11.5; 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_BUNDLE_IDENTIFIER = "org.atelierInmu.vChewing.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
@ -1713,6 +1861,15 @@
/* End XCBuildConfiguration section */ /* End XCBuildConfiguration section */
/* Begin XCConfigurationList 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" */ = { 5BD05BC927B2A42A004C4F1D /* Build configuration list for PBXNativeTarget "vChewingPhraseEditor" */ = {
isa = XCConfigurationList; isa = XCConfigurationList;
buildConfigurations = ( buildConfigurations = (

View File

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