Pre Merge pull request !66 from ShikiSuen/upd/1.8.3
This commit is contained in:
commit
dc74ba547f
2
AUTHORS
2
AUTHORS
|
@ -16,7 +16,7 @@ $ Contributors and volunteers of the upstream repo, having no responsibility in
|
|||
- Zonble Yang:
|
||||
- McBopomofo for macOS 2.x architect, especially state-based IME behavior management.
|
||||
- Voltaire candidate window MK2 (massively modified as MK3 in vChewing by Shiki Suen).
|
||||
- InputHandler.
|
||||
- InputSignal (previously "KeyHandlerInput").
|
||||
- Notifier window and Tooltip UI.
|
||||
- NSStringUtils and FSEventStreamHelper.
|
||||
- App-style installer (only preserved for developer purposes).
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 32ae1957bdbbb1daabf108f6352708677aec27fa
|
||||
Subproject commit 91a12bb5a355b56456beceec42cb001dea036b2b
|
|
@ -50,7 +50,7 @@ enum KeyCode: UInt16 {
|
|||
case kVolumeUp = 72
|
||||
case kVolumeDown = 73
|
||||
case kMute = 74
|
||||
case kLineFeed = 76 // Another keyCode to identify the Enter Key.
|
||||
case kLineFeed = 76 // Another keyCode to identify the Enter Key, typable by Fn+Enter.
|
||||
case kF18 = 79
|
||||
case kF19 = 80
|
||||
case kF20 = 90
|
||||
|
|
|
@ -103,7 +103,7 @@ class KeyHandler {
|
|||
///
|
||||
/// 威注音對游標前置與游標後置模式採取的候選字節點陣列抓取方法是分離的,且不使用 Node Crossing。
|
||||
var actualCandidateCursor: Int {
|
||||
mgrPrefs.useRearCursorMode ? min(compositorCursorIndex, compositorLength - 1) : max(compositorCursorIndex, 1)
|
||||
mgrPrefs.useRearCursorMode ? min(compositor.cursor, compositor.length - 1) : max(compositor.cursor, 1)
|
||||
}
|
||||
|
||||
/// 利用給定的讀音鏈來試圖爬取最接近的組字結果(最大相似度估算)。
|
||||
|
@ -161,10 +161,10 @@ class KeyHandler {
|
|||
/// 然後再將對應的節錨內的節點標記為「已經手動選字過」。
|
||||
/// - Parameters:
|
||||
/// - value: 給定之候選字字串。
|
||||
/// - respectCursorPushing: 若該選項為 true,則會在選字之後始終將游標推送至選字厚的節錨的前方。
|
||||
/// - respectCursorPushing: 若該選項為 true,則會在選字之後始終將游標推送至選字後的節錨的前方。
|
||||
func fixNode(candidate: (String, String), respectCursorPushing: Bool = true) {
|
||||
let theCandidate: Megrez.KeyValuePaired = .init(key: candidate.0, value: candidate.1)
|
||||
let adjustedCursor = max(0, min(actualCandidateCursor + (mgrPrefs.useRearCursorMode ? 1 : 0), compositorLength))
|
||||
let adjustedCursor = max(0, min(actualCandidateCursor + (mgrPrefs.useRearCursorMode ? 1 : 0), compositor.length))
|
||||
// 開始讓半衰模組觀察目前的狀況。
|
||||
let selectedNode: Megrez.NodeAnchor = compositor.fixNodeWithCandidate(theCandidate, at: adjustedCursor)
|
||||
// 不要針對逐字選字模式啟用臨時半衰記憶模型。
|
||||
|
@ -196,7 +196,7 @@ class KeyHandler {
|
|||
// 開始爬軌。
|
||||
walk()
|
||||
|
||||
/// 若偏好設定內啟用了相關選項,則會在選字之後始終將游標推送至選字厚的節錨的前方。
|
||||
/// 若偏好設定內啟用了相關選項,則會在選字之後始終將游標推送至選字後的節錨的前方。
|
||||
if mgrPrefs.moveCursorAfterSelectingCandidate, respectCursorPushing {
|
||||
compositor.jumpCursorBySpan(to: .front)
|
||||
}
|
||||
|
@ -254,7 +254,7 @@ class KeyHandler {
|
|||
/// 向半衰引擎詢問可能的選字建議。拿到的結果會是一個單元圖陣列。
|
||||
func fetchSuggestedCandidates() -> [Megrez.Unigram] {
|
||||
currentUOM.suggest(
|
||||
walkedAnchors: walkedAnchors, cursorIndex: compositorCursorIndex,
|
||||
walkedAnchors: walkedAnchors, cursorIndex: compositor.cursor,
|
||||
timestamp: NSDate().timeIntervalSince1970
|
||||
)
|
||||
}
|
||||
|
@ -273,7 +273,7 @@ class KeyHandler {
|
|||
IME.prtDebugIntel(
|
||||
"UOM: Suggestion retrieved, overriding the node score of the selected candidate.")
|
||||
compositor.overrideNodeScoreForSelectedCandidate(
|
||||
location: min(actualCandidateCursor + (mgrPrefs.useRearCursorMode ? 1 : 0), compositorLength),
|
||||
location: min(actualCandidateCursor + (mgrPrefs.useRearCursorMode ? 1 : 0), compositor.length),
|
||||
value: overrideValue,
|
||||
overridingScore: findHighestScore(nodeAnchors: rawAnchorsOfNodes, epsilon: kEpsilon)
|
||||
)
|
||||
|
@ -288,7 +288,7 @@ class KeyHandler {
|
|||
/// - epsilon: 半衰模組的衰減指數。
|
||||
/// - Returns: 尋獲的最高權重數值。
|
||||
func findHighestScore(nodeAnchors: [Megrez.NodeAnchor], epsilon: Double) -> Double {
|
||||
return nodeAnchors.map(\.node.highestUnigramScore).max() ?? 0 + epsilon
|
||||
nodeAnchors.map(\.node.highestUnigramScore).max() ?? 0 + epsilon
|
||||
}
|
||||
|
||||
// MARK: - Extracted methods and functions (Tekkon).
|
||||
|
@ -337,9 +337,6 @@ class KeyHandler {
|
|||
|
||||
// MARK: - Extracted methods and functions (Megrez).
|
||||
|
||||
/// 組字器是否為空。
|
||||
var isCompositorEmpty: Bool { compositor.isEmpty }
|
||||
|
||||
/// 獲取原始節錨資料陣列。
|
||||
var rawAnchorsOfNodes: [Megrez.NodeAnchor] {
|
||||
/// 警告:不要對游標前置風格使用 nodesCrossing,否則會導致游標行為與 macOS 內建注音輸入法不一致。
|
||||
|
@ -362,59 +359,6 @@ class KeyHandler {
|
|||
compositor = Megrez.Compositor(lm: currentLM, separator: "-")
|
||||
}
|
||||
|
||||
/// 自組字器獲取目前的讀音陣列。
|
||||
var currentReadings: [String] { compositor.readings }
|
||||
|
||||
/// 以給定的(讀音)索引鍵,來檢測當前主語言模型內是否有對應的資料在庫。
|
||||
func ifLangModelHasUnigrams(forKey reading: String) -> Bool {
|
||||
currentLM.hasUnigramsFor(key: reading)
|
||||
}
|
||||
|
||||
/// 在組字器的給定游標位置內插入讀音。
|
||||
func insertToCompositorAtCursor(reading: String) {
|
||||
compositor.insertReading(reading)
|
||||
}
|
||||
|
||||
/// 組字器的游標位置。
|
||||
var compositorCursorIndex: Int {
|
||||
get { compositor.cursor }
|
||||
set { compositor.cursor = newValue }
|
||||
}
|
||||
|
||||
/// 組字器的目前的長度。
|
||||
var compositorLength: Int {
|
||||
compositor.length
|
||||
}
|
||||
|
||||
/// 在組字器內,朝著與文字輸入方向相反的方向、砍掉一個與游標相鄰的讀音。
|
||||
///
|
||||
/// 在威注音的術語體系當中,「與文字輸入方向相反的方向」為向後(Rear)。
|
||||
func deleteCompositorReadingAtTheRearOfCursor() {
|
||||
compositor.dropReading(direction: .rear)
|
||||
}
|
||||
|
||||
/// 在組字器內,朝著往文字輸入方向、砍掉一個與游標相鄰的讀音。
|
||||
///
|
||||
/// 在威注音的術語體系當中,「文字輸入方向」為向前(Front)。
|
||||
func deleteCompositorReadingToTheFrontOfCursor() {
|
||||
compositor.dropReading(direction: .front)
|
||||
}
|
||||
|
||||
/// 獲取指定游標位置的鍵值長度。
|
||||
/// - Returns: 指定游標位置的鍵值長度。
|
||||
var keyLengthAtCurrentIndex: Int {
|
||||
walkedAnchors[compositorCursorIndex].node.key.split(separator: "-").count
|
||||
}
|
||||
|
||||
var nextPhrasePosition: Int {
|
||||
var nextPosition = 0
|
||||
for theAnchor in walkedAnchors {
|
||||
if nextPosition > actualCandidateCursor { break }
|
||||
nextPosition += theAnchor.spanLength
|
||||
}
|
||||
return min(nextPosition, compositorLength)
|
||||
}
|
||||
|
||||
/// 生成標點符號索引鍵。
|
||||
/// - Parameter input: 輸入的按鍵訊號。
|
||||
/// - Returns: 生成的標點符號索引鍵。
|
||||
|
|
|
@ -61,12 +61,12 @@ extension KeyHandler {
|
|||
if cancelCandidateKey {
|
||||
if (state is InputState.AssociatedPhrases)
|
||||
|| mgrPrefs.useSCPCTypingMode
|
||||
|| isCompositorEmpty
|
||||
|| compositor.isEmpty
|
||||
{
|
||||
// 如果此時發現當前組字緩衝區為真空的情況的話,
|
||||
// 就將當前的組字緩衝區析構處理、強制重設輸入狀態。
|
||||
// 否則,一個本不該出現的真空組字緩衝區會使前後方向鍵與 BackSpace 鍵失靈。
|
||||
// 所以這裡需要對 isCompositorEmpty 做判定。
|
||||
// 所以這裡需要對 compositor.isEmpty 做判定。
|
||||
clear()
|
||||
stateCallback(InputState.EmptyIgnoringPreviousState())
|
||||
} else {
|
||||
|
@ -326,12 +326,12 @@ extension KeyHandler {
|
|||
let punctuation: String = arrPunctuations.joined(separator: "")
|
||||
|
||||
var shouldAutoSelectCandidate: Bool =
|
||||
composer.inputValidityCheck(key: charCode) || ifLangModelHasUnigrams(forKey: customPunctuation)
|
||||
|| ifLangModelHasUnigrams(forKey: punctuation)
|
||||
composer.inputValidityCheck(key: charCode) || currentLM.hasUnigramsFor(key: customPunctuation)
|
||||
|| currentLM.hasUnigramsFor(key: punctuation)
|
||||
|
||||
if !shouldAutoSelectCandidate, input.isUpperCaseASCIILetterKey {
|
||||
let letter: String! = String(format: "%@%c", "_letter_", CChar(charCode))
|
||||
if ifLangModelHasUnigrams(forKey: letter) { shouldAutoSelectCandidate = true }
|
||||
if currentLM.hasUnigramsFor(key: letter) { shouldAutoSelectCandidate = true }
|
||||
}
|
||||
|
||||
if shouldAutoSelectCandidate {
|
||||
|
|
|
@ -188,7 +188,7 @@ extension KeyHandler {
|
|||
// 如果輸入法的辭典索引是漢語拼音的話,要注意上一行拿到的內容得是漢語拼音。
|
||||
|
||||
// 向語言模型詢問是否有對應的記錄。
|
||||
if !ifLangModelHasUnigrams(forKey: reading) {
|
||||
if !currentLM.hasUnigramsFor(key: reading) {
|
||||
IME.prtDebugIntel("B49C0979:語彙庫內無「\(reading)」的匹配記錄。")
|
||||
errorCallback()
|
||||
composer.clear()
|
||||
|
@ -198,7 +198,7 @@ extension KeyHandler {
|
|||
}
|
||||
|
||||
// 將該讀音插入至組字器內的軌格當中。
|
||||
insertToCompositorAtCursor(reading: reading)
|
||||
compositor.insertReading(reading)
|
||||
|
||||
// 讓組字器反爬軌格。
|
||||
let textToCommit = commitOverflownCompositionAndWalk
|
||||
|
@ -270,7 +270,7 @@ extension KeyHandler {
|
|||
if input.isSpace {
|
||||
/// 倘若沒有在偏好設定內將 Space 空格鍵設為選字窗呼叫用鍵的話………
|
||||
if !mgrPrefs.chooseCandidateUsingSpace {
|
||||
if compositorCursorIndex >= compositorLength {
|
||||
if compositor.cursor >= compositor.length {
|
||||
let composingBuffer = currentState.composingBuffer
|
||||
if !composingBuffer.isEmpty {
|
||||
stateCallback(InputState.Committing(textToCommit: composingBuffer))
|
||||
|
@ -278,8 +278,8 @@ extension KeyHandler {
|
|||
clear()
|
||||
stateCallback(InputState.Committing(textToCommit: " "))
|
||||
stateCallback(InputState.Empty())
|
||||
} else if ifLangModelHasUnigrams(forKey: " ") {
|
||||
insertToCompositorAtCursor(reading: " ")
|
||||
} else if currentLM.hasUnigramsFor(key: " ") {
|
||||
compositor.insertReading(" ")
|
||||
let textToCommit = commitOverflownCompositionAndWalk
|
||||
let inputting = buildInputtingState
|
||||
inputting.textToCommit = textToCommit
|
||||
|
@ -354,7 +354,7 @@ extension KeyHandler {
|
|||
// MARK: AbsorbedArrowKey
|
||||
|
||||
if input.isAbsorbedArrowKey || input.isExtraChooseCandidateKey || input.isExtraChooseCandidateKeyReverse {
|
||||
if input.isOptionHold, state.type == .ofInputting {
|
||||
if input.isOptionHold, state is InputState.Inputting {
|
||||
if input.isExtraChooseCandidateKey {
|
||||
return handleInlineCandidateRotation(
|
||||
state: state, reverseModifier: false, stateCallback: stateCallback, errorCallback: errorCallback
|
||||
|
@ -396,9 +396,9 @@ extension KeyHandler {
|
|||
|
||||
if input.isSymbolMenuPhysicalKey && !input.isShiftHold {
|
||||
if input.isOptionHold {
|
||||
if ifLangModelHasUnigrams(forKey: "_punctuation_list") {
|
||||
if currentLM.hasUnigramsFor(key: "_punctuation_list") {
|
||||
if composer.isEmpty {
|
||||
insertToCompositorAtCursor(reading: "_punctuation_list")
|
||||
compositor.insertReading("_punctuation_list")
|
||||
let textToCommit: String! = commitOverflownCompositionAndWalk
|
||||
let inputting = buildInputtingState
|
||||
inputting.textToCommit = textToCommit
|
||||
|
|
|
@ -54,35 +54,35 @@ extension KeyHandler {
|
|||
/// 每個節錨(NodeAnchor)都有自身的幅位長度(spanningLength),可以用來
|
||||
/// 累加、以此為依據,來校正「可見游標位置」。
|
||||
let spanningLength: Int = theAnchor.spanLength
|
||||
if readingCursorIndex + spanningLength <= compositorCursorIndex {
|
||||
if readingCursorIndex + spanningLength <= compositor.cursor {
|
||||
composedStringCursorIndex += strNodeValue.utf16.count
|
||||
readingCursorIndex += spanningLength
|
||||
} else {
|
||||
if codepointCount == spanningLength {
|
||||
var i = 0
|
||||
while i < codepointCount, readingCursorIndex < compositorCursorIndex {
|
||||
while i < codepointCount, readingCursorIndex < compositor.cursor {
|
||||
composedStringCursorIndex += arrSplit[i].utf16.count
|
||||
readingCursorIndex += 1
|
||||
i += 1
|
||||
}
|
||||
} else {
|
||||
if readingCursorIndex < compositorCursorIndex {
|
||||
if readingCursorIndex < compositor.cursor {
|
||||
composedStringCursorIndex += strNodeValue.utf16.count
|
||||
readingCursorIndex += spanningLength
|
||||
readingCursorIndex = min(readingCursorIndex, compositorCursorIndex)
|
||||
readingCursorIndex = min(readingCursorIndex, compositor.cursor)
|
||||
/// 接下來再處理這麼一種情況:
|
||||
/// 某些錨點內的當前候選字詞長度與讀音長度不相等。
|
||||
/// 但此時游標還是按照每個讀音單位來移動的,
|
||||
/// 所以需要上下文工具提示來顯示游標的相對位置。
|
||||
/// 這裡先計算一下要用在工具提示當中的顯示參數的內容。
|
||||
switch compositorCursorIndex {
|
||||
switch compositor.cursor {
|
||||
case compositor.readings.count...:
|
||||
tooltipParameterRef[0] = compositor.readings[compositor.readings.count - 1]
|
||||
tooltipParameterRef[0] = compositor.readings[compositor.cursor - 1]
|
||||
case 0:
|
||||
tooltipParameterRef[1] = compositor.readings[compositorCursorIndex]
|
||||
tooltipParameterRef[1] = compositor.readings[compositor.cursor]
|
||||
default:
|
||||
tooltipParameterRef[0] = compositor.readings[compositorCursorIndex - 1]
|
||||
tooltipParameterRef[1] = compositor.readings[compositorCursorIndex]
|
||||
tooltipParameterRef[0] = compositor.readings[compositor.cursor - 1]
|
||||
tooltipParameterRef[1] = compositor.readings[compositor.cursor]
|
||||
}
|
||||
/// 注音轉拼音
|
||||
for (i, _) in tooltipParameterRef.enumerated() {
|
||||
|
@ -301,7 +301,7 @@ extension KeyHandler {
|
|||
stateCallback: @escaping (InputStateProtocol) -> Void,
|
||||
errorCallback: @escaping () -> Void
|
||||
) -> Bool {
|
||||
if !ifLangModelHasUnigrams(forKey: customPunctuation) {
|
||||
if !currentLM.hasUnigramsFor(key: customPunctuation) {
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -313,7 +313,7 @@ extension KeyHandler {
|
|||
return true
|
||||
}
|
||||
|
||||
insertToCompositorAtCursor(reading: customPunctuation)
|
||||
compositor.insertReading(customPunctuation)
|
||||
let textToCommit = commitOverflownCompositionAndWalk
|
||||
let inputting = buildInputtingState
|
||||
inputting.textToCommit = textToCommit
|
||||
|
@ -372,7 +372,7 @@ extension KeyHandler {
|
|||
) -> Bool {
|
||||
guard state is InputState.Inputting else { return false }
|
||||
|
||||
var composingBuffer = currentReadings.joined(separator: "-")
|
||||
var composingBuffer = compositor.readings.joined(separator: "-")
|
||||
if mgrPrefs.inlineDumpPinyinInLieuOfZhuyin {
|
||||
composingBuffer = Tekkon.restoreToneOneInZhuyinKey(target: composingBuffer) // 恢復陰平標記
|
||||
composingBuffer = Tekkon.cnvPhonaToHanyuPinyin(target: composingBuffer) // 注音轉拼音
|
||||
|
@ -445,8 +445,8 @@ extension KeyHandler {
|
|||
if composer.hasToneMarker(withNothingElse: true) {
|
||||
composer.clear()
|
||||
} else if composer.isEmpty {
|
||||
if compositorCursorIndex > 0 {
|
||||
deleteCompositorReadingAtTheRearOfCursor()
|
||||
if compositor.cursor > 0 {
|
||||
compositor.dropReading(direction: .rear)
|
||||
walk()
|
||||
} else {
|
||||
IME.prtDebugIntel("9D69908D")
|
||||
|
@ -485,14 +485,14 @@ extension KeyHandler {
|
|||
return true
|
||||
}
|
||||
|
||||
guard compositorCursorIndex != compositorLength else {
|
||||
guard compositor.cursor != compositor.length else {
|
||||
IME.prtDebugIntel("9B69938D")
|
||||
errorCallback()
|
||||
stateCallback(state)
|
||||
return true
|
||||
}
|
||||
|
||||
deleteCompositorReadingToTheFrontOfCursor()
|
||||
compositor.dropReading(direction: .front)
|
||||
walk()
|
||||
let inputting = buildInputtingState
|
||||
// 這裡不用「count > 0」,因為該整數變數只要「!isEmpty」那就必定滿足這個條件。
|
||||
|
@ -544,8 +544,8 @@ extension KeyHandler {
|
|||
return true
|
||||
}
|
||||
|
||||
if compositorCursorIndex != 0 {
|
||||
compositorCursorIndex = 0
|
||||
if compositor.cursor != 0 {
|
||||
compositor.cursor = 0
|
||||
stateCallback(buildInputtingState)
|
||||
} else {
|
||||
IME.prtDebugIntel("66D97F90")
|
||||
|
@ -578,8 +578,8 @@ extension KeyHandler {
|
|||
return true
|
||||
}
|
||||
|
||||
if compositorCursorIndex != compositorLength {
|
||||
compositorCursorIndex = compositorLength
|
||||
if compositor.cursor != compositor.length {
|
||||
compositor.cursor = compositor.length
|
||||
stateCallback(buildInputtingState)
|
||||
} else {
|
||||
IME.prtDebugIntel("9B69908E")
|
||||
|
@ -650,7 +650,7 @@ extension KeyHandler {
|
|||
composingBuffer: currentState.composingBuffer,
|
||||
cursorIndex: currentState.cursorIndex,
|
||||
markerIndex: nextPosition,
|
||||
readings: currentReadings
|
||||
readings: compositor.readings
|
||||
)
|
||||
marking.tooltipForInputting = currentState.tooltip
|
||||
stateCallback(marking)
|
||||
|
@ -672,8 +672,8 @@ extension KeyHandler {
|
|||
}
|
||||
stateCallback(buildInputtingState)
|
||||
} else {
|
||||
if compositorCursorIndex < compositorLength {
|
||||
compositorCursorIndex += 1
|
||||
if compositor.cursor < compositor.length {
|
||||
compositor.cursor += 1
|
||||
stateCallback(buildInputtingState)
|
||||
} else {
|
||||
IME.prtDebugIntel("A96AAD58")
|
||||
|
@ -718,7 +718,7 @@ extension KeyHandler {
|
|||
composingBuffer: currentState.composingBuffer,
|
||||
cursorIndex: currentState.cursorIndex,
|
||||
markerIndex: previousPosition,
|
||||
readings: currentReadings
|
||||
readings: compositor.readings
|
||||
)
|
||||
marking.tooltipForInputting = currentState.tooltip
|
||||
stateCallback(marking)
|
||||
|
@ -740,8 +740,8 @@ extension KeyHandler {
|
|||
}
|
||||
stateCallback(buildInputtingState)
|
||||
} else {
|
||||
if compositorCursorIndex > 0 {
|
||||
compositorCursorIndex -= 1
|
||||
if compositor.cursor > 0 {
|
||||
compositor.cursor -= 1
|
||||
stateCallback(buildInputtingState)
|
||||
} else {
|
||||
IME.prtDebugIntel("7045E6F3")
|
||||
|
@ -795,7 +795,7 @@ extension KeyHandler {
|
|||
var length = 0
|
||||
var currentAnchor = Megrez.NodeAnchor()
|
||||
let cursorIndex = min(
|
||||
actualCandidateCursor + (mgrPrefs.useRearCursorMode ? 1 : 0), compositorLength
|
||||
actualCandidateCursor + (mgrPrefs.useRearCursorMode ? 1 : 0), compositor.length
|
||||
)
|
||||
for anchor in walkedAnchors {
|
||||
length += anchor.spanLength
|
||||
|
@ -817,7 +817,8 @@ extension KeyHandler {
|
|||
/// 讀音組成的雙字詞的權重、導致這個雙字詞並未在爬軌時被自動
|
||||
/// 選中的話,則使用者可以直接摁下本函式對應的按鍵來輪替候選字即可。
|
||||
/// (預設情況下是 (Shift+)Tab 來做正 (反) 向切換,但也可以用
|
||||
/// Shift(+CMD)+Space 來切換、以應對臉書綁架 Tab 鍵的情況。
|
||||
/// Shift(+CMD)+Space 或 Alt+↑/↓ 來切換(縱排輸入時則是 Alt+←/→)、
|
||||
/// 以應對臉書綁架 Tab 鍵的情況。
|
||||
if candidates[0].0 == currentPaired.key, candidates[0].1 == currentPaired.value {
|
||||
/// 如果第一個候選字詞是當前節點的候選字詞的值的話,
|
||||
/// 那就切到下一個(或上一個,也就是最後一個)候選字詞。
|
||||
|
|
|
@ -906,7 +906,7 @@ public struct Tekkon {
|
|||
}
|
||||
for key in Tekkon.mapArayuruPinyinIntonation.keys.sorted(by: { $0.count > $1.count }) {
|
||||
guard let value = Tekkon.mapArayuruPinyinIntonation[key] else { continue }
|
||||
result = result.replacingOccurrences(of: key, with: value)
|
||||
result = result.replacingOccurrences(of: key, with: (key == "1") ? "" : value)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
|
|
@ -42,6 +42,15 @@ extension vChewing {
|
|||
!rangeMap.isEmpty
|
||||
}
|
||||
|
||||
internal func cnvNgramKeyFromPinyinToPhona(target: String) -> String {
|
||||
guard target.contains("("), target.contains(","), target.contains(")") else {
|
||||
return target
|
||||
}
|
||||
let arrTarget = target.dropLast().dropFirst().split(separator: ",")
|
||||
guard arrTarget.count == 2 else { return target }
|
||||
return "(\(Tekkon.cnvHanyuPinyinToPhona(target: String(arrTarget[0]))),\(arrTarget[1]))"
|
||||
}
|
||||
|
||||
@discardableResult public mutating func open(_ path: String) -> Bool {
|
||||
if isLoaded() {
|
||||
return false
|
||||
|
@ -60,7 +69,7 @@ extension vChewing {
|
|||
if !theKey.isEmpty, theKey.first != "#" {
|
||||
for (i, _) in neta.filter({ $0.first != "#" && !$0.isEmpty }).enumerated() {
|
||||
if i == 0 { continue }
|
||||
rangeMap[theKey, default: []].append(($0, i))
|
||||
rangeMap[cnvNgramKeyFromPinyinToPhona(target: theKey), default: []].append(($0, i))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -102,8 +111,6 @@ extension vChewing {
|
|||
}
|
||||
|
||||
public func valuesFor(pair: Megrez.KeyValuePaired) -> [String] {
|
||||
var pairPinyin = pair
|
||||
pairPinyin.key = Tekkon.cnvPhonaToHanyuPinyin(target: pairPinyin.key)
|
||||
var pairs: [String] = []
|
||||
if let arrRangeRecords: [(Range<String.Index>, Int)] = rangeMap[pair.toNGramKey] {
|
||||
for (netaRange, index) in arrRangeRecords {
|
||||
|
@ -112,13 +119,6 @@ extension vChewing {
|
|||
pairs.append(theValue)
|
||||
}
|
||||
}
|
||||
if let arrRangeRecords: [(Range<String.Index>, Int)] = rangeMap[pairPinyin.toNGramKey] {
|
||||
for (netaRange, index) in arrRangeRecords {
|
||||
let neta = strData[netaRange].split(separator: " ")
|
||||
let theValue: String = .init(neta[index])
|
||||
pairs.append(theValue)
|
||||
}
|
||||
}
|
||||
if let arrRangeRecords: [(Range<String.Index>, Int)] = rangeMap[pair.value] {
|
||||
for (netaRange, index) in arrRangeRecords {
|
||||
let neta = strData[netaRange].split(separator: " ")
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.8.2</string>
|
||||
<string>1.8.3</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1982</string>
|
||||
<string>1983</string>
|
||||
<key>UpdateInfoEndpoint</key>
|
||||
<string>https://gitee.com/vchewing/vChewing-macOS/raw/main/Update-Info.plist</string>
|
||||
<key>UpdateInfoSite</key>
|
||||
|
|
|
@ -726,7 +726,7 @@
|
|||
<key>USE_HFS+_COMPRESSION</key>
|
||||
<false/>
|
||||
<key>VERSION</key>
|
||||
<string>1.8.2</string>
|
||||
<string>1.8.3</string>
|
||||
</dict>
|
||||
<key>TYPE</key>
|
||||
<integer>0</integer>
|
||||
|
|
|
@ -1389,7 +1389,7 @@
|
|||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1982;
|
||||
CURRENT_PROJECT_VERSION = 1983;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
|
@ -1399,7 +1399,7 @@
|
|||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MARKETING_VERSION = 1.8.2;
|
||||
MARKETING_VERSION = 1.8.3;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewingTests;
|
||||
|
@ -1428,13 +1428,13 @@
|
|||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1982;
|
||||
CURRENT_PROJECT_VERSION = 1983;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MARKETING_VERSION = 1.8.2;
|
||||
MARKETING_VERSION = 1.8.3;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewingTests;
|
||||
|
@ -1465,7 +1465,7 @@
|
|||
CODE_SIGN_IDENTITY = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1982;
|
||||
CURRENT_PROJECT_VERSION = 1983;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
|
@ -1486,7 +1486,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.8.2;
|
||||
MARKETING_VERSION = 1.8.3;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewing.vChewingPhraseEditor;
|
||||
|
@ -1515,7 +1515,7 @@
|
|||
CODE_SIGN_IDENTITY = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1982;
|
||||
CURRENT_PROJECT_VERSION = 1983;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
|
@ -1532,7 +1532,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.8.2;
|
||||
MARKETING_VERSION = 1.8.3;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewing.vChewingPhraseEditor;
|
||||
|
@ -1575,6 +1575,8 @@
|
|||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_LABEL = YES;
|
||||
GCC_WARN_UNUSED_PARAMETER = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.11.5;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
|
@ -1616,6 +1618,8 @@
|
|||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_LABEL = YES;
|
||||
GCC_WARN_UNUSED_PARAMETER = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.11.5;
|
||||
OTHER_CPLUSPLUSFLAGS = (
|
||||
|
@ -1642,7 +1646,7 @@
|
|||
CODE_SIGN_IDENTITY = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1982;
|
||||
CURRENT_PROJECT_VERSION = 1983;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_ASSET_PATHS = "";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
|
@ -1665,15 +1669,12 @@
|
|||
GCC_WARN_SHADOW = YES;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_LABEL = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
INFOPLIST_FILE = "Source/Resources/IME-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.8.2;
|
||||
MARKETING_VERSION = 1.8.3;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.inputmethod.vChewing;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
@ -1700,7 +1701,7 @@
|
|||
CODE_SIGN_IDENTITY = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1982;
|
||||
CURRENT_PROJECT_VERSION = 1983;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_ASSET_PATHS = "";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
|
@ -1717,15 +1718,12 @@
|
|||
GCC_WARN_SHADOW = YES;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_LABEL = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
INFOPLIST_FILE = "Source/Resources/IME-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.8.2;
|
||||
MARKETING_VERSION = 1.8.3;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.inputmethod.vChewing;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
@ -1747,7 +1745,7 @@
|
|||
CODE_SIGN_IDENTITY = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1982;
|
||||
CURRENT_PROJECT_VERSION = 1983;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
|
@ -1762,13 +1760,12 @@
|
|||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
INFOPLIST_FILE = "Installer/Installer-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.8.2;
|
||||
MARKETING_VERSION = 1.8.3;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.atelierInmu.vChewing.${PRODUCT_NAME:rfc1034identifier}";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
@ -1790,7 +1787,7 @@
|
|||
CODE_SIGN_IDENTITY = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1982;
|
||||
CURRENT_PROJECT_VERSION = 1983;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
|
@ -1799,13 +1796,12 @@
|
|||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
INFOPLIST_FILE = "Installer/Installer-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.8.2;
|
||||
MARKETING_VERSION = 1.8.3;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.atelierInmu.vChewing.${PRODUCT_NAME:rfc1034identifier}";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
|
|
@ -357,13 +357,15 @@ extension String {
|
|||
|
||||
extension vChewing.LMAssociates {
|
||||
public mutating func forceOpenStringInstead(_ strData: String) {
|
||||
strData.ranges(splitBy: "\n").forEach {
|
||||
strData.ranges(splitBy: "\n").filter({ !$0.isEmpty }).forEach {
|
||||
let neta = strData[$0].split(separator: " ")
|
||||
if neta.count >= 2 {
|
||||
let theKey = String(neta[0])
|
||||
if !neta[0].isEmpty, !neta[1].isEmpty, theKey.first != "#" {
|
||||
let theValue = $0
|
||||
rangeMap[theKey, default: []].append(theValue)
|
||||
if !theKey.isEmpty, theKey.first != "#" {
|
||||
for (i, _) in neta.filter({ $0.first != "#" && !$0.isEmpty }).enumerated() {
|
||||
if i == 0 { continue }
|
||||
rangeMap[theKey, default: []].append(($0, i))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue