@@ -151,6 +151,7 @@ extension Array where Element == Megrez.GramInPath {
151151 ///
152152 /// - Remark: 除非有專門指定游標,否則身為 `[GramInPath]` 自身的
153153 /// 「陣列最尾端」(也就是打字方向上最前方)的那個 Gram 會被當成 Head。
154+ /// 若游標落在多字詞節點內,Head 會選擇游標右方(文字輸入方向上的下一個)讀音。
154155 /// - Parameters:
155156 /// - cursor: 指定用於當作 head 的游標位置;若為 nil 則取陣列尾端。
156157 /// - maxContext: 最多向前取用的上下文節點數(含 head),預設 3。
@@ -159,41 +160,83 @@ extension Array where Element == Megrez.GramInPath {
159160 maxContext: Int = 3
160161 )
161162 -> ( ngramKey: String , candidate: String , headReading: String ) ? {
162- let perceptedGIP : Megrez . GramInPath ?
163- if let cursor, ( 0 ..< self . totalKeyCount) . contains ( cursor) {
164- perceptedGIP = findGram ( at: cursor) ? . gram
163+ guard maxContext > 0 else { return nil }
164+
165+ let headInfo : ( pair: Megrez . GramInPath , range: Range < Int > ) ?
166+ let resolvedCursor : Int
167+ if let cursor, ( 0 ..< totalKeyCount) . contains ( cursor) , let hit = findGram ( at: cursor) {
168+ headInfo = ( pair: hit. gram, range: hit. range)
169+ resolvedCursor = Swift . max (
170+ hit. range. lowerBound,
171+ Swift . min ( cursor, hit. range. upperBound - 1 )
172+ )
173+ } else if let tail = last {
174+ let lowerBound = totalKeyCount - tail. keyArray. count
175+ headInfo = ( pair: tail, range: lowerBound ..< totalKeyCount)
176+ resolvedCursor = Swift . max ( lowerBound, totalKeyCount - 1 )
165177 } else {
166- perceptedGIP = last
178+ headInfo = nil
179+ resolvedCursor = 0
167180 }
168- guard let perceptedGIP else { return nil }
169- var arrGIPs = self
170- while arrGIPs. last? . gram !== perceptedGIP. gram { arrGIPs. removeLast ( ) }
171- var isHead = true
172- var outputCells = [ String] ( )
173- loopProc: while !arrGIPs. isEmpty, let frontendPair = arrGIPs. last {
174- defer { arrGIPs = arrGIPs. dropLast ( ) }
175-
176- func makeNGramKeyCell( isHead: Bool ) -> String ? {
177- // 字音數與字數不一致的內容會被拋棄。
178- guard !frontendPair. isReadingMismatched else { return nil }
179- guard !frontendPair. value. isEmpty else { return nil }
180- guard !frontendPair. keyArray. joined ( ) . isEmpty else { return nil }
181- let keyChain = frontendPair. keyArray. joined ( separator: " - " )
182- guard !keyChain. contains ( " _ " ) else { return nil }
183- // 前置單元只記錄讀音,在其後的單元則同時記錄讀音與字詞
184- return isHead ? keyChain : " ( \( keyChain) : \( frontendPair. value) ) "
181+
182+ guard let headInfo else { return nil }
183+ let headPair = headInfo. pair
184+ let headRange = headInfo. range
185+ let headKeyOffset = Swift . max ( 0 , resolvedCursor - headRange. lowerBound)
186+ guard headPair. keyArray. indices. contains ( headKeyOffset) else { return nil }
187+
188+ let placeholder = " () "
189+ let readingSeparator = Megrez . Compositor. theSeparator
190+
191+ func isPunctuation( _ pair: Megrez . GramInPath ) -> Bool {
192+ guard let firstReading = pair. keyArray. first else { return false }
193+ return firstReading. first == " _ "
194+ }
195+
196+ func combinedString( for pair: Megrez . GramInPath ) -> String ? {
197+ guard !pair. value. isEmpty else { return nil }
198+ guard !pair. keyArray. isEmpty else { return nil }
199+ let reading = pair. joinedCurrentKey ( by: readingSeparator)
200+ guard !reading. isEmpty else { return nil }
201+ return " ( \( reading) , \( pair. value) ) "
202+ }
203+
204+ guard let headIndex = lastIndex ( where: { $0. gram === headPair. gram } )
205+ ?? firstIndex ( of: headPair)
206+ else { return nil }
207+
208+ var resultCells = [ String] ( repeating: placeholder, count: maxContext)
209+ resultCells [ maxContext - 1 ] = combinedString ( for: headPair) ?? placeholder
210+
211+ var contextSlot = maxContext - 2
212+ var currentIndex = headIndex - 1
213+ var encounteredPunctuation = false
214+
215+ while contextSlot >= 0 {
216+ guard currentIndex >= 0 else {
217+ resultCells [ contextSlot] = placeholder
218+ contextSlot -= 1
219+ continue
220+ }
221+ let currentPair = self [ currentIndex]
222+ currentIndex -= 1
223+
224+ if encounteredPunctuation || isPunctuation ( currentPair) {
225+ encounteredPunctuation = true
226+ resultCells [ contextSlot] = placeholder
227+ contextSlot -= 1
228+ continue
185229 }
186230
187- guard let keyCellStr = makeNGramKeyCell ( isHead: isHead) else { break loopProc }
188- outputCells. insert ( keyCellStr, at: 0 )
189- if outputCells. count >= maxContext { break loopProc }
190- if isHead { isHead = false }
231+ resultCells [ contextSlot] = combinedString ( for: currentPair) ?? placeholder
232+ contextSlot -= 1
191233 }
192- guard !outputCells. isEmpty else { return nil }
234+
235+ let headReading = headPair. keyArray [ headKeyOffset]
193236 return (
194- " ( \( outputCells . joined ( separator: " , " ) ) ) " ,
195- perceptedGIP . gram . value,
196- perceptedGIP . joinedCurrentKey ( by : " - " )
237+ resultCells . joined ( separator: " & " ) ,
238+ headPair . value,
239+ headReading
197240 )
198241 }
199242}
0 commit comments