Pre Merge pull request !48 from ShikiSuen/upd/1.7.1

This commit is contained in:
ShikiSuen 2022-06-18 07:48:48 +00:00 committed by Gitee
commit f30981f176
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
30 changed files with 1223 additions and 1092 deletions

View File

@ -57,7 +57,7 @@ extension String {
} }
} }
// MARK: - // MARK: -
// Ref: https://stackoverflow.com/a/32581409/4162914 // Ref: https://stackoverflow.com/a/32581409/4162914
extension Float { extension Float {
@ -67,7 +67,7 @@ extension Float {
} }
} }
// MARK: - // MARK: -
// Ref: https://stackoverflow.com/a/41581695/4162914 // Ref: https://stackoverflow.com/a/41581695/4162914
precedencegroup ExponentiationPrecedence { precedencegroup ExponentiationPrecedence {

View File

@ -47,7 +47,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.
public static func crossConvert(_ string: String) -> String? { public static func crossConvert(_ string: String) -> String? {
switch ctlInputMethod.currentKeyHandler.inputMode { switch IME.currentInputMode {
case InputMode.imeModeCHS: case InputMode.imeModeCHS:
return shared.traditionalize?.convert(string) return shared.traditionalize?.convert(string)
case InputMode.imeModeCHT: case InputMode.imeModeCHT:

@ -1 +1 @@
Subproject commit 0b755c7332fce88e6d1073164447fc451ec317be Subproject commit 1b67116c77dd654f156c43754694ac3a17a19a3a

View File

@ -28,39 +28,40 @@ import Cocoa
// InputState 使 Struct Struct // InputState 使 Struct Struct
/// Represents the states for the input method controller. /// ctlInputMethod
/// ///
/// An input method is actually a finite state machine. It receives the inputs /// Finite State Machine/
/// from hardware like keyboard and mouse, changes its state, updates user /// 使
/// interface by the state, and finally produces the text output and then them /// 使
/// to the client apps. It should be a one-way data flow, and the user interface ///
/// and text output should follow unconditionally one single data source.
/// ///
/// The InputState class is for representing what the input controller is doing, /// InputState
/// and the place to store the variables that could be used. For example, the /// 使
/// array for the candidate list is useful only when the user is choosing a ///
/// candidate, and the array should not exist when the input controller is in ///
/// another state.
/// ///
/// They are immutable objects. When the state changes, the controller should /// InputState
/// create a new state object to replace the current state instead of modifying /// InputState.Marking InputState.Inputting
/// the existing one. ///
/// ///
/// The input controller has following possible states: ///
/// ///
/// - Deactivated: The user is not using the input method yet. /// - .Deactivated: 使使
/// - Empty: The user has switched to this input method but inputted nothing yet, /// - .AssociatedPhrases:
/// or, he or she has committed text into the client apps and starts a new /// 西 .NotEmpty
/// input phase. /// - .Empty: 使
/// - Committing: The input controller is sending text to the client apps. ///
/// - Inputting: The user has inputted something and the input buffer is /// - .EmptyIgnorePreviousState: Empty
/// visible. ///
/// - Marking: The user is creating a area in the input buffer and about to /// - .Committing:
/// create a new user phrase. /// - .NotEmpty:
/// - Choosing Candidate: The candidate window is open to let the user to choose /// - .Inputting: 使Compositor
/// one among the candidates. /// - .Marking: 使
///
/// - .ChoosingCandidate: 使
/// - .SymbolTable:
class InputState { class InputState {
/// Represents that the input controller is deactivated. /// .Deactivated: 使使
class Deactivated: InputState { class Deactivated: InputState {
var description: String { var description: String {
"<InputState.Deactivated>" "<InputState.Deactivated>"
@ -69,7 +70,8 @@ class InputState {
// MARK: - // MARK: -
/// Represents that the composing buffer is empty. /// .Empty: 使
///
class Empty: InputState { class Empty: InputState {
var composingBuffer: String { var composingBuffer: String {
"" ""
@ -82,7 +84,8 @@ class InputState {
// MARK: - // MARK: -
/// Represents that the composing buffer is empty. /// .EmptyIgnorePreviousState: Empty
///
class EmptyIgnoringPreviousState: Empty { class EmptyIgnoringPreviousState: Empty {
override var description: String { override var description: String {
"<InputState.EmptyIgnoringPreviousState>" "<InputState.EmptyIgnoringPreviousState>"
@ -91,30 +94,76 @@ class InputState {
// MARK: - // MARK: -
/// Represents that the input controller is committing text into client app. /// .Committing:
class Committing: InputState { class Committing: InputState {
private(set) var poppedText: String = "" private(set) var textToCommit: String = ""
convenience init(poppedText: String) { convenience init(textToCommit: String) {
self.init() self.init()
self.poppedText = poppedText self.textToCommit = textToCommit
} }
var description: String { var description: String {
"<InputState.Committing poppedText:\(poppedText)>" "<InputState.Committing textToCommit:\(textToCommit)>"
} }
} }
// MARK: - // MARK: -
/// Represents that the composing buffer is not empty. /// .AssociatedPhrases:
/// 西 .NotEmpty
class AssociatedPhrases: InputState {
private(set) var candidates: [String] = []
private(set) var isTypingVertical: Bool = false
init(candidates: [String], isTypingVertical: Bool) {
self.candidates = candidates
self.isTypingVertical = isTypingVertical
super.init()
}
var description: String {
"<InputState.AssociatedPhrases, candidates:\(candidates), isTypingVertical:\(isTypingVertical)>"
}
}
// MARK: -
/// .NotEmpty:
/// - .Inputting: 使Compositor
/// - .Marking: 使
///
/// - .ChoosingCandidate: 使
/// - .SymbolTable:
class NotEmpty: InputState { class NotEmpty: InputState {
private(set) var composingBuffer: String private(set) var composingBuffer: String
private(set) var cursorIndex: Int = 0 { didSet { cursorIndex = max(cursorIndex, 0) } } private(set) var cursorIndex: Int = 0 { didSet { cursorIndex = max(cursorIndex, 0) } }
var composingBufferConverted: String {
let converted = IME.kanjiConversionIfRequired(composingBuffer)
if converted.utf16.count != composingBuffer.utf16.count
|| converted.count != composingBuffer.count
{
return composingBuffer
}
return converted
}
init(composingBuffer: String, cursorIndex: Int) { init(composingBuffer: String, cursorIndex: Int) {
self.composingBuffer = composingBuffer self.composingBuffer = composingBuffer
self.cursorIndex = cursorIndex super.init()
defer { self.cursorIndex = cursorIndex }
}
var attributedString: NSAttributedString {
///
/// JIS
let attributedString = NSAttributedString(
string: composingBufferConverted,
attributes: [
.underlineStyle: NSUnderlineStyle.single.rawValue,
.markedClauseSegment: 0,
]
)
return attributedString
} }
var description: String { var description: String {
@ -124,34 +173,24 @@ class InputState {
// MARK: - // MARK: -
/// Represents that the user is inputting text. /// .Inputting: 使Compositor
class Inputting: NotEmpty { class Inputting: NotEmpty {
var poppedText: String = "" var textToCommit: String = ""
var tooltip: String = "" var tooltip: String = ""
override init(composingBuffer: String, cursorIndex: Int) { override init(composingBuffer: String, cursorIndex: Int) {
super.init(composingBuffer: composingBuffer, cursorIndex: cursorIndex) super.init(composingBuffer: composingBuffer, cursorIndex: cursorIndex)
} }
var attributedString: NSAttributedString {
let attributedString = NSAttributedString(
string: composingBuffer,
attributes: [
.underlineStyle: NSUnderlineStyle.single.rawValue,
.markedClauseSegment: 0,
]
)
return attributedString
}
override var description: String { override var description: String {
"<InputState.Inputting, composingBuffer:\(composingBuffer), cursorIndex:\(cursorIndex)>, poppedText:\(poppedText)>" "<InputState.Inputting, composingBuffer:\(composingBuffer), cursorIndex:\(cursorIndex)>, textToCommit:\(textToCommit)>"
} }
} }
// MARK: - // MARK: -
/// Represents that the user is marking a range in the composing buffer. /// .Marking: 使
///
class Marking: NotEmpty { class Marking: NotEmpty {
private var allowedMarkRange: ClosedRange<Int> = mgrPrefs.minCandidateLength...mgrPrefs.maxCandidateLength private var allowedMarkRange: ClosedRange<Int> = mgrPrefs.minCandidateLength...mgrPrefs.maxCandidateLength
private(set) var markerIndex: Int = 0 { didSet { markerIndex = max(markerIndex, 0) } } private(set) var markerIndex: Int = 0 { didSet { markerIndex = max(markerIndex, 0) } }
@ -201,7 +240,7 @@ class InputState {
let selectedReadings = readings[literalMarkedRange] let selectedReadings = readings[literalMarkedRange]
let joined = selectedReadings.joined(separator: "-") let joined = selectedReadings.joined(separator: "-")
let exist = mgrLangModel.checkIfUserPhraseExist( let exist = mgrLangModel.checkIfUserPhraseExist(
userPhrase: text, mode: ctlInputMethod.currentKeyHandler.inputMode, key: joined userPhrase: text, mode: IME.currentInputMode, key: joined
) )
if exist { if exist {
deleteTargetExists = exist deleteTargetExists = exist
@ -223,16 +262,18 @@ class InputState {
private(set) var readings: [String] private(set) var readings: [String]
init(composingBuffer: String, cursorIndex: Int, markerIndex: Int, readings: [String]) { init(composingBuffer: String, cursorIndex: Int, markerIndex: Int, readings: [String]) {
self.markerIndex = markerIndex
let begin = min(cursorIndex, markerIndex) let begin = min(cursorIndex, markerIndex)
let end = max(cursorIndex, markerIndex) let end = max(cursorIndex, markerIndex)
markedRange = begin..<end markedRange = begin..<end
self.readings = readings self.readings = readings
super.init(composingBuffer: composingBuffer, cursorIndex: cursorIndex) super.init(composingBuffer: composingBuffer, cursorIndex: cursorIndex)
defer { self.markerIndex = markerIndex }
} }
var attributedString: NSAttributedString { override var attributedString: NSAttributedString {
let attributedString = NSMutableAttributedString(string: composingBuffer) ///
/// JIS
let attributedString = NSMutableAttributedString(string: composingBufferConverted)
let end = markedRange.upperBound let end = markedRange.upperBound
attributedString.setAttributes( attributedString.setAttributes(
@ -292,7 +333,7 @@ class InputState {
let selectedReadings = readings[literalMarkedRange] let selectedReadings = readings[literalMarkedRange]
let joined = selectedReadings.joined(separator: "-") let joined = selectedReadings.joined(separator: "-")
return mgrLangModel.checkIfUserPhraseExist( return mgrLangModel.checkIfUserPhraseExist(
userPhrase: text, mode: ctlInputMethod.currentKeyHandler.inputMode, key: joined userPhrase: text, mode: IME.currentInputMode, key: joined
) )
} }
@ -315,7 +356,7 @@ class InputState {
// MARK: - // MARK: -
/// Represents that the user is choosing in a candidates list. /// .ChoosingCandidate: 使
class ChoosingCandidate: NotEmpty { class ChoosingCandidate: NotEmpty {
private(set) var candidates: [String] private(set) var candidates: [String]
private(set) var isTypingVertical: Bool private(set) var isTypingVertical: Bool
@ -326,17 +367,6 @@ class InputState {
super.init(composingBuffer: composingBuffer, cursorIndex: cursorIndex) super.init(composingBuffer: composingBuffer, cursorIndex: cursorIndex)
} }
var attributedString: NSAttributedString {
let attributedString = NSAttributedString(
string: composingBuffer,
attributes: [
.underlineStyle: NSUnderlineStyle.single.rawValue,
.markedClauseSegment: 0,
]
)
return attributedString
}
override var description: String { override var description: String {
"<InputState.ChoosingCandidate, candidates:\(candidates), isTypingVertical:\(isTypingVertical), composingBuffer:\(composingBuffer), cursorIndex:\(cursorIndex)>" "<InputState.ChoosingCandidate, candidates:\(candidates), isTypingVertical:\(isTypingVertical), composingBuffer:\(composingBuffer), cursorIndex:\(cursorIndex)>"
} }
@ -344,22 +374,7 @@ class InputState {
// MARK: - // MARK: -
/// Represents that the user is choosing in a candidates list /// .SymbolTable:
/// in the associated phrases mode.
class AssociatedPhrases: InputState {
private(set) var candidates: [String] = []
private(set) var isTypingVertical: Bool = false
init(candidates: [String], isTypingVertical: Bool) {
self.candidates = candidates
self.isTypingVertical = isTypingVertical
super.init()
}
var description: String {
"<InputState.AssociatedPhrases, candidates:\(candidates), isTypingVertical:\(isTypingVertical)>"
}
}
class SymbolTable: ChoosingCandidate { class SymbolTable: ChoosingCandidate {
var node: SymbolNode var node: SymbolNode
@ -393,75 +408,3 @@ class InputState {
} }
} }
} }
class SymbolNode {
var title: String
var children: [SymbolNode]?
init(_ title: String, _ children: [SymbolNode]? = nil) {
self.title = title
self.children = children
}
init(_ title: String, symbols: String) {
self.title = title
children = Array(symbols).map { SymbolNode(String($0), nil) }
}
static let catCommonSymbols = String(
format: NSLocalizedString("catCommonSymbols", comment: ""))
static let catHoriBrackets = String(
format: NSLocalizedString("catHoriBrackets", comment: ""))
static let catVertBrackets = String(
format: NSLocalizedString("catVertBrackets", comment: ""))
static let catGreekLetters = String(
format: NSLocalizedString("catGreekLetters", comment: ""))
static let catMathSymbols = String(
format: NSLocalizedString("catMathSymbols", comment: ""))
static let catCurrencyUnits = String(
format: NSLocalizedString("catCurrencyUnits", comment: ""))
static let catSpecialSymbols = String(
format: NSLocalizedString("catSpecialSymbols", comment: ""))
static let catUnicodeSymbols = String(
format: NSLocalizedString("catUnicodeSymbols", comment: ""))
static let catCircledKanjis = String(
format: NSLocalizedString("catCircledKanjis", comment: ""))
static let catCircledKataKana = String(
format: NSLocalizedString("catCircledKataKana", comment: ""))
static let catBracketKanjis = String(
format: NSLocalizedString("catBracketKanjis", comment: ""))
static let catSingleTableLines = String(
format: NSLocalizedString("catSingleTableLines", comment: ""))
static let catDoubleTableLines = String(
format: NSLocalizedString("catDoubleTableLines", comment: ""))
static let catFillingBlocks = String(
format: NSLocalizedString("catFillingBlocks", comment: ""))
static let catLineSegments = String(
format: NSLocalizedString("catLineSegments", comment: ""))
static let root: SymbolNode = .init(
"/",
[
SymbolNode(""),
SymbolNode(catCommonSymbols, symbols: ",、。.?!;:‧‥﹐﹒˙·‘’“”〝〞‵′〃~$%@&#*"),
SymbolNode(catHoriBrackets, symbols: "()「」〔〕{}〈〉『』《》【】﹙﹚﹝﹞﹛﹜"),
SymbolNode(catVertBrackets, symbols: "︵︶﹁﹂︹︺︷︸︿﹀﹃﹄︽︾︻︼"),
SymbolNode(
catGreekLetters, symbols: "αβγδεζηθικλμνξοπρστυφχψωΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ"
),
SymbolNode(catMathSymbols, symbols: "+-×÷=≠≒∞±√<>﹤﹥≦≧∩∪ˇ⊥∠∟⊿㏒㏑∫∮∵∴╳﹢"),
SymbolNode(catCurrencyUnits, symbols: "$€¥¢£₽₨₩฿₺₮₱₭₴₦৲৳૱௹﷼₹₲₪₡₫៛₵₢₸₤₳₥₠₣₰₧₯₶₷"),
SymbolNode(catSpecialSymbols, symbols: "↑↓←→↖↗↙↘↺⇧⇩⇦⇨⇄⇆⇅⇵↻◎○●⊕⊙※△▲☆★◇◆□■▽▼§¥〒¢£♀♂↯"),
SymbolNode(catUnicodeSymbols, symbols: "♨☀☁☂☃♠♥♣♦♩♪♫♬☺☻"),
SymbolNode(catCircledKanjis, symbols: "㊟㊞㊚㊛㊊㊋㊌㊍㊎㊏㊐㊑㊒㊓㊔㊕㊖㊗︎㊘㊙︎㊜㊝㊠㊡㊢㊣㊤㊥㊦㊧㊨㊩㊪㊫㊬㊭㊮㊯㊰🈚︎🈯︎"),
SymbolNode(
catCircledKataKana, symbols: "㋐㋑㋒㋓㋔㋕㋖㋗㋘㋙㋚㋛㋜㋝㋞㋟㋠㋡㋢㋣㋤㋥㋦㋧㋨㋩㋪㋫㋬㋭㋮㋯㋰㋱㋲㋳㋴㋵㋶㋷㋸㋹㋺㋻㋼㋾"
),
SymbolNode(catBracketKanjis, symbols: "㈪㈫㈬㈭㈮㈯㈰㈱㈲㈳㈴㈵㈶㈷㈸㈹㈺㈻㈼㈽㈾㈿㉀㉁㉂㉃"),
SymbolNode(catSingleTableLines, symbols: "├─┼┴┬┤┌┐╞═╪╡│▕└┘╭╮╰╯"),
SymbolNode(catDoubleTableLines, symbols: "╔╦╗╠═╬╣╓╥╖╒╤╕║╚╩╝╟╫╢╙╨╜╞╪╡╘╧╛"),
SymbolNode(catFillingBlocks, symbols: "_ˍ▁▂▃▄▅▆▇█▏▎▍▌▋▊▉◢◣◥◤"),
SymbolNode(catLineSegments, symbols: "﹣﹦≡|∣∥–︱—︳╴¯ ̄﹉﹊﹍﹎﹋﹌﹏︴∕﹨╱╲/\"),
]
)
}

View File

@ -24,16 +24,15 @@ 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.
*/ */
/// 調
/// Megrez Tekkon
/// composer compositor
import Cocoa import Cocoa
public enum InputMode: String { // MARK: - (Delegate).
case imeModeCHS = "org.atelierInmu.inputmethod.vChewing.IMECHS"
case imeModeCHT = "org.atelierInmu.inputmethod.vChewing.IMECHT"
case imeModeNULL = ""
}
// MARK: - Delegate.
/// KeyHandler
protocol KeyHandlerDelegate { protocol KeyHandlerDelegate {
func ctlCandidate() -> ctlCandidate func ctlCandidate() -> ctlCandidate
func keyHandler( func keyHandler(
@ -44,80 +43,81 @@ protocol KeyHandlerDelegate {
-> Bool -> Bool
} }
// MARK: - Kernel. // MARK: - (Kernel).
/// KeyHandler 調
class KeyHandler { class KeyHandler {
///
let kEpsilon: Double = 0.000001 let kEpsilon: Double = 0.000001
let kMaxComposingBufferNeedsToWalkSize: Int = 10
var _composer: Tekkon.Composer = .init()
var _inputMode: String = ""
var _languageModel: vChewing.LMInstantiator = .init()
var _userOverrideModel: vChewing.LMUserOverride = .init()
var _builder: Megrez.BlockReadingBuilder
var _walkedNodes: [Megrez.NodeAnchor] = []
///
let kMaxComposingBufferNeedsToWalkSize = Int(max(12, ceil(Double(mgrPrefs.composingBufferSize) / 2)))
var composer: Tekkon.Composer = .init() //
var compositor: Megrez.Compositor //
var currentLM: vChewing.LMInstantiator = .init() //
var currentUOM: vChewing.LMUserOverride = .init() //
var walkedAnchors: [Megrez.NodeAnchor] = [] //
/// (ctlInputMethod)便
var delegate: KeyHandlerDelegate? var delegate: KeyHandlerDelegate?
var inputMode: InputMode { /// InputMode
get { /// IME UserPrefs
switch _inputMode { var inputMode: InputMode = IME.currentInputMode {
case "org.atelierInmu.inputmethod.vChewing.IMECHS": willSet {
return InputMode.imeModeCHS //
case "org.atelierInmu.inputmethod.vChewing.IMECHT":
return InputMode.imeModeCHT
default:
return InputMode.imeModeNULL
}
}
set {
let isCHS: Bool = (newValue == InputMode.imeModeCHS) let isCHS: Bool = (newValue == InputMode.imeModeCHS)
/// ctlInputMethod IME
// ctlInputMethod: IME.currentInputMode = newValue
ctlInputMethod.currentInputMode = isCHS ? InputMode.imeModeCHS.rawValue : InputMode.imeModeCHT.rawValue mgrPrefs.mostRecentInputMode = IME.currentInputMode.rawValue
mgrPrefs.mostRecentInputMode = ctlInputMethod.currentInputMode ///
currentLM = isCHS ? mgrLangModel.lmCHS : mgrLangModel.lmCHT
// _inputMode ctlInputMethod currentUOM = isCHS ? mgrLangModel.uomCHS : mgrLangModel.uomCHT
if _inputMode != ctlInputMethod.currentInputMode { ///
// Reinitiate language models if necessary syncBaseLMPrefs()
_languageModel = isCHS ? mgrLangModel.lmCHS : mgrLangModel.lmCHT ///
_userOverrideModel = isCHS ? mgrLangModel.uomCHS : mgrLangModel.uomCHT ///
ensureCompositor()
// Synchronize the sub-languageModel state settings to the new LM. ensureParser()
syncBaseLMPrefs()
// Create new grid builder and clear the composer.
createNewBuilder()
_composer.clear()
}
//
_inputMode = ctlInputMethod.currentInputMode
} }
} }
///
public init() { public init() {
_builder = Megrez.BlockReadingBuilder(lm: _languageModel, separator: "-") /// ensureCompositor()
compositor = Megrez.Compositor(lm: currentLM, separator: "-")
///
ensureParser() ensureParser()
inputMode = InputMode(rawValue: ctlInputMethod.currentInputMode) ?? InputMode.imeModeNULL /// inputMode
/// defer willSet
defer { inputMode = IME.currentInputMode }
} }
func clear() { func clear() {
_composer.clear() composer.clear()
_builder.clear() compositor.clear()
_walkedNodes.removeAll() walkedAnchors.removeAll()
} }
// MARK: - Functions dealing with Megrez. // MARK: - Functions dealing with Megrez.
func walk() { /// Megrez 使便
// Retrieve the most likely grid, i.e. a Maximum Likelihood Estimation ///
// of the best possible Mandarin characters given the input syllables, /// 使 Node Crossing
// using the Viterbi algorithm implemented in the Megrez library. var actualCandidateCursorIndex: Int {
// The walk() traces the grid to the end. mgrPrefs.useRearCursorMode ? min(compositorCursorIndex, compositorLength - 1) : max(compositorCursorIndex, 1)
_walkedNodes = _builder.walk() }
// if DEBUG mode is enabled, a GraphViz file is written to kGraphVizOutputfile. ///
///
/// Viterbi
///
///
func walk() {
walkedAnchors = compositor.walk()
// GraphViz
if mgrPrefs.isDebugModeEnabled { if mgrPrefs.isDebugModeEnabled {
let result = _builder.grid.dumpDOT let result = compositor.grid.dumpDOT
do { do {
try result.write( try result.write(
toFile: "/private/var/tmp/vChewing-visualization.dot", toFile: "/private/var/tmp/vChewing-visualization.dot",
@ -129,49 +129,54 @@ class KeyHandler {
} }
} }
///
///
/// Viterbi 使 O(N^2)
/// 使
/// 使
///
var popOverflowComposingTextAndWalk: String { var popOverflowComposingTextAndWalk: String {
// In ideal situations we can allow users to type infinitely in a buffer. var textToCommit = ""
// However, Viberti algorithm has a complexity of O(N^2), the walk will if compositor.grid.width > mgrPrefs.composingBufferSize {
// become slower as the number of nodes increase. Therefore, we need to if !walkedAnchors.isEmpty {
// auto-commit overflown texts which usually lose their influence over let anchor: Megrez.NodeAnchor = walkedAnchors[0]
// 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.isEmpty {
let anchor: Megrez.NodeAnchor = _walkedNodes[0]
if let theNode = anchor.node { if let theNode = anchor.node {
poppedText = theNode.currentKeyValue.value textToCommit = theNode.currentKeyValue.value
} }
_builder.removeHeadReadings(count: anchor.spanningLength) compositor.removeHeadReadings(count: anchor.spanningLength)
} }
} }
walk() walk()
return poppedText return textToCommit
} }
///
/// - Parameter key:
/// - Returns:
/// nil
func buildAssociatePhraseArray(withKey key: String) -> [String] { func buildAssociatePhraseArray(withKey key: String) -> [String] {
var arrResult: [String] = [] var arrResult: [String] = []
if _languageModel.hasAssociatedPhrasesForKey(key) { if currentLM.hasAssociatedPhrasesForKey(key) {
arrResult.append(contentsOf: _languageModel.associatedPhrasesForKey(key)) arrResult.append(contentsOf: currentLM.associatedPhrasesForKey(key))
} }
return arrResult return arrResult
} }
///
///
/// - Parameters:
/// - value:
/// - respectCursorPushing: true
func fixNode(value: String, respectCursorPushing: Bool = true) { func fixNode(value: String, respectCursorPushing: Bool = true) {
let cursorIndex = min(actualCandidateCursorIndex + (mgrPrefs.useRearCursorMode ? 1 : 0), builderLength) let cursorIndex = min(actualCandidateCursorIndex + (mgrPrefs.useRearCursorMode ? 1 : 0), compositorLength)
_builder.grid.fixNodeSelectedCandidate(location: cursorIndex, value: value) compositor.grid.fixNodeSelectedCandidate(location: cursorIndex, value: value)
// // // //
// let selectedNode: Megrez.NodeAnchor = _builder.grid.fixNodeSelectedCandidate( // let selectedNode: Megrez.NodeAnchor = compositor.grid.fixNodeSelectedCandidate(
// location: cursorIndex, value: value // location: cursorIndex, value: value
// ) // )
// // // //
// if !mgrPrefs.useSCPCTypingMode { // 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 // var addToUserOverrideModel = true
// if selectedNode.spanningLength != value.count { // if selectedNode.spanningLength != value.count {
// IME.prtDebugIntel("UOM: SpanningLength != value.count, dismissing.") // IME.prtDebugIntel("UOM: SpanningLength != value.count, dismissing.")
@ -179,7 +184,7 @@ class KeyHandler {
// } // }
// if addToUserOverrideModel { // if addToUserOverrideModel {
// if let theNode = selectedNode.node { // if let theNode = selectedNode.node {
// // SymbolLM Score -12 // // SymbolLM Score -12
// if theNode.scoreFor(candidate: value) <= -12 { // if theNode.scoreFor(candidate: value) <= -12 {
// IME.prtDebugIntel("UOM: Score <= -12, dismissing.") // IME.prtDebugIntel("UOM: Score <= -12, dismissing.")
// addToUserOverrideModel = false // addToUserOverrideModel = false
@ -188,26 +193,49 @@ class KeyHandler {
// } // }
// if addToUserOverrideModel { // if addToUserOverrideModel {
// IME.prtDebugIntel("UOM: Start Observation.") // IME.prtDebugIntel("UOM: Start Observation.")
// _userOverrideModel.observe( // // trigram
// walkedNodes: _walkedNodes, cursorIndex: cursorIndex, candidate: value, // // trigram
// currentUOM.observe(
// walkedNodes: walkedAnchors, cursorIndex: cursorIndex, candidate: value,
// timestamp: NSDate().timeIntervalSince1970 // timestamp: NSDate().timeIntervalSince1970
// ) // )
// } // }
// } // }
walk() walk()
///
if mgrPrefs.moveCursorAfterSelectingCandidate, respectCursorPushing { if mgrPrefs.moveCursorAfterSelectingCandidate, respectCursorPushing {
var nextPosition = 0 var nextPosition = 0
for node in _walkedNodes { for node in walkedAnchors {
if nextPosition >= cursorIndex { break } if nextPosition >= cursorIndex { break }
nextPosition += node.spanningLength nextPosition += node.spanningLength
} }
if nextPosition <= builderLength { if nextPosition <= compositorLength {
builderCursorIndex = nextPosition compositorCursorIndex = nextPosition
} }
} }
} }
///
func markNodesFixedIfNecessary() {
let width = compositor.grid.width
if width <= kMaxComposingBufferNeedsToWalkSize {
return
}
var index = 0
for anchor in walkedAnchors {
guard let node = anchor.node else { break }
if index >= width - kMaxComposingBufferNeedsToWalkSize { break }
if node.score < node.kSelectedCandidateScore {
compositor.grid.fixNodeSelectedCandidate(
location: index + anchor.spanningLength, value: node.currentKeyValue.value
)
}
index += anchor.spanningLength
}
}
///
var candidatesArray: [String] { var candidatesArray: [String] {
var arrCandidates: [String] = [] var arrCandidates: [String] = []
var arrNodes: [Megrez.NodeAnchor] = [] var arrNodes: [Megrez.NodeAnchor] = []
@ -233,20 +261,23 @@ class KeyHandler {
return arrCandidates return arrCandidates
} }
///
func dealWithOverrideModelSuggestions() { func dealWithOverrideModelSuggestions() {
/// trigram
let overrideValue = let overrideValue =
mgrPrefs.useSCPCTypingMode mgrPrefs.useSCPCTypingMode
? "" ? ""
: _userOverrideModel.suggest( : currentUOM.suggest(
walkedNodes: _walkedNodes, cursorIndex: builderCursorIndex, walkedNodes: walkedAnchors, cursorIndex: compositorCursorIndex,
timestamp: NSDate().timeIntervalSince1970 timestamp: NSDate().timeIntervalSince1970
) )
///
if !overrideValue.isEmpty { if !overrideValue.isEmpty {
IME.prtDebugIntel( IME.prtDebugIntel(
"UOM: Suggestion retrieved, overriding the node score of the selected candidate.") "UOM: Suggestion retrieved, overriding the node score of the selected candidate.")
_builder.grid.overrideNodeScoreForSelectedCandidate( compositor.grid.overrideNodeScoreForSelectedCandidate(
location: min(actualCandidateCursorIndex + (mgrPrefs.useRearCursorMode ? 1 : 0), builderLength), location: min(actualCandidateCursorIndex + (mgrPrefs.useRearCursorMode ? 1 : 0), compositorLength),
value: overrideValue, value: overrideValue,
overridingScore: findHighestScore(nodes: rawNodes, epsilon: kEpsilon) overridingScore: findHighestScore(nodes: rawNodes, epsilon: kEpsilon)
) )
@ -255,6 +286,11 @@ class KeyHandler {
} }
} }
///
/// - Parameters:
/// - nodes:
/// - epsilon:
/// - Returns:
func findHighestScore(nodes: [Megrez.NodeAnchor], epsilon: Double) -> Double { func findHighestScore(nodes: [Megrez.NodeAnchor], epsilon: Double) -> Double {
var highestScore: Double = 0 var highestScore: Double = 0
for currentAnchor in nodes { for currentAnchor in nodes {
@ -270,92 +306,144 @@ class KeyHandler {
// MARK: - Extracted methods and functions (Tekkon). // MARK: - Extracted methods and functions (Tekkon).
/// _
var currentMandarinParser: String {
mgrPrefs.mandarinParserName + "_"
}
///
func ensureParser() { func ensureParser() {
switch mgrPrefs.mandarinParser { switch mgrPrefs.mandarinParser {
case MandarinParser.ofStandard.rawValue: case MandarinParser.ofStandard.rawValue:
_composer.ensureParser(arrange: .ofDachen) composer.ensureParser(arrange: .ofDachen)
case MandarinParser.ofDachen26.rawValue: case MandarinParser.ofDachen26.rawValue:
_composer.ensureParser(arrange: .ofDachen26) composer.ensureParser(arrange: .ofDachen26)
case MandarinParser.ofEten.rawValue: case MandarinParser.ofETen.rawValue:
_composer.ensureParser(arrange: .ofEten) composer.ensureParser(arrange: .ofETen)
case MandarinParser.ofHsu.rawValue: case MandarinParser.ofHsu.rawValue:
_composer.ensureParser(arrange: .ofHsu) composer.ensureParser(arrange: .ofHsu)
case MandarinParser.ofEten26.rawValue: case MandarinParser.ofETen26.rawValue:
_composer.ensureParser(arrange: .ofEten26) composer.ensureParser(arrange: .ofETen26)
case MandarinParser.ofIBM.rawValue: case MandarinParser.ofIBM.rawValue:
_composer.ensureParser(arrange: .ofIBM) composer.ensureParser(arrange: .ofIBM)
case MandarinParser.ofMiTAC.rawValue: case MandarinParser.ofMiTAC.rawValue:
_composer.ensureParser(arrange: .ofMiTAC) composer.ensureParser(arrange: .ofMiTAC)
case MandarinParser.ofFakeSeigyou.rawValue: case MandarinParser.ofFakeSeigyou.rawValue:
_composer.ensureParser(arrange: .ofFakeSeigyou) composer.ensureParser(arrange: .ofFakeSeigyou)
case MandarinParser.ofHanyuPinyin.rawValue: case MandarinParser.ofHanyuPinyin.rawValue:
_composer.ensureParser(arrange: .ofHanyuPinyin) composer.ensureParser(arrange: .ofHanyuPinyin)
case MandarinParser.ofSecondaryPinyin.rawValue: case MandarinParser.ofSecondaryPinyin.rawValue:
_composer.ensureParser(arrange: .ofSecondaryPinyin) composer.ensureParser(arrange: .ofSecondaryPinyin)
case MandarinParser.ofYalePinyin.rawValue: case MandarinParser.ofYalePinyin.rawValue:
_composer.ensureParser(arrange: .ofYalePinyin) composer.ensureParser(arrange: .ofYalePinyin)
case MandarinParser.ofHualuoPinyin.rawValue: case MandarinParser.ofHualuoPinyin.rawValue:
_composer.ensureParser(arrange: .ofHualuoPinyin) composer.ensureParser(arrange: .ofHualuoPinyin)
case MandarinParser.ofUniversalPinyin.rawValue: case MandarinParser.ofUniversalPinyin.rawValue:
_composer.ensureParser(arrange: .ofUniversalPinyin) composer.ensureParser(arrange: .ofUniversalPinyin)
default: default:
_composer.ensureParser(arrange: .ofDachen) composer.ensureParser(arrange: .ofDachen)
mgrPrefs.mandarinParser = MandarinParser.ofStandard.rawValue mgrPrefs.mandarinParser = MandarinParser.ofStandard.rawValue
} }
_composer.clear() composer.clear()
}
/// Ruby
/// - Parameters:
/// - target:
/// - newSeparator:
/// - Returns:
func cnvZhuyinKeyToTextbookReading(target: String, newSeparator: String = "-") -> String {
var arrReturn: [String] = []
for neta in target.split(separator: "-") {
var newString = String(neta)
if String(neta.reversed()[0]) == "˙" {
newString = String(neta.dropLast())
newString.insert("˙", at: newString.startIndex)
}
arrReturn.append(newString)
}
return arrReturn.joined(separator: newSeparator)
}
/// Ruby
/// - Parameters:
/// - target:
/// - newSeparator:
/// - Returns:
func restoreToneOneInZhuyinKey(target: String, newSeparator: String = "-") -> String {
var arrReturn: [String] = []
for neta in target.split(separator: "-") {
var newNeta = String(neta)
if !"ˊˇˋ˙".contains(String(neta.reversed()[0])), !neta.contains("_") {
newNeta += "1"
}
arrReturn.append(newNeta)
}
return arrReturn.joined(separator: newSeparator)
} }
// MARK: - Extracted methods and functions (Megrez). // MARK: - Extracted methods and functions (Megrez).
var isBuilderEmpty: Bool { _builder.grid.width == 0 } ///
var isCompositorEmpty: Bool { compositor.isEmpty }
///
var rawNodes: [Megrez.NodeAnchor] { var rawNodes: [Megrez.NodeAnchor] {
/// 使 nodesCrossing macOS /// 使 nodesCrossing macOS
/// nodeCrossing /// nodeCrossing
mgrPrefs.useRearCursorMode mgrPrefs.useRearCursorMode
? _builder.grid.nodesBeginningAt(location: actualCandidateCursorIndex) ? compositor.grid.nodesBeginningAt(location: actualCandidateCursorIndex)
: _builder.grid.nodesEndingAt(location: actualCandidateCursorIndex) : compositor.grid.nodesEndingAt(location: actualCandidateCursorIndex)
} }
///
func syncBaseLMPrefs() { func syncBaseLMPrefs() {
_languageModel.isPhraseReplacementEnabled = mgrPrefs.phraseReplacementEnabled currentLM.isPhraseReplacementEnabled = mgrPrefs.phraseReplacementEnabled
_languageModel.isCNSEnabled = mgrPrefs.cns11643Enabled currentLM.isCNSEnabled = mgrPrefs.cns11643Enabled
_languageModel.isSymbolEnabled = mgrPrefs.symbolInputEnabled currentLM.isSymbolEnabled = mgrPrefs.symbolInputEnabled
} }
func createNewBuilder() { /// 使
// Each Mandarin syllable is separated by a hyphen. func ensureCompositor() {
_builder = Megrez.BlockReadingBuilder(lm: _languageModel, separator: "-") // 西
compositor = Megrez.Compositor(lm: currentLM, separator: "-")
} }
var currentReadings: [String] { _builder.readings } ///
var currentReadings: [String] { compositor.readings }
///
func ifLangModelHasUnigrams(forKey reading: String) -> Bool { func ifLangModelHasUnigrams(forKey reading: String) -> Bool {
_languageModel.hasUnigramsFor(key: reading) currentLM.hasUnigramsFor(key: reading)
} }
func insertReadingToBuilderAtCursor(reading: String) { ///
_builder.insertReadingAtCursor(reading: reading) func insertToCompositorAtCursor(reading: String) {
compositor.insertReadingAtCursor(reading: reading)
} }
var builderCursorIndex: Int { ///
get { _builder.cursorIndex } var compositorCursorIndex: Int {
set { _builder.cursorIndex = newValue } get { compositor.cursorIndex }
set { compositor.cursorIndex = newValue }
} }
var builderLength: Int { ///
_builder.length var compositorLength: Int {
compositor.length
} }
func deleteBuilderReadingInFrontOfCursor() { ///
_builder.deleteReadingAtTheRearOfCursor() ///
/// Rear
func deleteCompositorReadingAtTheRearOfCursor() {
compositor.deleteReadingAtTheRearOfCursor()
} }
func deleteBuilderReadingToTheFrontOfCursor() { ///
_builder.deleteReadingToTheFrontOfCursor() ///
} /// Front
func deleteCompositorReadingToTheFrontOfCursor() {
var keyLengthAtIndexZero: Int { compositor.deleteReadingToTheFrontOfCursor()
_walkedNodes[0].node?.currentKeyValue.value.count ?? 0
} }
} }

View File

@ -24,9 +24,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.
*/ */
/// 調
import Cocoa import Cocoa
// MARK: - § Handle Candidate State. // MARK: - § 調 (Handle Candidate State).
extension KeyHandler { extension KeyHandler {
func handleCandidate( func handleCandidate(
@ -43,7 +45,7 @@ extension KeyHandler {
return true return true
} }
// MARK: Cancel Candidate // MARK: (Cancel Candidate)
let cancelCandidateKey = let cancelCandidateKey =
input.isBackSpace || input.isESC || input.isDelete input.isBackSpace || input.isESC || input.isDelete
@ -52,12 +54,12 @@ extension KeyHandler {
if cancelCandidateKey { if cancelCandidateKey {
if (state is InputState.AssociatedPhrases) if (state is InputState.AssociatedPhrases)
|| mgrPrefs.useSCPCTypingMode || mgrPrefs.useSCPCTypingMode
|| isBuilderEmpty || isCompositorEmpty
{ {
// //
// //
// 使 BackSpace // 使 BackSpace
// isBuilderEmpty // isCompositorEmpty
clear() clear()
stateCallback(InputState.EmptyIgnoringPreviousState()) stateCallback(InputState.EmptyIgnoringPreviousState())
} else { } else {
@ -286,7 +288,7 @@ extension KeyHandler {
} }
} }
// MARK: - Associated Phrases // MARK: (Associated Phrases)
if state is InputState.AssociatedPhrases { if state is InputState.AssociatedPhrases {
if !input.isShiftHold { return false } if !input.isShiftHold { return false }
@ -322,9 +324,13 @@ extension KeyHandler {
if state is InputState.AssociatedPhrases { return false } if state is InputState.AssociatedPhrases { return false }
// MARK: SCPC Mode Processing // MARK: (SCPC Mode Processing)
if mgrPrefs.useSCPCTypingMode { if mgrPrefs.useSCPCTypingMode {
///
/// - /
/// -
var punctuationNamePrefix = "" var punctuationNamePrefix = ""
if input.isOptionHold && !input.isControlHold { if input.isOptionHold && !input.isControlHold {
@ -346,11 +352,13 @@ extension KeyHandler {
] ]
let customPunctuation: String = arrCustomPunctuations.joined(separator: "") let customPunctuation: String = arrCustomPunctuations.joined(separator: "")
///
let arrPunctuations: [String] = [punctuationNamePrefix, String(format: "%c", CChar(charCode))] let arrPunctuations: [String] = [punctuationNamePrefix, String(format: "%c", CChar(charCode))]
let punctuation: String = arrPunctuations.joined(separator: "") let punctuation: String = arrPunctuations.joined(separator: "")
var shouldAutoSelectCandidate: Bool = var shouldAutoSelectCandidate: Bool =
_composer.inputValidityCheck(key: charCode) || ifLangModelHasUnigrams(forKey: customPunctuation) composer.inputValidityCheck(key: charCode) || ifLangModelHasUnigrams(forKey: customPunctuation)
|| ifLangModelHasUnigrams(forKey: punctuation) || ifLangModelHasUnigrams(forKey: punctuation)
if !shouldAutoSelectCandidate, input.isUpperCaseASCIILetterKey { if !shouldAutoSelectCandidate, input.isUpperCaseASCIILetterKey {

View File

@ -24,10 +24,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.
*/ */
import Cocoa /// 調 IMK 調
import SwiftUI /// 調
// MARK: - § Handle Input with States. import Cocoa
// MARK: - § 調 (Handle Input with States)
extension KeyHandler { extension KeyHandler {
func handle( func handle(
@ -37,10 +39,9 @@ extension KeyHandler {
errorCallback: @escaping () -> Void errorCallback: @escaping () -> Void
) -> Bool { ) -> Bool {
let charCode: UniChar = input.charCode let charCode: UniChar = input.charCode
var state = state // Turn this incoming constant into variable. var state = state //
// Ignore the input if its inputText is empty. // inputTest
// Reason: such inputs may be functional key combinations.
guard let inputText: String = input.inputText, !inputText.isEmpty else { guard let inputText: String = input.inputText, !inputText.isEmpty else {
return false return false
} }
@ -53,8 +54,7 @@ extension KeyHandler {
return true return true
} }
// Ignore the input if the composing buffer is empty with no reading //
// and there is some function key combination.
let isFunctionKey: Bool = let isFunctionKey: Bool =
input.isControlHotKey || (input.isCommandHold || input.isOptionHotKey || input.isNumericPad) input.isControlHotKey || (input.isCommandHold || input.isOptionHotKey || input.isNumericPad)
if !(state is InputState.NotEmpty) && !(state is InputState.AssociatedPhrases) && isFunctionKey { if !(state is InputState.NotEmpty) && !(state is InputState.AssociatedPhrases) && isFunctionKey {
@ -63,37 +63,40 @@ extension KeyHandler {
// MARK: Caps Lock processing. // MARK: Caps Lock processing.
// If Caps Lock is ON, temporarily disable phonetic reading. /// Caps Lock
// Note: Alphanumerical mode processing. /// Shift Chromium
/// IMK Shift 使
/// Caps Lock
if input.isBackSpace || input.isEnter || input.isAbsorbedArrowKey || input.isExtraChooseCandidateKey if input.isBackSpace || input.isEnter || input.isAbsorbedArrowKey || input.isExtraChooseCandidateKey
|| input.isExtraChooseCandidateKeyReverse || input.isCursorForward || input.isCursorBackward || input.isExtraChooseCandidateKeyReverse || input.isCursorForward || input.isCursorBackward
{ {
// Do nothing if backspace is pressed -- we ignore the key // BackSpace
} else if input.isCapsLockOn { } else if input.isCapsLockOn {
// Process all possible combination, we hope. //
clear() clear()
stateCallback(InputState.Empty()) stateCallback(InputState.Empty())
// When shift is pressed, don't do further processing... // Shift
// ...since it outputs capital letter anyway.
if input.isShiftHold { if input.isShiftHold {
return false return false
} }
// If ASCII but not printable, don't use insertText:replacementRange: /// ASCII 使insertText:replacementRange:
// Certain apps don't handle non-ASCII char insertions. /// ASCII
/// Objective-C isPrintable()
/// CTools.h Swift
if charCode < 0x80, !CTools.isPrintable(charCode) { if charCode < 0x80, !CTools.isPrintable(charCode) {
return false return false
} }
// Commit the entire input buffer. //
stateCallback(InputState.Committing(poppedText: inputText.lowercased())) stateCallback(InputState.Committing(textToCommit: inputText.lowercased()))
stateCallback(InputState.Empty()) stateCallback(InputState.Empty())
return true return true
} }
// MARK: Numeric Pad Processing. // MARK: (Numeric Pad Processing)
if input.isNumericPad { if input.isNumericPad {
if !input.isLeft, !input.isRight, !input.isDown, if !input.isLeft, !input.isRight, !input.isDown,
@ -101,13 +104,13 @@ extension KeyHandler {
{ {
clear() clear()
stateCallback(InputState.Empty()) stateCallback(InputState.Empty())
stateCallback(InputState.Committing(poppedText: inputText.lowercased())) stateCallback(InputState.Committing(textToCommit: inputText.lowercased()))
stateCallback(InputState.Empty()) stateCallback(InputState.Empty())
return true return true
} }
} }
// MARK: Handle Candidates. // MARK: (Handle Candidates)
if state is InputState.ChoosingCandidate { if state is InputState.ChoosingCandidate {
return handleCandidate( return handleCandidate(
@ -115,7 +118,7 @@ extension KeyHandler {
) )
} }
// MARK: Handle Associated Phrases. // MARK: (Handle Associated Phrases)
if state is InputState.AssociatedPhrases { if state is InputState.AssociatedPhrases {
if handleCandidate( if handleCandidate(
@ -127,7 +130,7 @@ extension KeyHandler {
} }
} }
// MARK: Handle Marking. // MARK: 便使() (Handle Marking)
if let marking = state as? InputState.Marking { if let marking = state as? InputState.Marking {
if handleMarkingState( if handleMarkingState(
@ -140,63 +143,72 @@ extension KeyHandler {
stateCallback(state) stateCallback(state)
} }
// MARK: Handle BPMF Keys. // MARK: (Handle BPMF Keys)
var keyConsumedByReading = false var keyConsumedByReading = false
let skipPhoneticHandling = input.isReservedKey || input.isControlHold || input.isOptionHold let skipPhoneticHandling = input.isReservedKey || input.isControlHold || input.isOptionHold
// See if Phonetic reading is valid. // inputValidityCheck() charCode UniChar
if !skipPhoneticHandling && _composer.inputValidityCheck(key: charCode) { // keyConsumedByReading
_composer.receiveKey(fromCharCode: charCode) // composer.receiveKey() String UniChar
if !skipPhoneticHandling && composer.inputValidityCheck(key: charCode) {
composer.receiveKey(fromCharCode: charCode)
keyConsumedByReading = true keyConsumedByReading = true
// If we have a tone marker, we have to insert the reading to the // 調 updateClientComposingBuffer() return true
// builder in other words, if we don't have a tone marker, we just // 調
// update the composing buffer. let composeReading = composer.hasToneMarker()
let composeReading = _composer.hasToneMarker()
if !composeReading { if !composeReading {
stateCallback(buildInputtingState) stateCallback(buildInputtingState)
return true return true
} }
} }
var composeReading = _composer.hasToneMarker() // var composeReading = composer.hasToneMarker() //
// See if we have composition if Enter/Space is hit and buffer is not empty. // Enter Space _composer
// We use "|=" conditioning so that the tone marker key is also taken into account. // |=
// However, Swift does not support "|=". composeReading = composeReading || (!composer.isEmpty && (input.isSpace || input.isEnter))
composeReading = composeReading || (!_composer.isEmpty && (input.isSpace || input.isEnter))
if composeReading { if composeReading {
if input.isSpace, !_composer.hasToneMarker() { if input.isSpace, !composer.hasToneMarker() {
_composer.receiveKey(fromString: " ") // // 調
// 使 OVMandarin調
composer.receiveKey(fromString: " ")
} }
let reading = _composer.getComposition() let reading = composer.getComposition() //
//
// See whether we have a unigram for this... //
if !ifLangModelHasUnigrams(forKey: reading) { if !ifLangModelHasUnigrams(forKey: reading) {
IME.prtDebugIntel("B49C0979語彙庫內無「\(reading)」的匹配記錄。") IME.prtDebugIntel("B49C0979語彙庫內無「\(reading)」的匹配記錄。")
errorCallback() errorCallback()
_composer.clear() composer.clear()
stateCallback((builderLength == 0) ? InputState.EmptyIgnoringPreviousState() : buildInputtingState) //
return true stateCallback((compositorLength == 0) ? InputState.EmptyIgnoringPreviousState() : buildInputtingState)
return true // IMK
} }
// ... and insert it into the grid... //
insertReadingToBuilderAtCursor(reading: reading) insertToCompositorAtCursor(reading: reading)
// ... then walk the grid... //
let poppedText = popOverflowComposingTextAndWalk let textToCommit = popOverflowComposingTextAndWalk
// ... get and tweak override model suggestion if possible... //
// dealWithOverrideModelSuggestions() // 使 // dealWithOverrideModelSuggestions() // 使
// ... then update the text. //
_composer.clear() markNodesFixedIfNecessary()
//
composer.clear()
// updateClientComposingBuffer()
let inputting = buildInputtingState let inputting = buildInputtingState
inputting.poppedText = poppedText inputting.textToCommit = textToCommit
stateCallback(inputting) stateCallback(inputting)
///
if mgrPrefs.useSCPCTypingMode { if mgrPrefs.useSCPCTypingMode {
let choosingCandidates: InputState.ChoosingCandidate = buildCandidate( let choosingCandidates: InputState.ChoosingCandidate = buildCandidate(
state: inputting, state: inputting,
@ -205,7 +217,7 @@ extension KeyHandler {
if choosingCandidates.candidates.count == 1 { if choosingCandidates.candidates.count == 1 {
clear() clear()
let text: String = choosingCandidates.candidates.first ?? "" let text: String = choosingCandidates.candidates.first ?? ""
stateCallback(InputState.Committing(poppedText: text)) stateCallback(InputState.Committing(textToCommit: text))
if !mgrPrefs.associatedPhrasesEnabled { if !mgrPrefs.associatedPhrasesEnabled {
stateCallback(InputState.Empty()) stateCallback(InputState.Empty())
@ -225,45 +237,52 @@ extension KeyHandler {
stateCallback(choosingCandidates) stateCallback(choosingCandidates)
} }
} }
return true // Telling the client that the key is consumed. // ctlInputMethod IMK
return true
} }
// The only possibility for this to be true is that the Phonetic reading /// true 調調
// already has a tone marker but the last key is *not* a tone marker key. An /// 6jˊˊ調
// example is the sequence "6u" with the Standard layout, which produces "ˊ" /// 調ˊˊˊˊˇˊ
// but does not compose. Only sequences such as "ˊ", "ˊˊ", "ˊˇ", or "ˊ "
// would compose.
if keyConsumedByReading { if keyConsumedByReading {
// updateClientComposingBuffer()
stateCallback(buildInputtingState) stateCallback(buildInputtingState)
return true return true
} }
// MARK: Calling candidate window using Up / Down or PageUp / PageDn. // MARK: Calling candidate window using Up / Down or PageUp / PageDn.
if let currentState = state as? InputState.NotEmpty, _composer.isEmpty, //
if let currentState = state as? InputState.NotEmpty, composer.isEmpty,
input.isExtraChooseCandidateKey || input.isExtraChooseCandidateKeyReverse || input.isSpace input.isExtraChooseCandidateKey || input.isExtraChooseCandidateKeyReverse || input.isSpace
|| input.isPageDown || input.isPageUp || (input.isTab && mgrPrefs.specifyShiftTabKeyBehavior) || input.isPageDown || input.isPageUp || (input.isTab && mgrPrefs.specifyShiftTabKeyBehavior)
|| (input.isTypingVertical && (input.isverticalTypingOnlyChooseCandidateKey)) || (input.isTypingVertical && (input.isverticalTypingOnlyChooseCandidateKey))
{ {
if input.isSpace { if input.isSpace {
// If the Space key is NOT set to be a selection key /// Space
if input.isShiftHold || !mgrPrefs.chooseCandidateUsingSpace { if !mgrPrefs.chooseCandidateUsingSpace {
if builderCursorIndex >= builderLength { if compositorCursorIndex >= compositorLength {
let composingBuffer = currentState.composingBuffer let composingBuffer = currentState.composingBuffer
if !composingBuffer.isEmpty { if !composingBuffer.isEmpty {
stateCallback(InputState.Committing(poppedText: composingBuffer)) stateCallback(InputState.Committing(textToCommit: composingBuffer))
} }
clear() clear()
stateCallback(InputState.Committing(poppedText: " ")) stateCallback(InputState.Committing(textToCommit: " "))
stateCallback(InputState.Empty()) stateCallback(InputState.Empty())
} else if ifLangModelHasUnigrams(forKey: " ") { } else if ifLangModelHasUnigrams(forKey: " ") {
insertReadingToBuilderAtCursor(reading: " ") insertToCompositorAtCursor(reading: " ")
let poppedText = popOverflowComposingTextAndWalk let textToCommit = popOverflowComposingTextAndWalk
let inputting = buildInputtingState let inputting = buildInputtingState
inputting.poppedText = poppedText inputting.textToCommit = textToCommit
stateCallback(inputting) stateCallback(inputting)
} }
return true return true
} else if input.isShiftHold { // Tab Shift+CMD+Space /
return handleInlineCandidateRotation(
state: state, reverseModifier: input.isCommandHold, stateCallback: stateCallback,
errorCallback: errorCallback
)
} }
} }
stateCallback(buildCandidate(state: currentState, isTypingVertical: input.isTypingVertical)) stateCallback(buildCandidate(state: currentState, isTypingVertical: input.isTypingVertical))
@ -279,8 +298,8 @@ extension KeyHandler {
// MARK: Tab // MARK: Tab
if input.isTab { if input.isTab {
return handleTab( return handleInlineCandidateRotation(
state: state, isShiftHold: input.isShiftHold, stateCallback: stateCallback, errorCallback: errorCallback state: state, reverseModifier: input.isShiftHold, stateCallback: stateCallback, errorCallback: errorCallback
) )
} }
@ -362,11 +381,11 @@ extension KeyHandler {
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 _composer.isEmpty { if composer.isEmpty {
insertReadingToBuilderAtCursor(reading: "_punctuation_list") insertToCompositorAtCursor(reading: "_punctuation_list")
let poppedText: String! = popOverflowComposingTextAndWalk let textToCommit: String! = popOverflowComposingTextAndWalk
let inputting = buildInputtingState let inputting = buildInputtingState
inputting.poppedText = poppedText inputting.textToCommit = textToCommit
stateCallback(inputting) stateCallback(inputting)
stateCallback(buildCandidate(state: inputting, isTypingVertical: input.isTypingVertical)) stateCallback(buildCandidate(state: inputting, isTypingVertical: input.isTypingVertical))
} else { // If there is still unfinished bpmf reading, ignore the punctuation } else { // If there is still unfinished bpmf reading, ignore the punctuation
@ -378,7 +397,7 @@ extension KeyHandler {
} else { } else {
// commit buffer ESC // commit buffer ESC
// Enter 使 commit buffer // Enter 使 commit buffer
// bool _ = // bool _ =
_ = handleEnter(state: state, stateCallback: stateCallback, errorCallback: errorCallback) _ = handleEnter(state: state, stateCallback: stateCallback, errorCallback: errorCallback)
stateCallback(InputState.SymbolTable(node: SymbolNode.root, isTypingVertical: input.isTypingVertical)) stateCallback(InputState.SymbolTable(node: SymbolNode.root, isTypingVertical: input.isTypingVertical))
return true return true
@ -387,7 +406,9 @@ extension KeyHandler {
// MARK: Punctuation // MARK: Punctuation
// If nothing is matched, see if it's a punctuation key for current layout. ///
/// - /
/// -
var punctuationNamePrefix = "" var punctuationNamePrefix = ""
@ -418,7 +439,8 @@ extension KeyHandler {
return true return true
} }
// if nothing is matched, see if it's a punctuation key. ///
let arrPunctuations: [String] = [punctuationNamePrefix, String(format: "%c", CChar(charCode))] let arrPunctuations: [String] = [punctuationNamePrefix, String(format: "%c", CChar(charCode))]
let punctuation: String = arrPunctuations.joined(separator: "") let punctuation: String = arrPunctuations.joined(separator: "")
@ -446,14 +468,13 @@ extension KeyHandler {
} }
} }
// MARK: - Still Nothing. // MARK: - (Still Nothing)
// Still nothing? Then we update the composing buffer. /// ctlInputMethod
// Note that some app has strange behavior if we don't do this, ///
// "thinking" that the key is not actually consumed. /// F1-F12
// F1-F12 /// 便
// 便 if (state is InputState.NotEmpty) || !composer.isEmpty {
if (state is InputState.NotEmpty) || !_composer.isEmpty {
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

@ -1,89 +0,0 @@
// 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
// MARK: - § Misc functions.
extension KeyHandler {
var currentMandarinParser: String {
mgrPrefs.mandarinParserName + "_"
}
var actualCandidateCursorIndex: Int {
var cursorIndex = builderCursorIndex
switch mgrPrefs.useRearCursorMode {
case false:
do {
// macOS built-in Zhuyin style.
// (i.e. the cursor is always in front of the phrase.)
// No crossing.
switch cursorIndex {
case 0: cursorIndex = 1
default: break
}
}
case true:
do {
// Microsoft new phonetics style.
// (i.e. the cursor is always at the rear of the phrase.)
// No crossing.
switch cursorIndex {
case builderLength: cursorIndex -= 1
default: break
}
}
}
return cursorIndex
}
// Ruby
func cnvZhuyinKeyToTextbookReading(target: String, newSeparator: String = "-") -> String {
var arrReturn: [String] = []
for neta in target.split(separator: "-") {
var newString = String(neta)
if String(neta.reversed()[0]) == "˙" {
newString = String(neta.dropLast())
newString.insert("˙", at: newString.startIndex)
}
arrReturn.append(newString)
}
return arrReturn.joined(separator: newSeparator)
}
// Ruby
func restoreToneOneInZhuyinKey(target: String, newSeparator: String = "-") -> String {
var arrReturn: [String] = []
for neta in target.split(separator: "-") {
var newNeta = String(neta)
if !"ˊˇˋ˙".contains(String(neta.reversed()[0])), !neta.contains("_") {
newNeta += "1"
}
arrReturn.append(newNeta)
}
return arrReturn.joined(separator: newSeparator)
}
}

View File

@ -24,68 +24,66 @@ 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.
*/ */
/// 調調
import Cocoa import Cocoa
// MARK: - § State managements. // MARK: - § 調 (Functions Interact With States).
extension KeyHandler { extension KeyHandler {
// MARK: - State Building // MARK: - State Building
///
var buildInputtingState: InputState.Inputting { var buildInputtingState: InputState.Inputting {
// "Updating the composing buffer" means to request the client /// (Update the composing buffer)
// to "refresh" the text input buffer with our "composing text" /// NSAttributeString
var tooltipParameterRef: [String] = ["", ""] var tooltipParameterRef: [String] = ["", ""]
var composingBuffer = "" var composingBuffer = ""
var composedStringCursorIndex = 0 var composedStringCursorIndex = 0
var readingCursorIndex = 0 var readingCursorIndex = 0
// We must do some Unicode codepoint counting to find the actual cursor location for the client /// IMK UTF8 emoji
// i.e. we need to take UTF-16 into consideration, for which a surrogate pair takes 2 UniChars /// Swift.utf16NSString.length()
// locations. Since we are using Swift, we use .utf16 as the equivalent of NSString.length(). ///
for walkedNode in _walkedNodes { for walkedNode in walkedAnchors {
if let theNode = walkedNode.node { if let theNode = walkedNode.node {
let strNodeValue = theNode.currentKeyValue.value let strNodeValue = theNode.currentKeyValue.value
composingBuffer += strNodeValue composingBuffer += strNodeValue
let arrSplit: [String] = Array(strNodeValue).map { String($0) } let arrSplit: [String] = Array(strNodeValue).map { String($0) }
let codepointCount = arrSplit.count let codepointCount = arrSplit.count
// This re-aligns the cursor index in the composed string ///
// (the actual cursor on the screen) with the builder's logical /// NodeAnchorspanningLength
// 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.
let spanningLength: Int = walkedNode.spanningLength let spanningLength: Int = walkedNode.spanningLength
if readingCursorIndex + spanningLength <= builderCursorIndex { if readingCursorIndex + spanningLength <= compositorCursorIndex {
composedStringCursorIndex += strNodeValue.utf16.count composedStringCursorIndex += strNodeValue.utf16.count
readingCursorIndex += spanningLength readingCursorIndex += spanningLength
} else { } else {
if codepointCount == spanningLength { if codepointCount == spanningLength {
var i = 0 var i = 0
while i < codepointCount, readingCursorIndex < builderCursorIndex { while i < codepointCount, readingCursorIndex < compositorCursorIndex {
composedStringCursorIndex += arrSplit[i].utf16.count composedStringCursorIndex += arrSplit[i].utf16.count
readingCursorIndex += 1 readingCursorIndex += 1
i += 1 i += 1
} }
} else { } else {
if readingCursorIndex < builderCursorIndex { if readingCursorIndex < compositorCursorIndex {
composedStringCursorIndex += strNodeValue.utf16.count composedStringCursorIndex += strNodeValue.utf16.count
readingCursorIndex += spanningLength readingCursorIndex += spanningLength
if readingCursorIndex > builderCursorIndex { readingCursorIndex = min(readingCursorIndex, compositorCursorIndex)
readingCursorIndex = builderCursorIndex ///
} ///
// Now we start preparing the contents of the tooltips used ///
// in cases of moving cursors across certain emojis which emoji ///
// char count is inequal to the reading count. ///
// Example in McBopomofo: Typing (3 readings) gets a tree emoji. switch compositorCursorIndex {
// Example in vChewing: Typing (2 readings) gets a pasta emoji. case compositor.readings.count...:
switch builderCursorIndex { tooltipParameterRef[0] = compositor.readings[compositor.readings.count - 1]
case _builder.readings.count...:
tooltipParameterRef[0] = _builder.readings[_builder.readings.count - 1]
case 0: case 0:
tooltipParameterRef[1] = _builder.readings[builderCursorIndex] tooltipParameterRef[1] = compositor.readings[compositorCursorIndex]
default: default:
do { do {
tooltipParameterRef[0] = _builder.readings[builderCursorIndex - 1] tooltipParameterRef[0] = compositor.readings[compositorCursorIndex - 1]
tooltipParameterRef[1] = _builder.readings[builderCursorIndex] tooltipParameterRef[1] = compositor.readings[compositorCursorIndex]
} }
} }
} }
@ -94,9 +92,8 @@ extension KeyHandler {
} }
} }
// 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. /// 便 composer
// The reading text is what the user is typing.
var arrHead = [String.UTF16View.Element]() var arrHead = [String.UTF16View.Element]()
var arrTail = [String.UTF16View.Element]() var arrTail = [String.UTF16View.Element]()
@ -108,34 +105,49 @@ extension KeyHandler {
} }
} }
/// stringview
///
let head = String(utf16CodeUnits: arrHead, count: arrHead.count) let head = String(utf16CodeUnits: arrHead, count: arrHead.count)
let reading = _composer.getInlineCompositionForIMK(isHanyuPinyin: mgrPrefs.showHanyuPinyinInCompositionBuffer) let reading = composer.getInlineCompositionForIMK(isHanyuPinyin: mgrPrefs.showHanyuPinyinInCompositionBuffer)
let tail = String(utf16CodeUnits: arrTail, count: arrTail.count) let tail = String(utf16CodeUnits: arrTail, count: arrTail.count)
let composedText = head + reading + tail let composedText = head + reading + tail
let cursorIndex = composedStringCursorIndex + reading.utf16.count let cursorIndex = composedStringCursorIndex + reading.utf16.count
let stateResult = InputState.Inputting(composingBuffer: composedText, cursorIndex: cursorIndex) var cleanedComposition = ""
// Now we start weaving the contents of the tooltip. //
if tooltipParameterRef[0].isEmpty, tooltipParameterRef[1].isEmpty { for theChar in composedText {
stateResult.tooltip = "" if let charCode = theChar.utf16.first {
} else if tooltipParameterRef[0].isEmpty { if !(theChar.isASCII && !(charCode.isPrintable())) {
stateResult.tooltip = String( cleanedComposition += String(theChar)
format: NSLocalizedString("Cursor is to the rear of \"%@\".", comment: ""), }
tooltipParameterRef[1] }
)
} else if tooltipParameterRef[1].isEmpty {
stateResult.tooltip = String(
format: NSLocalizedString("Cursor is in front of \"%@\".", comment: ""),
tooltipParameterRef[0]
)
} else {
stateResult.tooltip = String(
format: NSLocalizedString("Cursor is between \"%@\" and \"%@\".", comment: ""),
tooltipParameterRef[0], tooltipParameterRef[1]
)
} }
/// 使
let stateResult = InputState.Inputting(composingBuffer: cleanedComposition, cursorIndex: cursorIndex)
///
switch (tooltipParameterRef[0].isEmpty, tooltipParameterRef[1].isEmpty) {
case (true, true): stateResult.tooltip.removeAll()
case (true, false):
stateResult.tooltip = String(
format: NSLocalizedString("Cursor is to the rear of \"%@\".", comment: ""),
tooltipParameterRef[1]
)
case (false, true):
stateResult.tooltip = String(
format: NSLocalizedString("Cursor is in front of \"%@\".", comment: ""),
tooltipParameterRef[0]
)
case (false, false):
stateResult.tooltip = String(
format: NSLocalizedString("Cursor is between \"%@\" and \"%@\".", comment: ""),
tooltipParameterRef[0], tooltipParameterRef[1]
)
}
///
if !stateResult.tooltip.isEmpty { if !stateResult.tooltip.isEmpty {
ctlInputMethod.tooltipController.setColor(state: .denialOverflow) ctlInputMethod.tooltipController.setColor(state: .denialOverflow)
} }
@ -145,6 +157,11 @@ extension KeyHandler {
// MARK: - // MARK: -
///
/// - Parameters:
/// - currentState:
/// - isTypingVertical:
/// - Returns:
func buildCandidate( func buildCandidate(
state currentState: InputState.NotEmpty, state currentState: InputState.NotEmpty,
isTypingVertical: Bool = false isTypingVertical: Bool = false
@ -159,13 +176,19 @@ extension KeyHandler {
// MARK: - // MARK: -
// buildAssociatePhraseStateWithKey ///
// 使 ///
// Core buildAssociatePhraseArray /// buildAssociatePhraseStateWithKey
// String Swift /// 使
// nil /// Core buildAssociatePhraseArray
// /// String Swift
// /// nil
///
///
/// - Parameters:
/// - key:
/// - isTypingVertical:
/// - Returns:
func buildAssociatePhraseState( func buildAssociatePhraseState(
withKey key: String!, withKey key: String!,
isTypingVertical: Bool isTypingVertical: Bool
@ -178,6 +201,13 @@ extension KeyHandler {
// MARK: - // MARK: -
///
/// - Parameters:
/// - state:
/// - input:
/// - stateCallback:
/// - errorCallback:
/// - Returns: ctlInputMethod IMK
func handleMarkingState( func handleMarkingState(
_ state: InputState.Marking, _ state: InputState.Marking,
input: InputSignal, input: InputSignal,
@ -246,8 +276,16 @@ extension KeyHandler {
return false return false
} }
// MARK: - // MARK: -
///
/// - Parameters:
/// - customPunctuation:
/// - state:
/// - isTypingVertical:
/// - stateCallback:
/// - errorCallback:
/// - Returns: ctlInputMethod IMK
func handlePunctuation( func handlePunctuation(
_ customPunctuation: String, _ customPunctuation: String,
state: InputState, state: InputState,
@ -259,22 +297,22 @@ extension KeyHandler {
return false return false
} }
if _composer.isEmpty { if composer.isEmpty {
insertReadingToBuilderAtCursor(reading: customPunctuation) insertToCompositorAtCursor(reading: customPunctuation)
let poppedText = popOverflowComposingTextAndWalk let textToCommit = popOverflowComposingTextAndWalk
let inputting = buildInputtingState let inputting = buildInputtingState
inputting.poppedText = poppedText inputting.textToCommit = textToCommit
stateCallback(inputting) stateCallback(inputting)
if mgrPrefs.useSCPCTypingMode, _composer.isEmpty { if mgrPrefs.useSCPCTypingMode, composer.isEmpty {
let candidateState = buildCandidate( let candidateState = buildCandidate(
state: inputting, state: inputting,
isTypingVertical: isTypingVertical isTypingVertical: isTypingVertical
) )
if candidateState.candidates.count == 1 { if candidateState.candidates.count == 1 {
clear() clear()
if let strPoppedText: String = candidateState.candidates.first { if let strtextToCommit: String = candidateState.candidates.first {
stateCallback(InputState.Committing(poppedText: strPoppedText) as InputState.Committing) stateCallback(InputState.Committing(textToCommit: strtextToCommit) as InputState.Committing)
stateCallback(InputState.Empty()) stateCallback(InputState.Empty())
} else { } else {
stateCallback(candidateState) stateCallback(candidateState)
@ -293,8 +331,13 @@ extension KeyHandler {
} }
} }
// MARK: - Enter // MARK: - Enter
/// Enter
/// - Parameters:
/// - state:
/// - stateCallback:
/// - Returns: ctlInputMethod IMK
func handleEnter( func handleEnter(
state: InputState, state: InputState,
stateCallback: @escaping (InputState) -> Void, stateCallback: @escaping (InputState) -> Void,
@ -303,13 +346,18 @@ extension KeyHandler {
guard let currentState = state as? InputState.Inputting else { return false } guard let currentState = state as? InputState.Inputting else { return false }
clear() clear()
stateCallback(InputState.Committing(poppedText: currentState.composingBuffer)) stateCallback(InputState.Committing(textToCommit: currentState.composingBuffer))
stateCallback(InputState.Empty()) stateCallback(InputState.Empty())
return true return true
} }
// MARK: - CMD+Enter // MARK: - CMD+Enter
/// CMD+Enter
/// - Parameters:
/// - state:
/// - stateCallback:
/// - Returns: ctlInputMethod IMK
func handleCtrlCommandEnter( func handleCtrlCommandEnter(
state: InputState, state: InputState,
stateCallback: @escaping (InputState) -> Void, stateCallback: @escaping (InputState) -> Void,
@ -329,13 +377,18 @@ extension KeyHandler {
clear() clear()
stateCallback(InputState.Committing(poppedText: composingBuffer)) stateCallback(InputState.Committing(textToCommit: composingBuffer))
stateCallback(InputState.Empty()) stateCallback(InputState.Empty())
return true return true
} }
// MARK: - CMD+Alt+Enter Ruby // MARK: - CMD+Alt+Enter Ruby
/// CMD+Alt+Enter Ruby
/// - Parameters:
/// - state:
/// - stateCallback:
/// - Returns: ctlInputMethod IMK
func handleCtrlOptionCommandEnter( func handleCtrlOptionCommandEnter(
state: InputState, state: InputState,
stateCallback: @escaping (InputState) -> Void, stateCallback: @escaping (InputState) -> Void,
@ -345,7 +398,7 @@ extension KeyHandler {
var composed = "" var composed = ""
for theAnchor in _walkedNodes { for theAnchor in walkedAnchors {
if let node = theAnchor.node { if let node = theAnchor.node {
var key = node.currentKeyValue.key var key = node.currentKeyValue.key
if mgrPrefs.inlineDumpPinyinInLieuOfZhuyin { if mgrPrefs.inlineDumpPinyinInLieuOfZhuyin {
@ -368,13 +421,19 @@ extension KeyHandler {
clear() clear()
stateCallback(InputState.Committing(poppedText: composed)) stateCallback(InputState.Committing(textToCommit: composed))
stateCallback(InputState.Empty()) stateCallback(InputState.Empty())
return true return true
} }
// MARK: - Backspace (macOS Delete) // MARK: - Backspace (macOS Delete)
/// Backspace (macOS Delete)
/// - Parameters:
/// - state:
/// - stateCallback:
/// - errorCallback:
/// - Returns: ctlInputMethod IMK
func handleBackspace( func handleBackspace(
state: InputState, state: InputState,
stateCallback: @escaping (InputState) -> Void, stateCallback: @escaping (InputState) -> Void,
@ -382,11 +441,11 @@ extension KeyHandler {
) -> Bool { ) -> Bool {
guard state is InputState.Inputting else { return false } guard state is InputState.Inputting else { return false }
if _composer.hasToneMarker(withNothingElse: true) { if composer.hasToneMarker(withNothingElse: true) {
_composer.clear() composer.clear()
} else if _composer.isEmpty { } else if composer.isEmpty {
if builderCursorIndex >= 0 { if compositorCursorIndex >= 0 {
deleteBuilderReadingInFrontOfCursor() deleteCompositorReadingAtTheRearOfCursor()
walk() walk()
} else { } else {
IME.prtDebugIntel("9D69908D") IME.prtDebugIntel("9D69908D")
@ -395,10 +454,10 @@ extension KeyHandler {
return true return true
} }
} else { } else {
_composer.doBackSpace() composer.doBackSpace()
} }
if _composer.isEmpty, builderLength == 0 { if composer.isEmpty, compositorLength == 0 {
stateCallback(InputState.EmptyIgnoringPreviousState()) stateCallback(InputState.EmptyIgnoringPreviousState())
} else { } else {
stateCallback(buildInputtingState) stateCallback(buildInputtingState)
@ -408,6 +467,12 @@ extension KeyHandler {
// MARK: - PC Delete (macOS Fn+BackSpace) // MARK: - PC Delete (macOS Fn+BackSpace)
/// PC Delete (macOS Fn+BackSpace)
/// - Parameters:
/// - state:
/// - stateCallback:
/// - errorCallback:
/// - Returns: ctlInputMethod IMK
func handleDelete( func handleDelete(
state: InputState, state: InputState,
stateCallback: @escaping (InputState) -> Void, stateCallback: @escaping (InputState) -> Void,
@ -415,9 +480,9 @@ extension KeyHandler {
) -> Bool { ) -> Bool {
guard state is InputState.Inputting else { return false } guard state is InputState.Inputting else { return false }
if _composer.isEmpty { if composer.isEmpty {
if builderCursorIndex != builderLength { if compositorCursorIndex != compositorLength {
deleteBuilderReadingToTheFrontOfCursor() deleteCompositorReadingToTheFrontOfCursor()
walk() walk()
let inputting = buildInputtingState let inputting = buildInputtingState
// count > 0!isEmpty滿 // count > 0!isEmpty滿
@ -442,13 +507,19 @@ extension KeyHandler {
// MARK: - 90 // MARK: - 90
/// 90
/// - Parameters:
/// - state:
/// - stateCallback:
/// - errorCallback:
/// - Returns: ctlInputMethod IMK
func handleAbsorbedArrowKey( func handleAbsorbedArrowKey(
state: InputState, state: InputState,
stateCallback: @escaping (InputState) -> Void, stateCallback: @escaping (InputState) -> Void,
errorCallback: @escaping () -> Void errorCallback: @escaping () -> Void
) -> Bool { ) -> Bool {
guard state is InputState.Inputting else { return false } guard state is InputState.Inputting else { return false }
if !_composer.isEmpty { if !composer.isEmpty {
IME.prtDebugIntel("9B6F908D") IME.prtDebugIntel("9B6F908D")
errorCallback() errorCallback()
} }
@ -456,8 +527,14 @@ extension KeyHandler {
return true return true
} }
// MARK: - Home // MARK: - Home
/// Home
/// - Parameters:
/// - state:
/// - stateCallback:
/// - errorCallback:
/// - Returns: ctlInputMethod IMK
func handleHome( func handleHome(
state: InputState, state: InputState,
stateCallback: @escaping (InputState) -> Void, stateCallback: @escaping (InputState) -> Void,
@ -465,15 +542,15 @@ extension KeyHandler {
) -> Bool { ) -> Bool {
guard state is InputState.Inputting else { return false } guard state is InputState.Inputting else { return false }
if !_composer.isEmpty { if !composer.isEmpty {
IME.prtDebugIntel("ABC44080") IME.prtDebugIntel("ABC44080")
errorCallback() errorCallback()
stateCallback(state) stateCallback(state)
return true return true
} }
if builderCursorIndex != 0 { if compositorCursorIndex != 0 {
builderCursorIndex = 0 compositorCursorIndex = 0
stateCallback(buildInputtingState) stateCallback(buildInputtingState)
} else { } else {
IME.prtDebugIntel("66D97F90") IME.prtDebugIntel("66D97F90")
@ -484,8 +561,14 @@ extension KeyHandler {
return true return true
} }
// MARK: - End // MARK: - End
/// End
/// - Parameters:
/// - state:
/// - stateCallback:
/// - errorCallback:
/// - Returns: ctlInputMethod IMK
func handleEnd( func handleEnd(
state: InputState, state: InputState,
stateCallback: @escaping (InputState) -> Void, stateCallback: @escaping (InputState) -> Void,
@ -493,15 +576,15 @@ extension KeyHandler {
) -> Bool { ) -> Bool {
guard state is InputState.Inputting else { return false } guard state is InputState.Inputting else { return false }
if !_composer.isEmpty { if !composer.isEmpty {
IME.prtDebugIntel("9B69908D") IME.prtDebugIntel("9B69908D")
errorCallback() errorCallback()
stateCallback(state) stateCallback(state)
return true return true
} }
if builderCursorIndex != builderLength { if compositorCursorIndex != compositorLength {
builderCursorIndex = builderLength compositorCursorIndex = compositorLength
stateCallback(buildInputtingState) stateCallback(buildInputtingState)
} else { } else {
IME.prtDebugIntel("9B69908E") IME.prtDebugIntel("9B69908E")
@ -512,8 +595,13 @@ extension KeyHandler {
return true return true
} }
// MARK: - Esc // MARK: - Esc
/// Esc
/// - Parameters:
/// - state:
/// - stateCallback:
/// - Returns: ctlInputMethod IMK
func handleEsc( func handleEsc(
state: InputState, state: InputState,
stateCallback: @escaping (InputState) -> Void, stateCallback: @escaping (InputState) -> Void,
@ -521,20 +609,16 @@ extension KeyHandler {
) -> Bool { ) -> Bool {
guard state is InputState.Inputting else { return false } guard state is InputState.Inputting else { return false }
let escToClearInputBufferEnabled: Bool = mgrPrefs.escToCleanInputBuffer if mgrPrefs.escToCleanInputBuffer {
///
if escToClearInputBufferEnabled { /// macOS Windows 使
// If the option is enabled, we clear everything in the buffer.
// This includes walked nodes and the reading. Note that this convention
// is by default in macOS 10.0-10.5 built-in Panasonic Hanin and later macOS Zhuyin.
// Some Windows users hate this design, hence the option here to disable it.
clear() clear()
stateCallback(InputState.EmptyIgnoringPreviousState()) stateCallback(InputState.EmptyIgnoringPreviousState())
} else { } else {
// If reading is not empty, we cancel the reading. ///
if !_composer.isEmpty { if !composer.isEmpty {
_composer.clear() composer.clear()
if builderLength == 0 { if compositorLength == 0 {
stateCallback(InputState.EmptyIgnoringPreviousState()) stateCallback(InputState.EmptyIgnoringPreviousState())
} else { } else {
stateCallback(buildInputtingState) stateCallback(buildInputtingState)
@ -546,6 +630,13 @@ extension KeyHandler {
// MARK: - // MARK: -
///
/// - Parameters:
/// - state:
/// - input:
/// - stateCallback:
/// - errorCallback:
/// - Returns: ctlInputMethod IMK
func handleForward( func handleForward(
state: InputState, state: InputState,
input: InputSignal, input: InputSignal,
@ -554,7 +645,7 @@ extension KeyHandler {
) -> Bool { ) -> Bool {
guard let currentState = state as? InputState.Inputting else { return false } guard let currentState = state as? InputState.Inputting else { return false }
if !_composer.isEmpty { if !composer.isEmpty {
IME.prtDebugIntel("B3BA5257") IME.prtDebugIntel("B3BA5257")
errorCallback() errorCallback()
stateCallback(state) stateCallback(state)
@ -580,8 +671,8 @@ extension KeyHandler {
stateCallback(state) stateCallback(state)
} }
} else { } else {
if builderCursorIndex < builderLength { if compositorCursorIndex < compositorLength {
builderCursorIndex += 1 compositorCursorIndex += 1
stateCallback(buildInputtingState) stateCallback(buildInputtingState)
} else { } else {
IME.prtDebugIntel("A96AAD58") IME.prtDebugIntel("A96AAD58")
@ -595,6 +686,13 @@ extension KeyHandler {
// MARK: - // MARK: -
///
/// - Parameters:
/// - state:
/// - input:
/// - stateCallback:
/// - errorCallback:
/// - Returns: ctlInputMethod IMK
func handleBackward( func handleBackward(
state: InputState, state: InputState,
input: InputSignal, input: InputSignal,
@ -603,7 +701,7 @@ extension KeyHandler {
) -> Bool { ) -> Bool {
guard let currentState = state as? InputState.Inputting else { return false } guard let currentState = state as? InputState.Inputting else { return false }
if !_composer.isEmpty { if !composer.isEmpty {
IME.prtDebugIntel("6ED95318") IME.prtDebugIntel("6ED95318")
errorCallback() errorCallback()
stateCallback(state) stateCallback(state)
@ -629,8 +727,8 @@ extension KeyHandler {
stateCallback(state) stateCallback(state)
} }
} else { } else {
if builderCursorIndex > 0 { if compositorCursorIndex > 0 {
builderCursorIndex -= 1 compositorCursorIndex -= 1
stateCallback(buildInputtingState) stateCallback(buildInputtingState)
} else { } else {
IME.prtDebugIntel("7045E6F3") IME.prtDebugIntel("7045E6F3")
@ -642,11 +740,18 @@ extension KeyHandler {
return true return true
} }
// MARK: - Tab // MARK: - Tab Shift+Space
func handleTab( ///
/// - Parameters:
/// - state:
/// - reverseModifier:
/// - stateCallback:
/// - errorCallback:
/// - Returns: ctlInputMethod IMK
func handleInlineCandidateRotation(
state: InputState, state: InputState,
isShiftHold: Bool, reverseModifier: Bool,
stateCallback: @escaping (InputState) -> Void, stateCallback: @escaping (InputState) -> Void,
errorCallback: @escaping () -> Void errorCallback: @escaping () -> Void
) -> Bool { ) -> Bool {
@ -660,13 +765,13 @@ extension KeyHandler {
return false return false
} }
guard _composer.isEmpty else { guard composer.isEmpty else {
IME.prtDebugIntel("A2DAF7BC") IME.prtDebugIntel("A2DAF7BC")
errorCallback() errorCallback()
return true return true
} }
// //
let candidates = buildCandidate(state: state).candidates let candidates = buildCandidate(state: state).candidates
guard !candidates.isEmpty else { guard !candidates.isEmpty else {
IME.prtDebugIntel("3378A6DF") IME.prtDebugIntel("3378A6DF")
@ -677,9 +782,9 @@ extension KeyHandler {
var length = 0 var length = 0
var currentAnchor = Megrez.NodeAnchor() var currentAnchor = Megrez.NodeAnchor()
let cursorIndex = min( let cursorIndex = min(
actualCandidateCursorIndex + (mgrPrefs.useRearCursorMode ? 1 : 0), builderLength actualCandidateCursorIndex + (mgrPrefs.useRearCursorMode ? 1 : 0), compositorLength
) )
for anchor in _walkedNodes { for anchor in walkedAnchors {
length += anchor.spanningLength length += anchor.spanningLength
if length >= cursorIndex { if length >= cursorIndex {
currentAnchor = anchor currentAnchor = anchor
@ -697,19 +802,18 @@ extension KeyHandler {
var currentIndex = 0 var currentIndex = 0
if currentNode.score < currentNode.kSelectedCandidateScore { if currentNode.score < currentNode.kSelectedCandidateScore {
// Once the user never select a candidate for the node, /// 使
// we start from the first candidate, so the user has a /// 使
// chance to use the unigram with two or more characters /// 2 使
// when type the tab key for the first time. ///
// ///
// In other words, if a user type two BPMF readings, /// 使
// but the score of seeing them as two unigrams is higher /// (Shift+)Tab ()
// than a phrase with two characters, the user can just /// Shift(+CMD)+Space Tab
// use the longer phrase by tapping the tab key.
if candidates[0] == currentValue { if candidates[0] == currentValue {
// If the first candidate is the value of the ///
// current node, we use next one. ///
if isShiftHold { if reverseModifier {
currentIndex = candidates.count - 1 currentIndex = candidates.count - 1
} else { } else {
currentIndex = 1 currentIndex = 1
@ -718,7 +822,7 @@ extension KeyHandler {
} else { } else {
for candidate in candidates { for candidate in candidates {
if candidate == currentValue { if candidate == currentValue {
if isShiftHold { if reverseModifier {
if currentIndex == 0 { if currentIndex == 0 {
currentIndex = candidates.count - 1 currentIndex = candidates.count - 1
} else { } else {

View File

@ -41,8 +41,8 @@ public struct Tekkon {
public enum MandarinParser: Int { public enum MandarinParser: Int {
case ofDachen = 0 case ofDachen = 0
case ofDachen26 = 1 case ofDachen26 = 1
case ofEten = 2 case ofETen = 2
case ofEten26 = 3 case ofETen26 = 3
case ofHsu = 4 case ofHsu = 4
case ofIBM = 5 case ofIBM = 5
case ofMiTAC = 6 case ofMiTAC = 6
@ -60,11 +60,11 @@ public struct Tekkon {
return "Dachen" return "Dachen"
case .ofDachen26: case .ofDachen26:
return "Dachen26" return "Dachen26"
case .ofEten: case .ofETen:
return "ETen" return "ETen"
case .ofHsu: case .ofHsu:
return "Hsu" return "Hsu"
case .ofEten26: case .ofETen26:
return "ETen26" return "ETen26"
case .ofIBM: case .ofIBM:
return "IBM" return "IBM"
@ -140,6 +140,7 @@ public struct Tekkon {
/// ///
public mutating func clear() { public mutating func clear() {
valueStorage = "" valueStorage = ""
type = .null
} }
/// ///
@ -151,7 +152,7 @@ public struct Tekkon {
ensureType() ensureType()
} }
/// ///
mutating func ensureType() { mutating func ensureType() {
if Tekkon.allowedConsonants.contains(value) { if Tekkon.allowedConsonants.contains(value) {
type = .consonant type = .consonant
@ -214,7 +215,7 @@ public struct Tekkon {
/// 調 /// 調
public var intonation: Phonabet = "" public var intonation: Phonabet = ""
/// ///
public var romajiBuffer: String = "" public var romajiBuffer: String = ""
/// Windows / macOS /// Windows / macOS
@ -227,7 +228,7 @@ public struct Tekkon {
consonant.value + semivowel.value + vowel.value + intonation.value consonant.value + semivowel.value + vowel.value + intonation.value
} }
/// value / /// value /
/// 調 /// 調
/// - Parameters: /// - Parameters:
/// - isHanyuPinyin: /// - isHanyuPinyin:
@ -250,7 +251,7 @@ public struct Tekkon {
} }
} }
// macOS InputMethod Kit 使 // macOS InputMethod Kit 使
/// - Parameters: /// - Parameters:
/// - isHanyuPinyin: /// - isHanyuPinyin:
public func getInlineCompositionForIMK(isHanyuPinyin: Bool = false) -> String { public func getInlineCompositionForIMK(isHanyuPinyin: Bool = false) -> String {
@ -279,12 +280,12 @@ public struct Tekkon {
} }
} }
/// ///
public var isPronouncable: Bool { public var isPronouncable: Bool {
!vowel.isEmpty || !semivowel.isEmpty || !consonant.isEmpty !vowel.isEmpty || !semivowel.isEmpty || !consonant.isEmpty
} }
// MARK: // MARK:
/// @input /// @input
/// @arrange .ofDachen /// @arrange .ofDachen
@ -308,7 +309,7 @@ public struct Tekkon {
// MARK: - Public Functions // MARK: - Public Functions
/// ///
/// ///
/// parser /// parser
/// - Parameters: /// - Parameters:
@ -321,12 +322,12 @@ public struct Tekkon {
return Tekkon.mapQwertyDachen[input] != nil return Tekkon.mapQwertyDachen[input] != nil
case .ofDachen26: case .ofDachen26:
return Tekkon.mapDachenCP26StaticKeys[input] != nil return Tekkon.mapDachenCP26StaticKeys[input] != nil
case .ofEten: case .ofETen:
return Tekkon.mapQwertyEtenTraditional[input] != nil return Tekkon.mapQwertyETenTraditional[input] != nil
case .ofHsu: case .ofHsu:
return Tekkon.mapHsuStaticKeys[input] != nil return Tekkon.mapHsuStaticKeys[input] != nil
case .ofEten26: case .ofETen26:
return Tekkon.mapEten26StaticKeys[input] != nil return Tekkon.mapETen26StaticKeys[input] != nil
case .ofIBM: case .ofIBM:
return Tekkon.mapQwertyIBM[input] != nil return Tekkon.mapQwertyIBM[input] != nil
case .ofMiTAC: case .ofMiTAC:
@ -343,7 +344,7 @@ public struct Tekkon {
} }
/// String /// String
/// UniChar /// UniChar
/// ///
/// 調 /// 調
/// - Parameters: /// - Parameters:
@ -356,7 +357,7 @@ public struct Tekkon {
intonation = Phonabet(theTone) intonation = Phonabet(theTone)
} }
} else { } else {
// romajiBuffer // romajiBuffer
if romajiBuffer.count > 5 { if romajiBuffer.count > 5 {
romajiBuffer = String(romajiBuffer.dropFirst()) romajiBuffer = String(romajiBuffer.dropFirst())
} }
@ -364,12 +365,12 @@ public struct Tekkon {
receiveSequence(romajiBufferBackup, isRomaji: true) receiveSequence(romajiBufferBackup, isRomaji: true)
romajiBuffer = romajiBufferBackup romajiBuffer = romajiBufferBackup
} }
default: receiveKey(fromPhonabet: translate(key: String(input))) default: receiveKey(fromPhonabet: translate(key: input))
} }
} }
/// UniChar /// UniChar
/// UniChar String /// UniChar String
/// ///
/// 調 /// 調
/// - Parameters: /// - Parameters:
@ -474,7 +475,7 @@ public struct Tekkon {
} }
} }
/// 調調 /// 調調
/// - Parameters: /// - Parameters:
/// - withNothingElse: 調 /// - withNothingElse: 調
public func hasToneMarker(withNothingElse: Bool = false) -> Bool { public func hasToneMarker(withNothingElse: Bool = false) -> Bool {
@ -493,11 +494,11 @@ public struct Tekkon {
// MARK: - Parser Processings // MARK: - Parser Processings
// //
/// String /// String
/// ///
/// ///
/// - Parameters: /// - Parameters:
/// - key: String /// - key: String
mutating func translate(key: String = "") -> String { mutating func translate(key: String = "") -> String {
@ -506,12 +507,12 @@ public struct Tekkon {
return Tekkon.mapQwertyDachen[key] ?? "" return Tekkon.mapQwertyDachen[key] ?? ""
case .ofDachen26: case .ofDachen26:
return handleDachen26(key: key) return handleDachen26(key: key)
case .ofEten: case .ofETen:
return Tekkon.mapQwertyEtenTraditional[key] ?? "" return Tekkon.mapQwertyETenTraditional[key] ?? ""
case .ofHsu: case .ofHsu:
return handleHsu(key: key) return handleHsu(key: key)
case .ofEten26: case .ofETen26:
return handleEten26(key: key) return handleETen26(key: key)
case .ofIBM: case .ofIBM:
return Tekkon.mapQwertyIBM[key] ?? "" return Tekkon.mapQwertyIBM[key] ?? ""
case .ofMiTAC: case .ofMiTAC:
@ -521,19 +522,18 @@ public struct Tekkon {
case .ofFakeSeigyou: case .ofFakeSeigyou:
return Tekkon.mapFakeSeigyou[key] ?? "" return Tekkon.mapFakeSeigyou[key] ?? ""
case .ofHanyuPinyin, .ofSecondaryPinyin, .ofYalePinyin, .ofHualuoPinyin, .ofUniversalPinyin: case .ofHanyuPinyin, .ofSecondaryPinyin, .ofYalePinyin, .ofHualuoPinyin, .ofUniversalPinyin:
break // break //
} }
return "" return ""
} }
/// ///
/// ///
/// ///
/// - Parameters: /// - Parameters:
/// - key: String /// - key: String
mutating func handleEten26(key: String = "") -> String { mutating func handleETen26(key: String = "") -> String {
var strReturn = "" var strReturn = Tekkon.mapETen26StaticKeys[key] ?? ""
strReturn = Tekkon.mapEten26StaticKeys[key] ?? ""
let incomingPhonabet = Phonabet(strReturn) let incomingPhonabet = Phonabet(strReturn)
switch key { switch key {
@ -612,16 +612,15 @@ public struct Tekkon {
/// ///
/// ///
/// ///
/// - Parameters: /// - Parameters:
/// - key: String /// - key: String
mutating func handleHsu(key: String = "") -> String { mutating func handleHsu(key: String = "") -> String {
var strReturn = "" var strReturn = Tekkon.mapHsuStaticKeys[key] ?? ""
strReturn = Tekkon.mapHsuStaticKeys[key] ?? ""
let incomingPhonabet = Phonabet(strReturn) let incomingPhonabet = Phonabet(strReturn)
if key == " ", value == "" { if key == " ", value == "" {
consonant = "" consonant.clear()
vowel = "" vowel = ""
} }
@ -719,7 +718,7 @@ public struct Tekkon {
consonant.selfReplace("", "") consonant.selfReplace("", "")
} }
if consonant == "", semivowel.isEmpty, vowel.isEmpty { if consonant == "", semivowel.isEmpty, vowel.isEmpty {
consonant = "" consonant.clear()
vowel = "" vowel = ""
} }
} }
@ -736,12 +735,11 @@ public struct Tekkon {
/// ///
/// ///
/// ///
/// - Parameters: /// - Parameters:
/// - key: String /// - key: String
mutating func handleDachen26(key: String = "") -> String { mutating func handleDachen26(key: String = "") -> String {
var strReturn = "" var strReturn = Tekkon.mapDachenCP26StaticKeys[key] ?? ""
strReturn = Tekkon.mapDachenCP26StaticKeys[key] ?? ""
switch key { switch key {
case "e": if isPronouncable { intonation = "ˊ" } else { consonant = "" } case "e": if isPronouncable { intonation = "ˊ" } else { consonant = "" }
@ -759,11 +757,11 @@ public struct Tekkon {
case "w": if consonant.isEmpty || consonant == "" { consonant = "" } else { consonant = "" } case "w": if consonant.isEmpty || consonant == "" { consonant = "" } else { consonant = "" }
case "m": case "m":
if semivowel == "", vowel != "" { if semivowel == "", vowel != "" {
semivowel = "" semivowel.clear()
vowel = "" vowel = ""
} else if semivowel != "", vowel == "" { } else if semivowel != "", vowel == "" {
semivowel = "" semivowel = ""
vowel = "" vowel.clear()
} else if !semivowel.isEmpty { } else if !semivowel.isEmpty {
vowel = "" vowel = ""
} else { } else {
@ -771,13 +769,13 @@ public struct Tekkon {
} }
case "u": case "u":
if semivowel == "", vowel != "" { if semivowel == "", vowel != "" {
semivowel = "" semivowel.clear()
vowel = "" vowel = ""
} else if semivowel != "", vowel == "" { } else if semivowel != "", vowel == "" {
semivowel = "" semivowel = ""
} else if semivowel == "", vowel == "" { } else if semivowel == "", vowel == "" {
semivowel = "" semivowel.clear()
vowel = "" vowel.clear()
} else if !semivowel.isEmpty { } else if !semivowel.isEmpty {
vowel = "" vowel = ""
} else { } else {
@ -834,6 +832,9 @@ public struct Tekkon {
return targetConverted return targetConverted
} }
/// 調 1
/// - Parameters:
/// - target: String
static func cnvHanyuPinyinToTextbookStyle(target: String) -> String { static func cnvHanyuPinyinToTextbookStyle(target: String) -> String {
var targetConverted = target var targetConverted = target
for pair in arrHanyuPinyinTextbookStyleConversionTable { for pair in arrHanyuPinyinTextbookStyleConversionTable {
@ -1296,14 +1297,14 @@ public struct Tekkon {
/// ///
/// 便 validity check /// 便 validity check
/// //// /// ////
static let mapEten26StaticKeys: [String: String] = [ static let mapETen26StaticKeys: [String: String] = [
"a": "", "b": "", "c": "", "d": "", "e": "", "f": "", "g": "", "h": "", "i": "", "j": "", "k": "", "a": "", "b": "", "c": "", "d": "", "e": "", "f": "", "g": "", "h": "", "i": "", "j": "", "k": "",
"l": "", "m": "", "n": "", "o": "", "p": "", "q": "", "r": "", "s": "", "t": "", "u": "", "v": "", "l": "", "m": "", "n": "", "o": "", "p": "", "q": "", "r": "", "s": "", "t": "", "u": "", "v": "",
"w": "", "x": "", "y": "", "z": "", " ": " ", "w": "", "x": "", "y": "", "z": "", " ": " ",
] ]
/// ///
static let mapQwertyEtenTraditional: [String: String] = [ static let mapQwertyETenTraditional: [String: String] = [
"'": "", ",": "", "-": "", ".": "", "/": "", "0": "", "1": "˙", "2": "ˊ", "3": "ˇ", "4": "ˋ", "7": "", "'": "", ",": "", "-": "", ".": "", "/": "", "0": "", "1": "˙", "2": "ˊ", "3": "ˇ", "4": "ˋ", "7": "",
"8": "", "9": "", ";": "", "=": "", "a": "", "b": "", "c": "", "d": "", "e": "", "f": "", "g": "", "8": "", "9": "", ";": "", "=": "", "a": "", "b": "", "c": "", "d": "", "e": "", "f": "", "g": "",
"h": "", "i": "", "j": "", "k": "", "l": "", "m": "", "n": "", "o": "", "p": "", "q": "", "r": "", "h": "", "i": "", "j": "", "k": "", "l": "", "m": "", "n": "", "o": "", "p": "", "q": "", "r": "",

View File

@ -27,41 +27,58 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import Cocoa import Cocoa
import InputMethodKit import InputMethodKit
///
private let kMinKeyLabelSize: CGFloat = 10 private let kMinKeyLabelSize: CGFloat = 10
///
private var ctlCandidateCurrent = ctlCandidateUniversal.init(.horizontal) private var ctlCandidateCurrent = ctlCandidateUniversal.init(.horizontal)
@objc(ctlInputMethod) ///
///
/// IMKInputController
///
/// / IMKInputController
/// 調
/// - Remark: IMKServer
/// IMKInputController
@objc(ctlInputMethod) // ObjC IMK ObjC
class ctlInputMethod: IMKInputController { class ctlInputMethod: IMKInputController {
@objc static var areWeDeleting = false /// 使使
static var areWeDeleting = false
///
static let tooltipController = TooltipController() static let tooltipController = TooltipController()
// MARK: - // MARK: -
private var currentClient: Any? /// 調
private var keyHandler: KeyHandler = .init() private var keyHandler: KeyHandler = .init()
///
private var state: InputState = .Empty() private var state: InputState = .Empty()
// KeyHandler 調 KeyHandler // MARK: -
// InputState ctlInputMethod
// keyHandler InputSignal
// currentKeyHandler KeyHandler
// currentKeyHandler
static var currentKeyHandler: KeyHandler = .init()
@objc static var currentInputMode = mgrPrefs.mostRecentInputMode
// MARK: - Keyboard Layout Specifier ///
func setKeyLayout() {
@objc func setKeyLayout() { client().overrideKeyboard(withKeyboardNamed: mgrPrefs.basicKeyboardLayout)
if let client = currentClient {
(client as? IMKTextInput)?.overrideKeyboard(withKeyboardNamed: mgrPrefs.basicKeyboardLayout)
}
} }
// MARK: - IMKInputController methods /// 調
func resetKeyHandler() {
keyHandler.clear()
handle(state: InputState.Empty())
}
// MARK: - IMKInputController
///
///
/// inputClient IMKServer IMKTextInput
/// - Remark: IMKInputController client()
/// - Parameters:
/// - server: IMKServer
/// - delegate:
/// - inputClient:
override init!(server: IMKServer!, delegate: Any!, client inputClient: Any!) { override init!(server: IMKServer!, delegate: Any!, client inputClient: Any!) {
super.init(server: server, delegate: delegate, client: inputClient) super.init(server: server, delegate: delegate, client: inputClient)
keyHandler.delegate = self keyHandler.delegate = self
@ -70,47 +87,44 @@ class ctlInputMethod: IMKInputController {
resetKeyHandler() resetKeyHandler()
} }
// MARK: - KeyHandler Reset Command // MARK: - IMKStateSetting
func resetKeyHandler(client sender: Any? = nil) { ///
keyHandler.clear() /// - Parameter sender: 使
if let client = sender as? IMKTextInput { override func activateServer(_ sender: Any!) {
handle(state: InputState.Empty(), client: client) _ = sender //
} else if let currentClient = currentClient {
handle(state: InputState.Empty(), client: currentClient)
}
}
// MARK: - IMKStateSetting protocol methods
override func activateServer(_ client: Any!) {
UserDefaults.standard.synchronize() UserDefaults.standard.synchronize()
// reset the state
currentClient = client
keyHandler.clear() keyHandler.clear()
keyHandler.ensureParser() keyHandler.ensureParser()
if let bundleCheckID = (client as? IMKTextInput)?.bundleIdentifier() { ///
if bundleCheckID != Bundle.main.bundleIdentifier { /// macOS
// Override the keyboard layout to the basic one. if client().bundleIdentifier() != Bundle.main.bundleIdentifier {
setKeyLayout() // Override the keyboard layout to the basic one.
handle(state: .Empty(), client: client) setKeyLayout()
} handle(state: .Empty())
} } //
(NSApp.delegate as? AppDelegate)?.checkForUpdate() (NSApp.delegate as? AppDelegate)?.checkForUpdate()
} }
override func deactivateServer(_ client: Any!) { ///
/// - Parameter sender: 使
override func deactivateServer(_ sender: Any!) {
_ = sender //
keyHandler.clear() keyHandler.clear()
currentClient = nil handle(state: .Empty())
handle(state: .Empty(), client: client) handle(state: .Deactivated())
handle(state: .Deactivated(), client: client)
} }
override func setValue(_ value: Any!, forTag tag: Int, client: Any!) { ///
_ = tag // Stop clang-format from ruining the parameters of this function. /// - Parameters:
/// - value: identifier bundle identifier info.plist
/// - tag: 使
/// - sender: 使
override func setValue(_ value: Any!, forTag tag: Int, client sender: Any!) {
_ = tag //
_ = sender //
var newInputMode = InputMode(rawValue: value as? String ?? "") ?? InputMode.imeModeNULL var newInputMode = InputMode(rawValue: value as? String ?? "") ?? InputMode.imeModeNULL
switch newInputMode { switch newInputMode {
case InputMode.imeModeCHS: case InputMode.imeModeCHS:
@ -126,32 +140,47 @@ class ctlInputMethod: IMKInputController {
UserDefaults.standard.synchronize() UserDefaults.standard.synchronize()
keyHandler.clear() keyHandler.clear()
keyHandler.inputMode = newInputMode keyHandler.inputMode = newInputMode
if let bundleCheckID = (client as? IMKTextInput)?.bundleIdentifier() { ///
if bundleCheckID != Bundle.main.bundleIdentifier { /// macOS
// Remember to override the keyboard layout again -- treat this as an activate event. if client().bundleIdentifier() != Bundle.main.bundleIdentifier {
setKeyLayout() // Remember to override the keyboard layout again -- treat this as an activate event.
handle(state: .Empty(), client: client) setKeyLayout()
} handle(state: .Empty())
} } //
} }
// //
ctlInputMethod.currentKeyHandler.inputMode = keyHandler.inputMode IME.currentInputMode = keyHandler.inputMode
} }
// MARK: - IMKServerInput protocol methods // MARK: - IMKServerInput
///
///
///
/// Swift `NSEvent.EventTypeMask = [.keyDown]` ObjC `NSKeyDownMask`
/// IMK
/// 使
/// `commitComposition(_ message)`
/// - Parameter sender: 使
/// - Returns: uint NSEvent NSEvent.h
override func recognizedEvents(_ sender: Any!) -> Int { override func recognizedEvents(_ sender: Any!) -> Int {
_ = sender // Stop clang-format from ruining the parameters of this function. _ = sender //
let events: NSEvent.EventTypeMask = [.keyDown, .flagsChanged] let events: NSEvent.EventTypeMask = [.keyDown, .flagsChanged]
return Int(events.rawValue) return Int(events.rawValue)
} }
/// NSEvent
/// - Parameters:
/// - event:
/// - sender: 使
/// - Returns: `true` IMK`false`
@objc(handleEvent:client:) override func handle(_ event: NSEvent!, client sender: Any!) -> Bool { @objc(handleEvent:client:) override func handle(_ event: NSEvent!, client sender: Any!) -> Bool {
// flags使 KeyHandler _ = sender //
// flags /// flags使 KeyHandler
// event.type == .flagsChanged return false /// flags
// NSInternalInconsistencyException /// event.type == .flagsChanged return false
/// NSInternalInconsistencyException
if event.type == .flagsChanged { if event.type == .flagsChanged {
return false return false
} }
@ -160,18 +189,15 @@ class ctlInputMethod: IMKInputController {
ctlInputMethod.areWeDeleting = event.modifierFlags.contains([.shift, .command]) ctlInputMethod.areWeDeleting = event.modifierFlags.contains([.shift, .command])
var textFrame = NSRect.zero var textFrame = NSRect.zero
guard let client = sender as? IMKTextInput else {
return false
}
let attributes: [AnyHashable: Any]? = client.attributes( let attributes: [AnyHashable: Any]? = client().attributes(
forCharacterIndex: 0, lineHeightRectangle: &textFrame forCharacterIndex: 0, lineHeightRectangle: &textFrame
) )
let isTypingVertical = let isTypingVertical =
(attributes?["IMKTextOrientation"] as? NSNumber)?.intValue == 0 || false (attributes?["IMKTextOrientation"] as? NSNumber)?.intValue == 0 || false
if client.bundleIdentifier() if client().bundleIdentifier()
== "org.atelierInmu.vChewing.vChewingPhraseEditor" == "org.atelierInmu.vChewing.vChewingPhraseEditor"
{ {
IME.areWeUsingOurOwnPhraseEditor = true IME.areWeUsingOurOwnPhraseEditor = true
@ -187,276 +213,202 @@ class ctlInputMethod: IMKInputController {
return false return false
} }
/// 調
/// result bool IMK
let result = keyHandler.handle(input: input, state: state) { newState in let result = keyHandler.handle(input: input, state: state) { newState in
self.handle(state: newState, client: client) self.handle(state: newState)
} errorCallback: { } errorCallback: {
clsSFX.beep() clsSFX.beep()
} }
return result return result
} }
// App Ctrl+Enter / Shift+Enter /// App Ctrl+Enter / Shift+Enter
// handle(event:) Event /// handle(event:) Event
// commitComposition /// commitComposition
/// - Parameter sender: 使
override func commitComposition(_ sender: Any!) { override func commitComposition(_ sender: Any!) {
resetKeyHandler(client: sender) _ = sender //
} if let state = state as? InputState.NotEmpty {
/// 調
// handle(state: InputState.Committing(textToCommit: state.composingBuffer))
override func composedString(_ sender: Any!) -> Any! { }
_ = sender // Stop clang-format from ruining the parameters of this function. resetKeyHandler()
return (state as? InputState.NotEmpty)?.composingBuffer ?? ""
} }
} }
// MARK: - State Handling // MARK: - 調 (State Handling)
extension ctlInputMethod { extension ctlInputMethod {
private func handle(state newState: InputState, client: Any?) { /// 調
let previous = state ///
///
///
/// - Parameter newState:
private func handle(state newState: InputState) {
let prevState = state
state = newState state = newState
if let newState = newState as? InputState.Deactivated { switch newState {
handle(state: newState, previous: previous, client: client) case let newState as InputState.Deactivated:
} else if let newState = newState as? InputState.Empty { handle(state: newState, previous: prevState)
handle(state: newState, previous: previous, client: client) case let newState as InputState.Empty:
} else if let newState = newState as? InputState.EmptyIgnoringPreviousState { handle(state: newState, previous: prevState)
handle(state: newState, previous: previous, client: client) case let newState as InputState.EmptyIgnoringPreviousState:
} else if let newState = newState as? InputState.Committing { handle(state: newState, previous: prevState)
handle(state: newState, previous: previous, client: client) case let newState as InputState.Committing:
} else if let newState = newState as? InputState.Inputting { handle(state: newState, previous: prevState)
handle(state: newState, previous: previous, client: client) case let newState as InputState.Inputting:
} else if let newState = newState as? InputState.Marking { handle(state: newState, previous: prevState)
handle(state: newState, previous: previous, client: client) case let newState as InputState.Marking:
} else if let newState = newState as? InputState.ChoosingCandidate { handle(state: newState, previous: prevState)
handle(state: newState, previous: previous, client: client) case let newState as InputState.ChoosingCandidate:
} else if let newState = newState as? InputState.AssociatedPhrases { handle(state: newState, previous: prevState)
handle(state: newState, previous: previous, client: client) case let newState as InputState.AssociatedPhrases:
} else if let newState = newState as? InputState.SymbolTable { handle(state: newState, previous: prevState)
handle(state: newState, previous: previous, client: client) case let newState as InputState.SymbolTable:
handle(state: newState, previous: prevState)
default: break
} }
} }
private func commit(text: String, client: Any!) { /// .NotEmpty()
func kanjiConversionIfRequired(_ text: String) -> String { private func setInlineDisplayWithCursor() {
if keyHandler.inputMode == InputMode.imeModeCHT { guard let state = state as? InputState.NotEmpty else {
if !mgrPrefs.chineseConversionEnabled, mgrPrefs.shiftJISShinjitaiOutputEnabled { clearInlineDisplay()
return vChewingKanjiConverter.cnvTradToJIS(text) return
}
if mgrPrefs.chineseConversionEnabled, !mgrPrefs.shiftJISShinjitaiOutputEnabled {
return vChewingKanjiConverter.cnvTradToKangXi(text)
}
//
if mgrPrefs.chineseConversionEnabled, mgrPrefs.shiftJISShinjitaiOutputEnabled {
return vChewingKanjiConverter.cnvTradToJIS(text)
}
// if (!mgrPrefs.chineseConversionEnabled && !mgrPrefs.shiftJISShinjitaiOutputEnabled) || (keyHandler.inputMode != InputMode.imeModeCHT);
return text
}
return text
} }
/// selectionRange
/// 0 replacementRangeNSNotFound
///
client().setMarkedText(
state.attributedString, selectionRange: NSRange(location: state.cursorIndex, length: 0),
replacementRange: NSRange(location: NSNotFound, length: NSNotFound)
)
}
let buffer = kanjiConversionIfRequired(text) /// .NotEmpty()
/// setInlineDisplayWithCursor()
private func clearInlineDisplay() {
client().setMarkedText(
"", selectionRange: NSRange(location: 0, length: 0),
replacementRange: NSRange(location: NSNotFound, length: NSNotFound)
)
}
///
private func commit(text: String) {
let buffer = IME.kanjiConversionIfRequired(text)
if buffer.isEmpty { if buffer.isEmpty {
return return
} }
var bufferOutput = "" client().insertText(
buffer, replacementRange: NSRange(location: NSNotFound, length: NSNotFound)
//
for theChar in buffer {
if let charCode = theChar.utf16.first {
if !(theChar.isASCII && !(charCode.isPrintable())) {
bufferOutput += String(theChar)
}
}
}
(client as? IMKTextInput)?.insertText(
bufferOutput, replacementRange: NSRange(location: NSNotFound, length: NSNotFound)
) )
} }
private func handle(state: InputState.Deactivated, previous: InputState, client: Any?) { private func handle(state: InputState.Deactivated, previous: InputState) {
_ = state // Stop clang-format from ruining the parameters of this function. _ = state //
currentClient = nil
ctlCandidateCurrent.delegate = nil ctlCandidateCurrent.delegate = nil
ctlCandidateCurrent.visible = false ctlCandidateCurrent.visible = false
hideTooltip() hideTooltip()
if let previous = previous as? InputState.NotEmpty { if let previous = previous as? InputState.NotEmpty {
commit(text: previous.composingBuffer, client: client) commit(text: previous.composingBuffer)
} }
(client as? IMKTextInput)?.setMarkedText( clearInlineDisplay()
"", selectionRange: NSRange(location: 0, length: 0),
replacementRange: NSRange(location: NSNotFound, length: NSNotFound)
)
} }
private func handle(state: InputState.Empty, previous: InputState, client: Any?) { private func handle(state: InputState.Empty, previous: InputState) {
_ = state // Stop clang-format from ruining the parameters of this function. _ = state //
ctlCandidateCurrent.visible = false ctlCandidateCurrent.visible = false
hideTooltip() hideTooltip()
guard let client = client as? IMKTextInput else {
return
}
if let previous = previous as? InputState.NotEmpty, if let previous = previous as? InputState.NotEmpty,
!(state is InputState.EmptyIgnoringPreviousState) !(state is InputState.EmptyIgnoringPreviousState)
{ {
commit(text: previous.composingBuffer, client: client) commit(text: previous.composingBuffer)
} }
client.setMarkedText( clearInlineDisplay()
"", selectionRange: NSRange(location: 0, length: 0),
replacementRange: NSRange(location: NSNotFound, length: NSNotFound)
)
} }
private func handle( private func handle(
state: InputState.EmptyIgnoringPreviousState, previous: InputState, client: Any! state: InputState.EmptyIgnoringPreviousState, previous: InputState
) { ) {
_ = state // Stop clang-format from ruining the parameters of this function. _ = state //
_ = previous // Stop clang-format from ruining the parameters of this function. _ = previous //
ctlCandidateCurrent.visible = false ctlCandidateCurrent.visible = false
hideTooltip() hideTooltip()
clearInlineDisplay()
guard let client = client as? IMKTextInput else {
return
}
client.setMarkedText(
"", selectionRange: NSRange(location: 0, length: 0),
replacementRange: NSRange(location: NSNotFound, length: NSNotFound)
)
} }
private func handle(state: InputState.Committing, previous: InputState, client: Any?) { private func handle(state: InputState.Committing, previous: InputState) {
_ = previous // Stop clang-format from ruining the parameters of this function. _ = previous //
ctlCandidateCurrent.visible = false ctlCandidateCurrent.visible = false
hideTooltip() hideTooltip()
let textToCommit = state.textToCommit
guard let client = client as? IMKTextInput else { if !textToCommit.isEmpty {
return commit(text: textToCommit)
} }
clearInlineDisplay()
let poppedText = state.poppedText
if !poppedText.isEmpty {
commit(text: poppedText, client: client)
}
client.setMarkedText(
"", selectionRange: NSRange(location: 0, length: 0),
replacementRange: NSRange(location: NSNotFound, length: NSNotFound)
)
} }
private func handle(state: InputState.Inputting, previous: InputState, client: Any?) { private func handle(state: InputState.Inputting, previous: InputState) {
_ = previous // Stop clang-format from ruining the parameters of this function. _ = previous //
ctlCandidateCurrent.visible = false ctlCandidateCurrent.visible = false
hideTooltip() hideTooltip()
let textToCommit = state.textToCommit
guard let client = client as? IMKTextInput else { if !textToCommit.isEmpty {
return commit(text: textToCommit)
} }
setInlineDisplayWithCursor()
let poppedText = state.poppedText
if !poppedText.isEmpty {
commit(text: poppedText, client: client)
}
// the selection range is where the cursor is, with the length being 0 and replacement range NSNotFound,
// i.e. the client app needs to take care of where to put this composing buffer
client.setMarkedText(
state.attributedString, selectionRange: NSRange(location: state.cursorIndex, length: 0),
replacementRange: NSRange(location: NSNotFound, length: NSNotFound)
)
if !state.tooltip.isEmpty { if !state.tooltip.isEmpty {
show( show(
tooltip: state.tooltip, composingBuffer: state.composingBuffer, tooltip: state.tooltip, composingBuffer: state.composingBuffer,
cursorIndex: state.cursorIndex, client: client cursorIndex: state.cursorIndex
) )
} }
} }
private func handle(state: InputState.Marking, previous: InputState, client: Any?) { private func handle(state: InputState.Marking, previous: InputState) {
_ = previous // Stop clang-format from ruining the parameters of this function. _ = previous //
ctlCandidateCurrent.visible = false ctlCandidateCurrent.visible = false
guard let client = client as? IMKTextInput else { setInlineDisplayWithCursor()
hideTooltip()
return
}
// the selection range is where the cursor is, with the length being 0 and replacement range NSNotFound,
// i.e. the client app needs to take care of where to put this composing buffer
client.setMarkedText(
state.attributedString, selectionRange: NSRange(location: state.cursorIndex, length: 0),
replacementRange: NSRange(location: NSNotFound, length: NSNotFound)
)
if state.tooltip.isEmpty { if state.tooltip.isEmpty {
hideTooltip() hideTooltip()
} else { } else {
show( show(
tooltip: state.tooltip, composingBuffer: state.composingBuffer, tooltip: state.tooltip, composingBuffer: state.composingBuffer,
cursorIndex: state.markerIndex, client: client cursorIndex: state.markerIndex
) )
} }
} }
private func handle(state: InputState.ChoosingCandidate, previous: InputState, client: Any?) { private func handle(state: InputState.ChoosingCandidate, previous: InputState) {
_ = previous // Stop clang-format from ruining the parameters of this function. _ = previous //
hideTooltip() hideTooltip()
guard let client = client as? IMKTextInput else { setInlineDisplayWithCursor()
ctlCandidateCurrent.visible = false show(candidateWindowWith: state)
return
}
// the selection range is where the cursor is, with the length being 0 and replacement range NSNotFound,
// i.e. the client app needs to take care of where to put this composing buffer
client.setMarkedText(
state.attributedString, selectionRange: NSRange(location: state.cursorIndex, length: 0),
replacementRange: NSRange(location: NSNotFound, length: NSNotFound)
)
show(candidateWindowWith: state, client: client)
} }
private func handle(state: InputState.SymbolTable, previous: InputState, client: Any?) { private func handle(state: InputState.SymbolTable, previous: InputState) {
_ = previous // Stop clang-format from ruining the parameters of this function. _ = previous //
hideTooltip() hideTooltip()
guard let client = client as? IMKTextInput else { setInlineDisplayWithCursor()
ctlCandidateCurrent.visible = false show(candidateWindowWith: state)
return
}
// the selection range is where the cursor is, with the length being 0 and replacement range NSNotFound,
// i.e. the client app needs to take care of where to put this composing buffer
client.setMarkedText(
state.attributedString, selectionRange: NSRange(location: state.cursorIndex, length: 0),
replacementRange: NSRange(location: NSNotFound, length: NSNotFound)
)
show(candidateWindowWith: state, client: client)
} }
private func handle(state: InputState.AssociatedPhrases, previous: InputState, client: Any?) { private func handle(state: InputState.AssociatedPhrases, previous: InputState) {
_ = previous // Stop clang-format from ruining the parameters of this function. _ = previous //
hideTooltip() hideTooltip()
guard let client = client as? IMKTextInput else { clearInlineDisplay()
ctlCandidateCurrent.visible = false show(candidateWindowWith: state)
return
}
client.setMarkedText(
"", selectionRange: NSRange(location: 0, length: 0),
replacementRange: NSRange(location: NSNotFound, length: NSNotFound)
)
show(candidateWindowWith: state, client: client)
} }
} }
// MARK: - // MARK: -
extension ctlInputMethod { extension ctlInputMethod {
private func show(candidateWindowWith state: InputState, client: Any!) { private func show(candidateWindowWith state: InputState) {
var isTypingVertical: Bool { var isTypingVertical: Bool {
if let state = state as? InputState.ChoosingCandidate { if let state = state as? InputState.ChoosingCandidate {
return state.isTypingVertical return state.isTypingVertical
@ -518,7 +470,7 @@ extension ctlInputMethod {
? "Sarasa Term Slab SC" : "Sarasa Term Slab TC" ? "Sarasa Term Slab SC" : "Sarasa Term Slab TC"
var finalReturnFont = var finalReturnFont =
NSFont(name: currentMUIFont, size: size) ?? NSFont.systemFont(ofSize: size) NSFont(name: currentMUIFont, size: size) ?? NSFont.systemFont(ofSize: size)
// macOS 11 Big Sur macOS 12 Monterey 使 // macOS 11 Big Sur macOS 12 Monterey 使
if #available(macOS 12.0, *) { finalReturnFont = NSFont.systemFont(ofSize: size) } if #available(macOS 12.0, *) { finalReturnFont = NSFont.systemFont(ofSize: size) }
if let name = name { if let name = name {
return NSFont(name: name, size: size) ?? finalReturnFont return NSFont(name: name, size: size) ?? finalReturnFont
@ -543,7 +495,6 @@ extension ctlInputMethod {
ctlCandidateCurrent.delegate = self ctlCandidateCurrent.delegate = self
ctlCandidateCurrent.reloadData() ctlCandidateCurrent.reloadData()
currentClient = client
ctlCandidateCurrent.visible = true ctlCandidateCurrent.visible = true
@ -558,7 +509,7 @@ extension ctlInputMethod {
} }
while lineHeightRect.origin.x == 0, lineHeightRect.origin.y == 0, cursor >= 0 { while lineHeightRect.origin.x == 0, lineHeightRect.origin.y == 0, cursor >= 0 {
(client as? IMKTextInput)?.attributes( client().attributes(
forCharacterIndex: cursor, lineHeightRectangle: &lineHeightRect forCharacterIndex: cursor, lineHeightRectangle: &lineHeightRect
) )
cursor -= 1 cursor -= 1
@ -579,14 +530,14 @@ extension ctlInputMethod {
} }
} }
private func show(tooltip: String, composingBuffer: String, cursorIndex: Int, client: Any!) { private func show(tooltip: String, composingBuffer: String, cursorIndex: Int) {
var lineHeightRect = NSRect(x: 0.0, y: 0.0, width: 16.0, height: 16.0) var lineHeightRect = NSRect(x: 0.0, y: 0.0, width: 16.0, height: 16.0)
var cursor = cursorIndex var cursor = cursorIndex
if cursor == composingBuffer.count, cursor != 0 { if cursor == composingBuffer.count, cursor != 0 {
cursor -= 1 cursor -= 1
} }
while lineHeightRect.origin.x == 0, lineHeightRect.origin.y == 0, cursor >= 0 { while lineHeightRect.origin.x == 0, lineHeightRect.origin.y == 0, cursor >= 0 {
(client as? IMKTextInput)?.attributes( client().attributes(
forCharacterIndex: cursor, lineHeightRectangle: &lineHeightRect forCharacterIndex: cursor, lineHeightRectangle: &lineHeightRect
) )
cursor -= 1 cursor -= 1
@ -644,7 +595,7 @@ extension ctlInputMethod: KeyHandlerDelegate {
extension ctlInputMethod: ctlCandidateDelegate { extension ctlInputMethod: ctlCandidateDelegate {
func candidateCountForController(_ controller: ctlCandidate) -> Int { func candidateCountForController(_ controller: ctlCandidate) -> Int {
_ = controller // Stop clang-format from ruining the parameters of this function. _ = controller //
if let state = state as? InputState.ChoosingCandidate { if let state = state as? InputState.ChoosingCandidate {
return state.candidates.count return state.candidates.count
} else if let state = state as? InputState.AssociatedPhrases { } else if let state = state as? InputState.AssociatedPhrases {
@ -656,7 +607,7 @@ extension ctlInputMethod: ctlCandidateDelegate {
func ctlCandidate(_ controller: ctlCandidate, candidateAtIndex index: Int) func ctlCandidate(_ controller: ctlCandidate, candidateAtIndex index: Int)
-> String -> String
{ {
_ = controller // Stop clang-format from ruining the parameters of this function. _ = controller //
if let state = state as? InputState.ChoosingCandidate { if let state = state as? InputState.ChoosingCandidate {
return state.candidates[index] return state.candidates[index]
} else if let state = state as? InputState.AssociatedPhrases { } else if let state = state as? InputState.AssociatedPhrases {
@ -666,21 +617,19 @@ extension ctlInputMethod: ctlCandidateDelegate {
} }
func ctlCandidate(_ controller: ctlCandidate, didSelectCandidateAtIndex index: Int) { func ctlCandidate(_ controller: ctlCandidate, didSelectCandidateAtIndex index: Int) {
_ = controller // Stop clang-format from ruining the parameters of this function. _ = controller //
let client = currentClient
if let state = state as? InputState.SymbolTable, if let state = state as? InputState.SymbolTable,
let node = state.node.children?[index] let node = state.node.children?[index]
{ {
if let children = node.children, !children.isEmpty { if let children = node.children, !children.isEmpty {
handle(state: .Empty(), client: client) // handle(state: .Empty()) //
handle( handle(
state: .SymbolTable(node: node, isTypingVertical: state.isTypingVertical), state: .SymbolTable(node: node, isTypingVertical: state.isTypingVertical)
client: currentClient
) )
} else { } else {
handle(state: .Committing(poppedText: node.title), client: client) handle(state: .Committing(textToCommit: node.title))
handle(state: .Empty(), client: client) handle(state: .Empty())
} }
return return
} }
@ -694,33 +643,33 @@ extension ctlInputMethod: ctlCandidateDelegate {
if mgrPrefs.useSCPCTypingMode { if mgrPrefs.useSCPCTypingMode {
keyHandler.clear() keyHandler.clear()
let composingBuffer = inputting.composingBuffer let composingBuffer = inputting.composingBuffer
handle(state: .Committing(poppedText: composingBuffer), client: client) handle(state: .Committing(textToCommit: composingBuffer))
if mgrPrefs.associatedPhrasesEnabled, if mgrPrefs.associatedPhrasesEnabled,
let associatePhrases = keyHandler.buildAssociatePhraseState( let associatePhrases = keyHandler.buildAssociatePhraseState(
withKey: composingBuffer, isTypingVertical: state.isTypingVertical withKey: composingBuffer, isTypingVertical: state.isTypingVertical
), !associatePhrases.candidates.isEmpty ), !associatePhrases.candidates.isEmpty
{ {
handle(state: associatePhrases, client: client) handle(state: associatePhrases)
} else { } else {
handle(state: .Empty(), client: client) handle(state: .Empty())
} }
} else { } else {
handle(state: inputting, client: client) handle(state: inputting)
} }
return return
} }
if let state = state as? InputState.AssociatedPhrases { if let state = state as? InputState.AssociatedPhrases {
let selectedValue = state.candidates[index] let selectedValue = state.candidates[index]
handle(state: .Committing(poppedText: selectedValue), client: currentClient) handle(state: .Committing(textToCommit: selectedValue))
if mgrPrefs.associatedPhrasesEnabled, if mgrPrefs.associatedPhrasesEnabled,
let associatePhrases = keyHandler.buildAssociatePhraseState( let associatePhrases = keyHandler.buildAssociatePhraseState(
withKey: selectedValue, isTypingVertical: state.isTypingVertical withKey: selectedValue, isTypingVertical: state.isTypingVertical
), !associatePhrases.candidates.isEmpty ), !associatePhrases.candidates.isEmpty
{ {
handle(state: associatePhrases, client: client) handle(state: associatePhrases)
} else { } else {
handle(state: .Empty(), client: client) handle(state: .Empty())
} }
} }
} }

View File

@ -171,7 +171,7 @@ extension ctlInputMethod {
) )
} }
// NSMenu modified key // NSMenu
setKeyLayout() setKeyLayout()
return menu return menu

View File

@ -28,10 +28,34 @@ import Cocoa
// The namespace of this input method. // The namespace of this input method.
public enum vChewing {} public enum vChewing {}
// The type of input modes.
public enum InputMode: String {
case imeModeCHS = "org.atelierInmu.inputmethod.vChewing.IMECHS"
case imeModeCHT = "org.atelierInmu.inputmethod.vChewing.IMECHT"
case imeModeNULL = ""
}
public enum IME { public enum IME {
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: -
static var currentInputMode: InputMode = .init(rawValue: mgrPrefs.mostRecentInputMode) ?? .imeModeNULL
static func kanjiConversionIfRequired(_ text: String) -> String {
if currentInputMode == InputMode.imeModeCHT {
switch (mgrPrefs.chineseConversionEnabled, mgrPrefs.shiftJISShinjitaiOutputEnabled) {
case (false, true): return vChewingKanjiConverter.cnvTradToJIS(text)
case (true, false): return vChewingKanjiConverter.cnvTradToKangXi(text)
//
case (true, true): return vChewingKanjiConverter.cnvTradToJIS(text)
case (false, false): return text
}
}
return text
}
// MARK: - // MARK: -
static var areWeUsingOurOwnPhraseEditor: Bool = false static var areWeUsingOurOwnPhraseEditor: Bool = false
@ -40,10 +64,10 @@ public enum IME {
static func getInputMode(isReversed: Bool = false) -> InputMode { static func getInputMode(isReversed: Bool = false) -> InputMode {
if isReversed { if isReversed {
return (ctlInputMethod.currentKeyHandler.inputMode == InputMode.imeModeCHT) return (IME.currentInputMode == InputMode.imeModeCHT)
? InputMode.imeModeCHS : InputMode.imeModeCHT ? InputMode.imeModeCHS : InputMode.imeModeCHT
} else { } else {
return ctlInputMethod.currentKeyHandler.inputMode return IME.currentInputMode
} }
} }
@ -64,7 +88,7 @@ public enum IME {
// MARK: - Initializing Language Models. // MARK: - Initializing Language Models.
static func initLangModels(userOnly: Bool) { static func initLangModels(userOnly: Bool) {
// mgrLangModel loadUserPhrases dataFolderPath // mgrLangModel loadUserPhrases dataFolderPath
// //
// //
mgrLangModel.loadUserAssociatesData() mgrLangModel.loadUserAssociatesData()

View File

@ -78,10 +78,10 @@ private let kMinCandidateListTextSize: CGFloat = 12
private let kMaxCandidateListTextSize: CGFloat = 196 private let kMaxCandidateListTextSize: CGFloat = 196
// default, min and max composing buffer size (in codepoints) // default, min and max composing buffer size (in codepoints)
// modern Macs can usually work up to 16 codepoints when the builder still // modern Macs can usually work up to 16 codepoints when the compositor still
// walks the grid with good performance slower Macs (like old PowerBooks) // walks the grid with good performance slower Macs (like old PowerBooks)
// will start to sputter beyond 12 such is the algorithmatic complexity // will start to sputter beyond 12 such is the algorithmatic complexity
// of the Viterbi algorithm used in the builder library (at O(N^2)) // of the Viterbi algorithm used in the Megrez library (at O(N^2))
private let kDefaultComposingBufferSize = 20 private let kDefaultComposingBufferSize = 20
private let kMinComposingBufferSize = 10 private let kMinComposingBufferSize = 10
private let kMaxComposingBufferSize = 40 private let kMaxComposingBufferSize = 40
@ -176,9 +176,9 @@ struct ComposingBufferSize {
enum MandarinParser: Int { enum MandarinParser: Int {
case ofStandard = 0 case ofStandard = 0
case ofEten = 1 case ofETen = 1
case ofHsu = 2 case ofHsu = 2
case ofEten26 = 3 case ofETen26 = 3
case ofIBM = 4 case ofIBM = 4
case ofMiTAC = 5 case ofMiTAC = 5
case ofFakeSeigyou = 6 case ofFakeSeigyou = 6
@ -193,11 +193,11 @@ enum MandarinParser: Int {
switch self { switch self {
case .ofStandard: case .ofStandard:
return "Standard" return "Standard"
case .ofEten: case .ofETen:
return "ETen" return "ETen"
case .ofHsu: case .ofHsu:
return "Hsu" return "Hsu"
case .ofEten26: case .ofETen26:
return "ETen26" return "ETen26"
case .ofIBM: case .ofIBM:
return "IBM" return "IBM"

View File

@ -24,9 +24,6 @@ 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.
*/ */
// NOTE: We still keep some of the comments left by Zonble,
// regardless that he is not in charge of this Swift module
import Foundation import Foundation
// //
@ -39,29 +36,25 @@ private var lmSymbols = vChewing.LMCoreNS(
) )
extension vChewing { extension vChewing {
/// LMInstantiator is a facade for managing a set of models including /// LMInstantiatorLMI
/// the input method language model, user phrases and excluded phrases. /// LanguageModel 使
///
/// ///
/// It is the primary model class that the input controller and grammar builder /// LMI 調
/// of vChewing talks to. When the grammar builder starts to build a sentence /// LMI
/// 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 /// LMI
/// model and user phrases. The process is
/// ///
/// 1) Get the original unigrams. /// 1.
/// 2) Drop the unigrams whose value is contained in the exclusion map. /// 2.
/// 3) Replace the values of the unigrams using the phrase replacement map. /// 3.
/// 4) Drop the duplicated phrases from the generated unigram array. /// 4.
/// ///
/// The controller can ask the model to load the primary input method language /// LMI LMI
/// 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 class LMInstantiator: Megrez.LanguageModel {
// //
public var isPhraseReplacementEnabled = false public var isPhraseReplacementEnabled = false
public var isCNSEnabled = false public var isCNSEnabled = false
public var isSymbolEnabled = false public var isSymbolEnabled = false
@ -76,9 +69,9 @@ extension vChewing {
/// LMCoreEX 2010-2013 mac /// LMCoreEX 2010-2013 mac
/// LMCoreNS plist /// LMCoreNS plist
// //
/// Reverse // Reverse
/// Reverse // Reverse
var lmCore = LMCoreNS( var lmCore = LMCoreNS(
reverse: false, consolidate: false, defaultScore: -9.9, forceDefaultScore: false reverse: false, consolidate: false, defaultScore: -9.9, forceDefaultScore: false
) )
@ -100,10 +93,10 @@ extension vChewing {
var lmReplacements = LMReplacments() var lmReplacements = LMReplacments()
var lmAssociates = LMAssociates() var lmAssociates = LMAssociates()
// //
override init() {} override init() {}
// 調 // 調
public var isLanguageModelLoaded: Bool { lmCore.isLoaded() } public var isLanguageModelLoaded: Bool { lmCore.isLoaded() }
public func loadLanguageModel(path: String) { public func loadLanguageModel(path: String) {
@ -194,12 +187,12 @@ extension vChewing {
// MARK: - Core Functions (Public) // 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] { } // public func bigramsForKeys(preceedingKey: String, key: String) -> [Megrez.Bigram] { }
/// Returns a list of available unigram for the given key. /// LMI
/// @param key:String represents the BPMF reading or a symbol key. /// - Parameter key:
/// For instance, it you pass "ˇ", it returns "" and other possible candidates. /// - Returns:
override open func unigramsFor(key: String) -> [Megrez.Unigram] { override open func unigramsFor(key: String) -> [Megrez.Unigram] {
if key == " " { if key == " " {
/// ///
@ -267,6 +260,11 @@ extension vChewing {
// MARK: - Core Functions (Private) // MARK: - Core Functions (Private)
///
/// - Parameters:
/// - unigrams:
/// - filteredPairs:
/// - Returns:
func filterAndTransform( func filterAndTransform(
unigrams: [Megrez.Unigram], unigrams: [Megrez.Unigram],
filter filteredPairs: Set<Megrez.KeyValuePair> filter filteredPairs: Set<Megrez.KeyValuePair>

View File

@ -132,12 +132,12 @@ extension vChewing {
/// 使 strData /// 使 strData
/// ///
/// ///
/// - parameters: /// - parameters:
/// - precedingKey: /// - precedingKey:
/// - key: /// - key:
public func bigramsForKeys(precedingKey: String, key: String) -> [Megrez.Bigram] { public func bigramsForKeys(precedingKey: String, key: String) -> [Megrez.Bigram] {
// Swift // Swift
// [Megrez.Bigram]() // [Megrez.Bigram]()
precedingKey == key ? [Megrez.Bigram]() : [Megrez.Bigram]() precedingKey == key ? [Megrez.Bigram]() : [Megrez.Bigram]()
} }

View File

@ -126,12 +126,12 @@ extension vChewing {
/// 使 UTF8 /// 使 UTF8
/// ///
/// ///
/// - parameters: /// - parameters:
/// - precedingKey: /// - precedingKey:
/// - key: /// - key:
public func bigramsForKeys(precedingKey: String, key: String) -> [Megrez.Bigram] { public func bigramsForKeys(precedingKey: String, key: String) -> [Megrez.Bigram] {
// Swift // Swift
// [Megrez.Bigram]() // [Megrez.Bigram]()
precedingKey == key ? [Megrez.Bigram]() : [Megrez.Bigram]() precedingKey == key ? [Megrez.Bigram]() : [Megrez.Bigram]()
} }
@ -167,7 +167,7 @@ extension vChewing {
rangeMap[cnvPhonabetToASCII(key)] != nil rangeMap[cnvPhonabetToASCII(key)] != nil
} }
/// ///
/// ///
/// 使 plist /// 使 plist
/// ///
@ -190,7 +190,7 @@ extension vChewing {
return strOutput return strOutput
} }
/// ///
/// ///
/// ASCII /// ASCII
/// - parameters: /// - parameters:

View File

@ -29,27 +29,34 @@ extension vChewing {
public class LMUserOverride { public class LMUserOverride {
// MARK: - Private Structures // MARK: - Private Structures
struct Override { // class
class Override {
var count: Int = 0 var count: Int = 0
var timestamp: Double = 0.0 var timestamp: Double = 0.0
} }
struct Observation { class Observation {
var count: Int = 0 var count: Int = 0
var overrides: [String: Override] = [:] var overrides: [String: Override] = [:]
mutating func update(candidate: String, timestamp: Double) { func update(candidate: String, timestamp: Double) {
count += 1 count += 1
if var neta = overrides[candidate] { if let neta = overrides[candidate] {
neta.timestamp = timestamp neta.timestamp = timestamp
neta.count += 1 neta.count += 1
overrides[candidate] = neta
} }
} }
} }
struct KeyObservationPair { class KeyObservationPair {
var key: String var key: String
var observation: Observation var observation: Observation
init(key: String, observation: Observation) {
self.key = key
self.observation = observation
}
} }
// MARK: - Main // MARK: - Main
@ -74,7 +81,7 @@ extension vChewing {
let key = convertKeyFrom(walkedNodes: walkedNodes, cursorIndex: cursorIndex) let key = convertKeyFrom(walkedNodes: walkedNodes, cursorIndex: cursorIndex)
guard mutLRUMap[key] != nil else { guard mutLRUMap[key] != nil else {
var observation: Observation = .init() let observation: Observation = .init()
observation.update(candidate: candidate, timestamp: timestamp) observation.update(candidate: candidate, timestamp: timestamp)
let koPair = KeyObservationPair(key: key, observation: observation) let koPair = KeyObservationPair(key: key, observation: observation)
mutLRUMap[key] = koPair mutLRUMap[key] = koPair
@ -87,7 +94,7 @@ extension vChewing {
IME.prtDebugIntel("UOM: Observation finished with new observation: \(key)") IME.prtDebugIntel("UOM: Observation finished with new observation: \(key)")
return return
} }
if var theNeta = mutLRUMap[key] { if let theNeta = mutLRUMap[key] {
theNeta.observation.update(candidate: candidate, timestamp: timestamp) theNeta.observation.update(candidate: candidate, timestamp: timestamp)
mutLRUList.insert(theNeta, at: 0) mutLRUList.insert(theNeta, at: 0)
mutLRUMap[key] = theNeta mutLRUMap[key] = theNeta

View File

@ -0,0 +1,99 @@
// 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
class SymbolNode {
var title: String
var children: [SymbolNode]?
init(_ title: String, _ children: [SymbolNode]? = nil) {
self.title = title
self.children = children
}
init(_ title: String, symbols: String) {
self.title = title
children = Array(symbols).map { SymbolNode(String($0), nil) }
}
static let catCommonSymbols = String(
format: NSLocalizedString("catCommonSymbols", comment: ""))
static let catHoriBrackets = String(
format: NSLocalizedString("catHoriBrackets", comment: ""))
static let catVertBrackets = String(
format: NSLocalizedString("catVertBrackets", comment: ""))
static let catGreekLetters = String(
format: NSLocalizedString("catGreekLetters", comment: ""))
static let catMathSymbols = String(
format: NSLocalizedString("catMathSymbols", comment: ""))
static let catCurrencyUnits = String(
format: NSLocalizedString("catCurrencyUnits", comment: ""))
static let catSpecialSymbols = String(
format: NSLocalizedString("catSpecialSymbols", comment: ""))
static let catUnicodeSymbols = String(
format: NSLocalizedString("catUnicodeSymbols", comment: ""))
static let catCircledKanjis = String(
format: NSLocalizedString("catCircledKanjis", comment: ""))
static let catCircledKataKana = String(
format: NSLocalizedString("catCircledKataKana", comment: ""))
static let catBracketKanjis = String(
format: NSLocalizedString("catBracketKanjis", comment: ""))
static let catSingleTableLines = String(
format: NSLocalizedString("catSingleTableLines", comment: ""))
static let catDoubleTableLines = String(
format: NSLocalizedString("catDoubleTableLines", comment: ""))
static let catFillingBlocks = String(
format: NSLocalizedString("catFillingBlocks", comment: ""))
static let catLineSegments = String(
format: NSLocalizedString("catLineSegments", comment: ""))
static let root: SymbolNode = .init(
"/",
[
SymbolNode(""),
SymbolNode(catCommonSymbols, symbols: ",、。.?!;:‧‥﹐﹒˙·‘’“”〝〞‵′〃~$%@&#*"),
SymbolNode(catHoriBrackets, symbols: "()「」〔〕{}〈〉『』《》【】﹙﹚﹝﹞﹛﹜"),
SymbolNode(catVertBrackets, symbols: "︵︶﹁﹂︹︺︷︸︿﹀﹃﹄︽︾︻︼"),
SymbolNode(
catGreekLetters, symbols: "αβγδεζηθικλμνξοπρστυφχψωΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ"
),
SymbolNode(catMathSymbols, symbols: "+-×÷=≠≒∞±√<>﹤﹥≦≧∩∪ˇ⊥∠∟⊿㏒㏑∫∮∵∴╳﹢"),
SymbolNode(catCurrencyUnits, symbols: "$€¥¢£₽₨₩฿₺₮₱₭₴₦৲৳૱௹﷼₹₲₪₡₫៛₵₢₸₤₳₥₠₣₰₧₯₶₷"),
SymbolNode(catSpecialSymbols, symbols: "↑↓←→↖↗↙↘↺⇧⇩⇦⇨⇄⇆⇅⇵↻◎○●⊕⊙※△▲☆★◇◆□■▽▼§¥〒¢£♀♂↯"),
SymbolNode(catUnicodeSymbols, symbols: "♨☀☁☂☃♠♥♣♦♩♪♫♬☺☻"),
SymbolNode(catCircledKanjis, symbols: "㊟㊞㊚㊛㊊㊋㊌㊍㊎㊏㊐㊑㊒㊓㊔㊕㊖㊗︎㊘㊙︎㊜㊝㊠㊡㊢㊣㊤㊥㊦㊧㊨㊩㊪㊫㊬㊭㊮㊯㊰🈚︎🈯︎"),
SymbolNode(
catCircledKataKana, symbols: "㋐㋑㋒㋓㋔㋕㋖㋗㋘㋙㋚㋛㋜㋝㋞㋟㋠㋡㋢㋣㋤㋥㋦㋧㋨㋩㋪㋫㋬㋭㋮㋯㋰㋱㋲㋳㋴㋵㋶㋷㋸㋹㋺㋻㋼㋾"
),
SymbolNode(catBracketKanjis, symbols: "㈪㈫㈬㈭㈮㈯㈰㈱㈲㈳㈴㈵㈶㈷㈸㈹㈺㈻㈼㈽㈾㈿㉀㉁㉂㉃"),
SymbolNode(catSingleTableLines, symbols: "├─┼┴┬┤┌┐╞═╪╡│▕└┘╭╮╰╯"),
SymbolNode(catDoubleTableLines, symbols: "╔╦╗╠═╬╣╓╥╖╒╤╕║╚╩╝╟╫╢╙╨╜╞╪╡╘╧╛"),
SymbolNode(catFillingBlocks, symbols: "_ˍ▁▂▃▄▅▆▇█▏▎▍▌▋▊▉◢◣◥◤"),
SymbolNode(catLineSegments, symbols: "﹣﹦≡|∣∥–︱—︳╴¯ ̄﹉﹊﹍﹎﹋﹌﹏︴∕﹨╱╲/\"),
]
)
}

View File

@ -29,7 +29,7 @@ import Cocoa
/// mgrLangModel /// mgrLangModel
/// mgrLangModel /// mgrLangModel
/// ///
/// mgrLangModel /// mgrLangModel
private var gLangModelCHS = vChewing.LMInstantiator() private var gLangModelCHS = vChewing.LMInstantiator()
private var gLangModelCHT = vChewing.LMInstantiator() private var gLangModelCHT = vChewing.LMInstantiator()
@ -37,7 +37,7 @@ private var gUserOverrideModelCHS = vChewing.LMUserOverride()
private var gUserOverrideModelCHT = vChewing.LMUserOverride() private var gUserOverrideModelCHT = vChewing.LMUserOverride()
enum mgrLangModel { enum mgrLangModel {
/// fileprivate /// fileprivate
public static var lmCHS: vChewing.LMInstantiator { gLangModelCHS } public static var lmCHS: vChewing.LMInstantiator { gLangModelCHS }
public static var lmCHT: vChewing.LMInstantiator { gLangModelCHT } public static var lmCHT: vChewing.LMInstantiator { gLangModelCHT }
public static var uomCHS: vChewing.LMUserOverride { gUserOverrideModelCHS } public static var uomCHS: vChewing.LMUserOverride { gUserOverrideModelCHS }
@ -295,7 +295,7 @@ enum mgrLangModel {
// The above "&" mutates the "isFolder" value to the real one received by the "folderExist". // The above "&" mutates the "isFolder" value to the real one received by the "folderExist".
// //
// //
var folderPath = folderPath // Convert the incoming constant to a variable. var folderPath = folderPath // Convert the incoming constant to a variable.
if isFolder.boolValue { if isFolder.boolValue {
folderPath?.ensureTrailingSlash() folderPath?.ensureTrailingSlash()
@ -351,7 +351,7 @@ enum mgrLangModel {
return true return true
} }
// MARK: - 使 mgrPrefs // MARK: - 使 mgrPrefs
// mgrPrefs // mgrPrefs

View File

@ -24,40 +24,43 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
extension Megrez { extension Megrez {
/// ///
public class BlockReadingBuilder { public class Compositor {
/// ///
private let kDroppedPathScore: Double = -999 private let kDroppedPathScore: Double = -999
/// ///
private var mutCursorIndex: Int = 0 private var mutCursorIndex: Int = 0
/// ///
private var mutReadings: [String] = [] private var mutReadings: [String] = []
/// ///
private var mutGrid: Grid = .init() private var mutGrid: Grid = .init()
/// 使 /// 使
private var mutLM: LanguageModel private var mutLM: LanguageModel
/// ///
public var maxBuildSpanLength: Int { mutGrid.maxBuildSpanLength } public var maxBuildSpanLength: Int { mutGrid.maxBuildSpanLength }
/// ///
public var joinSeparator: String = "" public var joinSeparator: String = ""
/// ///
public var cursorIndex: Int { public var cursorIndex: Int {
get { mutCursorIndex } get { mutCursorIndex }
set { mutCursorIndex = (newValue < 0) ? 0 : min(newValue, mutReadings.count) } set { mutCursorIndex = (newValue < 0) ? 0 : min(newValue, mutReadings.count) }
} }
/// ///
public var isEmpty: Bool { grid.isEmpty }
///
public var grid: Grid { mutGrid } public var grid: Grid { mutGrid }
/// ///
public var length: Int { mutReadings.count } public var length: Int { mutReadings.count }
/// ///
public var readings: [String] { mutReadings } public var readings: [String] { mutReadings }
/// ///
/// - Parameters: /// - Parameters:
/// - lm: Megrez.LanguageModel /// - lm: Megrez.LanguageModel
/// - length: 10 /// - length: 10
/// - separator: /// - separator:
public init(lm: LanguageModel, length: Int = 10, separator: String = "") { public init(lm: LanguageModel, length: Int = 10, separator: String = "") {
mutLM = lm mutLM = lm
@ -65,7 +68,7 @@ extension Megrez {
joinSeparator = separator joinSeparator = separator
} }
/// ///
public func clear() { public func clear() {
mutCursorIndex = 0 mutCursorIndex = 0
mutReadings.removeAll() mutReadings.removeAll()
@ -109,10 +112,10 @@ extension Megrez {
return true return true
} }
/// ///
/// ///
/// ///
/// ///
@discardableResult public func removeHeadReadings(count: Int) -> Bool { @discardableResult public func removeHeadReadings(count: Int) -> Bool {
let count = abs(count) // let count = abs(count) //
if count > length { if count > length {
@ -179,12 +182,14 @@ extension Megrez {
$0.scoreForSort > $1.scoreForSort $0.scoreForSort > $1.scoreForSort
} }
if let nodeOfNodeZero = nodes[0].node, nodeOfNodeZero.score >= nodeOfNodeZero.kSelectedCandidateScore { if let nodeZero = nodes[0].node, nodeZero.score >= nodeZero.kSelectedCandidateScore {
// 使 // 使
var nodeZero = nodes[0] var anchorZero = nodes[0]
nodeZero.accumulatedScore = accumulatedScore + nodeOfNodeZero.score anchorZero.accumulatedScore = accumulatedScore + nodeZero.score
var path: [NodeAnchor] = reverseWalk(at: location - nodeZero.spanningLength, score: nodeZero.accumulatedScore) var path: [NodeAnchor] = reverseWalk(
path.insert(nodeZero, at: 0) at: location - anchorZero.spanningLength, score: anchorZero.accumulatedScore
)
path.insert(anchorZero, at: 0)
paths.append(path) paths.append(path)
} else if !longPhrases.isEmpty { } else if !longPhrases.isEmpty {
var path = [NodeAnchor]() var path = [NodeAnchor]()
@ -202,17 +207,12 @@ extension Megrez {
continue continue
} }
theAnchor.accumulatedScore = accumulatedScore + theNode.score theAnchor.accumulatedScore = accumulatedScore + theNode.score
if joinedValue.count >= longPhrases[0].count { path = reverseWalk(
path = reverseWalk( at: location - theAnchor.spanningLength,
at: location - theAnchor.spanningLength, score: theAnchor.accumulatedScore, joinedPhrase: "", score: theAnchor.accumulatedScore,
longPhrases: .init() joinedPhrase: (joinedValue.count >= longPhrases[0].count) ? "" : joinedValue,
) longPhrases: .init()
} else { )
path = reverseWalk(
at: location - theAnchor.spanningLength, score: theAnchor.accumulatedScore, joinedPhrase: joinedValue,
longPhrases: longPhrases
)
}
path.insert(theAnchor, at: 0) path.insert(theAnchor, at: 0)
paths.append(path) paths.append(path)
} }
@ -234,17 +234,11 @@ extension Megrez {
guard let theNode = theAnchor.node else { continue } guard let theNode = theAnchor.node else { continue }
theAnchor.accumulatedScore = accumulatedScore + theNode.score theAnchor.accumulatedScore = accumulatedScore + theNode.score
var path = [NodeAnchor]() var path = [NodeAnchor]()
if theAnchor.spanningLength > 1 { path = reverseWalk(
path = reverseWalk( at: location - theAnchor.spanningLength, score: theAnchor.accumulatedScore,
at: location - theAnchor.spanningLength, score: theAnchor.accumulatedScore, joinedPhrase: "", joinedPhrase: (theAnchor.spanningLength > 1) ? "" : theNode.currentKeyValue.value,
longPhrases: .init() longPhrases: .init()
) )
} else {
path = reverseWalk(
at: location - theAnchor.spanningLength, score: theAnchor.accumulatedScore,
joinedPhrase: theNode.currentKeyValue.value, longPhrases: longPhrases
)
}
path.insert(theAnchor, at: 0) path.insert(theAnchor, at: 0)
paths.append(path) paths.append(path)
} }

View File

@ -38,6 +38,9 @@ extension Megrez {
/// ///
var width: Int { mutSpans.count } var width: Int { mutSpans.count }
///
var isEmpty: Bool { mutSpans.isEmpty }
public init(spanLength: Int = 10) { public init(spanLength: Int = 10) {
mutMaxBuildSpanLength = spanLength mutMaxBuildSpanLength = spanLength
mutSpans = [Megrez.Span]() mutSpans = [Megrez.Span]()
@ -256,9 +259,9 @@ extension Megrez.Grid {
strOutput += "\(np.currentKeyValue.value);\n" strOutput += "\(np.currentKeyValue.value);\n"
if (p + ni) < mutSpans.count { if (p + ni) < mutSpans.count {
let destinatedSpan = mutSpans[p + ni] let destinationSpan = mutSpans[p + ni]
for q in 0...(destinatedSpan.maximumLength) { for q in 0...(destinationSpan.maximumLength) {
if let dn = destinatedSpan.node(length: q) { if let dn = destinationSpan.node(length: q) {
strOutput += np.currentKeyValue.value + " -> " + dn.currentKeyValue.value + ";\n" strOutput += np.currentKeyValue.value + " -> " + dn.currentKeyValue.value + ";\n"
} }
} }

View File

@ -49,9 +49,7 @@ extension Megrez {
mutating func insert(node: Node, length: Int) { mutating func insert(node: Node, length: Int) {
let length = abs(length) // let length = abs(length) //
mutLengthNodeMap[length] = node mutLengthNodeMap[length] = node
if length > mutMaximumLength { mutMaximumLength = max(mutMaximumLength, length)
mutMaximumLength = length
}
} }
/// ///
@ -60,21 +58,19 @@ extension Megrez {
mutating func removeNodeOfLengthGreaterThan(_ length: Int) { mutating func removeNodeOfLengthGreaterThan(_ length: Int) {
let length = abs(length) // let length = abs(length) //
if length > mutMaximumLength { return } if length > mutMaximumLength { return }
var max = 0 var lenMax = 0
var removalList: [Int: Megrez.Node] = [:] var removalList: [Int: Megrez.Node] = [:]
for key in mutLengthNodeMap.keys { for key in mutLengthNodeMap.keys {
if key > length { if key > length {
removalList[key] = mutLengthNodeMap[key] removalList[key] = mutLengthNodeMap[key]
} else { } else {
if key > max { lenMax = max(lenMax, key)
max = key
}
} }
} }
for key in removalList.keys { for key in removalList.keys {
mutLengthNodeMap.removeValue(forKey: key) mutLengthNodeMap.removeValue(forKey: key)
} }
mutMaximumLength = max mutMaximumLength = lenMax
} }
/// ///

View File

@ -25,7 +25,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
extension Megrez { extension Megrez {
/// ///
public class Node: CustomStringConvertible { public class Node {
/// ///
private let mutLM: LanguageModel = .init() private let mutLM: LanguageModel = .init()
/// ///
@ -109,23 +109,16 @@ extension Megrez {
for neta in precedingKeyValues { for neta in precedingKeyValues {
let bigrams = mutPrecedingBigramMap[neta] ?? [] let bigrams = mutPrecedingBigramMap[neta] ?? []
for bigram in bigrams { for bigram in bigrams {
if bigram.score > max { guard bigram.score > max else { continue }
if let valRetrieved = mutValueUnigramIndexMap[bigram.keyValue.value] { if let valRetrieved = mutValueUnigramIndexMap[bigram.keyValue.value] {
newIndex = valRetrieved as Int newIndex = valRetrieved as Int
max = bigram.score max = bigram.score
}
} }
} }
} }
} }
mutScore = max
if mutScore != max { mutSelectedUnigramIndex = newIndex
mutScore = max
}
if mutSelectedUnigramIndex != newIndex {
mutSelectedUnigramIndex = newIndex
}
} }
/// ///
@ -170,13 +163,5 @@ extension Megrez {
} }
return 0.0 return 0.0
} }
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

@ -24,11 +24,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
extension Megrez { extension Megrez {
/// 使 /// 使
open class LanguageModel { open class LanguageModel {
public init() {} public init() {}
// Swift // Swift
/// ///
open func unigramsFor(key: String) -> [Megrez.Unigram] { open func unigramsFor(key: String) -> [Megrez.Unigram] {

View File

@ -49,7 +49,7 @@ extension Megrez {
hasher.combine(score) hasher.combine(score)
} }
// //
public static func compareScore(a: Unigram, b: Unigram) -> Bool { public static func compareScore(a: Unigram, b: Unigram) -> Bool {
a.score > b.score a.score > b.score
} }

View File

@ -178,7 +178,7 @@ private class vwrCandidateUniversal: NSView {
if index == highlightedIndex { if index == highlightedIndex {
let colorBlendAmount: CGFloat = IME.isDarkMode() ? 0.25 : 0 let colorBlendAmount: CGFloat = IME.isDarkMode() ? 0.25 : 0
// The background color of the highlightened candidate // The background color of the highlightened candidate
switch ctlInputMethod.currentKeyHandler.inputMode { switch IME.currentInputMode {
case InputMode.imeModeCHS: case InputMode.imeModeCHS:
NSColor.systemRed.blended( NSColor.systemRed.blended(
withFraction: colorBlendAmount, withFraction: colorBlendAmount,
@ -202,7 +202,7 @@ private class vwrCandidateUniversal: NSView {
} else { } else {
NSColor.controlBackgroundColor.setFill() NSColor.controlBackgroundColor.setFill()
} }
switch ctlInputMethod.currentKeyHandler.inputMode { switch IME.currentInputMode {
case InputMode.imeModeCHS: case InputMode.imeModeCHS:
if #available(macOS 12.0, *) { if #available(macOS 12.0, *) {
activeCandidateAttr[.languageIdentifier] = "zh-Hans" as AnyObject activeCandidateAttr[.languageIdentifier] = "zh-Hans" as AnyObject
@ -248,7 +248,7 @@ private class vwrCandidateUniversal: NSView {
if index == highlightedIndex { if index == highlightedIndex {
let colorBlendAmount: CGFloat = IME.isDarkMode() ? 0.25 : 0 let colorBlendAmount: CGFloat = IME.isDarkMode() ? 0.25 : 0
// The background color of the highlightened candidate // The background color of the highlightened candidate
switch ctlInputMethod.currentKeyHandler.inputMode { switch IME.currentInputMode {
case InputMode.imeModeCHS: case InputMode.imeModeCHS:
NSColor.systemRed.blended( NSColor.systemRed.blended(
withFraction: colorBlendAmount, withFraction: colorBlendAmount,
@ -272,7 +272,7 @@ private class vwrCandidateUniversal: NSView {
} else { } else {
NSColor.controlBackgroundColor.setFill() NSColor.controlBackgroundColor.setFill()
} }
switch ctlInputMethod.currentKeyHandler.inputMode { switch IME.currentInputMode {
case InputMode.imeModeCHS: case InputMode.imeModeCHS:
if #available(macOS 12.0, *) { if #available(macOS 12.0, *) {
activeCandidateAttr[.languageIdentifier] = "zh-Hans" as AnyObject activeCandidateAttr[.languageIdentifier] = "zh-Hans" as AnyObject

View File

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

View File

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

View File

@ -17,15 +17,15 @@
5B38F59D281E2E49007D5F5D /* 4_Node.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1A15FC0EB100ABF4B3 /* 4_Node.swift */; }; 5B38F59D281E2E49007D5F5D /* 4_Node.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1A15FC0EB100ABF4B3 /* 4_Node.swift */; };
5B38F59E281E2E49007D5F5D /* 6_Bigram.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1415FC0EB100ABF4B3 /* 6_Bigram.swift */; }; 5B38F59E281E2E49007D5F5D /* 6_Bigram.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1415FC0EB100ABF4B3 /* 6_Bigram.swift */; };
5B38F59F281E2E49007D5F5D /* 3_NodeAnchor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1B15FC0EB100ABF4B3 /* 3_NodeAnchor.swift */; }; 5B38F59F281E2E49007D5F5D /* 3_NodeAnchor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1B15FC0EB100ABF4B3 /* 3_NodeAnchor.swift */; };
5B38F5A1281E2E49007D5F5D /* 1_BlockReadingBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1515FC0EB100ABF4B3 /* 1_BlockReadingBuilder.swift */; }; 5B38F5A1281E2E49007D5F5D /* 1_Compositor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1515FC0EB100ABF4B3 /* 1_Compositor.swift */; };
5B38F5A2281E2E49007D5F5D /* 0_Megrez.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1615FC0EB100ABF4B3 /* 0_Megrez.swift */; }; 5B38F5A2281E2E49007D5F5D /* 0_Megrez.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1615FC0EB100ABF4B3 /* 0_Megrez.swift */; };
5B38F5A3281E2E49007D5F5D /* 3_Span.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1C15FC0EB100ABF4B3 /* 3_Span.swift */; }; 5B38F5A3281E2E49007D5F5D /* 3_Span.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1C15FC0EB100ABF4B3 /* 3_Span.swift */; };
5B38F5A4281E2E49007D5F5D /* 5_LanguageModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1915FC0EB100ABF4B3 /* 5_LanguageModel.swift */; }; 5B38F5A4281E2E49007D5F5D /* 5_LanguageModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1915FC0EB100ABF4B3 /* 5_LanguageModel.swift */; };
5B3A87BC28597CDB0090E163 /* SymbolNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B3A87BB28597CDB0090E163 /* SymbolNode.swift */; };
5B40730C281672610023DFFF /* lmAssociates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B407309281672610023DFFF /* lmAssociates.swift */; }; 5B40730C281672610023DFFF /* lmAssociates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B407309281672610023DFFF /* lmAssociates.swift */; };
5B40730D281672610023DFFF /* lmReplacements.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B40730A281672610023DFFF /* lmReplacements.swift */; }; 5B40730D281672610023DFFF /* lmReplacements.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B40730A281672610023DFFF /* lmReplacements.swift */; };
5B54E743283A7D89001ECBDC /* lmCoreNS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B54E742283A7D89001ECBDC /* lmCoreNS.swift */; }; 5B54E743283A7D89001ECBDC /* lmCoreNS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B54E742283A7D89001ECBDC /* lmCoreNS.swift */; };
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 */; };
5B62A32927AE77D100A19448 /* FSEventStreamHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A32827AE77D100A19448 /* FSEventStreamHelper.swift */; }; 5B62A32927AE77D100A19448 /* FSEventStreamHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A32827AE77D100A19448 /* FSEventStreamHelper.swift */; };
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 */; };
@ -111,7 +111,7 @@
D47B92C027972AD100458394 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47B92BF27972AC800458394 /* main.swift */; }; D47B92C027972AD100458394 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47B92BF27972AC800458394 /* main.swift */; };
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 */; };
D4A13D5A27A59F0B003BE359 /* ctlInputMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4A13D5927A59D5C003BE359 /* ctlInputMethod.swift */; }; D4A13D5A27A59F0B003BE359 /* ctlInputMethod_Core.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4A13D5927A59D5C003BE359 /* ctlInputMethod_Core.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 /* CTools.m in Sources */ = {isa = PBXBuildFile; fileRef = D4E569DB27A34CC100AC2CEF /* CTools.m */; }; D4E569DC27A34D0E00AC2CEF /* CTools.m in Sources */ = {isa = PBXBuildFile; fileRef = D4E569DB27A34CC100AC2CEF /* CTools.m */; };
@ -187,11 +187,11 @@
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; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = KeyHandler_States.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; }; 5B3133BE280B229700A4A505 /* KeyHandler_States.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = KeyHandler_States.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5B3A87BB28597CDB0090E163 /* SymbolNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SymbolNode.swift; sourceTree = "<group>"; };
5B407309281672610023DFFF /* lmAssociates.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; lineEnding = 0; path = lmAssociates.swift; sourceTree = "<group>"; usesTabs = 0; }; 5B407309281672610023DFFF /* lmAssociates.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; lineEnding = 0; path = lmAssociates.swift; sourceTree = "<group>"; usesTabs = 0; };
5B40730A281672610023DFFF /* lmReplacements.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; lineEnding = 0; path = lmReplacements.swift; sourceTree = "<group>"; usesTabs = 0; }; 5B40730A281672610023DFFF /* lmReplacements.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; lineEnding = 0; path = lmReplacements.swift; sourceTree = "<group>"; usesTabs = 0; };
5B54E742283A7D89001ECBDC /* lmCoreNS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = lmCoreNS.swift; sourceTree = "<group>"; }; 5B54E742283A7D89001ECBDC /* lmCoreNS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = lmCoreNS.swift; sourceTree = "<group>"; };
5B5E535127EF261400C6AA1E /* IME.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = IME.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; }; 5B5E535127EF261400C6AA1E /* IME.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = IME.swift; sourceTree = "<group>"; tabWidth = 2; 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; };
5B62A32827AE77D100A19448 /* FSEventStreamHelper.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = FSEventStreamHelper.swift; sourceTree = "<group>"; tabWidth = 2; 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; };
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; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = InputSourceHelper.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
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; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = mgrPrefs.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
@ -284,7 +284,7 @@
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 /* 6_Bigram.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 6_Bigram.swift; sourceTree = "<group>"; tabWidth = 2; 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 /* 1_BlockReadingBuilder.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 1_BlockReadingBuilder.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; }; 6A0D4F1515FC0EB100ABF4B3 /* 1_Compositor.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 1_Compositor.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
6A0D4F1615FC0EB100ABF4B3 /* 0_Megrez.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = 0_Megrez.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; }; 6A0D4F1615FC0EB100ABF4B3 /* 0_Megrez.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = 0_Megrez.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
6A0D4F1715FC0EB100ABF4B3 /* 2_Grid.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 2_Grid.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; }; 6A0D4F1715FC0EB100ABF4B3 /* 2_Grid.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 2_Grid.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
6A0D4F1815FC0EB100ABF4B3 /* 7_KeyValuePair.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 7_KeyValuePair.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; }; 6A0D4F1815FC0EB100ABF4B3 /* 7_KeyValuePair.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 7_KeyValuePair.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
@ -310,7 +310,7 @@
D47B92BF27972AC800458394 /* main.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = main.swift; sourceTree = "<group>"; tabWidth = 2; 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; };
D47F7DCD278BFB57002F9DD7 /* ctlPrefWindow.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlPrefWindow.swift; sourceTree = "<group>"; tabWidth = 2; 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; };
D47F7DCF278C0897002F9DD7 /* ctlNonModalAlertWindow.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlNonModalAlertWindow.swift; sourceTree = "<group>"; tabWidth = 2; 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; };
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_Core.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlInputMethod_Core.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
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 /* CTools.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = CTools.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; };
@ -405,6 +405,8 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
5B11328827B94CFB00E58451 /* AppleKeyboardConverter.swift */, 5B11328827B94CFB00E58451 /* AppleKeyboardConverter.swift */,
D4A13D5927A59D5C003BE359 /* ctlInputMethod_Core.swift */,
5BB802D927FABA8300CF1C19 /* ctlInputMethod_Menu.swift */,
D4E569DA27A34CC100AC2CEF /* CTools.h */, D4E569DA27A34CC100AC2CEF /* CTools.h */,
D4E569DB27A34CC100AC2CEF /* CTools.m */, D4E569DB27A34CC100AC2CEF /* CTools.m */,
D456576D279E4F7B00DF6BC9 /* InputSignal.swift */, D456576D279E4F7B00DF6BC9 /* InputSignal.swift */,
@ -412,7 +414,6 @@
5BD0113C2818543900609769 /* KeyHandler_Core.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 */,
5B3133BE280B229700A4A505 /* KeyHandler_States.swift */, 5B3133BE280B229700A4A505 /* KeyHandler_States.swift */,
5B62A33727AE79CD00A19448 /* StringUtils.swift */, 5B62A33727AE79CD00A19448 /* StringUtils.swift */,
5BAA8FBD282CAF380066C406 /* SyllableComposer.swift */, 5BAA8FBD282CAF380066C406 /* SyllableComposer.swift */,
@ -442,8 +443,6 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
5BDC1CF927FDF1310052C2B9 /* apiUpdate.swift */, 5BDC1CF927FDF1310052C2B9 /* apiUpdate.swift */,
5BB802D927FABA8300CF1C19 /* ctlInputMethod_Menu.swift */,
D4A13D5927A59D5C003BE359 /* ctlInputMethod.swift */,
5B5E535127EF261400C6AA1E /* IME.swift */, 5B5E535127EF261400C6AA1E /* IME.swift */,
5B62A33127AE792F00A19448 /* InputSourceHelper.swift */, 5B62A33127AE792F00A19448 /* InputSourceHelper.swift */,
5B62A33527AE795800A19448 /* mgrPrefs.swift */, 5B62A33527AE795800A19448 /* mgrPrefs.swift */,
@ -466,6 +465,7 @@
5B949BDA2816DDBC00D87B5D /* LMConsolidator.swift */, 5B949BDA2816DDBC00D87B5D /* LMConsolidator.swift */,
5BD0113A28180D6100609769 /* LMInstantiator.swift */, 5BD0113A28180D6100609769 /* LMInstantiator.swift */,
5BAEFACF28012565001F42C9 /* mgrLangModel.swift */, 5BAEFACF28012565001F42C9 /* mgrLangModel.swift */,
5B3A87BB28597CDB0090E163 /* SymbolNode.swift */,
); );
path = LangModelRelated; path = LangModelRelated;
sourceTree = "<group>"; sourceTree = "<group>";
@ -761,7 +761,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
6A0D4F1615FC0EB100ABF4B3 /* 0_Megrez.swift */, 6A0D4F1615FC0EB100ABF4B3 /* 0_Megrez.swift */,
6A0D4F1515FC0EB100ABF4B3 /* 1_BlockReadingBuilder.swift */, 6A0D4F1515FC0EB100ABF4B3 /* 1_Compositor.swift */,
6A0D4F1715FC0EB100ABF4B3 /* 2_Grid.swift */, 6A0D4F1715FC0EB100ABF4B3 /* 2_Grid.swift */,
6A0D4F1B15FC0EB100ABF4B3 /* 3_NodeAnchor.swift */, 6A0D4F1B15FC0EB100ABF4B3 /* 3_NodeAnchor.swift */,
6A0D4F1C15FC0EB100ABF4B3 /* 3_Span.swift */, 6A0D4F1C15FC0EB100ABF4B3 /* 3_Span.swift */,
@ -1059,7 +1059,7 @@
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 */,
D4A13D5A27A59F0B003BE359 /* ctlInputMethod.swift in Sources */, D4A13D5A27A59F0B003BE359 /* ctlInputMethod_Core.swift in Sources */,
5BA9FD4827FEF3C9002DE248 /* PreferencesWindowController.swift in Sources */, 5BA9FD4827FEF3C9002DE248 /* PreferencesWindowController.swift in Sources */,
5BD0113B28180D6100609769 /* LMInstantiator.swift in Sources */, 5BD0113B28180D6100609769 /* LMInstantiator.swift in Sources */,
D4E569DC27A34D0E00AC2CEF /* CTools.m in Sources */, D4E569DC27A34D0E00AC2CEF /* CTools.m in Sources */,
@ -1071,6 +1071,7 @@
D456576E279E4F7B00DF6BC9 /* InputSignal.swift in Sources */, D456576E279E4F7B00DF6BC9 /* InputSignal.swift in Sources */,
5BA9FD1027FEDB6B002DE248 /* suiPrefPaneKeyboard.swift in Sources */, 5BA9FD1027FEDB6B002DE248 /* suiPrefPaneKeyboard.swift in Sources */,
5B3133BF280B229700A4A505 /* KeyHandler_States.swift in Sources */, 5B3133BF280B229700A4A505 /* KeyHandler_States.swift in Sources */,
5B3A87BC28597CDB0090E163 /* SymbolNode.swift in Sources */,
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 */,
@ -1100,7 +1101,6 @@
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 */,
5B61B0CA280BEFD4002E3CFA /* KeyHandler_Misc.swift in Sources */,
5B38F59A281E2E49007D5F5D /* 6_Unigram.swift in Sources */, 5B38F59A281E2E49007D5F5D /* 6_Unigram.swift in Sources */,
5BA9FD4027FEF3C8002DE248 /* Localization.swift in Sources */, 5BA9FD4027FEF3C8002DE248 /* Localization.swift in Sources */,
5BAA8FBE282CAF380066C406 /* SyllableComposer.swift in Sources */, 5BAA8FBE282CAF380066C406 /* SyllableComposer.swift in Sources */,
@ -1113,7 +1113,7 @@
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 */,
5B38F5A1281E2E49007D5F5D /* 1_BlockReadingBuilder.swift in Sources */, 5B38F5A1281E2E49007D5F5D /* 1_Compositor.swift in Sources */,
5BDC1CFA27FDF1310052C2B9 /* apiUpdate.swift in Sources */, 5BDC1CFA27FDF1310052C2B9 /* apiUpdate.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
@ -1304,7 +1304,7 @@
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1970; CURRENT_PROJECT_VERSION = 1971;
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = dwarf; DEBUG_INFORMATION_FORMAT = dwarf;
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
@ -1328,7 +1328,7 @@
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 10.11.5; MACOSX_DEPLOYMENT_TARGET = 10.11.5;
MARKETING_VERSION = 1.7.0; MARKETING_VERSION = 1.7.1;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewing.vChewingPhraseEditor; PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewing.vChewingPhraseEditor;
@ -1361,7 +1361,7 @@
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1970; CURRENT_PROJECT_VERSION = 1971;
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO; ENABLE_NS_ASSERTIONS = NO;
@ -1381,7 +1381,7 @@
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 10.11.5; MACOSX_DEPLOYMENT_TARGET = 10.11.5;
MARKETING_VERSION = 1.7.0; MARKETING_VERSION = 1.7.1;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewing.vChewingPhraseEditor; PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewing.vChewingPhraseEditor;
@ -1499,7 +1499,7 @@
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1970; CURRENT_PROJECT_VERSION = 1971;
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = ""; DEVELOPMENT_TEAM = "";
@ -1535,7 +1535,7 @@
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 10.11.5; MACOSX_DEPLOYMENT_TARGET = 10.11.5;
MARKETING_VERSION = 1.7.0; MARKETING_VERSION = 1.7.1;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.inputmethod.vChewing; PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.inputmethod.vChewing;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
@ -1567,7 +1567,7 @@
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1970; CURRENT_PROJECT_VERSION = 1971;
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_ASSET_PATHS = "";
@ -1598,7 +1598,7 @@
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 10.11.5; MACOSX_DEPLOYMENT_TARGET = 10.11.5;
MARKETING_VERSION = 1.7.0; MARKETING_VERSION = 1.7.1;
PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.inputmethod.vChewing; PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.inputmethod.vChewing;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
@ -1625,7 +1625,7 @@
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1970; CURRENT_PROJECT_VERSION = 1971;
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = ""; DEVELOPMENT_TEAM = "";
GCC_C_LANGUAGE_STANDARD = gnu99; GCC_C_LANGUAGE_STANDARD = gnu99;
@ -1651,7 +1651,7 @@
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 10.11.5; MACOSX_DEPLOYMENT_TARGET = 10.11.5;
MARKETING_VERSION = 1.7.0; MARKETING_VERSION = 1.7.1;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "org.atelierInmu.vChewing.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_BUNDLE_IDENTIFIER = "org.atelierInmu.vChewing.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
@ -1678,7 +1678,7 @@
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1970; CURRENT_PROJECT_VERSION = 1971;
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = ""; DEVELOPMENT_TEAM = "";
@ -1699,7 +1699,7 @@
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 10.11.5; MACOSX_DEPLOYMENT_TARGET = 10.11.5;
MARKETING_VERSION = 1.7.0; MARKETING_VERSION = 1.7.1;
PRODUCT_BUNDLE_IDENTIFIER = "org.atelierInmu.vChewing.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_BUNDLE_IDENTIFIER = "org.atelierInmu.vChewing.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";