Megrez // Upgrade to v1.2.1 release.

This commit is contained in:
ShikiSuen 2022-06-15 22:28:12 +08:00
parent 4b83af377d
commit f4b164b05c
12 changed files with 109 additions and 129 deletions

View File

@ -65,8 +65,10 @@ class KeyHandler {
// Synchronize the sub-languageModel state settings to the new LM.
syncBaseLMPrefs()
// Create new grid builder and clear the composer.
createNewBuilder()
// Create new compositor and clear the composer.
// When it recreates, it adapts to the latest imeMode settings.
// This allows it to work with correct LMs.
reinitCompositor()
composer.clear()
}
}
@ -139,7 +141,7 @@ class KeyHandler {
}
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)
// //
// let selectedNode: Megrez.NodeAnchor = compositor.grid.fixNodeSelectedCandidate(
@ -180,8 +182,8 @@ class KeyHandler {
if nextPosition >= cursorIndex { break }
nextPosition += node.spanningLength
}
if nextPosition <= builderLength {
builderCursorIndex = nextPosition
if nextPosition <= compositorLength {
compositorCursorIndex = nextPosition
}
}
}
@ -216,7 +218,7 @@ class KeyHandler {
mgrPrefs.useSCPCTypingMode
? ""
: currentUOM.suggest(
walkedNodes: walkedAnchors, cursorIndex: builderCursorIndex,
walkedNodes: walkedAnchors, cursorIndex: compositorCursorIndex,
timestamp: NSDate().timeIntervalSince1970
)
@ -224,7 +226,7 @@ class KeyHandler {
IME.prtDebugIntel(
"UOM: Suggestion retrieved, overriding the node score of the selected candidate.")
compositor.grid.overrideNodeScoreForSelectedCandidate(
location: min(actualCandidateCursorIndex + (mgrPrefs.useRearCursorMode ? 1 : 0), builderLength),
location: min(actualCandidateCursorIndex + (mgrPrefs.useRearCursorMode ? 1 : 0), compositorLength),
value: overrideValue,
overridingScore: findHighestScore(nodes: rawNodes, epsilon: kEpsilon)
)
@ -285,7 +287,7 @@ class KeyHandler {
// MARK: - Extracted methods and functions (Megrez).
var isBuilderEmpty: Bool { compositor.grid.width == 0 }
var isCompositorEmpty: Bool { compositor.grid.width == 0 }
var rawNodes: [Megrez.NodeAnchor] {
/// 使 nodesCrossing macOS
@ -301,7 +303,7 @@ class KeyHandler {
currentLM.isSymbolEnabled = mgrPrefs.symbolInputEnabled
}
func createNewBuilder() {
func reinitCompositor() {
// Each Mandarin syllable is separated by a hyphen.
compositor = Megrez.Compositor(lm: currentLM, separator: "-")
}
@ -312,16 +314,16 @@ class KeyHandler {
currentLM.hasUnigramsFor(key: reading)
}
func insertReadingToBuilderAtCursor(reading: String) {
func insertToCompositorAtCursor(reading: String) {
compositor.insertReadingAtCursor(reading: reading)
}
var builderCursorIndex: Int {
var compositorCursorIndex: Int {
get { compositor.cursorIndex }
set { compositor.cursorIndex = newValue }
}
var builderLength: Int {
var compositorLength: Int {
compositor.length
}

View File

@ -52,12 +52,12 @@ extension KeyHandler {
if cancelCandidateKey {
if (state is InputState.AssociatedPhrases)
|| mgrPrefs.useSCPCTypingMode
|| isBuilderEmpty
|| isCompositorEmpty
{
//
//
// 使 BackSpace
// isBuilderEmpty
// isCompositorEmpty
clear()
stateCallback(InputState.EmptyIgnoringPreviousState())
} else {

View File

@ -151,7 +151,7 @@ extension KeyHandler {
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
// compositor in other words, if we don't have a tone marker, we just
// update the composing buffer.
let composeReading = composer.hasToneMarker()
if !composeReading {
@ -177,12 +177,12 @@ extension KeyHandler {
IME.prtDebugIntel("B49C0979語彙庫內無「\(reading)」的匹配記錄。")
errorCallback()
composer.clear()
stateCallback((builderLength == 0) ? InputState.EmptyIgnoringPreviousState() : buildInputtingState)
stateCallback((compositorLength == 0) ? InputState.EmptyIgnoringPreviousState() : buildInputtingState)
return true
}
// ... and insert it into the grid...
insertReadingToBuilderAtCursor(reading: reading)
insertToCompositorAtCursor(reading: reading)
// ... then walk the grid...
let poppedText = popOverflowComposingTextAndWalk
@ -248,7 +248,7 @@ extension KeyHandler {
if input.isSpace {
// If the Space key is NOT set to be a selection key
if input.isShiftHold || !mgrPrefs.chooseCandidateUsingSpace {
if builderCursorIndex >= builderLength {
if compositorCursorIndex >= compositorLength {
let composingBuffer = currentState.composingBuffer
if !composingBuffer.isEmpty {
stateCallback(InputState.Committing(poppedText: composingBuffer))
@ -257,7 +257,7 @@ extension KeyHandler {
stateCallback(InputState.Committing(poppedText: " "))
stateCallback(InputState.Empty())
} else if ifLangModelHasUnigrams(forKey: " ") {
insertReadingToBuilderAtCursor(reading: " ")
insertToCompositorAtCursor(reading: " ")
let poppedText = popOverflowComposingTextAndWalk
let inputting = buildInputtingState
inputting.poppedText = poppedText
@ -363,7 +363,7 @@ extension KeyHandler {
if input.isOptionHold {
if ifLangModelHasUnigrams(forKey: "_punctuation_list") {
if composer.isEmpty {
insertReadingToBuilderAtCursor(reading: "_punctuation_list")
insertToCompositorAtCursor(reading: "_punctuation_list")
let poppedText: String! = popOverflowComposingTextAndWalk
let inputting = buildInputtingState
inputting.poppedText = poppedText

View File

@ -34,7 +34,7 @@ extension KeyHandler {
}
var actualCandidateCursorIndex: Int {
var cursorIndex = builderCursorIndex
var cursorIndex = compositorCursorIndex
switch mgrPrefs.useRearCursorMode {
case false:
do {
@ -52,7 +52,7 @@ extension KeyHandler {
// (i.e. the cursor is always at the rear of the phrase.)
// No crossing.
switch cursorIndex {
case builderLength: cursorIndex -= 1
case compositorLength: cursorIndex -= 1
default: break
}
}

View File

@ -48,44 +48,44 @@ extension KeyHandler {
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
// (the actual cursor on the screen) with the compositor'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.
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
if readingCursorIndex > compositorCursorIndex {
readingCursorIndex = compositorCursorIndex
}
// 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 {
switch compositorCursorIndex {
case compositor.readings.count...:
tooltipParameterRef[0] = compositor.readings[compositor.readings.count - 1]
case 0:
tooltipParameterRef[1] = compositor.readings[builderCursorIndex]
tooltipParameterRef[1] = compositor.readings[compositorCursorIndex]
default:
do {
tooltipParameterRef[0] = compositor.readings[builderCursorIndex - 1]
tooltipParameterRef[1] = compositor.readings[builderCursorIndex]
tooltipParameterRef[0] = compositor.readings[compositorCursorIndex - 1]
tooltipParameterRef[1] = compositor.readings[compositorCursorIndex]
}
}
}
@ -260,7 +260,7 @@ extension KeyHandler {
}
if composer.isEmpty {
insertReadingToBuilderAtCursor(reading: customPunctuation)
insertToCompositorAtCursor(reading: customPunctuation)
let poppedText = popOverflowComposingTextAndWalk
let inputting = buildInputtingState
inputting.poppedText = poppedText
@ -385,7 +385,7 @@ extension KeyHandler {
if composer.hasToneMarker(withNothingElse: true) {
composer.clear()
} else if composer.isEmpty {
if builderCursorIndex >= 0 {
if compositorCursorIndex >= 0 {
deleteBuilderReadingInFrontOfCursor()
walk()
} else {
@ -398,7 +398,7 @@ extension KeyHandler {
composer.doBackSpace()
}
if composer.isEmpty, builderLength == 0 {
if composer.isEmpty, compositorLength == 0 {
stateCallback(InputState.EmptyIgnoringPreviousState())
} else {
stateCallback(buildInputtingState)
@ -416,7 +416,7 @@ extension KeyHandler {
guard state is InputState.Inputting else { return false }
if composer.isEmpty {
if builderCursorIndex != builderLength {
if compositorCursorIndex != compositorLength {
deleteBuilderReadingToTheFrontOfCursor()
walk()
let inputting = buildInputtingState
@ -472,8 +472,8 @@ extension KeyHandler {
return true
}
if builderCursorIndex != 0 {
builderCursorIndex = 0
if compositorCursorIndex != 0 {
compositorCursorIndex = 0
stateCallback(buildInputtingState)
} else {
IME.prtDebugIntel("66D97F90")
@ -500,8 +500,8 @@ extension KeyHandler {
return true
}
if builderCursorIndex != builderLength {
builderCursorIndex = builderLength
if compositorCursorIndex != compositorLength {
compositorCursorIndex = compositorLength
stateCallback(buildInputtingState)
} else {
IME.prtDebugIntel("9B69908E")
@ -534,7 +534,7 @@ extension KeyHandler {
// If reading is not empty, we cancel the reading.
if !composer.isEmpty {
composer.clear()
if builderLength == 0 {
if compositorLength == 0 {
stateCallback(InputState.EmptyIgnoringPreviousState())
} else {
stateCallback(buildInputtingState)
@ -580,8 +580,8 @@ extension KeyHandler {
stateCallback(state)
}
} else {
if builderCursorIndex < builderLength {
builderCursorIndex += 1
if compositorCursorIndex < compositorLength {
compositorCursorIndex += 1
stateCallback(buildInputtingState)
} else {
IME.prtDebugIntel("A96AAD58")
@ -629,8 +629,8 @@ extension KeyHandler {
stateCallback(state)
}
} else {
if builderCursorIndex > 0 {
builderCursorIndex -= 1
if compositorCursorIndex > 0 {
compositorCursorIndex -= 1
stateCallback(buildInputtingState)
} else {
IME.prtDebugIntel("7045E6F3")
@ -677,7 +677,7 @@ 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 walkedAnchors {
length += anchor.spanningLength

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

View File

@ -42,8 +42,8 @@ extension vChewing {
/// LMInstantiator is a facade for managing a set of models including
/// the input method language model, user phrases and excluded phrases.
///
/// It is the primary model class that the input controller and grammar builder
/// of vChewing talks to. When the grammar builder starts to build a sentence
/// It is the primary model class that the input controller and input compositor
/// 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
/// if there are valid unigrams, and use returned unigrams to produce the final
/// results.

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,7 +112,7 @@ extension Megrez {
return true
}
///
///
///
///
///
@ -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

@ -17,7 +17,7 @@
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 */; };
@ -286,7 +286,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; };
@ -764,7 +764,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 */,
@ -1117,7 +1117,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;