Try the most specific rule first, not the topmost
File order decided precedence, so a narrow rule had to be written above the broad one it carves an exception out of -- an ordering constraint the file cannot show and the user has to remember. *NIKOLA* below *NIK* silently matched nothing, and a catch-all * could only ever be the last line. Engine.New now sorts once and MatchIndex walks that order: most literal characters first, then fewest *, then account-scoped over unscoped. Literals are what a rule commits to and a * is what it gives up, so a bare * is tried last wherever it sits. The sort is stable, so equally specific rules keep file order and the earlier one wins -- which is all position decides now, and why AppendRule can keep appending without displacing a rule written by hand. The two orders must not be confused: Rules(), Usage and MatchIndex still speak in file positions, because that is what the rules screen numbers and what DeleteRules deletes by. A shadowed rule still reports zero usage, but a zero no longer says anything about where the rule sits. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -807,8 +807,10 @@ func newRuleListModel(t *testing.T) (*Model, string) {
|
||||
match = "*LIDL*"
|
||||
tag = "groceries"
|
||||
|
||||
# Dead: the rule above is just as specific and gets there first, so this one
|
||||
# never claims anything. A narrower pattern would have won instead.
|
||||
[[rule]]
|
||||
match = "*LIDL SOFIA*"
|
||||
match = "*LIDL*"
|
||||
tag = "shadowed"
|
||||
|
||||
[[rule]]
|
||||
|
||||
Reference in New Issue
Block a user