Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions IgnoreRules/CustomIgnoreSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using ExileCore.Shared.Attributes;
using ExileCore.Shared.Interfaces;
using ExileCore.Shared.Nodes;

namespace MinimapIcons.IgnoreRules;

public class CustomIgnoreSettings : ISettings
{
[Menu("Enable Custom Ignore Rules")]
public ToggleNode EnableCustomIgnoreRules { get; set; } = new ToggleNode(true);

[Menu("New Rule Type")]
public ListNode NewRuleType { get; set; } = new ListNode
{
Values = new System.Collections.Generic.List<string>
{
"Metadata Exact",
"Metadata Starts With",
"Metadata Contains",
"Name Exact",
"Name Contains"
},
Value = "Metadata Starts With"
};

[Menu("New Rule Pattern")]
public TextNode NewRulePattern { get; set; } = new TextNode("");

[Menu("Add Rule")]
public ButtonNode AddRule { get; set; } = new ButtonNode();

[Menu("Reload Rules from File")]
public ButtonNode ReloadRules { get; set; } = new ButtonNode();

[Menu("Open Config Folder")]
public ButtonNode OpenConfigFolder { get; set; } = new ButtonNode();

public ToggleNode Enable { get; set; } = new ToggleNode(true);
}
60 changes: 60 additions & 0 deletions IgnoreRules/IgnoreRule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using System;

namespace MinimapIcons.IgnoreRules;

public class IgnoreRule
{
public IgnoreRuleType Type { get; set; }
public string Pattern { get; set; }
public bool IsEnabled { get; set; } = true;

public IgnoreRule()
{
}

public IgnoreRule(IgnoreRuleType type, string pattern, bool isEnabled = true)
{
Type = type;
Pattern = pattern;
IsEnabled = isEnabled;
}

public bool Matches(string metadata, string renderName)
{
if (!IsEnabled || string.IsNullOrWhiteSpace(Pattern))
return false;

return Type switch
{
IgnoreRuleType.MetadataExact => metadata.Equals(Pattern, StringComparison.OrdinalIgnoreCase),
IgnoreRuleType.MetadataStartsWith => metadata.StartsWith(Pattern, StringComparison.OrdinalIgnoreCase),
IgnoreRuleType.MetadataContains => metadata.Contains(Pattern, StringComparison.OrdinalIgnoreCase),
IgnoreRuleType.NameExact => renderName.Equals(Pattern, StringComparison.OrdinalIgnoreCase),
IgnoreRuleType.NameContains => renderName.Contains(Pattern, StringComparison.OrdinalIgnoreCase),
_ => false
};
}

public override string ToString()
{
var prefix = Type switch
{
IgnoreRuleType.MetadataExact => "[META=]",
IgnoreRuleType.MetadataStartsWith => "[META^]",
IgnoreRuleType.MetadataContains => "[META*]",
IgnoreRuleType.NameExact => "[NAME=]",
IgnoreRuleType.NameContains => "[NAME*]",
_ => "[???]"
};
return $"{prefix} {Pattern}";
}
}

public enum IgnoreRuleType
{
MetadataExact, // Exact metadata match
MetadataStartsWith, // Metadata starts with
MetadataContains, // Metadata contains
NameExact, // Exact name match
NameContains // Name contains
}
181 changes: 181 additions & 0 deletions IgnoreRules/IgnoreRulesManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
using System.Collections.Generic;
using System.IO;

namespace MinimapIcons.IgnoreRules;

public class IgnoreRulesManager
{
private readonly List<IgnoreRule> _customRules = new();
private readonly Dictionary<string, bool> _cache = new();
private readonly string _customRulesFilePath;

public IReadOnlyList<IgnoreRule> CustomRules => _customRules.AsReadOnly();

public IgnoreRulesManager(string directoryFullName)
{
var configDir = Path.Combine(directoryFullName, "config");
if (!Directory.Exists(configDir))
Directory.CreateDirectory(configDir);

_customRulesFilePath = Path.Combine(configDir, "custom_ignore_rules.txt");
LoadCustomRules();
}


public void LoadCustomRules()
{
_customRules.Clear();
ClearCache();

if (!File.Exists(_customRulesFilePath))
{
SaveCustomRules(); // Create default file
return;
}

try
{
var lines = File.ReadAllLines(_customRulesFilePath);
foreach (var line in lines)
{
var trimmedLine = line.Trim();
if (string.IsNullOrWhiteSpace(trimmedLine) || trimmedLine.StartsWith("#"))
continue;

var rule = ParseRule(trimmedLine);
if (rule != null)
_customRules.Add(rule);
}
}
catch
{
// Silent fail - error will be handled by caller
Comment on lines +50 to +52
Copy link

Copilot AI Oct 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Catching all exceptions without logging prevents debugging of file parsing issues. Consider logging the exception details or at minimum the exception type to help users diagnose configuration file problems.

Suggested change
catch
{
// Silent fail - error will be handled by caller
catch (Exception ex)
{
Console.Error.WriteLine($"[IgnoreRulesManager] Failed to load custom rules from '{_customRulesFilePath}': {ex.GetType().Name}: {ex.Message}");

Copilot uses AI. Check for mistakes.
}
}

public void SaveCustomRules()
{
try
{
var lines = new List<string>
{
"# Custom Ignore Rules for MinimapIcons",
"# Format examples:",
Copy link

Copilot AI Oct 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistency between documentation here ('Format examples:') and the template file ('Rule formats:'). They should use the same wording for consistency.

Suggested change
"# Format examples:",
"# Rule formats:",

Copilot uses AI. Check for mistakes.
"# META=Metadata/Monsters/Exact/Path - Exact metadata match",
"# META^Metadata/Monsters/Prefix - Metadata starts with",
"# META*PartialMetadata - Metadata contains",
"# NAME=Exact Entity Name - Exact name match",
"# NAME*Partial Name - Name contains",
"# Lines starting with # are comments",
"# Prefix with ! to disable a rule without deleting it",
""
};

foreach (var rule in _customRules)
{
var prefix = rule.Type switch
{
IgnoreRuleType.MetadataExact => "META=",
IgnoreRuleType.MetadataStartsWith => "META^",
IgnoreRuleType.MetadataContains => "META*",
IgnoreRuleType.NameExact => "NAME=",
IgnoreRuleType.NameContains => "NAME*",
_ => "META^"
};

var line = $"{(rule.IsEnabled ? "" : "!")}{prefix}{rule.Pattern}";
lines.Add(line);
}

File.WriteAllLines(_customRulesFilePath, lines);
}
catch
{
// Silent fail - error will be handled by caller
}
Comment on lines +92 to +95
Copy link

Copilot AI Oct 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Catching all exceptions without logging prevents debugging of file save issues. Consider logging the exception details to help users diagnose permission or disk space problems.

Copilot uses AI. Check for mistakes.
}

private IgnoreRule ParseRule(string line)
{
var isEnabled = !line.StartsWith("!");
if (!isEnabled)
line = line.Substring(1);

IgnoreRuleType type;
string pattern;

if (line.StartsWith("META="))
{
type = IgnoreRuleType.MetadataExact;
pattern = line.Substring(5);
}
else if (line.StartsWith("META^"))
{
type = IgnoreRuleType.MetadataStartsWith;
pattern = line.Substring(5);
}
else if (line.StartsWith("META*"))
{
type = IgnoreRuleType.MetadataContains;
pattern = line.Substring(5);
}
else if (line.StartsWith("NAME="))
{
type = IgnoreRuleType.NameExact;
pattern = line.Substring(5);
}
else if (line.StartsWith("NAME*"))
{
type = IgnoreRuleType.NameContains;
pattern = line.Substring(5);
}
else
{
// Default to MetadataStartsWith for backward compatibility
type = IgnoreRuleType.MetadataStartsWith;
pattern = line;
}

return new IgnoreRule(type, pattern, isEnabled);
}

public void AddRule(IgnoreRule rule)
{
_customRules.Add(rule);
ClearCache();
SaveCustomRules();
}

public void RemoveRule(IgnoreRule rule)
{
_customRules.Remove(rule);
ClearCache();
SaveCustomRules();
}

public void ClearCache()
{
_cache.Clear();
}

public bool ShouldIgnore(string metadata, string renderName)
{
var cacheKey = $"{metadata}|{renderName}";

if (_cache.TryGetValue(cacheKey, out var cached))
return cached;

// Check only custom rules
foreach (var rule in _customRules)
{
if (rule.Matches(metadata, renderName))
{
_cache[cacheKey] = true;
return true;
}
}

_cache[cacheKey] = false;
return false;
}
}
6 changes: 5 additions & 1 deletion MapIconsSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using ExileCore.Shared.Interfaces;
using ExileCore.Shared.Nodes;
using MinimapIcons.IconsBuilder;
using MinimapIcons.IgnoreRules;

namespace MinimapIcons;

Expand Down Expand Up @@ -41,10 +42,13 @@ public class MapIconsSettings : ISettings
Content =
[
],
EnableControls = true,
EnableControls = true,
ItemFactory = () => new TextNode(""),
UseFlatItems = true,
};

[Menu("Custom Ignore Rules", CollapsedByDefault = true)]
public CustomIgnoreSettings CustomIgnoreSettings { get; set; } = new();

public IconsBuilderSettings IconsBuilderSettings { get; set; } = new();
}
Loading