diff --git a/Megrez.Tests/LMDataForTests.cs b/Megrez.Tests/LMDataForTests.cs index 9c91428..7b66d39 100644 --- a/Megrez.Tests/LMDataForTests.cs +++ b/Megrez.Tests/LMDataForTests.cs @@ -190,6 +190,7 @@ nian2zhong1 年中 -11.373044 gao1ke1ji4 高科技 -9.842421 zhe4yang4 這樣 -6.000000 // Non-LibTaBE ni3zhe4 你這 -9.000000 // Non-LibTaBE +ke1ke1 顆顆 -8.000000 // Non-LibTaBE jiao4 教 -3.676169 jiao4 較 -3.24869962 jiao4yu4 教育 -3.32220565 diff --git a/Megrez.Tests/Megrez.Tests.csproj b/Megrez.Tests/Megrez.Tests.csproj index f825975..9223003 100644 --- a/Megrez.Tests/Megrez.Tests.csproj +++ b/Megrez.Tests/Megrez.Tests.csproj @@ -4,7 +4,7 @@ net6.0 enable false - 2.6.0 + 2.6.2 diff --git a/Megrez.Tests/MegrezTests.cs b/Megrez.Tests/MegrezTests.cs index 9abf7de..cba4320 100644 --- a/Megrez.Tests/MegrezTests.cs +++ b/Megrez.Tests/MegrezTests.cs @@ -538,4 +538,42 @@ public void Test21_Compositor_hardCopy() { List resultB = compositorB.Walk().WalkedNodes; Assert.True(resultA.SequenceEqual(resultB)); } + + [Test] + public void Test22_Compositor_SanitizingNodeCrossing() { + SimpleLM theLM = new(input: StrSampleData); + string rawReadings = "ke1 ke1"; + Compositor compositor = new(langModel: theLM, separator: ""); + foreach (string key in rawReadings.Split(separator: " ")) { + compositor.InsertKey(key); + } + int a = compositor.FetchCandidatesAt(givenLocation: 1, filter: Compositor.CandidateFetchFilter.BeginAt) + .Select(x => x.KeyArray.Count) + .Max(); + int b = compositor.FetchCandidatesAt(givenLocation: 1, filter: Compositor.CandidateFetchFilter.EndAt) + .Select(x => x.KeyArray.Count) + .Max(); + int c = compositor.FetchCandidatesAt(givenLocation: 0, filter: Compositor.CandidateFetchFilter.BeginAt) + .Select(x => x.KeyArray.Count) + .Max(); + int d = compositor.FetchCandidatesAt(givenLocation: 2, filter: Compositor.CandidateFetchFilter.EndAt) + .Select(x => x.KeyArray.Count) + .Max(); + Assert.AreEqual(actual: $"{a} {b} {c} {d}", expected: "1 1 2 2"); + compositor.Cursor = compositor.Length; + compositor.InsertKey("jin1"); + a = compositor.FetchCandidatesAt(givenLocation: 1, filter: Compositor.CandidateFetchFilter.BeginAt) + .Select(x => x.KeyArray.Count) + .Max(); + b = compositor.FetchCandidatesAt(givenLocation: 1, filter: Compositor.CandidateFetchFilter.EndAt) + .Select(x => x.KeyArray.Count) + .Max(); + c = compositor.FetchCandidatesAt(givenLocation: 0, filter: Compositor.CandidateFetchFilter.BeginAt) + .Select(x => x.KeyArray.Count) + .Max(); + d = compositor.FetchCandidatesAt(givenLocation: 2, filter: Compositor.CandidateFetchFilter.EndAt) + .Select(x => x.KeyArray.Count) + .Max(); + Assert.AreEqual(actual: $"{a} {b} {c} {d}", expected: "1 1 2 2"); + } } diff --git a/Megrez.sln b/Megrez.sln index dc69c73..7f6e0e9 100644 --- a/Megrez.sln +++ b/Megrez.sln @@ -52,6 +52,6 @@ Global $0.DotNetNamingPolicy = $4 $4.DirectoryNamespaceAssociation = PrefixedHierarchical $0.StandardHeader = $5 - version = 2.6.0 + version = 2.6.2 EndGlobalSection EndGlobal diff --git a/Megrez/Megrez.csproj b/Megrez/Megrez.csproj index 33c44cc..7b423fa 100644 --- a/Megrez/Megrez.csproj +++ b/Megrez/Megrez.csproj @@ -3,16 +3,16 @@ net6.0 enable - 2.6.0 + 2.6.2 vChewing.Megrez Shiki Suen Atelier Inmu (c) 2022 and onwards The vChewing Project for Megrez-specific changes; (c) 2022 and onwards Lukhnos Liu for upstream contents. https://github.com/ShikiSuen/MegrezNT zh-TW - 2.6.0 - 2.6.0 - 2.6.0 + 2.6.2 + 2.6.2 + 2.6.2 Megrez True README.md diff --git a/Megrez/src/3_KeyValuePaired.cs b/Megrez/src/3_KeyValuePaired.cs index 546d330..f966fa7 100644 --- a/Megrez/src/3_KeyValuePaired.cs +++ b/Megrez/src/3_KeyValuePaired.cs @@ -165,21 +165,27 @@ public enum CandidateFetchFilter { /// /// 返回在當前位置的所有候選字詞(以詞音配對的形式)。如果組字器內有幅位、且游標 /// 位於組字器的(文字輸入順序的)最前方(也就是游標位置的數值是最大合規數值)的 - /// 話,那麼這裡會用到 location - 1、以免去在呼叫該函式後再處理的麻煩。 + /// 話,那麼這裡會對 location 的位置自動減去 1、以免去在呼叫該函式後再處理的麻煩。 /// - /// 游標位置。 + /// 游標位置,必須是顯示的游標位置、不得做任何事先糾偏處理。 /// 候選字音配對陣列。 /// - public List FetchCandidatesAt(int location, CandidateFetchFilter filter = CandidateFetchFilter.All) { + public List FetchCandidatesAt(int? givenLocation = null, + CandidateFetchFilter filter = CandidateFetchFilter.All) { List result = new(); if (Keys.IsEmpty()) return result; + int location = Math.Max(0, Math.Min(givenLocation ?? Cursor, Keys.Count)); + if (filter == CandidateFetchFilter.EndAt) { + if (location == Keys.Count) filter = CandidateFetchFilter.All; + location -= 1; + } location = Math.Max(0, Math.Min(location, Keys.Count - 1)); - // 按照讀音的長度(幅位長度)來給節點排序。 List anchors = FetchOverlappingNodesAt(location).StableSorted((x, y) => x.SpanLength.CompareTo(y.SpanLength)); string keyAtCursor = Keys[location]; - foreach (Node theNode in anchors.Select(x => x.Node).Where(x => !x.KeyArray.IsEmpty())) { + foreach (NodeAnchor theAnchor in anchors) { + Node theNode = theAnchor.Node; foreach (Unigram gram in theNode.Unigrams) { switch (filter) { case CandidateFetchFilter.All: @@ -187,10 +193,11 @@ public List FetchCandidatesAt(int location, CandidateFetchFilter if (!theNode.KeyArray.Contains(keyAtCursor)) continue; break; case CandidateFetchFilter.BeginAt: - if (theNode.KeyArray.First() != keyAtCursor) continue; + if (theAnchor.SpanIndex != location) continue; break; case CandidateFetchFilter.EndAt: if (theNode.KeyArray.Last() != keyAtCursor) continue; + if (theNode.SpanLength >= 2 && theAnchor.SpanIndex + theAnchor.SpanLength - 1 != location) continue; break; } result.Add(new(theNode.KeyArray, gram.Value)); diff --git a/Megrez/src/4_SpanUnit.cs b/Megrez/src/4_SpanUnit.cs index b46e49b..b424d1b 100644 --- a/Megrez/src/4_SpanUnit.cs +++ b/Megrez/src/4_SpanUnit.cs @@ -112,11 +112,13 @@ public bool DropNodesOfOrBeyond(int length) { /// 一個包含所有與該位置重疊的節點的陣列。 internal List FetchOverlappingNodesAt(int givenLocation) { List results = new(); - if (Spans.IsEmpty() || givenLocation >= Spans.Count) return results; + givenLocation = Math.Max(0, Math.Min(givenLocation, Keys.Count - 1)); + if (Spans.IsEmpty()) return results; // 先獲取該位置的所有單字節點。 foreach (int spanLength in new BRange(1, Spans[givenLocation].MaxLength + 1)) { if (Spans[givenLocation].NodeOf(spanLength) is not {} node) continue; + if (string.IsNullOrEmpty(node.KeyArray.Joined())) continue; results.Add(new(node, givenLocation)); } @@ -127,6 +129,7 @@ internal List FetchOverlappingNodesAt(int givenLocation) { if (alpha > bravo) continue; foreach (int theLength in new BRange(alpha, bravo + 1)) { if (Spans[theLocation].NodeOf(theLength) is not {} node) continue; + if (string.IsNullOrEmpty(node.KeyArray.Joined())) continue; results.Add(new(node, theLocation)); } }