Operation Longinus - phase 2. // Merge pull request #72

This commit is contained in:
ShikiSuen 2022-05-04 22:20:27 +08:00 committed by GitHub
commit 301595ce65
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
133 changed files with 12903 additions and 14660 deletions

View File

@ -3,7 +3,7 @@
"accessLevel" : "private" "accessLevel" : "private"
}, },
"indentation" : { "indentation" : {
"tabs" : 1 "spaces" : 2
}, },
"indentConditionalCompilationBlocks" : true, "indentConditionalCompilationBlocks" : true,
"indentSwitchCaseLabels" : true, "indentSwitchCaseLabels" : true,
@ -51,6 +51,6 @@
"UseWhereClausesInForLoops" : false, "UseWhereClausesInForLoops" : false,
"ValidateDocumentationComments" : false "ValidateDocumentationComments" : false
}, },
"tabWidth" : 4, "tabWidth" : 8,
"version" : 1 "version" : 1
} }

View File

@ -1,14 +1,14 @@
# 威注音輸入法研發參與相關說明 # 威注音輸入法研發參與相關說明
威注音輸入法歡迎有人參與。但為了不讓參與者們浪費各自的熱情,特設此文以說明該專案目前最需要協助的地方 威注音輸入法歡迎有熱心的志願者們參與
1. 有人能用 Swift 將該專案內這兩個源自 LibFormosa 的組件套件重寫: 威注音目前的 codebase 更能代表一個先進的 macOS 輸入法雛形專案的形態。目前的 dev 分支除了 Mandarin 模組(以及其與 KeyHandler 的對接的部分)以外被威注音使用的部分全都是清一色的 Swift codebase一目了然方便他人參與比某些其它開源品牌旗下的專案更具程式方面的生命力。為什麼這樣講呢那些傳統開源品牌的專案主要使用 C++ 這門不太友好的語言Mandarin 模組現在對我而言仍舊是天書,一大堆針對記憶體指針的操作完全看不懂。搞不清楚在這一層之上的功能邏輯的話,就無法制定 Swift 版的 coding 策略),這也是我這次用 Swift 重寫了語言模型引擎的原因(也是為後來者行方便)。
為了不讓參與者們浪費各自的熱情,特設此文以說明該專案目前最需要協助的地方。
1. 有人能用 Swift 將該專案內的這個源自 LibFormosa 的組件套件重寫:
- Mandarin 組件,用以分析普通話音韻數據、創建且控制 Syllable Composer 注音拼識組件。 - Mandarin 組件,用以分析普通話音韻數據、創建且控制 Syllable Composer 注音拼識組件。
- Gramambular 套裝,這包括了 Source 資料夾下的其餘全部的 (Obj)C(++) 檔案LMConsolidator 除外)。 - 一堆記憶體指針操作,實在看不懂這個組件的處理邏輯是什麼,無能為力。
- LMConsolidator 有 Swift 版本,已經用於威注音語彙編輯器內。給主程式用 C++ 版本僅為了與 Gramambular 協作方便。
- 這也包括了所有與 Language Model 有關的實現,因為都是 Gramambular 內的某個語言模組 Protocol 衍生出來的東西。
- LMInstantiator 是用來將語言模組副本化的組件,原本不屬於 Gramambular但與其衍生的各類語言模組高度耦合。
- KeyValueBlobReader 不屬於 Gramambular但與其衍生的各類語言模組高度耦合、也與 KeyHandler 高度耦合。
2. 讓 Alt+波浪鍵選單能夠在諸如 MS Word 以及終端機內正常工作(可以用方向鍵控制高亮候選內容,等)。 2. 讓 Alt+波浪鍵選單能夠在諸如 MS Word 以及終端機內正常工作(可以用方向鍵控制高亮候選內容,等)。
- 原理上而言恐怕得欺騙當前正在接受輸入的應用、使其誤以為當前有組字區。這只是推測。 - 原理上而言恐怕得欺騙當前正在接受輸入的應用、使其誤以為當前有組字區。這只是推測。
3. SQLite 實現。 3. SQLite 實現。
@ -25,11 +25,9 @@
該專案對源碼格式有規範,且 Swift 與其他 (Obj)C(++) 系語言持不同規範: 該專案對源碼格式有規範,且 Swift 與其他 (Obj)C(++) 系語言持不同規範:
- Swift: 採 [Apple 官方 Swift-Format](https://github.com/apple/swift-format),且施加如下例外修改項目: - Swift: 採 [Apple 官方 Swift-Format](https://github.com/apple/swift-format),且施加如下例外修改項目:
- Indentation 僅使用 `"indentation" : { "tabs" : 1 },`,不以空格來縮進。
- `"indentSwitchCaseLabels" : true,` - `"indentSwitchCaseLabels" : true,`
- `"lineLength" : 120,` - `"lineLength" : 120,`
- `"NoBlockComments" : false,` - `"NoBlockComments" : false,`
- `"tabWidth" : 4,`
- `"OnlyOneTrailingClosureArgument" : false,` // SwiftUI 相容 - `"OnlyOneTrailingClosureArgument" : false,` // SwiftUI 相容
- `"UseTripleSlashForDocumentationComments" : false,` - `"UseTripleSlashForDocumentationComments" : false,`
- `"DontRepeatTypeInStaticProperties" : false,` - `"DontRepeatTypeInStaticProperties" : false,`
@ -37,6 +35,6 @@
- 該規範以四個西文半形空格為行縮進單位。 - 該規範以四個西文半形空格為行縮進單位。
- 由於今後不會再用這類語言給該倉庫新增內容,所以相關規範就不改動了。 - 由於今後不會再用這類語言給該倉庫新增內容,所以相關規範就不改動了。
至於對 Swift 檔案改採 1-Tab 縮進,則是為了在尊重所有用戶的需求的同時、最大程度上節約檔案體積。使用者可自行修改 Xcode 的預設 Tab 縮進尺寸 之前,為了節省檔案體積,曾經對 Swift 檔案改採 1-Tab 縮進。然而,這會導致 Gitee 等線上 git 專案管理網站內的顯示變成 8-Space 縮進。於是,該專案對 Swift 檔案又改回了 2-Spaces 縮進
$ EOF. $ EOF.

View File

@ -28,6 +28,7 @@ clang-format: clang-format-swift clang-format-cpp
clang-format-swift: clang-format-swift:
@git ls-files --exclude-standard | grep -E '\.swift$$' | xargs swift-format format --in-place --configuration ./.clang-format-swift.json --parallel @git ls-files --exclude-standard | grep -E '\.swift$$' | xargs swift-format format --in-place --configuration ./.clang-format-swift.json --parallel
@git ls-files --exclude-standard | grep -E '\.swift$$' | xargs swift-format lint --configuration ./.clang-format-swift.json --parallel
clang-format-cpp: clang-format-cpp:
@git ls-files --exclude-standard | grep -E '\.(cpp|hpp|c|cc|cxx|hxx|ixx|h|m|mm|hh)$$' | xargs clang-format -i @git ls-files --exclude-standard | grep -E '\.(cpp|hpp|c|cc|cxx|hxx|ixx|h|m|mm|hh)$$' | xargs clang-format -i

View File

@ -48,10 +48,6 @@ let package = Package(
"src/UTF8StringSliceTest.cpp", "src/UTF8StringSliceTest.cpp",
"src/UTF8UtilTest.cpp", "src/UTF8UtilTest.cpp",
"deps/google-benchmark", "deps/google-benchmark",
"deps/gtest-1.11.0",
"deps/pybind11-2.5.0",
"deps/rapidjson-1.1.0",
"deps/tclap-1.2.2",
"src/CmdLineOutput.hpp", "src/CmdLineOutput.hpp",
"src/Config.hpp", "src/Config.hpp",

View File

@ -0,0 +1,69 @@
// Copyright (c) 2019 and onwards Robert Muckle-Jones (Apache 2.0 License).
import Foundation
public class LineReader {
let encoding: String.Encoding
let chunkSize: Int
var fileHandle: FileHandle
let delimData: Data
var buffer: Data
var atEof: Bool
public init(
file: FileHandle, encoding: String.Encoding = .utf8,
chunkSize: Int = 4096
) throws {
let fileHandle = file
self.encoding = encoding
self.chunkSize = chunkSize
self.fileHandle = fileHandle
delimData = "\n".data(using: encoding)!
buffer = Data(capacity: chunkSize)
atEof = false
}
/// Return next line, or nil on EOF.
public func nextLine() -> String? {
// Read data chunks from file until a line delimiter is found:
while !atEof {
// get a data from the buffer up to the next delimiter
if let range = buffer.range(of: delimData) {
// convert data to a string
let line = String(data: buffer.subdata(in: 0..<range.lowerBound), encoding: encoding)!
// remove that data from the buffer
buffer.removeSubrange(0..<range.upperBound)
return line
}
let nextData = fileHandle.readData(ofLength: chunkSize)
if !nextData.isEmpty {
buffer.append(nextData)
} else {
// End of file or read error
atEof = true
if !buffer.isEmpty {
// Buffer contains last line in file (not terminated by delimiter).
let line = String(data: buffer as Data, encoding: encoding)!
return line
}
}
}
return nil
}
/// Start reading from the beginning of file.
public func rewind() {
fileHandle.seek(toFileOffset: 0)
buffer.count = 0
atEof = false
}
}
extension LineReader: Sequence {
public func makeIterator() -> AnyIterator<String> {
AnyIterator {
self.nextLine()
}
}
}

View File

@ -24,31 +24,20 @@ 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.
*/ */
#ifndef USERSYMBOLLM_H #import <Foundation/Foundation.h>
#define USERSYMBOLLM_H
#include "LanguageModel.h" NS_ASSUME_NONNULL_BEGIN
#include "UserPhrasesLM.h"
#include <iostream>
#include <map>
#include <string>
namespace vChewing @interface Composer : NSObject
{ + (BOOL)chkKeyValidity:(UniChar)charCode;
+ (BOOL)isBufferEmpty;
+ (void)clearBuffer;
+ (void)combineReadingKey:(UniChar)charCode;
+ (BOOL)checkWhetherToneMarkerConfirms;
+ (NSString *)getSyllableComposition;
+ (void)doBackSpaceToBuffer;
+ (NSString *)getComposition;
+ (void)ensureParser;
@end
class UserSymbolLM : public UserPhrasesLM NS_ASSUME_NONNULL_END
{
public:
bool allowConsolidation() override
{
return true;
}
float overridedValue() override
{
return -12.0;
}
};
} // namespace vChewing
#endif

116
Source/3rdParty/OVMandarin/Composer.mm vendored Normal file
View File

@ -0,0 +1,116 @@
// Copyright (c) 2011 and onwards The OpenVanilla Project (MIT License).
// All possible vChewing-specific modifications are of:
// (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 "Composer.hh"
#import "Mandarin.h"
#import "vChewing-Swift.h"
static Mandarin::BopomofoReadingBuffer *PhoneticBuffer;
@implementation Composer
+ (BOOL)chkKeyValidity:(UniChar)charCode
{
return PhoneticBuffer->isValidKey((char)charCode);
}
+ (BOOL)isBufferEmpty
{
return PhoneticBuffer->isEmpty();
}
+ (void)clearBuffer
{
PhoneticBuffer->clear();
}
+ (void)combineReadingKey:(UniChar)charCode
{
PhoneticBuffer->combineKey((char)charCode);
}
+ (BOOL)checkWhetherToneMarkerConfirms
{
return PhoneticBuffer->hasToneMarker();
}
+ (NSString *)getSyllableComposition
{
return [NSString stringWithUTF8String:PhoneticBuffer->syllable().composedString().c_str()];
}
+ (void)doBackSpaceToBuffer
{
PhoneticBuffer->backspace();
}
+ (NSString *)getComposition
{
return [NSString stringWithUTF8String:PhoneticBuffer->composedString().c_str()];
}
+ (void)ensureParser
{
if (PhoneticBuffer)
{
switch (mgrPrefs.mandarinParser)
{
case MandarinParserOfStandard:
PhoneticBuffer->setKeyboardLayout(Mandarin::BopomofoKeyboardLayout::StandardLayout());
break;
case MandarinParserOfEten:
PhoneticBuffer->setKeyboardLayout(Mandarin::BopomofoKeyboardLayout::ETenLayout());
break;
case MandarinParserOfHsu:
PhoneticBuffer->setKeyboardLayout(Mandarin::BopomofoKeyboardLayout::HsuLayout());
break;
case MandarinParserOfEen26:
PhoneticBuffer->setKeyboardLayout(Mandarin::BopomofoKeyboardLayout::ETen26Layout());
break;
case MandarinParserOfIBM:
PhoneticBuffer->setKeyboardLayout(Mandarin::BopomofoKeyboardLayout::IBMLayout());
break;
case MandarinParserOfMiTAC:
PhoneticBuffer->setKeyboardLayout(Mandarin::BopomofoKeyboardLayout::MiTACLayout());
break;
case MandarinParserOfFakeSeigyou:
PhoneticBuffer->setKeyboardLayout(Mandarin::BopomofoKeyboardLayout::FakeSeigyouLayout());
break;
case MandarinParserOfHanyuPinyin:
PhoneticBuffer->setKeyboardLayout(Mandarin::BopomofoKeyboardLayout::HanyuPinyinLayout());
break;
default:
PhoneticBuffer->setKeyboardLayout(Mandarin::BopomofoKeyboardLayout::StandardLayout());
mgrPrefs.mandarinParser = MandarinParserOfStandard;
}
}
else
{
PhoneticBuffer = new Mandarin::BopomofoReadingBuffer(Mandarin::BopomofoKeyboardLayout::StandardLayout());
}
}
@end

View File

@ -46,7 +46,7 @@ public class OpenCCBridge: NSObject {
/// ///
/// - Parameter string: Text in Original Script. /// - Parameter string: Text in Original Script.
/// - Returns: Text converted to Different Script. /// - Returns: Text converted to Different Script.
@objc public static func crossConvert(_ string: String) -> String? { public static func crossConvert(_ string: String) -> String? {
switch ctlInputMethod.currentKeyHandler.inputMode { switch ctlInputMethod.currentKeyHandler.inputMode {
case InputMode.imeModeCHS: case InputMode.imeModeCHS:
return shared.traditionalize?.convert(string) return shared.traditionalize?.convert(string)

View File

@ -30,5 +30,5 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@import Foundation; @import Foundation;
#import "KeyHandler.h" #import "CTools.h"
#import "mgrLangModel.h" #import "Composer.hh"

View File

@ -76,7 +76,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, ctlNonModalAlertWindowDelega
} }
} }
@objc func showPreferences() { func showPreferences() {
if ctlPrefWindowInstance == nil { if ctlPrefWindowInstance == nil {
ctlPrefWindowInstance = ctlPrefWindow.init(windowNibName: "frmPrefWindow") ctlPrefWindowInstance = ctlPrefWindow.init(windowNibName: "frmPrefWindow")
} }
@ -88,7 +88,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, ctlNonModalAlertWindowDelega
} }
// New About Window // New About Window
@objc func showAbout() { func showAbout() {
if ctlAboutWindowInstance == nil { if ctlAboutWindowInstance == nil {
ctlAboutWindowInstance = ctlAboutWindow.init(windowNibName: "frmAboutWindow") ctlAboutWindowInstance = ctlAboutWindow.init(windowNibName: "frmAboutWindow")
} }
@ -98,12 +98,10 @@ class AppDelegate: NSObject, NSApplicationDelegate, ctlNonModalAlertWindowDelega
NSApp.setActivationPolicy(.accessory) NSApp.setActivationPolicy(.accessory)
} }
@objc(checkForUpdate)
func checkForUpdate() { func checkForUpdate() {
checkForUpdate(forced: false) checkForUpdate(forced: false)
} }
@objc(checkForUpdateForced:)
func checkForUpdate(forced: Bool) { func checkForUpdate(forced: Bool) {
if checkTask != nil { if checkTask != nil {
// busy // busy

View File

@ -39,12 +39,12 @@ class AppleKeyboardConverter: NSObject {
"org.unknown.keylayout.vChewingIBM", "org.unknown.keylayout.vChewingIBM",
"org.unknown.keylayout.vChewingMiTAC", "org.unknown.keylayout.vChewingMiTAC",
] ]
@objc class func isDynamicBasicKeyboardLayoutEnabled() -> Bool { class func isDynamicBasicKeyboardLayoutEnabled() -> Bool {
AppleKeyboardConverter.arrDynamicBasicKeyLayout.contains(mgrPrefs.basicKeyboardLayout) AppleKeyboardConverter.arrDynamicBasicKeyLayout.contains(mgrPrefs.basicKeyboardLayout)
} }
// Apple // Apple
@objc class func cnvApple2ABC(_ charCode: UniChar) -> UniChar { class func cnvApple2ABC(_ charCode: UniChar) -> UniChar {
var charCode = charCode var charCode = charCode
// OVMandarin OVMandarin // OVMandarin OVMandarin
if isDynamicBasicKeyboardLayoutEnabled() { if isDynamicBasicKeyboardLayoutEnabled() {
@ -185,7 +185,7 @@ class AppleKeyboardConverter: NSObject {
return charCode return charCode
} }
@objc class func cnvStringApple2ABC(_ strProcessed: String) -> String { class func cnvStringApple2ABC(_ strProcessed: String) -> String {
var strProcessed = strProcessed var strProcessed = strProcessed
if isDynamicBasicKeyboardLayoutEnabled() { if isDynamicBasicKeyboardLayoutEnabled() {
// Apple // Apple

View File

@ -1,6 +1,4 @@
// Copyright (c) 2011 and onwards The OpenVanilla Project (MIT License). // Copyright (c) 2021 and onwards The vChewing Project (MIT-NTL License).
// All possible vChewing-specific modifications are of:
// (c) 2021 and onwards The vChewing Project (MIT-NTL 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
@ -24,18 +22,12 @@ 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.
*/ */
#ifndef GRAMAMBULAR_H_ #import <Foundation/Foundation.h>
#define GRAMAMBULAR_H_
#include "Bigram.h" NS_ASSUME_NONNULL_BEGIN
#include "BlockReadingBuilder.h"
#include "Grid.h"
#include "KeyValuePair.h"
#include "LanguageModel.h"
#include "Node.h"
#include "NodeAnchor.h"
#include "Span.h"
#include "Unigram.h"
#include "Walker.h"
#endif @interface CTools : NSObject
+ (BOOL)isPrintable:(UniChar)charCode;
@end
NS_ASSUME_NONNULL_END

View File

@ -22,30 +22,11 @@ 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.
*/ */
#ifndef LMConsolidator_hpp #import "CTools.h"
#define LMConsolidator_hpp
#include <fstream> @implementation CTools
#include <iostream> + (BOOL)isPrintable:(UniChar)charCode
#include <map>
#include <regex>
#include <set>
#include <sstream>
#include <stdio.h>
#include <string>
#include <syslog.h>
using namespace std;
namespace vChewing
{ {
return isprint(charCode);
class LMConsolidator }
{ @end
public:
static bool CheckPragma(const char *path);
static bool FixEOF(const char *path);
static bool ConsolidateContent(const char *path, bool shouldCheckPragma);
};
} // namespace vChewing
#endif /* LMConsolidator_hpp */

View File

@ -29,7 +29,7 @@ import Cocoa
// Use KeyCodes as much as possible since its recognition won't be affected by macOS Base Keyboard Layouts. // Use KeyCodes as much as possible since its recognition won't be affected by macOS Base Keyboard Layouts.
// KeyCodes: https://eastmanreference.com/complete-list-of-applescript-key-codes // KeyCodes: https://eastmanreference.com/complete-list-of-applescript-key-codes
// Also: HIToolbox.framework/Versions/A/Headers/Events.h // Also: HIToolbox.framework/Versions/A/Headers/Events.h
@objc enum KeyCode: UInt16 { enum KeyCode: UInt16 {
case kNone = 0 case kNone = 0
case kCarriageReturn = 36 // Renamed from "kReturn" to avoid nomenclatural confusions. case kCarriageReturn = 36 // Renamed from "kReturn" to avoid nomenclatural confusions.
case kTab = 48 case kTab = 48
@ -91,11 +91,11 @@ enum CharCode: UInt /* 16 */ {
} }
class InputHandler: NSObject { class InputHandler: NSObject {
@objc private(set) var useVerticalMode: Bool private(set) var useVerticalMode: Bool
@objc private(set) var inputText: String? private(set) var inputText: String?
@objc private(set) var inputTextIgnoringModifiers: String? private(set) var inputTextIgnoringModifiers: String?
@objc private(set) var charCode: UInt16 private(set) var charCode: UInt16
@objc private(set) var keyCode: UInt16 private(set) var keyCode: UInt16
private var isFlagChanged: Bool private var isFlagChanged: Bool
private var flags: NSEvent.ModifierFlags private var flags: NSEvent.ModifierFlags
private var cursorForwardKey: KeyCode private var cursorForwardKey: KeyCode
@ -104,9 +104,9 @@ class InputHandler: NSObject {
private var extraChooseCandidateKeyReverse: KeyCode private var extraChooseCandidateKeyReverse: KeyCode
private var absorbedArrowKey: KeyCode private var absorbedArrowKey: KeyCode
private var verticalModeOnlyChooseCandidateKey: KeyCode private var verticalModeOnlyChooseCandidateKey: KeyCode
@objc private(set) var emacsKey: vChewingEmacsKey private(set) var emacsKey: vChewingEmacsKey
@objc init( init(
inputText: String?, keyCode: UInt16, charCode: UInt16, flags: NSEvent.ModifierFlags, inputText: String?, keyCode: UInt16, charCode: UInt16, flags: NSEvent.ModifierFlags,
isVerticalMode: Bool, inputTextIgnoringModifiers: String? = nil isVerticalMode: Bool, inputTextIgnoringModifiers: String? = nil
) { ) {
@ -133,7 +133,7 @@ class InputHandler: NSObject {
super.init() super.init()
} }
@objc init(event: NSEvent, isVerticalMode: Bool) { init(event: NSEvent, isVerticalMode: Bool) {
inputText = AppleKeyboardConverter.cnvStringApple2ABC(event.characters ?? "") inputText = AppleKeyboardConverter.cnvStringApple2ABC(event.characters ?? "")
inputTextIgnoringModifiers = AppleKeyboardConverter.cnvStringApple2ABC( inputTextIgnoringModifiers = AppleKeyboardConverter.cnvStringApple2ABC(
event.charactersIgnoringModifiers ?? "") event.charactersIgnoringModifiers ?? "")
@ -172,143 +172,143 @@ class InputHandler: NSObject {
"<\(super.description) inputText:\(String(describing: inputText)), inputTextIgnoringModifiers:\(String(describing: inputTextIgnoringModifiers)) charCode:\(charCode), keyCode:\(keyCode), flags:\(flags), cursorForwardKey:\(cursorForwardKey), cursorBackwardKey:\(cursorBackwardKey), extraChooseCandidateKey:\(extraChooseCandidateKey), extraChooseCandidateKeyReverse:\(extraChooseCandidateKeyReverse), absorbedArrowKey:\(absorbedArrowKey), verticalModeOnlyChooseCandidateKey:\(verticalModeOnlyChooseCandidateKey), emacsKey:\(emacsKey), useVerticalMode:\(useVerticalMode)>" "<\(super.description) inputText:\(String(describing: inputText)), inputTextIgnoringModifiers:\(String(describing: inputTextIgnoringModifiers)) charCode:\(charCode), keyCode:\(keyCode), flags:\(flags), cursorForwardKey:\(cursorForwardKey), cursorBackwardKey:\(cursorBackwardKey), extraChooseCandidateKey:\(extraChooseCandidateKey), extraChooseCandidateKeyReverse:\(extraChooseCandidateKeyReverse), absorbedArrowKey:\(absorbedArrowKey), verticalModeOnlyChooseCandidateKey:\(verticalModeOnlyChooseCandidateKey), emacsKey:\(emacsKey), useVerticalMode:\(useVerticalMode)>"
} }
@objc var isShiftHold: Bool { var isShiftHold: Bool {
flags.contains([.shift]) flags.contains([.shift])
} }
@objc var isCommandHold: Bool { var isCommandHold: Bool {
flags.contains([.command]) flags.contains([.command])
} }
@objc var isControlHold: Bool { var isControlHold: Bool {
flags.contains([.control]) flags.contains([.control])
} }
@objc var isControlHotKey: Bool { var isControlHotKey: Bool {
flags.contains([.control]) && inputText?.first?.isLetter ?? false flags.contains([.control]) && inputText?.first?.isLetter ?? false
} }
@objc var isOptionHotKey: Bool { var isOptionHotKey: Bool {
flags.contains([.option]) && inputText?.first?.isLetter ?? false flags.contains([.option]) && inputText?.first?.isLetter ?? false
} }
@objc var isOptionHold: Bool { var isOptionHold: Bool {
flags.contains([.option]) flags.contains([.option])
} }
@objc var isCapsLockOn: Bool { var isCapsLockOn: Bool {
flags.contains([.capsLock]) flags.contains([.capsLock])
} }
@objc var isNumericPad: Bool { var isNumericPad: Bool {
flags.contains([.numericPad]) flags.contains([.numericPad])
} }
@objc var isFunctionKeyHold: Bool { var isFunctionKeyHold: Bool {
flags.contains([.function]) flags.contains([.function])
} }
@objc var isReservedKey: Bool { var isReservedKey: Bool {
guard let code = KeyCode(rawValue: keyCode) else { guard let code = KeyCode(rawValue: keyCode) else {
return false return false
} }
return code.rawValue != KeyCode.kNone.rawValue return code.rawValue != KeyCode.kNone.rawValue
} }
@objc var isTab: Bool { var isTab: Bool {
KeyCode(rawValue: keyCode) == KeyCode.kTab KeyCode(rawValue: keyCode) == KeyCode.kTab
} }
@objc var isEnter: Bool { var isEnter: Bool {
(KeyCode(rawValue: keyCode) == KeyCode.kCarriageReturn) (KeyCode(rawValue: keyCode) == KeyCode.kCarriageReturn)
|| (KeyCode(rawValue: keyCode) == KeyCode.kLineFeed) || (KeyCode(rawValue: keyCode) == KeyCode.kLineFeed)
} }
@objc var isUp: Bool { var isUp: Bool {
KeyCode(rawValue: keyCode) == KeyCode.kUpArrow KeyCode(rawValue: keyCode) == KeyCode.kUpArrow
} }
@objc var isDown: Bool { var isDown: Bool {
KeyCode(rawValue: keyCode) == KeyCode.kDownArrow KeyCode(rawValue: keyCode) == KeyCode.kDownArrow
} }
@objc var isLeft: Bool { var isLeft: Bool {
KeyCode(rawValue: keyCode) == KeyCode.kLeftArrow KeyCode(rawValue: keyCode) == KeyCode.kLeftArrow
} }
@objc var isRight: Bool { var isRight: Bool {
KeyCode(rawValue: keyCode) == KeyCode.kRightArrow KeyCode(rawValue: keyCode) == KeyCode.kRightArrow
} }
@objc var isPageUp: Bool { var isPageUp: Bool {
KeyCode(rawValue: keyCode) == KeyCode.kPageUp KeyCode(rawValue: keyCode) == KeyCode.kPageUp
} }
@objc var isPageDown: Bool { var isPageDown: Bool {
KeyCode(rawValue: keyCode) == KeyCode.kPageDown KeyCode(rawValue: keyCode) == KeyCode.kPageDown
} }
@objc var isSpace: Bool { var isSpace: Bool {
KeyCode(rawValue: keyCode) == KeyCode.kSpace KeyCode(rawValue: keyCode) == KeyCode.kSpace
} }
@objc var isBackSpace: Bool { var isBackSpace: Bool {
KeyCode(rawValue: keyCode) == KeyCode.kBackSpace KeyCode(rawValue: keyCode) == KeyCode.kBackSpace
} }
@objc var isESC: Bool { var isESC: Bool {
KeyCode(rawValue: keyCode) == KeyCode.kEscape KeyCode(rawValue: keyCode) == KeyCode.kEscape
} }
@objc var isHome: Bool { var isHome: Bool {
KeyCode(rawValue: keyCode) == KeyCode.kHome KeyCode(rawValue: keyCode) == KeyCode.kHome
} }
@objc var isEnd: Bool { var isEnd: Bool {
KeyCode(rawValue: keyCode) == KeyCode.kEnd KeyCode(rawValue: keyCode) == KeyCode.kEnd
} }
@objc var isDelete: Bool { var isDelete: Bool {
KeyCode(rawValue: keyCode) == KeyCode.kWindowDelete KeyCode(rawValue: keyCode) == KeyCode.kWindowDelete
} }
@objc var isCursorBackward: Bool { var isCursorBackward: Bool {
KeyCode(rawValue: keyCode) == cursorBackwardKey KeyCode(rawValue: keyCode) == cursorBackwardKey
} }
@objc var isCursorForward: Bool { var isCursorForward: Bool {
KeyCode(rawValue: keyCode) == cursorForwardKey KeyCode(rawValue: keyCode) == cursorForwardKey
} }
@objc var isAbsorbedArrowKey: Bool { var isAbsorbedArrowKey: Bool {
KeyCode(rawValue: keyCode) == absorbedArrowKey KeyCode(rawValue: keyCode) == absorbedArrowKey
} }
@objc var isExtraChooseCandidateKey: Bool { var isExtraChooseCandidateKey: Bool {
KeyCode(rawValue: keyCode) == extraChooseCandidateKey KeyCode(rawValue: keyCode) == extraChooseCandidateKey
} }
@objc var isExtraChooseCandidateKeyReverse: Bool { var isExtraChooseCandidateKeyReverse: Bool {
KeyCode(rawValue: keyCode) == extraChooseCandidateKeyReverse KeyCode(rawValue: keyCode) == extraChooseCandidateKeyReverse
} }
@objc var isVerticalModeOnlyChooseCandidateKey: Bool { var isVerticalModeOnlyChooseCandidateKey: Bool {
KeyCode(rawValue: keyCode) == verticalModeOnlyChooseCandidateKey KeyCode(rawValue: keyCode) == verticalModeOnlyChooseCandidateKey
} }
@objc var isUpperCaseASCIILetterKey: Bool { var isUpperCaseASCIILetterKey: Bool {
// flags == .shift Shift // flags == .shift Shift
charCode >= 65 && charCode <= 90 && flags == .shift charCode >= 65 && charCode <= 90 && flags == .shift
} }
@objc var isSymbolMenuPhysicalKey: Bool { var isSymbolMenuPhysicalKey: Bool {
// KeyCode macOS Apple // KeyCode macOS Apple
// ![input isShift] 使 Shift // ![input isShift] 使 Shift
KeyCode(rawValue: keyCode) == KeyCode.kSymbolMenuPhysicalKey KeyCode(rawValue: keyCode) == KeyCode.kSymbolMenuPhysicalKey
} }
} }
@objc enum vChewingEmacsKey: UInt16 { enum vChewingEmacsKey: UInt16 {
case none = 0 case none = 0
case forward = 6 // F case forward = 6 // F
case backward = 2 // B case backward = 2 // B
@ -319,7 +319,7 @@ class InputHandler: NSObject {
} }
class EmacsKeyHelper: NSObject { class EmacsKeyHelper: NSObject {
@objc static func detect(charCode: UniChar, flags: NSEvent.ModifierFlags) -> vChewingEmacsKey { static func detect(charCode: UniChar, flags: NSEvent.ModifierFlags) -> vChewingEmacsKey {
let charCode = AppleKeyboardConverter.cnvApple2ABC(charCode) let charCode = AppleKeyboardConverter.cnvApple2ABC(charCode)
if flags.contains(.control) { if flags.contains(.control) {
return vChewingEmacsKey(rawValue: charCode) ?? .none return vChewingEmacsKey(rawValue: charCode) ?? .none

View File

@ -59,7 +59,6 @@ import Cocoa
/// one among the candidates. /// one among the candidates.
class InputState: NSObject { class InputState: NSObject {
/// Represents that the input controller is deactivated. /// Represents that the input controller is deactivated.
@objc(InputStateDeactivated)
class Deactivated: InputState { class Deactivated: InputState {
override var description: String { override var description: String {
"<InputState.Deactivated>" "<InputState.Deactivated>"
@ -69,9 +68,8 @@ class InputState: NSObject {
// MARK: - // MARK: -
/// Represents that the composing buffer is empty. /// Represents that the composing buffer is empty.
@objc(InputStateEmpty)
class Empty: InputState { class Empty: InputState {
@objc var composingBuffer: String { var composingBuffer: String {
"" ""
} }
@ -83,9 +81,8 @@ class InputState: NSObject {
// MARK: - // MARK: -
/// Represents that the composing buffer is empty. /// Represents that the composing buffer is empty.
@objc(InputStateEmptyIgnoringPreviousState)
class EmptyIgnoringPreviousState: InputState { class EmptyIgnoringPreviousState: InputState {
@objc var composingBuffer: String { var composingBuffer: String {
"" ""
} }
@ -97,11 +94,10 @@ class InputState: NSObject {
// MARK: - // MARK: -
/// Represents that the input controller is committing text into client app. /// Represents that the input controller is committing text into client app.
@objc(InputStateCommitting)
class Committing: InputState { class Committing: InputState {
@objc private(set) var poppedText: String = "" private(set) var poppedText: String = ""
@objc convenience init(poppedText: String) { convenience init(poppedText: String) {
self.init() self.init()
self.poppedText = poppedText self.poppedText = poppedText
} }
@ -114,12 +110,11 @@ class InputState: NSObject {
// MARK: - // MARK: -
/// Represents that the composing buffer is not empty. /// Represents that the composing buffer is not empty.
@objc(InputStateNotEmpty)
class NotEmpty: InputState { class NotEmpty: InputState {
@objc private(set) var composingBuffer: String private(set) var composingBuffer: String
@objc private(set) var cursorIndex: UInt private(set) var cursorIndex: UInt
@objc init(composingBuffer: String, cursorIndex: UInt) { init(composingBuffer: String, cursorIndex: UInt) {
self.composingBuffer = composingBuffer self.composingBuffer = composingBuffer
self.cursorIndex = cursorIndex self.cursorIndex = cursorIndex
} }
@ -132,16 +127,15 @@ class InputState: NSObject {
// MARK: - // MARK: -
/// Represents that the user is inputting text. /// Represents that the user is inputting text.
@objc(InputStateInputting)
class Inputting: NotEmpty { class Inputting: NotEmpty {
@objc var poppedText: String = "" var poppedText: String = ""
@objc var tooltip: String = "" var tooltip: String = ""
@objc override init(composingBuffer: String, cursorIndex: UInt) { override init(composingBuffer: String, cursorIndex: UInt) {
super.init(composingBuffer: composingBuffer, cursorIndex: cursorIndex) super.init(composingBuffer: composingBuffer, cursorIndex: cursorIndex)
} }
@objc var attributedString: NSAttributedString { var attributedString: NSAttributedString {
let attributedSting = NSAttributedString( let attributedSting = NSAttributedString(
string: composingBuffer, string: composingBuffer,
attributes: [ attributes: [
@ -163,12 +157,11 @@ class InputState: NSObject {
private let kMaxMarkRangeLength = mgrPrefs.maxCandidateLength private let kMaxMarkRangeLength = mgrPrefs.maxCandidateLength
/// Represents that the user is marking a range in the composing buffer. /// Represents that the user is marking a range in the composing buffer.
@objc(InputStateMarking)
class Marking: NotEmpty { class Marking: NotEmpty {
@objc private(set) var markerIndex: UInt private(set) var markerIndex: UInt
@objc private(set) var markedRange: NSRange private(set) var markedRange: NSRange
@objc private var deleteTargetExists = false private var deleteTargetExists = false
@objc var tooltip: String { var tooltip: String {
if composingBuffer.count != readings.count { if composingBuffer.count != readings.count {
TooltipController.backgroundColor = NSColor( TooltipController.backgroundColor = NSColor(
red: 0.55, green: 0.00, blue: 0.00, alpha: 1.00 red: 0.55, green: 0.00, blue: 0.00, alpha: 1.00
@ -251,10 +244,10 @@ class InputState: NSObject {
) )
} }
@objc var tooltipForInputting: String = "" var tooltipForInputting: String = ""
@objc private(set) var readings: [String] private(set) var readings: [String]
@objc init(composingBuffer: String, cursorIndex: UInt, markerIndex: UInt, readings: [String]) { init(composingBuffer: String, cursorIndex: UInt, markerIndex: UInt, readings: [String]) {
self.markerIndex = markerIndex self.markerIndex = markerIndex
let begin = min(cursorIndex, markerIndex) let begin = min(cursorIndex, markerIndex)
let end = max(cursorIndex, markerIndex) let end = max(cursorIndex, markerIndex)
@ -263,7 +256,7 @@ class InputState: NSObject {
super.init(composingBuffer: composingBuffer, cursorIndex: cursorIndex) super.init(composingBuffer: composingBuffer, cursorIndex: cursorIndex)
} }
@objc var attributedString: NSAttributedString { var attributedString: NSAttributedString {
let attributedSting = NSMutableAttributedString(string: composingBuffer) let attributedSting = NSMutableAttributedString(string: composingBuffer)
let end = markedRange.location + markedRange.length let end = markedRange.location + markedRange.length
@ -296,13 +289,13 @@ class InputState: NSObject {
"<InputState.Marking, composingBuffer:\(composingBuffer), cursorIndex:\(cursorIndex), markedRange:\(markedRange)>" "<InputState.Marking, composingBuffer:\(composingBuffer), cursorIndex:\(cursorIndex), markedRange:\(markedRange)>"
} }
@objc func convertToInputting() -> Inputting { func convertToInputting() -> Inputting {
let state = Inputting(composingBuffer: composingBuffer, cursorIndex: cursorIndex) let state = Inputting(composingBuffer: composingBuffer, cursorIndex: cursorIndex)
state.tooltip = tooltipForInputting state.tooltip = tooltipForInputting
return state return state
} }
@objc var validToWrite: Bool { var validToWrite: Bool {
/// vChewing allows users to input a string whose length differs /// vChewing allows users to input a string whose length differs
/// from the amount of Bopomofo readings. In this case, the range /// from the amount of Bopomofo readings. In this case, the range
/// in the composing buffer and the readings could not match, so /// in the composing buffer and the readings could not match, so
@ -323,7 +316,7 @@ class InputState: NSObject {
&& markedRange.length <= kMaxMarkRangeLength && markedRange.length <= kMaxMarkRangeLength
} }
@objc var chkIfUserPhraseExists: Bool { var chkIfUserPhraseExists: Bool {
let text = (composingBuffer as NSString).substring(with: markedRange) let text = (composingBuffer as NSString).substring(with: markedRange)
let (exactBegin, _) = (composingBuffer as NSString).characterIndex( let (exactBegin, _) = (composingBuffer as NSString).characterIndex(
from: markedRange.location) from: markedRange.location)
@ -337,7 +330,7 @@ class InputState: NSObject {
== true == true
} }
@objc var userPhrase: String { var userPhrase: String {
let text = (composingBuffer as NSString).substring(with: markedRange) let text = (composingBuffer as NSString).substring(with: markedRange)
let (exactBegin, _) = (composingBuffer as NSString).characterIndex( let (exactBegin, _) = (composingBuffer as NSString).characterIndex(
from: markedRange.location) from: markedRange.location)
@ -348,7 +341,7 @@ class InputState: NSObject {
return "\(text) \(joined)" return "\(text) \(joined)"
} }
@objc var userPhraseConverted: String { var userPhraseConverted: String {
let text = let text =
OpenCCBridge.crossConvert( OpenCCBridge.crossConvert(
(composingBuffer as NSString).substring(with: markedRange)) ?? "" (composingBuffer as NSString).substring(with: markedRange)) ?? ""
@ -366,18 +359,17 @@ class InputState: NSObject {
// MARK: - // MARK: -
/// Represents that the user is choosing in a candidates list. /// Represents that the user is choosing in a candidates list.
@objc(InputStateChoosingCandidate)
class ChoosingCandidate: NotEmpty { class ChoosingCandidate: NotEmpty {
@objc private(set) var candidates: [String] private(set) var candidates: [String]
@objc private(set) var useVerticalMode: Bool private(set) var useVerticalMode: Bool
@objc init(composingBuffer: String, cursorIndex: UInt, candidates: [String], useVerticalMode: Bool) { init(composingBuffer: String, cursorIndex: UInt, candidates: [String], useVerticalMode: Bool) {
self.candidates = candidates self.candidates = candidates
self.useVerticalMode = useVerticalMode self.useVerticalMode = useVerticalMode
super.init(composingBuffer: composingBuffer, cursorIndex: cursorIndex) super.init(composingBuffer: composingBuffer, cursorIndex: cursorIndex)
} }
@objc var attributedString: NSAttributedString { var attributedString: NSAttributedString {
let attributedSting = NSAttributedString( let attributedSting = NSAttributedString(
string: composingBuffer, string: composingBuffer,
attributes: [ attributes: [
@ -397,11 +389,10 @@ class InputState: NSObject {
/// Represents that the user is choosing in a candidates list /// Represents that the user is choosing in a candidates list
/// in the associated phrases mode. /// in the associated phrases mode.
@objc(InputStateAssociatedPhrases)
class AssociatedPhrases: InputState { class AssociatedPhrases: InputState {
@objc private(set) var candidates: [String] = [] private(set) var candidates: [String] = []
@objc private(set) var useVerticalMode: Bool = false private(set) var useVerticalMode: Bool = false
@objc init(candidates: [String], useVerticalMode: Bool) { init(candidates: [String], useVerticalMode: Bool) {
self.candidates = candidates self.candidates = candidates
self.useVerticalMode = useVerticalMode self.useVerticalMode = useVerticalMode
super.init() super.init()
@ -412,11 +403,10 @@ class InputState: NSObject {
} }
} }
@objc(InputStateSymbolTable)
class SymbolTable: ChoosingCandidate { class SymbolTable: ChoosingCandidate {
@objc var node: SymbolNode var node: SymbolNode
@objc init(node: SymbolNode, useVerticalMode: Bool) { init(node: SymbolNode, useVerticalMode: Bool) {
self.node = node self.node = node
let candidates = node.children?.map(\.title) ?? [String]() let candidates = node.children?.map(\.title) ?? [String]()
super.init( super.init(
@ -432,53 +422,53 @@ class InputState: NSObject {
} }
class SymbolNode: NSObject { class SymbolNode: NSObject {
@objc var title: String var title: String
@objc var children: [SymbolNode]? var children: [SymbolNode]?
@objc init(_ title: String, _ children: [SymbolNode]? = nil) { init(_ title: String, _ children: [SymbolNode]? = nil) {
self.title = title self.title = title
self.children = children self.children = children
super.init() super.init()
} }
@objc init(_ title: String, symbols: String) { init(_ title: String, symbols: String) {
self.title = title self.title = title
children = Array(symbols).map { SymbolNode(String($0), nil) } children = Array(symbols).map { SymbolNode(String($0), nil) }
super.init() super.init()
} }
@objc static let catCommonSymbols = String( static let catCommonSymbols = String(
format: NSLocalizedString("catCommonSymbols", comment: "")) format: NSLocalizedString("catCommonSymbols", comment: ""))
@objc static let catHoriBrackets = String( static let catHoriBrackets = String(
format: NSLocalizedString("catHoriBrackets", comment: "")) format: NSLocalizedString("catHoriBrackets", comment: ""))
@objc static let catVertBrackets = String( static let catVertBrackets = String(
format: NSLocalizedString("catVertBrackets", comment: "")) format: NSLocalizedString("catVertBrackets", comment: ""))
@objc static let catGreekLetters = String( static let catGreekLetters = String(
format: NSLocalizedString("catGreekLetters", comment: "")) format: NSLocalizedString("catGreekLetters", comment: ""))
@objc static let catMathSymbols = String( static let catMathSymbols = String(
format: NSLocalizedString("catMathSymbols", comment: "")) format: NSLocalizedString("catMathSymbols", comment: ""))
@objc static let catCurrencyUnits = String( static let catCurrencyUnits = String(
format: NSLocalizedString("catCurrencyUnits", comment: "")) format: NSLocalizedString("catCurrencyUnits", comment: ""))
@objc static let catSpecialSymbols = String( static let catSpecialSymbols = String(
format: NSLocalizedString("catSpecialSymbols", comment: "")) format: NSLocalizedString("catSpecialSymbols", comment: ""))
@objc static let catUnicodeSymbols = String( static let catUnicodeSymbols = String(
format: NSLocalizedString("catUnicodeSymbols", comment: "")) format: NSLocalizedString("catUnicodeSymbols", comment: ""))
@objc static let catCircledKanjis = String( static let catCircledKanjis = String(
format: NSLocalizedString("catCircledKanjis", comment: "")) format: NSLocalizedString("catCircledKanjis", comment: ""))
@objc static let catCircledKataKana = String( static let catCircledKataKana = String(
format: NSLocalizedString("catCircledKataKana", comment: "")) format: NSLocalizedString("catCircledKataKana", comment: ""))
@objc static let catBracketKanjis = String( static let catBracketKanjis = String(
format: NSLocalizedString("catBracketKanjis", comment: "")) format: NSLocalizedString("catBracketKanjis", comment: ""))
@objc static let catSingleTableLines = String( static let catSingleTableLines = String(
format: NSLocalizedString("catSingleTableLines", comment: "")) format: NSLocalizedString("catSingleTableLines", comment: ""))
@objc static let catDoubleTableLines = String( static let catDoubleTableLines = String(
format: NSLocalizedString("catDoubleTableLines", comment: "")) format: NSLocalizedString("catDoubleTableLines", comment: ""))
@objc static let catFillingBlocks = String( static let catFillingBlocks = String(
format: NSLocalizedString("catFillingBlocks", comment: "")) format: NSLocalizedString("catFillingBlocks", comment: ""))
@objc static let catLineSegments = String( static let catLineSegments = String(
format: NSLocalizedString("catLineSegments", comment: "")) format: NSLocalizedString("catLineSegments", comment: ""))
@objc static let root: SymbolNode = .init( static let root: SymbolNode = .init(
"/", "/",
[ [
SymbolNode(""), SymbolNode(""),

View File

@ -1,102 +0,0 @@
// Copyright (c) 2011 and onwards The OpenVanilla Project (MIT License).
// All possible vChewing-specific modifications are of:
// (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 <Foundation/Foundation.h>
@class InputHandler;
@class InputState;
NS_ASSUME_NONNULL_BEGIN
typedef NSString *const InputMode NS_TYPED_ENUM;
extern InputMode imeModeCHT;
extern InputMode imeModeCHS;
extern InputMode imeModeNULL;
struct BufferStatePackage
{
NSString *composedText;
NSInteger cursorIndex;
NSString *resultOfRear;
NSString *resultOfFront;
};
@class KeyHandler;
@protocol KeyHandlerDelegate <NSObject>
- (id)ctlCandidateForKeyHandler:(KeyHandler *)keyHandler;
- (void)keyHandler:(KeyHandler *)keyHandler didSelectCandidateAtIndex:(NSInteger)index ctlCandidate:(id)controller;
- (BOOL)keyHandler:(KeyHandler *)keyHandler didRequestWriteUserPhraseWithState:(InputState *)state;
@end
@interface KeyHandler : NSObject
- (BOOL)isBuilderEmpty;
- (void)fixNodeWithValue:(NSString *)value NS_SWIFT_NAME(fixNode(value:));
- (void)clear;
@property(strong, nonatomic) InputMode inputMode;
@property(weak, nonatomic) id<KeyHandlerDelegate> delegate;
// The following items need to be exposed to Swift:
- (void)_walk;
- (NSString *)_popOverflowComposingTextAndWalk;
- (NSArray<NSString *> *)_currentReadings;
- (BOOL)checkWhetherToneMarkerConfirmsPhoneticReadingBuffer;
- (BOOL)chkKeyValidity:(UniChar)value;
- (BOOL)ifLangModelHasUnigramsForKey:(NSString *)reading;
- (BOOL)isPhoneticReadingBufferEmpty;
- (BOOL)isPrintable:(UniChar)charCode;
- (NSArray<NSString *> *)buildAssociatePhraseArrayWithKey:(NSString *)key;
- (NSArray<NSString *> *)getCandidatesArray;
- (NSInteger)getKeyLengthAtIndexZero;
- (NSInteger)getBuilderCursorIndex;
- (NSInteger)getBuilderLength;
- (NSInteger)getPackagedCursorIndex;
- (NSString *)getComposedText;
- (NSString *)getCompositionFromPhoneticReadingBuffer;
- (NSString *)getStrLocationResult:(BOOL)isFront NS_SWIFT_NAME(getStrLocationResult(isFront:));
- (NSString *)getSyllableCompositionFromPhoneticReadingBuffer;
- (void)clearPhoneticReadingBuffer;
- (void)combinePhoneticReadingBufferKey:(UniChar)charCode;
- (void)createNewBuilder;
- (void)dealWithOverrideModelSuggestions;
- (void)deleteBuilderReadingAfterCursor;
- (void)deleteBuilderReadingInFrontOfCursor;
- (void)doBackSpaceToPhoneticReadingBuffer;
- (void)ensurePhoneticParser;
- (void)insertReadingToBuilderAtCursor:(NSString *)reading;
- (void)packageBufferStateMaterials;
- (void)removeBuilderAndReset:(BOOL)shouldReset;
- (void)setBuilderCursorIndex:(NSInteger)value;
- (void)setInputModesToLM:(BOOL)isCHS;
- (void)syncBaseLMPrefs;
@end
NS_ASSUME_NONNULL_END

View File

@ -1,637 +0,0 @@
// Copyright (c) 2011 and onwards The OpenVanilla Project (MIT License).
// All possible vChewing-specific modifications are of:
// (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 "KeyHandler.h"
#import "Gramambular.h"
#import "LMInstantiator.h"
#import "Mandarin.h"
#import "UserOverrideModel.h"
#import "mgrLangModel_Privates.h"
#import "vChewing-Swift.h"
#import <string>
InputMode imeModeCHS = ctlInputMethod.kIMEModeCHS;
InputMode imeModeCHT = ctlInputMethod.kIMEModeCHT;
InputMode imeModeNULL = ctlInputMethod.kIMEModeNULL;
typedef vChewing::LMInstantiator BaseLM;
typedef vChewing::UserOverrideModel UserOverrideLM;
typedef Gramambular::BlockReadingBuilder BlockBuilder;
typedef Mandarin::BopomofoReadingBuffer PhoneticBuffer;
static const double kEpsilon = 0.000001;
NSString *packagedComposedText;
NSInteger packagedCursorIndex;
NSString *packagedResultOfRear;
NSString *packagedResultOfFront;
// NON-SWIFTIFIABLE
static double FindHighestScore(const std::vector<Gramambular::NodeAnchor> &nodes, double epsilon)
{
double highestScore = 0.0;
for (auto ni = nodes.begin(), ne = nodes.end(); ni != ne; ++ni)
{
double score = ni->node->highestUnigramScore();
if (score > highestScore)
highestScore = score;
}
return highestScore + epsilon;
}
// NON-SWIFTIFIABLE
class NodeAnchorDescendingSorter
{
public:
bool operator()(const Gramambular::NodeAnchor &a, const Gramambular::NodeAnchor &b) const
{
return a.node->key().length() > b.node->key().length();
}
};
// if DEBUG is defined, a DOT file (GraphViz format) will be written to the
// specified path every time the grid is walked
#if DEBUG
static NSString *const kGraphVizOutputfile = @"/tmp/vChewing-visualization.dot";
#endif
// NON-SWIFTIFIABLE
@implementation KeyHandler
{
// the reading buffer that takes user input
PhoneticBuffer *_bpmfReadingBuffer;
// language model
BaseLM *_languageModel;
// user override model
UserOverrideLM *_userOverrideModel;
// the grid (lattice) builder for the unigrams (and bigrams)
BlockBuilder *_builder;
// latest walked path (trellis) using the Viterbi algorithm
std::vector<Gramambular::NodeAnchor> _walkedNodes;
NSString *_inputMode;
}
@synthesize delegate = _delegate;
// NON-SWIFTIFIABLE DUE TO VARIABLE AVAILABLE ACCESSIBILITY RANGE.
// VARIABLE: "_inputMode"
- (NSString *)inputMode
{
return _inputMode;
}
// NON-SWIFTIFIABLE
- (BOOL)isBuilderEmpty
{
return (_builder->grid().width() == 0);
}
// NON-SWIFTIFIABLE DUE TO VARIABLE AVAILABLE ACCESSIBILITY RANGE.
// VARIABLE: "_inputMode"
- (void)setInputMode:(NSString *)value
{
// 下面這句的「isKindOfClass」是做類型檢查
// 為了應對出現輸入法 plist 被改壞掉這樣的極端情況。
BOOL isCHS = [value isKindOfClass:[NSString class]] && [value isEqual:imeModeCHS];
// 緊接著將新的簡繁輸入模式提報給 ctlInputMethod:
ctlInputMethod.currentInputMode = isCHS ? imeModeCHS : imeModeCHT;
mgrPrefs.mostRecentInputMode = ctlInputMethod.currentInputMode;
// 拿當前的 _inputMode 與 ctlInputMethod 的提報結果對比,不同的話則套用新設定:
if (![_inputMode isEqualToString:ctlInputMethod.currentInputMode])
{
// Reinitiate language models if necessary
[self setInputModesToLM:isCHS];
// Synchronize the sub-languageModel state settings to the new LM.
[self syncBaseLMPrefs];
[self removeBuilderAndReset:YES];
if (![self isPhoneticReadingBufferEmpty])
[self clearPhoneticReadingBuffer];
}
_inputMode = ctlInputMethod.currentInputMode;
}
// NON-SWIFTIFIABLE: Required by an ObjC(pp)-based class.
- (void)dealloc
{ // clean up everything
if (_bpmfReadingBuffer)
delete _bpmfReadingBuffer;
if (_builder)
[self removeBuilderAndReset:NO];
}
// NON-SWIFTIFIABLE: Not placeable in swift extensions.
- (instancetype)init
{
self = [super init];
if (self)
{
[self ensurePhoneticParser];
[self setInputMode:ctlInputMethod.currentInputMode];
}
return self;
}
// NON-SWIFTIFIABLE
- (void)fixNodeWithValue:(NSString *)value
{
NSInteger cursorIndex = [self getActualCandidateCursorIndex];
std::string stringValue(value.UTF8String);
Gramambular::NodeAnchor selectedNode = _builder->grid().fixNodeSelectedCandidate(cursorIndex, stringValue);
if (!mgrPrefs.useSCPCTypingMode)
{ // 不要針對逐字選字模式啟用臨時半衰記憶模型。
// If the length of the readings and the characters do not match,
// it often means it is a special symbol and it should not be stored
// in the user override model.
BOOL addToOverrideModel = YES;
if (selectedNode.spanningLength != [value count])
addToOverrideModel = NO;
if (addToOverrideModel)
{
double score = selectedNode.node->scoreForCandidate(stringValue);
if (score <= -12) // 威注音的 SymbolLM 的 Score 是 -12。
addToOverrideModel = NO;
}
if (addToOverrideModel)
_userOverrideModel->observe(_walkedNodes, cursorIndex, stringValue, [[NSDate date] timeIntervalSince1970]);
}
[self _walk];
if (mgrPrefs.moveCursorAfterSelectingCandidate)
{
size_t nextPosition = 0;
for (auto node : _walkedNodes)
{
if (nextPosition >= cursorIndex)
break;
nextPosition += node.spanningLength;
}
if (nextPosition <= [self getBuilderLength])
[self setBuilderCursorIndex:nextPosition];
}
}
// NON-SWIFTIFIABLE
- (void)clear
{
[self clearPhoneticReadingBuffer];
_builder->clear();
_walkedNodes.clear();
}
#pragma mark - States Building
// NON-SWIFTIFIABLE
- (void)packageBufferStateMaterials
{
// We gather the data through this function, package it,
// and sent it to our Swift extension to build the InputState.Inputting there.
// Otherwise, ObjC++ always bugs for "expecting a type".
// "updating the composing buffer" means to request the client to "refresh" the text input buffer
// with our "composing text"
NSMutableString *composingBuffer = [[NSMutableString alloc] init];
NSInteger composedStringCursorIndex = 0;
// we must do some Unicode codepoint counting to find the actual cursor location for the client
// i.e. we need to take UTF-16 into consideration, for which a surrogate pair takes 2 UniChars
// locations
size_t readingCursorIndex = 0;
size_t builderCursorIndex = [self getBuilderCursorIndex];
NSString *resultOfRear = @"";
NSString *resultOfFront = @"";
for (std::vector<Gramambular::NodeAnchor>::iterator wi = _walkedNodes.begin(), we = _walkedNodes.end(); wi != we;
++wi)
{
if ((*wi).node)
{
std::string nodeStr = (*wi).node->currentKeyValue().value;
NSString *valueString = [NSString stringWithUTF8String:nodeStr.c_str()];
[composingBuffer appendString:valueString];
NSArray<NSString *> *splited = [valueString split];
NSInteger codepointCount = splited.count;
// this re-aligns the cursor index in the composed string
// (the actual cursor on the screen) with the builder's logical
// cursor (reading) cursor; each built node has a "spanning length"
// (e.g. two reading blocks has a spanning length of 2), and we
// accumulate those lengths to calculate the displayed cursor
// index
size_t spanningLength = (*wi).spanningLength;
if (readingCursorIndex + spanningLength <= builderCursorIndex)
{
composedStringCursorIndex += [valueString length];
readingCursorIndex += spanningLength;
}
else
{
if (codepointCount == spanningLength)
{
for (size_t i = 0; i < codepointCount && readingCursorIndex < builderCursorIndex; i++)
{
composedStringCursorIndex += [splited[i] length];
readingCursorIndex++;
}
}
else
{
if (readingCursorIndex < builderCursorIndex)
{
composedStringCursorIndex += [valueString length];
readingCursorIndex += spanningLength;
if (readingCursorIndex > builderCursorIndex)
{
readingCursorIndex = builderCursorIndex;
}
if (builderCursorIndex == 0)
{
resultOfFront =
[NSString stringWithUTF8String:_builder->readings()[builderCursorIndex].c_str()];
}
else if (builderCursorIndex >= _builder->readings().size())
{
resultOfRear = [NSString
stringWithUTF8String:_builder->readings()[_builder->readings().size() - 1].c_str()];
}
else
{
resultOfFront =
[NSString stringWithUTF8String:_builder->readings()[builderCursorIndex].c_str()];
resultOfRear =
[NSString stringWithUTF8String:_builder->readings()[builderCursorIndex - 1].c_str()];
}
}
}
}
}
}
// now we gather all the info, we separate the composing buffer to two parts, head and tail,
// and insert the reading text (the Mandarin syllable) in between them;
// the reading text is what the user is typing
NSString *head = [composingBuffer substringToIndex:composedStringCursorIndex];
NSString *reading = [self getCompositionFromPhoneticReadingBuffer];
NSString *tail = [composingBuffer substringFromIndex:composedStringCursorIndex];
NSString *composedText = [head stringByAppendingString:[reading stringByAppendingString:tail]];
NSInteger cursorIndex = composedStringCursorIndex + [reading length];
packagedComposedText = composedText;
packagedCursorIndex = cursorIndex;
packagedResultOfRear = resultOfRear;
packagedResultOfFront = resultOfFront;
}
// NON-SWIFTIFIABLE DUE TO VARIABLE AVAILABLE ACCESSIBILITY RANGE.
- (NSString *)getStrLocationResult:(BOOL)isFront
{
if (isFront)
return packagedResultOfFront;
else
return packagedResultOfRear;
}
// NON-SWIFTIFIABLE DUE TO VARIABLE AVAILABLE ACCESSIBILITY RANGE.
- (NSString *)getComposedText
{
return packagedComposedText;
}
// NON-SWIFTIFIABLE DUE TO VARIABLE AVAILABLE ACCESSIBILITY RANGE.
- (NSInteger)getPackagedCursorIndex
{
return packagedCursorIndex;
}
// NON-SWIFTIFIABLE
- (void)_walk
{
// retrieve the most likely trellis, i.e. a Maximum Likelihood Estimation
// of the best possible Mandarin characters given the input syllables,
// using the Viterbi algorithm implemented in the Gramambular library
Gramambular::Walker walker(&_builder->grid());
// the reverse walk traces the trellis from the end
_walkedNodes = walker.reverseWalk(_builder->grid().width());
// then we reverse the nodes so that we get the forward-walked nodes
reverse(_walkedNodes.begin(), _walkedNodes.end());
// if DEBUG is defined, a GraphViz file is written to kGraphVizOutputfile
#if DEBUG
std::string dotDump = _builder->grid().dumpDOT();
NSString *dotStr = [NSString stringWithUTF8String:dotDump.c_str()];
NSError *error = nil;
BOOL __unused success = [dotStr writeToFile:kGraphVizOutputfile
atomically:YES
encoding:NSUTF8StringEncoding
error:&error];
#endif
}
// NON-SWIFTIFIABLE
- (NSString *)_popOverflowComposingTextAndWalk
{
// in an ideal world, we can as well let the user type forever,
// but because the Viterbi algorithm has a complexity of O(N^2),
// the walk will become slower as the number of nodes increase,
// therefore we need to auto-commit overflown texts which usually
// lose their influence over the whole MLE anyway -- so that when
// the user type along, the already composed text in the rear side
// of the buffer will be committed (i.e. "popped out").
NSString *poppedText = @"";
NSInteger composingBufferSize = mgrPrefs.composingBufferSize;
if (_builder->grid().width() > (size_t)composingBufferSize)
{
if (_walkedNodes.size() > 0)
{
Gramambular::NodeAnchor &anchor = _walkedNodes[0];
poppedText = [NSString stringWithUTF8String:anchor.node->currentKeyValue().value.c_str()];
_builder->removeHeadReadings(anchor.spanningLength);
}
}
[self _walk];
return poppedText;
}
// NON-SWIFTIFIABLE
- (NSArray<NSString *> *)_currentReadings
{
NSMutableArray<NSString *> *readingsArray = [[NSMutableArray alloc] init];
std::vector<std::string> v = _builder->readings();
for (std::vector<std::string>::iterator it_i = v.begin(); it_i != v.end(); ++it_i)
[readingsArray addObject:[NSString stringWithUTF8String:it_i->c_str()]];
return readingsArray;
}
// NON-SWIFTIFIABLE
- (NSArray<NSString *> *)buildAssociatePhraseArrayWithKey:(NSString *)key
{
NSMutableArray<NSString *> *array = [NSMutableArray array];
std::string cppKey = std::string(key.UTF8String);
if (_languageModel->hasAssociatedPhrasesForKey(cppKey))
{
std::vector<std::string> phrases = _languageModel->associatedPhrasesForKey(cppKey);
for (auto phrase : phrases)
{
NSString *item = [[NSString alloc] initWithUTF8String:phrase.c_str()];
[array addObject:item];
}
}
return array;
}
#pragma mark - 必須用 ObjCpp 處理的部分: Mandarin
- (BOOL)chkKeyValidity:(UniChar)charCode
{
return _bpmfReadingBuffer->isValidKey((char)charCode);
}
- (BOOL)isPhoneticReadingBufferEmpty
{
return _bpmfReadingBuffer->isEmpty();
}
- (void)clearPhoneticReadingBuffer
{
_bpmfReadingBuffer->clear();
}
- (void)combinePhoneticReadingBufferKey:(UniChar)charCode
{
_bpmfReadingBuffer->combineKey((char)charCode);
}
- (BOOL)checkWhetherToneMarkerConfirmsPhoneticReadingBuffer
{
return _bpmfReadingBuffer->hasToneMarker();
}
- (NSString *)getSyllableCompositionFromPhoneticReadingBuffer
{
return [NSString stringWithUTF8String:_bpmfReadingBuffer->syllable().composedString().c_str()];
}
- (void)doBackSpaceToPhoneticReadingBuffer
{
_bpmfReadingBuffer->backspace();
}
- (NSString *)getCompositionFromPhoneticReadingBuffer
{
return [NSString stringWithUTF8String:_bpmfReadingBuffer->composedString().c_str()];
}
- (void)ensurePhoneticParser
{
if (_bpmfReadingBuffer)
{
switch (mgrPrefs.mandarinParser)
{
case MandarinParserOfStandard:
_bpmfReadingBuffer->setKeyboardLayout(Mandarin::BopomofoKeyboardLayout::StandardLayout());
break;
case MandarinParserOfEten:
_bpmfReadingBuffer->setKeyboardLayout(Mandarin::BopomofoKeyboardLayout::ETenLayout());
break;
case MandarinParserOfHsu:
_bpmfReadingBuffer->setKeyboardLayout(Mandarin::BopomofoKeyboardLayout::HsuLayout());
break;
case MandarinParserOfEen26:
_bpmfReadingBuffer->setKeyboardLayout(Mandarin::BopomofoKeyboardLayout::ETen26Layout());
break;
case MandarinParserOfIBM:
_bpmfReadingBuffer->setKeyboardLayout(Mandarin::BopomofoKeyboardLayout::IBMLayout());
break;
case MandarinParserOfMiTAC:
_bpmfReadingBuffer->setKeyboardLayout(Mandarin::BopomofoKeyboardLayout::MiTACLayout());
break;
case MandarinParserOfFakeSeigyou:
_bpmfReadingBuffer->setKeyboardLayout(Mandarin::BopomofoKeyboardLayout::FakeSeigyouLayout());
break;
case MandarinParserOfHanyuPinyin:
_bpmfReadingBuffer->setKeyboardLayout(Mandarin::BopomofoKeyboardLayout::HanyuPinyinLayout());
break;
default:
_bpmfReadingBuffer->setKeyboardLayout(Mandarin::BopomofoKeyboardLayout::StandardLayout());
mgrPrefs.mandarinParser = MandarinParserOfStandard;
}
}
else
{
_bpmfReadingBuffer = new Mandarin::BopomofoReadingBuffer(Mandarin::BopomofoKeyboardLayout::StandardLayout());
}
}
#pragma mark - 必須用 ObjCpp 處理的部分: Gramambular 等
- (void)removeBuilderAndReset:(BOOL)shouldReset
{
if (_builder)
{
delete _builder;
if (shouldReset)
[self createNewBuilder];
}
else if (shouldReset)
[self createNewBuilder];
}
- (void)createNewBuilder
{
_builder = new Gramambular::BlockReadingBuilder(_languageModel);
// Each Mandarin syllable is separated by a hyphen.
_builder->setJoinSeparator("-");
}
- (void)setInputModesToLM:(BOOL)isCHS
{
_languageModel = isCHS ? [mgrLangModel lmCHS] : [mgrLangModel lmCHT];
_userOverrideModel = isCHS ? [mgrLangModel userOverrideModelCHS] : [mgrLangModel userOverrideModelCHT];
}
- (void)syncBaseLMPrefs
{
if (_languageModel)
{
_languageModel->setPhraseReplacementEnabled(mgrPrefs.phraseReplacementEnabled);
_languageModel->setSymbolEnabled(mgrPrefs.symbolInputEnabled);
_languageModel->setCNSEnabled(mgrPrefs.cns11643Enabled);
}
}
// ----
- (BOOL)ifLangModelHasUnigramsForKey:(NSString *)reading
{
return _languageModel->hasUnigramsForKey((std::string)[reading UTF8String]);
}
- (void)insertReadingToBuilderAtCursor:(NSString *)reading
{
_builder->insertReadingAtCursor((std::string)[reading UTF8String]);
}
- (void)dealWithOverrideModelSuggestions
{
// 這一整段都太 C++ 且只出現一次,就整個端過來了。
// 拆開封裝的話,只會把問題搞得更麻煩而已。
std::string overrideValue = (mgrPrefs.useSCPCTypingMode)
? ""
: _userOverrideModel->suggest(_walkedNodes, [self getBuilderCursorIndex],
[[NSDate date] timeIntervalSince1970]);
if (!overrideValue.empty())
{
NSInteger cursorIndex = [self getActualCandidateCursorIndex];
std::vector<Gramambular::NodeAnchor> nodes = mgrPrefs.setRearCursorMode
? _builder->grid().nodesCrossingOrEndingAt(cursorIndex)
: _builder->grid().nodesEndingAt(cursorIndex);
double highestScore = FindHighestScore(nodes, kEpsilon);
_builder->grid().overrideNodeScoreForSelectedCandidate(cursorIndex, overrideValue,
static_cast<float>(highestScore));
}
}
- (void)setBuilderCursorIndex:(NSInteger)value
{
_builder->setCursorIndex(value);
}
- (NSInteger)getBuilderCursorIndex
{
return _builder->cursorIndex();
}
- (NSInteger)getBuilderLength
{
return _builder->length();
}
- (void)deleteBuilderReadingInFrontOfCursor
{
_builder->deleteReadingBeforeCursor();
}
- (void)deleteBuilderReadingAfterCursor
{
_builder->deleteReadingAfterCursor();
}
- (NSArray<NSString *> *)getCandidatesArray
{
NSMutableArray<NSString *> *candidatesArray = [[NSMutableArray alloc] init];
NSInteger cursorIndex = [self getActualCandidateCursorIndex];
std::vector<Gramambular::NodeAnchor> nodes = mgrPrefs.setRearCursorMode
? _builder->grid().nodesCrossingOrEndingAt(cursorIndex)
: _builder->grid().nodesEndingAt(cursorIndex);
// sort the nodes, so that longer nodes (representing longer phrases) are placed at the top of the candidate list
stable_sort(nodes.begin(), nodes.end(), NodeAnchorDescendingSorter());
// then use the C++ trick to retrieve the candidates for each node at/crossing the cursor
for (std::vector<Gramambular::NodeAnchor>::iterator ni = nodes.begin(), ne = nodes.end(); ni != ne; ++ni)
{
const std::vector<Gramambular::KeyValuePair> &candidates = (*ni).node->candidates();
for (std::vector<Gramambular::KeyValuePair>::const_iterator ci = candidates.begin(), ce = candidates.end();
ci != ce; ++ci)
[candidatesArray addObject:[NSString stringWithUTF8String:(*ci).value.c_str()]];
}
return candidatesArray;
}
- (NSInteger)getKeyLengthAtIndexZero
{
return [NSString stringWithUTF8String:_walkedNodes[0].node->currentKeyValue().value.c_str()].length;
}
#pragma mark - 威注音認為有必要單獨拿出來處理的部分,交給 Swift 則有些困難。
- (BOOL)isPrintable:(UniChar)charCode
{
return isprint(charCode);
}
@end

View File

@ -0,0 +1,324 @@
// Copyright (c) 2021 and onwards The vChewing Project (MIT-NTL License).
// Refactored from the ObjCpp-version of this class by:
// (c) 2011 and onwards The OpenVanilla Project (MIT License).
/*
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
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 Cocoa
public enum InputMode: String {
case imeModeCHS = "org.atelierInmu.inputmethod.vChewing.IMECHS"
case imeModeCHT = "org.atelierInmu.inputmethod.vChewing.IMECHT"
case imeModeNULL = ""
}
// MARK: - Delegate.
protocol KeyHandlerDelegate: NSObjectProtocol {
func ctlCandidate(for _: KeyHandler) -> Any
func keyHandler(
_: KeyHandler, didSelectCandidateAt index: Int,
ctlCandidate controller: Any
)
func keyHandler(_ keyHandler: KeyHandler, didRequestWriteUserPhraseWith state: InputState)
-> Bool
}
// MARK: - Kernel.
class KeyHandler: NSObject {
let kEpsilon: Double = 0.000001
var _inputMode: String = ""
var _languageModel: vChewing.LMInstantiator = .init()
var _userOverrideModel: vChewing.LMUserOverride = .init()
var _builder: Megrez.BlockReadingBuilder
var _walkedNodes: [Megrez.NodeAnchor] = []
weak var delegate: KeyHandlerDelegate?
var inputMode: InputMode {
get {
switch _inputMode {
case "org.atelierInmu.inputmethod.vChewing.IMECHS":
return InputMode.imeModeCHS
case "org.atelierInmu.inputmethod.vChewing.IMECHT":
return InputMode.imeModeCHT
default:
return InputMode.imeModeNULL
}
}
set { setInputMode(newValue.rawValue) }
}
override init() {
_builder = Megrez.BlockReadingBuilder(lm: _languageModel)
super.init()
Composer.ensureParser()
setInputMode(ctlInputMethod.currentInputMode)
}
func clear() {
Composer.clearBuffer()
_builder.clear()
_walkedNodes.removeAll()
}
// ObjC 使
func setInputMode(_ value: String) {
// isKindOfClass
// plist
let isCHS: Bool = (value == InputMode.imeModeCHS.rawValue)
// ctlInputMethod:
ctlInputMethod.currentInputMode = isCHS ? InputMode.imeModeCHS.rawValue : InputMode.imeModeCHT.rawValue
mgrPrefs.mostRecentInputMode = ctlInputMethod.currentInputMode
// _inputMode ctlInputMethod
if _inputMode != ctlInputMethod.currentInputMode {
// Reinitiate language models if necessary
setInputModesToLM(isCHS: isCHS)
// Synchronize the sub-languageModel state settings to the new LM.
syncBaseLMPrefs()
// Create new grid builder.
createNewBuilder()
if !Composer.isBufferEmpty() {
Composer.clearBuffer()
}
}
//
_inputMode = ctlInputMethod.currentInputMode
}
// MARK: - Functions dealing with Megrez.
func walk() {
// Retrieve the most likely trellis, i.e. a Maximum Likelihood Estimation
// of the best possible Mandarin characters given the input syllables,
// using the Viterbi algorithm implemented in the Gramambular library
let walker = Megrez.Walker(grid: _builder.grid())
// the reverse walk traces the trellis from the end
let walked: [Megrez.NodeAnchor] = walker.reverseWalk(at: _builder.grid().width())
// then we use ".reversed()" to reverse the nodes so that we get the forward-walked nodes
_walkedNodes.removeAll()
_walkedNodes.append(contentsOf: walked.reversed())
}
func popOverflowComposingTextAndWalk() -> String {
// In ideal situations we can allow users to type infinitely in a buffer.
// However, Viberti algorithm has a complexity of O(N^2), the walk will
// become slower as the number of nodes increase. Therefore, we need to
// auto-commit overflown texts which usually lose their influence over
// the whole MLE anyway -- so that when the user type along, the already
// composed text in the rear side of the buffer will be committed out.
// (i.e. popped out.)
var poppedText = ""
if _builder.grid().width() > mgrPrefs.composingBufferSize {
if _walkedNodes.count > 0 {
let anchor: Megrez.NodeAnchor = _walkedNodes[0]
if let theNode = anchor.node {
poppedText = theNode.currentKeyValue().value
}
_builder.removeHeadReadings(count: anchor.spanningLength)
}
}
walk()
return poppedText
}
func buildAssociatePhraseArray(withKey key: String) -> [String] {
var arrResult: [String] = []
if _languageModel.hasAssociatedPhrasesForKey(key) {
arrResult.append(contentsOf: _languageModel.associatedPhrasesForKey(key))
}
return arrResult
}
func fixNode(value: String) {
let cursorIndex: Int = getActualCandidateCursorIndex()
let selectedNode: Megrez.NodeAnchor = _builder.grid().fixNodeSelectedCandidate(
location: cursorIndex, value: value
)
//
if !mgrPrefs.useSCPCTypingMode {
// If the length of the readings and the characters do not match,
// it often means it is a special symbol and it should not be stored
// in the user override model.
var addToUserOverrideModel = true
if selectedNode.spanningLength != value.count {
addToUserOverrideModel = false
}
if addToUserOverrideModel {
if let theNode = selectedNode.node {
// SymbolLM Score -12
if theNode.scoreFor(candidate: value) <= -12 {
addToUserOverrideModel = false
}
}
}
if addToUserOverrideModel {
_userOverrideModel.observe(
walkedNodes: _walkedNodes, cursorIndex: cursorIndex, candidate: value,
timestamp: NSDate().timeIntervalSince1970
)
}
}
walk()
if mgrPrefs.moveCursorAfterSelectingCandidate {
var nextPosition = 0
for node in _walkedNodes {
if nextPosition >= cursorIndex { break }
nextPosition += node.spanningLength
}
if nextPosition <= getBuilderLength() {
setBuilderCursorIndex(value: nextPosition)
}
}
}
func getCandidatesArray() -> [String] {
var arrCandidates: [String] = []
var arrNodes: [Megrez.NodeAnchor] = []
arrNodes.append(contentsOf: getRawNodes())
/// nodes
///
///
if !arrNodes.isEmpty {
// sort the nodes, so that longer nodes (representing longer phrases)
// are placed at the top of the candidate list
arrNodes.sort { $0.keyLength > $1.keyLength }
// then use the Swift trick to retrieve the candidates for each node at/crossing the cursor
for currentNodeAnchor in arrNodes {
if let currentNode = currentNodeAnchor.node {
for currentCandidate in currentNode.candidates() {
arrCandidates.append(currentCandidate.value)
}
}
}
}
return arrCandidates
}
func dealWithOverrideModelSuggestions() {
let overrideValue =
mgrPrefs.useSCPCTypingMode
? ""
: _userOverrideModel.suggest(
walkedNodes: _walkedNodes, cursorIndex: getBuilderCursorIndex(),
timestamp: NSDate().timeIntervalSince1970
)
if !overrideValue.isEmpty {
_builder.grid().overrideNodeScoreForSelectedCandidate(
location: getActualCandidateCursorIndex(),
value: overrideValue,
overridingScore: findHighestScore(nodes: getRawNodes(), epsilon: kEpsilon)
)
}
}
func findHighestScore(nodes: [Megrez.NodeAnchor], epsilon: Double) -> Double {
var highestScore: Double = 0
for currentAnchor in nodes {
if let theNode = currentAnchor.node {
let score = theNode.highestUnigramScore()
if score > highestScore {
highestScore = score
}
}
}
return highestScore + epsilon
}
// MARK: - Extracted methods and functions.
func isBuilderEmpty() -> Bool { _builder.grid().width() == 0 }
func getRawNodes() -> [Megrez.NodeAnchor] {
/// 使 nodesCrossing macOS
/// nodeCrossing Megrez
/// Windows
mgrPrefs.setRearCursorMode
? _builder.grid().nodesCrossingOrEndingAt(location: getActualCandidateCursorIndex())
: _builder.grid().nodesEndingAt(location: getActualCandidateCursorIndex())
}
func setInputModesToLM(isCHS: Bool) {
_languageModel = isCHS ? mgrLangModel.lmCHS : mgrLangModel.lmCHT
_userOverrideModel = isCHS ? mgrLangModel.uomCHS : mgrLangModel.uomCHT
}
func syncBaseLMPrefs() {
_languageModel.isPhraseReplacementEnabled = mgrPrefs.phraseReplacementEnabled
_languageModel.isCNSEnabled = mgrPrefs.cns11643Enabled
_languageModel.isSymbolEnabled = mgrPrefs.symbolInputEnabled
}
func createNewBuilder() {
_builder = Megrez.BlockReadingBuilder(lm: _languageModel)
// Each Mandarin syllable is separated by a hyphen.
_builder.setJoinSeparator(separator: "-")
}
func currentReadings() -> [String] { _builder.readings() }
func ifLangModelHasUnigrams(forKey reading: String) -> Bool {
_languageModel.hasUnigramsFor(key: reading)
}
func insertReadingToBuilderAtCursor(reading: String) {
_builder.insertReadingAtCursor(reading: reading)
}
func setBuilderCursorIndex(value: Int) {
_builder.setCursorIndex(newIndex: value)
}
func getBuilderCursorIndex() -> Int {
_builder.cursorIndex()
}
func getBuilderLength() -> Int {
_builder.length()
}
func deleteBuilderReadingInFrontOfCursor() {
_builder.deleteReadingBeforeCursor()
}
func deleteBuilderReadingAfterCursor() {
_builder.deleteReadingAfterCursor()
}
func getKeyLengthAtIndexZero() -> Int {
_walkedNodes[0].node?.currentKeyValue().value.count ?? 0
}
}

View File

@ -28,7 +28,7 @@ import Cocoa
// MARK: - § Handle Candidate State. // MARK: - § Handle Candidate State.
@objc extension KeyHandler { extension KeyHandler {
func handleCandidate( func handleCandidate(
state: InputState, state: InputState,
input: InputHandler, input: InputHandler,
@ -331,7 +331,7 @@ import Cocoa
let punctuation: String = arrPunctuations.joined(separator: "") let punctuation: String = arrPunctuations.joined(separator: "")
var shouldAutoSelectCandidate: Bool = var shouldAutoSelectCandidate: Bool =
chkKeyValidity(charCode) || ifLangModelHasUnigrams(forKey: customPunctuation) Composer.chkKeyValidity(charCode) || ifLangModelHasUnigrams(forKey: customPunctuation)
|| ifLangModelHasUnigrams(forKey: punctuation) || ifLangModelHasUnigrams(forKey: punctuation)
if !shouldAutoSelectCandidate, input.isUpperCaseASCIILetterKey { if !shouldAutoSelectCandidate, input.isUpperCaseASCIILetterKey {

View File

@ -28,7 +28,7 @@ import Cocoa
// MARK: - § Handle Input with States. // MARK: - § Handle Input with States.
@objc extension KeyHandler { extension KeyHandler {
func handle( func handle(
input: InputHandler, input: InputHandler,
state: InputState, state: InputState,
@ -75,7 +75,7 @@ import Cocoa
// If ASCII but not printable, don't use insertText:replacementRange: // If ASCII but not printable, don't use insertText:replacementRange:
// Certain apps don't handle non-ASCII char insertions. // Certain apps don't handle non-ASCII char insertions.
if charCode < 0x80, !isPrintable(charCode) { if charCode < 0x80, !CTools.isPrintable(charCode) {
return false return false
} }
@ -90,7 +90,7 @@ import Cocoa
if input.isNumericPad { if input.isNumericPad {
if !input.isLeft, !input.isRight, !input.isDown, if !input.isLeft, !input.isRight, !input.isDown,
!input.isUp, !input.isSpace, isPrintable(charCode) !input.isUp, !input.isSpace, CTools.isPrintable(charCode)
{ {
clear() clear()
stateCallback(InputState.Empty()) stateCallback(InputState.Empty())
@ -139,13 +139,13 @@ import Cocoa
let skipPhoneticHandling = input.isReservedKey || input.isControlHold || input.isOptionHold let skipPhoneticHandling = input.isReservedKey || input.isControlHold || input.isOptionHold
// See if Phonetic reading is valid. // See if Phonetic reading is valid.
if !skipPhoneticHandling && chkKeyValidity(charCode) { if !skipPhoneticHandling && Composer.chkKeyValidity(charCode) {
combinePhoneticReadingBufferKey(charCode) Composer.combineReadingKey(charCode)
// If we have a tone marker, we have to insert the reading to the // If we have a tone marker, we have to insert the reading to the
// builder in other words, if we don't have a tone marker, we just // builder in other words, if we don't have a tone marker, we just
// update the composing buffer. // update the composing buffer.
composeReading = checkWhetherToneMarkerConfirmsPhoneticReadingBuffer() composeReading = Composer.checkWhetherToneMarkerConfirms()
if !composeReading { if !composeReading {
stateCallback(buildInputtingState()) stateCallback(buildInputtingState())
return true return true
@ -155,28 +155,28 @@ import Cocoa
// See if we have composition if Enter/Space is hit and buffer is not empty. // See if we have composition if Enter/Space is hit and buffer is not empty.
// We use "|=" conditioning so that the tone marker key is also taken into account. // We use "|=" conditioning so that the tone marker key is also taken into account.
// However, Swift does not support "|=". // However, Swift does not support "|=".
composeReading = composeReading || (!isPhoneticReadingBufferEmpty() && (input.isSpace || input.isEnter)) composeReading = composeReading || (!Composer.isBufferEmpty() && (input.isSpace || input.isEnter))
if composeReading { if composeReading {
let reading = getSyllableCompositionFromPhoneticReadingBuffer() let reading = Composer.getSyllableComposition()
if !ifLangModelHasUnigrams(forKey: reading) { if !ifLangModelHasUnigrams(forKey: reading) {
IME.prtDebugIntel("B49C0979") IME.prtDebugIntel("B49C0979:語彙庫內無「\(reading)」的匹配記錄。")
errorCallback() errorCallback()
stateCallback(buildInputtingState()) stateCallback(buildInputtingState())
return true return true
} }
// ... and insert it into the lattice grid... // ... and insert it into the lattice grid...
insertReadingToBuilder(atCursor: reading) insertReadingToBuilderAtCursor(reading: reading)
// ... then walk the lattice grid... // ... then walk the lattice grid...
let poppedText = _popOverflowComposingTextAndWalk() let poppedText = popOverflowComposingTextAndWalk()
// ... get and tweak override model suggestion if possible... // ... get and tweak override model suggestion if possible...
dealWithOverrideModelSuggestions() dealWithOverrideModelSuggestions()
// ... then update the text. // ... then update the text.
clearPhoneticReadingBuffer() Composer.clearBuffer()
let inputting = buildInputtingState() let inputting = buildInputtingState()
inputting.poppedText = poppedText inputting.poppedText = poppedText
@ -216,7 +216,7 @@ import Cocoa
// MARK: Calling candidate window using Space or Down or PageUp / PageDn. // MARK: Calling candidate window using Space or Down or PageUp / PageDn.
if let currentState = state as? InputState.NotEmpty { if let currentState = state as? InputState.NotEmpty {
if isPhoneticReadingBufferEmpty(), if Composer.isBufferEmpty(),
input.isExtraChooseCandidateKey || input.isExtraChooseCandidateKeyReverse || input.isSpace input.isExtraChooseCandidateKey || input.isExtraChooseCandidateKeyReverse || input.isSpace
|| input.isPageDown || input.isPageUp || input.isTab || input.isPageDown || input.isPageUp || input.isTab
|| (input.useVerticalMode && (input.isVerticalModeOnlyChooseCandidateKey)) || (input.useVerticalMode && (input.isVerticalModeOnlyChooseCandidateKey))
@ -233,8 +233,8 @@ import Cocoa
stateCallback(InputState.Committing(poppedText: " ")) stateCallback(InputState.Committing(poppedText: " "))
stateCallback(InputState.Empty()) stateCallback(InputState.Empty())
} else if ifLangModelHasUnigrams(forKey: " ") { } else if ifLangModelHasUnigrams(forKey: " ") {
insertReadingToBuilder(atCursor: " ") insertReadingToBuilderAtCursor(reading: " ")
let poppedText = _popOverflowComposingTextAndWalk() let poppedText = popOverflowComposingTextAndWalk()
let inputting = buildInputtingState() let inputting = buildInputtingState()
inputting.poppedText = poppedText inputting.poppedText = poppedText
stateCallback(inputting) stateCallback(inputting)
@ -329,9 +329,9 @@ import Cocoa
if input.isSymbolMenuPhysicalKey && !input.isShiftHold { if input.isSymbolMenuPhysicalKey && !input.isShiftHold {
if !input.isOptionHold { if !input.isOptionHold {
if ifLangModelHasUnigrams(forKey: "_punctuation_list") { if ifLangModelHasUnigrams(forKey: "_punctuation_list") {
if isPhoneticReadingBufferEmpty() { if Composer.isBufferEmpty() {
insertReadingToBuilder(atCursor: "_punctuation_list") insertReadingToBuilderAtCursor(reading: "_punctuation_list")
let poppedText: String! = _popOverflowComposingTextAndWalk() let poppedText: String! = popOverflowComposingTextAndWalk()
let inputting = buildInputtingState() let inputting = buildInputtingState()
inputting.poppedText = poppedText inputting.poppedText = poppedText
stateCallback(inputting) stateCallback(inputting)
@ -354,7 +354,7 @@ import Cocoa
// MARK: Punctuation // MARK: Punctuation
// if nothing is matched, see if it's a punctuation key for current layout. // If nothing is matched, see if it's a punctuation key for current layout.
var punctuationNamePrefix = "" var punctuationNamePrefix = ""
@ -418,7 +418,7 @@ import Cocoa
// "thinking" that the key is not actually consumed. // "thinking" that the key is not actually consumed.
// F1-F12 // F1-F12
// 便 // 便
if (state is InputState.NotEmpty) || !isPhoneticReadingBufferEmpty() { if (state is InputState.NotEmpty) || !Composer.isBufferEmpty() {
IME.prtDebugIntel( IME.prtDebugIntel(
"Blocked data: charCode: \(charCode), keyCode: \(input.keyCode)") "Blocked data: charCode: \(charCode), keyCode: \(input.keyCode)")
IME.prtDebugIntel("A9BFF20E") IME.prtDebugIntel("A9BFF20E")

View File

@ -28,7 +28,7 @@ import Cocoa
// MARK: - § Misc functions. // MARK: - § Misc functions.
@objc extension KeyHandler { extension KeyHandler {
func getCurrentMandarinParser() -> String { func getCurrentMandarinParser() -> String {
mgrPrefs.mandarinParserName + "_" mgrPrefs.mandarinParserName + "_"
} }
@ -43,7 +43,7 @@ import Cocoa
&& (cursorIndex < getBuilderLength())) && (cursorIndex < getBuilderLength()))
|| cursorIndex == 0 || cursorIndex == 0
{ {
if cursorIndex == 0 && !mgrPrefs.setRearCursorMode { if cursorIndex == 0, !mgrPrefs.setRearCursorMode {
cursorIndex += getKeyLengthAtIndexZero() cursorIndex += getKeyLengthAtIndexZero()
} else { } else {
cursorIndex += 1 cursorIndex += 1

View File

@ -28,49 +28,71 @@ import Cocoa
// MARK: - § State managements. // MARK: - § State managements.
@objc extension KeyHandler { extension KeyHandler {
// MARK: - State Building // MARK: - State Building
func buildInputtingState() -> InputState.Inputting { func buildInputtingState() -> InputState.Inputting {
// // "Updating the composing buffer" means to request the client
packageBufferStateMaterials() // to "refresh" the text input buffer with our "composing text"
// var composingBuffer = ""
let composedText = getComposedText() var composedStringCursorIndex = 0
let packagedCursorIndex = UInt(getPackagedCursorIndex())
let resultOfRear = getStrLocationResult(isFront: false)
let resultOfFront = getStrLocationResult(isFront: true)
// var readingCursorIndex: size_t = 0
let newState = InputState.Inputting(composingBuffer: composedText, cursorIndex: packagedCursorIndex) let builderCursorIndex: size_t = getBuilderCursorIndex()
// // We must do some Unicode codepoint counting to find the actual cursor location for the client
var tooltip = "" // i.e. we need to take UTF-16 into consideration, for which a surrogate pair takes 2 UniChars
// locations. These processes are inherited from the ObjC++ version of this class and might be
// unnecessary in Swift, but this deduction requires further experiments.
for walkedNode in _walkedNodes {
if let theNode = walkedNode.node {
let strNodeValue = theNode.currentKeyValue().value
composingBuffer += strNodeValue
// let arrSplit: [NSString] = (strNodeValue as NSString).split()
// TODO: let codepointCount = arrSplit.count
// if ctlInputMethod.currentKeyHandler.inputMode == InputMode.imeModeCHT {
// if mgrPrefs.chineseConversionEnabled && !mgrPrefs.shiftJISShinjitaiOutputEnabled {
// tooltip = String(
// format: "%@%@%@", NSLocalizedString("Force KangXi Writing", comment: ""), "\n",
// NSLocalizedString("NotificationSwitchON", comment: ""))
// } else if mgrPrefs.shiftJISShinjitaiOutputEnabled {
// tooltip = String(
// format: "%@%@%@", NSLocalizedString("JIS Shinjitai Output", comment: ""), "\n",
// NSLocalizedString("NotificationSwitchON", comment: ""))
// }
// }
// NSString Emoji // This re-aligns the cursor index in the composed string
// // (the actual cursor on the screen) with the builder's logical
if resultOfRear != "" || resultOfFront != "" { // cursor (reading) cursor; each built node has a "spanning length"
tooltip = String( // (e.g. two reading blocks has a spanning length of 2), and we
format: NSLocalizedString("Cursor is between \"%@\" and \"%@\".", comment: ""), // accumulate those lengths to calculate the displayed cursor
resultOfFront, resultOfRear // index.
) let spanningLength: Int = walkedNode.spanningLength
if readingCursorIndex + spanningLength <= builderCursorIndex {
composedStringCursorIndex += (strNodeValue as NSString).length
readingCursorIndex += spanningLength
} else {
if codepointCount == spanningLength {
var i = 0
while i < codepointCount, readingCursorIndex < builderCursorIndex {
composedStringCursorIndex += arrSplit[i].length
readingCursorIndex += 1
i += 1
} }
} else {
if readingCursorIndex < builderCursorIndex {
composedStringCursorIndex += (strNodeValue as NSString).length
readingCursorIndex += spanningLength
if readingCursorIndex > builderCursorIndex {
readingCursorIndex = builderCursorIndex
}
}
}
}
}
}
// Now, we gather all the intel, separate the composing buffer to two parts (head and tail),
// and insert the reading text (the Mandarin syllable) in between them.
// The reading text is what the user is typing.
newState.tooltip = tooltip let head = String((composingBuffer as NSString).substring(to: composedStringCursorIndex))
return newState let reading = Composer.getComposition()
let tail = String((composingBuffer as NSString).substring(from: composedStringCursorIndex))
let composedText = head + reading + tail
let cursorIndex = composedStringCursorIndex + reading.count
return InputState.Inputting(composingBuffer: composedText, cursorIndex: UInt(cursorIndex))
} }
// MARK: - // MARK: -
@ -102,7 +124,8 @@ import Cocoa
) -> InputState.AssociatedPhrases! { ) -> InputState.AssociatedPhrases! {
//  Xcode //  Xcode
InputState.AssociatedPhrases( InputState.AssociatedPhrases(
candidates: buildAssociatePhraseArray(withKey: key), useVerticalMode: useVerticalMode) candidates: buildAssociatePhraseArray(withKey: key), useVerticalMode: useVerticalMode
)
} }
// MARK: - // MARK: -
@ -190,14 +213,14 @@ import Cocoa
return false return false
} }
if isPhoneticReadingBufferEmpty() { if Composer.isBufferEmpty() {
insertReadingToBuilder(atCursor: customPunctuation) insertReadingToBuilderAtCursor(reading: customPunctuation)
let poppedText = _popOverflowComposingTextAndWalk() let poppedText = popOverflowComposingTextAndWalk()
let inputting = buildInputtingState() let inputting = buildInputtingState()
inputting.poppedText = poppedText inputting.poppedText = poppedText
stateCallback(inputting) stateCallback(inputting)
if mgrPrefs.useSCPCTypingMode, isPhoneticReadingBufferEmpty() { if mgrPrefs.useSCPCTypingMode, Composer.isBufferEmpty() {
let candidateState = buildCandidate( let candidateState = buildCandidate(
state: inputting, state: inputting,
useVerticalMode: useVerticalMode useVerticalMode: useVerticalMode
@ -256,7 +279,7 @@ import Cocoa
return false return false
} }
let readings: [String] = _currentReadings() let readings: [String] = currentReadings()
let composingBuffer = let composingBuffer =
(IME.areWeUsingOurOwnPhraseEditor) (IME.areWeUsingOurOwnPhraseEditor)
? readings.joined(separator: "-") ? readings.joined(separator: "-")
@ -280,10 +303,10 @@ import Cocoa
return false return false
} }
if isPhoneticReadingBufferEmpty() { if Composer.isBufferEmpty() {
if getBuilderCursorIndex() >= 0 { if getBuilderCursorIndex() >= 0 {
deleteBuilderReadingInFrontOfCursor() deleteBuilderReadingInFrontOfCursor()
_walk() walk()
} else { } else {
IME.prtDebugIntel("9D69908D") IME.prtDebugIntel("9D69908D")
errorCallback() errorCallback()
@ -291,10 +314,10 @@ import Cocoa
return true return true
} }
} else { } else {
doBackSpaceToPhoneticReadingBuffer() Composer.doBackSpaceToBuffer()
} }
if isPhoneticReadingBufferEmpty(), getBuilderLength() == 0 { if Composer.isBufferEmpty(), getBuilderLength() == 0 {
stateCallback(InputState.EmptyIgnoringPreviousState()) stateCallback(InputState.EmptyIgnoringPreviousState())
} else { } else {
stateCallback(buildInputtingState()) stateCallback(buildInputtingState())
@ -313,10 +336,10 @@ import Cocoa
return false return false
} }
if isPhoneticReadingBufferEmpty() { if Composer.isBufferEmpty() {
if getBuilderCursorIndex() != getBuilderLength() { if getBuilderCursorIndex() != getBuilderLength() {
deleteBuilderReadingAfterCursor() deleteBuilderReadingAfterCursor()
_walk() walk()
let inputting = buildInputtingState() let inputting = buildInputtingState()
// count > 0!isEmpty滿 // count > 0!isEmpty滿
if !inputting.composingBuffer.isEmpty { if !inputting.composingBuffer.isEmpty {
@ -348,7 +371,7 @@ import Cocoa
if !(state is InputState.Inputting) { if !(state is InputState.Inputting) {
return false return false
} }
if !isPhoneticReadingBufferEmpty() { if !Composer.isBufferEmpty() {
IME.prtDebugIntel("9B6F908D") IME.prtDebugIntel("9B6F908D")
errorCallback() errorCallback()
} }
@ -367,7 +390,7 @@ import Cocoa
return false return false
} }
if !isPhoneticReadingBufferEmpty() { if !Composer.isBufferEmpty() {
IME.prtDebugIntel("ABC44080") IME.prtDebugIntel("ABC44080")
errorCallback() errorCallback()
stateCallback(state) stateCallback(state)
@ -375,7 +398,7 @@ import Cocoa
} }
if getBuilderCursorIndex() != 0 { if getBuilderCursorIndex() != 0 {
setBuilderCursorIndex(0) setBuilderCursorIndex(value: 0)
stateCallback(buildInputtingState()) stateCallback(buildInputtingState())
} else { } else {
IME.prtDebugIntel("66D97F90") IME.prtDebugIntel("66D97F90")
@ -397,7 +420,7 @@ import Cocoa
return false return false
} }
if !isPhoneticReadingBufferEmpty() { if !Composer.isBufferEmpty() {
IME.prtDebugIntel("9B69908D") IME.prtDebugIntel("9B69908D")
errorCallback() errorCallback()
stateCallback(state) stateCallback(state)
@ -405,7 +428,7 @@ import Cocoa
} }
if getBuilderCursorIndex() != getBuilderLength() { if getBuilderCursorIndex() != getBuilderLength() {
setBuilderCursorIndex(getBuilderLength()) setBuilderCursorIndex(value: getBuilderLength())
stateCallback(buildInputtingState()) stateCallback(buildInputtingState())
} else { } else {
IME.prtDebugIntel("9B69908E") IME.prtDebugIntel("9B69908E")
@ -436,8 +459,8 @@ import Cocoa
stateCallback(InputState.EmptyIgnoringPreviousState()) stateCallback(InputState.EmptyIgnoringPreviousState())
} else { } else {
// If reading is not empty, we cancel the reading. // If reading is not empty, we cancel the reading.
if !isPhoneticReadingBufferEmpty() { if !Composer.isBufferEmpty() {
clearPhoneticReadingBuffer() Composer.clearBuffer()
if getBuilderLength() == 0 { if getBuilderLength() == 0 {
stateCallback(InputState.Empty()) stateCallback(InputState.Empty())
} else { } else {
@ -458,7 +481,7 @@ import Cocoa
) -> Bool { ) -> Bool {
if !(state is InputState.Inputting) { return false } if !(state is InputState.Inputting) { return false }
if !isPhoneticReadingBufferEmpty() { if !Composer.isBufferEmpty() {
IME.prtDebugIntel("B3BA5257") IME.prtDebugIntel("B3BA5257")
errorCallback() errorCallback()
stateCallback(state) stateCallback(state)
@ -475,7 +498,7 @@ import Cocoa
composingBuffer: currentState.composingBuffer, composingBuffer: currentState.composingBuffer,
cursorIndex: currentState.cursorIndex, cursorIndex: currentState.cursorIndex,
markerIndex: UInt(nextPosition), markerIndex: UInt(nextPosition),
readings: _currentReadings() readings: currentReadings()
) )
marking.tooltipForInputting = currentState.tooltip marking.tooltipForInputting = currentState.tooltip
stateCallback(marking) stateCallback(marking)
@ -486,7 +509,7 @@ import Cocoa
} }
} else { } else {
if getBuilderCursorIndex() < getBuilderLength() { if getBuilderCursorIndex() < getBuilderLength() {
setBuilderCursorIndex(getBuilderCursorIndex() + 1) setBuilderCursorIndex(value: getBuilderCursorIndex() + 1)
stateCallback(buildInputtingState()) stateCallback(buildInputtingState())
} else { } else {
IME.prtDebugIntel("A96AAD58") IME.prtDebugIntel("A96AAD58")
@ -509,7 +532,7 @@ import Cocoa
) -> Bool { ) -> Bool {
if !(state is InputState.Inputting) { return false } if !(state is InputState.Inputting) { return false }
if !isPhoneticReadingBufferEmpty() { if !Composer.isBufferEmpty() {
IME.prtDebugIntel("6ED95318") IME.prtDebugIntel("6ED95318")
errorCallback() errorCallback()
stateCallback(state) stateCallback(state)
@ -526,7 +549,7 @@ import Cocoa
composingBuffer: currentState.composingBuffer, composingBuffer: currentState.composingBuffer,
cursorIndex: currentState.cursorIndex, cursorIndex: currentState.cursorIndex,
markerIndex: UInt(previousPosition), markerIndex: UInt(previousPosition),
readings: _currentReadings() readings: currentReadings()
) )
marking.tooltipForInputting = currentState.tooltip marking.tooltipForInputting = currentState.tooltip
stateCallback(marking) stateCallback(marking)
@ -537,7 +560,7 @@ import Cocoa
} }
} else { } else {
if getBuilderCursorIndex() > 0 { if getBuilderCursorIndex() > 0 {
setBuilderCursorIndex(getBuilderCursorIndex() - 1) setBuilderCursorIndex(value: getBuilderCursorIndex() - 1)
stateCallback(buildInputtingState()) stateCallback(buildInputtingState())
} else { } else {
IME.prtDebugIntel("7045E6F3") IME.prtDebugIntel("7045E6F3")

View File

@ -1,155 +0,0 @@
// Copyright (c) 2011 and onwards The OpenVanilla Project (MIT License).
// All possible vChewing-specific modifications are of:
// (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.
*/
#include "KeyValueBlobReader.h"
namespace vChewing
{
KeyValueBlobReader::State KeyValueBlobReader::Next(KeyValue *out)
{
static auto new_line = [](char c) { return c == '\n' || c == '\r'; };
static auto blank = [](char c) { return c == ' ' || c == '\t'; };
static auto blank_or_newline = [](char c) { return blank(c) || new_line(c); };
static auto content_char = [](char c) { return !blank(c) && !new_line(c); };
if (state_ == State::ERROR)
{
return state_;
}
const char *key_begin = nullptr;
size_t key_length = 0;
const char *value_begin = nullptr;
size_t value_length = 0;
while (true)
{
state_ = SkipUntilNot(blank_or_newline);
if (state_ != State::CAN_CONTINUE)
{
return state_;
}
// Check if it's a comment line; if so, read until end of line.
if (*current_ != '#')
{
break;
}
state_ = SkipUntil(new_line);
if (state_ != State::CAN_CONTINUE)
{
return state_;
}
}
// No need to check whether* current_ is a content_char, since content_char
// is defined as not blank and not new_line.
key_begin = current_;
state_ = SkipUntilNot(content_char);
if (state_ != State::CAN_CONTINUE)
{
goto error;
}
key_length = current_ - key_begin;
// There should be at least one blank character after the key string.
if (!blank(*current_))
{
goto error;
}
state_ = SkipUntilNot(blank);
if (state_ != State::CAN_CONTINUE)
{
goto error;
}
if (!content_char(*current_))
{
goto error;
}
value_begin = current_;
// value must only contain content characters, blanks not are allowed.
// also, there's no need to check the state after this, since we will always
// emit the value. This also avoids the situation where trailing spaces in a
// line would become part of the value.
SkipUntilNot(content_char);
value_length = current_ - value_begin;
// Unconditionally skip until the end of the line. This prevents the case
// like "foo bar baz\n" where baz should not be treated as the Next key.
SkipUntil(new_line);
if (out != nullptr)
{
*out = KeyValue{std::string_view{key_begin, key_length}, std::string_view{value_begin, value_length}};
}
state_ = State::HAS_PAIR;
return state_;
error:
state_ = State::ERROR;
return state_;
}
KeyValueBlobReader::State KeyValueBlobReader::SkipUntilNot(const std::function<bool(char)> &f)
{
while (current_ != end_ && *current_)
{
if (!f(*current_))
{
return State::CAN_CONTINUE;
}
++current_;
}
return State::END;
}
KeyValueBlobReader::State KeyValueBlobReader::SkipUntil(const std::function<bool(char)> &f)
{
while (current_ != end_ && *current_)
{
if (f(*current_))
{
return State::CAN_CONTINUE;
}
++current_;
}
return State::END;
}
std::ostream &operator<<(std::ostream &os, const KeyValueBlobReader::KeyValue &kv)
{
os << "(key: " << kv.key << ", value: " << kv.value << ")";
return os;
}
} // namespace vChewing

View File

@ -1,107 +0,0 @@
// Copyright (c) 2011 and onwards The OpenVanilla Project (MIT License).
// All possible vChewing-specific modifications are of:
// (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.
*/
#ifndef SOURCE_ENGINE_KEYVALUEBLOBREADER_H_
#define SOURCE_ENGINE_KEYVALUEBLOBREADER_H_
#include <cstddef>
#include <functional>
#include <iostream>
#include <string_view>
// A reader for text-based, blank-separated key-value pairs in a binary blob.
//
// This reader is suitable for reading language model files that entirely
// consist of key-value pairs. Leading or trailing spaces are ignored.
// Lines that start with "#" are treated as comments. Values cannot contain
// spaces. Any space after the value string is parsed is ignored. This implies
// that after a blank, anything that comes after the value can be used as
// comment. Both ' ' and '\t' are treated as blank characters, and the parser
// is agnostic to how lines are ended, and so LF, CR LF, and CR are all valid
// line endings.
//
// std::string_view is used to allow returning results efficiently. As a result,
// the blob is a const char* and will never be mutated. This implies, for
// example, read-only mmap can be used to parse large files.
namespace vChewing
{
class KeyValueBlobReader
{
public:
enum class State : int
{
// There are no more key-value pairs in this blob.
END = 0,
// The reader has produced a new key-value pair.
HAS_PAIR = 1,
// An error is encountered and the parsing stopped.
ERROR = -1,
// Internal-only state: the parser can continue parsing.
CAN_CONTINUE = 2
};
struct KeyValue
{
constexpr KeyValue() : key(""), value("")
{
}
constexpr KeyValue(std::string_view k, std::string_view v) : key(k), value(v)
{
}
bool operator==(const KeyValue &another) const
{
return key == another.key && value == another.value;
}
std::string_view key;
std::string_view value;
};
KeyValueBlobReader(const char *blob, size_t size) : current_(blob), end_(blob + size)
{
}
// Parse the next key-value pair and return the state of the reader. If
// `out` is passed, out will be set to the produced key-value pair if there
// is one.
State Next(KeyValue *out = nullptr);
private:
State SkipUntil(const std::function<bool(char)> &f);
State SkipUntilNot(const std::function<bool(char)> &f);
const char *current_;
const char *end_;
State state_ = State::CAN_CONTINUE;
};
std::ostream &operator<<(std::ostream &, const KeyValueBlobReader::KeyValue &);
} // namespace vChewing
#endif // SOURCE_ENGINE_KEYVALUEBLOBREADER_H_

View File

@ -47,7 +47,7 @@ extension NSString {
return (string.count, string) return (string.count, string)
} }
@objc public func nextUtf16Position(for index: Int) -> Int { public func nextUtf16Position(for index: Int) -> Int {
var (fixedIndex, string) = characterIndex(from: index) var (fixedIndex, string) = characterIndex(from: index)
if fixedIndex < string.count { if fixedIndex < string.count {
fixedIndex += 1 fixedIndex += 1
@ -55,7 +55,7 @@ extension NSString {
return string[..<string.index(string.startIndex, offsetBy: fixedIndex)].utf16.count return string[..<string.index(string.startIndex, offsetBy: fixedIndex)].utf16.count
} }
@objc public func previousUtf16Position(for index: Int) -> Int { public func previousUtf16Position(for index: Int) -> Int {
var (fixedIndex, string) = characterIndex(from: index) var (fixedIndex, string) = characterIndex(from: index)
if fixedIndex > 0 { if fixedIndex > 0 {
fixedIndex -= 1 fixedIndex -= 1
@ -63,11 +63,11 @@ extension NSString {
return string[..<string.index(string.startIndex, offsetBy: fixedIndex)].utf16.count return string[..<string.index(string.startIndex, offsetBy: fixedIndex)].utf16.count
} }
@objc public var count: Int { public var count: Int {
(self as String).count (self as String).count
} }
@objc public func split() -> [NSString] { public func split() -> [NSString] {
Array(self as String).map { Array(self as String).map {
NSString(string: String($0)) NSString(string: String($0))
} }

View File

@ -31,7 +31,7 @@ extension String {
} }
class vChewingKanjiConverter: NSObject { class vChewingKanjiConverter: NSObject {
@objc class func cnvTradToKangXi(_ strObj: String) -> String { class func cnvTradToKangXi(_ strObj: String) -> String {
var strObj = strObj var strObj = strObj
strObj.selfReplace("", "") strObj.selfReplace("", "")
strObj.selfReplace("", "") strObj.selfReplace("", "")
@ -217,7 +217,7 @@ class vChewingKanjiConverter: NSObject {
return strObj return strObj
} }
@objc class func cnvTradToJIS(_ strObj: String) -> String { class func cnvTradToJIS(_ strObj: String) -> String {
// //
var strObj = cnvTradToKangXi(strObj) var strObj = cnvTradToKangXi(strObj)
strObj.selfReplace("", "") strObj.selfReplace("", "")

View File

@ -1,176 +0,0 @@
// 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.
*/
#include "LMConsolidator.h"
#include "vChewing-Swift.h"
namespace vChewing
{
constexpr std::string_view FORMATTED_PRAGMA_HEADER =
"# 𝙵𝙾𝚁𝙼𝙰𝚃 𝚘𝚛𝚐.𝚊𝚝𝚎𝚕𝚒𝚎𝚛𝙸𝚗𝚖𝚞.𝚟𝚌𝚑𝚎𝚠𝚒𝚗𝚐.𝚞𝚜𝚎𝚛𝙻𝚊𝚗𝚐𝚞𝚊𝚐𝚎𝙼𝚘𝚍𝚎𝚕𝙳𝚊𝚝𝚊.𝚏𝚘𝚛𝚖𝚊𝚝𝚝𝚎𝚍";
// HEADER VERIFIER. CREDIT: Shiki Suen
bool LMConsolidator::CheckPragma(const char *path)
{
ifstream zfdCheckPragma(path);
if (zfdCheckPragma.good())
{
string firstLine;
getline(zfdCheckPragma, firstLine);
if (mgrPrefs.isDebugModeEnabled)
syslog(LOG_CONS, "HEADER SEEN ||%s", firstLine.c_str());
if (firstLine != FORMATTED_PRAGMA_HEADER)
{
if (mgrPrefs.isDebugModeEnabled)
syslog(LOG_CONS, "HEADER VERIFICATION FAILED. START IN-PLACE CONSOLIDATING PROCESS.");
return false;
}
}
if (mgrPrefs.isDebugModeEnabled)
syslog(LOG_CONS, "HEADER VERIFICATION SUCCESSFUL.");
return true;
}
// EOF FIXER. CREDIT: Shiki Suen.
bool LMConsolidator::FixEOF(const char *path)
{
std::fstream zfdEOFFixerIncomingStream(path);
zfdEOFFixerIncomingStream.seekg(-1, std::ios_base::end);
char z;
zfdEOFFixerIncomingStream.get(z);
if (z != '\n')
{
if (mgrPrefs.isDebugModeEnabled)
syslog(LOG_CONS, "// REPORT: Data File not ended with a new line.\n");
if (mgrPrefs.isDebugModeEnabled)
syslog(LOG_CONS, "// DATA FILE: %s", path);
if (mgrPrefs.isDebugModeEnabled)
syslog(LOG_CONS, "// PROCEDURE: Trying to insert a new line as EOF before per-line check process.\n");
std::ofstream zfdEOFFixerOutput(path, std::ios_base::app);
zfdEOFFixerOutput << std::endl;
zfdEOFFixerOutput.close();
if (zfdEOFFixerOutput.fail())
{
if (mgrPrefs.isDebugModeEnabled)
syslog(LOG_CONS, "// REPORT: Failed to append a newline to the data file. Insufficient Privileges?\n");
if (mgrPrefs.isDebugModeEnabled)
syslog(LOG_CONS, "// DATA FILE: %s", path);
return false;
}
}
zfdEOFFixerIncomingStream.close();
if (zfdEOFFixerIncomingStream.fail())
{
if (mgrPrefs.isDebugModeEnabled)
syslog(LOG_CONS,
"// REPORT: Failed to read lines through the data file for EOF check. Insufficient Privileges?\n");
if (mgrPrefs.isDebugModeEnabled)
syslog(LOG_CONS, "// DATA FILE: %s", path);
return false;
}
return true;
} // END: EOF FIXER.
// CONTENT CONSOLIDATOR. CREDIT: Shiki Suen.
bool LMConsolidator::ConsolidateContent(const char *path, bool shouldCheckPragma)
{
bool pragmaCheckResult = LMConsolidator::CheckPragma(path);
if (pragmaCheckResult && shouldCheckPragma)
{
return true;
}
ifstream zfdContentConsolidatorIncomingStream(path);
vector<string> vecEntry;
while (!zfdContentConsolidatorIncomingStream.eof())
{ // Xcode 13 能用的 ObjCpp 與 Cpp 並無原生支援「\h」這個 Regex 參數的能力,只能逐行處理。
string zfdBuffer;
getline(zfdContentConsolidatorIncomingStream, zfdBuffer);
vecEntry.push_back(zfdBuffer);
}
// 第一遍 for 用來統整每行內的內容。
// regex sedCJKWhiteSpace("\\x{3000}"), sedNonBreakWhiteSpace("\\x{A0}"), sedWhiteSpace("\\s+"),
// sedLeadingSpace("^\\s"), sedTrailingSpace("\\s$"); // 這樣寫會導致輸入法敲不了任何字,推測 Xcode 13 支援的 cpp /
// objCpp 可能對某些 Regex 寫法有相容性問題。 regex sedCJKWhiteSpace(" "), sedNonBreakWhiteSpace(" "),
// sedWhiteSpace("\\s+"), sedLeadingSpace("^\\s"), sedTrailingSpace("\\s$"); // RegEx 先定義好。
regex sedToConsolidate("( +| +| +|\t+)+"), sedToTrim("(^\\s|\\s$)");
for (int i = 0; i < vecEntry.size(); i++)
{ // 第一遍 for 用來統整每行內的內容。
if (vecEntry[i].size() != 0)
{ // 不要理會空行,否則給空行加上 endl 等於再加空行。
// RegEx 處理順序:先將全形空格換成西文空格,然後合併任何意義上的連續空格(包括 tab
// 等),最後去除每行首尾空格。 vecEntry[i] = regex_replace(vecEntry[i], sedCJKWhiteSpace, " ").c_str(); //
// 中日韓全形空格轉為 ASCII 空格。 vecEntry[i] = regex_replace(vecEntry[i], sedNonBreakWhiteSpace, "
// ").c_str(); // Non-Break 型空格轉為 ASCII 空格。 vecEntry[i] = regex_replace(vecEntry[i], sedWhiteSpace,
// " ").c_str(); // 所有意義上的連續的 \s 型空格都轉為單個 ASCII 空格。 vecEntry[i] =
// regex_replace(vecEntry[i], sedLeadingSpace, "").c_str(); // 去掉行首空格。 vecEntry[i] =
// regex_replace(vecEntry[i], sedTrailingSpace, "").c_str(); // 去掉行尾空格。
// 上述命令分步驟執行容易產生效能問題,故濃縮為下述兩句。
vecEntry[i] = regex_replace(vecEntry[i], sedToConsolidate, " ").c_str();
vecEntry[i] = regex_replace(vecEntry[i], sedToTrim, "").c_str();
}
}
// 在第二遍 for 運算之前,針對 vecEntry 去除重複條目。
std::reverse(vecEntry.begin(), vecEntry.end()); // 先首尾顛倒,免得破壞最新的 override 資訊。
vecEntry.erase(unique(vecEntry.begin(), vecEntry.end()), vecEntry.end()); // 去重複。
std::reverse(vecEntry.begin(), vecEntry.end()); // 再顛倒回來。
// 統整完畢。開始將統整過的內容寫入檔案。
ofstream zfdContentConsolidatorOutput(path); // 這裡是要從頭開始重寫檔案內容,所以不需要「 ios_base::app 」。
if (!pragmaCheckResult)
{
zfdContentConsolidatorOutput << FORMATTED_PRAGMA_HEADER << endl; // 寫入經過整理處理的 HEADER。
}
for (int i = 0; i < vecEntry.size(); i++)
{ // 第二遍 for 用來寫入統整過的內容。
if (vecEntry[i].size() != 0)
{ // 這句很重要,不然還是會把經過 RegEx 處理後出現的空行搞到檔案裡。
zfdContentConsolidatorOutput << vecEntry[i]
<< endl; // 這裡是必須得加上 endl 的,不然所有行都變成一個整合行。
}
}
zfdContentConsolidatorOutput.close();
if (zfdContentConsolidatorOutput.fail())
{
if (mgrPrefs.isDebugModeEnabled)
syslog(LOG_CONS,
"// REPORT: Failed to write content-consolidated data to the file. Insufficient Privileges?\n");
if (mgrPrefs.isDebugModeEnabled)
syslog(LOG_CONS, "// DATA FILE: %s", path);
return false;
}
zfdContentConsolidatorIncomingStream.close();
if (zfdContentConsolidatorIncomingStream.fail())
{
if (mgrPrefs.isDebugModeEnabled)
syslog(LOG_CONS, "// REPORT: Failed to read lines through the data file for content-consolidation. "
"Insufficient Privileges?\n");
if (mgrPrefs.isDebugModeEnabled)
syslog(LOG_CONS, "// DATA FILE: %s", path);
return false;
}
return true;
} // END: CONTENT CONSOLIDATOR.
} // namespace vChewing

View File

@ -25,13 +25,16 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import Carbon import Carbon
import Cocoa import Cocoa
// The namespace of this input method.
public enum vChewing {}
public class IME: NSObject { public class IME: NSObject {
static let arrSupportedLocales = ["en", "zh-Hant", "zh-Hans", "ja"] static let arrSupportedLocales = ["en", "zh-Hant", "zh-Hans", "ja"]
static let dlgOpenPath = NSOpenPanel() static let dlgOpenPath = NSOpenPanel()
// MARK: - // MARK: -
@objc static var areWeUsingOurOwnPhraseEditor: Bool = false static var areWeUsingOurOwnPhraseEditor: Bool = false
// MARK: - ctlInputMethod // MARK: - ctlInputMethod
@ -46,7 +49,7 @@ public class IME: NSObject {
// MARK: - Print debug information to the console. // MARK: - Print debug information to the console.
@objc static func prtDebugIntel(_ strPrint: String) { static func prtDebugIntel(_ strPrint: String) {
if mgrPrefs.isDebugModeEnabled { if mgrPrefs.isDebugModeEnabled {
NSLog("vChewingErrorCallback: %@", strPrint) NSLog("vChewingErrorCallback: %@", strPrint)
} }
@ -54,27 +57,29 @@ public class IME: NSObject {
// MARK: - Tell whether this IME is running with Root privileges. // MARK: - Tell whether this IME is running with Root privileges.
@objc static var isSudoMode: Bool { static var isSudoMode: Bool {
NSUserName() == "root" NSUserName() == "root"
} }
// MARK: - Initializing Language Models. // MARK: - Initializing Language Models.
@objc static func initLangModels(userOnly: Bool) { static func initLangModels(userOnly: Bool) {
if !userOnly { DispatchQueue.global(qos: .userInitiated).async {
mgrLangModel.loadDataModels() //
}
// mgrLangModel loadUserPhrases dataFolderPath // mgrLangModel loadUserPhrases dataFolderPath
// //
// //
mgrLangModel.loadUserPhrases()
mgrLangModel.loadUserPhraseReplacement()
mgrLangModel.loadUserAssociatedPhrases() mgrLangModel.loadUserAssociatedPhrases()
mgrLangModel.loadUserPhraseReplacement()
mgrLangModel.loadUserPhrases()
}
if !userOnly {
// mgrLangModel.loadDataModels()
}
} }
// MARK: - System Dark Mode Status Detector. // MARK: - System Dark Mode Status Detector.
@objc static func isDarkMode() -> Bool { static func isDarkMode() -> Bool {
if #available(macOS 10.15, *) { if #available(macOS 10.15, *) {
let appearanceDescription = NSApplication.shared.effectiveAppearance.debugDescription let appearanceDescription = NSApplication.shared.effectiveAppearance.debugDescription
.lowercased() .lowercased()

View File

@ -37,7 +37,6 @@ public class InputSourceHelper: NSObject {
TISCreateInputSourceList(nil, true).takeRetainedValue() as! [TISInputSource] TISCreateInputSourceList(nil, true).takeRetainedValue() as! [TISInputSource]
} }
@objc(inputSourceForProperty:stringValue:)
public static func inputSource(for propertyKey: CFString, stringValue: String) public static func inputSource(for propertyKey: CFString, stringValue: String)
-> TISInputSource? -> TISInputSource?
{ {
@ -57,12 +56,10 @@ public class InputSourceHelper: NSObject {
return nil return nil
} }
@objc(inputSourceForInputSourceID:)
public static func inputSource(for sourceID: String) -> TISInputSource? { public static func inputSource(for sourceID: String) -> TISInputSource? {
inputSource(for: kTISPropertyInputSourceID, stringValue: sourceID) inputSource(for: kTISPropertyInputSourceID, stringValue: sourceID)
} }
@objc(inputSourceEnabled:)
public static func inputSourceEnabled(for source: TISInputSource) -> Bool { public static func inputSourceEnabled(for source: TISInputSource) -> Bool {
if let valuePts = TISGetInputSourceProperty(source, kTISPropertyInputSourceIsEnabled) { if let valuePts = TISGetInputSourceProperty(source, kTISPropertyInputSourceIsEnabled) {
let value = Unmanaged<CFBoolean>.fromOpaque(valuePts).takeUnretainedValue() let value = Unmanaged<CFBoolean>.fromOpaque(valuePts).takeUnretainedValue()
@ -71,13 +68,11 @@ public class InputSourceHelper: NSObject {
return false return false
} }
@objc(enableInputSource:)
public static func enable(inputSource: TISInputSource) -> Bool { public static func enable(inputSource: TISInputSource) -> Bool {
let status = TISEnableInputSource(inputSource) let status = TISEnableInputSource(inputSource)
return status == noErr return status == noErr
} }
@objc(enableAllInputModesForInputSourceBundleID:)
public static func enableAllInputMode(for inputSourceBundleD: String) -> Bool { public static func enableAllInputMode(for inputSourceBundleD: String) -> Bool {
var enabled = false var enabled = false
for source in allInstalledInputSources() { for source in allInstalledInputSources() {
@ -99,7 +94,6 @@ public class InputSourceHelper: NSObject {
return enabled return enabled
} }
@objc(enableInputMode:forInputSourceBundleID:)
public static func enable(inputMode modeID: String, for bundleID: String) -> Bool { public static func enable(inputMode modeID: String, for bundleID: String) -> Bool {
for source in allInstalledInputSources() { for source in allInstalledInputSources() {
guard let bundleIDPtr = TISGetInputSourceProperty(source, kTISPropertyBundleID), guard let bundleIDPtr = TISGetInputSourceProperty(source, kTISPropertyBundleID),
@ -122,13 +116,11 @@ public class InputSourceHelper: NSObject {
return false return false
} }
@objc(disableInputSource:)
public static func disable(inputSource: TISInputSource) -> Bool { public static func disable(inputSource: TISInputSource) -> Bool {
let status = TISDisableInputSource(inputSource) let status = TISDisableInputSource(inputSource)
return status == noErr return status == noErr
} }
@objc(registerInputSource:)
public static func registerTnputSource(at url: URL) -> Bool { public static func registerTnputSource(at url: URL) -> Bool {
let status = TISRegisterInputSource(url as CFURL) let status = TISRegisterInputSource(url as CFURL)
return status == noErr return status == noErr

View File

@ -38,10 +38,6 @@ extension ctlCandidate {
@objc(ctlInputMethod) @objc(ctlInputMethod)
class ctlInputMethod: IMKInputController { class ctlInputMethod: IMKInputController {
@objc static let kIMEModeCHS = "org.atelierInmu.inputmethod.vChewing.IMECHS"
@objc static let kIMEModeCHT = "org.atelierInmu.inputmethod.vChewing.IMECHT"
@objc static let kIMEModeNULL = "org.atelierInmu.inputmethod.vChewing.IMENULL"
@objc static var areWeDeleting = false @objc static var areWeDeleting = false
private static let tooltipController = TooltipController() private static let tooltipController = TooltipController()
@ -97,7 +93,7 @@ class ctlInputMethod: IMKInputController {
currentClient = client currentClient = client
keyHandler.clear() keyHandler.clear()
keyHandler.ensurePhoneticParser() Composer.ensureParser()
if let bundleCheckID = (client as? IMKTextInput)?.bundleIdentifier() { if let bundleCheckID = (client as? IMKTextInput)?.bundleIdentifier() {
if bundleCheckID != Bundle.main.bundleIdentifier { if bundleCheckID != Bundle.main.bundleIdentifier {
// Override the keyboard layout to the basic one. // Override the keyboard layout to the basic one.
@ -116,7 +112,7 @@ class ctlInputMethod: IMKInputController {
} }
override func setValue(_ value: Any!, forTag _: Int, client: Any!) { override func setValue(_ value: Any!, forTag _: Int, client: Any!) {
var newInputMode = InputMode(rawValue: value as? String ?? InputMode.imeModeNULL.rawValue) var newInputMode = InputMode(rawValue: value as? String ?? "") ?? InputMode.imeModeNULL
switch newInputMode { switch newInputMode {
case InputMode.imeModeCHS: case InputMode.imeModeCHS:
newInputMode = InputMode.imeModeCHS newInputMode = InputMode.imeModeCHS

View File

@ -86,13 +86,15 @@ extension ctlInputMethod {
halfWidthPunctuationItem.keyEquivalentModifierMask = [.command, .control] halfWidthPunctuationItem.keyEquivalentModifierMask = [.command, .control]
halfWidthPunctuationItem.state = mgrPrefs.halfWidthPunctuationEnabled.state halfWidthPunctuationItem.state = mgrPrefs.halfWidthPunctuationEnabled.state
if optionKeyPressed { if optionKeyPressed || mgrPrefs.phraseReplacementEnabled {
let phaseReplacementItem = menu.addItem( let phaseReplacementItem = menu.addItem(
withTitle: NSLocalizedString("Use Phrase Replacement", comment: ""), withTitle: NSLocalizedString("Use Phrase Replacement", comment: ""),
action: #selector(togglePhraseReplacement(_:)), keyEquivalent: "" action: #selector(togglePhraseReplacement(_:)), keyEquivalent: ""
) )
phaseReplacementItem.state = mgrPrefs.phraseReplacementEnabled.state phaseReplacementItem.state = mgrPrefs.phraseReplacementEnabled.state
}
if optionKeyPressed {
let toggleSymbolInputItem = menu.addItem( let toggleSymbolInputItem = menu.addItem(
withTitle: NSLocalizedString("Symbol & Emoji Input", comment: ""), withTitle: NSLocalizedString("Symbol & Emoji Input", comment: ""),
action: #selector(toggleSymbolEnabled(_:)), keyEquivalent: "" action: #selector(toggleSymbolEnabled(_:)), keyEquivalent: ""
@ -345,8 +347,7 @@ extension ctlInputMethod {
} }
@objc func reloadUserPhrases(_: Any?) { @objc func reloadUserPhrases(_: Any?) {
mgrLangModel.loadUserPhrases() IME.initLangModels(userOnly: true)
mgrLangModel.loadUserPhraseReplacement()
} }
@objc func showAbout(_: Any?) { @objc func showAbout(_: Any?) {

View File

@ -80,7 +80,7 @@ private let kDefaultKeys = "123456789"
// MARK: - UserDefaults extension. // MARK: - UserDefaults extension.
@objc extension UserDefaults { extension UserDefaults {
func setDefault(_ value: Any?, forKey defaultName: String) { func setDefault(_ value: Any?, forKey defaultName: String) {
if object(forKey: defaultName) == nil { if object(forKey: defaultName) == nil {
set(value, forKey: defaultName) set(value, forKey: defaultName)
@ -236,7 +236,7 @@ public class mgrPrefs: NSObject {
// MARK: - Preferences Module plist // MARK: - Preferences Module plist
@objc public static func setMissingDefaults() { public static func setMissingDefaults() {
UserDefaults.standard.setDefault(mgrPrefs.isDebugModeEnabled, forKey: UserDef.kIsDebugModeEnabled) UserDefaults.standard.setDefault(mgrPrefs.isDebugModeEnabled, forKey: UserDef.kIsDebugModeEnabled)
UserDefaults.standard.setDefault(mgrPrefs.mostRecentInputMode, forKey: UserDef.kMostRecentInputMode) UserDefaults.standard.setDefault(mgrPrefs.mostRecentInputMode, forKey: UserDef.kMostRecentInputMode)
UserDefaults.standard.setDefault(mgrPrefs.checkUpdateAutomatically, forKey: UserDef.kCheckUpdateAutomatically) UserDefaults.standard.setDefault(mgrPrefs.checkUpdateAutomatically, forKey: UserDef.kCheckUpdateAutomatically)
@ -278,90 +278,90 @@ public class mgrPrefs: NSObject {
} }
@UserDefault(key: UserDef.kIsDebugModeEnabled, defaultValue: false) @UserDefault(key: UserDef.kIsDebugModeEnabled, defaultValue: false)
@objc static var isDebugModeEnabled: Bool static var isDebugModeEnabled: Bool
@UserDefault(key: UserDef.kMostRecentInputMode, defaultValue: "") @UserDefault(key: UserDef.kMostRecentInputMode, defaultValue: "")
@objc static var mostRecentInputMode: String static var mostRecentInputMode: String
@UserDefault(key: UserDef.kCheckUpdateAutomatically, defaultValue: false) @UserDefault(key: UserDef.kCheckUpdateAutomatically, defaultValue: false)
@objc static var checkUpdateAutomatically: Bool static var checkUpdateAutomatically: Bool
@UserDefault(key: UserDef.kUserDataFolderSpecified, defaultValue: "") @UserDefault(key: UserDef.kUserDataFolderSpecified, defaultValue: "")
@objc static var userDataFolderSpecified: String static var userDataFolderSpecified: String
@objc static func ifSpecifiedUserDataPathExistsInPlist() -> Bool { static func ifSpecifiedUserDataPathExistsInPlist() -> Bool {
UserDefaults.standard.object(forKey: UserDef.kUserDataFolderSpecified) != nil UserDefaults.standard.object(forKey: UserDef.kUserDataFolderSpecified) != nil
} }
@objc static func resetSpecifiedUserDataFolder() { static func resetSpecifiedUserDataFolder() {
UserDefaults.standard.removeObject(forKey: "UserDataFolderSpecified") UserDefaults.standard.removeObject(forKey: "UserDataFolderSpecified")
IME.initLangModels(userOnly: true) IME.initLangModels(userOnly: true)
} }
@UserDefault(key: UserDef.kAppleLanguages, defaultValue: []) @UserDefault(key: UserDef.kAppleLanguages, defaultValue: [])
@objc static var appleLanguages: [String] static var appleLanguages: [String]
@UserDefault(key: UserDef.kMandarinParser, defaultValue: 0) @UserDefault(key: UserDef.kMandarinParser, defaultValue: 0)
@objc static var mandarinParser: Int @objc static var mandarinParser: Int
@objc static var mandarinParserName: String { static var mandarinParserName: String {
(MandarinParser(rawValue: mandarinParser) ?? MandarinParser.ofStandard).name (MandarinParser(rawValue: mandarinParser) ?? MandarinParser.ofStandard).name
} }
@UserDefault( @UserDefault(
key: UserDef.kBasicKeyboardLayout, defaultValue: "com.apple.keylayout.ZhuyinBopomofo" key: UserDef.kBasicKeyboardLayout, defaultValue: "com.apple.keylayout.ZhuyinBopomofo"
) )
@objc static var basicKeyboardLayout: String static var basicKeyboardLayout: String
@UserDefault(key: UserDef.kShowPageButtonsInCandidateWindow, defaultValue: true) @UserDefault(key: UserDef.kShowPageButtonsInCandidateWindow, defaultValue: true)
@objc static var showPageButtonsInCandidateWindow: Bool static var showPageButtonsInCandidateWindow: Bool
@CandidateListTextSize(key: UserDef.kCandidateListTextSize) @CandidateListTextSize(key: UserDef.kCandidateListTextSize)
@objc static var candidateListTextSize: CGFloat static var candidateListTextSize: CGFloat
@UserDefault(key: UserDef.kShouldAutoReloadUserDataFiles, defaultValue: true) @UserDefault(key: UserDef.kShouldAutoReloadUserDataFiles, defaultValue: true)
@objc static var shouldAutoReloadUserDataFiles: Bool static var shouldAutoReloadUserDataFiles: Bool
@UserDefault(key: UserDef.kSetRearCursorMode, defaultValue: false) @UserDefault(key: UserDef.kSetRearCursorMode, defaultValue: false)
@objc static var setRearCursorMode: Bool static var setRearCursorMode: Bool
@UserDefault(key: UserDef.kMoveCursorAfterSelectingCandidate, defaultValue: true) @UserDefault(key: UserDef.kMoveCursorAfterSelectingCandidate, defaultValue: true)
@objc static var moveCursorAfterSelectingCandidate: Bool static var moveCursorAfterSelectingCandidate: Bool
@UserDefault(key: UserDef.kUseHorizontalCandidateList, defaultValue: true) @UserDefault(key: UserDef.kUseHorizontalCandidateList, defaultValue: true)
@objc static var useHorizontalCandidateList: Bool static var useHorizontalCandidateList: Bool
@ComposingBufferSize(key: UserDef.kComposingBufferSize) @ComposingBufferSize(key: UserDef.kComposingBufferSize)
@objc static var composingBufferSize: Int static var composingBufferSize: Int
@UserDefault(key: UserDef.kChooseCandidateUsingSpace, defaultValue: true) @UserDefault(key: UserDef.kChooseCandidateUsingSpace, defaultValue: true)
@objc static var chooseCandidateUsingSpace: Bool static var chooseCandidateUsingSpace: Bool
@UserDefault(key: UserDef.kUseSCPCTypingMode, defaultValue: false) @UserDefault(key: UserDef.kUseSCPCTypingMode, defaultValue: false)
@objc static var useSCPCTypingMode: Bool static var useSCPCTypingMode: Bool
@objc static func toggleSCPCTypingModeEnabled() -> Bool { static func toggleSCPCTypingModeEnabled() -> Bool {
useSCPCTypingMode = !useSCPCTypingMode useSCPCTypingMode = !useSCPCTypingMode
UserDefaults.standard.set(useSCPCTypingMode, forKey: UserDef.kUseSCPCTypingMode) UserDefaults.standard.set(useSCPCTypingMode, forKey: UserDef.kUseSCPCTypingMode)
return useSCPCTypingMode return useSCPCTypingMode
} }
@UserDefault(key: UserDef.kMaxCandidateLength, defaultValue: kDefaultComposingBufferSize * 2) @UserDefault(key: UserDef.kMaxCandidateLength, defaultValue: kDefaultComposingBufferSize * 2)
@objc static var maxCandidateLength: Int static var maxCandidateLength: Int
@UserDefault(key: UserDef.kShouldNotFartInLieuOfBeep, defaultValue: true) @UserDefault(key: UserDef.kShouldNotFartInLieuOfBeep, defaultValue: true)
@objc static var shouldNotFartInLieuOfBeep: Bool static var shouldNotFartInLieuOfBeep: Bool
@objc static func toggleShouldNotFartInLieuOfBeep() -> Bool { static func toggleShouldNotFartInLieuOfBeep() -> Bool {
shouldNotFartInLieuOfBeep = !shouldNotFartInLieuOfBeep shouldNotFartInLieuOfBeep = !shouldNotFartInLieuOfBeep
UserDefaults.standard.set(shouldNotFartInLieuOfBeep, forKey: UserDef.kShouldNotFartInLieuOfBeep) UserDefaults.standard.set(shouldNotFartInLieuOfBeep, forKey: UserDef.kShouldNotFartInLieuOfBeep)
return shouldNotFartInLieuOfBeep return shouldNotFartInLieuOfBeep
} }
@UserDefault(key: UserDef.kCNS11643Enabled, defaultValue: false) @UserDefault(key: UserDef.kCNS11643Enabled, defaultValue: false)
@objc static var cns11643Enabled: Bool static var cns11643Enabled: Bool
@objc static func toggleCNS11643Enabled() -> Bool { static func toggleCNS11643Enabled() -> Bool {
cns11643Enabled = !cns11643Enabled cns11643Enabled = !cns11643Enabled
mgrLangModel.setCNSEnabled(cns11643Enabled) // mgrLangModel.setCNSEnabled(cns11643Enabled) //
UserDefaults.standard.set(cns11643Enabled, forKey: UserDef.kCNS11643Enabled) UserDefaults.standard.set(cns11643Enabled, forKey: UserDef.kCNS11643Enabled)
@ -369,9 +369,9 @@ public class mgrPrefs: NSObject {
} }
@UserDefault(key: UserDef.kSymbolInputEnabled, defaultValue: true) @UserDefault(key: UserDef.kSymbolInputEnabled, defaultValue: true)
@objc static var symbolInputEnabled: Bool static var symbolInputEnabled: Bool
@objc static func toggleSymbolInputEnabled() -> Bool { static func toggleSymbolInputEnabled() -> Bool {
symbolInputEnabled = !symbolInputEnabled symbolInputEnabled = !symbolInputEnabled
mgrLangModel.setSymbolEnabled(symbolInputEnabled) // mgrLangModel.setSymbolEnabled(symbolInputEnabled) //
UserDefaults.standard.set(symbolInputEnabled, forKey: UserDef.kSymbolInputEnabled) UserDefaults.standard.set(symbolInputEnabled, forKey: UserDef.kSymbolInputEnabled)
@ -379,9 +379,9 @@ public class mgrPrefs: NSObject {
} }
@UserDefault(key: UserDef.kChineseConversionEnabled, defaultValue: false) @UserDefault(key: UserDef.kChineseConversionEnabled, defaultValue: false)
@objc static var chineseConversionEnabled: Bool static var chineseConversionEnabled: Bool
@objc @discardableResult static func toggleChineseConversionEnabled() -> Bool { @discardableResult static func toggleChineseConversionEnabled() -> Bool {
chineseConversionEnabled = !chineseConversionEnabled chineseConversionEnabled = !chineseConversionEnabled
// JIS // JIS
if chineseConversionEnabled, shiftJISShinjitaiOutputEnabled { if chineseConversionEnabled, shiftJISShinjitaiOutputEnabled {
@ -395,9 +395,9 @@ public class mgrPrefs: NSObject {
} }
@UserDefault(key: UserDef.kShiftJISShinjitaiOutputEnabled, defaultValue: false) @UserDefault(key: UserDef.kShiftJISShinjitaiOutputEnabled, defaultValue: false)
@objc static var shiftJISShinjitaiOutputEnabled: Bool static var shiftJISShinjitaiOutputEnabled: Bool
@objc @discardableResult static func toggleShiftJISShinjitaiOutputEnabled() -> Bool { @discardableResult static func toggleShiftJISShinjitaiOutputEnabled() -> Bool {
shiftJISShinjitaiOutputEnabled = !shiftJISShinjitaiOutputEnabled shiftJISShinjitaiOutputEnabled = !shiftJISShinjitaiOutputEnabled
// JIS // JIS
if shiftJISShinjitaiOutputEnabled, chineseConversionEnabled { if shiftJISShinjitaiOutputEnabled, chineseConversionEnabled {
@ -410,42 +410,42 @@ public class mgrPrefs: NSObject {
} }
@UserDefault(key: UserDef.kHalfWidthPunctuationEnabled, defaultValue: false) @UserDefault(key: UserDef.kHalfWidthPunctuationEnabled, defaultValue: false)
@objc static var halfWidthPunctuationEnabled: Bool static var halfWidthPunctuationEnabled: Bool
@objc static func toggleHalfWidthPunctuationEnabled() -> Bool { static func toggleHalfWidthPunctuationEnabled() -> Bool {
halfWidthPunctuationEnabled = !halfWidthPunctuationEnabled halfWidthPunctuationEnabled = !halfWidthPunctuationEnabled
return halfWidthPunctuationEnabled return halfWidthPunctuationEnabled
} }
@UserDefault(key: UserDef.kEscToCleanInputBuffer, defaultValue: true) @UserDefault(key: UserDef.kEscToCleanInputBuffer, defaultValue: true)
@objc static var escToCleanInputBuffer: Bool static var escToCleanInputBuffer: Bool
@UserDefault(key: UserDef.kSpecifyShiftTabKeyBehavior, defaultValue: false) @UserDefault(key: UserDef.kSpecifyShiftTabKeyBehavior, defaultValue: false)
@objc static var specifyShiftTabKeyBehavior: Bool static var specifyShiftTabKeyBehavior: Bool
@UserDefault(key: UserDef.kSpecifyShiftSpaceKeyBehavior, defaultValue: false) @UserDefault(key: UserDef.kSpecifyShiftSpaceKeyBehavior, defaultValue: false)
@objc static var specifyShiftSpaceKeyBehavior: Bool static var specifyShiftSpaceKeyBehavior: Bool
// MARK: - Optional settings // MARK: - Optional settings
@UserDefault(key: UserDef.kCandidateTextFontName, defaultValue: nil) @UserDefault(key: UserDef.kCandidateTextFontName, defaultValue: nil)
@objc static var candidateTextFontName: String? static var candidateTextFontName: String?
@UserDefault(key: UserDef.kCandidateKeyLabelFontName, defaultValue: nil) @UserDefault(key: UserDef.kCandidateKeyLabelFontName, defaultValue: nil)
@objc static var candidateKeyLabelFontName: String? static var candidateKeyLabelFontName: String?
@UserDefault(key: UserDef.kCandidateKeys, defaultValue: kDefaultKeys) @UserDefault(key: UserDef.kCandidateKeys, defaultValue: kDefaultKeys)
@objc static var candidateKeys: String static var candidateKeys: String
@objc static var defaultCandidateKeys: String { static var defaultCandidateKeys: String {
kDefaultKeys kDefaultKeys
} }
@objc static var suggestedCandidateKeys: [String] { static var suggestedCandidateKeys: [String] {
[kDefaultKeys, "234567890", "QWERTYUIO", "QWERTASDF", "ASDFGHJKL", "ASDFZXCVB"] [kDefaultKeys, "234567890", "QWERTYUIO", "QWERTASDF", "ASDFGHJKL", "ASDFZXCVB"]
} }
@objc static func validate(candidateKeys: String) throws { static func validate(candidateKeys: String) throws {
let trimmed = candidateKeys.trimmingCharacters(in: .whitespacesAndNewlines) let trimmed = candidateKeys.trimmingCharacters(in: .whitespacesAndNewlines)
if trimmed.isEmpty { if trimmed.isEmpty {
throw CandidateKeyError.empty throw CandidateKeyError.empty
@ -500,9 +500,9 @@ public class mgrPrefs: NSObject {
} }
@UserDefault(key: UserDef.kPhraseReplacementEnabled, defaultValue: false) @UserDefault(key: UserDef.kPhraseReplacementEnabled, defaultValue: false)
@objc static var phraseReplacementEnabled: Bool static var phraseReplacementEnabled: Bool
@objc static func togglePhraseReplacementEnabled() -> Bool { static func togglePhraseReplacementEnabled() -> Bool {
phraseReplacementEnabled = !phraseReplacementEnabled phraseReplacementEnabled = !phraseReplacementEnabled
mgrLangModel.setPhraseReplacementEnabled(phraseReplacementEnabled) mgrLangModel.setPhraseReplacementEnabled(phraseReplacementEnabled)
UserDefaults.standard.set(phraseReplacementEnabled, forKey: UserDef.kPhraseReplacementEnabled) UserDefaults.standard.set(phraseReplacementEnabled, forKey: UserDef.kPhraseReplacementEnabled)
@ -510,9 +510,9 @@ public class mgrPrefs: NSObject {
} }
@UserDefault(key: UserDef.kAssociatedPhrasesEnabled, defaultValue: false) @UserDefault(key: UserDef.kAssociatedPhrasesEnabled, defaultValue: false)
@objc static var associatedPhrasesEnabled: Bool static var associatedPhrasesEnabled: Bool
@objc static func toggleAssociatedPhrasesEnabled() -> Bool { static func toggleAssociatedPhrasesEnabled() -> Bool {
associatedPhrasesEnabled = !associatedPhrasesEnabled associatedPhrasesEnabled = !associatedPhrasesEnabled
UserDefaults.standard.set(associatedPhrasesEnabled, forKey: UserDef.kAssociatedPhrasesEnabled) UserDefaults.standard.set(associatedPhrasesEnabled, forKey: UserDef.kAssociatedPhrasesEnabled)
return associatedPhrasesEnabled return associatedPhrasesEnabled

View File

@ -0,0 +1,167 @@
// 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 Foundation
extension vChewing {
public enum LMConsolidator {
public static let kPragmaHeader = "# 𝙵𝙾𝚁𝙼𝙰𝚃 𝚘𝚛𝚐.𝚊𝚝𝚎𝚕𝚒𝚎𝚛𝙸𝚗𝚖𝚞.𝚟𝚌𝚑𝚎𝚠𝚒𝚗𝚐.𝚞𝚜𝚎𝚛𝙻𝚊𝚗𝚐𝚞𝚊𝚐𝚎𝙼𝚘𝚍𝚎𝚕𝙳𝚊𝚝𝚊.𝚏𝚘𝚛𝚖𝚊𝚝𝚝𝚎𝚍"
public static func checkPragma(path: String) -> Bool {
if FileManager.default.fileExists(atPath: path) {
let fileHandle = FileHandle(forReadingAtPath: path)!
do {
let lineReader = try LineReader(file: fileHandle)
for strLine in lineReader { // i=0
if strLine != kPragmaHeader {
IME.prtDebugIntel("Header Mismatch, Starting In-Place Consolidation.")
return false
} else {
IME.prtDebugIntel("Header Verification Succeeded: \(strLine).")
return true
}
}
} catch {
IME.prtDebugIntel("Header Verification Failed: File Access Error.")
return false
}
}
IME.prtDebugIntel("Header Verification Failed: File Missing.")
return false
}
@discardableResult public static func fixEOF(path: String) -> Bool {
let urlPath = URL(fileURLWithPath: path)
if FileManager.default.fileExists(atPath: path) {
var strIncoming = ""
do {
strIncoming += try String(contentsOf: urlPath, encoding: .utf8)
if !strIncoming.hasSuffix("\n") {
IME.prtDebugIntel("EOF Fix Necessity Confirmed, Start Fixing.")
if let writeFile = FileHandle(forUpdatingAtPath: path),
let endl = "\n".data(using: .utf8)
{
writeFile.seekToEndOfFile()
writeFile.write(endl)
writeFile.closeFile()
} else {
return false
}
}
} catch {
IME.prtDebugIntel("EOF Fix Failed w/ File: \(path)")
IME.prtDebugIntel("EOF Fix Failed w/ Error: \(error).")
return false
}
IME.prtDebugIntel("EOF Successfully Ensured (with possible autofixes performed).")
return true
}
IME.prtDebugIntel("EOF Fix Failed: File Missing at \(path).")
return false
}
@discardableResult public static func consolidate(path: String, pragma shouldCheckPragma: Bool) -> Bool {
var pragmaResult = false
if shouldCheckPragma {
pragmaResult = checkPragma(path: path)
if pragmaResult {
return true
}
}
let urlPath = URL(fileURLWithPath: path)
if FileManager.default.fileExists(atPath: path) {
var strProcessed = ""
do {
strProcessed += try String(contentsOf: urlPath, encoding: .utf8)
// Step 1: Consolidating formats per line.
// -------
// CJKWhiteSpace (\x{3000}) to ASCII Space
// NonBreakWhiteSpace (\x{A0}) to ASCII Space
// Tab to ASCII Space
// ASCII
strProcessed.regReplace(pattern: #"( +| +| +|\t+)+"#, replaceWith: " ")
//
strProcessed.regReplace(pattern: #"(^ | $)"#, replaceWith: "")
// CR & FF to LF,
strProcessed.regReplace(pattern: #"(\f+|\r+|\n+)+"#, replaceWith: "\n")
if strProcessed.prefix(1) == " " { //
strProcessed.removeFirst()
}
if strProcessed.suffix(1) == " " { //
strProcessed.removeLast()
}
// Step 3: Add Formatted Pragma, the Sorted Header:
if !pragmaResult {
strProcessed = kPragmaHeader + "\n" + strProcessed // Add Sorted Header
}
// Step 4: Deduplication.
let arrData = strProcessed.components(separatedBy: "\n")
strProcessed = "" // Reset its value
// reversed override
let arrDataDeduplicated = Array(NSOrderedSet(array: arrData.reversed()).array as! [String])
for lineData in arrDataDeduplicated.reversed() {
strProcessed += lineData
strProcessed += "\n"
}
// Step 5: Remove duplicated newlines at the end of the file.
strProcessed.regReplace(pattern: "\\n+", replaceWith: "\n")
// Step 6: Write consolidated file contents.
try strProcessed.write(to: urlPath, atomically: false, encoding: .utf8)
} catch {
IME.prtDebugIntel("Consolidation Failed w/ File: \(path)")
IME.prtDebugIntel("Consolidation Failed w/ Error: \(error).")
return false
}
IME.prtDebugIntel("Either Consolidation Successful Or No-Need-To-Consolidate.")
return true
}
IME.prtDebugIntel("Consolidation Failed: File Missing at \(path).")
return false
}
}
}
// MARK: - String Extension
extension String {
fileprivate mutating func regReplace(pattern: String, replaceWith: String = "") {
// Ref: https://stackoverflow.com/a/40993403/4162914 && https://stackoverflow.com/a/71291137/4162914
do {
let regex = try NSRegularExpression(
pattern: pattern, options: [.caseInsensitive, .anchorsMatchLines]
)
let range = NSRange(startIndex..., in: self)
self = regex.stringByReplacingMatches(
in: self, options: [], range: range, withTemplate: replaceWith
)
} catch { return }
}
}

View File

@ -1,167 +0,0 @@
// Copyright (c) 2011 and onwards The OpenVanilla Project (MIT License).
// All possible vChewing-specific modifications are of:
// (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.
*/
#ifndef LMInstantiator_H
#define LMInstantiator_H
#include "AssociatedPhrases.h"
#include "CNSLM.h"
#include "CoreLM.h"
#include "ParselessLM.h"
#include "PhraseReplacementMap.h"
#include "SymbolLM.h"
#include "UserPhrasesLM.h"
#include "UserSymbolLM.h"
#include <stdio.h>
#include <unordered_set>
namespace vChewing
{
using namespace Gramambular;
/// LMInstantiator is a facade for managing a set of models including
/// the input method language model, user phrases and excluded phrases.
///
/// It is the primary model class that the input controller and grammar builder
/// of vChewing talks to. When the grammar builder starts to build a sentence
/// from a series of BPMF readings, it passes the readings to the model to see
/// if there are valid unigrams, and use returned unigrams to produce the final
/// results.
///
/// LMInstantiator combine and transform the unigrams from the primary language
/// model and user phrases. The process is
///
/// 1) Get the original unigrams.
/// 2) Drop the unigrams whose value is contained in the exclusion map.
/// 3) Replace the values of the unigrams using the phrase replacement map.
/// 4) Replace the values of the unigrams using an external converter lambda.
/// 5) Drop the duplicated phrases.
///
/// The controller can ask the model to load the primary input method language
/// model while launching and to load the user phrases anytime if the custom
/// files are modified. It does not keep the reference of the data pathes but
/// you have to pass the paths when you ask it to do loading.
class LMInstantiator : public Gramambular::LanguageModel
{
public:
LMInstantiator();
~LMInstantiator();
/// Asks to load the primary language model at the given path.
/// @param languageModelPath The path of the language model.
void loadLanguageModel(const char *languageModelPath);
/// If the data model is already loaded.
bool isDataModelLoaded();
/// Asks to load the primary language model at the given path.
/// @param miscDataPath The path of the misc data model.
void loadMiscData(const char *miscDataPath);
/// If the data model is already loaded.
bool isMiscDataLoaded();
/// Asks to load the primary language model at the given path.
/// @param symbolDataPath The path of the symbol data model.
void loadSymbolData(const char *symbolDataPath);
/// If the data model is already loaded.
bool isSymbolDataLoaded();
/// Asks to load the primary language model at the given path.
/// @param cnsDataPath The path of the CNS data model.
void loadCNSData(const char *cnsDataPath);
/// If the data model is already loaded.
bool isCNSDataLoaded();
/// Asks to load the user phrases and excluded phrases at the given path.
/// @param userPhrasesPath The path of user phrases.
/// @param excludedPhrasesPath The path of excluded phrases.
void loadUserPhrases(const char *userPhrasesPath, const char *excludedPhrasesPath);
/// Asks to load the user symbol data at the given path.
/// @param userSymbolDataPath The path of user symbol data.
void loadUserSymbolData(const char *userPhrasesPath);
/// Asks to load the user associated phrases at the given path.
/// @param userAssociatedPhrasesPath The path of the user associated phrases.
void loadUserAssociatedPhrases(const char *userAssociatedPhrasesPath);
/// Asks to load the phrase replacement table at the given path.
/// @param phraseReplacementPath The path of the phrase replacement table.
void loadPhraseReplacementMap(const char *phraseReplacementPath);
/// Not implemented since we do not have data to provide bigram function.
const std::vector<Gramambular::Bigram> bigramsForKeys(const std::string &preceedingKey, const std::string &key);
/// Returns a list of available unigram for the given key.
/// @param key A std::string represents the BPMF reading or a symbol key. For
/// example, it you pass "ㄇㄚ", it returns "嗎", "媽", and so on.
const std::vector<Gramambular::Unigram> unigramsForKey(const std::string &key);
/// If the model has unigrams for the given key.
/// @param key The key.
bool hasUnigramsForKey(const std::string &key);
/// Enables or disables phrase replacement.
void setPhraseReplacementEnabled(bool enabled);
/// If phrase replacement is enabled or not.
bool phraseReplacementEnabled();
/// Enables or disables symbol input.
void setSymbolEnabled(bool enabled);
/// If symbol input is enabled or not.
bool symbolEnabled();
/// Enables or disables CNS11643 input.
void setCNSEnabled(bool enabled);
/// If CNS11643 input is enabled or not.
bool cnsEnabled();
const std::vector<std::string> associatedPhrasesForKey(const std::string &key);
bool hasAssociatedPhrasesForKey(const std::string &key);
protected:
/// Filters and converts the input unigrams and return a new list of unigrams.
///
/// @param unigrams The unigrams to be processed.
/// @param excludedValues The values to excluded unigrams.
/// @param insertedValues The values for unigrams already in the results.
/// It helps to prevent duplicated unigrams. Please note that the method
/// has a side effect that it inserts values to `insertedValues`.
const std::vector<Gramambular::Unigram> filterAndTransformUnigrams(
const std::vector<Gramambular::Unigram> unigrams, const std::unordered_set<std::string> &excludedValues,
std::unordered_set<std::string> &insertedValues);
ParselessLM m_languageModel;
CoreLM m_miscModel;
SymbolLM m_symbolModel;
CNSLM m_cnsModel;
UserPhrasesLM m_userPhrases;
UserPhrasesLM m_excludedPhrases;
UserSymbolLM m_userSymbolModel;
PhraseReplacementMap m_phraseReplacement;
AssociatedPhrases m_associatedPhrases;
bool m_phraseReplacementEnabled;
bool m_cnsEnabled;
bool m_symbolEnabled;
};
}; // namespace vChewing
#endif

View File

@ -1,323 +0,0 @@
// Copyright (c) 2011 and onwards The OpenVanilla Project (MIT License).
// All possible vChewing-specific modifications are of:
// (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.
*/
#include "LMInstantiator.h"
#include <algorithm>
#include <iterator>
namespace vChewing
{
LMInstantiator::LMInstantiator()
{
}
LMInstantiator::~LMInstantiator()
{
m_languageModel.close();
m_miscModel.close();
m_userPhrases.close();
m_userSymbolModel.close();
m_cnsModel.close();
m_excludedPhrases.close();
m_phraseReplacement.close();
m_associatedPhrases.close();
}
void LMInstantiator::loadLanguageModel(const char *languageModelDataPath)
{
if (languageModelDataPath)
{
m_languageModel.close();
m_languageModel.open(languageModelDataPath);
}
}
bool LMInstantiator::isDataModelLoaded()
{
return m_languageModel.isLoaded();
}
void LMInstantiator::loadCNSData(const char *cnsDataPath)
{
if (cnsDataPath)
{
m_cnsModel.close();
m_cnsModel.open(cnsDataPath);
}
}
bool LMInstantiator::isCNSDataLoaded()
{
return m_cnsModel.isLoaded();
}
void LMInstantiator::loadMiscData(const char *miscDataPath)
{
if (miscDataPath)
{
m_miscModel.close();
m_miscModel.open(miscDataPath);
}
}
bool LMInstantiator::isMiscDataLoaded()
{
return m_miscModel.isLoaded();
}
void LMInstantiator::loadSymbolData(const char *symbolDataPath)
{
if (symbolDataPath)
{
m_symbolModel.close();
m_symbolModel.open(symbolDataPath);
}
}
bool LMInstantiator::isSymbolDataLoaded()
{
return m_symbolModel.isLoaded();
}
void LMInstantiator::loadUserPhrases(const char *userPhrasesDataPath, const char *excludedPhrasesDataPath)
{
if (userPhrasesDataPath)
{
m_userPhrases.close();
m_userPhrases.open(userPhrasesDataPath);
}
if (excludedPhrasesDataPath)
{
m_excludedPhrases.close();
m_excludedPhrases.open(excludedPhrasesDataPath);
}
}
void LMInstantiator::loadUserSymbolData(const char *userSymbolDataPath)
{
if (userSymbolDataPath)
{
m_userSymbolModel.close();
m_userSymbolModel.open(userSymbolDataPath);
}
}
void LMInstantiator::loadUserAssociatedPhrases(const char *userAssociatedPhrasesPath)
{
if (userAssociatedPhrasesPath)
{
m_associatedPhrases.close();
m_associatedPhrases.open(userAssociatedPhrasesPath);
}
}
void LMInstantiator::loadPhraseReplacementMap(const char *phraseReplacementPath)
{
if (phraseReplacementPath)
{
m_phraseReplacement.close();
m_phraseReplacement.open(phraseReplacementPath);
}
}
const std::vector<Gramambular::Bigram> LMInstantiator::bigramsForKeys(const std::string &preceedingKey,
const std::string &key)
{
return std::vector<Gramambular::Bigram>();
}
const std::vector<Gramambular::Unigram> LMInstantiator::unigramsForKey(const std::string &key)
{
if (key == " ")
{
std::vector<Gramambular::Unigram> spaceUnigrams;
Gramambular::Unigram g;
g.keyValue.key = " ";
g.keyValue.value = " ";
g.score = 0;
spaceUnigrams.push_back(g);
return spaceUnigrams;
}
std::vector<Gramambular::Unigram> allUnigrams;
std::vector<Gramambular::Unigram> miscUnigrams;
std::vector<Gramambular::Unigram> symbolUnigrams;
std::vector<Gramambular::Unigram> userUnigrams;
std::vector<Gramambular::Unigram> userSymbolUnigrams;
std::vector<Gramambular::Unigram> cnsUnigrams;
std::unordered_set<std::string> excludedValues;
std::unordered_set<std::string> insertedValues;
if (m_excludedPhrases.hasUnigramsForKey(key))
{
std::vector<Gramambular::Unigram> excludedUnigrams = m_excludedPhrases.unigramsForKey(key);
transform(excludedUnigrams.begin(), excludedUnigrams.end(), inserter(excludedValues, excludedValues.end()),
[](const Gramambular::Unigram &u) { return u.keyValue.value; });
}
if (m_userPhrases.hasUnigramsForKey(key))
{
std::vector<Gramambular::Unigram> rawUserUnigrams = m_userPhrases.unigramsForKey(key);
// 用這句指令讓使用者語彙檔案內的詞條優先順序隨著行數增加而逐漸增高。
// 這樣一來就可以在就地新增語彙時徹底複寫優先權。
std::reverse(rawUserUnigrams.begin(), rawUserUnigrams.end());
userUnigrams = filterAndTransformUnigrams(rawUserUnigrams, excludedValues, insertedValues);
}
if (m_languageModel.hasUnigramsForKey(key))
{
std::vector<Gramambular::Unigram> rawGlobalUnigrams = m_languageModel.unigramsForKey(key);
allUnigrams = filterAndTransformUnigrams(rawGlobalUnigrams, excludedValues, insertedValues);
}
if (m_miscModel.hasUnigramsForKey(key))
{
std::vector<Gramambular::Unigram> rawMiscUnigrams = m_miscModel.unigramsForKey(key);
miscUnigrams = filterAndTransformUnigrams(rawMiscUnigrams, excludedValues, insertedValues);
}
if (m_symbolModel.hasUnigramsForKey(key) && m_symbolEnabled)
{
std::vector<Gramambular::Unigram> rawSymbolUnigrams = m_symbolModel.unigramsForKey(key);
symbolUnigrams = filterAndTransformUnigrams(rawSymbolUnigrams, excludedValues, insertedValues);
}
if (m_userSymbolModel.hasUnigramsForKey(key) && m_symbolEnabled)
{
std::vector<Gramambular::Unigram> rawUserSymbolUnigrams = m_userSymbolModel.unigramsForKey(key);
userSymbolUnigrams = filterAndTransformUnigrams(rawUserSymbolUnigrams, excludedValues, insertedValues);
}
if (m_cnsModel.hasUnigramsForKey(key) && m_cnsEnabled)
{
std::vector<Gramambular::Unigram> rawCNSUnigrams = m_cnsModel.unigramsForKey(key);
cnsUnigrams = filterAndTransformUnigrams(rawCNSUnigrams, excludedValues, insertedValues);
}
allUnigrams.insert(allUnigrams.begin(), userUnigrams.begin(), userUnigrams.end());
allUnigrams.insert(allUnigrams.end(), cnsUnigrams.begin(), cnsUnigrams.end());
allUnigrams.insert(allUnigrams.begin(), miscUnigrams.begin(), miscUnigrams.end());
allUnigrams.insert(allUnigrams.end(), userSymbolUnigrams.begin(), userSymbolUnigrams.end());
allUnigrams.insert(allUnigrams.end(), symbolUnigrams.begin(), symbolUnigrams.end());
return allUnigrams;
}
bool LMInstantiator::hasUnigramsForKey(const std::string &key)
{
if (key == " ")
{
return true;
}
if (!m_excludedPhrases.hasUnigramsForKey(key))
{
return m_userPhrases.hasUnigramsForKey(key) || m_languageModel.hasUnigramsForKey(key);
}
return unigramsForKey(key).size() > 0;
}
void LMInstantiator::setPhraseReplacementEnabled(bool enabled)
{
m_phraseReplacementEnabled = enabled;
}
bool LMInstantiator::phraseReplacementEnabled()
{
return m_phraseReplacementEnabled;
}
void LMInstantiator::setCNSEnabled(bool enabled)
{
m_cnsEnabled = enabled;
}
bool LMInstantiator::cnsEnabled()
{
return m_cnsEnabled;
}
void LMInstantiator::setSymbolEnabled(bool enabled)
{
m_symbolEnabled = enabled;
}
bool LMInstantiator::symbolEnabled()
{
return m_symbolEnabled;
}
const std::vector<Gramambular::Unigram> LMInstantiator::filterAndTransformUnigrams(
const std::vector<Gramambular::Unigram> unigrams, const std::unordered_set<std::string> &excludedValues,
std::unordered_set<std::string> &insertedValues)
{
std::vector<Gramambular::Unigram> results;
for (auto &&unigram : unigrams)
{
// excludedValues filters out the unigrams with the original value.
// insertedValues filters out the ones with the converted value
std::string originalValue = unigram.keyValue.value;
if (excludedValues.find(originalValue) != excludedValues.end())
{
continue;
}
std::string value = originalValue;
if (m_phraseReplacementEnabled)
{
std::string replacement = m_phraseReplacement.valueForKey(value);
if (replacement != "")
{
value = replacement;
}
}
if (insertedValues.find(value) == insertedValues.end())
{
Gramambular::Unigram g;
g.keyValue.value = value;
g.keyValue.key = unigram.keyValue.key;
g.score = unigram.score;
results.push_back(g);
insertedValues.insert(value);
}
}
return results;
}
const std::vector<std::string> LMInstantiator::associatedPhrasesForKey(const std::string &key)
{
return m_associatedPhrases.valuesForKey(key);
}
bool LMInstantiator::hasAssociatedPhrasesForKey(const std::string &key)
{
return m_associatedPhrases.hasValuesForKey(key);
}
} // namespace vChewing

View File

@ -0,0 +1,301 @@
// Copyright (c) 2021 and onwards The vChewing Project (MIT-NTL License).
// Refactored from the ObjCpp-version of this class by:
// (c) 2011 and onwards The OpenVanilla Project (MIT License).
/*
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
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.
*/
// NOTE: We still keep some of the comments left by Zonble,
// regardless that he is not in charge of this Swift module
import Foundation
//
// LMInstantiator 100MB
private var lmCNS = vChewing.LMLite(consolidate: false)
private var lmSymbols = vChewing.LMCore(reverse: true, consolidate: false, defaultScore: -13.0, forceDefaultScore: true)
extension vChewing {
/// LMInstantiator is a facade for managing a set of models including
/// the input method language model, user phrases and excluded phrases.
///
/// It is the primary model class that the input controller and grammar builder
/// of vChewing talks to. When the grammar builder starts to build a sentence
/// from a series of BPMF readings, it passes the readings to the model to see
/// if there are valid unigrams, and use returned unigrams to produce the final
/// results.
///
/// LMInstantiator combine and transform the unigrams from the primary language
/// model and user phrases. The process is
///
/// 1) Get the original unigrams.
/// 2) Drop the unigrams whose value is contained in the exclusion map.
/// 3) Replace the values of the unigrams using the phrase replacement map.
/// 4) Drop the duplicated phrases from the generated unigram array.
///
/// The controller can ask the model to load the primary input method language
/// model while launching and to load the user phrases anytime if the custom
/// files are modified. It does not keep the reference of the data pathes but
/// you have to pass the paths when you ask it to load.
public class LMInstantiator: Megrez.LanguageModel {
//
public var isPhraseReplacementEnabled = false
public var isCNSEnabled = false
public var isSymbolEnabled = false
///
/// LMCore key [Unigram]
///
///
/// 使 LMLite
/// LMLite
/// LMLite LMCore
/// LMReplacements LMAssociates 使
//
/// Reverse
/// Reverse
var lmCore = LMCore(reverse: false, consolidate: false, defaultScore: -9.5, forceDefaultScore: false)
var lmMisc = LMCore(reverse: true, consolidate: false, defaultScore: -1, forceDefaultScore: false)
// 使
// 使使
var lmUserPhrases = LMLite(consolidate: true)
var lmFiltered = LMLite(consolidate: true)
var lmUserSymbols = LMLite(consolidate: true)
var lmReplacements = LMReplacments()
var lmAssociates = LMAssociates()
//
override init() {}
// 調
public func isDataModelLoaded() -> Bool { lmCore.isLoaded() }
public func loadLanguageModel(path: String) {
if FileManager.default.isReadableFile(atPath: path) {
lmCore.open(path)
IME.prtDebugIntel("lmCore: \(lmCore.count) entries of data loaded from: \(path)")
} else {
IME.prtDebugIntel("lmCore: File access failure: \(path)")
}
}
public func isCNSDataLoaded() -> Bool { lmCNS.isLoaded() }
public func loadCNSData(path: String) {
if FileManager.default.isReadableFile(atPath: path) {
lmCNS.open(path)
IME.prtDebugIntel("lmCNS: \(lmCNS.count) entries of data loaded from: \(path)")
} else {
IME.prtDebugIntel("lmCNS: File access failure: \(path)")
}
}
public func isMiscDataLoaded() -> Bool { lmMisc.isLoaded() }
public func loadMiscData(path: String) {
if FileManager.default.isReadableFile(atPath: path) {
lmMisc.open(path)
IME.prtDebugIntel("lmMisc: \(lmMisc.count) entries of data loaded from: \(path)")
} else {
IME.prtDebugIntel("lmMisc: File access failure: \(path)")
}
}
public func isSymbolDataLoaded() -> Bool { lmSymbols.isLoaded() }
public func loadSymbolData(path: String) {
if FileManager.default.isReadableFile(atPath: path) {
lmSymbols.open(path)
IME.prtDebugIntel("lmSymbol: \(lmSymbols.count) entries of data loaded from: \(path)")
} else {
IME.prtDebugIntel("lmSymbols: File access failure: \(path)")
}
}
public func loadUserPhrases(path: String, filterPath: String) {
if FileManager.default.isReadableFile(atPath: path) {
lmUserPhrases.close()
lmUserPhrases.open(path)
IME.prtDebugIntel("lmUserPhrases: \(lmUserPhrases.count) entries of data loaded from: \(path)")
} else {
IME.prtDebugIntel("lmUserPhrases: File access failure: \(path)")
}
if FileManager.default.isReadableFile(atPath: filterPath) {
lmFiltered.close()
lmFiltered.open(filterPath)
IME.prtDebugIntel("lmFiltered: \(lmFiltered.count) entries of data loaded from: \(path)")
} else {
IME.prtDebugIntel("lmFiltered: File access failure: \(path)")
}
}
public func loadUserSymbolData(path: String) {
if FileManager.default.isReadableFile(atPath: path) {
lmUserSymbols.close()
lmUserSymbols.open(path)
IME.prtDebugIntel("lmUserSymbol: \(lmUserSymbols.count) entries of data loaded from: \(path)")
} else {
IME.prtDebugIntel("lmUserSymbol: File access failure: \(path)")
}
}
public func loadUserAssociatedPhrases(path: String) {
if FileManager.default.isReadableFile(atPath: path) {
lmAssociates.close()
lmAssociates.open(path)
IME.prtDebugIntel("lmAssociates: \(lmAssociates.count) entries of data loaded from: \(path)")
} else {
IME.prtDebugIntel("lmAssociates: File access failure: \(path)")
}
}
public func loadPhraseReplacementMap(path: String) {
if FileManager.default.isReadableFile(atPath: path) {
lmReplacements.close()
lmReplacements.open(path)
IME.prtDebugIntel("lmReplacements: \(lmReplacements.count) entries of data loaded from: \(path)")
} else {
IME.prtDebugIntel("lmReplacements: File access failure: \(path)")
}
}
// MARK: - Core Functions (Public)
/// Not implemented since we do not have data to provide bigram function.
// public func bigramsForKeys(preceedingKey: String, key: String) -> [Megrez.Bigram] { }
/// Returns a list of available unigram for the given key.
/// @param key:String represents the BPMF reading or a symbol key.
/// For instance, it you pass "ˇ", it returns "" and other possible candidates.
override open func unigramsFor(key: String) -> [Megrez.Unigram] {
if key == " " {
///
let spaceUnigram = Megrez.Unigram(
keyValue: Megrez.KeyValuePair(key: " ", value: " "),
score: 0
)
return [spaceUnigram]
}
///
var rawAllUnigrams: [Megrez.Unigram] = []
// reversed 使
//
// rawUserUnigrams
rawAllUnigrams += lmUserPhrases.unigramsFor(key: key, score: 0.0).reversed()
if lmUserPhrases.unigramsFor(key: key).isEmpty {
IME.prtDebugIntel("Not found in UserPhrasesUnigram(\(lmUserPhrases.count)): \(key)")
}
// LMMisc LMCore score (-10.0, 0.0)
rawAllUnigrams += lmMisc.unigramsFor(key: key)
rawAllUnigrams += lmCore.unigramsFor(key: key)
if isCNSEnabled {
rawAllUnigrams += lmCNS.unigramsFor(key: key, score: -11)
}
if isSymbolEnabled {
rawAllUnigrams += lmUserSymbols.unigramsFor(key: key, score: -12.0)
if lmUserSymbols.unigramsFor(key: key).isEmpty {
IME.prtDebugIntel("Not found in UserSymbolUnigram(\(lmUserSymbols.count)): \(key)")
}
rawAllUnigrams += lmSymbols.unigramsFor(key: key)
}
//
var insertedPairs: Set<Megrez.KeyValuePair> = [] //
var filteredPairs: Set<Megrez.KeyValuePair> = [] //
// KeyValuePair
for unigram in lmFiltered.unigramsFor(key: key) {
filteredPairs.insert(unigram.keyValue)
}
var debugOutput = "\n"
for neta in rawAllUnigrams {
debugOutput += "RAW: \(neta.keyValue.key) \(neta.keyValue.value) \(neta.score)\n"
}
if debugOutput == "\n" {
debugOutput = "RAW: No match found in all unigrams."
}
IME.prtDebugIntel(debugOutput)
return filterAndTransform(
unigrams: rawAllUnigrams,
filter: filteredPairs, inserted: &insertedPairs
)
}
/// If the model has unigrams for the given key.
/// @param key The key.
override open func hasUnigramsFor(key: String) -> Bool {
if key == " " { return true }
if !lmFiltered.hasUnigramsFor(key: key) {
return lmUserPhrases.hasUnigramsFor(key: key) || lmCore.hasUnigramsFor(key: key)
}
return !unigramsFor(key: key).isEmpty
}
public func associatedPhrasesForKey(_ key: String) -> [String] {
lmAssociates.valuesFor(key: key) ?? []
}
public func hasAssociatedPhrasesForKey(_ key: String) -> Bool {
lmAssociates.hasValuesFor(key: key)
}
// MARK: - Core Functions (Private)
func filterAndTransform(
unigrams: [Megrez.Unigram],
filter filteredPairs: Set<Megrez.KeyValuePair>,
inserted insertedPairs: inout Set<Megrez.KeyValuePair>
) -> [Megrez.Unigram] {
var results: [Megrez.Unigram] = []
for unigram in unigrams {
var pair: Megrez.KeyValuePair = unigram.keyValue
if filteredPairs.contains(pair) {
continue
}
if isPhraseReplacementEnabled {
let replacement = lmReplacements.valuesFor(key: pair.value)
if !replacement.isEmpty {
IME.prtDebugIntel("\(pair.value) -> \(replacement)")
pair.value = replacement
}
}
if !insertedPairs.contains(pair) {
results.append(Megrez.Unigram(keyValue: pair, score: unigram.score))
insertedPairs.insert(pair)
}
}
return results
}
}
}

View File

@ -34,7 +34,7 @@ namespace vChewing
{ {
// About 20 generations. // About 20 generations.
static const double DecayThreshould = 1.0 / 1048576.0; static const double DecayThreshold = 1.0 / 1048576.0;
static double Score(size_t eventCount, size_t totalCount, double eventTimestamp, double timestamp, double lambda); static double Score(size_t eventCount, size_t totalCount, double eventTimestamp, double timestamp, double lambda);
static bool IsEndingPunctuation(const std::string &value); static bool IsEndingPunctuation(const std::string &value);
@ -126,7 +126,7 @@ void UserOverrideModel::Observation::update(const std::string &candidate, double
static double Score(size_t eventCount, size_t totalCount, double eventTimestamp, double timestamp, double lambda) static double Score(size_t eventCount, size_t totalCount, double eventTimestamp, double timestamp, double lambda)
{ {
double decay = exp((timestamp - eventTimestamp) * lambda); double decay = exp((timestamp - eventTimestamp) * lambda);
if (decay < DecayThreshould) if (decay < DecayThreshold)
{ {
return 0.0; return 0.0;
} }

View File

@ -0,0 +1,120 @@
// Copyright (c) 2021 and onwards The vChewing Project (MIT-NTL License).
// Refactored from the ObjCpp-version of this class by:
// (c) 2011 and onwards The OpenVanilla Project (MIT License).
/*
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
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 Foundation
extension vChewing {
@frozen public struct LMAssociates {
var keyValueMap: [String: [Megrez.KeyValuePair]] = [:]
public var count: Int {
keyValueMap.count
}
public init() {
keyValueMap = [:]
}
public func isLoaded() -> Bool {
!keyValueMap.isEmpty
}
@discardableResult public mutating func open(_ path: String) -> Bool {
if isLoaded() {
return false
}
LMConsolidator.fixEOF(path: path)
LMConsolidator.consolidate(path: path, pragma: true)
var arrData: [String] = []
do {
arrData = try String(contentsOfFile: path, encoding: .utf8).components(separatedBy: "\n")
} catch {
IME.prtDebugIntel("\(error)")
IME.prtDebugIntel("↑ Exception happened when reading Associated Phrases data.")
return false
}
for (lineID, lineContent) in arrData.enumerated() {
if !lineContent.hasPrefix("#") {
let lineContent = lineContent.replacingOccurrences(of: "\t", with: " ")
if lineContent.components(separatedBy: " ").count < 2 {
if lineContent != "", lineContent != " " {
IME.prtDebugIntel("Line #\(lineID + 1) Wrecked: \(lineContent)")
}
continue
}
var currentKV = Megrez.KeyValuePair()
for (unitID, unitContent) in lineContent.components(separatedBy: " ").enumerated() {
switch unitID {
case 0:
currentKV.key = unitContent
case 1:
currentKV.value = unitContent
default: break
}
}
keyValueMap[currentKV.key, default: []].append(currentKV)
}
}
return true
}
public mutating func close() {
if isLoaded() {
keyValueMap.removeAll()
}
}
public func dump() {
var strDump = ""
for entry in keyValueMap {
let rows: [Megrez.KeyValuePair] = entry.value
for row in rows {
let addline = row.key + " " + row.value + "\n"
strDump += addline
}
}
IME.prtDebugIntel(strDump)
}
public func valuesFor(key: String) -> [String]? {
var v: [String] = []
if let matched = keyValueMap[key] {
for entry in matched as [Megrez.KeyValuePair] {
v.append(entry.value)
}
}
return v
}
public func hasValuesFor(key: String) -> Bool {
keyValueMap[key] != nil
}
}
}

View File

@ -0,0 +1,155 @@
// 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.
*/
// 使 Swift String
import Foundation
extension vChewing {
@frozen public struct LMCore {
var keyValueScoreMap: [String: [Megrez.Unigram]] = [:]
var shouldReverse: Bool = false
var allowConsolidation: Bool = false
var defaultScore: Double = 0
var shouldForceDefaultScore: Bool = false
public var count: Int {
keyValueScoreMap.count
}
public init(
reverse: Bool = false, consolidate: Bool = false, defaultScore scoreDefault: Double = 0,
forceDefaultScore: Bool = false
) {
keyValueScoreMap = [:]
allowConsolidation = consolidate
shouldReverse = reverse
defaultScore = scoreDefault
shouldForceDefaultScore = forceDefaultScore
}
public func isLoaded() -> Bool {
!keyValueScoreMap.isEmpty
}
@discardableResult public mutating func open(_ path: String) -> Bool {
if isLoaded() {
return false
}
if allowConsolidation {
LMConsolidator.fixEOF(path: path)
LMConsolidator.consolidate(path: path, pragma: true)
}
var arrData: [String] = []
do {
arrData = try String(contentsOfFile: path, encoding: .utf8).components(separatedBy: "\n")
} catch {
IME.prtDebugIntel("\(error)")
IME.prtDebugIntel("↑ Exception happened when reading Associated Phrases data.")
return false
}
for (lineID, lineContent) in arrData.enumerated() {
if !lineContent.hasPrefix("#") {
let lineContent = lineContent.replacingOccurrences(of: "\t", with: " ")
if lineContent.components(separatedBy: " ").count < 2 {
if lineContent != "", lineContent != " " {
IME.prtDebugIntel("Line #\(lineID + 1) Wrecked: \(lineContent)")
}
continue
}
var currentUnigram = Megrez.Unigram(keyValue: Megrez.KeyValuePair(), score: defaultScore)
var columnOne = ""
var columnTwo = ""
for (unitID, unitContent) in lineContent.components(separatedBy: " ").enumerated() {
switch unitID {
case 0:
columnOne = unitContent
case 1:
columnTwo = unitContent
case 2:
if !shouldForceDefaultScore {
if let unitContentConverted = Double(unitContent) {
currentUnigram.score = unitContentConverted
} else {
IME.prtDebugIntel("Line #\(lineID) Score Data Wrecked: \(lineContent)")
}
}
default: break
}
}
//
if columnOne.contains("_punctuation_") {
currentUnigram.score -= (Double(lineID) * 0.000001)
}
let kvPair =
shouldReverse
? Megrez.KeyValuePair(key: columnTwo, value: columnOne)
: Megrez.KeyValuePair(key: columnOne, value: columnTwo)
currentUnigram.keyValue = kvPair
let key = shouldReverse ? columnTwo : columnOne
keyValueScoreMap[key, default: []].append(currentUnigram)
}
}
return true
}
public mutating func close() {
if isLoaded() {
keyValueScoreMap.removeAll()
}
}
// MARK: - Advanced features
public func dump() {
var strDump = ""
for entry in keyValueScoreMap {
let rows: [Megrez.Unigram] = entry.value
for row in rows {
let addline = row.keyValue.key + " " + row.keyValue.value + " " + String(row.score) + "\n"
strDump += addline
}
}
IME.prtDebugIntel(strDump)
}
public func bigramsForKeys(precedingKey: String, key: String) -> [Megrez.Bigram] {
// Swift
// [Megrez.Bigram]()
precedingKey == key ? [Megrez.Bigram]() : [Megrez.Bigram]()
}
public func unigramsFor(key: String) -> [Megrez.Unigram] {
keyValueScoreMap[key] ?? [Megrez.Unigram]()
}
public func hasUnigramsFor(key: String) -> Bool {
keyValueScoreMap[key] != nil
}
}
}

View File

@ -0,0 +1,124 @@
// Copyright (c) 2021 and onwards The vChewing Project (MIT-NTL License).
// Refactored from the ObjCpp-version of this class by:
// (c) 2011 and onwards The OpenVanilla Project (MIT License).
/*
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
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 Foundation
extension vChewing {
@frozen public struct LMLite {
var keyValueMap: [String: [Megrez.KeyValuePair]] = [:]
var allowConsolidation = false
public var count: Int {
keyValueMap.count
}
public init(consolidate: Bool = false) {
keyValueMap = [:]
allowConsolidation = consolidate
}
public func isLoaded() -> Bool {
!keyValueMap.isEmpty
}
@discardableResult public mutating func open(_ path: String) -> Bool {
if isLoaded() {
return false
}
if allowConsolidation {
LMConsolidator.fixEOF(path: path)
LMConsolidator.consolidate(path: path, pragma: true)
}
var arrData: [String] = []
do {
arrData = try String(contentsOfFile: path, encoding: .utf8).components(separatedBy: "\n")
} catch {
IME.prtDebugIntel("\(error)")
IME.prtDebugIntel("↑ Exception happened when reading Associated Phrases data.")
return false
}
for (lineID, lineContent) in arrData.enumerated() {
if !lineContent.hasPrefix("#") {
let lineContent = lineContent.replacingOccurrences(of: "\t", with: " ")
if lineContent.components(separatedBy: " ").count < 2 {
if lineContent != "", lineContent != " " {
IME.prtDebugIntel("Line #\(lineID + 1) Wrecked: \(lineContent)")
}
continue
}
var currentKV = Megrez.KeyValuePair()
for (unitID, unitContent) in lineContent.components(separatedBy: " ").enumerated() {
switch unitID {
case 0:
currentKV.value = unitContent
case 1:
currentKV.key = unitContent
default: break
}
}
keyValueMap[currentKV.key, default: []].append(currentKV)
}
}
return true
}
public mutating func close() {
if isLoaded() {
keyValueMap.removeAll()
}
}
public func dump() {
var strDump = ""
for entry in keyValueMap {
let rows: [Megrez.KeyValuePair] = entry.value
for row in rows {
let addline = row.key + " " + row.value + "\n"
strDump += addline
}
}
IME.prtDebugIntel(strDump)
}
public func unigramsFor(key: String, score givenScore: Double = 0.0) -> [Megrez.Unigram] {
var v: [Megrez.Unigram] = []
if let matched = keyValueMap[key] {
for entry in matched as [Megrez.KeyValuePair] {
v.append(Megrez.Unigram(keyValue: entry, score: givenScore))
}
}
return v
}
public func hasUnigramsFor(key: String) -> Bool {
keyValueMap[key] != nil
}
}
}

View File

@ -0,0 +1,107 @@
// Copyright (c) 2021 and onwards The vChewing Project (MIT-NTL License).
// Refactored from the ObjCpp-version of this class by:
// (c) 2011 and onwards The OpenVanilla Project (MIT License).
/*
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
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 Foundation
extension vChewing {
@frozen public struct LMReplacments {
var keyValueMap: [String: String] = [:]
public var count: Int {
keyValueMap.count
}
public init() {
keyValueMap = [:]
}
public func isLoaded() -> Bool {
!keyValueMap.isEmpty
}
@discardableResult public mutating func open(_ path: String) -> Bool {
if isLoaded() {
return false
}
LMConsolidator.fixEOF(path: path)
LMConsolidator.consolidate(path: path, pragma: true)
var arrData: [String] = []
do {
arrData = try String(contentsOfFile: path, encoding: .utf8).components(separatedBy: "\n")
} catch {
IME.prtDebugIntel("\(error)")
IME.prtDebugIntel("↑ Exception happened when reading Associated Phrases data.")
return false
}
for (lineID, lineContent) in arrData.enumerated() {
if !lineContent.hasPrefix("#") {
let lineContent = lineContent.replacingOccurrences(of: "\t", with: " ")
if lineContent.components(separatedBy: " ").count < 2 {
if lineContent != "", lineContent != " " {
IME.prtDebugIntel("Line #\(lineID + 1) Wrecked: \(lineContent)")
}
continue
}
var currentKV = Megrez.KeyValuePair()
for (unitID, unitContent) in lineContent.components(separatedBy: " ").enumerated() {
switch unitID {
case 0:
currentKV.key = unitContent
case 1:
currentKV.value = unitContent
default: break
}
}
keyValueMap[currentKV.key] = currentKV.value
}
}
return true
}
public mutating func close() {
if isLoaded() {
keyValueMap.removeAll()
}
}
public func dump() {
var strDump = ""
for entry in keyValueMap {
strDump += entry.key + " " + entry.value + "\n"
}
IME.prtDebugIntel(strDump)
}
public func valuesFor(key: String) -> String {
keyValueMap[key] ?? ""
}
}
}

View File

@ -0,0 +1,223 @@
// Copyright (c) 2021 and onwards The vChewing Project (MIT-NTL License).
// Refactored from the ObjCpp-version of this class by:
// (c) 2011 and onwards The OpenVanilla Project (MIT License).
/*
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
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 Foundation
extension vChewing {
public class LMUserOverride {
// MARK: - Private Structures
struct Override {
var count: Int = 0
var timestamp: Double = 0.0
}
struct Observation {
var count: Int = 0
var overrides: [String: Override] = [:]
mutating func update(candidate: String, timestamp: Double) {
count += 1
if var neta = overrides[candidate] {
neta.timestamp = timestamp
neta.count += 1
}
}
}
struct KeyObservationPair: Equatable {
var key: String
var observation: Observation
var hashValue: Int { key.hashValue }
init(key: String, observation: Observation) {
self.key = key
self.observation = observation
}
static func == (lhs: KeyObservationPair, rhs: KeyObservationPair) -> Bool {
lhs.key == rhs.key
}
}
// MARK: - Main
var mutCapacity: Int
var mutDecayExponent: Double
var mutLRUList = [KeyObservationPair]()
var mutLRUMap: [String: KeyObservationPair] = [:]
let kDecayThreshold: Double = 1.0 / 1_048_576.0
public init(capacity: Int = 500, decayConstant: Double = 5400.0) {
mutCapacity = abs(capacity) // Ensures that this value is always > 0.
mutDecayExponent = log(0.5) / decayConstant
}
public func observe(
walkedNodes: [Megrez.NodeAnchor],
cursorIndex: Int,
candidate: String,
timestamp: Double
) {
let key = getWalkedNodesToKey(walkedNodes: walkedNodes, cursorIndex: cursorIndex)
guard !key.isEmpty
else {
return
}
guard let map = mutLRUMap[key] else {
var observation: Observation = .init()
observation.update(candidate: candidate, timestamp: timestamp)
mutLRUMap[key] = KeyObservationPair(key: key, observation: observation)
mutLRUList.insert(KeyObservationPair(key: key, observation: observation), at: 0)
if mutLRUList.count > mutCapacity {
mutLRUMap[mutLRUList.reversed()[0].key] = nil
mutLRUList.removeLast()
}
return
}
var obs = map.observation
obs.update(candidate: candidate, timestamp: timestamp)
let pair = KeyObservationPair(key: key, observation: obs)
mutLRUList.insert(pair, at: 0)
}
public func suggest(
walkedNodes: [Megrez.NodeAnchor],
cursorIndex: Int,
timestamp: Double
) -> String {
let key = getWalkedNodesToKey(walkedNodes: walkedNodes, cursorIndex: cursorIndex)
guard let keyValuePair = mutLRUMap[key],
!key.isEmpty
else {
return ""
}
IME.prtDebugIntel("Suggest - A: \(key)")
IME.prtDebugIntel("Suggest - B: \(keyValuePair.key)")
let observation = keyValuePair.observation
var candidate = ""
var score = 0.0
for overrideNeta in Array(observation.overrides) {
let overrideScore = getScore(
eventCount: overrideNeta.value.count,
totalCount: observation.count,
eventTimestamp: overrideNeta.value.timestamp,
timestamp: timestamp,
lambda: mutDecayExponent
)
if overrideScore == 0.0 {
continue
}
if overrideScore > score {
candidate = overrideNeta.key
score = overrideScore
}
}
return candidate
}
func isEndingPunctuation(value: String) -> Bool {
["", "", "", "", "", "", "", ""].contains(value)
}
public func getScore(
eventCount: Int,
totalCount: Int,
eventTimestamp: Double,
timestamp: Double,
lambda: Double
) -> Double {
let decay = exp((timestamp - eventTimestamp) * lambda)
if decay < kDecayThreshold {
return 0.0
}
let prob = Double(eventCount) / Double(totalCount)
return prob * decay
}
func getWalkedNodesToKey(
walkedNodes: [Megrez.NodeAnchor], cursorIndex: Int
) -> String {
var strOutput = ""
var arrNodes: [Megrez.NodeAnchor] = []
var intLength = 0
for nodeNeta in walkedNodes {
arrNodes.append(nodeNeta)
intLength += nodeNeta.spanningLength
if intLength >= cursorIndex {
break
}
}
// .reversed 使 Swift
//
var arrNodesReversed: [Megrez.NodeAnchor] = []
arrNodesReversed.append(contentsOf: arrNodes.reversed())
if arrNodesReversed.isEmpty {
return ""
}
var strCurrent = "()"
var strPrev = "()"
var strAnterior = "()"
for (theIndex, theAnchor) in arrNodesReversed.enumerated() {
if strCurrent != "()", let nodeCurrent = theAnchor.node {
let keyCurrent = nodeCurrent.currentKeyValue().key
let valCurrent = nodeCurrent.currentKeyValue().value
strCurrent = "(\(keyCurrent), \(valCurrent))"
if let nodePrev = arrNodesReversed[theIndex + 1].node {
let keyPrev = nodePrev.currentKeyValue().key
let valPrev = nodePrev.currentKeyValue().value
strPrev = "(\(keyPrev), \(valPrev))"
}
if let nodeAnterior = arrNodesReversed[theIndex + 2].node {
let keyAnterior = nodeAnterior.currentKeyValue().key
let valAnterior = nodeAnterior.currentKeyValue().value
strAnterior = "(\(keyAnterior), \(valAnterior))"
}
break //
}
}
strOutput = "(\(strAnterior),\(strPrev),\(strCurrent))"
if strOutput == "((),(),())" {
strOutput = ""
}
return strOutput
}
}
}

View File

@ -1,69 +0,0 @@
// Copyright (c) 2011 and onwards The OpenVanilla Project (MIT License).
// All possible vChewing-specific modifications are of:
// (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.
*/
#ifndef ASSOCIATEDPHRASES_H
#define ASSOCIATEDPHRASES_H
#include <iostream>
#include <map>
#include <string>
#include <vector>
namespace vChewing
{
class AssociatedPhrases
{
public:
AssociatedPhrases();
~AssociatedPhrases();
const bool isLoaded();
bool open(const char *path);
void close();
const std::vector<std::string> valuesForKey(const std::string &key);
const bool hasValuesForKey(const std::string &key);
protected:
struct Row
{
Row(std::string_view &k, std::string_view &v) : key(k), value(v)
{
}
std::string_view key;
std::string_view value;
};
std::map<std::string_view, std::vector<Row>> keyRowMap;
int fd;
void *data;
size_t length;
};
} // namespace vChewing
#endif /* AssociatedPhrases_hpp */

View File

@ -1,146 +0,0 @@
// Copyright (c) 2011 and onwards The OpenVanilla Project (MIT License).
// All possible vChewing-specific modifications are of:
// (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.
*/
#include "AssociatedPhrases.h"
#include "vChewing-Swift.h"
#include <fcntl.h>
#include <fstream>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include "KeyValueBlobReader.h"
#include "LMConsolidator.h"
namespace vChewing
{
AssociatedPhrases::AssociatedPhrases() : fd(-1), data(0), length(0)
{
}
AssociatedPhrases::~AssociatedPhrases()
{
if (data)
{
close();
}
}
const bool AssociatedPhrases::isLoaded()
{
if (data)
{
return true;
}
return false;
}
bool AssociatedPhrases::open(const char *path)
{
if (data)
{
return false;
}
LMConsolidator::FixEOF(path);
LMConsolidator::ConsolidateContent(path, true);
fd = ::open(path, O_RDONLY);
if (fd == -1)
{
printf("open:: file not exist");
return false;
}
struct stat sb;
if (fstat(fd, &sb) == -1)
{
printf("open:: cannot open file");
return false;
}
length = (size_t)sb.st_size;
data = mmap(NULL, length, PROT_READ, MAP_SHARED, fd, 0);
if (!data)
{
::close(fd);
return false;
}
KeyValueBlobReader reader(static_cast<char *>(data), length);
KeyValueBlobReader::KeyValue keyValue;
KeyValueBlobReader::State state;
while ((state = reader.Next(&keyValue)) == KeyValueBlobReader::State::HAS_PAIR)
{
keyRowMap[keyValue.key].emplace_back(keyValue.key, keyValue.value);
}
// 下面這一段或許可以做成開關、來詢問是否對使用者語彙採取寬鬆策略(哪怕有行內容寫錯也會放行)
if (state == KeyValueBlobReader::State::ERROR)
{
// close();
if (mgrPrefs.isDebugModeEnabled)
syslog(LOG_CONS, "AssociatedPhrases: Failed at Open Step 5. On Error Resume Next.\n");
// return false;
}
return true;
}
void AssociatedPhrases::close()
{
if (data)
{
munmap(data, length);
::close(fd);
data = 0;
}
keyRowMap.clear();
}
const std::vector<std::string> AssociatedPhrases::valuesForKey(const std::string &key)
{
std::vector<std::string> v;
auto iter = keyRowMap.find(key);
if (iter != keyRowMap.end())
{
const std::vector<Row> &rows = iter->second;
for (const auto &row : rows)
{
std::string_view value = row.value;
v.push_back({value.data(), value.size()});
}
}
return v;
}
const bool AssociatedPhrases::hasValuesForKey(const std::string &key)
{
return keyRowMap.find(key) != keyRowMap.end();
}
}; // namespace vChewing

View File

@ -1,85 +0,0 @@
// Copyright (c) 2011 and onwards The OpenVanilla Project (MIT License).
// All possible vChewing-specific modifications are of:
// (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.
*/
#ifndef CoreLM_H
#define CoreLM_H
#include "LanguageModel.h"
#include <iostream>
#include <map>
#include <string>
#include <vector>
// this class relies on the fact that we have a space-separated data
// format, and we use mmap and zero-out the separators and line feeds
// to avoid creating new string objects; the parser is a simple DFA
using namespace std;
using namespace Gramambular;
namespace vChewing
{
class CoreLM : public Gramambular::LanguageModel
{
public:
CoreLM();
~CoreLM();
bool isLoaded();
bool open(const char *path);
void close();
void dump();
virtual const std::vector<Gramambular::Bigram> bigramsForKeys(const string &preceedingKey, const string &key);
virtual const std::vector<Gramambular::Unigram> unigramsForKey(const string &key);
virtual bool hasUnigramsForKey(const string &key);
protected:
struct CStringCmp
{
bool operator()(const char *s1, const char *s2) const
{
return strcmp(s1, s2) < 0;
}
};
struct Row
{
const char *key;
const char *value;
const char *logProbability;
};
map<const char *, vector<Row>, CStringCmp> keyRowMap;
int fd;
void *data;
size_t length;
};
}; // namespace vChewing
#endif

View File

@ -1,365 +0,0 @@
// Copyright (c) 2011 and onwards The OpenVanilla Project (MIT License).
// All possible vChewing-specific modifications are of:
// (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.
*/
#include "CoreLM.h"
#include "vChewing-Swift.h"
#include <fcntl.h>
#include <fstream>
#include <sys/mman.h>
#include <sys/stat.h>
#include <syslog.h>
#include <unistd.h>
using namespace Gramambular;
vChewing::CoreLM::CoreLM() : fd(-1), data(0), length(0)
{
}
vChewing::CoreLM::~CoreLM()
{
if (data)
{
close();
}
}
bool vChewing::CoreLM::isLoaded()
{
if (data)
{
return true;
}
return false;
}
bool vChewing::CoreLM::open(const char *path)
{
if (data)
{
return false;
}
fd = ::open(path, O_RDONLY);
if (fd == -1)
{
return false;
}
struct stat sb;
if (fstat(fd, &sb) == -1)
{
return false;
}
length = (size_t)sb.st_size;
data = mmap(NULL, length, PROT_WRITE, MAP_PRIVATE, fd, 0);
if (!data)
{
::close(fd);
return false;
}
// Regular expression for parsing:
// (\n*\w\w*\s\w\w*\s\w\w*)*$
//
// Expanded as DFA (in Graphviz):
//
// digraph finite_state_machine {
// rankdir = LR;
// size = "10";
//
// node [shape = doublecircle]; End;
// node [shape = circle];
//
// Start -> End [ label = "EOF"];
// Start -> Error [ label = "\\s" ];
// Start -> Start [ label = "\\n" ];
// Start -> 1 [ label = "\\w" ];
//
// 1 -> Error [ label = "\\n, EOF" ];
// 1 -> 2 [ label = "\\s" ];
// 1 -> 1 [ label = "\\w" ];
//
// 2 -> Error [ label = "\\n, \\s, EOF" ];
// 2 -> 3 [ label = "\\w" ];
//
// 3 -> Error [ label = "\\n, EOF "];
// 3 -> 4 [ label = "\\s" ];
// 3 -> 3 [ label = "\\w" ];
//
// 4 -> Error [ label = "\\n, \\s, EOF" ];
// 4 -> 5 [ label = "\\w" ];
//
// 5 -> Error [ label = "\\s, EOF" ];
// 5 -> Start [ label = "\\n" ];
// 5 -> 5 [ label = "\\w" ];
// }
char *head = (char *)data;
char *end = (char *)data + length;
char c;
Row row;
start:
// EOF -> end
if (head == end)
{
goto end;
}
c = *head;
// \s -> error
if (c == ' ')
{
if (mgrPrefs.isDebugModeEnabled)
syslog(LOG_CONS, "vChewingDebug: CoreLM // Start: \\s -> error");
goto error;
}
// \n -> start
else if (c == '\n')
{
head++;
goto start;
}
// \w -> record column star, state1
row.value = head;
head++;
// fall through to state 1
state1:
// EOF -> error
if (head == end)
{
if (mgrPrefs.isDebugModeEnabled)
syslog(LOG_CONS, "vChewingDebug: CoreLM // state 1: EOF -> error");
goto error;
}
c = *head;
// \n -> error
if (c == '\n')
{
if (mgrPrefs.isDebugModeEnabled)
syslog(LOG_CONS, "vChewingDebug: CoreLM // state 1: \\n -> error");
goto error;
}
// \s -> state2 + zero out ending + record column start
else if (c == ' ')
{
*head = 0;
head++;
row.key = head;
goto state2;
}
// \w -> state1
head++;
goto state1;
state2:
// eof -> error
if (head == end)
{
if (mgrPrefs.isDebugModeEnabled)
syslog(LOG_CONS, "vChewingDebug: CoreLM // state 2: EOF -> error");
goto error;
}
c = *head;
// \n, \s -> error
if (c == '\n' || c == ' ')
{
if (mgrPrefs.isDebugModeEnabled)
syslog(LOG_CONS, "vChewingDebug: CoreLM // state 2: \\n \\s -> error");
goto error;
}
// \w -> state3
head++;
// fall through to state 3
state3:
// eof -> error
if (head == end)
{
if (mgrPrefs.isDebugModeEnabled)
syslog(LOG_CONS, "vChewingDebug: CoreLM // state 3: EOF -> error");
goto error;
}
c = *head;
// \n -> error
if (c == '\n')
{
if (mgrPrefs.isDebugModeEnabled)
syslog(LOG_CONS, "vChewingDebug: CoreLM // state 3: \\n -> error");
goto error;
}
// \s -> state4 + zero out ending + record column start
else if (c == ' ')
{
*head = 0;
head++;
row.logProbability = head;
goto state4;
}
// \w -> state3
head++;
goto state3;
state4:
// eof -> error
if (head == end)
{
if (mgrPrefs.isDebugModeEnabled)
syslog(LOG_CONS, "vChewingDebug: CoreLM // state 4: EOF -> error");
goto error;
}
c = *head;
// \n, \s -> error
if (c == '\n' || c == ' ')
{
if (mgrPrefs.isDebugModeEnabled)
syslog(LOG_CONS, "vChewingDebug: CoreLM // state 4: \\n \\s -> error");
goto error;
}
// \w -> state5
head++;
// fall through to state 5
state5:
// eof -> error
if (head == end)
{
if (mgrPrefs.isDebugModeEnabled)
syslog(LOG_CONS, "vChewingDebug: CoreLM // state 5: EOF -> error");
goto error;
}
c = *head;
// \s -> error
if (c == ' ')
{
if (mgrPrefs.isDebugModeEnabled)
syslog(LOG_CONS, "vChewingDebug: CoreLM // state 5: \\s -> error");
goto error;
}
// \n -> start
else if (c == '\n')
{
*head = 0;
head++;
keyRowMap[row.key].push_back(row);
goto start;
}
// \w -> state 5
head++;
goto state5;
error:
close();
return false;
end:
static const char *space = " ";
static const char *zero = "0.0";
Row emptyRow;
emptyRow.key = space;
emptyRow.value = space;
emptyRow.logProbability = zero;
keyRowMap[space].push_back(emptyRow);
if (mgrPrefs.isDebugModeEnabled)
syslog(LOG_CONS, "vChewingDebug: CoreLM // File Load Complete.");
return true;
}
void vChewing::CoreLM::close()
{
if (data)
{
munmap(data, length);
::close(fd);
data = 0;
}
keyRowMap.clear();
}
void vChewing::CoreLM::dump()
{
size_t rows = 0;
for (map<const char *, vector<Row>>::const_iterator i = keyRowMap.begin(), e = keyRowMap.end(); i != e; ++i)
{
const vector<Row> &r = (*i).second;
for (vector<Row>::const_iterator ri = r.begin(), re = r.end(); ri != re; ++ri)
{
const Row &row = *ri;
cerr << row.key << " " << row.value << " " << row.logProbability << "\n";
rows++;
}
}
}
const std::vector<Gramambular::Bigram> vChewing::CoreLM::bigramsForKeys(const string &preceedingKey, const string &key)
{
return std::vector<Gramambular::Bigram>();
}
const std::vector<Gramambular::Unigram> vChewing::CoreLM::unigramsForKey(const string &key)
{
std::vector<Gramambular::Unigram> v;
map<const char *, vector<Row>>::const_iterator i = keyRowMap.find(key.c_str());
if (i != keyRowMap.end())
{
for (vector<Row>::const_iterator ri = (*i).second.begin(), re = (*i).second.end(); ri != re; ++ri)
{
Unigram g;
const Row &r = *ri;
g.keyValue.key = r.key;
g.keyValue.value = r.value;
g.score = atof(r.logProbability);
v.push_back(g);
}
}
return v;
}
bool vChewing::CoreLM::hasUnigramsForKey(const string &key)
{
return keyRowMap.find(key.c_str()) != keyRowMap.end();
}

View File

@ -1,56 +0,0 @@
// Copyright (c) 2011 and onwards The OpenVanilla Project (MIT License).
// All possible vChewing-specific modifications are of:
// (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.
*/
#ifndef PHRASEREPLACEMENTMAP_H
#define PHRASEREPLACEMENTMAP_H
#include <iostream>
#include <map>
#include <string>
namespace vChewing
{
class PhraseReplacementMap
{
public:
PhraseReplacementMap();
~PhraseReplacementMap();
bool open(const char *path);
void close();
const std::string valueForKey(const std::string &key);
protected:
std::map<std::string_view, std::string_view> keyValueMap;
int fd;
void *data;
size_t length;
};
} // namespace vChewing
#endif

View File

@ -1,130 +0,0 @@
// Copyright (c) 2011 and onwards The OpenVanilla Project (MIT License).
// All possible vChewing-specific modifications are of:
// (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.
*/
#include "PhraseReplacementMap.h"
#include "vChewing-Swift.h"
#include <fcntl.h>
#include <fstream>
#include <sys/mman.h>
#include <sys/stat.h>
#include <syslog.h>
#include <unistd.h>
#include "KeyValueBlobReader.h"
#include "LMConsolidator.h"
namespace vChewing
{
using std::string;
PhraseReplacementMap::PhraseReplacementMap() : fd(-1), data(0), length(0)
{
}
PhraseReplacementMap::~PhraseReplacementMap()
{
if (data)
{
close();
}
}
bool PhraseReplacementMap::open(const char *path)
{
if (data)
{
return false;
}
LMConsolidator::FixEOF(path);
LMConsolidator::ConsolidateContent(path, true);
fd = ::open(path, O_RDONLY);
if (fd == -1)
{
printf("open:: file not exist");
return false;
}
struct stat sb;
if (fstat(fd, &sb) == -1)
{
printf("open:: cannot open file");
return false;
}
length = (size_t)sb.st_size;
data = mmap(NULL, length, PROT_READ, MAP_SHARED, fd, 0);
if (!data)
{
::close(fd);
return false;
}
KeyValueBlobReader reader(static_cast<char *>(data), length);
KeyValueBlobReader::KeyValue keyValue;
KeyValueBlobReader::State state;
while ((state = reader.Next(&keyValue)) == KeyValueBlobReader::State::HAS_PAIR)
{
keyValueMap[keyValue.key] = keyValue.value;
}
// 下面這一段或許可以做成開關、來詢問是否對使用者語彙採取寬鬆策略(哪怕有行內容寫錯也會放行)
if (state == KeyValueBlobReader::State::ERROR)
{
// close();
if (mgrPrefs.isDebugModeEnabled)
syslog(LOG_CONS, "PhraseReplacementMap: Failed at Open Step 5. On Error Resume Next.\n");
// return false;
}
return true;
}
void PhraseReplacementMap::close()
{
if (data)
{
munmap(data, length);
::close(fd);
data = 0;
}
keyValueMap.clear();
}
const std::string PhraseReplacementMap::valueForKey(const std::string &key)
{
auto iter = keyValueMap.find(key);
if (iter != keyValueMap.end())
{
const std::string_view v = iter->second;
return {v.data(), v.size()};
}
return string("");
}
}

View File

@ -1,82 +0,0 @@
// Copyright (c) 2011 and onwards The OpenVanilla Project (MIT License).
// All possible vChewing-specific modifications are of:
// (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.
*/
#ifndef USERPHRASESLM_H
#define USERPHRASESLM_H
#include "LanguageModel.h"
#include <iostream>
#include <map>
#include <string>
namespace vChewing
{
class UserPhrasesLM : public Gramambular::LanguageModel
{
public:
UserPhrasesLM();
~UserPhrasesLM();
bool isLoaded();
bool open(const char *path);
void close();
void dump();
virtual bool allowConsolidation()
{
return true;
}
virtual float overridedValue()
{
return 0.0;
}
virtual const std::vector<Gramambular::Bigram> bigramsForKeys(const std::string &preceedingKey,
const std::string &key);
virtual const std::vector<Gramambular::Unigram> unigramsForKey(const std::string &key);
virtual bool hasUnigramsForKey(const std::string &key);
protected:
struct Row
{
Row(std::string_view &k, std::string_view &v) : key(k), value(v)
{
}
std::string_view key;
std::string_view value;
};
std::map<std::string_view, std::vector<Row>> keyRowMap;
int fd;
void *data;
size_t length;
};
} // namespace vChewing
#endif

View File

@ -1,174 +0,0 @@
// Copyright (c) 2011 and onwards The OpenVanilla Project (MIT License).
// All possible vChewing-specific modifications are of:
// (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.
*/
#include "UserPhrasesLM.h"
#include "vChewing-Swift.h"
#include <fcntl.h>
#include <fstream>
#include <sys/mman.h>
#include <sys/stat.h>
#include <syslog.h>
#include <unistd.h>
#include "KeyValueBlobReader.h"
#include "LMConsolidator.h"
namespace vChewing
{
UserPhrasesLM::UserPhrasesLM() : fd(-1), data(0), length(0)
{
}
UserPhrasesLM::~UserPhrasesLM()
{
if (data)
{
close();
}
}
bool UserPhrasesLM::isLoaded()
{
if (data)
{
return true;
}
return false;
}
bool UserPhrasesLM::open(const char *path)
{
if (data)
{
return false;
}
if (allowConsolidation())
{
LMConsolidator::FixEOF(path);
LMConsolidator::ConsolidateContent(path, true);
}
fd = ::open(path, O_RDONLY);
if (fd == -1)
{
printf("open:: file not exist");
return false;
}
struct stat sb;
if (fstat(fd, &sb) == -1)
{
printf("open:: cannot open file");
return false;
}
length = (size_t)sb.st_size;
data = mmap(NULL, length, PROT_READ, MAP_SHARED, fd, 0);
if (!data)
{
::close(fd);
return false;
}
KeyValueBlobReader reader(static_cast<char *>(data), length);
KeyValueBlobReader::KeyValue keyValue;
KeyValueBlobReader::State state;
while ((state = reader.Next(&keyValue)) == KeyValueBlobReader::State::HAS_PAIR)
{
// We invert the key and value, since in user phrases, "key" is the phrase value, and "value" is the BPMF
// reading.
keyRowMap[keyValue.value].emplace_back(keyValue.value, keyValue.key);
}
// 下面這一段或許可以做成開關、來詢問是否對使用者語彙採取寬鬆策略(哪怕有行內容寫錯也會放行)
if (state == KeyValueBlobReader::State::ERROR)
{
// close();
if (mgrPrefs.isDebugModeEnabled)
syslog(LOG_CONS, "UserPhrasesLM: Failed at Open Step 5. On Error Resume Next.\n");
// return false;
}
return true;
}
void UserPhrasesLM::close()
{
if (data)
{
munmap(data, length);
::close(fd);
data = 0;
}
keyRowMap.clear();
}
void UserPhrasesLM::dump()
{
for (const auto &entry : keyRowMap)
{
const std::vector<Row> &rows = entry.second;
for (const auto &row : rows)
{
std::cerr << row.key << " " << row.value << "\n";
}
}
}
const std::vector<Gramambular::Bigram> UserPhrasesLM::bigramsForKeys(const std::string &preceedingKey,
const std::string &key)
{
return std::vector<Gramambular::Bigram>();
}
const std::vector<Gramambular::Unigram> UserPhrasesLM::unigramsForKey(const std::string &key)
{
std::vector<Gramambular::Unigram> v;
auto iter = keyRowMap.find(key);
if (iter != keyRowMap.end())
{
const std::vector<Row> &rows = iter->second;
for (const auto &row : rows)
{
Gramambular::Unigram g;
g.keyValue.key = row.key;
g.keyValue.value = row.value;
g.score = overridedValue();
v.push_back(g);
}
}
return v;
}
bool UserPhrasesLM::hasUnigramsForKey(const std::string &key)
{
return keyRowMap.find(key) != keyRowMap.end();
}
}; // namespace vChewing

View File

@ -1,54 +0,0 @@
// Copyright (c) 2011 and onwards The OpenVanilla Project (MIT License).
// All possible vChewing-specific modifications are of:
// (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 "KeyHandler.h"
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface mgrLangModel : NSObject
+ (void)loadDataModel:(InputMode)mode;
+ (void)loadUserPhrases;
+ (void)loadUserAssociatedPhrases;
+ (void)loadUserPhraseReplacement;
+ (BOOL)checkIfUserPhraseExist:(NSString *)userPhrase
inputMode:(InputMode)mode
key:(NSString *)key NS_SWIFT_NAME(checkIfUserPhraseExist(userPhrase:mode:key:));
+ (void)consolidateGivenFile:(NSString *)path shouldCheckPragma:(BOOL)shouldCheckPragma;
+ (void)setPhraseReplacementEnabled:(BOOL)phraseReplacementEnabled;
+ (void)setCNSEnabled:(BOOL)cnsEnabled;
+ (void)setSymbolEnabled:(BOOL)symbolEnabled;
@end
/// The following methods are merely for testing.
@interface mgrLangModel ()
+ (void)loadDataModels;
@end
NS_ASSUME_NONNULL_END

View File

@ -1,195 +0,0 @@
// Copyright (c) 2011 and onwards The OpenVanilla Project (MIT License).
// All possible vChewing-specific modifications are of:
// (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 "mgrLangModel.h"
#import "LMConsolidator.h"
#import "mgrLangModel_Privates.h"
#import "vChewing-Swift.h"
static const int kUserOverrideModelCapacity = 500;
static const double kObservedOverrideHalflife = 5400.0;
static vChewing::LMInstantiator gLangModelCHT;
static vChewing::LMInstantiator gLangModelCHS;
static vChewing::UserOverrideModel gUserOverrideModelCHT(kUserOverrideModelCapacity, kObservedOverrideHalflife);
static vChewing::UserOverrideModel gUserOverrideModelCHS(kUserOverrideModelCapacity, kObservedOverrideHalflife);
@implementation mgrLangModel
// 這個函數無法遷移至 Swift
static void LTLoadLanguageModelFile(NSString *filenameWithoutExtension, vChewing::LMInstantiator &lm)
{
NSString *dataPath = [mgrLangModel getBundleDataPath:filenameWithoutExtension];
lm.loadLanguageModel([dataPath UTF8String]);
}
// 這個函數無法遷移至 Swift
+ (void)loadDataModels
{
if (!gLangModelCHT.isDataModelLoaded())
LTLoadLanguageModelFile(@"data-cht", gLangModelCHT);
if (!gLangModelCHT.isMiscDataLoaded())
gLangModelCHT.loadMiscData([[self getBundleDataPath:@"data-zhuyinwen"] UTF8String]);
if (!gLangModelCHT.isSymbolDataLoaded())
gLangModelCHT.loadSymbolData([[self getBundleDataPath:@"data-symbols"] UTF8String]);
if (!gLangModelCHT.isCNSDataLoaded())
gLangModelCHT.loadCNSData([[self getBundleDataPath:@"char-kanji-cns"] UTF8String]);
// -----------------
if (!gLangModelCHS.isDataModelLoaded())
LTLoadLanguageModelFile(@"data-chs", gLangModelCHS);
if (!gLangModelCHS.isMiscDataLoaded())
gLangModelCHS.loadMiscData([[self getBundleDataPath:@"data-zhuyinwen"] UTF8String]);
if (!gLangModelCHS.isSymbolDataLoaded())
gLangModelCHS.loadSymbolData([[self getBundleDataPath:@"data-symbols"] UTF8String]);
if (!gLangModelCHS.isCNSDataLoaded())
gLangModelCHS.loadCNSData([[self getBundleDataPath:@"char-kanji-cns"] UTF8String]);
}
// 這個函數無法遷移至 Swift
+ (void)loadDataModel:(InputMode)mode
{
if ([mode isEqualToString:imeModeCHT])
{
if (!gLangModelCHT.isDataModelLoaded())
LTLoadLanguageModelFile(@"data-cht", gLangModelCHT);
if (!gLangModelCHT.isMiscDataLoaded())
gLangModelCHT.loadMiscData([[self getBundleDataPath:@"data-zhuyinwen"] UTF8String]);
if (!gLangModelCHT.isSymbolDataLoaded())
gLangModelCHT.loadSymbolData([[self getBundleDataPath:@"data-symbols"] UTF8String]);
if (!gLangModelCHT.isCNSDataLoaded())
gLangModelCHT.loadCNSData([[self getBundleDataPath:@"char-kanji-cns"] UTF8String]);
}
if ([mode isEqualToString:imeModeCHS])
{
if (!gLangModelCHS.isDataModelLoaded())
LTLoadLanguageModelFile(@"data-chs", gLangModelCHS);
if (!gLangModelCHS.isMiscDataLoaded())
gLangModelCHS.loadMiscData([[self getBundleDataPath:@"data-zhuyinwen"] UTF8String]);
if (!gLangModelCHS.isSymbolDataLoaded())
gLangModelCHS.loadSymbolData([[self getBundleDataPath:@"data-symbols"] UTF8String]);
if (!gLangModelCHS.isCNSDataLoaded())
gLangModelCHS.loadCNSData([[self getBundleDataPath:@"char-kanji-cns"] UTF8String]);
}
}
// 這個函數無法遷移至 Swift
+ (void)loadUserPhrases
{
gLangModelCHT.loadUserPhrases([[self userPhrasesDataPath:imeModeCHT] UTF8String],
[[self excludedPhrasesDataPath:imeModeCHT] UTF8String]);
gLangModelCHS.loadUserPhrases([[self userPhrasesDataPath:imeModeCHS] UTF8String],
[[self excludedPhrasesDataPath:imeModeCHS] UTF8String]);
gLangModelCHT.loadUserSymbolData([[self userSymbolDataPath:imeModeCHT] UTF8String]);
gLangModelCHS.loadUserSymbolData([[self userSymbolDataPath:imeModeCHS] UTF8String]);
}
// 這個函數無法遷移至 Swift
+ (void)loadUserAssociatedPhrases
{
gLangModelCHT.loadUserAssociatedPhrases([[self userAssociatedPhrasesDataPath:imeModeCHT] UTF8String]);
gLangModelCHS.loadUserAssociatedPhrases([[self userAssociatedPhrasesDataPath:imeModeCHS] UTF8String]);
}
// 這個函數無法遷移至 Swift
+ (void)loadUserPhraseReplacement
{
gLangModelCHT.loadPhraseReplacementMap([[self phraseReplacementDataPath:imeModeCHT] UTF8String]);
gLangModelCHS.loadPhraseReplacementMap([[self phraseReplacementDataPath:imeModeCHS] UTF8String]);
}
// 這個函數無法遷移至 Swift
+ (BOOL)checkIfUserPhraseExist:(NSString *)userPhrase
inputMode:(InputMode)mode
key:(NSString *)key NS_SWIFT_NAME(checkIfUserPhraseExist(userPhrase:mode:key:))
{
string unigramKey = string(key.UTF8String);
vector<vChewing::Unigram> unigrams = [mode isEqualToString:imeModeCHT] ? gLangModelCHT.unigramsForKey(unigramKey)
: gLangModelCHS.unigramsForKey(unigramKey);
string userPhraseString = string(userPhrase.UTF8String);
for (auto unigram : unigrams)
{
if (unigram.keyValue.value == userPhraseString)
{
return YES;
}
}
return NO;
}
// 這個函數無法遷移至 Swift
+ (void)consolidateGivenFile:(NSString *)path shouldCheckPragma:(BOOL)shouldCheckPragma
{
vChewing::LMConsolidator::ConsolidateContent([path UTF8String], shouldCheckPragma);
}
// 這個函數無法遷移至 Swift
+ (vChewing::LMInstantiator *)lmCHT
{
return &gLangModelCHT;
}
// 這個函數無法遷移至 Swift
+ (vChewing::LMInstantiator *)lmCHS
{
return &gLangModelCHS;
}
// 這個函數無法遷移至 Swift
+ (vChewing::UserOverrideModel *)userOverrideModelCHT
{
return &gUserOverrideModelCHT;
}
// 這個函數無法遷移至 Swift
+ (vChewing::UserOverrideModel *)userOverrideModelCHS
{
return &gUserOverrideModelCHS;
}
// 這個函數無法遷移至 Swift
+ (void)setPhraseReplacementEnabled:(BOOL)phraseReplacementEnabled
{
gLangModelCHT.setPhraseReplacementEnabled(phraseReplacementEnabled);
gLangModelCHS.setPhraseReplacementEnabled(phraseReplacementEnabled);
}
// 這個函數無法遷移至 Swift
+ (void)setCNSEnabled:(BOOL)cnsEnabled
{
gLangModelCHT.setCNSEnabled(cnsEnabled);
gLangModelCHS.setCNSEnabled(cnsEnabled);
}
// 這個函數無法遷移至 Swift
+ (void)setSymbolEnabled:(BOOL)symbolEnabled
{
gLangModelCHT.setSymbolEnabled(symbolEnabled);
gLangModelCHS.setSymbolEnabled(symbolEnabled);
}
@end

View File

@ -26,7 +26,195 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import Cocoa import Cocoa
@objc extension mgrLangModel { /// mgrLangModel
/// mgrLangModel
///
/// mgrLangModel
private var gLangModelCHS = vChewing.LMInstantiator()
private var gLangModelCHT = vChewing.LMInstantiator()
private var gUserOverrideModelCHS = vChewing.LMUserOverride()
private var gUserOverrideModelCHT = vChewing.LMUserOverride()
class mgrLangModel: NSObject {
/// fileprivate
public static var lmCHS: vChewing.LMInstantiator { gLangModelCHS }
public static var lmCHT: vChewing.LMInstantiator { gLangModelCHT }
public static var uomCHS: vChewing.LMUserOverride { gUserOverrideModelCHS }
public static var uomCHT: vChewing.LMUserOverride { gUserOverrideModelCHT }
// MARK: - Functions reacting directly with language models.
static func loadCoreLanguageModelFile(filenameSansExtension: String, langModel lm: inout vChewing.LMInstantiator) {
let dataPath: String = mgrLangModel.getBundleDataPath(filenameSansExtension)
lm.loadLanguageModel(path: dataPath)
}
public static func loadDataModels() {
DispatchQueue.global(qos: .userInitiated).async {
if !gLangModelCHT.isCNSDataLoaded() {
gLangModelCHT.loadCNSData(path: getBundleDataPath("char-kanji-cns"))
}
if !gLangModelCHT.isMiscDataLoaded() {
gLangModelCHT.loadMiscData(path: getBundleDataPath("data-zhuyinwen"))
}
if !gLangModelCHT.isSymbolDataLoaded() {
gLangModelCHT.loadSymbolData(path: getBundleDataPath("data-symbols"))
}
if !gLangModelCHS.isCNSDataLoaded() {
gLangModelCHS.loadCNSData(path: getBundleDataPath("char-kanji-cns"))
}
if !gLangModelCHS.isMiscDataLoaded() {
gLangModelCHS.loadMiscData(path: getBundleDataPath("data-zhuyinwen"))
}
if !gLangModelCHS.isSymbolDataLoaded() {
gLangModelCHS.loadSymbolData(path: getBundleDataPath("data-symbols"))
}
}
if !gLangModelCHT.isDataModelLoaded() {
NotifierController.notify(
message: String(
format: "%@", NSLocalizedString("Loading CHT Core Dict...", comment: "")
)
)
loadCoreLanguageModelFile(filenameSansExtension: "data-cht", langModel: &gLangModelCHT)
NotifierController.notify(
message: String(
format: "%@", NSLocalizedString("Core Dict loading complete.", comment: "")
)
)
}
if !gLangModelCHS.isDataModelLoaded() {
NotifierController.notify(
message: String(
format: "%@", NSLocalizedString("Loading CHS Core Dict...", comment: "")
)
)
loadCoreLanguageModelFile(filenameSansExtension: "data-chs", langModel: &gLangModelCHS)
NotifierController.notify(
message: String(
format: "%@", NSLocalizedString("Core Dict loading complete.", comment: "")
)
)
}
}
public static func loadDataModel(_ mode: InputMode) {
if mode == InputMode.imeModeCHS {
DispatchQueue.global(qos: .userInitiated).async {
if !gLangModelCHS.isMiscDataLoaded() {
gLangModelCHS.loadMiscData(path: getBundleDataPath("data-zhuyinwen"))
}
if !gLangModelCHS.isSymbolDataLoaded() {
gLangModelCHS.loadSymbolData(path: getBundleDataPath("data-symbols"))
}
if !gLangModelCHS.isCNSDataLoaded() {
gLangModelCHS.loadCNSData(path: getBundleDataPath("char-kanji-cns"))
}
}
if !gLangModelCHS.isDataModelLoaded() {
NotifierController.notify(
message: String(
format: "%@", NSLocalizedString("Loading CHS Core Dict...", comment: "")
)
)
loadCoreLanguageModelFile(filenameSansExtension: "data-chs", langModel: &gLangModelCHS)
NotifierController.notify(
message: String(
format: "%@", NSLocalizedString("Core Dict loading complete.", comment: "")
)
)
}
} else if mode == InputMode.imeModeCHT {
DispatchQueue.global(qos: .userInitiated).async {
if !gLangModelCHT.isMiscDataLoaded() {
gLangModelCHT.loadMiscData(path: getBundleDataPath("data-zhuyinwen"))
}
if !gLangModelCHT.isSymbolDataLoaded() {
gLangModelCHT.loadSymbolData(path: getBundleDataPath("data-symbols"))
}
if !gLangModelCHT.isCNSDataLoaded() {
gLangModelCHT.loadCNSData(path: getBundleDataPath("char-kanji-cns"))
}
}
if !gLangModelCHT.isDataModelLoaded() {
NotifierController.notify(
message: String(
format: "%@", NSLocalizedString("Loading CHT Core Dict...", comment: "")
)
)
loadCoreLanguageModelFile(filenameSansExtension: "data-cht", langModel: &gLangModelCHT)
NotifierController.notify(
message: String(
format: "%@", NSLocalizedString("Core Dict loading complete.", comment: "")
)
)
}
}
}
public static func loadUserPhrases() {
gLangModelCHT.loadUserPhrases(
path: userPhrasesDataPath(InputMode.imeModeCHT),
filterPath: excludedPhrasesDataPath(InputMode.imeModeCHT)
)
gLangModelCHS.loadUserPhrases(
path: userPhrasesDataPath(InputMode.imeModeCHS),
filterPath: excludedPhrasesDataPath(InputMode.imeModeCHS)
)
gLangModelCHT.loadUserSymbolData(path: userSymbolDataPath(InputMode.imeModeCHT))
gLangModelCHS.loadUserSymbolData(path: userSymbolDataPath(InputMode.imeModeCHS))
}
public static func loadUserAssociatedPhrases() {
gLangModelCHT.loadUserAssociatedPhrases(
path: mgrLangModel.userAssociatedPhrasesDataPath(InputMode.imeModeCHT)
)
gLangModelCHS.loadUserAssociatedPhrases(
path: mgrLangModel.userAssociatedPhrasesDataPath(InputMode.imeModeCHS)
)
}
public static func loadUserPhraseReplacement() {
gLangModelCHT.loadPhraseReplacementMap(
path: mgrLangModel.phraseReplacementDataPath(InputMode.imeModeCHT)
)
gLangModelCHS.loadPhraseReplacementMap(
path: mgrLangModel.phraseReplacementDataPath(InputMode.imeModeCHS)
)
}
public static func checkIfUserPhraseExist(
userPhrase: String,
mode: InputMode,
key unigramKey: String
) -> Bool {
let unigrams: [Megrez.Unigram] =
(mode == InputMode.imeModeCHT)
? gLangModelCHT.unigramsFor(key: unigramKey) : gLangModelCHS.unigramsFor(key: unigramKey)
for unigram in unigrams {
if unigram.keyValue.value == userPhrase {
return true
}
}
return false
}
public static func setPhraseReplacementEnabled(_ state: Bool) {
gLangModelCHT.isPhraseReplacementEnabled = state
gLangModelCHS.isPhraseReplacementEnabled = state
}
public static func setCNSEnabled(_ state: Bool) {
gLangModelCHT.isCNSEnabled = state
gLangModelCHS.isCNSEnabled = state
}
public static func setSymbolEnabled(_ state: Bool) {
gLangModelCHT.isSymbolEnabled = state
gLangModelCHS.isSymbolEnabled = state
}
// MARK: - // MARK: -
static func getBundleDataPath(_ filenameSansExt: String) -> String { static func getBundleDataPath(_ filenameSansExt: String) -> String {
@ -80,7 +268,7 @@ import Cocoa
do { do {
try templateData.write(to: URL(fileURLWithPath: filePath)) try templateData.write(to: URL(fileURLWithPath: filePath))
} catch { } catch {
IME.prtDebugIntel("Failed to write file") IME.prtDebugIntel("Failed to write template data to: \(filePath)")
return false return false
} }
} }
@ -219,13 +407,15 @@ import Cocoa
// module shipped in the vChewing Phrase Editor. // module shipped in the vChewing Phrase Editor.
currentMarkedPhrase += "\t#𝙾𝚟𝚎𝚛𝚛𝚒𝚍𝚎" currentMarkedPhrase += "\t#𝙾𝚟𝚎𝚛𝚛𝚒𝚍𝚎"
} }
currentMarkedPhrase += "\n"
if let writeFile = FileHandle(forUpdatingAtPath: path), if let writeFile = FileHandle(forUpdatingAtPath: path),
let data = currentMarkedPhrase.data(using: .utf8) let data = currentMarkedPhrase.data(using: .utf8),
let endl = "\n".data(using: .utf8)
{ {
writeFile.seekToEndOfFile() writeFile.seekToEndOfFile()
writeFile.write(endl)
writeFile.write(data) writeFile.write(data)
writeFile.write(endl)
writeFile.closeFile() writeFile.closeFile()
} else { } else {
return false return false
@ -233,7 +423,9 @@ import Cocoa
// 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.
consolidate(givenFile: path, shouldCheckPragma: false) if !vChewing.LMConsolidator.consolidate(path: path, pragma: false) {
return false
}
// We use FSEventStream to monitor possible changes of the user phrase folder, hence the // We use FSEventStream to monitor possible changes of the user phrase folder, hence the
// lack of the needs of manually load data here unless FSEventStream is disabled by user. // lack of the needs of manually load data here unless FSEventStream is disabled by user.

View File

@ -1,40 +0,0 @@
// Copyright (c) 2011 and onwards The OpenVanilla Project (MIT License).
// All possible vChewing-specific modifications are of:
// (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 "LMInstantiator.h"
#import "UserOverrideModel.h"
#import "mgrLangModel.h"
NS_ASSUME_NONNULL_BEGIN
@interface mgrLangModel ()
@property(class, readonly, nonatomic) vChewing::LMInstantiator *lmCHT;
@property(class, readonly, nonatomic) vChewing::LMInstantiator *lmCHS;
@property(class, readonly, nonatomic) vChewing::UserOverrideModel *userOverrideModelCHS;
@property(class, readonly, nonatomic) vChewing::UserOverrideModel *userOverrideModelCHT;
@end
NS_ASSUME_NONNULL_END

View File

@ -1,110 +0,0 @@
// Copyright (c) 2011 and onwards The OpenVanilla Project (MIT License).
// All possible vChewing-specific modifications are of:
// (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.
*/
#ifndef BIGRAM_H_
#define BIGRAM_H_
#include <vector>
#include "KeyValuePair.h"
namespace Gramambular
{
class Bigram
{
public:
Bigram();
KeyValuePair preceedingKeyValue;
KeyValuePair keyValue;
double score;
bool operator==(const Bigram &another) const;
bool operator<(const Bigram &another) const;
};
inline std::ostream &operator<<(std::ostream &stream, const Bigram &gram)
{
std::streamsize p = stream.precision();
stream.precision(6);
stream << "(" << gram.keyValue << "|" << gram.preceedingKeyValue << "," << gram.score << ")";
stream.precision(p);
return stream;
}
inline std::ostream &operator<<(std::ostream &stream, const std::vector<Bigram> &grams)
{
stream << "[" << grams.size() << "]=>{";
size_t index = 0;
for (std::vector<Bigram>::const_iterator gi = grams.begin(); gi != grams.end(); ++gi, ++index)
{
stream << index << "=>";
stream << *gi;
if (gi + 1 != grams.end())
{
stream << ",";
}
}
stream << "}";
return stream;
}
inline Bigram::Bigram() : score(0.0)
{
}
inline bool Bigram::operator==(const Bigram &another) const
{
return preceedingKeyValue == another.preceedingKeyValue && keyValue == another.keyValue && score == another.score;
}
inline bool Bigram::operator<(const Bigram &another) const
{
if (preceedingKeyValue < another.preceedingKeyValue)
{
return true;
}
else if (preceedingKeyValue == another.preceedingKeyValue)
{
if (keyValue < another.keyValue)
{
return true;
}
else if (keyValue == another.keyValue)
{
return score < another.score;
}
return false;
}
return false;
}
} // namespace Gramambular
#endif

View File

@ -1,242 +0,0 @@
// Copyright (c) 2011 and onwards The OpenVanilla Project (MIT License).
// All possible vChewing-specific modifications are of:
// (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.
*/
#ifndef BLOCKREADINGBUILDER_H_
#define BLOCKREADINGBUILDER_H_
#include <string>
#include <vector>
#include "Grid.h"
#include "LanguageModel.h"
namespace Gramambular
{
class BlockReadingBuilder
{
public:
explicit BlockReadingBuilder(LanguageModel *lm);
void clear();
size_t length() const;
size_t cursorIndex() const;
void setCursorIndex(size_t newIndex);
void insertReadingAtCursor(const std::string &reading);
bool deleteReadingBeforeCursor(); // backspace
bool deleteReadingAfterCursor(); // delete
bool removeHeadReadings(size_t count);
void setJoinSeparator(const std::string &separator);
const std::string joinSeparator() const;
std::vector<std::string> readings() const;
Grid &grid();
protected:
void build();
static const std::string Join(std::vector<std::string>::const_iterator begin,
std::vector<std::string>::const_iterator end, const std::string &separator);
// 規定最多可以組成的詞的字數上限為 10
static const size_t MaximumBuildSpanLength = 10;
size_t m_cursorIndex;
std::vector<std::string> m_readings;
Grid m_grid;
LanguageModel *m_LM;
std::string m_joinSeparator;
};
inline BlockReadingBuilder::BlockReadingBuilder(LanguageModel *lm) : m_LM(lm), m_cursorIndex(0)
{
}
inline void BlockReadingBuilder::clear()
{
m_cursorIndex = 0;
m_readings.clear();
m_grid.clear();
}
inline size_t BlockReadingBuilder::length() const
{
return m_readings.size();
}
inline size_t BlockReadingBuilder::cursorIndex() const
{
return m_cursorIndex;
}
inline void BlockReadingBuilder::setCursorIndex(size_t newIndex)
{
m_cursorIndex = newIndex > m_readings.size() ? m_readings.size() : newIndex;
}
inline void BlockReadingBuilder::insertReadingAtCursor(const std::string &reading)
{
m_readings.insert(m_readings.begin() + m_cursorIndex, reading);
m_grid.expandGridByOneAtLocation(m_cursorIndex);
build();
m_cursorIndex++;
}
inline std::vector<std::string> BlockReadingBuilder::readings() const
{
return m_readings;
}
inline bool BlockReadingBuilder::deleteReadingBeforeCursor()
{
if (!m_cursorIndex)
{
return false;
}
m_readings.erase(m_readings.begin() + m_cursorIndex - 1, m_readings.begin() + m_cursorIndex);
m_cursorIndex--;
m_grid.shrinkGridByOneAtLocation(m_cursorIndex);
build();
return true;
}
inline bool BlockReadingBuilder::deleteReadingAfterCursor()
{
if (m_cursorIndex == m_readings.size())
{
return false;
}
m_readings.erase(m_readings.begin() + m_cursorIndex, m_readings.begin() + m_cursorIndex + 1);
m_grid.shrinkGridByOneAtLocation(m_cursorIndex);
build();
return true;
}
inline bool BlockReadingBuilder::removeHeadReadings(size_t count)
{
if (count > length())
{
return false;
}
for (size_t i = 0; i < count; i++)
{
if (m_cursorIndex)
{
m_cursorIndex--;
}
m_readings.erase(m_readings.begin(), m_readings.begin() + 1);
m_grid.shrinkGridByOneAtLocation(0);
build();
}
return true;
}
inline void BlockReadingBuilder::setJoinSeparator(const std::string &separator)
{
m_joinSeparator = separator;
}
inline const std::string BlockReadingBuilder::joinSeparator() const
{
return m_joinSeparator;
}
inline Grid &BlockReadingBuilder::grid()
{
return m_grid;
}
inline void BlockReadingBuilder::build()
{
if (!m_LM)
{
return;
}
size_t begin = 0;
size_t end = m_cursorIndex + MaximumBuildSpanLength;
if (m_cursorIndex < MaximumBuildSpanLength)
{
begin = 0;
}
else
{
begin = m_cursorIndex - MaximumBuildSpanLength;
}
if (end > m_readings.size())
{
end = m_readings.size();
}
for (size_t p = begin; p < end; p++)
{
for (size_t q = 1; q <= MaximumBuildSpanLength && p + q <= end; q++)
{
std::string combinedReading = Join(m_readings.begin() + p, m_readings.begin() + p + q, m_joinSeparator);
if (!m_grid.hasNodeAtLocationSpanningLengthMatchingKey(p, q, combinedReading))
{
std::vector<Unigram> unigrams = m_LM->unigramsForKey(combinedReading);
if (unigrams.size() > 0)
{
Node n(combinedReading, unigrams, std::vector<Bigram>());
m_grid.insertNode(n, p, q);
}
}
}
}
}
inline const std::string BlockReadingBuilder::Join(std::vector<std::string>::const_iterator begin,
std::vector<std::string>::const_iterator end,
const std::string &separator)
{
std::string result;
for (std::vector<std::string>::const_iterator iter = begin; iter != end;)
{
result += *iter;
++iter;
if (iter != end)
{
result += separator;
}
}
return result;
}
} // namespace Gramambular
#endif

View File

@ -1,313 +0,0 @@
// Copyright (c) 2011 and onwards The OpenVanilla Project (MIT License).
// All possible vChewing-specific modifications are of:
// (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.
*/
#ifndef GRID_H_
#define GRID_H_
#include <map>
#include <string>
#include <vector>
#include "NodeAnchor.h"
#include "Span.h"
namespace Gramambular
{
class Grid
{
public:
void clear();
void insertNode(const Node &node, size_t location, size_t spanningLength);
bool hasNodeAtLocationSpanningLengthMatchingKey(size_t location, size_t spanningLength, const std::string &key);
void expandGridByOneAtLocation(size_t location);
void shrinkGridByOneAtLocation(size_t location);
size_t width() const;
std::vector<NodeAnchor> nodesEndingAt(size_t location);
std::vector<NodeAnchor> nodesCrossingOrEndingAt(size_t location);
// "Freeze" the node with the unigram that represents the selected candidate
// value. After this, the node that contains the unigram will always be
// evaluated to that unigram, while all other overlapping nodes will be reset
// to their initial state (that is, if any of those nodes were "frozen" or
// fixed, they will be unfrozen.)
NodeAnchor fixNodeSelectedCandidate(size_t location, const std::string &value);
// Similar to fixNodeSelectedCandidate, but instead of "freezing" the node,
// only boost the unigram that represents the value with an overriding score.
// This has the same side effect as fixNodeSelectedCandidate, which is that
// all other overlapping nodes will be reset to their initial state.
void overrideNodeScoreForSelectedCandidate(size_t location, const std::string &value, float overridingScore);
std::string dumpDOT()
{
std::stringstream sst;
sst << "digraph {" << std::endl;
sst << "graph [ rankdir=LR ];" << std::endl;
sst << "BOS;" << std::endl;
for (size_t p = 0; p < m_spans.size(); p++)
{
Span &span = m_spans[p];
for (size_t ni = 0; ni <= span.maximumLength(); ni++)
{
Node *np = span.nodeOfLength(ni);
if (np)
{
if (!p)
{
sst << "BOS -> " << np->currentKeyValue().value << ";" << std::endl;
}
sst << np->currentKeyValue().value << ";" << std::endl;
if (p + ni < m_spans.size())
{
Span &dstSpan = m_spans[p + ni];
for (size_t q = 0; q <= dstSpan.maximumLength(); q++)
{
Node *dn = dstSpan.nodeOfLength(q);
if (dn)
{
sst << np->currentKeyValue().value << " -> " << dn->currentKeyValue().value << ";"
<< std::endl;
}
}
}
if (p + ni == m_spans.size())
{
sst << np->currentKeyValue().value << " -> "
<< "EOS;" << std::endl;
}
}
}
}
sst << "EOS;" << std::endl;
sst << "}";
return sst.str();
}
protected:
std::vector<Span> m_spans;
};
inline void Grid::clear()
{
m_spans.clear();
}
inline void Grid::insertNode(const Node &node, size_t location, size_t spanningLength)
{
if (location >= m_spans.size())
{
size_t diff = location - m_spans.size() + 1;
for (size_t i = 0; i < diff; i++)
{
m_spans.push_back(Span());
}
}
m_spans[location].insertNodeOfLength(node, spanningLength);
}
inline bool Grid::hasNodeAtLocationSpanningLengthMatchingKey(size_t location, size_t spanningLength,
const std::string &key)
{
if (location > m_spans.size())
{
return false;
}
const Node *n = m_spans[location].nodeOfLength(spanningLength);
if (!n)
{
return false;
}
return key == n->key();
}
inline void Grid::expandGridByOneAtLocation(size_t location)
{
if (!location || location == m_spans.size())
{
m_spans.insert(m_spans.begin() + location, Span());
}
else
{
m_spans.insert(m_spans.begin() + location, Span());
for (size_t i = 0; i < location; i++)
{
// zaps overlapping spans
m_spans[i].removeNodeOfLengthGreaterThan(location - i);
}
}
}
inline void Grid::shrinkGridByOneAtLocation(size_t location)
{
if (location >= m_spans.size())
{
return;
}
m_spans.erase(m_spans.begin() + location);
for (size_t i = 0; i < location; i++)
{
// zaps overlapping spans
m_spans[i].removeNodeOfLengthGreaterThan(location - i);
}
}
inline size_t Grid::width() const
{
return m_spans.size();
}
// macOS 10.6 開始的內建注音的游標前置選字風格
inline std::vector<NodeAnchor> Grid::nodesEndingAt(size_t location)
{
std::vector<NodeAnchor> result;
if (m_spans.size() && location <= m_spans.size())
{
for (size_t i = 0; i < location; i++)
{
Span &span = m_spans[i];
if (i + span.maximumLength() >= location)
{
Node *np = span.nodeOfLength(location - i);
if (np)
{
NodeAnchor na;
na.node = np;
na.location = i;
na.spanningLength = location - i;
result.push_back(na);
}
}
}
}
return result;
}
// Windows 版奇摩注音輸入法的游標後置的選字風格。
// 與微軟新注音相異的是,這個風格允許在詞的中間叫出候選字窗。
inline std::vector<NodeAnchor> Grid::nodesCrossingOrEndingAt(size_t location)
{
std::vector<NodeAnchor> result;
if (m_spans.size() && location <= m_spans.size())
{
for (size_t i = 0; i < location; i++)
{
Span &span = m_spans[i];
if (i + span.maximumLength() >= location)
{
for (size_t j = 1, m = span.maximumLength(); j <= m; j++)
{
if (i + j < location)
{
continue;
}
Node *np = span.nodeOfLength(j);
if (np)
{
NodeAnchor na;
na.node = np;
na.location = i;
na.spanningLength = location - i;
result.push_back(na);
}
}
}
}
}
return result;
}
// For nodes found at the location, fix their currently-selected candidate using
// the supplied string value.
inline NodeAnchor Grid::fixNodeSelectedCandidate(size_t location, const std::string &value)
{
std::vector<NodeAnchor> nodes = nodesCrossingOrEndingAt(location);
NodeAnchor node;
for (auto nodeAnchor : nodes)
{
auto candidates = nodeAnchor.node->candidates();
// Reset the candidate-fixed state of every node at the location.
const_cast<Node *>(nodeAnchor.node)->resetCandidate();
for (size_t i = 0, c = candidates.size(); i < c; ++i)
{
if (candidates[i].value == value)
{
const_cast<Node *>(nodeAnchor.node)->selectCandidateAtIndex(i);
node = nodeAnchor;
break;
}
}
}
return node;
}
inline void Grid::overrideNodeScoreForSelectedCandidate(size_t location, const std::string &value,
float overridingScore)
{
std::vector<NodeAnchor> nodes = nodesCrossingOrEndingAt(location);
for (auto nodeAnchor : nodes)
{
auto candidates = nodeAnchor.node->candidates();
// Reset the candidate-fixed state of every node at the location.
const_cast<Node *>(nodeAnchor.node)->resetCandidate();
for (size_t i = 0, c = candidates.size(); i < c; ++i)
{
if (candidates[i].value == value)
{
const_cast<Node *>(nodeAnchor.node)->selectFloatingCandidateAtIndex(i, overridingScore);
break;
}
}
}
}
} // namespace Gramambular
#endif

View File

@ -1,71 +0,0 @@
// Copyright (c) 2011 and onwards The OpenVanilla Project (MIT License).
// All possible vChewing-specific modifications are of:
// (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.
*/
#ifndef KEYVALUEPAIR_H_
#define KEYVALUEPAIR_H_
#include <ostream>
#include <string>
namespace Gramambular
{
class KeyValuePair
{
public:
std::string key;
std::string value;
bool operator==(const KeyValuePair &another) const;
bool operator<(const KeyValuePair &another) const;
};
inline std::ostream &operator<<(std::ostream &stream, const KeyValuePair &pair)
{
stream << "(" << pair.key << "," << pair.value << ")";
return stream;
}
inline bool KeyValuePair::operator==(const KeyValuePair &another) const
{
return key == another.key && value == another.value;
}
inline bool KeyValuePair::operator<(const KeyValuePair &another) const
{
if (key < another.key)
{
return true;
}
else if (key == another.key)
{
return value < another.value;
}
return false;
}
} // namespace Gramambular
#endif

View File

@ -1,249 +0,0 @@
// Copyright (c) 2011 and onwards The OpenVanilla Project (MIT License).
// All possible vChewing-specific modifications are of:
// (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.
*/
#ifndef NODE_H_
#define NODE_H_
#include <limits>
#include <map>
#include <string>
#include <vector>
#include "LanguageModel.h"
namespace Gramambular
{
class Node
{
public:
Node();
Node(const std::string &key, const std::vector<Unigram> &unigrams, const std::vector<Bigram> &bigrams);
void primeNodeWithPreceedingKeyValues(const std::vector<KeyValuePair> &keyValues);
bool isCandidateFixed() const;
const std::vector<KeyValuePair> &candidates() const;
void selectCandidateAtIndex(size_t index = 0, bool fix = true);
void resetCandidate();
void selectFloatingCandidateAtIndex(size_t index, double score);
const std::string &key() const;
double score() const;
double scoreForCandidate(const std::string &candidate) const;
const KeyValuePair currentKeyValue() const;
double highestUnigramScore() const;
protected:
const LanguageModel *m_LM;
std::string m_key;
double m_score;
std::vector<Unigram> m_unigrams;
std::vector<KeyValuePair> m_candidates;
std::map<std::string, size_t> m_valueUnigramIndexMap;
std::map<KeyValuePair, std::vector<Bigram>> m_preceedingGramBigramMap;
bool m_candidateFixed;
size_t m_selectedUnigramIndex;
friend std::ostream &operator<<(std::ostream &stream, const Node &node);
};
inline std::ostream &operator<<(std::ostream &stream, const Node &node)
{
stream << "(node,key:" << node.m_key << ",fixed:" << (node.m_candidateFixed ? "true" : "false")
<< ",selected:" << node.m_selectedUnigramIndex << "," << node.m_unigrams << ")";
return stream;
}
inline Node::Node() : m_candidateFixed(false), m_selectedUnigramIndex(0), m_score(0.0)
{
}
inline Node::Node(const std::string &key, const std::vector<Unigram> &unigrams, const std::vector<Bigram> &bigrams)
: m_key(key), m_unigrams(unigrams), m_candidateFixed(false), m_selectedUnigramIndex(0), m_score(0.0)
{
stable_sort(m_unigrams.begin(), m_unigrams.end(), Unigram::ScoreCompare);
if (m_unigrams.size())
{
m_score = m_unigrams[0].score;
}
size_t i = 0;
for (std::vector<Unigram>::const_iterator ui = m_unigrams.begin(); ui != m_unigrams.end(); ++ui)
{
m_valueUnigramIndexMap[(*ui).keyValue.value] = i;
i++;
m_candidates.push_back((*ui).keyValue);
}
for (std::vector<Bigram>::const_iterator bi = bigrams.begin(); bi != bigrams.end(); ++bi)
{
m_preceedingGramBigramMap[(*bi).preceedingKeyValue].push_back(*bi);
}
}
inline void Node::primeNodeWithPreceedingKeyValues(const std::vector<KeyValuePair> &keyValues)
{
size_t newIndex = m_selectedUnigramIndex;
double max = m_score;
if (!isCandidateFixed())
{
for (std::vector<KeyValuePair>::const_iterator kvi = keyValues.begin(); kvi != keyValues.end(); ++kvi)
{
std::map<KeyValuePair, std::vector<Bigram>>::const_iterator f = m_preceedingGramBigramMap.find(*kvi);
if (f != m_preceedingGramBigramMap.end())
{
const std::vector<Bigram> &bigrams = (*f).second;
for (std::vector<Bigram>::const_iterator bi = bigrams.begin(); bi != bigrams.end(); ++bi)
{
const Bigram &bigram = *bi;
if (bigram.score > max)
{
std::map<std::string, size_t>::const_iterator uf =
m_valueUnigramIndexMap.find((*bi).keyValue.value);
if (uf != m_valueUnigramIndexMap.end())
{
newIndex = (*uf).second;
max = bigram.score;
}
}
}
}
}
}
if (m_score != max)
{
m_score = max;
}
if (newIndex != m_selectedUnigramIndex)
{
m_selectedUnigramIndex = newIndex;
}
}
inline bool Node::isCandidateFixed() const
{
return m_candidateFixed;
}
inline const std::vector<KeyValuePair> &Node::candidates() const
{
return m_candidates;
}
inline void Node::selectCandidateAtIndex(size_t index, bool fix)
{
if (index >= m_unigrams.size())
{
m_selectedUnigramIndex = 0;
}
else
{
m_selectedUnigramIndex = index;
}
m_candidateFixed = fix;
m_score = 99;
}
inline void Node::resetCandidate()
{
m_selectedUnigramIndex = 0;
m_candidateFixed = 0;
if (m_unigrams.size())
{
m_score = m_unigrams[0].score;
}
}
inline void Node::selectFloatingCandidateAtIndex(size_t index, double score)
{
if (index >= m_unigrams.size())
{
m_selectedUnigramIndex = 0;
}
else
{
m_selectedUnigramIndex = index;
}
m_candidateFixed = false;
m_score = score;
}
inline const std::string &Node::key() const
{
return m_key;
}
inline double Node::score() const
{
return m_score;
}
inline double Node::scoreForCandidate(const std::string &candidate) const
{
for (auto unigram : m_unigrams)
{
if (unigram.keyValue.value == candidate)
{
return unigram.score;
}
}
return 0.0;
}
inline double Node::highestUnigramScore() const
{
if (m_unigrams.empty())
{
return 0.0;
}
return m_unigrams[0].score;
}
inline const KeyValuePair Node::currentKeyValue() const
{
if (m_selectedUnigramIndex >= m_unigrams.size())
{
return KeyValuePair();
}
else
{
return m_candidates[m_selectedUnigramIndex];
}
}
} // namespace Gramambular
#endif

View File

@ -1,75 +0,0 @@
// Copyright (c) 2011 and onwards The OpenVanilla Project (MIT License).
// All possible vChewing-specific modifications are of:
// (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.
*/
#ifndef NODEANCHOR_H_
#define NODEANCHOR_H_
#include <vector>
#include "Node.h"
namespace Gramambular
{
struct NodeAnchor
{
const Node *node = nullptr;
size_t location = 0;
size_t spanningLength = 0;
double accumulatedScore = 0.0;
};
inline std::ostream &operator<<(std::ostream &stream, const NodeAnchor &anchor)
{
stream << "{@(" << anchor.location << "," << anchor.spanningLength << "),";
if (anchor.node)
{
stream << *(anchor.node);
}
else
{
stream << "null";
}
stream << "}";
return stream;
}
inline std::ostream &operator<<(std::ostream &stream, const std::vector<NodeAnchor> &anchor)
{
for (std::vector<NodeAnchor>::const_iterator i = anchor.begin(); i != anchor.end(); ++i)
{
stream << *i;
if (i + 1 != anchor.end())
{
stream << "<-";
}
}
return stream;
}
} // namespace Gramambular
#endif

View File

@ -1,112 +0,0 @@
// Copyright (c) 2011 and onwards The OpenVanilla Project (MIT License).
// All possible vChewing-specific modifications are of:
// (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.
*/
#ifndef SPAN_H_
#define SPAN_H_
#include <map>
#include <set>
#include <sstream>
#include "Node.h"
namespace Gramambular
{
class Span
{
public:
void clear();
void insertNodeOfLength(const Node &node, size_t length);
void removeNodeOfLengthGreaterThan(size_t length);
Node *nodeOfLength(size_t length);
size_t maximumLength() const;
protected:
std::map<size_t, Node> m_lengthNodeMap;
size_t m_maximumLength = 0;
};
inline void Span::clear()
{
m_lengthNodeMap.clear();
m_maximumLength = 0;
}
inline void Span::insertNodeOfLength(const Node &node, size_t length)
{
m_lengthNodeMap[length] = node;
if (length > m_maximumLength)
{
m_maximumLength = length;
}
}
inline void Span::removeNodeOfLengthGreaterThan(size_t length)
{
if (length > m_maximumLength)
{
return;
}
size_t max = 0;
std::set<size_t> removeSet;
for (std::map<size_t, Node>::iterator i = m_lengthNodeMap.begin(), e = m_lengthNodeMap.end(); i != e; ++i)
{
if ((*i).first > length)
{
removeSet.insert((*i).first);
}
else
{
if ((*i).first > max)
{
max = (*i).first;
}
}
}
for (std::set<size_t>::iterator i = removeSet.begin(), e = removeSet.end(); i != e; ++i)
{
m_lengthNodeMap.erase(*i);
}
m_maximumLength = max;
}
inline Node *Span::nodeOfLength(size_t length)
{
std::map<size_t, Node>::iterator f = m_lengthNodeMap.find(length);
return f == m_lengthNodeMap.end() ? 0 : &(*f).second;
}
inline size_t Span::maximumLength() const
{
return m_maximumLength;
}
} // namespace Gramambular
#endif

View File

@ -1,108 +0,0 @@
// Copyright (c) 2011 and onwards The OpenVanilla Project (MIT License).
// All possible vChewing-specific modifications are of:
// (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.
*/
#ifndef UNIGRAM_H_
#define UNIGRAM_H_
#include <vector>
#include "KeyValuePair.h"
namespace Gramambular
{
class Unigram
{
public:
Unigram();
KeyValuePair keyValue;
double score;
bool operator==(const Unigram &another) const;
bool operator<(const Unigram &another) const;
static bool ScoreCompare(const Unigram &a, const Unigram &b);
};
inline std::ostream &operator<<(std::ostream &stream, const Unigram &gram)
{
std::streamsize p = stream.precision();
stream.precision(6);
stream << "(" << gram.keyValue << "," << gram.score << ")";
stream.precision(p);
return stream;
}
inline std::ostream &operator<<(std::ostream &stream, const std::vector<Unigram> &grams)
{
stream << "[" << grams.size() << "]=>{";
size_t index = 0;
for (std::vector<Unigram>::const_iterator gi = grams.begin(); gi != grams.end(); ++gi, ++index)
{
stream << index << "=>";
stream << *gi;
if (gi + 1 != grams.end())
{
stream << ",";
}
}
stream << "}";
return stream;
}
inline Unigram::Unigram() : score(0.0)
{
}
inline bool Unigram::operator==(const Unigram &another) const
{
return keyValue == another.keyValue && score == another.score;
}
inline bool Unigram::operator<(const Unigram &another) const
{
if (keyValue < another.keyValue)
{
return true;
}
else if (keyValue == another.keyValue)
{
return score < another.score;
}
return false;
}
inline bool Unigram::ScoreCompare(const Unigram &a, const Unigram &b)
{
return a.score > b.score;
}
} // namespace Gramambular
#endif

View File

@ -1,96 +0,0 @@
// Copyright (c) 2011 and onwards The OpenVanilla Project (MIT License).
// All possible vChewing-specific modifications are of:
// (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.
*/
#ifndef WALKER_H_
#define WALKER_H_
#include <algorithm>
#include <vector>
#include "Grid.h"
namespace Gramambular
{
class Walker
{
public:
explicit Walker(Grid *inGrid);
const std::vector<NodeAnchor> reverseWalk(size_t location, double accumulatedScore = 0.0);
protected:
Grid *m_grid;
};
inline Walker::Walker(Grid *inGrid) : m_grid(inGrid)
{
}
inline const std::vector<NodeAnchor> Walker::reverseWalk(size_t location, double accumulatedScore)
{
if (!location || location > m_grid->width())
{
return std::vector<NodeAnchor>();
}
std::vector<std::vector<NodeAnchor>> paths;
std::vector<NodeAnchor> nodes = m_grid->nodesEndingAt(location);
for (std::vector<NodeAnchor>::iterator ni = nodes.begin(); ni != nodes.end(); ++ni)
{
if (!(*ni).node)
{
continue;
}
(*ni).accumulatedScore = accumulatedScore + (*ni).node->score();
std::vector<NodeAnchor> path = reverseWalk(location - (*ni).spanningLength, (*ni).accumulatedScore);
path.insert(path.begin(), *ni);
paths.push_back(path);
}
if (!paths.size())
{
return std::vector<NodeAnchor>();
}
std::vector<NodeAnchor> *result = &*(paths.begin());
for (std::vector<std::vector<NodeAnchor>>::iterator pi = paths.begin(); pi != paths.end(); ++pi)
{
if ((*pi).back().accumulatedScore > result->back().accumulatedScore)
{
result = &*pi;
}
}
return *result;
}
} // namespace Gramambular
#endif

View File

@ -1,6 +1,5 @@
// Copyright (c) 2011 and onwards The OpenVanilla Project (MIT License). // Swiftified by (c) 2022 and onwards The vChewing Project (MIT-NTL License).
// All possible vChewing-specific modifications are of: // Rebranded from (c) Lukhnos Liu's C++ library "Gramambular" (MIT License).
// (c) 2021 and onwards The vChewing Project (MIT-NTL 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
@ -24,31 +23,5 @@ 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.
*/ */
#ifndef SYMBOLLM_H /// The namespace for this package.
#define SYMBOLLM_H public enum Megrez {}
#include "LanguageModel.h"
#include "UserPhrasesLM.h"
#include <iostream>
#include <map>
#include <string>
namespace vChewing
{
class SymbolLM : public UserPhrasesLM
{
public:
bool allowConsolidation() override
{
return false;
}
float overridedValue() override
{
return -13.0;
}
};
} // namespace vChewing
#endif

View File

@ -0,0 +1,146 @@
// Swiftified by (c) 2022 and onwards The vChewing Project (MIT-NTL License).
// Rebranded from (c) Lukhnos Liu's C++ library "Gramambular" (MIT License).
/*
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
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.
*/
extension Megrez {
public class BlockReadingBuilder {
let kMaximumBuildSpanLength = 10 // 10
var mutCursorIndex: Int = 0
var mutReadings: [String] = []
var mutGrid: Grid = .init()
var mutLM: LanguageModel
var mutJoinSeparator: String = ""
public init(lm: LanguageModel) {
mutLM = lm
}
public func clear() {
mutCursorIndex = 0
mutReadings.removeAll()
mutGrid.clear()
}
public func length() -> Int { mutReadings.count }
public func cursorIndex() -> Int { mutCursorIndex }
public func setCursorIndex(newIndex: Int) {
mutCursorIndex = min(newIndex, mutReadings.count)
}
public func insertReadingAtCursor(reading: String) {
mutReadings.insert(reading, at: mutCursorIndex)
mutGrid.expandGridByOneAt(location: mutCursorIndex)
build()
mutCursorIndex += 1
}
public func readings() -> [String] { mutReadings }
@discardableResult public func deleteReadingBeforeCursor() -> Bool {
if mutCursorIndex == 0 {
return false
}
mutReadings.remove(at: mutCursorIndex - 1)
mutCursorIndex -= 1
mutGrid.shrinkGridByOneAt(location: mutCursorIndex)
build()
return true
}
@discardableResult public func deleteReadingAfterCursor() -> Bool {
if mutCursorIndex == mutReadings.count {
return false
}
mutReadings.remove(at: mutCursorIndex)
mutGrid.shrinkGridByOneAt(location: mutCursorIndex)
build()
return true
}
@discardableResult public func removeHeadReadings(count: Int) -> Bool {
if count > length() {
return false
}
var i = 0
while i < count {
if mutCursorIndex != 0 {
mutCursorIndex -= 1
}
mutReadings.removeFirst()
mutGrid.shrinkGridByOneAt(location: 0)
build()
i += 1
}
return true
}
public func setJoinSeparator(separator: String) {
mutJoinSeparator = separator
}
public func joinSeparator() -> String { mutJoinSeparator }
public func grid() -> Grid { mutGrid }
public func build() {
// if (mutLM == nil) { return } // nil
let itrBegin: Int =
(mutCursorIndex < kMaximumBuildSpanLength) ? 0 : mutCursorIndex - kMaximumBuildSpanLength
let itrEnd: Int = min(mutCursorIndex + kMaximumBuildSpanLength, mutReadings.count)
var p = itrBegin
while p < itrEnd {
var q = 1
while q <= kMaximumBuildSpanLength, p + q <= itrEnd {
let strSlice = mutReadings[p..<(p + q)]
let combinedReading: String = join(slice: strSlice, separator: mutJoinSeparator)
if !mutGrid.hasMatchedNode(location: p, spanningLength: q, key: combinedReading) {
let unigrams: [Unigram] = mutLM.unigramsFor(key: combinedReading)
if !unigrams.isEmpty {
let n = Node(key: combinedReading, unigrams: unigrams)
mutGrid.insertNode(node: n, location: p, spanningLength: q)
}
}
q += 1
}
p += 1
}
}
public func join(slice strSlice: ArraySlice<String>, separator: String) -> String {
var arrResult: [String] = []
for value in strSlice {
arrResult.append(value)
}
return arrResult.joined(separator: separator)
}
}
}

View File

@ -0,0 +1,74 @@
// Swiftified by (c) 2022 and onwards The vChewing Project (MIT-NTL License).
// Rebranded from (c) Lukhnos Liu's C++ library "Gramambular" (MIT License).
/*
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
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.
*/
extension Megrez {
public class Walker {
var mutGrid: Grid
public init(grid: Megrez.Grid = Megrez.Grid()) {
mutGrid = grid
}
public func reverseWalk(at location: Int, score accumulatedScore: Double = 0.0) -> [NodeAnchor] {
if location == 0 || location > mutGrid.width() {
return [] as [NodeAnchor]
}
var paths: [[NodeAnchor]] = []
let nodes: [NodeAnchor] = mutGrid.nodesEndingAt(location: location)
for n in nodes {
var n = n
if n.node == nil {
continue
}
n.accumulatedScore = accumulatedScore + n.node!.score()
var path: [NodeAnchor] = reverseWalk(
at: location - n.spanningLength,
score: n.accumulatedScore
)
path.insert(n, at: 0)
paths.append(path)
}
if !paths.isEmpty {
if var result = paths.first {
for value in paths {
if let vLast = value.last, let rLast = result.last {
if vLast.accumulatedScore > rLast.accumulatedScore {
result = value
}
}
}
return result
}
}
return [] as [NodeAnchor]
}
}
}

View File

@ -0,0 +1,180 @@
// Swiftified by (c) 2022 and onwards The vChewing Project (MIT-NTL License).
// Rebranded from (c) Lukhnos Liu's C++ library "Gramambular" (MIT License).
/*
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
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.
*/
extension Megrez {
public class Grid {
var mutSpans: [Megrez.Span]
public init() {
mutSpans = [Megrez.Span]()
}
public func clear() {
mutSpans = [Megrez.Span]()
}
public func insertNode(node: Node, location: Int, spanningLength: Int) {
if location >= mutSpans.count {
let diff = location - mutSpans.count + 1
var i = 0
while i < diff {
mutSpans.append(Span())
i += 1
}
}
mutSpans[location].insert(node: node, length: spanningLength)
}
public func hasMatchedNode(location: Int, spanningLength: Int, key: String) -> Bool {
if location > mutSpans.count {
return false
}
let n = mutSpans[location].node(length: spanningLength)
return n == nil ? false : key == n?.key()
}
public func expandGridByOneAt(location: Int) {
mutSpans.append(Span())
if location > 0, location < mutSpans.count {
var i = 0
while i < location {
// zaps overlapping spans
mutSpans[i].removeNodeOfLengthGreaterThan(location - i)
i += 1
}
}
}
public func shrinkGridByOneAt(location: Int) {
if location >= mutSpans.count {
return
}
mutSpans.remove(at: location)
var i = 0
while i < location {
// zaps overlapping spans
mutSpans[i].removeNodeOfLengthGreaterThan(location - i)
i += 1
}
}
public func width() -> Int { mutSpans.count }
public func nodesEndingAt(location: Int) -> [NodeAnchor] {
var results: [NodeAnchor] = []
if !mutSpans.isEmpty, location <= mutSpans.count {
var i = 0
while i < location {
let span = mutSpans[i]
if i + span.maximumLength >= location {
if let np = span.node(length: location - i) {
results.append(
NodeAnchor(
node: np,
location: i,
spanningLength: location - i
)
)
}
}
i += 1
}
}
return results
}
public func nodesCrossingOrEndingAt(location: Int) -> [NodeAnchor] {
var results: [NodeAnchor] = []
if !mutSpans.isEmpty, location <= mutSpans.count {
var i = 0
while i < location {
let span = mutSpans[i]
if i + span.maximumLength >= location {
var j = 1
while j <= span.maximumLength {
if i + j < location {
j += 1
continue
}
if let np = span.node(length: j) {
results.append(
NodeAnchor(
node: np,
location: i,
spanningLength: location - i
)
)
}
j += 1
}
}
i += 1
}
}
return results
}
public func fixNodeSelectedCandidate(location: Int, value: String) -> NodeAnchor {
var node = NodeAnchor()
let nodes = nodesCrossingOrEndingAt(location: location)
for nodeAnchor in nodes {
// Reset the candidate-fixed state of every node at the location.
let candidates = nodeAnchor.node?.candidates() ?? []
nodeAnchor.node?.resetCandidate()
for (i, candidate) in candidates.enumerated() {
if candidate.value == value {
nodeAnchor.node?.selectCandidateAt(index: i)
node = nodeAnchor
break
}
}
}
return node
}
public func overrideNodeScoreForSelectedCandidate(location: Int, value: String, overridingScore: Double) {
for nodeAnchor in nodesCrossingOrEndingAt(location: location) {
var nodeAnchor = nodeAnchor
if let theNode = nodeAnchor.node {
let candidates = theNode.candidates()
// Reset the candidate-fixed state of every node at the location.
theNode.resetCandidate()
nodeAnchor.node = theNode
for (i, candidate) in candidates.enumerated() {
if candidate.value == value {
theNode.selectFloatingCandidateAt(index: i, score: overridingScore)
nodeAnchor.node = theNode
break
}
}
}
}
}
}
}

View File

@ -1,6 +1,5 @@
// Copyright (c) 2011 and onwards The OpenVanilla Project (MIT License). // Swiftified by (c) 2022 and onwards The vChewing Project (MIT-NTL License).
// All possible vChewing-specific modifications are of: // Rebranded from (c) Lukhnos Liu's C++ library "Gramambular" (MIT License).
// (c) 2021 and onwards The vChewing Project (MIT-NTL 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
@ -24,31 +23,14 @@ 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.
*/ */
#ifndef CNSLM_H extension Megrez {
#define CNSLM_H @frozen public struct NodeAnchor {
public var node: Node?
#include "LanguageModel.h" public var location: Int = 0
#include "UserPhrasesLM.h" public var spanningLength: Int = 0
#include <iostream> public var accumulatedScore: Double = 0.0
#include <map> public var keyLength: Int {
#include <string> node?.key().count ?? 0
}
namespace vChewing
{
class CNSLM : public UserPhrasesLM
{
public:
bool allowConsolidation() override
{
return false;
} }
float overridedValue() override
{
return -11.0;
} }
};
} // namespace vChewing
#endif

View File

@ -0,0 +1,74 @@
// Swiftified by (c) 2022 and onwards The vChewing Project (MIT-NTL License).
// Rebranded from (c) Lukhnos Liu's C++ library "Gramambular" (MIT License).
/*
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
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.
*/
extension Megrez {
@frozen public struct Span {
private var mutLengthNodeMap: [Int: Megrez.Node]
private var mutMaximumLength: Int
var maximumLength: Int {
mutMaximumLength
}
public init() {
mutLengthNodeMap = [:]
mutMaximumLength = 0
}
mutating func clear() {
mutLengthNodeMap.removeAll()
mutMaximumLength = 0
}
mutating func insert(node: Node, length: Int) {
mutLengthNodeMap[length] = node
if length > mutMaximumLength {
mutMaximumLength = length
}
}
mutating func removeNodeOfLengthGreaterThan(_ length: Int) {
if length > mutMaximumLength { return }
var max = 0
var removalList: [Int: Megrez.Node] = [:]
for key in mutLengthNodeMap.keys {
if key > length {
removalList[key] = mutLengthNodeMap[key]
} else {
if key > max {
max = key
}
}
}
for key in removalList.keys {
mutLengthNodeMap.removeValue(forKey: key)
}
mutMaximumLength = max
}
public func node(length: Int) -> Node? {
mutLengthNodeMap[length]
}
}
}

View File

@ -0,0 +1,161 @@
// Swiftified by (c) 2022 and onwards The vChewing Project (MIT-NTL License).
// Rebranded from (c) Lukhnos Liu's C++ library "Gramambular" (MIT License).
/*
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
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.
*/
extension Megrez {
public class Node {
let mutLM: LanguageModel
var mutKey: String
var mutScore: Double = 0
var mutUnigrams: [Unigram]
var mutCandidates: [KeyValuePair]
var mutValueUnigramIndexMap: [String: Int]
var mutPrecedingBigramMap: [KeyValuePair: [Megrez.Bigram]]
var mutCandidateFixed: Bool = false
var mutSelectedUnigramIndex: Int = 0
public init(key: String, unigrams: [Megrez.Unigram], bigrams: [Megrez.Bigram] = []) {
mutLM = LanguageModel()
mutKey = key
mutScore = 0
mutUnigrams = unigrams
mutCandidates = []
mutValueUnigramIndexMap = [:]
mutPrecedingBigramMap = [:]
mutCandidateFixed = false
mutSelectedUnigramIndex = 0
if bigrams == [] {
node(key: key, unigrams: unigrams, bigrams: bigrams)
} else {
node(key: key, unigrams: unigrams)
}
}
public func node(key: String, unigrams: [Megrez.Unigram], bigrams: [Megrez.Bigram] = []) {
var unigrams = unigrams
mutKey = key
unigrams.sort {
$0.score > $1.score
}
if !mutUnigrams.isEmpty {
mutScore = mutUnigrams[0].score
}
for (i, theGram) in unigrams.enumerated() {
mutValueUnigramIndexMap[theGram.keyValue.value] = i
mutCandidates.append(theGram.keyValue)
}
for gram in bigrams {
mutPrecedingBigramMap[gram.precedingKeyValue]?.append(gram)
}
}
public func primeNodeWith(precedingKeyValues: [KeyValuePair]) {
var newIndex = mutSelectedUnigramIndex
var max = mutScore
if !isCandidateFixed() {
for neta in precedingKeyValues {
let bigrams = mutPrecedingBigramMap[neta] ?? []
for bigram in bigrams {
if bigram.score > max {
if let valRetrieved = mutValueUnigramIndexMap[bigram.keyValue.value] {
newIndex = valRetrieved as Int
max = bigram.score
}
}
}
}
}
if mutScore != max {
mutScore = max
}
if mutSelectedUnigramIndex != newIndex {
mutSelectedUnigramIndex = newIndex
}
}
public func isCandidateFixed() -> Bool { mutCandidateFixed }
public func candidates() -> [KeyValuePair] { mutCandidates }
public func selectCandidateAt(index: Int = 0, fix: Bool = false) {
mutSelectedUnigramIndex = index >= mutUnigrams.count ? 0 : index
mutCandidateFixed = fix
mutScore = 99
}
public func resetCandidate() {
mutSelectedUnigramIndex = 0
mutCandidateFixed = false
if !mutUnigrams.isEmpty {
mutScore = mutUnigrams[0].score
}
}
public func selectFloatingCandidateAt(index: Int, score: Double) {
mutSelectedUnigramIndex = index >= mutUnigrams.count ? 0 : index
mutCandidateFixed = false
mutScore = score
}
public func key() -> String { mutKey }
public func score() -> Double { mutScore }
public func scoreFor(candidate: String) -> Double {
for unigram in mutUnigrams {
if unigram.keyValue.value == candidate {
return unigram.score
}
}
return 0.0
}
public func currentKeyValue() -> KeyValuePair {
mutSelectedUnigramIndex >= mutUnigrams.count ? KeyValuePair() : mutCandidates[mutSelectedUnigramIndex]
}
public func highestUnigramScore() -> Double {
mutUnigrams.isEmpty ? 0.0 : mutUnigrams[0].score
}
public static func == (lhs: Node, rhs: Node) -> Bool {
lhs.mutUnigrams == rhs.mutUnigrams && lhs.mutCandidates == rhs.mutCandidates
&& lhs.mutValueUnigramIndexMap == rhs.mutValueUnigramIndexMap
&& lhs.mutPrecedingBigramMap == rhs.mutPrecedingBigramMap
&& lhs.mutCandidateFixed == rhs.mutCandidateFixed
&& lhs.mutSelectedUnigramIndex == rhs.mutSelectedUnigramIndex
}
}
}

View File

@ -1,6 +1,5 @@
// Copyright (c) 2011 and onwards The OpenVanilla Project (MIT License). // Swiftified by (c) 2022 and onwards The vChewing Project (MIT-NTL License).
// All possible vChewing-specific modifications are of: // Rebranded from (c) Lukhnos Liu's C++ library "Gramambular" (MIT License).
// (c) 2021 and onwards The vChewing Project (MIT-NTL 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
@ -24,29 +23,22 @@ 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.
*/ */
#ifndef LANGUAGEMODEL_H_ extension Megrez {
#define LANGUAGEMODEL_H_ // 使
// Swift
open class LanguageModel {
public init() {}
#include <string> open func unigramsFor(key: String) -> [Megrez.Unigram] {
#include <vector> key.isEmpty ? [Megrez.Unigram]() : [Megrez.Unigram]()
#include "Bigram.h"
#include "Unigram.h"
namespace Gramambular
{
class LanguageModel
{
public:
virtual ~LanguageModel()
{
} }
virtual const std::vector<Bigram> bigramsForKeys(const std::string &preceedingKey, const std::string &key) = 0; open func bigramsForKeys(precedingKey: String, key: String) -> [Megrez.Bigram] {
virtual const std::vector<Unigram> unigramsForKey(const std::string &key) = 0; precedingKey == key ? [Megrez.Bigram]() : [Megrez.Bigram]()
virtual bool hasUnigramsForKey(const std::string &key) = 0; }
};
} // namespace Gramambular
#endif open func hasUnigramsFor(key: String) -> Bool {
key.count != 0
}
}
}

View File

@ -0,0 +1,74 @@
// Swiftified by (c) 2022 and onwards The vChewing Project (MIT-NTL License).
// Rebranded from (c) Lukhnos Liu's C++ library "Gramambular" (MIT License).
/*
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
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.
*/
extension Megrez {
@frozen public struct Bigram: Equatable {
public var keyValue: KeyValuePair
public var precedingKeyValue: KeyValuePair
public var score: Double
// var paired: String
public init(precedingKeyValue: KeyValuePair, keyValue: KeyValuePair, score: Double) {
self.keyValue = keyValue
self.precedingKeyValue = precedingKeyValue
self.score = score
// paired = "(" + keyValue.paired + "|" + precedingKeyValue.paired + "," + String(score) + ")"
}
public func hash(into hasher: inout Hasher) {
hasher.combine(keyValue)
hasher.combine(precedingKeyValue)
hasher.combine(score)
// hasher.combine(paired)
}
// static func getPairedBigrams(grams: [Bigram]) -> String {
// var arrOutputContent = [""]
// var index = 0
// for gram in grams {
// arrOutputContent.append(contentsOf: [String(index) + "=>" + gram.paired])
// index += 1
// }
// return "[" + String(grams.count) + "]=>{" + arrOutputContent.joined(separator: ",") + "}"
// }
public static func == (lhs: Bigram, rhs: Bigram) -> Bool {
lhs.precedingKeyValue == rhs.precedingKeyValue && lhs.keyValue == rhs.keyValue && lhs.score == rhs.score
}
public static func < (lhs: Bigram, rhs: Bigram) -> Bool {
lhs.precedingKeyValue < rhs.precedingKeyValue
|| (lhs.keyValue < rhs.keyValue || (lhs.keyValue == rhs.keyValue && lhs.keyValue < rhs.keyValue))
}
var description: String {
"\(keyValue):\(score)"
}
var debugDescription: String {
"Bigram(keyValue: \(keyValue), score: \(score))"
}
}
}

View File

@ -0,0 +1,75 @@
// Swiftified by (c) 2022 and onwards The vChewing Project (MIT-NTL License).
// Rebranded from (c) Lukhnos Liu's C++ library "Gramambular" (MIT License).
/*
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
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.
*/
extension Megrez {
@frozen public struct Unigram: Equatable {
public var keyValue: KeyValuePair
public var score: Double
// var paired: String
public init(keyValue: KeyValuePair, score: Double) {
self.keyValue = keyValue
self.score = score
// paired = "(" + keyValue.paired + "," + String(score) + ")"
}
public func hash(into hasher: inout Hasher) {
hasher.combine(keyValue)
hasher.combine(score)
// hasher.combine(paired)
}
//
public static func compareScore(a: Unigram, b: Unigram) -> Bool {
a.score > b.score
}
// static func getPairedUnigrams(grams: [Unigram]) -> String {
// var arrOutputContent = [""]
// var index = 0
// for gram in grams {
// arrOutputContent.append(contentsOf: [String(index) + "=>" + gram.paired])
// index += 1
// }
// return "[" + String(grams.count) + "]=>{" + arrOutputContent.joined(separator: ",") + "}"
// }
public static func == (lhs: Unigram, rhs: Unigram) -> Bool {
lhs.keyValue == rhs.keyValue && lhs.score == rhs.score
}
public static func < (lhs: Unigram, rhs: Unigram) -> Bool {
lhs.keyValue < rhs.keyValue || (lhs.keyValue == rhs.keyValue && lhs.keyValue < rhs.keyValue)
}
var description: String {
"\(keyValue):\(score)"
}
var debugDescription: String {
"Unigram(keyValue: \(keyValue), score: \(score))"
}
}
}

View File

@ -0,0 +1,72 @@
// Swiftified by (c) 2022 and onwards The vChewing Project (MIT-NTL License).
// Rebranded from (c) Lukhnos Liu's C++ library "Gramambular" (MIT License).
/*
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
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.
*/
extension Megrez {
@frozen public struct KeyValuePair: Equatable, Hashable, Comparable {
public var key: String
public var value: String
// public var paired: String
public init(key: String = "", value: String = "") {
self.key = key
self.value = value
// paired = "(" + key + "," + value + ")"
}
public func hash(into hasher: inout Hasher) {
hasher.combine(key)
hasher.combine(value)
// hasher.combine(paired)
}
public static func == (lhs: KeyValuePair, rhs: KeyValuePair) -> Bool {
lhs.key.count == rhs.key.count && lhs.value == rhs.value
}
public static func < (lhs: KeyValuePair, rhs: KeyValuePair) -> Bool {
(lhs.key.count < rhs.key.count) || (lhs.key.count == rhs.key.count && lhs.value < rhs.value)
}
public static func > (lhs: KeyValuePair, rhs: KeyValuePair) -> Bool {
(lhs.key.count > rhs.key.count) || (lhs.key.count == rhs.key.count && lhs.value > rhs.value)
}
public static func <= (lhs: KeyValuePair, rhs: KeyValuePair) -> Bool {
(lhs.key.count <= rhs.key.count) || (lhs.key.count == rhs.key.count && lhs.value <= rhs.value)
}
public static func >= (lhs: KeyValuePair, rhs: KeyValuePair) -> Bool {
(lhs.key.count >= rhs.key.count) || (lhs.key.count == rhs.key.count && lhs.value >= rhs.value)
}
public var description: String {
"(\(key), \(value))"
}
public var debugDescription: String {
"KeyValuePair(key: \(key), value: \(value))"
}
}
}

View File

@ -59,11 +59,11 @@ public class clsSFX: NSObject, NSSoundDelegate {
currentBeep = beep currentBeep = beep
} }
@objc public func sound(_: NSSound, didFinishPlaying _: Bool) { public func sound(_: NSSound, didFinishPlaying _: Bool) {
currentBeep = nil currentBeep = nil
} }
@objc static func beep() { static func beep() {
shared.beep() shared.beep()
} }
} }

View File

@ -57,6 +57,9 @@
"Edit User Symbol & Emoji Data…" = "Edit User Symbol & Emoji Data…"; "Edit User Symbol & Emoji Data…" = "Edit User Symbol & Emoji Data…";
"Choose your desired user data folder." = "Choose your desired user data folder."; "Choose your desired user data folder." = "Choose your desired user data folder.";
"Cursor is between \"%@\" and \"%@\"." = "Cursor is between \"%@\" and \"%@\"."; "Cursor is between \"%@\" and \"%@\"." = "Cursor is between \"%@\" and \"%@\".";
"Loading CHS Core Dict..." = "Loading CHS Core Dict...";
"Loading CHT Core Dict..." = "Loading CHT Core Dict...";
"Core Dict loading complete." = "Core Dict loading complete.";
// 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";

View File

@ -57,6 +57,9 @@
"Edit User Symbol & Emoji Data…" = "Edit User Symbol & Emoji Data…"; "Edit User Symbol & Emoji Data…" = "Edit User Symbol & Emoji Data…";
"Choose your desired user data folder." = "Choose your desired user data folder."; "Choose your desired user data folder." = "Choose your desired user data folder.";
"Cursor is between \"%@\" and \"%@\"." = "Cursor is between \"%@\" and \"%@\"."; "Cursor is between \"%@\" and \"%@\"." = "Cursor is between \"%@\" and \"%@\".";
"Loading CHS Core Dict..." = "Loading CHS Core Dict...";
"Loading CHT Core Dict..." = "Loading CHT Core Dict...";
"Core Dict loading complete." = "Core Dict loading complete.";
// 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";

View File

@ -57,6 +57,9 @@
"Edit User Symbol & Emoji Data…" = "ユーザー符号&絵文字辞書を編集…"; "Edit User Symbol & Emoji Data…" = "ユーザー符号&絵文字辞書を編集…";
"Choose your desired user data folder." = "欲しがるユーザー辞書フォルダをお選びください。"; "Choose your desired user data folder." = "欲しがるユーザー辞書フォルダをお選びください。";
"Cursor is between \"%@\" and \"%@\"." = "カーソルは「%@」と「%@」に間れ。"; "Cursor is between \"%@\" and \"%@\"." = "カーソルは「%@」と「%@」に間れ。";
"Loading CHS Core Dict..." = "簡体中国語核心辞書読込中…";
"Loading CHT Core Dict..." = "繁体中国語核心辞書読込中…";
"Core Dict loading complete." = "核心辞書読込完了";
// The followings are the category names used in the Symbol menu. // The followings are the category names used in the Symbol menu.
"catCommonSymbols" = "常用"; "catCommonSymbols" = "常用";

View File

@ -57,6 +57,9 @@
"Edit User Symbol & Emoji Data…" = "编辑自订符号&绘文字资料…"; "Edit User Symbol & Emoji Data…" = "编辑自订符号&绘文字资料…";
"Choose your desired user data folder." = "请选择您想指定的使用者语汇档案目录。"; "Choose your desired user data folder." = "请选择您想指定的使用者语汇档案目录。";
"Cursor is between \"%@\" and \"%@\"." = "游标介于「%@」与「%@」之间。"; "Cursor is between \"%@\" and \"%@\"." = "游标介于「%@」与「%@」之间。";
"Loading CHS Core Dict..." = "载入简体中文核心辞典…";
"Loading CHT Core Dict..." = "载入繁体中文核心辞典…";
"Core Dict loading complete." = "核心辞典载入完毕";
// The followings are the category names used in the Symbol menu. // The followings are the category names used in the Symbol menu.
"catCommonSymbols" = "常用"; "catCommonSymbols" = "常用";

View File

@ -57,6 +57,9 @@
"Edit User Symbol & Emoji Data…" = "編輯自訂符號&繪文字資料…"; "Edit User Symbol & Emoji Data…" = "編輯自訂符號&繪文字資料…";
"Choose your desired user data folder." = "請選擇您想指定的使用者語彙檔案目錄。"; "Choose your desired user data folder." = "請選擇您想指定的使用者語彙檔案目錄。";
"Cursor is between \"%@\" and \"%@\"." = "游標介於「%@」與「%@」之間。"; "Cursor is between \"%@\" and \"%@\"." = "游標介於「%@」與「%@」之間。";
"Loading CHS Core Dict..." = "載入簡體中文核心辭典…";
"Loading CHT Core Dict..." = "載入繁體中文核心辭典…";
"Core Dict loading complete." = "核心辭典載入完畢";
// The followings are the category names used in the Symbol menu. // The followings are the category names used in the Symbol menu.
"catCommonSymbols" = "常用"; "catCommonSymbols" = "常用";

View File

@ -26,10 +26,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import Cocoa import Cocoa
@objc(VTCandidateKeyLabel)
public class CandidateKeyLabel: NSObject { public class CandidateKeyLabel: NSObject {
@objc public private(set) var key: String public private(set) var key: String
@objc public private(set) var displayedText: String public private(set) var displayedText: String
public init(key: String, displayedText: String) { public init(key: String, displayedText: String) {
self.key = key self.key = key
@ -38,7 +37,6 @@ public class CandidateKeyLabel: NSObject {
} }
} }
@objc(ctlCandidateDelegate)
public protocol ctlCandidateDelegate: AnyObject { public protocol ctlCandidateDelegate: AnyObject {
func candidateCountForController(_ controller: ctlCandidate) -> UInt func candidateCountForController(_ controller: ctlCandidate) -> UInt
func ctlCandidate(_ controller: ctlCandidate, candidateAtIndex index: UInt) func ctlCandidate(_ controller: ctlCandidate, candidateAtIndex index: UInt)
@ -48,16 +46,15 @@ public protocol ctlCandidateDelegate: AnyObject {
) )
} }
@objc(ctlCandidate)
public class ctlCandidate: NSWindowController { public class ctlCandidate: NSWindowController {
@objc public weak var delegate: ctlCandidateDelegate? { public weak var delegate: ctlCandidateDelegate? {
didSet { didSet {
reloadData() reloadData()
} }
} }
@objc public var selectedCandidateIndex: UInt = .max public var selectedCandidateIndex: UInt = .max
@objc public var visible: Bool = false { public var visible: Bool = false {
didSet { didSet {
NSObject.cancelPreviousPerformRequests(withTarget: self) NSObject.cancelPreviousPerformRequests(withTarget: self)
if visible { if visible {
@ -68,7 +65,7 @@ public class ctlCandidate: NSWindowController {
} }
} }
@objc public var windowTopLeftPoint: NSPoint { public var windowTopLeftPoint: NSPoint {
get { get {
guard let frameRect = window?.frame else { guard let frameRect = window?.frame else {
return NSPoint.zero return NSPoint.zero
@ -82,36 +79,36 @@ public class ctlCandidate: NSWindowController {
} }
} }
@objc public var keyLabels: [CandidateKeyLabel] = ["1", "2", "3", "4", "5", "6", "7", "8", "9"] public var keyLabels: [CandidateKeyLabel] = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
.map { .map {
CandidateKeyLabel(key: $0, displayedText: $0) CandidateKeyLabel(key: $0, displayedText: $0)
} }
@objc public var keyLabelFont: NSFont = NSFont.monospacedDigitSystemFont( public var keyLabelFont: NSFont = NSFont.monospacedDigitSystemFont(
ofSize: 14, weight: .medium ofSize: 14, weight: .medium
) )
@objc public var candidateFont: NSFont = NSFont.systemFont(ofSize: 18) public var candidateFont: NSFont = NSFont.systemFont(ofSize: 18)
@objc public var tooltip: String = "" public var tooltip: String = ""
@objc public func reloadData() {} public func reloadData() {}
@objc public func showNextPage() -> Bool { public func showNextPage() -> Bool {
false false
} }
@objc public func showPreviousPage() -> Bool { public func showPreviousPage() -> Bool {
false false
} }
@objc public func highlightNextCandidate() -> Bool { public func highlightNextCandidate() -> Bool {
false false
} }
@objc public func highlightPreviousCandidate() -> Bool { public func highlightPreviousCandidate() -> Bool {
false false
} }
@objc public func candidateIndexAtKeyLabelIndex(_: UInt) -> UInt { public func candidateIndexAtKeyLabelIndex(_: UInt) -> UInt {
UInt.max UInt.max
} }
@ -125,7 +122,6 @@ public class ctlCandidate: NSWindowController {
/// - windowTopLeftPoint: The given location. /// - windowTopLeftPoint: The given location.
/// - height: The height that helps the window not to be out of the bottom /// - height: The height that helps the window not to be out of the bottom
/// of a screen. /// of a screen.
@objc(setWindowTopLeftPoint:bottomOutOfScreenAdjustmentHeight:)
public func set(windowTopLeftPoint: NSPoint, bottomOutOfScreenAdjustmentHeight height: CGFloat) { public func set(windowTopLeftPoint: NSPoint, bottomOutOfScreenAdjustmentHeight height: CGFloat) {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()) { DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()) {
self.doSet( self.doSet(

View File

@ -87,7 +87,7 @@ public class NotifierController: NSWindowController, NotifierWindowDelegate {
private static var instanceCount = 0 private static var instanceCount = 0
private static var lastLocation = NSPoint.zero private static var lastLocation = NSPoint.zero
@objc public static func notify(message: String, stay: Bool = false) { public static func notify(message: String, stay: Bool = false) {
let controller = NotifierController() let controller = NotifierController()
controller.message = message controller.message = message
controller.shouldStay = stay controller.shouldStay = stay

View File

@ -64,7 +64,6 @@ public class TooltipController: NSWindowController {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
@objc(showTooltip:atPoint:)
public func show(tooltip: String, at point: NSPoint) { public func show(tooltip: String, at point: NSPoint) {
messageTextField.textColor = TooltipController.textColor messageTextField.textColor = TooltipController.textColor
messageTextField.backgroundColor = TooltipController.backgroundColor messageTextField.backgroundColor = TooltipController.backgroundColor

View File

@ -26,7 +26,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import Cocoa import Cocoa
@objc(AboutWindow) class ctlAboutWindow: NSWindowController { class ctlAboutWindow: NSWindowController {
@IBOutlet var appVersionLabel: NSTextField! @IBOutlet var appVersionLabel: NSTextField!
@IBOutlet var appCopyrightLabel: NSTextField! @IBOutlet var appCopyrightLabel: NSTextField!
@IBOutlet var appEULAContent: NSTextView! @IBOutlet var appEULAContent: NSTextView!

View File

@ -26,13 +26,12 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import Cocoa import Cocoa
@objc protocol ctlNonModalAlertWindowDelegate: AnyObject { protocol ctlNonModalAlertWindowDelegate: AnyObject {
func ctlNonModalAlertWindowDidConfirm(_ controller: ctlNonModalAlertWindow) func ctlNonModalAlertWindowDidConfirm(_ controller: ctlNonModalAlertWindow)
func ctlNonModalAlertWindowDidCancel(_ controller: ctlNonModalAlertWindow) func ctlNonModalAlertWindowDidCancel(_ controller: ctlNonModalAlertWindow)
} }
class ctlNonModalAlertWindow: NSWindowController { class ctlNonModalAlertWindow: NSWindowController {
@objc(sharedInstance)
static let shared = ctlNonModalAlertWindow(windowNibName: "frmNonModalAlertWindow") static let shared = ctlNonModalAlertWindow(windowNibName: "frmNonModalAlertWindow")
@IBOutlet var titleTextField: NSTextField! @IBOutlet var titleTextField: NSTextField!
@ -41,7 +40,7 @@ class ctlNonModalAlertWindow: NSWindowController {
@IBOutlet var cancelButton: NSButton! @IBOutlet var cancelButton: NSButton!
weak var delegate: ctlNonModalAlertWindowDelegate? weak var delegate: ctlNonModalAlertWindowDelegate?
@objc func show( func show(
title: String, content: String, confirmButtonTitle: String, cancelButtonTitle: String?, title: String, content: String, confirmButtonTitle: String, cancelButtonTitle: String?,
cancelAsDefault: Bool, delegate: ctlNonModalAlertWindowDelegate? cancelAsDefault: Bool, delegate: ctlNonModalAlertWindowDelegate?
) { ) {

View File

@ -30,7 +30,7 @@ import Cocoa
// Please note that the class should be exposed using the same class name // Please note that the class should be exposed using the same class name
// in Objective-C in order to let IMK to see the same class name as // in Objective-C in order to let IMK to see the same class name as
// the "InputMethodServerPreferencesWindowControllerClass" in Info.plist. // the "InputMethodServerPreferencesWindowControllerClass" in Info.plist.
@objc(ctlPrefWindow) class ctlPrefWindow: NSWindowController { class ctlPrefWindow: NSWindowController {
@IBOutlet var fontSizePopUpButton: NSPopUpButton! @IBOutlet var fontSizePopUpButton: NSPopUpButton!
@IBOutlet var uiLanguageButton: NSPopUpButton! @IBOutlet var uiLanguageButton: NSPopUpButton!
@IBOutlet var basicKeyboardLayoutButton: NSPopUpButton! @IBOutlet var basicKeyboardLayoutButton: NSPopUpButton!

View File

@ -31,9 +31,6 @@ class WindowController: NSWindowController, NSWindowDelegate {
required init?(coder aDecoder: NSCoder) { required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder) super.init(coder: aDecoder)
/** NSWindows loaded from the storyboard will be cascaded
based on the original frame of the window in the storyboard.
*/
shouldCascadeWindows = true shouldCascadeWindows = true
} }
} }

View File

@ -7,6 +7,7 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
5B00A230282011980058E5DB /* lmLite.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B00A22F282011980058E5DB /* lmLite.swift */; };
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 */; };
5B27AD6A27CB1F9B000ED75B /* data-symbols.txt in Resources */ = {isa = PBXBuildFile; fileRef = 5B27AD6827CB1F9B000ED75B /* data-symbols.txt */; }; 5B27AD6A27CB1F9B000ED75B /* data-symbols.txt in Resources */ = {isa = PBXBuildFile; fileRef = 5B27AD6827CB1F9B000ED75B /* data-symbols.txt */; };
@ -14,10 +15,23 @@
5B2DB16F27AF6891006D874E /* data-chs.txt in Resources */ = {isa = PBXBuildFile; fileRef = 5B2DB16D27AF6891006D874E /* data-chs.txt */; }; 5B2DB16F27AF6891006D874E /* data-chs.txt in Resources */ = {isa = PBXBuildFile; fileRef = 5B2DB16D27AF6891006D874E /* data-chs.txt */; };
5B2DB17027AF6891006D874E /* data-cht.txt in Resources */ = {isa = PBXBuildFile; fileRef = 5B2DB16E27AF6891006D874E /* data-cht.txt */; }; 5B2DB17027AF6891006D874E /* data-cht.txt in Resources */ = {isa = PBXBuildFile; fileRef = 5B2DB16E27AF6891006D874E /* data-cht.txt */; };
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 */; };
5B38F59B281E2E49007D5F5D /* 7_KeyValuePair.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1815FC0EB100ABF4B3 /* 7_KeyValuePair.swift */; };
5B38F59C281E2E49007D5F5D /* 2_Grid.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1715FC0EB100ABF4B3 /* 2_Grid.swift */; };
5B38F59D281E2E49007D5F5D /* 4_Node.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1A15FC0EB100ABF4B3 /* 4_Node.swift */; };
5B38F59E281E2E49007D5F5D /* 6_Bigram.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1415FC0EB100ABF4B3 /* 6_Bigram.swift */; };
5B38F59F281E2E49007D5F5D /* 3_NodeAnchor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1B15FC0EB100ABF4B3 /* 3_NodeAnchor.swift */; };
5B38F5A0281E2E49007D5F5D /* 1_Walker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1E15FC0EB100ABF4B3 /* 1_Walker.swift */; };
5B38F5A1281E2E49007D5F5D /* 1_BlockReadingBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1515FC0EB100ABF4B3 /* 1_BlockReadingBuilder.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 */; };
5B38F5A4281E2E49007D5F5D /* 5_LanguageModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1915FC0EB100ABF4B3 /* 5_LanguageModel.swift */; };
5B407153281F94E6009C24CB /* Composer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B407152281F94E6009C24CB /* Composer.mm */; };
5B40730C281672610023DFFF /* lmAssociates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B407309281672610023DFFF /* lmAssociates.swift */; };
5B40730D281672610023DFFF /* lmReplacements.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B40730A281672610023DFFF /* lmReplacements.swift */; };
5B5E535227EF261400C6AA1E /* IME.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B5E535127EF261400C6AA1E /* IME.swift */; }; 5B5E535227EF261400C6AA1E /* IME.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B5E535127EF261400C6AA1E /* IME.swift */; };
5B61B0CA280BEFD4002E3CFA /* KeyHandler_Misc.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B61B0C9280BEFD4002E3CFA /* KeyHandler_Misc.swift */; }; 5B61B0CA280BEFD4002E3CFA /* KeyHandler_Misc.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B61B0C9280BEFD4002E3CFA /* KeyHandler_Misc.swift */; };
5B62A32927AE77D100A19448 /* FSEventStreamHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A32827AE77D100A19448 /* FSEventStreamHelper.swift */; }; 5B62A32927AE77D100A19448 /* FSEventStreamHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A32827AE77D100A19448 /* FSEventStreamHelper.swift */; };
5B62A32F27AE78B000A19448 /* CoreLM.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A32D27AE78B000A19448 /* CoreLM.mm */; };
5B62A33227AE792F00A19448 /* InputSourceHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A33127AE792F00A19448 /* InputSourceHelper.swift */; }; 5B62A33227AE792F00A19448 /* InputSourceHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A33127AE792F00A19448 /* InputSourceHelper.swift */; };
5B62A33627AE795800A19448 /* mgrPrefs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A33527AE795800A19448 /* mgrPrefs.swift */; }; 5B62A33627AE795800A19448 /* mgrPrefs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A33527AE795800A19448 /* mgrPrefs.swift */; };
5B62A33827AE79CD00A19448 /* NSStringUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A33727AE79CD00A19448 /* NSStringUtils.swift */; }; 5B62A33827AE79CD00A19448 /* NSStringUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A33727AE79CD00A19448 /* NSStringUtils.swift */; };
@ -34,6 +48,10 @@
5B782EC4280C243C007276DE /* KeyHandler_HandleCandidate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B782EC3280C243C007276DE /* KeyHandler_HandleCandidate.swift */; }; 5B782EC4280C243C007276DE /* KeyHandler_HandleCandidate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B782EC3280C243C007276DE /* KeyHandler_HandleCandidate.swift */; };
5B7BC4B027AFFBE800F66C24 /* frmPrefWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5B7BC4AE27AFFBE800F66C24 /* frmPrefWindow.xib */; }; 5B7BC4B027AFFBE800F66C24 /* frmPrefWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5B7BC4AE27AFFBE800F66C24 /* frmPrefWindow.xib */; };
5B7F225D2808501000DDD3CB /* KeyHandler_HandleInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B7F225C2808501000DDD3CB /* KeyHandler_HandleInput.swift */; }; 5B7F225D2808501000DDD3CB /* KeyHandler_HandleInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B7F225C2808501000DDD3CB /* KeyHandler_HandleInput.swift */; };
5B949BD92816DC5400D87B5D /* LineReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B949BD82816DC5400D87B5D /* LineReader.swift */; };
5B949BDB2816DDBC00D87B5D /* LMConsolidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B949BDA2816DDBC00D87B5D /* LMConsolidator.swift */; };
5BA0DF312817857D009E73BB /* lmUserOverride.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA0DF2E2817857D009E73BB /* lmUserOverride.swift */; };
5BA0DF322817857D009E73BB /* lmCore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA0DF2F2817857D009E73BB /* lmCore.swift */; };
5BA9FD0F27FEDB6B002DE248 /* suiPrefPaneGeneral.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA9FD0A27FEDB6B002DE248 /* suiPrefPaneGeneral.swift */; }; 5BA9FD0F27FEDB6B002DE248 /* suiPrefPaneGeneral.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA9FD0A27FEDB6B002DE248 /* suiPrefPaneGeneral.swift */; };
5BA9FD1027FEDB6B002DE248 /* suiPrefPaneKeyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA9FD0B27FEDB6B002DE248 /* suiPrefPaneKeyboard.swift */; }; 5BA9FD1027FEDB6B002DE248 /* suiPrefPaneKeyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA9FD0B27FEDB6B002DE248 /* suiPrefPaneKeyboard.swift */; };
5BA9FD1127FEDB6B002DE248 /* ctlPrefUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA9FD0C27FEDB6B002DE248 /* ctlPrefUI.swift */; }; 5BA9FD1127FEDB6B002DE248 /* ctlPrefUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA9FD0C27FEDB6B002DE248 /* ctlPrefUI.swift */; };
@ -66,6 +84,8 @@
5BBBB77627AED70B0023B93A /* MenuIcon-TCVIM.png in Resources */ = {isa = PBXBuildFile; fileRef = 5BBBB77227AED70B0023B93A /* MenuIcon-TCVIM.png */; }; 5BBBB77627AED70B0023B93A /* MenuIcon-TCVIM.png in Resources */ = {isa = PBXBuildFile; fileRef = 5BBBB77227AED70B0023B93A /* MenuIcon-TCVIM.png */; };
5BBBB77A27AEDC690023B93A /* clsSFX.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BBBB77927AEDC690023B93A /* clsSFX.swift */; }; 5BBBB77A27AEDC690023B93A /* clsSFX.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BBBB77927AEDC690023B93A /* clsSFX.swift */; };
5BC2652227E04B7E00700291 /* uninstall.sh in Resources */ = {isa = PBXBuildFile; fileRef = 5BC2652127E04B7B00700291 /* uninstall.sh */; }; 5BC2652227E04B7E00700291 /* uninstall.sh in Resources */ = {isa = PBXBuildFile; fileRef = 5BC2652127E04B7B00700291 /* uninstall.sh */; };
5BD0113B28180D6100609769 /* LMInstantiator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BD0113A28180D6100609769 /* LMInstantiator.swift */; };
5BD0113D2818543900609769 /* KeyHandler_Core.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BD0113C2818543900609769 /* KeyHandler_Core.swift */; };
5BD05B8127B22F3C004C4F1D /* char-kanji-cns.txt in Resources */ = {isa = PBXBuildFile; fileRef = 5BD05B8027B22F3C004C4F1D /* char-kanji-cns.txt */; }; 5BD05B8127B22F3C004C4F1D /* char-kanji-cns.txt in Resources */ = {isa = PBXBuildFile; fileRef = 5BD05B8027B22F3C004C4F1D /* char-kanji-cns.txt */; };
5BD05BCA27B2A43D004C4F1D /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6A2E40F5253A69DA00D1AE1D /* Images.xcassets */; }; 5BD05BCA27B2A43D004C4F1D /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6A2E40F5253A69DA00D1AE1D /* Images.xcassets */; };
5BD05C5D27B2BBA9004C4F1D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5BD05C5B27B2BBA9004C4F1D /* Main.storyboard */; }; 5BD05C5D27B2BBA9004C4F1D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5BD05C5B27B2BBA9004C4F1D /* Main.storyboard */; };
@ -78,7 +98,6 @@
5BDCBB2E27B4E67A00D0CC59 /* vChewingPhraseEditor.app in Resources */ = {isa = PBXBuildFile; fileRef = 5BD05BB827B2A429004C4F1D /* vChewingPhraseEditor.app */; }; 5BDCBB2E27B4E67A00D0CC59 /* vChewingPhraseEditor.app in Resources */ = {isa = PBXBuildFile; fileRef = 5BD05BB827B2A429004C4F1D /* vChewingPhraseEditor.app */; };
5BE78BD927B3775B005EA1BE /* ctlAboutWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BE78BD827B37750005EA1BE /* ctlAboutWindow.swift */; }; 5BE78BD927B3775B005EA1BE /* ctlAboutWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BE78BD827B37750005EA1BE /* ctlAboutWindow.swift */; };
5BE78BDD27B3776D005EA1BE /* frmAboutWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5BE78BDA27B37764005EA1BE /* frmAboutWindow.xib */; }; 5BE78BDD27B3776D005EA1BE /* frmAboutWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5BE78BDA27B37764005EA1BE /* frmAboutWindow.xib */; };
5BE78BE027B38804005EA1BE /* LMConsolidator.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A32727AE77BB00A19448 /* LMConsolidator.mm */; };
5BF8423127BAA942008E7E4C /* vChewingKanjiConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BF8423027BAA942008E7E4C /* vChewingKanjiConverter.swift */; }; 5BF8423127BAA942008E7E4C /* vChewingKanjiConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BF8423027BAA942008E7E4C /* vChewingKanjiConverter.swift */; };
6A0D4F4515FC0EB100ABF4B3 /* Mandarin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F2015FC0EB100ABF4B3 /* Mandarin.cpp */; }; 6A0D4F4515FC0EB100ABF4B3 /* Mandarin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F2015FC0EB100ABF4B3 /* Mandarin.cpp */; };
6A187E2616004C5900466B2E /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6A187E2816004C5900466B2E /* MainMenu.xib */; }; 6A187E2616004C5900466B2E /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6A187E2816004C5900466B2E /* MainMenu.xib */; };
@ -89,25 +108,16 @@
6ACA41FC15FC1D9000935EF6 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6ACA41EE15FC1D9000935EF6 /* Localizable.strings */; }; 6ACA41FC15FC1D9000935EF6 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6ACA41EE15FC1D9000935EF6 /* Localizable.strings */; };
6ACA41FD15FC1D9000935EF6 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6ACA41F015FC1D9000935EF6 /* MainMenu.xib */; }; 6ACA41FD15FC1D9000935EF6 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6ACA41F015FC1D9000935EF6 /* MainMenu.xib */; };
6ACA420215FC1E5200935EF6 /* vChewing.app in Resources */ = {isa = PBXBuildFile; fileRef = 6A0D4EA215FC0D2D00ABF4B3 /* vChewing.app */; }; 6ACA420215FC1E5200935EF6 /* vChewing.app in Resources */ = {isa = PBXBuildFile; fileRef = 6A0D4EA215FC0D2D00ABF4B3 /* vChewing.app */; };
6ACC3D3F27914F2400F1B140 /* KeyValueBlobReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6ACC3D3E27914F2400F1B140 /* KeyValueBlobReader.cpp */; };
6ACC3D442793701600F1B140 /* ParselessPhraseDB.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6ACC3D402793701600F1B140 /* ParselessPhraseDB.cpp */; };
6ACC3D452793701600F1B140 /* ParselessLM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6ACC3D422793701600F1B140 /* ParselessLM.cpp */; };
D41355D8278D74B5005E5CBD /* mgrLangModel.mm in Sources */ = {isa = PBXBuildFile; fileRef = D41355D7278D7409005E5CBD /* mgrLangModel.mm */; };
D41355DB278E6D17005E5CBD /* LMInstantiator.mm in Sources */ = {isa = PBXBuildFile; fileRef = D41355D9278E6D17005E5CBD /* LMInstantiator.mm */; };
D41355DE278EA3ED005E5CBD /* UserPhrasesLM.mm in Sources */ = {isa = PBXBuildFile; fileRef = D41355DC278EA3ED005E5CBD /* UserPhrasesLM.mm */; };
D427F76C278CA2B0004A2160 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D427F76B278CA1BA004A2160 /* AppDelegate.swift */; }; D427F76C278CA2B0004A2160 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D427F76B278CA1BA004A2160 /* AppDelegate.swift */; };
D44FB74D2792189A003C80A6 /* PhraseReplacementMap.mm in Sources */ = {isa = PBXBuildFile; fileRef = D44FB74B2792189A003C80A6 /* PhraseReplacementMap.mm */; };
D456576E279E4F7B00DF6BC9 /* InputHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = D456576D279E4F7B00DF6BC9 /* InputHandler.swift */; }; D456576E279E4F7B00DF6BC9 /* InputHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = D456576D279E4F7B00DF6BC9 /* InputHandler.swift */; };
D461B792279DAC010070E734 /* InputState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D461B791279DAC010070E734 /* InputState.swift */; }; D461B792279DAC010070E734 /* InputState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D461B791279DAC010070E734 /* InputState.swift */; };
D47B92C027972AD100458394 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47B92BF27972AC800458394 /* main.swift */; }; D47B92C027972AD100458394 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47B92BF27972AC800458394 /* main.swift */; };
D47D73AC27A6CAE600255A50 /* AssociatedPhrases.mm in Sources */ = {isa = PBXBuildFile; fileRef = D47D73AA27A6CAE600255A50 /* AssociatedPhrases.mm */; };
D47F7DCE278BFB57002F9DD7 /* ctlPrefWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47F7DCD278BFB57002F9DD7 /* ctlPrefWindow.swift */; }; D47F7DCE278BFB57002F9DD7 /* ctlPrefWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47F7DCD278BFB57002F9DD7 /* ctlPrefWindow.swift */; };
D47F7DD0278C0897002F9DD7 /* ctlNonModalAlertWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47F7DCF278C0897002F9DD7 /* ctlNonModalAlertWindow.swift */; }; D47F7DD0278C0897002F9DD7 /* ctlNonModalAlertWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47F7DCF278C0897002F9DD7 /* ctlNonModalAlertWindow.swift */; };
D47F7DD3278C1263002F9DD7 /* UserOverrideModel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D47F7DD2278C1263002F9DD7 /* UserOverrideModel.cpp */; };
D4A13D5A27A59F0B003BE359 /* ctlInputMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4A13D5927A59D5C003BE359 /* ctlInputMethod.swift */; }; D4A13D5A27A59F0B003BE359 /* ctlInputMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4A13D5927A59D5C003BE359 /* ctlInputMethod.swift */; };
D4E33D8A27A838CF006DB1CF /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = D4E33D8827A838CF006DB1CF /* Localizable.strings */; }; D4E33D8A27A838CF006DB1CF /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = D4E33D8827A838CF006DB1CF /* Localizable.strings */; };
D4E33D8F27A838F0006DB1CF /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = D4E33D8D27A838F0006DB1CF /* InfoPlist.strings */; }; D4E33D8F27A838F0006DB1CF /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = D4E33D8D27A838F0006DB1CF /* InfoPlist.strings */; };
D4E569DC27A34D0E00AC2CEF /* KeyHandler.mm in Sources */ = {isa = PBXBuildFile; fileRef = D4E569DB27A34CC100AC2CEF /* KeyHandler.mm */; }; D4E569DC27A34D0E00AC2CEF /* CTools.m in Sources */ = {isa = PBXBuildFile; fileRef = D4E569DB27A34CC100AC2CEF /* CTools.m */; };
D4F0BBDF279AF1AF0071253C /* ArchiveUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4F0BBDE279AF1AF0071253C /* ArchiveUtil.swift */; }; D4F0BBDF279AF1AF0071253C /* ArchiveUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4F0BBDE279AF1AF0071253C /* ArchiveUtil.swift */; };
D4F0BBE1279AF8B30071253C /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4F0BBE0279AF8B30071253C /* AppDelegate.swift */; }; D4F0BBE1279AF8B30071253C /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4F0BBE0279AF8B30071253C /* AppDelegate.swift */; };
D4F0BBE4279B08900071253C /* Chronosphere.m in Sources */ = {isa = PBXBuildFile; fileRef = D4F0BBE3279B08900071253C /* Chronosphere.m */; }; D4F0BBE4279B08900071253C /* Chronosphere.m in Sources */ = {isa = PBXBuildFile; fileRef = D4F0BBE3279B08900071253C /* Chronosphere.m */; };
@ -151,6 +161,7 @@
/* End PBXCopyFilesBuildPhase section */ /* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
5B00A22F282011980058E5DB /* lmLite.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = lmLite.swift; sourceTree = "<group>"; usesTabs = 0; };
5B04305327B529D800CB65BC /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/InfoPlist.strings"; sourceTree = "<group>"; }; 5B04305327B529D800CB65BC /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
5B04305427B529D800CB65BC /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = "<group>"; }; 5B04305427B529D800CB65BC /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = "<group>"; };
5B04305527B529D800CB65BC /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/MainMenu.strings"; sourceTree = "<group>"; }; 5B04305527B529D800CB65BC /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/MainMenu.strings"; sourceTree = "<group>"; };
@ -174,9 +185,9 @@
5B05A47B27AFF7CA00437698 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; }; 5B05A47B27AFF7CA00437698 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
5B05A47C27AFF7CF00437698 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; }; 5B05A47C27AFF7CF00437698 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
5B05A47F27AFF84200437698 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/frmAboutWindow.strings; sourceTree = "<group>"; }; 5B05A47F27AFF84200437698 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/frmAboutWindow.strings; sourceTree = "<group>"; };
5B0AF8B427B2C8290096FE54 /* StringExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = StringExtension.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5B0AF8B427B2C8290096FE54 /* StringExtension.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = StringExtension.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5B0C5EDF27C7D9870078037C /* dataCompiler.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = dataCompiler.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5B0C5EDF27C7D9870078037C /* dataCompiler.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = dataCompiler.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5B11328827B94CFB00E58451 /* AppleKeyboardConverter.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = AppleKeyboardConverter.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5B11328827B94CFB00E58451 /* AppleKeyboardConverter.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = AppleKeyboardConverter.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5B18BA6F27C7BD8B0056EB19 /* LICENSE-CHS.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = "LICENSE-CHS.txt"; sourceTree = "<group>"; }; 5B18BA6F27C7BD8B0056EB19 /* LICENSE-CHS.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = "LICENSE-CHS.txt"; sourceTree = "<group>"; };
5B18BA7027C7BD8B0056EB19 /* Makefile */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = "<group>"; }; 5B18BA7027C7BD8B0056EB19 /* Makefile */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = "<group>"; };
5B18BA7127C7BD8B0056EB19 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; }; 5B18BA7127C7BD8B0056EB19 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
@ -189,55 +200,56 @@
5B2DB16E27AF6891006D874E /* data-cht.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = "data-cht.txt"; path = "Data/data-cht.txt"; sourceTree = "<group>"; }; 5B2DB16E27AF6891006D874E /* data-cht.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = "data-cht.txt"; path = "Data/data-cht.txt"; 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>"; };
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; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = KeyHandler_States.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 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; };
5B5E535127EF261400C6AA1E /* IME.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = IME.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5B407151281F94E6009C24CB /* Composer.hh */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Composer.hh; sourceTree = "<group>"; };
5B61B0C9280BEFD4002E3CFA /* KeyHandler_Misc.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = KeyHandler_Misc.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5B407152281F94E6009C24CB /* Composer.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = Composer.mm; sourceTree = "<group>"; };
5B62A32627AE77BB00A19448 /* LMConsolidator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = LMConsolidator.h; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; }; 5B407309281672610023DFFF /* lmAssociates.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; lineEnding = 0; path = lmAssociates.swift; sourceTree = "<group>"; usesTabs = 0; };
5B62A32727AE77BB00A19448 /* LMConsolidator.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = LMConsolidator.mm; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; }; 5B40730A281672610023DFFF /* lmReplacements.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; lineEnding = 0; path = lmReplacements.swift; sourceTree = "<group>"; usesTabs = 0; };
5B62A32827AE77D100A19448 /* FSEventStreamHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = FSEventStreamHelper.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5B5E535127EF261400C6AA1E /* IME.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = IME.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5B62A32B27AE78B000A19448 /* CNSLM.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = CNSLM.h; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; }; 5B61B0C9280BEFD4002E3CFA /* KeyHandler_Misc.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = KeyHandler_Misc.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5B62A32C27AE78B000A19448 /* CoreLM.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = CoreLM.h; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; }; 5B62A32827AE77D100A19448 /* FSEventStreamHelper.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = FSEventStreamHelper.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5B62A32D27AE78B000A19448 /* CoreLM.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = CoreLM.mm; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; }; 5B62A33127AE792F00A19448 /* InputSourceHelper.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = InputSourceHelper.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5B62A33127AE792F00A19448 /* InputSourceHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = InputSourceHelper.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5B62A33527AE795800A19448 /* mgrPrefs.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = mgrPrefs.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5B62A33527AE795800A19448 /* mgrPrefs.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = mgrPrefs.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5B62A33727AE79CD00A19448 /* NSStringUtils.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = NSStringUtils.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5B62A33727AE79CD00A19448 /* NSStringUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = NSStringUtils.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5B62A33C27AE7CC100A19448 /* ctlAboutWindow.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlAboutWindow.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5B62A33C27AE7CC100A19448 /* ctlAboutWindow.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ctlAboutWindow.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5B62A33F27AE7CD900A19448 /* ctlCandidateHorizontal.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlCandidateHorizontal.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5B62A33F27AE7CD900A19448 /* ctlCandidateHorizontal.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ctlCandidateHorizontal.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5B62A34027AE7CD900A19448 /* ctlCandidate.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlCandidate.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5B62A34027AE7CD900A19448 /* ctlCandidate.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ctlCandidate.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5B62A34127AE7CD900A19448 /* ctlCandidateVertical.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlCandidateVertical.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5B62A34127AE7CD900A19448 /* ctlCandidateVertical.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ctlCandidateVertical.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5B62A34327AE7CD900A19448 /* TooltipController.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = TooltipController.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5B62A34327AE7CD900A19448 /* TooltipController.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = TooltipController.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5B62A34527AE7CD900A19448 /* NotifierController.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = NotifierController.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5B62A34527AE7CD900A19448 /* NotifierController.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = NotifierController.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; };
5B707CE527D9F3A10099EF99 /* SwiftyOpenCC */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = SwiftyOpenCC; path = Packages/SwiftyOpenCC; sourceTree = "<group>"; }; 5B707CE527D9F3A10099EF99 /* SwiftyOpenCC */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = SwiftyOpenCC; path = Packages/SwiftyOpenCC; sourceTree = "<group>"; };
5B707CE727D9F4590099EF99 /* OpenCCBridge.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = OpenCCBridge.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5B707CE727D9F4590099EF99 /* OpenCCBridge.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = OpenCCBridge.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5B7111C727DEF9FF00444310 /* UserSymbolLM.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = UserSymbolLM.h; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; };
5B73FB5427B2BD6900E9BF49 /* PhraseEditor-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "PhraseEditor-Info.plist"; path = "UserPhraseEditor/PhraseEditor-Info.plist"; sourceTree = SOURCE_ROOT; }; 5B73FB5427B2BD6900E9BF49 /* PhraseEditor-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "PhraseEditor-Info.plist"; path = "UserPhraseEditor/PhraseEditor-Info.plist"; sourceTree = SOURCE_ROOT; };
5B73FB5F27B2BE1300E9BF49 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; }; 5B73FB5F27B2BE1300E9BF49 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
5B782EC3280C243C007276DE /* KeyHandler_HandleCandidate.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = KeyHandler_HandleCandidate.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5B782EC3280C243C007276DE /* KeyHandler_HandleCandidate.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = KeyHandler_HandleCandidate.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5B7BC4AF27AFFBE800F66C24 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/frmPrefWindow.xib; sourceTree = "<group>"; }; 5B7BC4AF27AFFBE800F66C24 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/frmPrefWindow.xib; sourceTree = "<group>"; };
5B7BC4B227AFFC0B00F66C24 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/frmPrefWindow.strings; sourceTree = "<group>"; }; 5B7BC4B227AFFC0B00F66C24 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/frmPrefWindow.strings; sourceTree = "<group>"; };
5B7F225C2808501000DDD3CB /* KeyHandler_HandleInput.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = KeyHandler_HandleInput.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5B7F225C2808501000DDD3CB /* KeyHandler_HandleInput.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = KeyHandler_HandleInput.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5B8F43ED27C9BC220069AC27 /* SymbolLM.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = SymbolLM.h; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; }; 5B949BD82816DC5400D87B5D /* LineReader.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; lineEnding = 0; path = LineReader.swift; sourceTree = "<group>"; usesTabs = 0; };
5BA9FD0A27FEDB6B002DE248 /* suiPrefPaneGeneral.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = suiPrefPaneGeneral.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5B949BDA2816DDBC00D87B5D /* LMConsolidator.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; lineEnding = 0; path = LMConsolidator.swift; sourceTree = "<group>"; usesTabs = 0; };
5BA9FD0B27FEDB6B002DE248 /* suiPrefPaneKeyboard.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = suiPrefPaneKeyboard.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5BA0DF2E2817857D009E73BB /* lmUserOverride.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; lineEnding = 0; path = lmUserOverride.swift; sourceTree = "<group>"; usesTabs = 0; };
5BA9FD0C27FEDB6B002DE248 /* ctlPrefUI.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ctlPrefUI.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5BA0DF2F2817857D009E73BB /* lmCore.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; lineEnding = 0; path = lmCore.swift; sourceTree = "<group>"; usesTabs = 0; };
5BA9FD0D27FEDB6B002DE248 /* suiPrefPaneExperience.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = suiPrefPaneExperience.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5BA9FD0A27FEDB6B002DE248 /* suiPrefPaneGeneral.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = suiPrefPaneGeneral.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5BA9FD0E27FEDB6B002DE248 /* suiPrefPaneDictionary.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = suiPrefPaneDictionary.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5BA9FD0B27FEDB6B002DE248 /* suiPrefPaneKeyboard.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = suiPrefPaneKeyboard.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5BA9FD3127FEF3C8002DE248 /* Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Utilities.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5BA9FD0C27FEDB6B002DE248 /* ctlPrefUI.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlPrefUI.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5BA9FD3227FEF3C8002DE248 /* Pane.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Pane.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5BA9FD0D27FEDB6B002DE248 /* suiPrefPaneExperience.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = suiPrefPaneExperience.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5BA9FD3327FEF3C8002DE248 /* Localization.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Localization.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5BA9FD0E27FEDB6B002DE248 /* suiPrefPaneDictionary.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = suiPrefPaneDictionary.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5BA9FD3427FEF3C8002DE248 /* PreferencesStyle.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = PreferencesStyle.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5BA9FD3127FEF3C8002DE248 /* Utilities.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = Utilities.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5BA9FD3527FEF3C8002DE248 /* PreferencePane.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = PreferencePane.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5BA9FD3227FEF3C8002DE248 /* Pane.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = Pane.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5BA9FD3627FEF3C8002DE248 /* Preferences.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Preferences.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5BA9FD3327FEF3C8002DE248 /* Localization.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = Localization.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5BA9FD3727FEF3C8002DE248 /* SegmentedControlStyleViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = SegmentedControlStyleViewController.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5BA9FD3427FEF3C8002DE248 /* PreferencesStyle.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = PreferencesStyle.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5BA9FD3827FEF3C8002DE248 /* ToolbarItemStyleViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ToolbarItemStyleViewController.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5BA9FD3527FEF3C8002DE248 /* PreferencePane.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = PreferencePane.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5BA9FD3927FEF3C8002DE248 /* Container.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Container.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5BA9FD3627FEF3C8002DE248 /* Preferences.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Preferences.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5BA9FD3A27FEF3C8002DE248 /* PreferencesStyleController.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = PreferencesStyleController.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5BA9FD3727FEF3C8002DE248 /* SegmentedControlStyleViewController.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = SegmentedControlStyleViewController.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5BA9FD3B27FEF3C8002DE248 /* PreferencesWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = PreferencesWindowController.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5BA9FD3827FEF3C8002DE248 /* ToolbarItemStyleViewController.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ToolbarItemStyleViewController.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5BA9FD3C27FEF3C8002DE248 /* Section.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Section.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5BA9FD3927FEF3C8002DE248 /* Container.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = Container.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5BA9FD3D27FEF3C8002DE248 /* PreferencesTabViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = PreferencesTabViewController.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5BA9FD3A27FEF3C8002DE248 /* PreferencesStyleController.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = PreferencesStyleController.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5BA9FD8A28006B41002DE248 /* VDKComboBox.swift */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = VDKComboBox.swift; sourceTree = "<group>"; tabWidth = 2; }; 5BA9FD3B27FEF3C8002DE248 /* PreferencesWindowController.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = PreferencesWindowController.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5BAEFACF28012565001F42C9 /* mgrLangModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = mgrLangModel.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5BA9FD3C27FEF3C8002DE248 /* Section.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = Section.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5BB802D927FABA8300CF1C19 /* ctlInputMethod_Menu.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ctlInputMethod_Menu.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5BA9FD3D27FEF3C8002DE248 /* PreferencesTabViewController.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = PreferencesTabViewController.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5BA9FD8A28006B41002DE248 /* VDKComboBox.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = VDKComboBox.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5BAEFACF28012565001F42C9 /* mgrLangModel.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = mgrLangModel.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5BB802D927FABA8300CF1C19 /* ctlInputMethod_Menu.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlInputMethod_Menu.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5BBBB75D27AED54C0023B93A /* Beep.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = Beep.m4a; sourceTree = "<group>"; }; 5BBBB75D27AED54C0023B93A /* Beep.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = Beep.m4a; sourceTree = "<group>"; };
5BBBB75E27AED54C0023B93A /* Fart.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = Fart.m4a; sourceTree = "<group>"; }; 5BBBB75E27AED54C0023B93A /* Fart.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = Fart.m4a; sourceTree = "<group>"; };
5BBBB76627AED5DB0023B93A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/frmNonModalAlertWindow.xib; sourceTree = "<group>"; }; 5BBBB76627AED5DB0023B93A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/frmNonModalAlertWindow.xib; sourceTree = "<group>"; };
@ -247,21 +259,23 @@
5BBBB77127AED70B0023B93A /* MenuIcon-SCVIM.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "MenuIcon-SCVIM.png"; sourceTree = "<group>"; }; 5BBBB77127AED70B0023B93A /* MenuIcon-SCVIM.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "MenuIcon-SCVIM.png"; sourceTree = "<group>"; };
5BBBB77227AED70B0023B93A /* MenuIcon-TCVIM.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "MenuIcon-TCVIM.png"; sourceTree = "<group>"; }; 5BBBB77227AED70B0023B93A /* MenuIcon-TCVIM.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "MenuIcon-TCVIM.png"; sourceTree = "<group>"; };
5BBBB77727AEDB290023B93A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/MainMenu.strings; sourceTree = "<group>"; }; 5BBBB77727AEDB290023B93A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/MainMenu.strings; sourceTree = "<group>"; };
5BBBB77927AEDC690023B93A /* clsSFX.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = clsSFX.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5BBBB77927AEDC690023B93A /* clsSFX.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = clsSFX.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5BBD627827B6C4D900271480 /* Update-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Update-Info.plist"; sourceTree = "<group>"; }; 5BBD627827B6C4D900271480 /* Update-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Update-Info.plist"; sourceTree = "<group>"; };
5BC0AAC927F58472002D33E9 /* pkgPreInstall.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = pkgPreInstall.sh; sourceTree = "<group>"; }; 5BC0AAC927F58472002D33E9 /* pkgPreInstall.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = pkgPreInstall.sh; sourceTree = "<group>"; };
5BC0AACA27F58472002D33E9 /* pkgPostInstall.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = pkgPostInstall.sh; sourceTree = "<group>"; }; 5BC0AACA27F58472002D33E9 /* pkgPostInstall.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = pkgPostInstall.sh; sourceTree = "<group>"; };
5BC2652127E04B7B00700291 /* uninstall.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; lineEnding = 0; path = uninstall.sh; sourceTree = "<group>"; }; 5BC2652127E04B7B00700291 /* uninstall.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; lineEnding = 0; path = uninstall.sh; sourceTree = "<group>"; };
5BD0113A28180D6100609769 /* LMInstantiator.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; lineEnding = 0; path = LMInstantiator.swift; sourceTree = "<group>"; usesTabs = 0; };
5BD0113C2818543900609769 /* KeyHandler_Core.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; lineEnding = 0; path = KeyHandler_Core.swift; sourceTree = "<group>"; usesTabs = 0; };
5BD05B8027B22F3C004C4F1D /* char-kanji-cns.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = "char-kanji-cns.txt"; path = "Data/components/common/char-kanji-cns.txt"; sourceTree = "<group>"; }; 5BD05B8027B22F3C004C4F1D /* char-kanji-cns.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = "char-kanji-cns.txt"; path = "Data/components/common/char-kanji-cns.txt"; sourceTree = "<group>"; };
5BD05BB827B2A429004C4F1D /* vChewingPhraseEditor.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = vChewingPhraseEditor.app; sourceTree = BUILT_PRODUCTS_DIR; }; 5BD05BB827B2A429004C4F1D /* vChewingPhraseEditor.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = vChewingPhraseEditor.app; sourceTree = BUILT_PRODUCTS_DIR; };
5BD05BC627B2A42A004C4F1D /* vChewingPhraseEditor.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = vChewingPhraseEditor.entitlements; sourceTree = "<group>"; }; 5BD05BC627B2A42A004C4F1D /* vChewingPhraseEditor.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = vChewingPhraseEditor.entitlements; sourceTree = "<group>"; };
5BD05C5C27B2BBA9004C4F1D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; }; 5BD05C5C27B2BBA9004C4F1D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
5BD05C6127B2BBEF004C4F1D /* Document.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Document.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5BD05C6127B2BBEF004C4F1D /* Document.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = Document.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5BD05C6227B2BBEF004C4F1D /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = AppDelegate.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5BD05C6227B2BBEF004C4F1D /* AppDelegate.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = AppDelegate.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5BD05C6327B2BBEF004C4F1D /* Content.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Content.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5BD05C6327B2BBEF004C4F1D /* Content.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = Content.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5BD05C6427B2BBEF004C4F1D /* WindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = WindowController.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5BD05C6427B2BBEF004C4F1D /* WindowController.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = WindowController.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5BD05C6527B2BBEF004C4F1D /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ViewController.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5BD05C6527B2BBEF004C4F1D /* ViewController.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ViewController.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5BDC1CF927FDF1310052C2B9 /* apiUpdate.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = apiUpdate.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5BDC1CF927FDF1310052C2B9 /* apiUpdate.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = apiUpdate.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5BDCBB4227B4F6C600D0CC59 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/MainMenu.strings"; sourceTree = "<group>"; }; 5BDCBB4227B4F6C600D0CC59 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/MainMenu.strings"; sourceTree = "<group>"; };
5BDCBB4327B4F6C600D0CC59 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/frmAboutWindow.strings"; sourceTree = "<group>"; }; 5BDCBB4327B4F6C600D0CC59 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/frmAboutWindow.strings"; sourceTree = "<group>"; };
5BDCBB4527B4F6C600D0CC59 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/frmPrefWindow.strings"; sourceTree = "<group>"; }; 5BDCBB4527B4F6C600D0CC59 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/frmPrefWindow.strings"; sourceTree = "<group>"; };
@ -271,25 +285,26 @@
5BDCBB4A27B4F6C700D0CC59 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.strings"; sourceTree = "<group>"; }; 5BDCBB4A27B4F6C700D0CC59 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.strings"; sourceTree = "<group>"; };
5BDCBB4B27B4F6C700D0CC59 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/frmAboutWindow.strings"; sourceTree = "<group>"; }; 5BDCBB4B27B4F6C700D0CC59 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/frmAboutWindow.strings"; sourceTree = "<group>"; };
5BDCBB4D27B4F6C700D0CC59 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/InfoPlist.strings"; sourceTree = "<group>"; }; 5BDCBB4D27B4F6C700D0CC59 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
5BE78BD827B37750005EA1BE /* ctlAboutWindow.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ctlAboutWindow.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5BE78BD827B37750005EA1BE /* ctlAboutWindow.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlAboutWindow.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5BE78BDB27B37764005EA1BE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/frmAboutWindow.xib; sourceTree = "<group>"; }; 5BE78BDB27B37764005EA1BE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/frmAboutWindow.xib; sourceTree = "<group>"; };
5BE78BDF27B37968005EA1BE /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/frmAboutWindow.strings; sourceTree = "<group>"; }; 5BE78BDF27B37968005EA1BE /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/frmAboutWindow.strings; sourceTree = "<group>"; };
5BF8423027BAA942008E7E4C /* vChewingKanjiConverter.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = vChewingKanjiConverter.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; 5BE8A8C4281EE65300197741 /* CONTRIBUTING.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = CONTRIBUTING.md; sourceTree = "<group>"; };
5BF8423027BAA942008E7E4C /* vChewingKanjiConverter.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = vChewingKanjiConverter.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5BFDF48C27B51867009523B6 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Main.strings"; sourceTree = "<group>"; }; 5BFDF48C27B51867009523B6 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Main.strings"; sourceTree = "<group>"; };
6A0D4EA215FC0D2D00ABF4B3 /* vChewing.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = vChewing.app; sourceTree = BUILT_PRODUCTS_DIR; }; 6A0D4EA215FC0D2D00ABF4B3 /* vChewing.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = vChewing.app; sourceTree = BUILT_PRODUCTS_DIR; };
6A0D4EF515FC0DA600ABF4B3 /* IME-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "IME-Info.plist"; sourceTree = "<group>"; }; 6A0D4EF515FC0DA600ABF4B3 /* IME-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "IME-Info.plist"; sourceTree = "<group>"; };
6A0D4EF615FC0DA600ABF4B3 /* vChewing-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "vChewing-Prefix.pch"; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; }; 6A0D4EF615FC0DA600ABF4B3 /* vChewing-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "vChewing-Prefix.pch"; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; };
6A0D4F1415FC0EB100ABF4B3 /* Bigram.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = Bigram.h; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; }; 6A0D4F1415FC0EB100ABF4B3 /* 6_Bigram.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 6_Bigram.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
6A0D4F1515FC0EB100ABF4B3 /* BlockReadingBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = BlockReadingBuilder.h; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; }; 6A0D4F1515FC0EB100ABF4B3 /* 1_BlockReadingBuilder.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 1_BlockReadingBuilder.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
6A0D4F1615FC0EB100ABF4B3 /* Gramambular.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = Gramambular.h; sourceTree = "<group>"; tabWidth = 4; 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 /* Grid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = Grid.h; sourceTree = "<group>"; tabWidth = 4; 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 /* KeyValuePair.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = KeyValuePair.h; sourceTree = "<group>"; tabWidth = 4; 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; };
6A0D4F1915FC0EB100ABF4B3 /* LanguageModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = LanguageModel.h; sourceTree = "<group>"; tabWidth = 4; 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 /* Node.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = Node.h; sourceTree = "<group>"; tabWidth = 4; 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 /* NodeAnchor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = NodeAnchor.h; sourceTree = "<group>"; tabWidth = 4; 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; };
6A0D4F1C15FC0EB100ABF4B3 /* Span.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = Span.h; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; }; 6A0D4F1C15FC0EB100ABF4B3 /* 3_Span.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 3_Span.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
6A0D4F1D15FC0EB100ABF4B3 /* Unigram.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = Unigram.h; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; }; 6A0D4F1D15FC0EB100ABF4B3 /* 6_Unigram.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 6_Unigram.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
6A0D4F1E15FC0EB100ABF4B3 /* Walker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = Walker.h; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; }; 6A0D4F1E15FC0EB100ABF4B3 /* 1_Walker.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 1_Walker.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
6A0D4F2015FC0EB100ABF4B3 /* Mandarin.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = Mandarin.cpp; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; }; 6A0D4F2015FC0EB100ABF4B3 /* Mandarin.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = Mandarin.cpp; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; };
6A0D4F2115FC0EB100ABF4B3 /* Mandarin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = Mandarin.h; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; }; 6A0D4F2115FC0EB100ABF4B3 /* Mandarin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = Mandarin.h; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; };
6A15B32421A51F2300B92CD3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; }; 6A15B32421A51F2300B92CD3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
@ -302,39 +317,26 @@
6ACA41EF15FC1D9000935EF6 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; }; 6ACA41EF15FC1D9000935EF6 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
6ACA41F215FC1D9000935EF6 /* Installer-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "Installer-Info.plist"; path = "Installer/Installer-Info.plist"; sourceTree = SOURCE_ROOT; }; 6ACA41F215FC1D9000935EF6 /* Installer-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "Installer-Info.plist"; path = "Installer/Installer-Info.plist"; sourceTree = SOURCE_ROOT; };
6ACA41F315FC1D9000935EF6 /* Installer-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "Installer-Prefix.pch"; path = "Installer/Installer-Prefix.pch"; sourceTree = SOURCE_ROOT; }; 6ACA41F315FC1D9000935EF6 /* Installer-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "Installer-Prefix.pch"; path = "Installer/Installer-Prefix.pch"; sourceTree = SOURCE_ROOT; };
6ACC3D3C27914AAB00F1B140 /* KeyValueBlobReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = KeyValueBlobReader.h; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; };
6ACC3D3E27914F2400F1B140 /* KeyValueBlobReader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = KeyValueBlobReader.cpp; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; };
6ACC3D402793701600F1B140 /* ParselessPhraseDB.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = ParselessPhraseDB.cpp; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; }; 6ACC3D402793701600F1B140 /* ParselessPhraseDB.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = ParselessPhraseDB.cpp; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; };
6ACC3D412793701600F1B140 /* ParselessPhraseDB.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = ParselessPhraseDB.h; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; }; 6ACC3D412793701600F1B140 /* ParselessPhraseDB.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = ParselessPhraseDB.h; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; };
6ACC3D422793701600F1B140 /* ParselessLM.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = ParselessLM.cpp; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; }; 6ACC3D422793701600F1B140 /* ParselessLM.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = ParselessLM.cpp; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; };
6ACC3D432793701600F1B140 /* ParselessLM.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = ParselessLM.h; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; }; 6ACC3D432793701600F1B140 /* ParselessLM.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = ParselessLM.h; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; };
D41355D6278D7409005E5CBD /* mgrLangModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = mgrLangModel.h; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; }; D427A9BF25ED28CC005D43E0 /* vChewing-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = "vChewing-Bridging-Header.h"; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; };
D41355D7278D7409005E5CBD /* mgrLangModel.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = mgrLangModel.mm; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; }; D427F76B278CA1BA004A2160 /* AppDelegate.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = AppDelegate.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
D41355D9278E6D17005E5CBD /* LMInstantiator.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = LMInstantiator.mm; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; }; D456576D279E4F7B00DF6BC9 /* InputHandler.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = InputHandler.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
D41355DA278E6D17005E5CBD /* LMInstantiator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = LMInstantiator.h; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; }; D461B791279DAC010070E734 /* InputState.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = InputState.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
D41355DC278EA3ED005E5CBD /* UserPhrasesLM.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = UserPhrasesLM.mm; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; }; D47B92BF27972AC800458394 /* main.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = main.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
D41355DD278EA3ED005E5CBD /* UserPhrasesLM.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = UserPhrasesLM.h; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; }; D47F7DCD278BFB57002F9DD7 /* ctlPrefWindow.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlPrefWindow.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
D427A9BF25ED28CC005D43E0 /* vChewing-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = "vChewing-Bridging-Header.h"; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; }; D47F7DCF278C0897002F9DD7 /* ctlNonModalAlertWindow.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlNonModalAlertWindow.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
D427F76B278CA1BA004A2160 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = AppDelegate.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; };
D44FB74B2792189A003C80A6 /* PhraseReplacementMap.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = PhraseReplacementMap.mm; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; };
D44FB74C2792189A003C80A6 /* PhraseReplacementMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = PhraseReplacementMap.h; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; };
D456576D279E4F7B00DF6BC9 /* InputHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = InputHandler.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; };
D461B791279DAC010070E734 /* InputState.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = InputState.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; };
D47B92BF27972AC800458394 /* main.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = main.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; };
D47D73AA27A6CAE600255A50 /* AssociatedPhrases.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = AssociatedPhrases.mm; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; };
D47D73AB27A6CAE600255A50 /* AssociatedPhrases.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = AssociatedPhrases.h; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; };
D47F7DCD278BFB57002F9DD7 /* ctlPrefWindow.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ctlPrefWindow.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; };
D47F7DCF278C0897002F9DD7 /* ctlNonModalAlertWindow.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ctlNonModalAlertWindow.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; };
D47F7DD1278C1263002F9DD7 /* UserOverrideModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = UserOverrideModel.h; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; }; D47F7DD1278C1263002F9DD7 /* UserOverrideModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = UserOverrideModel.h; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; };
D47F7DD2278C1263002F9DD7 /* UserOverrideModel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = UserOverrideModel.cpp; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; }; D47F7DD2278C1263002F9DD7 /* UserOverrideModel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = UserOverrideModel.cpp; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; };
D495583A27A5C6C4006ADE1C /* mgrLangModel_Privates.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = mgrLangModel_Privates.h; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; }; D4A13D5927A59D5C003BE359 /* ctlInputMethod.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlInputMethod.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
D4A13D5927A59D5C003BE359 /* ctlInputMethod.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ctlInputMethod.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; };
D4E33D8927A838CF006DB1CF /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = "<group>"; }; D4E33D8927A838CF006DB1CF /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = "<group>"; };
D4E33D8E27A838F0006DB1CF /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/InfoPlist.strings; sourceTree = "<group>"; }; D4E33D8E27A838F0006DB1CF /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/InfoPlist.strings; sourceTree = "<group>"; };
D4E569DA27A34CC100AC2CEF /* KeyHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = KeyHandler.h; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; }; D4E569DA27A34CC100AC2CEF /* CTools.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = CTools.h; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; };
D4E569DB27A34CC100AC2CEF /* KeyHandler.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = KeyHandler.mm; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; }; D4E569DB27A34CC100AC2CEF /* CTools.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = CTools.m; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; };
D4F0BBDE279AF1AF0071253C /* ArchiveUtil.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ArchiveUtil.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; D4F0BBDE279AF1AF0071253C /* ArchiveUtil.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ArchiveUtil.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
D4F0BBE0279AF8B30071253C /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = AppDelegate.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 1; }; D4F0BBE0279AF8B30071253C /* AppDelegate.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = AppDelegate.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
D4F0BBE2279B08900071253C /* Chronosphere.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = Chronosphere.h; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; }; D4F0BBE2279B08900071253C /* Chronosphere.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = Chronosphere.h; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; };
D4F0BBE3279B08900071253C /* Chronosphere.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = Chronosphere.m; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; }; D4F0BBE3279B08900071253C /* Chronosphere.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = Chronosphere.m; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; };
/* End PBXFileReference section */ /* End PBXFileReference section */
@ -377,6 +379,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
5BC2652127E04B7B00700291 /* uninstall.sh */, 5BC2652127E04B7B00700291 /* uninstall.sh */,
5BE8A8C4281EE65300197741 /* CONTRIBUTING.md */,
5B18BA6F27C7BD8B0056EB19 /* LICENSE-CHS.txt */, 5B18BA6F27C7BD8B0056EB19 /* LICENSE-CHS.txt */,
5B18BA7427C7BD8C0056EB19 /* LICENSE-CHT.txt */, 5B18BA7427C7BD8C0056EB19 /* LICENSE-CHT.txt */,
5B18BA7327C7BD8C0056EB19 /* LICENSE-JPN.txt */, 5B18BA7327C7BD8C0056EB19 /* LICENSE-JPN.txt */,
@ -386,19 +389,22 @@
name = MiscRootFiles; name = MiscRootFiles;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
5B4D47B627C9186900220DDC /* InstantiatedModels */ = { 5B407308281672610023DFFF /* SubLMs */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
5B62A32B27AE78B000A19448 /* CNSLM.h */, 5B407309281672610023DFFF /* lmAssociates.swift */,
5B8F43ED27C9BC220069AC27 /* SymbolLM.h */, 5BA0DF2F2817857D009E73BB /* lmCore.swift */,
5B7111C727DEF9FF00444310 /* UserSymbolLM.h */, 5B00A22F282011980058E5DB /* lmLite.swift */,
5B40730A281672610023DFFF /* lmReplacements.swift */,
5BA0DF2E2817857D009E73BB /* lmUserOverride.swift */,
); );
path = InstantiatedModels; path = SubLMs;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
5B62A30127AE732800A19448 /* 3rdParty */ = { 5B62A30127AE732800A19448 /* 3rdParty */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
5B949BD72816DC4400D87B5D /* LineReader */,
5B707CE627D9F43E0099EF99 /* OpenCCBridge */, 5B707CE627D9F43E0099EF99 /* OpenCCBridge */,
5B62A30227AE733500A19448 /* OVMandarin */, 5B62A30227AE733500A19448 /* OVMandarin */,
5BA9FCEA27FED652002DE248 /* SindreSorhus */, 5BA9FCEA27FED652002DE248 /* SindreSorhus */,
@ -412,6 +418,8 @@
children = ( children = (
6A0D4F2015FC0EB100ABF4B3 /* Mandarin.cpp */, 6A0D4F2015FC0EB100ABF4B3 /* Mandarin.cpp */,
6A0D4F2115FC0EB100ABF4B3 /* Mandarin.h */, 6A0D4F2115FC0EB100ABF4B3 /* Mandarin.h */,
5B407151281F94E6009C24CB /* Composer.hh */,
5B407152281F94E6009C24CB /* Composer.mm */,
); );
path = OVMandarin; path = OVMandarin;
sourceTree = "<group>"; sourceTree = "<group>";
@ -428,16 +436,15 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
5B11328827B94CFB00E58451 /* AppleKeyboardConverter.swift */, 5B11328827B94CFB00E58451 /* AppleKeyboardConverter.swift */,
D4E569DA27A34CC100AC2CEF /* CTools.h */,
D4E569DB27A34CC100AC2CEF /* CTools.m */,
D456576D279E4F7B00DF6BC9 /* InputHandler.swift */, D456576D279E4F7B00DF6BC9 /* InputHandler.swift */,
D461B791279DAC010070E734 /* InputState.swift */, D461B791279DAC010070E734 /* InputState.swift */,
5BD0113C2818543900609769 /* KeyHandler_Core.swift */,
5B782EC3280C243C007276DE /* KeyHandler_HandleCandidate.swift */, 5B782EC3280C243C007276DE /* KeyHandler_HandleCandidate.swift */,
5B7F225C2808501000DDD3CB /* KeyHandler_HandleInput.swift */, 5B7F225C2808501000DDD3CB /* KeyHandler_HandleInput.swift */,
5B61B0C9280BEFD4002E3CFA /* KeyHandler_Misc.swift */, 5B61B0C9280BEFD4002E3CFA /* KeyHandler_Misc.swift */,
5B3133BE280B229700A4A505 /* KeyHandler_States.swift */, 5B3133BE280B229700A4A505 /* KeyHandler_States.swift */,
D4E569DA27A34CC100AC2CEF /* KeyHandler.h */,
D4E569DB27A34CC100AC2CEF /* KeyHandler.mm */,
6ACC3D3E27914F2400F1B140 /* KeyValueBlobReader.cpp */,
6ACC3D3C27914AAB00F1B140 /* KeyValueBlobReader.h */,
5B62A33727AE79CD00A19448 /* NSStringUtils.swift */, 5B62A33727AE79CD00A19448 /* NSStringUtils.swift */,
5BF8423027BAA942008E7E4C /* vChewingKanjiConverter.swift */, 5BF8423027BAA942008E7E4C /* vChewingKanjiConverter.swift */,
); );
@ -457,8 +464,6 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
5B62A32827AE77D100A19448 /* FSEventStreamHelper.swift */, 5B62A32827AE77D100A19448 /* FSEventStreamHelper.swift */,
5B62A32627AE77BB00A19448 /* LMConsolidator.h */,
5B62A32727AE77BB00A19448 /* LMConsolidator.mm */,
); );
path = FileHandlers; path = FileHandlers;
sourceTree = "<group>"; sourceTree = "<group>";
@ -479,7 +484,7 @@
5B62A32327AE756800A19448 /* LanguageParsers */ = { 5B62A32327AE756800A19448 /* LanguageParsers */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
6A0D4F1315FC0EB100ABF4B3 /* Gramambular */, 6A0D4F1315FC0EB100ABF4B3 /* Megrez */,
); );
path = LanguageParsers; path = LanguageParsers;
sourceTree = "<group>"; sourceTree = "<group>";
@ -487,37 +492,26 @@
5B62A32427AE757300A19448 /* LangModelRelated */ = { 5B62A32427AE757300A19448 /* LangModelRelated */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
5B62A32527AE758000A19448 /* SubLanguageModels */, 5B62A32527AE758000A19448 /* OldFileReferences */,
D41355DA278E6D17005E5CBD /* LMInstantiator.h */, 5B407308281672610023DFFF /* SubLMs */,
D41355D9278E6D17005E5CBD /* LMInstantiator.mm */, 5B949BDA2816DDBC00D87B5D /* LMConsolidator.swift */,
D495583A27A5C6C4006ADE1C /* mgrLangModel_Privates.h */, 5BD0113A28180D6100609769 /* LMInstantiator.swift */,
D41355D6278D7409005E5CBD /* mgrLangModel.h */,
D41355D7278D7409005E5CBD /* mgrLangModel.mm */,
5BAEFACF28012565001F42C9 /* mgrLangModel.swift */, 5BAEFACF28012565001F42C9 /* mgrLangModel.swift */,
); );
path = LangModelRelated; path = LangModelRelated;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
5B62A32527AE758000A19448 /* SubLanguageModels */ = { 5B62A32527AE758000A19448 /* OldFileReferences */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
5B4D47B627C9186900220DDC /* InstantiatedModels */,
D47D73AB27A6CAE600255A50 /* AssociatedPhrases.h */,
D47D73AA27A6CAE600255A50 /* AssociatedPhrases.mm */,
5B62A32C27AE78B000A19448 /* CoreLM.h */,
5B62A32D27AE78B000A19448 /* CoreLM.mm */,
6ACC3D422793701600F1B140 /* ParselessLM.cpp */, 6ACC3D422793701600F1B140 /* ParselessLM.cpp */,
6ACC3D432793701600F1B140 /* ParselessLM.h */, 6ACC3D432793701600F1B140 /* ParselessLM.h */,
6ACC3D402793701600F1B140 /* ParselessPhraseDB.cpp */, 6ACC3D402793701600F1B140 /* ParselessPhraseDB.cpp */,
6ACC3D412793701600F1B140 /* ParselessPhraseDB.h */, 6ACC3D412793701600F1B140 /* ParselessPhraseDB.h */,
D44FB74C2792189A003C80A6 /* PhraseReplacementMap.h */,
D44FB74B2792189A003C80A6 /* PhraseReplacementMap.mm */,
D47F7DD2278C1263002F9DD7 /* UserOverrideModel.cpp */, D47F7DD2278C1263002F9DD7 /* UserOverrideModel.cpp */,
D47F7DD1278C1263002F9DD7 /* UserOverrideModel.h */, D47F7DD1278C1263002F9DD7 /* UserOverrideModel.h */,
D41355DD278EA3ED005E5CBD /* UserPhrasesLM.h */,
D41355DC278EA3ED005E5CBD /* UserPhrasesLM.mm */,
); );
path = SubLanguageModels; path = OldFileReferences;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
5B62A33027AE78E500A19448 /* Resources */ = { 5B62A33027AE78E500A19448 /* Resources */ = {
@ -620,6 +614,14 @@
path = OpenCCBridge; path = OpenCCBridge;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
5B949BD72816DC4400D87B5D /* LineReader */ = {
isa = PBXGroup;
children = (
5B949BD82816DC5400D87B5D /* LineReader.swift */,
);
path = LineReader;
sourceTree = "<group>";
};
5BA9FCEA27FED652002DE248 /* SindreSorhus */ = { 5BA9FCEA27FED652002DE248 /* SindreSorhus */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -800,22 +802,22 @@
path = Modules; path = Modules;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
6A0D4F1315FC0EB100ABF4B3 /* Gramambular */ = { 6A0D4F1315FC0EB100ABF4B3 /* Megrez */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
6A0D4F1415FC0EB100ABF4B3 /* Bigram.h */, 6A0D4F1615FC0EB100ABF4B3 /* 0_Megrez.swift */,
6A0D4F1515FC0EB100ABF4B3 /* BlockReadingBuilder.h */, 6A0D4F1515FC0EB100ABF4B3 /* 1_BlockReadingBuilder.swift */,
6A0D4F1615FC0EB100ABF4B3 /* Gramambular.h */, 6A0D4F1E15FC0EB100ABF4B3 /* 1_Walker.swift */,
6A0D4F1715FC0EB100ABF4B3 /* Grid.h */, 6A0D4F1715FC0EB100ABF4B3 /* 2_Grid.swift */,
6A0D4F1815FC0EB100ABF4B3 /* KeyValuePair.h */, 6A0D4F1B15FC0EB100ABF4B3 /* 3_NodeAnchor.swift */,
6A0D4F1915FC0EB100ABF4B3 /* LanguageModel.h */, 6A0D4F1C15FC0EB100ABF4B3 /* 3_Span.swift */,
6A0D4F1A15FC0EB100ABF4B3 /* Node.h */, 6A0D4F1A15FC0EB100ABF4B3 /* 4_Node.swift */,
6A0D4F1B15FC0EB100ABF4B3 /* NodeAnchor.h */, 6A0D4F1915FC0EB100ABF4B3 /* 5_LanguageModel.swift */,
6A0D4F1C15FC0EB100ABF4B3 /* Span.h */, 6A0D4F1415FC0EB100ABF4B3 /* 6_Bigram.swift */,
6A0D4F1D15FC0EB100ABF4B3 /* Unigram.h */, 6A0D4F1D15FC0EB100ABF4B3 /* 6_Unigram.swift */,
6A0D4F1E15FC0EB100ABF4B3 /* Walker.h */, 6A0D4F1815FC0EB100ABF4B3 /* 7_KeyValuePair.swift */,
); );
path = Gramambular; path = Megrez;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
6ACA41E715FC1D9000935EF6 /* Installer */ = { 6ACA41E715FC1D9000935EF6 /* Installer */ = {
@ -1069,66 +1071,76 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
5B38F59D281E2E49007D5F5D /* 4_Node.swift in Sources */,
5B38F5A3281E2E49007D5F5D /* 3_Span.swift in Sources */,
5B40730C281672610023DFFF /* lmAssociates.swift in Sources */,
5B707CE827D9F4590099EF99 /* OpenCCBridge.swift in Sources */, 5B707CE827D9F4590099EF99 /* OpenCCBridge.swift in Sources */,
D427F76C278CA2B0004A2160 /* AppDelegate.swift in Sources */, D427F76C278CA2B0004A2160 /* AppDelegate.swift in Sources */,
5BA9FD4527FEF3C9002DE248 /* ToolbarItemStyleViewController.swift in Sources */, 5BA9FD4527FEF3C9002DE248 /* ToolbarItemStyleViewController.swift in Sources */,
5BA0DF322817857D009E73BB /* lmCore.swift in Sources */,
5BA9FD4127FEF3C8002DE248 /* PreferencesStyle.swift in Sources */, 5BA9FD4127FEF3C8002DE248 /* PreferencesStyle.swift in Sources */,
5B7F225D2808501000DDD3CB /* KeyHandler_HandleInput.swift in Sources */, 5B7F225D2808501000DDD3CB /* KeyHandler_HandleInput.swift in Sources */,
5BA9FD1227FEDB6B002DE248 /* suiPrefPaneExperience.swift in Sources */, 5BA9FD1227FEDB6B002DE248 /* suiPrefPaneExperience.swift in Sources */,
6ACC3D442793701600F1B140 /* ParselessPhraseDB.cpp in Sources */,
D461B792279DAC010070E734 /* InputState.swift in Sources */, D461B792279DAC010070E734 /* InputState.swift in Sources */,
5B62A33D27AE7CC100A19448 /* ctlAboutWindow.swift in Sources */, 5B62A33D27AE7CC100A19448 /* ctlAboutWindow.swift in Sources */,
D47B92C027972AD100458394 /* main.swift in Sources */, D47B92C027972AD100458394 /* main.swift in Sources */,
D44FB74D2792189A003C80A6 /* PhraseReplacementMap.mm in Sources */,
D4A13D5A27A59F0B003BE359 /* ctlInputMethod.swift in Sources */, D4A13D5A27A59F0B003BE359 /* ctlInputMethod.swift in Sources */,
5BA9FD4827FEF3C9002DE248 /* PreferencesWindowController.swift in Sources */, 5BA9FD4827FEF3C9002DE248 /* PreferencesWindowController.swift in Sources */,
D4E569DC27A34D0E00AC2CEF /* KeyHandler.mm in Sources */, 5BD0113B28180D6100609769 /* LMInstantiator.swift in Sources */,
D4E569DC27A34D0E00AC2CEF /* CTools.m in Sources */,
5BA9FD4627FEF3C9002DE248 /* Container.swift in Sources */, 5BA9FD4627FEF3C9002DE248 /* Container.swift in Sources */,
D47F7DD0278C0897002F9DD7 /* ctlNonModalAlertWindow.swift in Sources */, D47F7DD0278C0897002F9DD7 /* ctlNonModalAlertWindow.swift in Sources */,
5B62A32F27AE78B000A19448 /* CoreLM.mm in Sources */, 5B38F5A2281E2E49007D5F5D /* 0_Megrez.swift in Sources */,
5BE78BE027B38804005EA1BE /* LMConsolidator.mm in Sources */, 5B949BD92816DC5400D87B5D /* LineReader.swift in Sources */,
D456576E279E4F7B00DF6BC9 /* InputHandler.swift in Sources */, D456576E279E4F7B00DF6BC9 /* InputHandler.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 */,
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 */,
5BD0113D2818543900609769 /* KeyHandler_Core.swift in Sources */,
5BA9FD4227FEF3C8002DE248 /* PreferencePane.swift in Sources */, 5BA9FD4227FEF3C8002DE248 /* PreferencePane.swift in Sources */,
5BA0DF312817857D009E73BB /* lmUserOverride.swift in Sources */,
5BA9FD8B28006B41002DE248 /* VDKComboBox.swift in Sources */, 5BA9FD8B28006B41002DE248 /* VDKComboBox.swift in Sources */,
D47D73AC27A6CAE600255A50 /* AssociatedPhrases.mm in Sources */,
5BA9FD4A27FEF3C9002DE248 /* PreferencesTabViewController.swift in Sources */, 5BA9FD4A27FEF3C9002DE248 /* PreferencesTabViewController.swift in Sources */,
5B62A34A27AE7CD900A19448 /* NotifierController.swift in Sources */, 5B62A34A27AE7CD900A19448 /* NotifierController.swift in Sources */,
5B11328927B94CFB00E58451 /* AppleKeyboardConverter.swift in Sources */, 5B11328927B94CFB00E58451 /* AppleKeyboardConverter.swift in Sources */,
D41355DB278E6D17005E5CBD /* LMInstantiator.mm in Sources */,
5B62A32927AE77D100A19448 /* FSEventStreamHelper.swift in Sources */, 5B62A32927AE77D100A19448 /* FSEventStreamHelper.swift in Sources */,
D47F7DD3278C1263002F9DD7 /* UserOverrideModel.cpp in Sources */, 5B38F59B281E2E49007D5F5D /* 7_KeyValuePair.swift in Sources */,
5B62A33627AE795800A19448 /* mgrPrefs.swift in Sources */, 5B62A33627AE795800A19448 /* mgrPrefs.swift in Sources */,
5B38F5A4281E2E49007D5F5D /* 5_LanguageModel.swift in Sources */,
5BAEFAD028012565001F42C9 /* mgrLangModel.swift in Sources */, 5BAEFAD028012565001F42C9 /* mgrLangModel.swift in Sources */,
5B782EC4280C243C007276DE /* KeyHandler_HandleCandidate.swift in Sources */, 5B782EC4280C243C007276DE /* KeyHandler_HandleCandidate.swift in Sources */,
5B62A33827AE79CD00A19448 /* NSStringUtils.swift in Sources */, 5B62A33827AE79CD00A19448 /* NSStringUtils.swift in Sources */,
5BA9FD0F27FEDB6B002DE248 /* suiPrefPaneGeneral.swift in Sources */, 5BA9FD0F27FEDB6B002DE248 /* suiPrefPaneGeneral.swift in Sources */,
5BA9FD4927FEF3C9002DE248 /* Section.swift in Sources */, 5BA9FD4927FEF3C9002DE248 /* Section.swift in Sources */,
5B407153281F94E6009C24CB /* Composer.mm in Sources */,
5BA9FD3E27FEF3C8002DE248 /* Utilities.swift in Sources */, 5BA9FD3E27FEF3C8002DE248 /* Utilities.swift in Sources */,
5BA9FD1127FEDB6B002DE248 /* ctlPrefUI.swift in Sources */, 5BA9FD1127FEDB6B002DE248 /* ctlPrefUI.swift in Sources */,
5B38F59C281E2E49007D5F5D /* 2_Grid.swift in Sources */,
5B40730D281672610023DFFF /* lmReplacements.swift in Sources */,
5B38F59E281E2E49007D5F5D /* 6_Bigram.swift in Sources */,
5B62A33227AE792F00A19448 /* InputSourceHelper.swift in Sources */, 5B62A33227AE792F00A19448 /* InputSourceHelper.swift in Sources */,
5B5E535227EF261400C6AA1E /* IME.swift in Sources */, 5B5E535227EF261400C6AA1E /* IME.swift in Sources */,
5B62A34927AE7CD900A19448 /* TooltipController.swift in Sources */, 5B62A34927AE7CD900A19448 /* TooltipController.swift in Sources */,
6A0D4F4515FC0EB100ABF4B3 /* Mandarin.cpp in Sources */, 6A0D4F4515FC0EB100ABF4B3 /* Mandarin.cpp in Sources */,
5B61B0CA280BEFD4002E3CFA /* KeyHandler_Misc.swift in Sources */, 5B61B0CA280BEFD4002E3CFA /* KeyHandler_Misc.swift in Sources */,
5B38F59A281E2E49007D5F5D /* 6_Unigram.swift in Sources */,
5B38F5A0281E2E49007D5F5D /* 1_Walker.swift in Sources */,
5B62A34827AE7CD900A19448 /* ctlCandidateVertical.swift in Sources */, 5B62A34827AE7CD900A19448 /* ctlCandidateVertical.swift in Sources */,
5BA9FD4027FEF3C8002DE248 /* Localization.swift in Sources */, 5BA9FD4027FEF3C8002DE248 /* Localization.swift in Sources */,
5BA9FD1327FEDB6B002DE248 /* suiPrefPaneDictionary.swift in Sources */, 5BA9FD1327FEDB6B002DE248 /* suiPrefPaneDictionary.swift in Sources */,
6ACC3D452793701600F1B140 /* ParselessLM.cpp in Sources */, 5B00A230282011980058E5DB /* lmLite.swift in Sources */,
5BBBB77A27AEDC690023B93A /* clsSFX.swift in Sources */, 5BBBB77A27AEDC690023B93A /* clsSFX.swift in Sources */,
5BA9FD4727FEF3C9002DE248 /* PreferencesStyleController.swift in Sources */, 5BA9FD4727FEF3C9002DE248 /* PreferencesStyleController.swift in Sources */,
5BF8423127BAA942008E7E4C /* vChewingKanjiConverter.swift in Sources */, 5BF8423127BAA942008E7E4C /* vChewingKanjiConverter.swift in Sources */,
5B949BDB2816DDBC00D87B5D /* LMConsolidator.swift in Sources */,
5B38F59F281E2E49007D5F5D /* 3_NodeAnchor.swift in Sources */,
5B62A34627AE7CD900A19448 /* ctlCandidateHorizontal.swift in Sources */, 5B62A34627AE7CD900A19448 /* ctlCandidateHorizontal.swift in Sources */,
5B62A34727AE7CD900A19448 /* ctlCandidate.swift in Sources */, 5B62A34727AE7CD900A19448 /* ctlCandidate.swift in Sources */,
5BA9FD3F27FEF3C8002DE248 /* Pane.swift in Sources */, 5BA9FD3F27FEF3C8002DE248 /* Pane.swift in Sources */,
5BB802DA27FABA8300CF1C19 /* ctlInputMethod_Menu.swift in Sources */, 5BB802DA27FABA8300CF1C19 /* ctlInputMethod_Menu.swift in Sources */,
D41355DE278EA3ED005E5CBD /* UserPhrasesLM.mm in Sources */, 5B38F5A1281E2E49007D5F5D /* 1_BlockReadingBuilder.swift in Sources */,
6ACC3D3F27914F2400F1B140 /* KeyValueBlobReader.cpp in Sources */,
D41355D8278D74B5005E5CBD /* mgrLangModel.mm in Sources */,
5BDC1CFA27FDF1310052C2B9 /* apiUpdate.swift in Sources */, 5BDC1CFA27FDF1310052C2B9 /* apiUpdate.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
@ -1490,6 +1502,7 @@
"$(OTHER_CFLAGS)", "$(OTHER_CFLAGS)",
"-fcxx-modules", "-fcxx-modules",
); );
SWIFT_COMPILATION_MODE = wholemodule;
}; };
name = Release; name = Release;
}; };