Megrez // Upgrade to v1.2.1 release.
This commit is contained in:
parent
4b83af377d
commit
f4b164b05c
|
@ -65,8 +65,10 @@ class KeyHandler {
|
||||||
// Synchronize the sub-languageModel state settings to the new LM.
|
// Synchronize the sub-languageModel state settings to the new LM.
|
||||||
syncBaseLMPrefs()
|
syncBaseLMPrefs()
|
||||||
|
|
||||||
// Create new grid builder and clear the composer.
|
// Create new compositor and clear the composer.
|
||||||
createNewBuilder()
|
// When it recreates, it adapts to the latest imeMode settings.
|
||||||
|
// This allows it to work with correct LMs.
|
||||||
|
reinitCompositor()
|
||||||
composer.clear()
|
composer.clear()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -139,7 +141,7 @@ class KeyHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
func fixNode(value: String, respectCursorPushing: Bool = true) {
|
func fixNode(value: String, respectCursorPushing: Bool = true) {
|
||||||
let cursorIndex = min(actualCandidateCursorIndex + (mgrPrefs.useRearCursorMode ? 1 : 0), builderLength)
|
let cursorIndex = min(actualCandidateCursorIndex + (mgrPrefs.useRearCursorMode ? 1 : 0), compositorLength)
|
||||||
compositor.grid.fixNodeSelectedCandidate(location: cursorIndex, value: value)
|
compositor.grid.fixNodeSelectedCandidate(location: cursorIndex, value: value)
|
||||||
// // 因半衰模組失能,故禁用之。
|
// // 因半衰模組失能,故禁用之。
|
||||||
// let selectedNode: Megrez.NodeAnchor = compositor.grid.fixNodeSelectedCandidate(
|
// let selectedNode: Megrez.NodeAnchor = compositor.grid.fixNodeSelectedCandidate(
|
||||||
|
@ -180,8 +182,8 @@ class KeyHandler {
|
||||||
if nextPosition >= cursorIndex { break }
|
if nextPosition >= cursorIndex { break }
|
||||||
nextPosition += node.spanningLength
|
nextPosition += node.spanningLength
|
||||||
}
|
}
|
||||||
if nextPosition <= builderLength {
|
if nextPosition <= compositorLength {
|
||||||
builderCursorIndex = nextPosition
|
compositorCursorIndex = nextPosition
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -216,7 +218,7 @@ class KeyHandler {
|
||||||
mgrPrefs.useSCPCTypingMode
|
mgrPrefs.useSCPCTypingMode
|
||||||
? ""
|
? ""
|
||||||
: currentUOM.suggest(
|
: currentUOM.suggest(
|
||||||
walkedNodes: walkedAnchors, cursorIndex: builderCursorIndex,
|
walkedNodes: walkedAnchors, cursorIndex: compositorCursorIndex,
|
||||||
timestamp: NSDate().timeIntervalSince1970
|
timestamp: NSDate().timeIntervalSince1970
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -224,7 +226,7 @@ class KeyHandler {
|
||||||
IME.prtDebugIntel(
|
IME.prtDebugIntel(
|
||||||
"UOM: Suggestion retrieved, overriding the node score of the selected candidate.")
|
"UOM: Suggestion retrieved, overriding the node score of the selected candidate.")
|
||||||
compositor.grid.overrideNodeScoreForSelectedCandidate(
|
compositor.grid.overrideNodeScoreForSelectedCandidate(
|
||||||
location: min(actualCandidateCursorIndex + (mgrPrefs.useRearCursorMode ? 1 : 0), builderLength),
|
location: min(actualCandidateCursorIndex + (mgrPrefs.useRearCursorMode ? 1 : 0), compositorLength),
|
||||||
value: overrideValue,
|
value: overrideValue,
|
||||||
overridingScore: findHighestScore(nodes: rawNodes, epsilon: kEpsilon)
|
overridingScore: findHighestScore(nodes: rawNodes, epsilon: kEpsilon)
|
||||||
)
|
)
|
||||||
|
@ -285,7 +287,7 @@ class KeyHandler {
|
||||||
|
|
||||||
// MARK: - Extracted methods and functions (Megrez).
|
// MARK: - Extracted methods and functions (Megrez).
|
||||||
|
|
||||||
var isBuilderEmpty: Bool { compositor.grid.width == 0 }
|
var isCompositorEmpty: Bool { compositor.grid.width == 0 }
|
||||||
|
|
||||||
var rawNodes: [Megrez.NodeAnchor] {
|
var rawNodes: [Megrez.NodeAnchor] {
|
||||||
/// 警告:不要對游標前置風格使用 nodesCrossing,否則會導致游標行為與 macOS 內建注音輸入法不一致。
|
/// 警告:不要對游標前置風格使用 nodesCrossing,否則會導致游標行為與 macOS 內建注音輸入法不一致。
|
||||||
|
@ -301,7 +303,7 @@ class KeyHandler {
|
||||||
currentLM.isSymbolEnabled = mgrPrefs.symbolInputEnabled
|
currentLM.isSymbolEnabled = mgrPrefs.symbolInputEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
func createNewBuilder() {
|
func reinitCompositor() {
|
||||||
// Each Mandarin syllable is separated by a hyphen.
|
// Each Mandarin syllable is separated by a hyphen.
|
||||||
compositor = Megrez.Compositor(lm: currentLM, separator: "-")
|
compositor = Megrez.Compositor(lm: currentLM, separator: "-")
|
||||||
}
|
}
|
||||||
|
@ -312,16 +314,16 @@ class KeyHandler {
|
||||||
currentLM.hasUnigramsFor(key: reading)
|
currentLM.hasUnigramsFor(key: reading)
|
||||||
}
|
}
|
||||||
|
|
||||||
func insertReadingToBuilderAtCursor(reading: String) {
|
func insertToCompositorAtCursor(reading: String) {
|
||||||
compositor.insertReadingAtCursor(reading: reading)
|
compositor.insertReadingAtCursor(reading: reading)
|
||||||
}
|
}
|
||||||
|
|
||||||
var builderCursorIndex: Int {
|
var compositorCursorIndex: Int {
|
||||||
get { compositor.cursorIndex }
|
get { compositor.cursorIndex }
|
||||||
set { compositor.cursorIndex = newValue }
|
set { compositor.cursorIndex = newValue }
|
||||||
}
|
}
|
||||||
|
|
||||||
var builderLength: Int {
|
var compositorLength: Int {
|
||||||
compositor.length
|
compositor.length
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,12 +52,12 @@ extension KeyHandler {
|
||||||
if cancelCandidateKey {
|
if cancelCandidateKey {
|
||||||
if (state is InputState.AssociatedPhrases)
|
if (state is InputState.AssociatedPhrases)
|
||||||
|| mgrPrefs.useSCPCTypingMode
|
|| mgrPrefs.useSCPCTypingMode
|
||||||
|| isBuilderEmpty
|
|| isCompositorEmpty
|
||||||
{
|
{
|
||||||
// 如果此時發現當前組字緩衝區為真空的情況的話,
|
// 如果此時發現當前組字緩衝區為真空的情況的話,
|
||||||
// 就將當前的組字緩衝區析構處理、強制重設輸入狀態。
|
// 就將當前的組字緩衝區析構處理、強制重設輸入狀態。
|
||||||
// 否則,一個本不該出現的真空組字緩衝區會使前後方向鍵與 BackSpace 鍵失靈。
|
// 否則,一個本不該出現的真空組字緩衝區會使前後方向鍵與 BackSpace 鍵失靈。
|
||||||
// 所以這裡需要對 isBuilderEmpty 做判定。
|
// 所以這裡需要對 isCompositorEmpty 做判定。
|
||||||
clear()
|
clear()
|
||||||
stateCallback(InputState.EmptyIgnoringPreviousState())
|
stateCallback(InputState.EmptyIgnoringPreviousState())
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -151,7 +151,7 @@ extension KeyHandler {
|
||||||
keyConsumedByReading = true
|
keyConsumedByReading = true
|
||||||
|
|
||||||
// If we have a tone marker, we have to insert the reading to the
|
// If we have a tone marker, we have to insert the reading to the
|
||||||
// builder in other words, if we don't have a tone marker, we just
|
// compositor in other words, if we don't have a tone marker, we just
|
||||||
// update the composing buffer.
|
// update the composing buffer.
|
||||||
let composeReading = composer.hasToneMarker()
|
let composeReading = composer.hasToneMarker()
|
||||||
if !composeReading {
|
if !composeReading {
|
||||||
|
@ -177,12 +177,12 @@ extension KeyHandler {
|
||||||
IME.prtDebugIntel("B49C0979:語彙庫內無「\(reading)」的匹配記錄。")
|
IME.prtDebugIntel("B49C0979:語彙庫內無「\(reading)」的匹配記錄。")
|
||||||
errorCallback()
|
errorCallback()
|
||||||
composer.clear()
|
composer.clear()
|
||||||
stateCallback((builderLength == 0) ? InputState.EmptyIgnoringPreviousState() : buildInputtingState)
|
stateCallback((compositorLength == 0) ? InputState.EmptyIgnoringPreviousState() : buildInputtingState)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// ... and insert it into the grid...
|
// ... and insert it into the grid...
|
||||||
insertReadingToBuilderAtCursor(reading: reading)
|
insertToCompositorAtCursor(reading: reading)
|
||||||
|
|
||||||
// ... then walk the grid...
|
// ... then walk the grid...
|
||||||
let poppedText = popOverflowComposingTextAndWalk
|
let poppedText = popOverflowComposingTextAndWalk
|
||||||
|
@ -248,7 +248,7 @@ extension KeyHandler {
|
||||||
if input.isSpace {
|
if input.isSpace {
|
||||||
// If the Space key is NOT set to be a selection key
|
// If the Space key is NOT set to be a selection key
|
||||||
if input.isShiftHold || !mgrPrefs.chooseCandidateUsingSpace {
|
if input.isShiftHold || !mgrPrefs.chooseCandidateUsingSpace {
|
||||||
if builderCursorIndex >= builderLength {
|
if compositorCursorIndex >= compositorLength {
|
||||||
let composingBuffer = currentState.composingBuffer
|
let composingBuffer = currentState.composingBuffer
|
||||||
if !composingBuffer.isEmpty {
|
if !composingBuffer.isEmpty {
|
||||||
stateCallback(InputState.Committing(poppedText: composingBuffer))
|
stateCallback(InputState.Committing(poppedText: composingBuffer))
|
||||||
|
@ -257,7 +257,7 @@ extension KeyHandler {
|
||||||
stateCallback(InputState.Committing(poppedText: " "))
|
stateCallback(InputState.Committing(poppedText: " "))
|
||||||
stateCallback(InputState.Empty())
|
stateCallback(InputState.Empty())
|
||||||
} else if ifLangModelHasUnigrams(forKey: " ") {
|
} else if ifLangModelHasUnigrams(forKey: " ") {
|
||||||
insertReadingToBuilderAtCursor(reading: " ")
|
insertToCompositorAtCursor(reading: " ")
|
||||||
let poppedText = popOverflowComposingTextAndWalk
|
let poppedText = popOverflowComposingTextAndWalk
|
||||||
let inputting = buildInputtingState
|
let inputting = buildInputtingState
|
||||||
inputting.poppedText = poppedText
|
inputting.poppedText = poppedText
|
||||||
|
@ -363,7 +363,7 @@ extension KeyHandler {
|
||||||
if input.isOptionHold {
|
if input.isOptionHold {
|
||||||
if ifLangModelHasUnigrams(forKey: "_punctuation_list") {
|
if ifLangModelHasUnigrams(forKey: "_punctuation_list") {
|
||||||
if composer.isEmpty {
|
if composer.isEmpty {
|
||||||
insertReadingToBuilderAtCursor(reading: "_punctuation_list")
|
insertToCompositorAtCursor(reading: "_punctuation_list")
|
||||||
let poppedText: String! = popOverflowComposingTextAndWalk
|
let poppedText: String! = popOverflowComposingTextAndWalk
|
||||||
let inputting = buildInputtingState
|
let inputting = buildInputtingState
|
||||||
inputting.poppedText = poppedText
|
inputting.poppedText = poppedText
|
||||||
|
|
|
@ -34,7 +34,7 @@ extension KeyHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
var actualCandidateCursorIndex: Int {
|
var actualCandidateCursorIndex: Int {
|
||||||
var cursorIndex = builderCursorIndex
|
var cursorIndex = compositorCursorIndex
|
||||||
switch mgrPrefs.useRearCursorMode {
|
switch mgrPrefs.useRearCursorMode {
|
||||||
case false:
|
case false:
|
||||||
do {
|
do {
|
||||||
|
@ -52,7 +52,7 @@ extension KeyHandler {
|
||||||
// (i.e. the cursor is always at the rear of the phrase.)
|
// (i.e. the cursor is always at the rear of the phrase.)
|
||||||
// No crossing.
|
// No crossing.
|
||||||
switch cursorIndex {
|
switch cursorIndex {
|
||||||
case builderLength: cursorIndex -= 1
|
case compositorLength: cursorIndex -= 1
|
||||||
default: break
|
default: break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,44 +48,44 @@ extension KeyHandler {
|
||||||
let arrSplit: [String] = Array(strNodeValue).map { String($0) }
|
let arrSplit: [String] = Array(strNodeValue).map { String($0) }
|
||||||
let codepointCount = arrSplit.count
|
let codepointCount = arrSplit.count
|
||||||
// This re-aligns the cursor index in the composed string
|
// This re-aligns the cursor index in the composed string
|
||||||
// (the actual cursor on the screen) with the builder's logical
|
// (the actual cursor on the screen) with the compositor's logical
|
||||||
// cursor (reading) cursor; each built node has a "spanning length"
|
// cursor (reading) cursor; each built node has a "spanning length"
|
||||||
// (e.g. two reading blocks has a spanning length of 2), and we
|
// (e.g. two reading blocks has a spanning length of 2), and we
|
||||||
// accumulate those lengths to calculate the displayed cursor
|
// accumulate those lengths to calculate the displayed cursor
|
||||||
// index.
|
// index.
|
||||||
let spanningLength: Int = walkedNode.spanningLength
|
let spanningLength: Int = walkedNode.spanningLength
|
||||||
if readingCursorIndex + spanningLength <= builderCursorIndex {
|
if readingCursorIndex + spanningLength <= compositorCursorIndex {
|
||||||
composedStringCursorIndex += strNodeValue.utf16.count
|
composedStringCursorIndex += strNodeValue.utf16.count
|
||||||
readingCursorIndex += spanningLength
|
readingCursorIndex += spanningLength
|
||||||
} else {
|
} else {
|
||||||
if codepointCount == spanningLength {
|
if codepointCount == spanningLength {
|
||||||
var i = 0
|
var i = 0
|
||||||
while i < codepointCount, readingCursorIndex < builderCursorIndex {
|
while i < codepointCount, readingCursorIndex < compositorCursorIndex {
|
||||||
composedStringCursorIndex += arrSplit[i].utf16.count
|
composedStringCursorIndex += arrSplit[i].utf16.count
|
||||||
readingCursorIndex += 1
|
readingCursorIndex += 1
|
||||||
i += 1
|
i += 1
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if readingCursorIndex < builderCursorIndex {
|
if readingCursorIndex < compositorCursorIndex {
|
||||||
composedStringCursorIndex += strNodeValue.utf16.count
|
composedStringCursorIndex += strNodeValue.utf16.count
|
||||||
readingCursorIndex += spanningLength
|
readingCursorIndex += spanningLength
|
||||||
if readingCursorIndex > builderCursorIndex {
|
if readingCursorIndex > compositorCursorIndex {
|
||||||
readingCursorIndex = builderCursorIndex
|
readingCursorIndex = compositorCursorIndex
|
||||||
}
|
}
|
||||||
// Now we start preparing the contents of the tooltips used
|
// Now we start preparing the contents of the tooltips used
|
||||||
// in cases of moving cursors across certain emojis which emoji
|
// in cases of moving cursors across certain emojis which emoji
|
||||||
// char count is inequal to the reading count.
|
// char count is inequal to the reading count.
|
||||||
// Example in McBopomofo: Typing 王建民 (3 readings) gets a tree emoji.
|
// Example in McBopomofo: Typing 王建民 (3 readings) gets a tree emoji.
|
||||||
// Example in vChewing: Typing 義麵 (2 readings) gets a pasta emoji.
|
// Example in vChewing: Typing 義麵 (2 readings) gets a pasta emoji.
|
||||||
switch builderCursorIndex {
|
switch compositorCursorIndex {
|
||||||
case compositor.readings.count...:
|
case compositor.readings.count...:
|
||||||
tooltipParameterRef[0] = compositor.readings[compositor.readings.count - 1]
|
tooltipParameterRef[0] = compositor.readings[compositor.readings.count - 1]
|
||||||
case 0:
|
case 0:
|
||||||
tooltipParameterRef[1] = compositor.readings[builderCursorIndex]
|
tooltipParameterRef[1] = compositor.readings[compositorCursorIndex]
|
||||||
default:
|
default:
|
||||||
do {
|
do {
|
||||||
tooltipParameterRef[0] = compositor.readings[builderCursorIndex - 1]
|
tooltipParameterRef[0] = compositor.readings[compositorCursorIndex - 1]
|
||||||
tooltipParameterRef[1] = compositor.readings[builderCursorIndex]
|
tooltipParameterRef[1] = compositor.readings[compositorCursorIndex]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -260,7 +260,7 @@ extension KeyHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
if composer.isEmpty {
|
if composer.isEmpty {
|
||||||
insertReadingToBuilderAtCursor(reading: customPunctuation)
|
insertToCompositorAtCursor(reading: customPunctuation)
|
||||||
let poppedText = popOverflowComposingTextAndWalk
|
let poppedText = popOverflowComposingTextAndWalk
|
||||||
let inputting = buildInputtingState
|
let inputting = buildInputtingState
|
||||||
inputting.poppedText = poppedText
|
inputting.poppedText = poppedText
|
||||||
|
@ -385,7 +385,7 @@ extension KeyHandler {
|
||||||
if composer.hasToneMarker(withNothingElse: true) {
|
if composer.hasToneMarker(withNothingElse: true) {
|
||||||
composer.clear()
|
composer.clear()
|
||||||
} else if composer.isEmpty {
|
} else if composer.isEmpty {
|
||||||
if builderCursorIndex >= 0 {
|
if compositorCursorIndex >= 0 {
|
||||||
deleteBuilderReadingInFrontOfCursor()
|
deleteBuilderReadingInFrontOfCursor()
|
||||||
walk()
|
walk()
|
||||||
} else {
|
} else {
|
||||||
|
@ -398,7 +398,7 @@ extension KeyHandler {
|
||||||
composer.doBackSpace()
|
composer.doBackSpace()
|
||||||
}
|
}
|
||||||
|
|
||||||
if composer.isEmpty, builderLength == 0 {
|
if composer.isEmpty, compositorLength == 0 {
|
||||||
stateCallback(InputState.EmptyIgnoringPreviousState())
|
stateCallback(InputState.EmptyIgnoringPreviousState())
|
||||||
} else {
|
} else {
|
||||||
stateCallback(buildInputtingState)
|
stateCallback(buildInputtingState)
|
||||||
|
@ -416,7 +416,7 @@ extension KeyHandler {
|
||||||
guard state is InputState.Inputting else { return false }
|
guard state is InputState.Inputting else { return false }
|
||||||
|
|
||||||
if composer.isEmpty {
|
if composer.isEmpty {
|
||||||
if builderCursorIndex != builderLength {
|
if compositorCursorIndex != compositorLength {
|
||||||
deleteBuilderReadingToTheFrontOfCursor()
|
deleteBuilderReadingToTheFrontOfCursor()
|
||||||
walk()
|
walk()
|
||||||
let inputting = buildInputtingState
|
let inputting = buildInputtingState
|
||||||
|
@ -472,8 +472,8 @@ extension KeyHandler {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if builderCursorIndex != 0 {
|
if compositorCursorIndex != 0 {
|
||||||
builderCursorIndex = 0
|
compositorCursorIndex = 0
|
||||||
stateCallback(buildInputtingState)
|
stateCallback(buildInputtingState)
|
||||||
} else {
|
} else {
|
||||||
IME.prtDebugIntel("66D97F90")
|
IME.prtDebugIntel("66D97F90")
|
||||||
|
@ -500,8 +500,8 @@ extension KeyHandler {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if builderCursorIndex != builderLength {
|
if compositorCursorIndex != compositorLength {
|
||||||
builderCursorIndex = builderLength
|
compositorCursorIndex = compositorLength
|
||||||
stateCallback(buildInputtingState)
|
stateCallback(buildInputtingState)
|
||||||
} else {
|
} else {
|
||||||
IME.prtDebugIntel("9B69908E")
|
IME.prtDebugIntel("9B69908E")
|
||||||
|
@ -534,7 +534,7 @@ extension KeyHandler {
|
||||||
// If reading is not empty, we cancel the reading.
|
// If reading is not empty, we cancel the reading.
|
||||||
if !composer.isEmpty {
|
if !composer.isEmpty {
|
||||||
composer.clear()
|
composer.clear()
|
||||||
if builderLength == 0 {
|
if compositorLength == 0 {
|
||||||
stateCallback(InputState.EmptyIgnoringPreviousState())
|
stateCallback(InputState.EmptyIgnoringPreviousState())
|
||||||
} else {
|
} else {
|
||||||
stateCallback(buildInputtingState)
|
stateCallback(buildInputtingState)
|
||||||
|
@ -580,8 +580,8 @@ extension KeyHandler {
|
||||||
stateCallback(state)
|
stateCallback(state)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if builderCursorIndex < builderLength {
|
if compositorCursorIndex < compositorLength {
|
||||||
builderCursorIndex += 1
|
compositorCursorIndex += 1
|
||||||
stateCallback(buildInputtingState)
|
stateCallback(buildInputtingState)
|
||||||
} else {
|
} else {
|
||||||
IME.prtDebugIntel("A96AAD58")
|
IME.prtDebugIntel("A96AAD58")
|
||||||
|
@ -629,8 +629,8 @@ extension KeyHandler {
|
||||||
stateCallback(state)
|
stateCallback(state)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if builderCursorIndex > 0 {
|
if compositorCursorIndex > 0 {
|
||||||
builderCursorIndex -= 1
|
compositorCursorIndex -= 1
|
||||||
stateCallback(buildInputtingState)
|
stateCallback(buildInputtingState)
|
||||||
} else {
|
} else {
|
||||||
IME.prtDebugIntel("7045E6F3")
|
IME.prtDebugIntel("7045E6F3")
|
||||||
|
@ -677,7 +677,7 @@ extension KeyHandler {
|
||||||
var length = 0
|
var length = 0
|
||||||
var currentAnchor = Megrez.NodeAnchor()
|
var currentAnchor = Megrez.NodeAnchor()
|
||||||
let cursorIndex = min(
|
let cursorIndex = min(
|
||||||
actualCandidateCursorIndex + (mgrPrefs.useRearCursorMode ? 1 : 0), builderLength
|
actualCandidateCursorIndex + (mgrPrefs.useRearCursorMode ? 1 : 0), compositorLength
|
||||||
)
|
)
|
||||||
for anchor in walkedAnchors {
|
for anchor in walkedAnchors {
|
||||||
length += anchor.spanningLength
|
length += anchor.spanningLength
|
||||||
|
|
|
@ -78,10 +78,10 @@ private let kMinCandidateListTextSize: CGFloat = 12
|
||||||
private let kMaxCandidateListTextSize: CGFloat = 196
|
private let kMaxCandidateListTextSize: CGFloat = 196
|
||||||
|
|
||||||
// default, min and max composing buffer size (in codepoints)
|
// default, min and max composing buffer size (in codepoints)
|
||||||
// modern Macs can usually work up to 16 codepoints when the builder still
|
// modern Macs can usually work up to 16 codepoints when the compositor still
|
||||||
// walks the grid with good performance slower Macs (like old PowerBooks)
|
// walks the grid with good performance slower Macs (like old PowerBooks)
|
||||||
// will start to sputter beyond 12 such is the algorithmatic complexity
|
// will start to sputter beyond 12 such is the algorithmatic complexity
|
||||||
// of the Viterbi algorithm used in the builder library (at O(N^2))
|
// of the Viterbi algorithm used in the Megrez library (at O(N^2))
|
||||||
private let kDefaultComposingBufferSize = 20
|
private let kDefaultComposingBufferSize = 20
|
||||||
private let kMinComposingBufferSize = 10
|
private let kMinComposingBufferSize = 10
|
||||||
private let kMaxComposingBufferSize = 40
|
private let kMaxComposingBufferSize = 40
|
||||||
|
|
|
@ -42,8 +42,8 @@ extension vChewing {
|
||||||
/// LMInstantiator is a facade for managing a set of models including
|
/// LMInstantiator is a facade for managing a set of models including
|
||||||
/// the input method language model, user phrases and excluded phrases.
|
/// the input method language model, user phrases and excluded phrases.
|
||||||
///
|
///
|
||||||
/// It is the primary model class that the input controller and grammar builder
|
/// It is the primary model class that the input controller and input compositor
|
||||||
/// of vChewing talks to. When the grammar builder starts to build a sentence
|
/// of vChewing talks to. When the input compositor starts to build a sentence
|
||||||
/// from a series of BPMF readings, it passes the readings to the model to see
|
/// 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
|
/// if there are valid unigrams, and use returned unigrams to produce the final
|
||||||
/// results.
|
/// results.
|
||||||
|
|
|
@ -24,40 +24,43 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
extension Megrez {
|
extension Megrez {
|
||||||
/// 分節讀音槽。
|
/// 組字器。
|
||||||
public class BlockReadingBuilder {
|
public class Compositor {
|
||||||
/// 給被丟掉的節點路徑施加的負權重。
|
/// 給被丟掉的節點路徑施加的負權重。
|
||||||
private let kDroppedPathScore: Double = -999
|
private let kDroppedPathScore: Double = -999
|
||||||
/// 該分節讀音槽的游標位置。
|
/// 該組字器的游標位置。
|
||||||
private var mutCursorIndex: Int = 0
|
private var mutCursorIndex: Int = 0
|
||||||
/// 該分節讀音槽的讀音陣列。
|
/// 該組字器的讀音陣列。
|
||||||
private var mutReadings: [String] = []
|
private var mutReadings: [String] = []
|
||||||
/// 該分節讀音槽的軌格。
|
/// 該組字器的軌格。
|
||||||
private var mutGrid: Grid = .init()
|
private var mutGrid: Grid = .init()
|
||||||
/// 該分節讀音槽所使用的語言模型。
|
/// 該組字器所使用的語言模型。
|
||||||
private var mutLM: LanguageModel
|
private var mutLM: LanguageModel
|
||||||
|
|
||||||
/// 公開該分節讀音槽內可以允許的最大詞長。
|
/// 公開該組字器內可以允許的最大詞長。
|
||||||
public var maxBuildSpanLength: Int { mutGrid.maxBuildSpanLength }
|
public var maxBuildSpanLength: Int { mutGrid.maxBuildSpanLength }
|
||||||
/// 公開:多字讀音鍵當中用以分割漢字讀音的記號,預設為空。
|
/// 公開:多字讀音鍵當中用以分割漢字讀音的記號,預設為空。
|
||||||
public var joinSeparator: String = ""
|
public var joinSeparator: String = ""
|
||||||
/// 公開:該分節讀音槽的游標位置。
|
/// 公開:該組字器的游標位置。
|
||||||
public var cursorIndex: Int {
|
public var cursorIndex: Int {
|
||||||
get { mutCursorIndex }
|
get { mutCursorIndex }
|
||||||
set { mutCursorIndex = (newValue < 0) ? 0 : min(newValue, mutReadings.count) }
|
set { mutCursorIndex = (newValue < 0) ? 0 : min(newValue, mutReadings.count) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 公開:該分節讀音槽的軌格(唯讀)。
|
/// 公開:該組字器是否為空。
|
||||||
|
public var isEmpty: Bool { grid.isEmpty }
|
||||||
|
|
||||||
|
/// 公開:該組字器的軌格(唯讀)。
|
||||||
public var grid: Grid { mutGrid }
|
public var grid: Grid { mutGrid }
|
||||||
/// 公開:該分節讀音槽的長度,也就是內建漢字讀音的數量(唯讀)。
|
/// 公開:該組字器的長度,也就是內建漢字讀音的數量(唯讀)。
|
||||||
public var length: Int { mutReadings.count }
|
public var length: Int { mutReadings.count }
|
||||||
/// 公開:該分節讀音槽的讀音陣列(唯讀)。
|
/// 公開:該組字器的讀音陣列(唯讀)。
|
||||||
public var readings: [String] { mutReadings }
|
public var readings: [String] { mutReadings }
|
||||||
|
|
||||||
/// 分節讀音槽。
|
/// 組字器。
|
||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
/// - lm: 語言模型。可以是任何基於 Megrez.LanguageModel 的衍生型別。
|
/// - lm: 語言模型。可以是任何基於 Megrez.LanguageModel 的衍生型別。
|
||||||
/// - length: 指定該分節讀音槽內可以允許的最大詞長,預設為 10 字。
|
/// - length: 指定該組字器內可以允許的最大詞長,預設為 10 字。
|
||||||
/// - separator: 多字讀音鍵當中用以分割漢字讀音的記號,預設為空。
|
/// - separator: 多字讀音鍵當中用以分割漢字讀音的記號,預設為空。
|
||||||
public init(lm: LanguageModel, length: Int = 10, separator: String = "") {
|
public init(lm: LanguageModel, length: Int = 10, separator: String = "") {
|
||||||
mutLM = lm
|
mutLM = lm
|
||||||
|
@ -65,7 +68,7 @@ extension Megrez {
|
||||||
joinSeparator = separator
|
joinSeparator = separator
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 分節讀音槽自我清空專用函數。
|
/// 組字器自我清空專用函數。
|
||||||
public func clear() {
|
public func clear() {
|
||||||
mutCursorIndex = 0
|
mutCursorIndex = 0
|
||||||
mutReadings.removeAll()
|
mutReadings.removeAll()
|
||||||
|
@ -109,7 +112,7 @@ extension Megrez {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 移除該分節讀音槽的第一個讀音單元。
|
/// 移除該組字器的第一個讀音單元。
|
||||||
///
|
///
|
||||||
/// 用於輸入法組字區長度上限處理:
|
/// 用於輸入法組字區長度上限處理:
|
||||||
/// 將該位置要溢出的敲字內容遞交之後、再執行這個函數。
|
/// 將該位置要溢出的敲字內容遞交之後、再執行這個函數。
|
||||||
|
@ -179,12 +182,14 @@ extension Megrez {
|
||||||
$0.scoreForSort > $1.scoreForSort
|
$0.scoreForSort > $1.scoreForSort
|
||||||
}
|
}
|
||||||
|
|
||||||
if let nodeOfNodeZero = nodes[0].node, nodeOfNodeZero.score >= nodeOfNodeZero.kSelectedCandidateScore {
|
if let nodeZero = nodes[0].node, nodeZero.score >= nodeZero.kSelectedCandidateScore {
|
||||||
// 在使用者有選過候選字詞的情況下,摒棄非依此據而成的節點路徑。
|
// 在使用者有選過候選字詞的情況下,摒棄非依此據而成的節點路徑。
|
||||||
var nodeZero = nodes[0]
|
var anchorZero = nodes[0]
|
||||||
nodeZero.accumulatedScore = accumulatedScore + nodeOfNodeZero.score
|
anchorZero.accumulatedScore = accumulatedScore + nodeZero.score
|
||||||
var path: [NodeAnchor] = reverseWalk(at: location - nodeZero.spanningLength, score: nodeZero.accumulatedScore)
|
var path: [NodeAnchor] = reverseWalk(
|
||||||
path.insert(nodeZero, at: 0)
|
at: location - anchorZero.spanningLength, score: anchorZero.accumulatedScore
|
||||||
|
)
|
||||||
|
path.insert(anchorZero, at: 0)
|
||||||
paths.append(path)
|
paths.append(path)
|
||||||
} else if !longPhrases.isEmpty {
|
} else if !longPhrases.isEmpty {
|
||||||
var path = [NodeAnchor]()
|
var path = [NodeAnchor]()
|
||||||
|
@ -202,17 +207,12 @@ extension Megrez {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
theAnchor.accumulatedScore = accumulatedScore + theNode.score
|
theAnchor.accumulatedScore = accumulatedScore + theNode.score
|
||||||
if joinedValue.count >= longPhrases[0].count {
|
path = reverseWalk(
|
||||||
path = reverseWalk(
|
at: location - theAnchor.spanningLength,
|
||||||
at: location - theAnchor.spanningLength, score: theAnchor.accumulatedScore, joinedPhrase: "",
|
score: theAnchor.accumulatedScore,
|
||||||
longPhrases: .init()
|
joinedPhrase: (joinedValue.count >= longPhrases[0].count) ? "" : joinedValue,
|
||||||
)
|
longPhrases: .init()
|
||||||
} else {
|
)
|
||||||
path = reverseWalk(
|
|
||||||
at: location - theAnchor.spanningLength, score: theAnchor.accumulatedScore, joinedPhrase: joinedValue,
|
|
||||||
longPhrases: longPhrases
|
|
||||||
)
|
|
||||||
}
|
|
||||||
path.insert(theAnchor, at: 0)
|
path.insert(theAnchor, at: 0)
|
||||||
paths.append(path)
|
paths.append(path)
|
||||||
}
|
}
|
||||||
|
@ -234,17 +234,11 @@ extension Megrez {
|
||||||
guard let theNode = theAnchor.node else { continue }
|
guard let theNode = theAnchor.node else { continue }
|
||||||
theAnchor.accumulatedScore = accumulatedScore + theNode.score
|
theAnchor.accumulatedScore = accumulatedScore + theNode.score
|
||||||
var path = [NodeAnchor]()
|
var path = [NodeAnchor]()
|
||||||
if theAnchor.spanningLength > 1 {
|
path = reverseWalk(
|
||||||
path = reverseWalk(
|
at: location - theAnchor.spanningLength, score: theAnchor.accumulatedScore,
|
||||||
at: location - theAnchor.spanningLength, score: theAnchor.accumulatedScore, joinedPhrase: "",
|
joinedPhrase: (theAnchor.spanningLength > 1) ? "" : theNode.currentKeyValue.value,
|
||||||
longPhrases: .init()
|
longPhrases: .init()
|
||||||
)
|
)
|
||||||
} else {
|
|
||||||
path = reverseWalk(
|
|
||||||
at: location - theAnchor.spanningLength, score: theAnchor.accumulatedScore,
|
|
||||||
joinedPhrase: theNode.currentKeyValue.value, longPhrases: longPhrases
|
|
||||||
)
|
|
||||||
}
|
|
||||||
path.insert(theAnchor, at: 0)
|
path.insert(theAnchor, at: 0)
|
||||||
paths.append(path)
|
paths.append(path)
|
||||||
}
|
}
|
|
@ -38,6 +38,9 @@ extension Megrez {
|
||||||
/// 軌格的寬度,也就是其內的幅位陣列當中的幅位數量。
|
/// 軌格的寬度,也就是其內的幅位陣列當中的幅位數量。
|
||||||
var width: Int { mutSpans.count }
|
var width: Int { mutSpans.count }
|
||||||
|
|
||||||
|
/// 軌格是否為空。
|
||||||
|
var isEmpty: Bool { mutSpans.isEmpty }
|
||||||
|
|
||||||
public init(spanLength: Int = 10) {
|
public init(spanLength: Int = 10) {
|
||||||
mutMaxBuildSpanLength = spanLength
|
mutMaxBuildSpanLength = spanLength
|
||||||
mutSpans = [Megrez.Span]()
|
mutSpans = [Megrez.Span]()
|
||||||
|
@ -256,9 +259,9 @@ extension Megrez.Grid {
|
||||||
strOutput += "\(np.currentKeyValue.value);\n"
|
strOutput += "\(np.currentKeyValue.value);\n"
|
||||||
|
|
||||||
if (p + ni) < mutSpans.count {
|
if (p + ni) < mutSpans.count {
|
||||||
let destinatedSpan = mutSpans[p + ni]
|
let destinationSpan = mutSpans[p + ni]
|
||||||
for q in 0...(destinatedSpan.maximumLength) {
|
for q in 0...(destinationSpan.maximumLength) {
|
||||||
if let dn = destinatedSpan.node(length: q) {
|
if let dn = destinationSpan.node(length: q) {
|
||||||
strOutput += np.currentKeyValue.value + " -> " + dn.currentKeyValue.value + ";\n"
|
strOutput += np.currentKeyValue.value + " -> " + dn.currentKeyValue.value + ";\n"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,9 +49,7 @@ extension Megrez {
|
||||||
mutating func insert(node: Node, length: Int) {
|
mutating func insert(node: Node, length: Int) {
|
||||||
let length = abs(length) // 防呆
|
let length = abs(length) // 防呆
|
||||||
mutLengthNodeMap[length] = node
|
mutLengthNodeMap[length] = node
|
||||||
if length > mutMaximumLength {
|
mutMaximumLength = max(mutMaximumLength, length)
|
||||||
mutMaximumLength = length
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 移除任何比給定的長度更長的節點。
|
/// 移除任何比給定的長度更長的節點。
|
||||||
|
@ -60,21 +58,19 @@ extension Megrez {
|
||||||
mutating func removeNodeOfLengthGreaterThan(_ length: Int) {
|
mutating func removeNodeOfLengthGreaterThan(_ length: Int) {
|
||||||
let length = abs(length) // 防呆
|
let length = abs(length) // 防呆
|
||||||
if length > mutMaximumLength { return }
|
if length > mutMaximumLength { return }
|
||||||
var max = 0
|
var lenMax = 0
|
||||||
var removalList: [Int: Megrez.Node] = [:]
|
var removalList: [Int: Megrez.Node] = [:]
|
||||||
for key in mutLengthNodeMap.keys {
|
for key in mutLengthNodeMap.keys {
|
||||||
if key > length {
|
if key > length {
|
||||||
removalList[key] = mutLengthNodeMap[key]
|
removalList[key] = mutLengthNodeMap[key]
|
||||||
} else {
|
} else {
|
||||||
if key > max {
|
lenMax = max(lenMax, key)
|
||||||
max = key
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for key in removalList.keys {
|
for key in removalList.keys {
|
||||||
mutLengthNodeMap.removeValue(forKey: key)
|
mutLengthNodeMap.removeValue(forKey: key)
|
||||||
}
|
}
|
||||||
mutMaximumLength = max
|
mutMaximumLength = lenMax
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 給定節點長度,獲取節點。
|
/// 給定節點長度,獲取節點。
|
||||||
|
|
|
@ -25,7 +25,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
extension Megrez {
|
extension Megrez {
|
||||||
/// 節點。
|
/// 節點。
|
||||||
public class Node: CustomStringConvertible {
|
public class Node {
|
||||||
/// 當前節點對應的語言模型。
|
/// 當前節點對應的語言模型。
|
||||||
private let mutLM: LanguageModel = .init()
|
private let mutLM: LanguageModel = .init()
|
||||||
/// 鍵。
|
/// 鍵。
|
||||||
|
@ -109,23 +109,16 @@ extension Megrez {
|
||||||
for neta in precedingKeyValues {
|
for neta in precedingKeyValues {
|
||||||
let bigrams = mutPrecedingBigramMap[neta] ?? []
|
let bigrams = mutPrecedingBigramMap[neta] ?? []
|
||||||
for bigram in bigrams {
|
for bigram in bigrams {
|
||||||
if bigram.score > max {
|
guard bigram.score > max else { continue }
|
||||||
if let valRetrieved = mutValueUnigramIndexMap[bigram.keyValue.value] {
|
if let valRetrieved = mutValueUnigramIndexMap[bigram.keyValue.value] {
|
||||||
newIndex = valRetrieved as Int
|
newIndex = valRetrieved as Int
|
||||||
max = bigram.score
|
max = bigram.score
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mutScore = max
|
||||||
if mutScore != max {
|
mutSelectedUnigramIndex = newIndex
|
||||||
mutScore = max
|
|
||||||
}
|
|
||||||
|
|
||||||
if mutSelectedUnigramIndex != newIndex {
|
|
||||||
mutSelectedUnigramIndex = newIndex
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 選中位於給定索引位置的候選字詞。
|
/// 選中位於給定索引位置的候選字詞。
|
||||||
|
@ -170,13 +163,5 @@ extension Megrez {
|
||||||
}
|
}
|
||||||
return 0.0
|
return 0.0
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func == (lhs: Node, rhs: Node) -> Bool {
|
|
||||||
lhs.mutUnigrams == rhs.mutUnigrams && lhs.mutCandidates == rhs.mutCandidates
|
|
||||||
&& lhs.mutValueUnigramIndexMap == rhs.mutValueUnigramIndexMap
|
|
||||||
&& lhs.mutPrecedingBigramMap == rhs.mutPrecedingBigramMap
|
|
||||||
&& lhs.mutCandidateFixed == rhs.mutCandidateFixed
|
|
||||||
&& lhs.mutSelectedUnigramIndex == rhs.mutSelectedUnigramIndex
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
5B38F59D281E2E49007D5F5D /* 4_Node.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1A15FC0EB100ABF4B3 /* 4_Node.swift */; };
|
5B38F59D281E2E49007D5F5D /* 4_Node.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1A15FC0EB100ABF4B3 /* 4_Node.swift */; };
|
||||||
5B38F59E281E2E49007D5F5D /* 6_Bigram.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1415FC0EB100ABF4B3 /* 6_Bigram.swift */; };
|
5B38F59E281E2E49007D5F5D /* 6_Bigram.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1415FC0EB100ABF4B3 /* 6_Bigram.swift */; };
|
||||||
5B38F59F281E2E49007D5F5D /* 3_NodeAnchor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1B15FC0EB100ABF4B3 /* 3_NodeAnchor.swift */; };
|
5B38F59F281E2E49007D5F5D /* 3_NodeAnchor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1B15FC0EB100ABF4B3 /* 3_NodeAnchor.swift */; };
|
||||||
5B38F5A1281E2E49007D5F5D /* 1_BlockReadingBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1515FC0EB100ABF4B3 /* 1_BlockReadingBuilder.swift */; };
|
5B38F5A1281E2E49007D5F5D /* 1_Compositor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1515FC0EB100ABF4B3 /* 1_Compositor.swift */; };
|
||||||
5B38F5A2281E2E49007D5F5D /* 0_Megrez.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1615FC0EB100ABF4B3 /* 0_Megrez.swift */; };
|
5B38F5A2281E2E49007D5F5D /* 0_Megrez.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1615FC0EB100ABF4B3 /* 0_Megrez.swift */; };
|
||||||
5B38F5A3281E2E49007D5F5D /* 3_Span.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1C15FC0EB100ABF4B3 /* 3_Span.swift */; };
|
5B38F5A3281E2E49007D5F5D /* 3_Span.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1C15FC0EB100ABF4B3 /* 3_Span.swift */; };
|
||||||
5B38F5A4281E2E49007D5F5D /* 5_LanguageModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1915FC0EB100ABF4B3 /* 5_LanguageModel.swift */; };
|
5B38F5A4281E2E49007D5F5D /* 5_LanguageModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1915FC0EB100ABF4B3 /* 5_LanguageModel.swift */; };
|
||||||
|
@ -286,7 +286,7 @@
|
||||||
6A0D4EF515FC0DA600ABF4B3 /* IME-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "IME-Info.plist"; sourceTree = "<group>"; };
|
6A0D4EF515FC0DA600ABF4B3 /* IME-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "IME-Info.plist"; sourceTree = "<group>"; };
|
||||||
6A0D4EF615FC0DA600ABF4B3 /* vChewing-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "vChewing-Prefix.pch"; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; };
|
6A0D4EF615FC0DA600ABF4B3 /* vChewing-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "vChewing-Prefix.pch"; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; };
|
||||||
6A0D4F1415FC0EB100ABF4B3 /* 6_Bigram.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 6_Bigram.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
6A0D4F1415FC0EB100ABF4B3 /* 6_Bigram.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 6_Bigram.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||||
6A0D4F1515FC0EB100ABF4B3 /* 1_BlockReadingBuilder.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 1_BlockReadingBuilder.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
6A0D4F1515FC0EB100ABF4B3 /* 1_Compositor.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 1_Compositor.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||||
6A0D4F1615FC0EB100ABF4B3 /* 0_Megrez.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = 0_Megrez.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
6A0D4F1615FC0EB100ABF4B3 /* 0_Megrez.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = 0_Megrez.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||||
6A0D4F1715FC0EB100ABF4B3 /* 2_Grid.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 2_Grid.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
6A0D4F1715FC0EB100ABF4B3 /* 2_Grid.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 2_Grid.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||||
6A0D4F1815FC0EB100ABF4B3 /* 7_KeyValuePair.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 7_KeyValuePair.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
6A0D4F1815FC0EB100ABF4B3 /* 7_KeyValuePair.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 7_KeyValuePair.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||||
|
@ -764,7 +764,7 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
6A0D4F1615FC0EB100ABF4B3 /* 0_Megrez.swift */,
|
6A0D4F1615FC0EB100ABF4B3 /* 0_Megrez.swift */,
|
||||||
6A0D4F1515FC0EB100ABF4B3 /* 1_BlockReadingBuilder.swift */,
|
6A0D4F1515FC0EB100ABF4B3 /* 1_Compositor.swift */,
|
||||||
6A0D4F1715FC0EB100ABF4B3 /* 2_Grid.swift */,
|
6A0D4F1715FC0EB100ABF4B3 /* 2_Grid.swift */,
|
||||||
6A0D4F1B15FC0EB100ABF4B3 /* 3_NodeAnchor.swift */,
|
6A0D4F1B15FC0EB100ABF4B3 /* 3_NodeAnchor.swift */,
|
||||||
6A0D4F1C15FC0EB100ABF4B3 /* 3_Span.swift */,
|
6A0D4F1C15FC0EB100ABF4B3 /* 3_Span.swift */,
|
||||||
|
@ -1117,7 +1117,7 @@
|
||||||
5B62A34727AE7CD900A19448 /* ctlCandidate.swift in Sources */,
|
5B62A34727AE7CD900A19448 /* ctlCandidate.swift in Sources */,
|
||||||
5BA9FD3F27FEF3C8002DE248 /* Pane.swift in Sources */,
|
5BA9FD3F27FEF3C8002DE248 /* Pane.swift in Sources */,
|
||||||
5BB802DA27FABA8300CF1C19 /* ctlInputMethod_Menu.swift in Sources */,
|
5BB802DA27FABA8300CF1C19 /* ctlInputMethod_Menu.swift in Sources */,
|
||||||
5B38F5A1281E2E49007D5F5D /* 1_BlockReadingBuilder.swift in Sources */,
|
5B38F5A1281E2E49007D5F5D /* 1_Compositor.swift in Sources */,
|
||||||
5BDC1CFA27FDF1310052C2B9 /* apiUpdate.swift in Sources */,
|
5BDC1CFA27FDF1310052C2B9 /* apiUpdate.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
|
Loading…
Reference in New Issue