diff --git a/Cmdline/Action/Search.cs b/Cmdline/Action/Search.cs
index 8c1a5b6ae2..3b1deb394e 100644
--- a/Cmdline/Action/Search.cs
+++ b/Cmdline/Action/Search.cs
@@ -54,21 +54,19 @@ public int RunCommand(CKAN.KSP ksp, object raw_options)
/// The search term. Case insensitive.
public List PerformSearch(CKAN.KSP ksp, string term)
{
+ // Remove spaces and special characters from the search term.
+ term = CkanModule.nonAlphaNums.Replace(term, "");
+
var registry = RegistryManager.Instance(ksp).registry;
return registry
.Available(ksp.VersionCriteria())
.Where((module) =>
{
- // Extract the description. This is an optional field and may be null.
- string modDesc = string.Empty;
-
- if (!string.IsNullOrEmpty(module.description))
- {
- modDesc = module.description;
- }
-
// Look for a match in each string.
- return module.name.IndexOf(term, StringComparison.OrdinalIgnoreCase) > -1 || module.identifier.IndexOf(term, StringComparison.OrdinalIgnoreCase) > -1 || modDesc.IndexOf(term, StringComparison.OrdinalIgnoreCase) > -1;
+ return module.SearchableName.IndexOf(term, StringComparison.OrdinalIgnoreCase) > -1
+ || module.SearchableIdentifier.IndexOf(term, StringComparison.OrdinalIgnoreCase) > -1
+ || module.SearchableAbstract.IndexOf(term, StringComparison.OrdinalIgnoreCase) > -1
+ || module.SearchableDescription.IndexOf(term, StringComparison.OrdinalIgnoreCase) > -1;
}).ToList();
}
diff --git a/ConsoleUI/ModListScreen.cs b/ConsoleUI/ModListScreen.cs
index 5882e0b9b6..51f9002523 100644
--- a/ConsoleUI/ModListScreen.cs
+++ b/ConsoleUI/ModListScreen.cs
@@ -48,18 +48,17 @@ public ModListScreen(KSPManager mgr, bool dbg)
},
1, 0, ListSortDirection.Descending,
(CkanModule m, string filter) => {
+ // Search for author
if (filter.StartsWith("@")) {
string authorFilt = filter.Substring(1);
if (string.IsNullOrEmpty(authorFilt)) {
return true;
- } else if (m.author != null) {
- foreach (string auth in m.author) {
- if (auth.IndexOf(authorFilt, StringComparison.CurrentCultureIgnoreCase) == 0) {
- return true;
- }
- }
+ } else {
+ // Remove special characters from search term
+ authorFilt = CkanModule.nonAlphaNums.Replace(authorFilt, "");
+ return m.SearchableAuthors.IndexOf(authorFilt, StringComparison.CurrentCultureIgnoreCase) == 0;
}
- return false;
+ // Other special search params: installed, updatable, new, conflicting and dependends
} else if (filter.StartsWith("~")) {
if (filter.Length <= 1) {
// Don't blank the list for just "~" by itself
@@ -97,9 +96,12 @@ public ModListScreen(KSPManager mgr, bool dbg)
}
return false;
} else {
- return m.identifier.IndexOf(filter, StringComparison.CurrentCultureIgnoreCase) >= 0
- || m.name.IndexOf( filter, StringComparison.CurrentCultureIgnoreCase) >= 0
- || m.@abstract.IndexOf( filter, StringComparison.CurrentCultureIgnoreCase) >= 0;
+ filter = CkanModule.nonAlphaNums.Replace(filter, "");
+
+ return m.SearchableIdentifier.IndexOf( filter, StringComparison.CurrentCultureIgnoreCase) >= 0
+ || m.SearchableName.IndexOf( filter, StringComparison.CurrentCultureIgnoreCase) >= 0
+ || m.SearchableAbstract.IndexOf( filter, StringComparison.CurrentCultureIgnoreCase) >= 0
+ || m.SearchableDescription.IndexOf(filter, StringComparison.CurrentCultureIgnoreCase) >= 0;
}
}
);
diff --git a/Core/Registry/AvailableModule.cs b/Core/Registry/AvailableModule.cs
index 6684e6f6e7..b3910c0c08 100644
--- a/Core/Registry/AvailableModule.cs
+++ b/Core/Registry/AvailableModule.cs
@@ -28,8 +28,9 @@ private AvailableModule()
}
[OnDeserialized]
- internal void SetIdentifier(StreamingContext context)
+ internal void DeserialisationFixes(StreamingContext context)
{
+ // Set identifier
var mod = module_version.Values.LastOrDefault();
identifier = mod.identifier;
Debug.Assert(module_version.Values.All(m=>identifier.Equals(m.identifier)));
diff --git a/Core/Types/CkanModule.cs b/Core/Types/CkanModule.cs
index 3071cca452..24f6f24ec7 100644
--- a/Core/Types/CkanModule.cs
+++ b/Core/Types/CkanModule.cs
@@ -480,6 +480,21 @@ public List ProvidesList
}
}
+ // These are used to simplify the search by dropping special chars.
+ [JsonIgnore]
+ public string SearchableName;
+ [JsonIgnore]
+ public string SearchableIdentifier;
+ [JsonIgnore]
+ public string SearchableAbstract;
+ [JsonIgnore]
+ public string SearchableDescription;
+ [JsonIgnore]
+ public string SearchableAuthors;
+ // This regex finds all those special chars.
+ [JsonIgnore]
+ public static readonly Regex nonAlphaNums = new Regex("[^a-zA-Z0-9]", RegexOptions.Compiled);
+
#endregion
#region Constructors
@@ -527,7 +542,6 @@ public CkanModule(string json, IGameComparator comparator)
// Check everything in the spec is defined.
// TODO: This *can* and *should* be done with JSON attributes!
-
foreach (string field in required_fields)
{
object value = null;
@@ -552,6 +566,21 @@ public CkanModule(string json, IGameComparator comparator)
throw new BadMetadataKraken(null, error);
}
}
+
+ // Calculate the Searchables.
+ CalculateSearchables();
+ }
+
+ ///
+ /// Calculate the mod properties used for searching via Regex.
+ ///
+ public void CalculateSearchables()
+ {
+ SearchableIdentifier = identifier == null ? string.Empty : CkanModule.nonAlphaNums.Replace(identifier, "");
+ SearchableName = name == null ? string.Empty : CkanModule.nonAlphaNums.Replace(name, "");
+ SearchableAbstract = @abstract == null ? string.Empty : CkanModule.nonAlphaNums.Replace(@abstract, "");
+ SearchableDescription = description == null ? string.Empty : CkanModule.nonAlphaNums.Replace(description, "");
+ SearchableAuthors = author == null ? string.Empty : CkanModule.nonAlphaNums.Replace(String.Join("", author), "");
}
public string serialise()
@@ -575,6 +604,8 @@ private void DeSerialisationFixes(StreamingContext like_i_could_care)
license = license ?? new List { License.UnknownLicense };
@abstract = @abstract ?? string.Empty;
name = name ?? string.Empty;
+
+ CalculateSearchables();
}
///
diff --git a/GUI/CKAN-GUI.csproj b/GUI/CKAN-GUI.csproj
index e82dcf4976..b1ca27d5cf 100644
--- a/GUI/CKAN-GUI.csproj
+++ b/GUI/CKAN-GUI.csproj
@@ -248,7 +248,7 @@
Component
- Form
+ Form
SelectionDialog.cs
diff --git a/GUI/GUIMod.cs b/GUI/GUIMod.cs
index 60a21362b3..ff3d3f9705 100644
--- a/GUI/GUIMod.cs
+++ b/GUI/GUIMod.cs
@@ -3,7 +3,6 @@
using System.Windows.Forms;
using System.Linq;
using CKAN.Versioning;
-using CKAN.GameVersionProviders;
namespace CKAN
{
@@ -32,6 +31,7 @@ public sealed class GUIMod
public string KSPCompatibilityLong { get; private set; }
public string Abstract { get; private set; }
+ public string Description { get; private set; }
public string Homepage { get; private set; }
public string Identifier { get; private set; }
public bool IsInstallChecked { get; set; }
@@ -41,6 +41,12 @@ public sealed class GUIMod
public bool IsCKAN { get; private set; }
public string Abbrevation { get; private set; }
+ public string SearchableName { get; private set; }
+ public string SearchableIdentifier { get; private set; }
+ public string SearchableAbstract { get; private set; }
+ public string SearchableDescription { get; private set; }
+ public string SearchableAuthors { get; private set; }
+
///
/// Return whether this mod is installable.
/// Used for determining whether to show a checkbox in the leftmost column.
@@ -99,6 +105,7 @@ public GUIMod(CkanModule mod, IRegistryQuerier registry, KspVersionCriteria curr
Name = mod.name.Trim();
Abstract = mod.@abstract.Trim();
+ Description = mod.description?.Trim() ?? string.Empty;
Abbrevation = new string(Name.Split(' ').Where(s => s.Length > 0).Select(s => s[0]).ToArray());
Authors = mod.author == null ? "N/A" : String.Join(",", mod.author);
@@ -116,6 +123,12 @@ public GUIMod(CkanModule mod, IRegistryQuerier registry, KspVersionCriteria curr
?? "N/A";
}
+ // Get the Searchables.
+ SearchableName = mod.SearchableName;
+ SearchableAbstract = mod.SearchableAbstract;
+ SearchableDescription = mod.SearchableDescription;
+ SearchableAuthors = mod.SearchableAuthors;
+
UpdateIsCached();
}
@@ -186,8 +199,9 @@ public GUIMod(string identifier, IRegistryQuerier registry, KspVersionCriteria c
}
// If we have a homepage provided, use that; otherwise use the spacedock page, curse page or the github repo so that users have somewhere to get more info than just the abstract.
-
Homepage = "N/A";
+
+ SearchableIdentifier = CkanModule.nonAlphaNums.Replace(Identifier, "");
}
///
diff --git a/GUI/MainModInfo.cs b/GUI/MainModInfo.cs
index 48bc5095ec..5c8133c506 100644
--- a/GUI/MainModInfo.cs
+++ b/GUI/MainModInfo.cs
@@ -135,9 +135,9 @@ private void UpdateModInfo(GUIMod gui_module)
Util.Invoke(MetadataModuleVersionTextBox, () => MetadataModuleVersionTextBox.Text = gui_module.LatestVersion.ToString());
Util.Invoke(MetadataModuleLicenseTextBox, () => MetadataModuleLicenseTextBox.Text = string.Join(", ", module.license));
Util.Invoke(MetadataModuleAuthorTextBox, () => MetadataModuleAuthorTextBox.Text = gui_module.Authors);
- Util.Invoke(MetadataModuleAbstractLabel, () => MetadataModuleAbstractLabel.Text = module.@abstract);
- Util.Invoke(MetadataModuleDescriptionTextBox, () => MetadataModuleDescriptionTextBox.Text = module.description);
- Util.Invoke(MetadataIdentifierTextBox, () => MetadataIdentifierTextBox.Text = module.identifier);
+ Util.Invoke(MetadataModuleAbstractLabel, () => MetadataModuleAbstractLabel.Text = gui_module.Abstract);
+ Util.Invoke(MetadataModuleDescriptionTextBox, () => MetadataModuleDescriptionTextBox.Text = gui_module.Description);
+ Util.Invoke(MetadataIdentifierTextBox, () => MetadataIdentifierTextBox.Text = gui_module.Identifier);
// If we have a homepage provided, use that; otherwise use the spacedock page, curse page or the github repo so that users have somewhere to get more info than just the abstract.
Util.Invoke(MetadataModuleHomePageLinkLabel, () => MetadataModuleHomePageLinkLabel.Text = gui_module.Homepage.ToString());
diff --git a/GUI/MainModList.cs b/GUI/MainModList.cs
index 2e90a74516..43bcf90780 100644
--- a/GUI/MainModList.cs
+++ b/GUI/MainModList.cs
@@ -802,7 +802,7 @@ public async Task> ComputeChangeSetFromModList(
public bool IsVisible(GUIMod mod)
{
var nameMatchesFilter = IsNameInNameFilter(mod);
- var authorMatchesFilter = IsAuthorInauthorFilter(mod);
+ var authorMatchesFilter = IsAuthorInAuthorFilter(mod);
var abstractMatchesFilter = IsAbstractInDescriptionFilter(mod);
var modMatchesType = IsModInFilter(ModFilter, mod);
var isVisible = nameMatchesFilter && modMatchesType && authorMatchesFilter && abstractMatchesFilter;
@@ -934,19 +934,26 @@ public string StripEpoch(string version)
private bool IsNameInNameFilter(GUIMod mod)
{
- return mod.Name.IndexOf(ModNameFilter, StringComparison.InvariantCultureIgnoreCase) != -1
- || mod.Abbrevation.IndexOf(ModNameFilter, StringComparison.InvariantCultureIgnoreCase) != -1
- || mod.Identifier.IndexOf(ModNameFilter, StringComparison.InvariantCultureIgnoreCase) != -1;
+ string sanitisedModNameFilter = CkanModule.nonAlphaNums.Replace(ModNameFilter, "");
+
+ return mod.Abbrevation.IndexOf(ModNameFilter, StringComparison.InvariantCultureIgnoreCase) != -1
+ || mod.SearchableName.IndexOf(sanitisedModNameFilter, StringComparison.InvariantCultureIgnoreCase) != -1
+ || mod.SearchableIdentifier.IndexOf(sanitisedModNameFilter, StringComparison.InvariantCultureIgnoreCase) != -1;
}
- private bool IsAuthorInauthorFilter(GUIMod mod)
+ private bool IsAuthorInAuthorFilter(GUIMod mod)
{
- return mod.Authors.IndexOf(ModAuthorFilter, StringComparison.InvariantCultureIgnoreCase) != -1;
+ string sanitisedModAuthorFilter = CkanModule.nonAlphaNums.Replace(ModAuthorFilter, "");
+
+ return mod.SearchableAuthors.IndexOf(sanitisedModAuthorFilter, StringComparison.InvariantCultureIgnoreCase) != -1;
}
private bool IsAbstractInDescriptionFilter(GUIMod mod)
{
- return mod.Abstract.IndexOf(ModDescriptionFilter, StringComparison.InvariantCultureIgnoreCase) != -1;
+ string sanitisedModDescriptionFilter = CkanModule.nonAlphaNums.Replace(ModDescriptionFilter, "");
+
+ return mod.SearchableAbstract.IndexOf(sanitisedModDescriptionFilter, StringComparison.InvariantCultureIgnoreCase) != -1
+ || mod.SearchableDescription.IndexOf(sanitisedModDescriptionFilter, StringComparison.InvariantCultureIgnoreCase) != -1;
}
private static bool IsModInFilter(GUIModFilter filter, GUIMod m)
diff --git a/GUI/SelectionDialog.cs b/GUI/SelectionDialog.cs
index cb04ff19ed..2123aaabb6 100644
--- a/GUI/SelectionDialog.cs
+++ b/GUI/SelectionDialog.cs
@@ -3,7 +3,7 @@
namespace CKAN
{
- public partial class SelectionDialog : FormCompatibility
+ public partial class SelectionDialog : Form
{
int currentSelected;