Skip to content

Commit

Permalink
Use regex to search for all words separately
Browse files Browse the repository at this point in the history
  • Loading branch information
uramer committed Jan 22, 2025
1 parent a8acc04 commit c10dd7b
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 112 deletions.
109 changes: 33 additions & 76 deletions Systems/Handbook/Gui/GuiDialogHandbook.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using Vintagestory.API.Client;
using Vintagestory.API.Common;
using Vintagestory.API.Config;
using Vintagestory.API.Util;

namespace Vintagestory.GameContent
{
Expand Down Expand Up @@ -451,88 +451,45 @@ protected void FilterItemsBySearchText(string text)
FilterItems();
}


private int CountMatches(string text, Regex regex)
{
return regex.Matches(text).Count;
}

public void FilterItems()
{
string text = currentSearchText?.ToLowerInvariant();
string[] texts;
bool logicalAnd = false; // true if "and" is present; false if "or" or no logical operator is present
if (text == null)
string searchText = currentSearchText?.ToLowerInvariant() ?? "";
string[] searchWords = Regex.Split(searchText, "\\s+", RegexOptions.Multiline);
var pattern = searchText.Length == 0 ? ".*" : $"({String.Join("|", searchWords.Select(w => $"{Regex.Escape(w)}"))})";
var regex = new Regex(pattern, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled | RegexOptions.Multiline);

var weightedPages = new List<WeightedHandbookPage>();
allHandbookPages.ForEach(page =>
{
texts = new string[0];
}
else
var pageText = page.GetPageText();
var titleMathces = CountMatches(pageText.Title ?? "", regex);
var textMatches = CountMatches(pageText.Text ?? "", regex);
if (titleMathces > 0 || textMatches > 0)
weightedPages.Add(new WeightedHandbookPage {
Page = page,
TitleMatches = titleMathces,
TitleLength = pageText.Title?.Length ?? 0,
TextMatches = textMatches,
});
});
weightedPages.Sort((a, b) =>
{
if (text.Contains(" or ", StringComparison.Ordinal))
{
texts = text.Split(new string[] { " or " }, StringSplitOptions.RemoveEmptyEntries).OrderBy(str => str.Length).ToArray();
}
else if (text.Contains(" and ", StringComparison.Ordinal))
{
texts = text.Split(new string[] { " and " }, StringSplitOptions.RemoveEmptyEntries).OrderBy(str => str.Length).ToArray();
logicalAnd = texts.Length > 1;
}
else
{
texts = new string[] { text };
}
int countEmpty = 0;
for (int i = 0; i < texts.Length; i++)
{
texts[i] = texts[i].ToSearchFriendly().Trim(); // Only remove diacritical marks etc after splitting on " or ", helps with languages such as Icelandic where "ör" is a word (a type of bow)
if (texts[i].Length == 0) countEmpty++;
}
if (countEmpty > 0)
{
string[] newTexts = new string[texts.Length - countEmpty];
int j = 0;
for (int i = 0; i < texts.Length; i++)
{
if (texts[i].Length == 0) continue;
newTexts[j++] = texts[i];
}
texts = newTexts;
logicalAnd = logicalAnd && texts.Length > 1;
}
}
var titleSort = b.TitleMatches - a.TitleMatches;
if (titleSort != 0) return titleSort;
var textSort = b.TextMatches - a.TextMatches;
if (textSort != 0) return textSort;
// Prefer shorter matches, efffectively prioritizing more "exact" matches
// e. g. "Iron Plate" over "Plate Armor (Iron)" when searching "Iron Plate"
return a.TitleLength - b.TitleLength;
});

List<WeightedHandbookPage> foundPages = new List<WeightedHandbookPage>();
shownHandbookPages.Clear();

if (!loadingPagesAsync)
{
for (int i = 0; i < allHandbookPages.Count; i++)
{
GuiHandbookPage page = allHandbookPages[i];
if (currentCatgoryCode != null && page.CategoryCode != currentCatgoryCode) continue;
if (page.IsDuplicate) continue;

float weight = 1;
bool matched = logicalAnd; // Normally (for no logical operator or logical operator OR) no match unless any found; if it's logical AND then we have no match if any in texts are not found (and texts length cannot be 0)

for (int j = 0; j < texts.Length; j++)
{
weight = page.GetTextMatchWeight(texts[j]);
if (weight > 0)
{
if (!logicalAnd) { matched = true; break; }
}
else
{
if (logicalAnd) { matched = false; break; };
}
}
if (!matched && texts.Length > 0) continue;

foundPages.Add(new WeightedHandbookPage() { Page = page, Weight = weight });
}

foreach (var val in foundPages.OrderByDescending(wpage => wpage.Weight))
{
shownHandbookPages.Add(val.Page);
}
}
weightedPages.ForEach(page => shownHandbookPages.Add(page.Page));

GuiElementFlatList stacklist = overviewGui.GetFlatList("stacklist");
stacklist.CalcTotalHeight();
Expand Down
13 changes: 5 additions & 8 deletions Systems/Handbook/Gui/GuiHandbookCommandPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,12 @@ protected virtual RichTextComponentBase[] GetPageText(ICoreClientAPI capi, ItemS
return VtmlUtil.Richtextify(capi, "<font size=\"24\"><strong>" + Command.CallSyntax + "</strong></font>\n\n" + TextCacheAll, CairoFont.WhiteSmallText());
}

public override float GetTextMatchWeight(string searchText)
public override PageText GetPageText()
{
string title = TextCacheTitle;
if (title.Equals(searchText, StringComparison.InvariantCultureIgnoreCase)) return searchWeightOffset + 3;
if (title.StartsWith(searchText + " ", StringComparison.InvariantCultureIgnoreCase)) return searchWeightOffset + 2.75f + Math.Max(0, 15 - title.Length) / 100f;
if (title.StartsWith(searchText, StringComparison.InvariantCultureIgnoreCase)) return searchWeightOffset + 2.5f + Math.Max(0, 15 - title.Length) / 100f;
if (title.CaseInsensitiveContains(searchText)) return searchWeightOffset + 2;
if (TextCacheAll.CaseInsensitiveContains(searchText)) return searchWeightOffset + 1;
return 0;
return new PageText {
Title = TextCacheTitle,
Text = TextCacheAll,
};
}
}

Expand Down
14 changes: 6 additions & 8 deletions Systems/Handbook/Gui/GuiHandbookItemStackPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,15 +117,13 @@ protected virtual RichTextComponentBase[] GetPageText(ICoreClientAPI capi, ItemS
return Stack.Collectible.GetBehavior<CollectibleBehaviorHandbookTextAndExtraInfo>()?.GetHandbookInfo(dummySlot, capi, allStacks, openDetailPageFor) ?? new RichTextComponentBase[0];
}

public override float GetTextMatchWeight(string searchText)
public override PageText GetPageText()
{
string title = TextCacheTitle;
if (title.Equals(searchText, StringComparison.InvariantCultureIgnoreCase)) return searchWeightOffset + 3;
if (title.StartsWith(searchText + " ", StringComparison.InvariantCultureIgnoreCase)) return searchWeightOffset + 2.75f + Math.Max(0, 15 - title.Length) / 100f;
if (title.StartsWith(searchText, StringComparison.InvariantCultureIgnoreCase)) return searchWeightOffset + 2.5f + Math.Max(0, 15 - title.Length) / 100f;
if (title.CaseInsensitiveContains(searchText)) return searchWeightOffset + 2;
if (TextCacheAll.CaseInsensitiveContains(searchText)) return searchWeightOffset + 1;
return 0;
return new PageText
{
Title = TextCacheTitle,
Text = TextCacheAll,
};
}
}

Expand Down
13 changes: 10 additions & 3 deletions Systems/Handbook/Gui/GuiHandbookPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,18 @@ namespace Vintagestory.GameContent
{
public struct WeightedHandbookPage
{
public float Weight;
public int TitleMatches;
public int TextMatches;
public int TitleLength;
public GuiHandbookPage Page;
}

public struct PageText
{
public string Title;
public string Text;
}

public abstract class GuiHandbookPage : IFlatListItem
{
public int PageNumber;
Expand All @@ -25,8 +33,7 @@ public abstract class GuiHandbookPage : IFlatListItem
public abstract void RenderListEntryTo(ICoreClientAPI capi, float dt, double x, double y, double cellWdith, double cellHeight);
public abstract void Dispose();
public bool Visible { get; set; } = true;

public abstract float GetTextMatchWeight(string text);
public abstract PageText GetPageText();
public abstract bool IsDuplicate { get; }

public abstract void ComposePage(GuiComposer detailViewGui, ElementBounds textBounds, ItemStack[] allstacks, ActionConsumable<string> openDetailPageFor);
Expand Down
13 changes: 6 additions & 7 deletions Systems/Handbook/Gui/GuiHandbookTextPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,13 @@ public void Recompose(ICoreClientAPI capi)
Texture = new TextTextureUtil(capi).GenTextTexture(Lang.Get(Title), CairoFont.WhiteSmallText());
}

public override float GetTextMatchWeight(string searchText)
public override PageText GetPageText()
{
if (titleCached.Equals(searchText, StringComparison.InvariantCultureIgnoreCase)) return 4;
if (titleCached.StartsWith(searchText + " ", StringComparison.InvariantCultureIgnoreCase)) return 3.5f;
if (titleCached.StartsWith(searchText, StringComparison.InvariantCultureIgnoreCase)) return 3f;
if (titleCached.CaseInsensitiveContains(searchText)) return 2.75f;
if (Text.CaseInsensitiveContains(searchText)) return 1.25f;
return 0;
return new PageText
{
Title = titleCached,
Text = Text,
};
}

public override void RenderListEntryTo(ICoreClientAPI capi, float dt, double x, double y, double cellWidth, double cellHeight)
Expand Down
10 changes: 0 additions & 10 deletions Systems/Handbook/Tutorial/GuiHandbookTutorialPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,16 +149,6 @@ public override void ComposePage(GuiComposer detailViewGui, ElementBounds textBo
detailViewGui.AddRichtext(comps, textBounds, "richtext");
}

public override float GetTextMatchWeight(string text)
{
/*string title = TextCacheTitle;
if (title.Equals(searchText, StringComparison.InvariantCultureIgnoreCase)) return 3;
if (title.StartsWith(searchText, StringComparison.InvariantCultureIgnoreCase)) return 2.5f;
if (title.CaseInsensitiveContains(searchText)) return 2;
if (TextCacheAll.CaseInsensitiveContains(searchText)) return 1;*/
return 0;
}


public override void Dispose()
{
Expand Down

0 comments on commit c10dd7b

Please sign in to comment.