1.7.1 // Maintenance. Merge PR!48 from upd/1.7.1

This commit is contained in:
ShikiSuen 2022-06-18 07:56:15 +00:00 committed by Gitee
commit cd8bcbd0ea
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
extension Float {
@ -67,7 +67,7 @@ extension Float {
}
}
// MARK: -
// MARK: -
// Ref: https://stackoverflow.com/a/41581695/4162914
precedencegroup ExponentiationPrecedence {

View File

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

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

View File

@ -28,39 +28,40 @@ import Cocoa
// 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
/// 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.
/// Finite State Machine/
/// 使
/// 使
///
///
/// The InputState class is for representing what the input controller is doing,
/// 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.
/// InputState
/// 使
///
///
///
/// They are immutable objects. When the state changes, the controller should
/// create a new state object to replace the current state instead of modifying
/// the existing one.
/// InputState
/// InputState.Marking InputState.Inputting
///
///
/// The input controller has following possible states:
///
///
/// - Deactivated: The user is not using the input method yet.
/// - Empty: The user has switched to this input method but inputted nothing yet,
/// or, he or she has committed text into the client apps and starts a new
/// input phase.
/// - Committing: The input controller is sending text to the client apps.
/// - Inputting: The user has inputted something and the input buffer is
/// visible.
/// - Marking: The user is creating a area in the input buffer and about to
/// create a new user phrase.
/// - Choosing Candidate: The candidate window is open to let the user to choose
/// one among the candidates.
/// - .Deactivated: 使使
/// - .AssociatedPhrases:
/// 西 .NotEmpty
/// - .Empty: 使
///
/// - .EmptyIgnorePreviousState: Empty
///
/// - .Committing:
/// - .NotEmpty:
/// - .Inputting: 使Compositor
/// - .Marking: 使
///
/// - .ChoosingCandidate: 使
/// - .SymbolTable:
class InputState {
/// Represents that the input controller is deactivated.
/// .Deactivated: 使使
class Deactivated: InputState {
var description: String {
"<InputState.Deactivated>"
@ -69,7 +70,8 @@ class InputState {
// MARK: -
/// Represents that the composing buffer is empty.
/// .Empty: 使
///
class Empty: InputState {
var composingBuffer: String {
""
@ -82,7 +84,8 @@ class InputState {
// MARK: -
/// Represents that the composing buffer is empty.
/// .EmptyIgnorePreviousState: Empty
///
class EmptyIgnoringPreviousState: Empty {
override var description: String {
"<InputState.EmptyIgnoringPreviousState>"
@ -91,30 +94,76 @@ class InputState {
// MARK: -
/// Represents that the input controller is committing text into client app.
/// .Committing:
class Committing: InputState {
private(set) var poppedText: String = ""
private(set) var textToCommit: String = ""
convenience init(poppedText: String) {
convenience init(textToCommit: String) {
self.init()
self.poppedText = poppedText
self.textToCommit = textToCommit
}
var description: String {
"<InputState.Committing poppedText:\(poppedText)>"
"<InputState.Committing textToCommit:\(textToCommit)>"
}
}
// 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 {
private(set) var composingBuffer: String
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) {
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 {
@ -124,34 +173,24 @@ class InputState {
// MARK: -
/// Represents that the user is inputting text.
/// .Inputting: 使Compositor
class Inputting: NotEmpty {
var poppedText: String = ""
var textToCommit: String = ""
var tooltip: String = ""
override init(composingBuffer: String, cursorIndex: Int) {
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 {
"<InputState.Inputting, composingBuffer:\(composingBuffer), cursorIndex:\(cursorIndex)>, poppedText:\(poppedText)>"
"<InputState.Inputting, composingBuffer:\(composingBuffer), cursorIndex:\(cursorIndex)>, textToCommit:\(textToCommit)>"
}
}
// MARK: -
/// Represents that the user is marking a range in the composing buffer.
/// .Marking: 使
///
class Marking: NotEmpty {
private var allowedMarkRange: ClosedRange<Int> = mgrPrefs.minCandidateLength...mgrPrefs.maxCandidateLength
private(set) var markerIndex: Int = 0 { didSet { markerIndex = max(markerIndex, 0) } }
@ -201,7 +240,7 @@ class InputState {
let selectedReadings = readings[literalMarkedRange]
let joined = selectedReadings.joined(separator: "-")
let exist = mgrLangModel.checkIfUserPhraseExist(
userPhrase: text, mode: ctlInputMethod.currentKeyHandler.inputMode, key: joined
userPhrase: text, mode: IME.currentInputMode, key: joined
)
if exist {
deleteTargetExists = exist
@ -223,16 +262,18 @@ class InputState {
private(set) var readings: [String]
init(composingBuffer: String, cursorIndex: Int, markerIndex: Int, readings: [String]) {
self.markerIndex = markerIndex
let begin = min(cursorIndex, markerIndex)
let end = max(cursorIndex, markerIndex)
markedRange = begin..<end
self.readings = readings
super.init(composingBuffer: composingBuffer, cursorIndex: cursorIndex)
defer { self.markerIndex = markerIndex }
}
var attributedString: NSAttributedString {
let attributedString = NSMutableAttributedString(string: composingBuffer)
override var attributedString: NSAttributedString {
///
/// JIS
let attributedString = NSMutableAttributedString(string: composingBufferConverted)
let end = markedRange.upperBound
attributedString.setAttributes(
@ -292,7 +333,7 @@ class InputState {
let selectedReadings = readings[literalMarkedRange]
let joined = selectedReadings.joined(separator: "-")
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: -
/// Represents that the user is choosing in a candidates list.
/// .ChoosingCandidate: 使
class ChoosingCandidate: NotEmpty {
private(set) var candidates: [String]
private(set) var isTypingVertical: Bool
@ -326,17 +367,6 @@ class InputState {
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 {
"<InputState.ChoosingCandidate, candidates:\(candidates), isTypingVertical:\(isTypingVertical), composingBuffer:\(composingBuffer), cursorIndex:\(cursorIndex)>"
}
@ -344,22 +374,7 @@ class InputState {
// MARK: -
/// Represents that the user is choosing in a candidates list
/// 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)>"
}
}
/// .SymbolTable:
class SymbolTable: ChoosingCandidate {
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.
*/
/// 調
/// Megrez Tekkon
/// composer compositor
import Cocoa
public enum InputMode: String {
case imeModeCHS = "org.atelierInmu.inputmethod.vChewing.IMECHS"
case imeModeCHT = "org.atelierInmu.inputmethod.vChewing.IMECHT"
case imeModeNULL = ""
}
// MARK: - Delegate.
// MARK: - (Delegate).
/// KeyHandler
protocol KeyHandlerDelegate {
func ctlCandidate() -> ctlCandidate
func keyHandler(
@ -44,80 +43,81 @@ protocol KeyHandlerDelegate {
-> Bool
}
// MARK: - Kernel.
// MARK: - (Kernel).
/// KeyHandler 調
class KeyHandler {
///
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 inputMode: InputMode {
get {
switch _inputMode {
case "org.atelierInmu.inputmethod.vChewing.IMECHS":
return InputMode.imeModeCHS
case "org.atelierInmu.inputmethod.vChewing.IMECHT":
return InputMode.imeModeCHT
default:
return InputMode.imeModeNULL
}
}
set {
/// InputMode
/// IME UserPrefs
var inputMode: InputMode = IME.currentInputMode {
willSet {
//
let isCHS: Bool = (newValue == InputMode.imeModeCHS)
// ctlInputMethod:
ctlInputMethod.currentInputMode = isCHS ? InputMode.imeModeCHS.rawValue : InputMode.imeModeCHT.rawValue
mgrPrefs.mostRecentInputMode = ctlInputMethod.currentInputMode
// _inputMode ctlInputMethod
if _inputMode != ctlInputMethod.currentInputMode {
// Reinitiate language models if necessary
_languageModel = isCHS ? mgrLangModel.lmCHS : mgrLangModel.lmCHT
_userOverrideModel = isCHS ? mgrLangModel.uomCHS : mgrLangModel.uomCHT
// Synchronize the sub-languageModel state settings to the new LM.
syncBaseLMPrefs()
// Create new grid builder and clear the composer.
createNewBuilder()
_composer.clear()
}
//
_inputMode = ctlInputMethod.currentInputMode
/// ctlInputMethod IME
IME.currentInputMode = newValue
mgrPrefs.mostRecentInputMode = IME.currentInputMode.rawValue
///
currentLM = isCHS ? mgrLangModel.lmCHS : mgrLangModel.lmCHT
currentUOM = isCHS ? mgrLangModel.uomCHS : mgrLangModel.uomCHT
///
syncBaseLMPrefs()
///
///
ensureCompositor()
ensureParser()
}
}
///
public init() {
_builder = Megrez.BlockReadingBuilder(lm: _languageModel, separator: "-")
/// ensureCompositor()
compositor = Megrez.Compositor(lm: currentLM, separator: "-")
///
ensureParser()
inputMode = InputMode(rawValue: ctlInputMethod.currentInputMode) ?? InputMode.imeModeNULL
/// inputMode
/// defer willSet
defer { inputMode = IME.currentInputMode }
}
func clear() {
_composer.clear()
_builder.clear()
_walkedNodes.removeAll()
composer.clear()
compositor.clear()
walkedAnchors.removeAll()
}
// MARK: - Functions dealing with Megrez.
func walk() {
// Retrieve the most likely grid, i.e. a Maximum Likelihood Estimation
// of the best possible Mandarin characters given the input syllables,
// using the Viterbi algorithm implemented in the Megrez library.
// The walk() traces the grid to the end.
_walkedNodes = _builder.walk()
/// Megrez 使便
///
/// 使 Node Crossing
var actualCandidateCursorIndex: Int {
mgrPrefs.useRearCursorMode ? min(compositorCursorIndex, compositorLength - 1) : max(compositorCursorIndex, 1)
}
// if DEBUG mode is enabled, a GraphViz file is written to kGraphVizOutputfile.
///
///
/// Viterbi
///
///
func walk() {
walkedAnchors = compositor.walk()
// GraphViz
if mgrPrefs.isDebugModeEnabled {
let result = _builder.grid.dumpDOT
let result = compositor.grid.dumpDOT
do {
try result.write(
toFile: "/private/var/tmp/vChewing-visualization.dot",
@ -129,49 +129,54 @@ class KeyHandler {
}
}
///
///
/// Viterbi 使 O(N^2)
/// 使
/// 使
///
var popOverflowComposingTextAndWalk: String {
// In ideal situations we can allow users to type infinitely in a buffer.
// However, Viberti algorithm has a complexity of O(N^2), the walk will
// become slower as the number of nodes increase. Therefore, we need to
// auto-commit overflown texts which usually lose their influence over
// the whole MLE anyway -- so that when the user type along, the already
// composed text in the rear side of the buffer will be committed out.
// (i.e. popped out.)
var poppedText = ""
if _builder.grid.width > mgrPrefs.composingBufferSize {
if !_walkedNodes.isEmpty {
let anchor: Megrez.NodeAnchor = _walkedNodes[0]
var textToCommit = ""
if compositor.grid.width > mgrPrefs.composingBufferSize {
if !walkedAnchors.isEmpty {
let anchor: Megrez.NodeAnchor = walkedAnchors[0]
if let theNode = anchor.node {
poppedText = theNode.currentKeyValue.value
textToCommit = theNode.currentKeyValue.value
}
_builder.removeHeadReadings(count: anchor.spanningLength)
compositor.removeHeadReadings(count: anchor.spanningLength)
}
}
walk()
return poppedText
return textToCommit
}
///
/// - Parameter key:
/// - Returns:
/// nil
func buildAssociatePhraseArray(withKey key: String) -> [String] {
var arrResult: [String] = []
if _languageModel.hasAssociatedPhrasesForKey(key) {
arrResult.append(contentsOf: _languageModel.associatedPhrasesForKey(key))
if currentLM.hasAssociatedPhrasesForKey(key) {
arrResult.append(contentsOf: currentLM.associatedPhrasesForKey(key))
}
return arrResult
}
///
///
/// - Parameters:
/// - value:
/// - respectCursorPushing: true
func fixNode(value: String, respectCursorPushing: Bool = true) {
let cursorIndex = min(actualCandidateCursorIndex + (mgrPrefs.useRearCursorMode ? 1 : 0), builderLength)
_builder.grid.fixNodeSelectedCandidate(location: cursorIndex, value: value)
let cursorIndex = min(actualCandidateCursorIndex + (mgrPrefs.useRearCursorMode ? 1 : 0), compositorLength)
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
// )
// //
// if !mgrPrefs.useSCPCTypingMode {
// // If the length of the readings and the characters do not match,
// // it often means it is a special symbol and it should not be stored
// // in the user override model.
// //
// var addToUserOverrideModel = true
// if selectedNode.spanningLength != value.count {
// IME.prtDebugIntel("UOM: SpanningLength != value.count, dismissing.")
@ -179,7 +184,7 @@ class KeyHandler {
// }
// if addToUserOverrideModel {
// if let theNode = selectedNode.node {
// // SymbolLM Score -12
// // SymbolLM Score -12
// if theNode.scoreFor(candidate: value) <= -12 {
// IME.prtDebugIntel("UOM: Score <= -12, dismissing.")
// addToUserOverrideModel = false
@ -188,26 +193,49 @@ class KeyHandler {
// }
// if addToUserOverrideModel {
// IME.prtDebugIntel("UOM: Start Observation.")
// _userOverrideModel.observe(
// walkedNodes: _walkedNodes, cursorIndex: cursorIndex, candidate: value,
// // trigram
// // trigram
// currentUOM.observe(
// walkedNodes: walkedAnchors, cursorIndex: cursorIndex, candidate: value,
// timestamp: NSDate().timeIntervalSince1970
// )
// }
// }
walk()
///
if mgrPrefs.moveCursorAfterSelectingCandidate, respectCursorPushing {
var nextPosition = 0
for node in _walkedNodes {
for node in walkedAnchors {
if nextPosition >= cursorIndex { break }
nextPosition += node.spanningLength
}
if nextPosition <= builderLength {
builderCursorIndex = nextPosition
if nextPosition <= compositorLength {
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 arrCandidates: [String] = []
var arrNodes: [Megrez.NodeAnchor] = []
@ -233,20 +261,23 @@ class KeyHandler {
return arrCandidates
}
///
func dealWithOverrideModelSuggestions() {
/// trigram
let overrideValue =
mgrPrefs.useSCPCTypingMode
? ""
: _userOverrideModel.suggest(
walkedNodes: _walkedNodes, cursorIndex: builderCursorIndex,
: currentUOM.suggest(
walkedNodes: walkedAnchors, cursorIndex: compositorCursorIndex,
timestamp: NSDate().timeIntervalSince1970
)
///
if !overrideValue.isEmpty {
IME.prtDebugIntel(
"UOM: Suggestion retrieved, overriding the node score of the selected candidate.")
_builder.grid.overrideNodeScoreForSelectedCandidate(
location: min(actualCandidateCursorIndex + (mgrPrefs.useRearCursorMode ? 1 : 0), builderLength),
compositor.grid.overrideNodeScoreForSelectedCandidate(
location: min(actualCandidateCursorIndex + (mgrPrefs.useRearCursorMode ? 1 : 0), compositorLength),
value: overrideValue,
overridingScore: findHighestScore(nodes: rawNodes, epsilon: kEpsilon)
)
@ -255,6 +286,11 @@ class KeyHandler {
}
}
///
/// - Parameters:
/// - nodes:
/// - epsilon:
/// - Returns:
func findHighestScore(nodes: [Megrez.NodeAnchor], epsilon: Double) -> Double {
var highestScore: Double = 0
for currentAnchor in nodes {
@ -270,92 +306,144 @@ class KeyHandler {
// MARK: - Extracted methods and functions (Tekkon).
/// _
var currentMandarinParser: String {
mgrPrefs.mandarinParserName + "_"
}
///
func ensureParser() {
switch mgrPrefs.mandarinParser {
case MandarinParser.ofStandard.rawValue:
_composer.ensureParser(arrange: .ofDachen)
composer.ensureParser(arrange: .ofDachen)
case MandarinParser.ofDachen26.rawValue:
_composer.ensureParser(arrange: .ofDachen26)
case MandarinParser.ofEten.rawValue:
_composer.ensureParser(arrange: .ofEten)
composer.ensureParser(arrange: .ofDachen26)
case MandarinParser.ofETen.rawValue:
composer.ensureParser(arrange: .ofETen)
case MandarinParser.ofHsu.rawValue:
_composer.ensureParser(arrange: .ofHsu)
case MandarinParser.ofEten26.rawValue:
_composer.ensureParser(arrange: .ofEten26)
composer.ensureParser(arrange: .ofHsu)
case MandarinParser.ofETen26.rawValue:
composer.ensureParser(arrange: .ofETen26)
case MandarinParser.ofIBM.rawValue:
_composer.ensureParser(arrange: .ofIBM)
composer.ensureParser(arrange: .ofIBM)
case MandarinParser.ofMiTAC.rawValue:
_composer.ensureParser(arrange: .ofMiTAC)
composer.ensureParser(arrange: .ofMiTAC)
case MandarinParser.ofFakeSeigyou.rawValue:
_composer.ensureParser(arrange: .ofFakeSeigyou)
composer.ensureParser(arrange: .ofFakeSeigyou)
case MandarinParser.ofHanyuPinyin.rawValue:
_composer.ensureParser(arrange: .ofHanyuPinyin)
composer.ensureParser(arrange: .ofHanyuPinyin)
case MandarinParser.ofSecondaryPinyin.rawValue:
_composer.ensureParser(arrange: .ofSecondaryPinyin)
composer.ensureParser(arrange: .ofSecondaryPinyin)
case MandarinParser.ofYalePinyin.rawValue:
_composer.ensureParser(arrange: .ofYalePinyin)
composer.ensureParser(arrange: .ofYalePinyin)
case MandarinParser.ofHualuoPinyin.rawValue:
_composer.ensureParser(arrange: .ofHualuoPinyin)
composer.ensureParser(arrange: .ofHualuoPinyin)
case MandarinParser.ofUniversalPinyin.rawValue:
_composer.ensureParser(arrange: .ofUniversalPinyin)
composer.ensureParser(arrange: .ofUniversalPinyin)
default:
_composer.ensureParser(arrange: .ofDachen)
composer.ensureParser(arrange: .ofDachen)
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).
var isBuilderEmpty: Bool { _builder.grid.width == 0 }
///
var isCompositorEmpty: Bool { compositor.isEmpty }
///
var rawNodes: [Megrez.NodeAnchor] {
/// 使 nodesCrossing macOS
/// nodeCrossing
mgrPrefs.useRearCursorMode
? _builder.grid.nodesBeginningAt(location: actualCandidateCursorIndex)
: _builder.grid.nodesEndingAt(location: actualCandidateCursorIndex)
? compositor.grid.nodesBeginningAt(location: actualCandidateCursorIndex)
: compositor.grid.nodesEndingAt(location: actualCandidateCursorIndex)
}
///
func syncBaseLMPrefs() {
_languageModel.isPhraseReplacementEnabled = mgrPrefs.phraseReplacementEnabled
_languageModel.isCNSEnabled = mgrPrefs.cns11643Enabled
_languageModel.isSymbolEnabled = mgrPrefs.symbolInputEnabled
currentLM.isPhraseReplacementEnabled = mgrPrefs.phraseReplacementEnabled
currentLM.isCNSEnabled = mgrPrefs.cns11643Enabled
currentLM.isSymbolEnabled = mgrPrefs.symbolInputEnabled
}
func createNewBuilder() {
// Each Mandarin syllable is separated by a hyphen.
_builder = Megrez.BlockReadingBuilder(lm: _languageModel, separator: "-")
/// 使
func ensureCompositor() {
// 西
compositor = Megrez.Compositor(lm: currentLM, separator: "-")
}
var currentReadings: [String] { _builder.readings }
///
var currentReadings: [String] { compositor.readings }
///
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 }
set { _builder.cursorIndex = newValue }
///
var compositorCursorIndex: Int {
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()
}
var keyLengthAtIndexZero: Int {
_walkedNodes[0].node?.currentKeyValue.value.count ?? 0
///
///
/// Front
func deleteCompositorReadingToTheFrontOfCursor() {
compositor.deleteReadingToTheFrontOfCursor()
}
}

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.
*/
/// 調
import Cocoa
// MARK: - § Handle Candidate State.
// MARK: - § 調 (Handle Candidate State).
extension KeyHandler {
func handleCandidate(
@ -43,7 +45,7 @@ extension KeyHandler {
return true
}
// MARK: Cancel Candidate
// MARK: (Cancel Candidate)
let cancelCandidateKey =
input.isBackSpace || input.isESC || input.isDelete
@ -52,12 +54,12 @@ extension KeyHandler {
if cancelCandidateKey {
if (state is InputState.AssociatedPhrases)
|| mgrPrefs.useSCPCTypingMode
|| isBuilderEmpty
|| isCompositorEmpty
{
//
//
// 使 BackSpace
// isBuilderEmpty
// isCompositorEmpty
clear()
stateCallback(InputState.EmptyIgnoringPreviousState())
} else {
@ -286,7 +288,7 @@ extension KeyHandler {
}
}
// MARK: - Associated Phrases
// MARK: (Associated Phrases)
if state is InputState.AssociatedPhrases {
if !input.isShiftHold { return false }
@ -322,9 +324,13 @@ extension KeyHandler {
if state is InputState.AssociatedPhrases { return false }
// MARK: SCPC Mode Processing
// MARK: (SCPC Mode Processing)
if mgrPrefs.useSCPCTypingMode {
///
/// - /
/// -
var punctuationNamePrefix = ""
if input.isOptionHold && !input.isControlHold {
@ -346,11 +352,13 @@ extension KeyHandler {
]
let customPunctuation: String = arrCustomPunctuations.joined(separator: "")
///
let arrPunctuations: [String] = [punctuationNamePrefix, String(format: "%c", CChar(charCode))]
let punctuation: String = arrPunctuations.joined(separator: "")
var shouldAutoSelectCandidate: Bool =
_composer.inputValidityCheck(key: charCode) || ifLangModelHasUnigrams(forKey: customPunctuation)
composer.inputValidityCheck(key: charCode) || ifLangModelHasUnigrams(forKey: customPunctuation)
|| ifLangModelHasUnigrams(forKey: punctuation)
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.
*/
import Cocoa
import SwiftUI
/// 調 IMK 調
/// 調
// MARK: - § Handle Input with States.
import Cocoa
// MARK: - § 調 (Handle Input with States)
extension KeyHandler {
func handle(
@ -37,10 +39,9 @@ extension KeyHandler {
errorCallback: @escaping () -> Void
) -> Bool {
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.
// Reason: such inputs may be functional key combinations.
// inputTest
guard let inputText: String = input.inputText, !inputText.isEmpty else {
return false
}
@ -53,8 +54,7 @@ extension KeyHandler {
return true
}
// Ignore the input if the composing buffer is empty with no reading
// and there is some function key combination.
//
let isFunctionKey: Bool =
input.isControlHotKey || (input.isCommandHold || input.isOptionHotKey || input.isNumericPad)
if !(state is InputState.NotEmpty) && !(state is InputState.AssociatedPhrases) && isFunctionKey {
@ -63,37 +63,40 @@ extension KeyHandler {
// MARK: Caps Lock processing.
// If Caps Lock is ON, temporarily disable phonetic reading.
// Note: Alphanumerical mode processing.
/// Caps Lock
/// Shift Chromium
/// IMK Shift 使
/// Caps Lock
if input.isBackSpace || input.isEnter || input.isAbsorbedArrowKey || input.isExtraChooseCandidateKey
|| input.isExtraChooseCandidateKeyReverse || input.isCursorForward || input.isCursorBackward
{
// Do nothing if backspace is pressed -- we ignore the key
// BackSpace
} else if input.isCapsLockOn {
// Process all possible combination, we hope.
//
clear()
stateCallback(InputState.Empty())
// When shift is pressed, don't do further processing...
// ...since it outputs capital letter anyway.
// Shift
if input.isShiftHold {
return false
}
// If ASCII but not printable, don't use insertText:replacementRange:
// Certain apps don't handle non-ASCII char insertions.
/// ASCII 使insertText:replacementRange:
/// ASCII
/// Objective-C isPrintable()
/// CTools.h Swift
if charCode < 0x80, !CTools.isPrintable(charCode) {
return false
}
// Commit the entire input buffer.
stateCallback(InputState.Committing(poppedText: inputText.lowercased()))
//
stateCallback(InputState.Committing(textToCommit: inputText.lowercased()))
stateCallback(InputState.Empty())
return true
}
// MARK: Numeric Pad Processing.
// MARK: (Numeric Pad Processing)
if input.isNumericPad {
if !input.isLeft, !input.isRight, !input.isDown,
@ -101,13 +104,13 @@ extension KeyHandler {
{
clear()
stateCallback(InputState.Empty())
stateCallback(InputState.Committing(poppedText: inputText.lowercased()))
stateCallback(InputState.Committing(textToCommit: inputText.lowercased()))
stateCallback(InputState.Empty())
return true
}
}
// MARK: Handle Candidates.
// MARK: (Handle Candidates)
if state is InputState.ChoosingCandidate {
return handleCandidate(
@ -115,7 +118,7 @@ extension KeyHandler {
)
}
// MARK: Handle Associated Phrases.
// MARK: (Handle Associated Phrases)
if state is InputState.AssociatedPhrases {
if handleCandidate(
@ -127,7 +130,7 @@ extension KeyHandler {
}
}
// MARK: Handle Marking.
// MARK: 便使() (Handle Marking)
if let marking = state as? InputState.Marking {
if handleMarkingState(
@ -140,63 +143,72 @@ extension KeyHandler {
stateCallback(state)
}
// MARK: Handle BPMF Keys.
// MARK: (Handle BPMF Keys)
var keyConsumedByReading = false
let skipPhoneticHandling = input.isReservedKey || input.isControlHold || input.isOptionHold
// See if Phonetic reading is valid.
if !skipPhoneticHandling && _composer.inputValidityCheck(key: charCode) {
_composer.receiveKey(fromCharCode: charCode)
// inputValidityCheck() charCode UniChar
// keyConsumedByReading
// composer.receiveKey() String UniChar
if !skipPhoneticHandling && composer.inputValidityCheck(key: charCode) {
composer.receiveKey(fromCharCode: charCode)
keyConsumedByReading = true
// If we have a tone marker, we have to insert the reading to the
// builder in other words, if we don't have a tone marker, we just
// update the composing buffer.
let composeReading = _composer.hasToneMarker()
// 調 updateClientComposingBuffer() return true
// 調
let composeReading = composer.hasToneMarker()
if !composeReading {
stateCallback(buildInputtingState)
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.
// 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))
// Enter Space _composer
// |=
composeReading = composeReading || (!composer.isEmpty && (input.isSpace || input.isEnter))
if composeReading {
if input.isSpace, !_composer.hasToneMarker() {
_composer.receiveKey(fromString: " ") //
if input.isSpace, !composer.hasToneMarker() {
// 調
// 使 OVMandarin調
composer.receiveKey(fromString: " ")
}
let reading = _composer.getComposition()
let reading = composer.getComposition() //
//
// See whether we have a unigram for this...
//
if !ifLangModelHasUnigrams(forKey: reading) {
IME.prtDebugIntel("B49C0979語彙庫內無「\(reading)」的匹配記錄。")
errorCallback()
_composer.clear()
stateCallback((builderLength == 0) ? InputState.EmptyIgnoringPreviousState() : buildInputtingState)
return true
composer.clear()
//
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() // 使
// ... then update the text.
_composer.clear()
//
markNodesFixedIfNecessary()
//
composer.clear()
// updateClientComposingBuffer()
let inputting = buildInputtingState
inputting.poppedText = poppedText
inputting.textToCommit = textToCommit
stateCallback(inputting)
///
if mgrPrefs.useSCPCTypingMode {
let choosingCandidates: InputState.ChoosingCandidate = buildCandidate(
state: inputting,
@ -205,7 +217,7 @@ extension KeyHandler {
if choosingCandidates.candidates.count == 1 {
clear()
let text: String = choosingCandidates.candidates.first ?? ""
stateCallback(InputState.Committing(poppedText: text))
stateCallback(InputState.Committing(textToCommit: text))
if !mgrPrefs.associatedPhrasesEnabled {
stateCallback(InputState.Empty())
@ -225,45 +237,52 @@ extension KeyHandler {
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
// already has a tone marker but the last key is *not* a tone marker key. An
// example is the sequence "6u" with the Standard layout, which produces "ˊ"
// but does not compose. Only sequences such as "ˊ", "ˊˊ", "ˊˇ", or "ˊ "
// would compose.
/// true 調調
/// 6jˊˊ調
/// 調ˊˊˊˊˇˊ
if keyConsumedByReading {
// updateClientComposingBuffer()
stateCallback(buildInputtingState)
return true
}
// 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.isPageDown || input.isPageUp || (input.isTab && mgrPrefs.specifyShiftTabKeyBehavior)
|| (input.isTypingVertical && (input.isverticalTypingOnlyChooseCandidateKey))
{
if input.isSpace {
// If the Space key is NOT set to be a selection key
if input.isShiftHold || !mgrPrefs.chooseCandidateUsingSpace {
if builderCursorIndex >= builderLength {
/// Space
if !mgrPrefs.chooseCandidateUsingSpace {
if compositorCursorIndex >= compositorLength {
let composingBuffer = currentState.composingBuffer
if !composingBuffer.isEmpty {
stateCallback(InputState.Committing(poppedText: composingBuffer))
stateCallback(InputState.Committing(textToCommit: composingBuffer))
}
clear()
stateCallback(InputState.Committing(poppedText: " "))
stateCallback(InputState.Committing(textToCommit: " "))
stateCallback(InputState.Empty())
} else if ifLangModelHasUnigrams(forKey: " ") {
insertReadingToBuilderAtCursor(reading: " ")
let poppedText = popOverflowComposingTextAndWalk
insertToCompositorAtCursor(reading: " ")
let textToCommit = popOverflowComposingTextAndWalk
let inputting = buildInputtingState
inputting.poppedText = poppedText
inputting.textToCommit = textToCommit
stateCallback(inputting)
}
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))
@ -279,8 +298,8 @@ extension KeyHandler {
// MARK: Tab
if input.isTab {
return handleTab(
state: state, isShiftHold: input.isShiftHold, stateCallback: stateCallback, errorCallback: errorCallback
return handleInlineCandidateRotation(
state: state, reverseModifier: input.isShiftHold, stateCallback: stateCallback, errorCallback: errorCallback
)
}
@ -362,11 +381,11 @@ extension KeyHandler {
if input.isSymbolMenuPhysicalKey && !input.isShiftHold {
if input.isOptionHold {
if ifLangModelHasUnigrams(forKey: "_punctuation_list") {
if _composer.isEmpty {
insertReadingToBuilderAtCursor(reading: "_punctuation_list")
let poppedText: String! = popOverflowComposingTextAndWalk
if composer.isEmpty {
insertToCompositorAtCursor(reading: "_punctuation_list")
let textToCommit: String! = popOverflowComposingTextAndWalk
let inputting = buildInputtingState
inputting.poppedText = poppedText
inputting.textToCommit = textToCommit
stateCallback(inputting)
stateCallback(buildCandidate(state: inputting, isTypingVertical: input.isTypingVertical))
} else { // If there is still unfinished bpmf reading, ignore the punctuation
@ -378,7 +397,7 @@ extension KeyHandler {
} else {
// commit buffer ESC
// Enter 使 commit buffer
// bool _ =
// bool _ =
_ = handleEnter(state: state, stateCallback: stateCallback, errorCallback: errorCallback)
stateCallback(InputState.SymbolTable(node: SymbolNode.root, isTypingVertical: input.isTypingVertical))
return true
@ -387,7 +406,9 @@ extension KeyHandler {
// MARK: Punctuation
// If nothing is matched, see if it's a punctuation key for current layout.
///
/// - /
/// -
var punctuationNamePrefix = ""
@ -418,7 +439,8 @@ extension KeyHandler {
return true
}
// if nothing is matched, see if it's a punctuation key.
///
let arrPunctuations: [String] = [punctuationNamePrefix, String(format: "%c", CChar(charCode))]
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.
// Note that some app has strange behavior if we don't do this,
// "thinking" that the key is not actually consumed.
// F1-F12
// 便
if (state is InputState.NotEmpty) || !_composer.isEmpty {
/// ctlInputMethod
///
/// F1-F12
/// 便
if (state is InputState.NotEmpty) || !composer.isEmpty {
IME.prtDebugIntel(
"Blocked data: charCode: \(charCode), keyCode: \(input.keyCode)")
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.
*/
/// 調調
import Cocoa
// MARK: - § State managements.
// MARK: - § 調 (Functions Interact With States).
extension KeyHandler {
// MARK: - State Building
///
var buildInputtingState: InputState.Inputting {
// "Updating the composing buffer" means to request the client
// to "refresh" the text input buffer with our "composing text"
/// (Update the composing buffer)
/// NSAttributeString
var tooltipParameterRef: [String] = ["", ""]
var composingBuffer = ""
var composedStringCursorIndex = 0
var readingCursorIndex = 0
// We must do some Unicode codepoint counting to find the actual cursor location for the client
// i.e. we need to take UTF-16 into consideration, for which a surrogate pair takes 2 UniChars
// locations. Since we are using Swift, we use .utf16 as the equivalent of NSString.length().
for walkedNode in _walkedNodes {
/// IMK UTF8 emoji
/// Swift.utf16NSString.length()
///
for walkedNode in walkedAnchors {
if let theNode = walkedNode.node {
let strNodeValue = theNode.currentKeyValue.value
composingBuffer += strNodeValue
let arrSplit: [String] = Array(strNodeValue).map { String($0) }
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
// 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.
///
/// NodeAnchorspanningLength
///
let spanningLength: Int = walkedNode.spanningLength
if readingCursorIndex + spanningLength <= builderCursorIndex {
if readingCursorIndex + spanningLength <= compositorCursorIndex {
composedStringCursorIndex += strNodeValue.utf16.count
readingCursorIndex += spanningLength
} else {
if codepointCount == spanningLength {
var i = 0
while i < codepointCount, readingCursorIndex < builderCursorIndex {
while i < codepointCount, readingCursorIndex < compositorCursorIndex {
composedStringCursorIndex += arrSplit[i].utf16.count
readingCursorIndex += 1
i += 1
}
} else {
if readingCursorIndex < builderCursorIndex {
if readingCursorIndex < compositorCursorIndex {
composedStringCursorIndex += strNodeValue.utf16.count
readingCursorIndex += spanningLength
if readingCursorIndex > builderCursorIndex {
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.
// Example in vChewing: Typing (2 readings) gets a pasta emoji.
switch builderCursorIndex {
case _builder.readings.count...:
tooltipParameterRef[0] = _builder.readings[_builder.readings.count - 1]
readingCursorIndex = min(readingCursorIndex, compositorCursorIndex)
///
///
///
///
///
switch compositorCursorIndex {
case compositor.readings.count...:
tooltipParameterRef[0] = compositor.readings[compositor.readings.count - 1]
case 0:
tooltipParameterRef[1] = _builder.readings[builderCursorIndex]
tooltipParameterRef[1] = compositor.readings[compositorCursorIndex]
default:
do {
tooltipParameterRef[0] = _builder.readings[builderCursorIndex - 1]
tooltipParameterRef[1] = _builder.readings[builderCursorIndex]
tooltipParameterRef[0] = compositor.readings[compositorCursorIndex - 1]
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.
// The reading text is what the user is typing.
///
/// 便 composer
var arrHead = [String.UTF16View.Element]()
var arrTail = [String.UTF16View.Element]()
@ -108,34 +105,49 @@ extension KeyHandler {
}
}
/// stringview
///
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 composedText = head + reading + tail
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 {
stateResult.tooltip = ""
} else if tooltipParameterRef[0].isEmpty {
stateResult.tooltip = String(
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]
)
//
for theChar in composedText {
if let charCode = theChar.utf16.first {
if !(theChar.isASCII && !(charCode.isPrintable())) {
cleanedComposition += String(theChar)
}
}
}
/// 使
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 {
ctlInputMethod.tooltipController.setColor(state: .denialOverflow)
}
@ -145,6 +157,11 @@ extension KeyHandler {
// MARK: -
///
/// - Parameters:
/// - currentState:
/// - isTypingVertical:
/// - Returns:
func buildCandidate(
state currentState: InputState.NotEmpty,
isTypingVertical: Bool = false
@ -159,13 +176,19 @@ extension KeyHandler {
// MARK: -
// buildAssociatePhraseStateWithKey
// 使
// Core buildAssociatePhraseArray
// String Swift
// nil
//
//
///
///
/// buildAssociatePhraseStateWithKey
/// 使
/// Core buildAssociatePhraseArray
/// String Swift
/// nil
///
///
/// - Parameters:
/// - key:
/// - isTypingVertical:
/// - Returns:
func buildAssociatePhraseState(
withKey key: String!,
isTypingVertical: Bool
@ -178,6 +201,13 @@ extension KeyHandler {
// MARK: -
///
/// - Parameters:
/// - state:
/// - input:
/// - stateCallback:
/// - errorCallback:
/// - Returns: ctlInputMethod IMK
func handleMarkingState(
_ state: InputState.Marking,
input: InputSignal,
@ -246,8 +276,16 @@ extension KeyHandler {
return false
}
// MARK: -
// MARK: -
///
/// - Parameters:
/// - customPunctuation:
/// - state:
/// - isTypingVertical:
/// - stateCallback:
/// - errorCallback:
/// - Returns: ctlInputMethod IMK
func handlePunctuation(
_ customPunctuation: String,
state: InputState,
@ -259,22 +297,22 @@ extension KeyHandler {
return false
}
if _composer.isEmpty {
insertReadingToBuilderAtCursor(reading: customPunctuation)
let poppedText = popOverflowComposingTextAndWalk
if composer.isEmpty {
insertToCompositorAtCursor(reading: customPunctuation)
let textToCommit = popOverflowComposingTextAndWalk
let inputting = buildInputtingState
inputting.poppedText = poppedText
inputting.textToCommit = textToCommit
stateCallback(inputting)
if mgrPrefs.useSCPCTypingMode, _composer.isEmpty {
if mgrPrefs.useSCPCTypingMode, composer.isEmpty {
let candidateState = buildCandidate(
state: inputting,
isTypingVertical: isTypingVertical
)
if candidateState.candidates.count == 1 {
clear()
if let strPoppedText: String = candidateState.candidates.first {
stateCallback(InputState.Committing(poppedText: strPoppedText) as InputState.Committing)
if let strtextToCommit: String = candidateState.candidates.first {
stateCallback(InputState.Committing(textToCommit: strtextToCommit) as InputState.Committing)
stateCallback(InputState.Empty())
} else {
stateCallback(candidateState)
@ -293,8 +331,13 @@ extension KeyHandler {
}
}
// MARK: - Enter
// MARK: - Enter
/// Enter
/// - Parameters:
/// - state:
/// - stateCallback:
/// - Returns: ctlInputMethod IMK
func handleEnter(
state: InputState,
stateCallback: @escaping (InputState) -> Void,
@ -303,13 +346,18 @@ extension KeyHandler {
guard let currentState = state as? InputState.Inputting else { return false }
clear()
stateCallback(InputState.Committing(poppedText: currentState.composingBuffer))
stateCallback(InputState.Committing(textToCommit: currentState.composingBuffer))
stateCallback(InputState.Empty())
return true
}
// MARK: - CMD+Enter
// MARK: - CMD+Enter
/// CMD+Enter
/// - Parameters:
/// - state:
/// - stateCallback:
/// - Returns: ctlInputMethod IMK
func handleCtrlCommandEnter(
state: InputState,
stateCallback: @escaping (InputState) -> Void,
@ -329,13 +377,18 @@ extension KeyHandler {
clear()
stateCallback(InputState.Committing(poppedText: composingBuffer))
stateCallback(InputState.Committing(textToCommit: composingBuffer))
stateCallback(InputState.Empty())
return true
}
// MARK: - CMD+Alt+Enter Ruby
// MARK: - CMD+Alt+Enter Ruby
/// CMD+Alt+Enter Ruby
/// - Parameters:
/// - state:
/// - stateCallback:
/// - Returns: ctlInputMethod IMK
func handleCtrlOptionCommandEnter(
state: InputState,
stateCallback: @escaping (InputState) -> Void,
@ -345,7 +398,7 @@ extension KeyHandler {
var composed = ""
for theAnchor in _walkedNodes {
for theAnchor in walkedAnchors {
if let node = theAnchor.node {
var key = node.currentKeyValue.key
if mgrPrefs.inlineDumpPinyinInLieuOfZhuyin {
@ -368,13 +421,19 @@ extension KeyHandler {
clear()
stateCallback(InputState.Committing(poppedText: composed))
stateCallback(InputState.Committing(textToCommit: composed))
stateCallback(InputState.Empty())
return true
}
// MARK: - Backspace (macOS Delete)
/// Backspace (macOS Delete)
/// - Parameters:
/// - state:
/// - stateCallback:
/// - errorCallback:
/// - Returns: ctlInputMethod IMK
func handleBackspace(
state: InputState,
stateCallback: @escaping (InputState) -> Void,
@ -382,11 +441,11 @@ extension KeyHandler {
) -> Bool {
guard state is InputState.Inputting else { return false }
if _composer.hasToneMarker(withNothingElse: true) {
_composer.clear()
} else if _composer.isEmpty {
if builderCursorIndex >= 0 {
deleteBuilderReadingInFrontOfCursor()
if composer.hasToneMarker(withNothingElse: true) {
composer.clear()
} else if composer.isEmpty {
if compositorCursorIndex >= 0 {
deleteCompositorReadingAtTheRearOfCursor()
walk()
} else {
IME.prtDebugIntel("9D69908D")
@ -395,10 +454,10 @@ extension KeyHandler {
return true
}
} else {
_composer.doBackSpace()
composer.doBackSpace()
}
if _composer.isEmpty, builderLength == 0 {
if composer.isEmpty, compositorLength == 0 {
stateCallback(InputState.EmptyIgnoringPreviousState())
} else {
stateCallback(buildInputtingState)
@ -408,6 +467,12 @@ extension KeyHandler {
// MARK: - PC Delete (macOS Fn+BackSpace)
/// PC Delete (macOS Fn+BackSpace)
/// - Parameters:
/// - state:
/// - stateCallback:
/// - errorCallback:
/// - Returns: ctlInputMethod IMK
func handleDelete(
state: InputState,
stateCallback: @escaping (InputState) -> Void,
@ -415,9 +480,9 @@ extension KeyHandler {
) -> Bool {
guard state is InputState.Inputting else { return false }
if _composer.isEmpty {
if builderCursorIndex != builderLength {
deleteBuilderReadingToTheFrontOfCursor()
if composer.isEmpty {
if compositorCursorIndex != compositorLength {
deleteCompositorReadingToTheFrontOfCursor()
walk()
let inputting = buildInputtingState
// count > 0!isEmpty滿
@ -442,13 +507,19 @@ extension KeyHandler {
// MARK: - 90
/// 90
/// - Parameters:
/// - state:
/// - stateCallback:
/// - errorCallback:
/// - Returns: ctlInputMethod IMK
func handleAbsorbedArrowKey(
state: InputState,
stateCallback: @escaping (InputState) -> Void,
errorCallback: @escaping () -> Void
) -> Bool {
guard state is InputState.Inputting else { return false }
if !_composer.isEmpty {
if !composer.isEmpty {
IME.prtDebugIntel("9B6F908D")
errorCallback()
}
@ -456,8 +527,14 @@ extension KeyHandler {
return true
}
// MARK: - Home
// MARK: - Home
/// Home
/// - Parameters:
/// - state:
/// - stateCallback:
/// - errorCallback:
/// - Returns: ctlInputMethod IMK
func handleHome(
state: InputState,
stateCallback: @escaping (InputState) -> Void,
@ -465,15 +542,15 @@ extension KeyHandler {
) -> Bool {
guard state is InputState.Inputting else { return false }
if !_composer.isEmpty {
if !composer.isEmpty {
IME.prtDebugIntel("ABC44080")
errorCallback()
stateCallback(state)
return true
}
if builderCursorIndex != 0 {
builderCursorIndex = 0
if compositorCursorIndex != 0 {
compositorCursorIndex = 0
stateCallback(buildInputtingState)
} else {
IME.prtDebugIntel("66D97F90")
@ -484,8 +561,14 @@ extension KeyHandler {
return true
}
// MARK: - End
// MARK: - End
/// End
/// - Parameters:
/// - state:
/// - stateCallback:
/// - errorCallback:
/// - Returns: ctlInputMethod IMK
func handleEnd(
state: InputState,
stateCallback: @escaping (InputState) -> Void,
@ -493,15 +576,15 @@ extension KeyHandler {
) -> Bool {
guard state is InputState.Inputting else { return false }
if !_composer.isEmpty {
if !composer.isEmpty {
IME.prtDebugIntel("9B69908D")
errorCallback()
stateCallback(state)
return true
}
if builderCursorIndex != builderLength {
builderCursorIndex = builderLength
if compositorCursorIndex != compositorLength {
compositorCursorIndex = compositorLength
stateCallback(buildInputtingState)
} else {
IME.prtDebugIntel("9B69908E")
@ -512,8 +595,13 @@ extension KeyHandler {
return true
}
// MARK: - Esc
// MARK: - Esc
/// Esc
/// - Parameters:
/// - state:
/// - stateCallback:
/// - Returns: ctlInputMethod IMK
func handleEsc(
state: InputState,
stateCallback: @escaping (InputState) -> Void,
@ -521,20 +609,16 @@ extension KeyHandler {
) -> Bool {
guard state is InputState.Inputting else { return false }
let escToClearInputBufferEnabled: Bool = mgrPrefs.escToCleanInputBuffer
if escToClearInputBufferEnabled {
// 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.
if mgrPrefs.escToCleanInputBuffer {
///
/// macOS Windows 使
clear()
stateCallback(InputState.EmptyIgnoringPreviousState())
} else {
// If reading is not empty, we cancel the reading.
if !_composer.isEmpty {
_composer.clear()
if builderLength == 0 {
///
if !composer.isEmpty {
composer.clear()
if compositorLength == 0 {
stateCallback(InputState.EmptyIgnoringPreviousState())
} else {
stateCallback(buildInputtingState)
@ -546,6 +630,13 @@ extension KeyHandler {
// MARK: -
///
/// - Parameters:
/// - state:
/// - input:
/// - stateCallback:
/// - errorCallback:
/// - Returns: ctlInputMethod IMK
func handleForward(
state: InputState,
input: InputSignal,
@ -554,7 +645,7 @@ extension KeyHandler {
) -> Bool {
guard let currentState = state as? InputState.Inputting else { return false }
if !_composer.isEmpty {
if !composer.isEmpty {
IME.prtDebugIntel("B3BA5257")
errorCallback()
stateCallback(state)
@ -580,8 +671,8 @@ extension KeyHandler {
stateCallback(state)
}
} else {
if builderCursorIndex < builderLength {
builderCursorIndex += 1
if compositorCursorIndex < compositorLength {
compositorCursorIndex += 1
stateCallback(buildInputtingState)
} else {
IME.prtDebugIntel("A96AAD58")
@ -595,6 +686,13 @@ extension KeyHandler {
// MARK: -
///
/// - Parameters:
/// - state:
/// - input:
/// - stateCallback:
/// - errorCallback:
/// - Returns: ctlInputMethod IMK
func handleBackward(
state: InputState,
input: InputSignal,
@ -603,7 +701,7 @@ extension KeyHandler {
) -> Bool {
guard let currentState = state as? InputState.Inputting else { return false }
if !_composer.isEmpty {
if !composer.isEmpty {
IME.prtDebugIntel("6ED95318")
errorCallback()
stateCallback(state)
@ -629,8 +727,8 @@ extension KeyHandler {
stateCallback(state)
}
} else {
if builderCursorIndex > 0 {
builderCursorIndex -= 1
if compositorCursorIndex > 0 {
compositorCursorIndex -= 1
stateCallback(buildInputtingState)
} else {
IME.prtDebugIntel("7045E6F3")
@ -642,11 +740,18 @@ extension KeyHandler {
return true
}
// MARK: - Tab
// MARK: - Tab Shift+Space
func handleTab(
///
/// - Parameters:
/// - state:
/// - reverseModifier:
/// - stateCallback:
/// - errorCallback:
/// - Returns: ctlInputMethod IMK
func handleInlineCandidateRotation(
state: InputState,
isShiftHold: Bool,
reverseModifier: Bool,
stateCallback: @escaping (InputState) -> Void,
errorCallback: @escaping () -> Void
) -> Bool {
@ -660,13 +765,13 @@ extension KeyHandler {
return false
}
guard _composer.isEmpty else {
guard composer.isEmpty else {
IME.prtDebugIntel("A2DAF7BC")
errorCallback()
return true
}
//
//
let candidates = buildCandidate(state: state).candidates
guard !candidates.isEmpty else {
IME.prtDebugIntel("3378A6DF")
@ -677,9 +782,9 @@ extension KeyHandler {
var length = 0
var currentAnchor = Megrez.NodeAnchor()
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
if length >= cursorIndex {
currentAnchor = anchor
@ -697,19 +802,18 @@ extension KeyHandler {
var currentIndex = 0
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
// 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
// than a phrase with two characters, the user can just
// use the longer phrase by tapping the tab key.
/// 使
/// 使
/// 2 使
///
///
/// 使
/// (Shift+)Tab ()
/// Shift(+CMD)+Space Tab
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
} else {
currentIndex = 1
@ -718,7 +822,7 @@ extension KeyHandler {
} else {
for candidate in candidates {
if candidate == currentValue {
if isShiftHold {
if reverseModifier {
if currentIndex == 0 {
currentIndex = candidates.count - 1
} else {

View File

@ -41,8 +41,8 @@ public struct Tekkon {
public enum MandarinParser: Int {
case ofDachen = 0
case ofDachen26 = 1
case ofEten = 2
case ofEten26 = 3
case ofETen = 2
case ofETen26 = 3
case ofHsu = 4
case ofIBM = 5
case ofMiTAC = 6
@ -60,11 +60,11 @@ public struct Tekkon {
return "Dachen"
case .ofDachen26:
return "Dachen26"
case .ofEten:
case .ofETen:
return "ETen"
case .ofHsu:
return "Hsu"
case .ofEten26:
case .ofETen26:
return "ETen26"
case .ofIBM:
return "IBM"
@ -140,6 +140,7 @@ public struct Tekkon {
///
public mutating func clear() {
valueStorage = ""
type = .null
}
///
@ -151,7 +152,7 @@ public struct Tekkon {
ensureType()
}
///
///
mutating func ensureType() {
if Tekkon.allowedConsonants.contains(value) {
type = .consonant
@ -214,7 +215,7 @@ public struct Tekkon {
/// 調
public var intonation: Phonabet = ""
///
///
public var romajiBuffer: String = ""
/// Windows / macOS
@ -227,7 +228,7 @@ public struct Tekkon {
consonant.value + semivowel.value + vowel.value + intonation.value
}
/// value /
/// value /
/// 調
/// - Parameters:
/// - isHanyuPinyin:
@ -250,7 +251,7 @@ public struct Tekkon {
}
}
// macOS InputMethod Kit 使
// macOS InputMethod Kit 使
/// - Parameters:
/// - isHanyuPinyin:
public func getInlineCompositionForIMK(isHanyuPinyin: Bool = false) -> String {
@ -279,12 +280,12 @@ public struct Tekkon {
}
}
///
///
public var isPronouncable: Bool {
!vowel.isEmpty || !semivowel.isEmpty || !consonant.isEmpty
}
// MARK:
// MARK:
/// @input
/// @arrange .ofDachen
@ -308,7 +309,7 @@ public struct Tekkon {
// MARK: - Public Functions
///
///
///
/// parser
/// - Parameters:
@ -321,12 +322,12 @@ public struct Tekkon {
return Tekkon.mapQwertyDachen[input] != nil
case .ofDachen26:
return Tekkon.mapDachenCP26StaticKeys[input] != nil
case .ofEten:
return Tekkon.mapQwertyEtenTraditional[input] != nil
case .ofETen:
return Tekkon.mapQwertyETenTraditional[input] != nil
case .ofHsu:
return Tekkon.mapHsuStaticKeys[input] != nil
case .ofEten26:
return Tekkon.mapEten26StaticKeys[input] != nil
case .ofETen26:
return Tekkon.mapETen26StaticKeys[input] != nil
case .ofIBM:
return Tekkon.mapQwertyIBM[input] != nil
case .ofMiTAC:
@ -343,7 +344,7 @@ public struct Tekkon {
}
/// String
/// UniChar
/// UniChar
///
/// 調
/// - Parameters:
@ -356,7 +357,7 @@ public struct Tekkon {
intonation = Phonabet(theTone)
}
} else {
// romajiBuffer
// romajiBuffer
if romajiBuffer.count > 5 {
romajiBuffer = String(romajiBuffer.dropFirst())
}
@ -364,12 +365,12 @@ public struct Tekkon {
receiveSequence(romajiBufferBackup, isRomaji: true)
romajiBuffer = romajiBufferBackup
}
default: receiveKey(fromPhonabet: translate(key: String(input)))
default: receiveKey(fromPhonabet: translate(key: input))
}
}
/// UniChar
/// UniChar String
/// UniChar String
///
/// 調
/// - Parameters:
@ -474,7 +475,7 @@ public struct Tekkon {
}
}
/// 調調
/// 調調
/// - Parameters:
/// - withNothingElse: 調
public func hasToneMarker(withNothingElse: Bool = false) -> Bool {
@ -493,11 +494,11 @@ public struct Tekkon {
// MARK: - Parser Processings
//
//
/// String
///
///
///
/// - Parameters:
/// - key: String
mutating func translate(key: String = "") -> String {
@ -506,12 +507,12 @@ public struct Tekkon {
return Tekkon.mapQwertyDachen[key] ?? ""
case .ofDachen26:
return handleDachen26(key: key)
case .ofEten:
return Tekkon.mapQwertyEtenTraditional[key] ?? ""
case .ofETen:
return Tekkon.mapQwertyETenTraditional[key] ?? ""
case .ofHsu:
return handleHsu(key: key)
case .ofEten26:
return handleEten26(key: key)
case .ofETen26:
return handleETen26(key: key)
case .ofIBM:
return Tekkon.mapQwertyIBM[key] ?? ""
case .ofMiTAC:
@ -521,19 +522,18 @@ public struct Tekkon {
case .ofFakeSeigyou:
return Tekkon.mapFakeSeigyou[key] ?? ""
case .ofHanyuPinyin, .ofSecondaryPinyin, .ofYalePinyin, .ofHualuoPinyin, .ofUniversalPinyin:
break //
break //
}
return ""
}
///
///
///
///
/// - Parameters:
/// - key: String
mutating func handleEten26(key: String = "") -> String {
var strReturn = ""
strReturn = Tekkon.mapEten26StaticKeys[key] ?? ""
mutating func handleETen26(key: String = "") -> String {
var strReturn = Tekkon.mapETen26StaticKeys[key] ?? ""
let incomingPhonabet = Phonabet(strReturn)
switch key {
@ -612,16 +612,15 @@ public struct Tekkon {
///
///
///
///
/// - Parameters:
/// - key: String
mutating func handleHsu(key: String = "") -> String {
var strReturn = ""
strReturn = Tekkon.mapHsuStaticKeys[key] ?? ""
var strReturn = Tekkon.mapHsuStaticKeys[key] ?? ""
let incomingPhonabet = Phonabet(strReturn)
if key == " ", value == "" {
consonant = ""
consonant.clear()
vowel = ""
}
@ -719,7 +718,7 @@ public struct Tekkon {
consonant.selfReplace("", "")
}
if consonant == "", semivowel.isEmpty, vowel.isEmpty {
consonant = ""
consonant.clear()
vowel = ""
}
}
@ -736,12 +735,11 @@ public struct Tekkon {
///
///
///
///
/// - Parameters:
/// - key: String
mutating func handleDachen26(key: String = "") -> String {
var strReturn = ""
strReturn = Tekkon.mapDachenCP26StaticKeys[key] ?? ""
var strReturn = Tekkon.mapDachenCP26StaticKeys[key] ?? ""
switch key {
case "e": if isPronouncable { intonation = "ˊ" } else { consonant = "" }
@ -759,11 +757,11 @@ public struct Tekkon {
case "w": if consonant.isEmpty || consonant == "" { consonant = "" } else { consonant = "" }
case "m":
if semivowel == "", vowel != "" {
semivowel = ""
semivowel.clear()
vowel = ""
} else if semivowel != "", vowel == "" {
semivowel = ""
vowel = ""
vowel.clear()
} else if !semivowel.isEmpty {
vowel = ""
} else {
@ -771,13 +769,13 @@ public struct Tekkon {
}
case "u":
if semivowel == "", vowel != "" {
semivowel = ""
semivowel.clear()
vowel = ""
} else if semivowel != "", vowel == "" {
semivowel = ""
} else if semivowel == "", vowel == "" {
semivowel = ""
vowel = ""
semivowel.clear()
vowel.clear()
} else if !semivowel.isEmpty {
vowel = ""
} else {
@ -834,6 +832,9 @@ public struct Tekkon {
return targetConverted
}
/// 調 1
/// - Parameters:
/// - target: String
static func cnvHanyuPinyinToTextbookStyle(target: String) -> String {
var targetConverted = target
for pair in arrHanyuPinyinTextbookStyleConversionTable {
@ -1296,14 +1297,14 @@ public struct Tekkon {
///
/// 便 validity check
/// ////
static let mapEten26StaticKeys: [String: String] = [
static let mapETen26StaticKeys: [String: String] = [
"a": "", "b": "", "c": "", "d": "", "e": "", "f": "", "g": "", "h": "", "i": "", "j": "", "k": "",
"l": "", "m": "", "n": "", "o": "", "p": "", "q": "", "r": "", "s": "", "t": "", "u": "", "v": "",
"w": "", "x": "", "y": "", "z": "", " ": " ",
]
///
static let mapQwertyEtenTraditional: [String: String] = [
static let mapQwertyETenTraditional: [String: String] = [
"'": "", ",": "", "-": "", ".": "", "/": "", "0": "", "1": "˙", "2": "ˊ", "3": "ˇ", "4": "ˋ", "7": "",
"8": "", "9": "", ";": "", "=": "", "a": "", "b": "", "c": "", "d": "", "e": "", "f": "", "g": "",
"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 InputMethodKit
///
private let kMinKeyLabelSize: CGFloat = 10
///
private var ctlCandidateCurrent = ctlCandidateUniversal.init(.horizontal)
@objc(ctlInputMethod)
///
///
/// IMKInputController
///
/// / IMKInputController
/// 調
/// - Remark: IMKServer
/// IMKInputController
@objc(ctlInputMethod) // ObjC IMK ObjC
class ctlInputMethod: IMKInputController {
@objc static var areWeDeleting = false
/// 使使
static var areWeDeleting = false
///
static let tooltipController = TooltipController()
// MARK: -
private var currentClient: Any?
/// 調
private var keyHandler: KeyHandler = .init()
///
private var state: InputState = .Empty()
// KeyHandler 調 KeyHandler
// InputState ctlInputMethod
// keyHandler InputSignal
// currentKeyHandler KeyHandler
// currentKeyHandler
static var currentKeyHandler: KeyHandler = .init()
@objc static var currentInputMode = mgrPrefs.mostRecentInputMode
// MARK: -
// MARK: - Keyboard Layout Specifier
@objc func setKeyLayout() {
if let client = currentClient {
(client as? IMKTextInput)?.overrideKeyboard(withKeyboardNamed: mgrPrefs.basicKeyboardLayout)
}
///
func setKeyLayout() {
client().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!) {
super.init(server: server, delegate: delegate, client: inputClient)
keyHandler.delegate = self
@ -70,47 +87,44 @@ class ctlInputMethod: IMKInputController {
resetKeyHandler()
}
// MARK: - KeyHandler Reset Command
// MARK: - IMKStateSetting
func resetKeyHandler(client sender: Any? = nil) {
keyHandler.clear()
if let client = sender as? IMKTextInput {
handle(state: InputState.Empty(), client: client)
} else if let currentClient = currentClient {
handle(state: InputState.Empty(), client: currentClient)
}
}
// MARK: - IMKStateSetting protocol methods
override func activateServer(_ client: Any!) {
///
/// - Parameter sender: 使
override func activateServer(_ sender: Any!) {
_ = sender //
UserDefaults.standard.synchronize()
// reset the state
currentClient = client
keyHandler.clear()
keyHandler.ensureParser()
if let bundleCheckID = (client as? IMKTextInput)?.bundleIdentifier() {
if bundleCheckID != Bundle.main.bundleIdentifier {
// Override the keyboard layout to the basic one.
setKeyLayout()
handle(state: .Empty(), client: client)
}
}
///
/// macOS
if client().bundleIdentifier() != Bundle.main.bundleIdentifier {
// Override the keyboard layout to the basic one.
setKeyLayout()
handle(state: .Empty())
} //
(NSApp.delegate as? AppDelegate)?.checkForUpdate()
}
override func deactivateServer(_ client: Any!) {
///
/// - Parameter sender: 使
override func deactivateServer(_ sender: Any!) {
_ = sender //
keyHandler.clear()
currentClient = nil
handle(state: .Empty(), client: client)
handle(state: .Deactivated(), client: client)
handle(state: .Empty())
handle(state: .Deactivated())
}
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
switch newInputMode {
case InputMode.imeModeCHS:
@ -126,32 +140,47 @@ class ctlInputMethod: IMKInputController {
UserDefaults.standard.synchronize()
keyHandler.clear()
keyHandler.inputMode = newInputMode
if let bundleCheckID = (client as? IMKTextInput)?.bundleIdentifier() {
if bundleCheckID != Bundle.main.bundleIdentifier {
// Remember to override the keyboard layout again -- treat this as an activate event.
setKeyLayout()
handle(state: .Empty(), client: client)
}
}
///
/// macOS
if client().bundleIdentifier() != Bundle.main.bundleIdentifier {
// Remember to override the keyboard layout again -- treat this as an activate event.
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 {
_ = sender // Stop clang-format from ruining the parameters of this function.
_ = sender //
let events: NSEvent.EventTypeMask = [.keyDown, .flagsChanged]
return Int(events.rawValue)
}
/// NSEvent
/// - Parameters:
/// - event:
/// - sender: 使
/// - Returns: `true` IMK`false`
@objc(handleEvent:client:) override func handle(_ event: NSEvent!, client sender: Any!) -> Bool {
// flags使 KeyHandler
// flags
// event.type == .flagsChanged return false
// NSInternalInconsistencyException
_ = sender //
/// flags使 KeyHandler
/// flags
/// event.type == .flagsChanged return false
/// NSInternalInconsistencyException
if event.type == .flagsChanged {
return false
}
@ -160,18 +189,15 @@ class ctlInputMethod: IMKInputController {
ctlInputMethod.areWeDeleting = event.modifierFlags.contains([.shift, .command])
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
)
let isTypingVertical =
(attributes?["IMKTextOrientation"] as? NSNumber)?.intValue == 0 || false
if client.bundleIdentifier()
if client().bundleIdentifier()
== "org.atelierInmu.vChewing.vChewingPhraseEditor"
{
IME.areWeUsingOurOwnPhraseEditor = true
@ -187,276 +213,202 @@ class ctlInputMethod: IMKInputController {
return false
}
/// 調
/// result bool IMK
let result = keyHandler.handle(input: input, state: state) { newState in
self.handle(state: newState, client: client)
self.handle(state: newState)
} errorCallback: {
clsSFX.beep()
}
return result
}
// App Ctrl+Enter / Shift+Enter
// handle(event:) Event
// commitComposition
/// App Ctrl+Enter / Shift+Enter
/// handle(event:) Event
/// commitComposition
/// - Parameter sender: 使
override func commitComposition(_ sender: Any!) {
resetKeyHandler(client: sender)
}
//
override func composedString(_ sender: Any!) -> Any! {
_ = sender // Stop clang-format from ruining the parameters of this function.
return (state as? InputState.NotEmpty)?.composingBuffer ?? ""
_ = sender //
if let state = state as? InputState.NotEmpty {
/// 調
handle(state: InputState.Committing(textToCommit: state.composingBuffer))
}
resetKeyHandler()
}
}
// MARK: - State Handling
// MARK: - 調 (State Handling)
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
if let newState = newState as? InputState.Deactivated {
handle(state: newState, previous: previous, client: client)
} else if let newState = newState as? InputState.Empty {
handle(state: newState, previous: previous, client: client)
} else if let newState = newState as? InputState.EmptyIgnoringPreviousState {
handle(state: newState, previous: previous, client: client)
} else if let newState = newState as? InputState.Committing {
handle(state: newState, previous: previous, client: client)
} else if let newState = newState as? InputState.Inputting {
handle(state: newState, previous: previous, client: client)
} else if let newState = newState as? InputState.Marking {
handle(state: newState, previous: previous, client: client)
} else if let newState = newState as? InputState.ChoosingCandidate {
handle(state: newState, previous: previous, client: client)
} else if let newState = newState as? InputState.AssociatedPhrases {
handle(state: newState, previous: previous, client: client)
} else if let newState = newState as? InputState.SymbolTable {
handle(state: newState, previous: previous, client: client)
switch newState {
case let newState as InputState.Deactivated:
handle(state: newState, previous: prevState)
case let newState as InputState.Empty:
handle(state: newState, previous: prevState)
case let newState as InputState.EmptyIgnoringPreviousState:
handle(state: newState, previous: prevState)
case let newState as InputState.Committing:
handle(state: newState, previous: prevState)
case let newState as InputState.Inputting:
handle(state: newState, previous: prevState)
case let newState as InputState.Marking:
handle(state: newState, previous: prevState)
case let newState as InputState.ChoosingCandidate:
handle(state: newState, previous: prevState)
case let newState as InputState.AssociatedPhrases:
handle(state: newState, previous: prevState)
case let newState as InputState.SymbolTable:
handle(state: newState, previous: prevState)
default: break
}
}
private func commit(text: String, client: Any!) {
func kanjiConversionIfRequired(_ text: String) -> String {
if keyHandler.inputMode == InputMode.imeModeCHT {
if !mgrPrefs.chineseConversionEnabled, mgrPrefs.shiftJISShinjitaiOutputEnabled {
return vChewingKanjiConverter.cnvTradToJIS(text)
}
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
/// .NotEmpty()
private func setInlineDisplayWithCursor() {
guard let state = state as? InputState.NotEmpty else {
clearInlineDisplay()
return
}
/// 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 {
return
}
var bufferOutput = ""
//
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)
client().insertText(
buffer, replacementRange: NSRange(location: NSNotFound, length: NSNotFound)
)
}
private func handle(state: InputState.Deactivated, previous: InputState, client: Any?) {
_ = state // Stop clang-format from ruining the parameters of this function.
currentClient = nil
private func handle(state: InputState.Deactivated, previous: InputState) {
_ = state //
ctlCandidateCurrent.delegate = nil
ctlCandidateCurrent.visible = false
hideTooltip()
if let previous = previous as? InputState.NotEmpty {
commit(text: previous.composingBuffer, client: client)
commit(text: previous.composingBuffer)
}
(client as? IMKTextInput)?.setMarkedText(
"", selectionRange: NSRange(location: 0, length: 0),
replacementRange: NSRange(location: NSNotFound, length: NSNotFound)
)
clearInlineDisplay()
}
private func handle(state: InputState.Empty, previous: InputState, client: Any?) {
_ = state // Stop clang-format from ruining the parameters of this function.
private func handle(state: InputState.Empty, previous: InputState) {
_ = state //
ctlCandidateCurrent.visible = false
hideTooltip()
guard let client = client as? IMKTextInput else {
return
}
if let previous = previous as? InputState.NotEmpty,
!(state is InputState.EmptyIgnoringPreviousState)
{
commit(text: previous.composingBuffer, client: client)
commit(text: previous.composingBuffer)
}
client.setMarkedText(
"", selectionRange: NSRange(location: 0, length: 0),
replacementRange: NSRange(location: NSNotFound, length: NSNotFound)
)
clearInlineDisplay()
}
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.
_ = previous // Stop clang-format from ruining the parameters of this function.
_ = state //
_ = previous //
ctlCandidateCurrent.visible = false
hideTooltip()
guard let client = client as? IMKTextInput else {
return
}
client.setMarkedText(
"", selectionRange: NSRange(location: 0, length: 0),
replacementRange: NSRange(location: NSNotFound, length: NSNotFound)
)
clearInlineDisplay()
}
private func handle(state: InputState.Committing, previous: InputState, client: Any?) {
_ = previous // Stop clang-format from ruining the parameters of this function.
private func handle(state: InputState.Committing, previous: InputState) {
_ = previous //
ctlCandidateCurrent.visible = false
hideTooltip()
guard let client = client as? IMKTextInput else {
return
let textToCommit = state.textToCommit
if !textToCommit.isEmpty {
commit(text: textToCommit)
}
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)
)
clearInlineDisplay()
}
private func handle(state: InputState.Inputting, previous: InputState, client: Any?) {
_ = previous // Stop clang-format from ruining the parameters of this function.
private func handle(state: InputState.Inputting, previous: InputState) {
_ = previous //
ctlCandidateCurrent.visible = false
hideTooltip()
guard let client = client as? IMKTextInput else {
return
let textToCommit = state.textToCommit
if !textToCommit.isEmpty {
commit(text: textToCommit)
}
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)
)
setInlineDisplayWithCursor()
if !state.tooltip.isEmpty {
show(
tooltip: state.tooltip, composingBuffer: state.composingBuffer,
cursorIndex: state.cursorIndex, client: client
cursorIndex: state.cursorIndex
)
}
}
private func handle(state: InputState.Marking, previous: InputState, client: Any?) {
_ = previous // Stop clang-format from ruining the parameters of this function.
private func handle(state: InputState.Marking, previous: InputState) {
_ = previous //
ctlCandidateCurrent.visible = false
guard let client = client as? IMKTextInput else {
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)
)
setInlineDisplayWithCursor()
if state.tooltip.isEmpty {
hideTooltip()
} else {
show(
tooltip: state.tooltip, composingBuffer: state.composingBuffer,
cursorIndex: state.markerIndex, client: client
cursorIndex: state.markerIndex
)
}
}
private func handle(state: InputState.ChoosingCandidate, previous: InputState, client: Any?) {
_ = previous // Stop clang-format from ruining the parameters of this function.
private func handle(state: InputState.ChoosingCandidate, previous: InputState) {
_ = previous //
hideTooltip()
guard let client = client as? IMKTextInput else {
ctlCandidateCurrent.visible = false
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)
setInlineDisplayWithCursor()
show(candidateWindowWith: state)
}
private func handle(state: InputState.SymbolTable, previous: InputState, client: Any?) {
_ = previous // Stop clang-format from ruining the parameters of this function.
private func handle(state: InputState.SymbolTable, previous: InputState) {
_ = previous //
hideTooltip()
guard let client = client as? IMKTextInput else {
ctlCandidateCurrent.visible = false
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)
setInlineDisplayWithCursor()
show(candidateWindowWith: state)
}
private func handle(state: InputState.AssociatedPhrases, previous: InputState, client: Any?) {
_ = previous // Stop clang-format from ruining the parameters of this function.
private func handle(state: InputState.AssociatedPhrases, previous: InputState) {
_ = previous //
hideTooltip()
guard let client = client as? IMKTextInput else {
ctlCandidateCurrent.visible = false
return
}
client.setMarkedText(
"", selectionRange: NSRange(location: 0, length: 0),
replacementRange: NSRange(location: NSNotFound, length: NSNotFound)
)
show(candidateWindowWith: state, client: client)
clearInlineDisplay()
show(candidateWindowWith: state)
}
}
// MARK: -
extension ctlInputMethod {
private func show(candidateWindowWith state: InputState, client: Any!) {
private func show(candidateWindowWith state: InputState) {
var isTypingVertical: Bool {
if let state = state as? InputState.ChoosingCandidate {
return state.isTypingVertical
@ -518,7 +470,7 @@ extension ctlInputMethod {
? "Sarasa Term Slab SC" : "Sarasa Term Slab TC"
var finalReturnFont =
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 let name = name {
return NSFont(name: name, size: size) ?? finalReturnFont
@ -543,7 +495,6 @@ extension ctlInputMethod {
ctlCandidateCurrent.delegate = self
ctlCandidateCurrent.reloadData()
currentClient = client
ctlCandidateCurrent.visible = true
@ -558,7 +509,7 @@ extension ctlInputMethod {
}
while lineHeightRect.origin.x == 0, lineHeightRect.origin.y == 0, cursor >= 0 {
(client as? IMKTextInput)?.attributes(
client().attributes(
forCharacterIndex: cursor, lineHeightRectangle: &lineHeightRect
)
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 cursor = cursorIndex
if cursor == composingBuffer.count, cursor != 0 {
cursor -= 1
}
while lineHeightRect.origin.x == 0, lineHeightRect.origin.y == 0, cursor >= 0 {
(client as? IMKTextInput)?.attributes(
client().attributes(
forCharacterIndex: cursor, lineHeightRectangle: &lineHeightRect
)
cursor -= 1
@ -644,7 +595,7 @@ extension ctlInputMethod: KeyHandlerDelegate {
extension ctlInputMethod: ctlCandidateDelegate {
func candidateCountForController(_ controller: ctlCandidate) -> Int {
_ = controller // Stop clang-format from ruining the parameters of this function.
_ = controller //
if let state = state as? InputState.ChoosingCandidate {
return state.candidates.count
} else if let state = state as? InputState.AssociatedPhrases {
@ -656,7 +607,7 @@ extension ctlInputMethod: ctlCandidateDelegate {
func ctlCandidate(_ controller: ctlCandidate, candidateAtIndex index: Int)
-> String
{
_ = controller // Stop clang-format from ruining the parameters of this function.
_ = controller //
if let state = state as? InputState.ChoosingCandidate {
return state.candidates[index]
} else if let state = state as? InputState.AssociatedPhrases {
@ -666,21 +617,19 @@ extension ctlInputMethod: ctlCandidateDelegate {
}
func ctlCandidate(_ controller: ctlCandidate, didSelectCandidateAtIndex index: Int) {
_ = controller // Stop clang-format from ruining the parameters of this function.
let client = currentClient
_ = controller //
if let state = state as? InputState.SymbolTable,
let node = state.node.children?[index]
{
if let children = node.children, !children.isEmpty {
handle(state: .Empty(), client: client) //
handle(state: .Empty()) //
handle(
state: .SymbolTable(node: node, isTypingVertical: state.isTypingVertical),
client: currentClient
state: .SymbolTable(node: node, isTypingVertical: state.isTypingVertical)
)
} else {
handle(state: .Committing(poppedText: node.title), client: client)
handle(state: .Empty(), client: client)
handle(state: .Committing(textToCommit: node.title))
handle(state: .Empty())
}
return
}
@ -694,33 +643,33 @@ extension ctlInputMethod: ctlCandidateDelegate {
if mgrPrefs.useSCPCTypingMode {
keyHandler.clear()
let composingBuffer = inputting.composingBuffer
handle(state: .Committing(poppedText: composingBuffer), client: client)
handle(state: .Committing(textToCommit: composingBuffer))
if mgrPrefs.associatedPhrasesEnabled,
let associatePhrases = keyHandler.buildAssociatePhraseState(
withKey: composingBuffer, isTypingVertical: state.isTypingVertical
), !associatePhrases.candidates.isEmpty
{
handle(state: associatePhrases, client: client)
handle(state: associatePhrases)
} else {
handle(state: .Empty(), client: client)
handle(state: .Empty())
}
} else {
handle(state: inputting, client: client)
handle(state: inputting)
}
return
}
if let state = state as? InputState.AssociatedPhrases {
let selectedValue = state.candidates[index]
handle(state: .Committing(poppedText: selectedValue), client: currentClient)
handle(state: .Committing(textToCommit: selectedValue))
if mgrPrefs.associatedPhrasesEnabled,
let associatePhrases = keyHandler.buildAssociatePhraseState(
withKey: selectedValue, isTypingVertical: state.isTypingVertical
), !associatePhrases.candidates.isEmpty
{
handle(state: associatePhrases, client: client)
handle(state: associatePhrases)
} else {
handle(state: .Empty(), client: client)
handle(state: .Empty())
}
}
}

View File

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

View File

@ -28,10 +28,34 @@ import Cocoa
// The namespace of this input method.
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 {
static let arrSupportedLocales = ["en", "zh-Hant", "zh-Hans", "ja"]
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: -
static var areWeUsingOurOwnPhraseEditor: Bool = false
@ -40,10 +64,10 @@ public enum IME {
static func getInputMode(isReversed: Bool = false) -> InputMode {
if isReversed {
return (ctlInputMethod.currentKeyHandler.inputMode == InputMode.imeModeCHT)
return (IME.currentInputMode == InputMode.imeModeCHT)
? InputMode.imeModeCHS : InputMode.imeModeCHT
} else {
return ctlInputMethod.currentKeyHandler.inputMode
return IME.currentInputMode
}
}
@ -64,7 +88,7 @@ public enum IME {
// MARK: - Initializing Language Models.
static func initLangModels(userOnly: Bool) {
// mgrLangModel loadUserPhrases dataFolderPath
// mgrLangModel loadUserPhrases dataFolderPath
//
//
mgrLangModel.loadUserAssociatesData()

View File

@ -78,10 +78,10 @@ private let kMinCandidateListTextSize: CGFloat = 12
private let kMaxCandidateListTextSize: CGFloat = 196
// 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)
// 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 kMinComposingBufferSize = 10
private let kMaxComposingBufferSize = 40
@ -176,9 +176,9 @@ struct ComposingBufferSize {
enum MandarinParser: Int {
case ofStandard = 0
case ofEten = 1
case ofETen = 1
case ofHsu = 2
case ofEten26 = 3
case ofETen26 = 3
case ofIBM = 4
case ofMiTAC = 5
case ofFakeSeigyou = 6
@ -193,11 +193,11 @@ enum MandarinParser: Int {
switch self {
case .ofStandard:
return "Standard"
case .ofEten:
case .ofETen:
return "ETen"
case .ofHsu:
return "Hsu"
case .ofEten26:
case .ofETen26:
return "ETen26"
case .ofIBM:
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.
*/
// NOTE: We still keep some of the comments left by Zonble,
// regardless that he is not in charge of this Swift module
import Foundation
//
@ -39,29 +36,25 @@ private var lmSymbols = vChewing.LMCoreNS(
)
extension vChewing {
/// LMInstantiator is a facade for managing a set of models including
/// the input method language model, user phrases and excluded phrases.
/// LMInstantiatorLMI
/// LanguageModel 使
///
///
/// It is the primary model class that the input controller and grammar builder
/// of vChewing talks to. When the grammar builder starts to build a sentence
/// from a series of BPMF readings, it passes the readings to the model to see
/// if there are valid unigrams, and use returned unigrams to produce the final
/// results.
/// LMI 調
/// LMI
///
///
/// LMInstantiator combine and transform the unigrams from the primary language
/// model and user phrases. The process is
/// LMI
///
/// 1) Get the original unigrams.
/// 2) Drop the unigrams whose value is contained in the exclusion map.
/// 3) Replace the values of the unigrams using the phrase replacement map.
/// 4) Drop the duplicated phrases from the generated unigram array.
/// 1.
/// 2.
/// 3.
/// 4.
///
/// The controller can ask the model to load the primary input method language
/// model while launching and to load the user phrases anytime if the custom
/// files are modified. It does not keep the reference of the data pathes but
/// you have to pass the paths when you ask it to load.
/// LMI LMI
///
public class LMInstantiator: Megrez.LanguageModel {
//
//
public var isPhraseReplacementEnabled = false
public var isCNSEnabled = false
public var isSymbolEnabled = false
@ -76,9 +69,9 @@ extension vChewing {
/// LMCoreEX 2010-2013 mac
/// LMCoreNS plist
//
/// Reverse
/// Reverse
//
// Reverse
// Reverse
var lmCore = LMCoreNS(
reverse: false, consolidate: false, defaultScore: -9.9, forceDefaultScore: false
)
@ -100,10 +93,10 @@ extension vChewing {
var lmReplacements = LMReplacments()
var lmAssociates = LMAssociates()
//
//
override init() {}
// 調
// 調
public var isLanguageModelLoaded: Bool { lmCore.isLoaded() }
public func loadLanguageModel(path: String) {
@ -194,12 +187,12 @@ extension vChewing {
// MARK: - Core Functions (Public)
/// Not implemented since we do not have data to provide bigram function.
///
// public func bigramsForKeys(preceedingKey: String, key: String) -> [Megrez.Bigram] { }
/// Returns a list of available unigram for the given key.
/// @param key:String represents the BPMF reading or a symbol key.
/// For instance, it you pass "ˇ", it returns "" and other possible candidates.
/// LMI
/// - Parameter key:
/// - Returns:
override open func unigramsFor(key: String) -> [Megrez.Unigram] {
if key == " " {
///
@ -267,6 +260,11 @@ extension vChewing {
// MARK: - Core Functions (Private)
///
/// - Parameters:
/// - unigrams:
/// - filteredPairs:
/// - Returns:
func filterAndTransform(
unigrams: [Megrez.Unigram],
filter filteredPairs: Set<Megrez.KeyValuePair>

View File

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

View File

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

View File

@ -29,27 +29,34 @@ extension vChewing {
public class LMUserOverride {
// MARK: - Private Structures
struct Override {
// class
class Override {
var count: Int = 0
var timestamp: Double = 0.0
}
struct Observation {
class Observation {
var count: Int = 0
var overrides: [String: Override] = [:]
mutating func update(candidate: String, timestamp: Double) {
func update(candidate: String, timestamp: Double) {
count += 1
if var neta = overrides[candidate] {
if let neta = overrides[candidate] {
neta.timestamp = timestamp
neta.count += 1
overrides[candidate] = neta
}
}
}
struct KeyObservationPair {
class KeyObservationPair {
var key: String
var observation: Observation
init(key: String, observation: Observation) {
self.key = key
self.observation = observation
}
}
// MARK: - Main
@ -74,7 +81,7 @@ extension vChewing {
let key = convertKeyFrom(walkedNodes: walkedNodes, cursorIndex: cursorIndex)
guard mutLRUMap[key] != nil else {
var observation: Observation = .init()
let observation: Observation = .init()
observation.update(candidate: candidate, timestamp: timestamp)
let koPair = KeyObservationPair(key: key, observation: observation)
mutLRUMap[key] = koPair
@ -87,7 +94,7 @@ extension vChewing {
IME.prtDebugIntel("UOM: Observation finished with new observation: \(key)")
return
}
if var theNeta = mutLRUMap[key] {
if let theNeta = mutLRUMap[key] {
theNeta.observation.update(candidate: candidate, timestamp: timestamp)
mutLRUList.insert(theNeta, at: 0)
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
private var gLangModelCHS = vChewing.LMInstantiator()
private var gLangModelCHT = vChewing.LMInstantiator()
@ -37,7 +37,7 @@ private var gUserOverrideModelCHS = vChewing.LMUserOverride()
private var gUserOverrideModelCHT = vChewing.LMUserOverride()
enum mgrLangModel {
/// fileprivate
/// fileprivate
public static var lmCHS: vChewing.LMInstantiator { gLangModelCHS }
public static var lmCHT: vChewing.LMInstantiator { gLangModelCHT }
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".
//
//
//
var folderPath = folderPath // Convert the incoming constant to a variable.
if isFolder.boolValue {
folderPath?.ensureTrailingSlash()
@ -351,7 +351,7 @@ enum mgrLangModel {
return true
}
// MARK: - 使 mgrPrefs
// MARK: - 使 mgrPrefs
// mgrPrefs

View File

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

View File

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

View File

@ -49,9 +49,7 @@ extension Megrez {
mutating func insert(node: Node, length: Int) {
let length = abs(length) //
mutLengthNodeMap[length] = node
if length > mutMaximumLength {
mutMaximumLength = length
}
mutMaximumLength = max(mutMaximumLength, length)
}
///
@ -60,21 +58,19 @@ extension Megrez {
mutating func removeNodeOfLengthGreaterThan(_ length: Int) {
let length = abs(length) //
if length > mutMaximumLength { return }
var max = 0
var lenMax = 0
var removalList: [Int: Megrez.Node] = [:]
for key in mutLengthNodeMap.keys {
if key > length {
removalList[key] = mutLengthNodeMap[key]
} else {
if key > max {
max = key
}
lenMax = max(lenMax, key)
}
}
for key in removalList.keys {
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 {
///
public class Node: CustomStringConvertible {
public class Node {
///
private let mutLM: LanguageModel = .init()
///
@ -109,23 +109,16 @@ extension Megrez {
for neta in precedingKeyValues {
let bigrams = mutPrecedingBigramMap[neta] ?? []
for bigram in bigrams {
if bigram.score > max {
if let valRetrieved = mutValueUnigramIndexMap[bigram.keyValue.value] {
newIndex = valRetrieved as Int
max = bigram.score
}
guard bigram.score > max else { continue }
if let valRetrieved = mutValueUnigramIndexMap[bigram.keyValue.value] {
newIndex = valRetrieved as Int
max = bigram.score
}
}
}
}
if mutScore != max {
mutScore = max
}
if mutSelectedUnigramIndex != newIndex {
mutSelectedUnigramIndex = newIndex
}
mutScore = max
mutSelectedUnigramIndex = newIndex
}
///
@ -170,13 +163,5 @@ extension Megrez {
}
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 {
/// 使
/// 使
open class LanguageModel {
public init() {}
// Swift
// Swift
///
open func unigramsFor(key: String) -> [Megrez.Unigram] {

View File

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

View File

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

View File

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

View File

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

View File

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