Refactor TranslationMapping class

- Always add index to mapping rather than only Chinese characters
- Simplify mapping algorithm
- Add unit test for TranslationMapping
This commit is contained in:
VictoriousRaptor 2025-07-13 15:56:25 +08:00
parent 100f753e9b
commit d9e89ad610
3 changed files with 66 additions and 68 deletions

View file

@ -107,12 +107,12 @@ namespace Flow.Launcher.Infrastructure
if (previousIsChinese)
{
resultBuilder.Append(' ');
map.AddNewIndex(i, resultBuilder.Length, translated.Length);
map.AddNewIndex(resultBuilder.Length, translated.Length);
resultBuilder.Append(translated);
}
else
{
map.AddNewIndex(i, resultBuilder.Length, translated.Length);
map.AddNewIndex(resultBuilder.Length, translated.Length);
resultBuilder.Append(translated);
previousIsChinese = true;
}
@ -124,6 +124,7 @@ namespace Flow.Launcher.Infrastructure
previousIsChinese = false;
resultBuilder.Append(' ');
}
map.AddNewIndex(resultBuilder.Length, resultList[i].Length);
resultBuilder.Append(resultList[i]);
}
}

View file

@ -8,82 +8,23 @@ namespace Flow.Launcher.Infrastructure
{
private bool constructed;
private readonly List<int> originalIndexes = new();
private readonly List<int> translatedIndexes = new();
// Asssuming one original item maps to multi translated items
// list[i] is the last translated index + 1 of original index i
private readonly List<int> originalToTranslated = [];
private int translatedLength = 0;
public void AddNewIndex(int originalIndex, int translatedIndex, int length)
public void AddNewIndex(int translatedIndex, int length)
{
if (constructed)
throw new InvalidOperationException("Mapping shouldn't be changed after constructed");
originalIndexes.Add(originalIndex);
translatedIndexes.Add(translatedIndex);
translatedIndexes.Add(translatedIndex + length);
translatedLength += length - 1;
originalToTranslated.Add(translatedIndex + length);
}
public int MapToOriginalIndex(int translatedIndex)
{
if (translatedIndex > translatedIndexes.Last())
return translatedIndex - translatedLength - 1;
int loc = originalToTranslated.BinarySearch(translatedIndex);
int lowerBound = 0;
int upperBound = originalIndexes.Count - 1;
int count = 0;
// Corner case handle
if (translatedIndex < translatedIndexes[0])
return translatedIndex;
if (translatedIndex > translatedIndexes.Last())
{
int indexDef = 0;
for (int k = 0; k < originalIndexes.Count; k++)
{
indexDef += translatedIndexes[k * 2 + 1] - translatedIndexes[k * 2];
}
return translatedIndex - indexDef - 1;
}
// Binary Search with Range
for (int i = originalIndexes.Count / 2;; count++)
{
if (translatedIndex < translatedIndexes[i * 2])
{
// move to lower middle
upperBound = i;
i = (i + lowerBound) / 2;
}
else if (translatedIndex > translatedIndexes[i * 2 + 1] - 1)
{
lowerBound = i;
// move to upper middle
// due to floor of integer division, move one up on corner case
i = (i + upperBound + 1) / 2;
}
else
{
return originalIndexes[i];
}
if (upperBound - lowerBound <= 1 &&
translatedIndex > translatedIndexes[lowerBound * 2 + 1] &&
translatedIndex < translatedIndexes[upperBound * 2])
{
int indexDef = 0;
for (int j = 0; j < upperBound; j++)
{
indexDef += translatedIndexes[j * 2 + 1] - translatedIndexes[j * 2];
}
return translatedIndex - indexDef - 1;
}
}
return loc > 0 ? loc : ~loc;
}
public void endConstruct()

View file

@ -0,0 +1,56 @@
using Flow.Launcher.Infrastructure;
using NUnit.Framework;
using NUnit.Framework.Legacy;
namespace Flow.Launcher.Test
{
[TestFixture]
public class TranslationMappingTest
{
[Test]
public void AddNewIndex_ShouldAddTranslatedIndexPlusLength()
{
var mapping = new TranslationMapping();
mapping.AddNewIndex(5, 3);
mapping.AddNewIndex(8, 2);
// 5+3=8, 8+2=10
ClassicAssert.AreEqual(2, GetOriginalToTranslatedCount(mapping));
ClassicAssert.AreEqual(8, GetOriginalToTranslatedAt(mapping, 0));
ClassicAssert.AreEqual(10, GetOriginalToTranslatedAt(mapping, 1));
}
[TestCase(0, 0)]
[TestCase(2, 1)]
[TestCase(3, 1)]
[TestCase(5, 2)]
[TestCase(6, 2)]
public void MapToOriginalIndex_ShouldReturnExpectedIndex(int translatedIndex, int expectedOriginalIndex)
{
var mapping = new TranslationMapping();
// a测试
// a Ce Shi
mapping.AddNewIndex(0, 1);
mapping.AddNewIndex(2, 2);
mapping.AddNewIndex(5, 3);
var result = mapping.MapToOriginalIndex(translatedIndex);
ClassicAssert.AreEqual(expectedOriginalIndex, result);
}
private int GetOriginalToTranslatedCount(TranslationMapping mapping)
{
var field = typeof(TranslationMapping).GetField("originalToTranslated", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
var list = (System.Collections.Generic.List<int>)field.GetValue(mapping);
return list.Count;
}
private int GetOriginalToTranslatedAt(TranslationMapping mapping, int index)
{
var field = typeof(TranslationMapping).GetField("originalToTranslated", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
var list = (System.Collections.Generic.List<int>)field.GetValue(mapping);
return list[index];
}
}
}