mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-08-29 17:07:19 +02:00
Enhancement - caseless compare
- Fixed bug detecting multiple failed negative attribute matches.
This commit is contained in:
parent
4f1183a358
commit
b1700f3cf6
3 changed files with 28 additions and 18 deletions
26
src/Att.cpp
26
src/Att.cpp
|
@ -104,7 +104,7 @@ Att::Att (const std::string& name, const std::string& mod, const std::string& va
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
Att::Att (const std::string& name, const std::string& mod, int value)
|
Att::Att (const std::string& name, const std::string& mod, int value)
|
||||||
{
|
{
|
||||||
mName = name;
|
mName = name;
|
||||||
|
|
||||||
std::stringstream s;
|
std::stringstream s;
|
||||||
s << value;
|
s << value;
|
||||||
|
@ -124,7 +124,7 @@ Att::Att (const std::string& name, const std::string& value)
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
Att::Att (const std::string& name, int value)
|
Att::Att (const std::string& name, int value)
|
||||||
{
|
{
|
||||||
mName = name;
|
mName = name;
|
||||||
|
|
||||||
std::stringstream s;
|
std::stringstream s;
|
||||||
s << value;
|
s << value;
|
||||||
|
@ -415,6 +415,7 @@ std::string Att::modType (const std::string& name) const
|
||||||
{
|
{
|
||||||
if (name == "hasnt" ||
|
if (name == "hasnt" ||
|
||||||
name == "isnt" ||
|
name == "isnt" ||
|
||||||
|
name == "not" || // TODO Verify this.
|
||||||
name == "noword")
|
name == "noword")
|
||||||
return "negative";
|
return "negative";
|
||||||
|
|
||||||
|
@ -486,32 +487,33 @@ void Att::parse (Nibbler& n)
|
||||||
bool Att::match (const Att& other) const
|
bool Att::match (const Att& other) const
|
||||||
{
|
{
|
||||||
// All matches are assumed to pass, any short-circuit on non-match.
|
// All matches are assumed to pass, any short-circuit on non-match.
|
||||||
|
bool case_sensitive = context.config.getBoolean ("search.case.sensitive");
|
||||||
|
|
||||||
// If there are no mods, just perform a straight compare on value.
|
// If there are no mods, just perform a straight compare on value.
|
||||||
if (mMod == "")
|
if (mMod == "")
|
||||||
{
|
{
|
||||||
if (mValue != other.mValue)
|
if (!compare (mValue, other.mValue, (bool) case_sensitive))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// has = contains as a substring.
|
// has = contains as a substring.
|
||||||
else if (mMod == "has" || mMod == "contains") // TODO i18n
|
else if (mMod == "has" || mMod == "contains") // TODO i18n
|
||||||
{
|
{
|
||||||
if (other.mValue.find (mValue) == std::string::npos)
|
if (find (other.mValue, mValue, (bool) case_sensitive) == std::string::npos)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// is = equal. Nop.
|
// is = equal. Nop.
|
||||||
else if (mMod == "is" || mMod == "equals") // TODO i18n
|
else if (mMod == "is" || mMod == "equals") // TODO i18n
|
||||||
{
|
{
|
||||||
if (mValue != other.mValue)
|
if (!compare (mValue, other.mValue, (bool) case_sensitive))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// isnt = not equal.
|
// isnt = not equal.
|
||||||
else if (mMod == "isnt" || mMod == "not") // TODO i18n
|
else if (mMod == "isnt" || mMod == "not") // TODO i18n
|
||||||
{
|
{
|
||||||
if (mValue == other.mValue)
|
if (compare (mValue, other.mValue, (bool) case_sensitive))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -535,7 +537,7 @@ bool Att::match (const Att& other) const
|
||||||
if (other.mValue.length () < mValue.length ())
|
if (other.mValue.length () < mValue.length ())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (mValue != other.mValue.substr (0, mValue.length ()))
|
if (!compare (mValue, other.mValue.substr (0, mValue.length ())))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -545,16 +547,16 @@ bool Att::match (const Att& other) const
|
||||||
if (other.mValue.length () < mValue.length ())
|
if (other.mValue.length () < mValue.length ())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (mValue != other.mValue.substr (
|
if (!compare (mValue, other.mValue.substr (
|
||||||
other.mValue.length () - mValue.length (),
|
other.mValue.length () - mValue.length (),
|
||||||
std::string::npos))
|
std::string::npos), (bool) case_sensitive))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// hasnt = does not contain as a substring.
|
// hasnt = does not contain as a substring.
|
||||||
else if (mMod == "hasnt") // TODO i18n
|
else if (mMod == "hasnt") // TODO i18n
|
||||||
{
|
{
|
||||||
if (other.mValue.find (mValue) != std::string::npos)
|
if (find (other.mValue, mValue, (bool) case_sensitive) != std::string::npos)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -622,7 +624,7 @@ bool Att::match (const Att& other) const
|
||||||
else if (mMod == "word") // TODO i18n
|
else if (mMod == "word") // TODO i18n
|
||||||
{
|
{
|
||||||
// Fail if the substring is not found.
|
// Fail if the substring is not found.
|
||||||
std::string::size_type sub = other.mValue.find (mValue);
|
std::string::size_type sub = find (other.mValue, mValue, (bool) case_sensitive);
|
||||||
if (sub == std::string::npos)
|
if (sub == std::string::npos)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -638,7 +640,7 @@ bool Att::match (const Att& other) const
|
||||||
else if (mMod == "noword") // TODO i18n
|
else if (mMod == "noword") // TODO i18n
|
||||||
{
|
{
|
||||||
// Fail if the substring is not found.
|
// Fail if the substring is not found.
|
||||||
std::string::size_type sub = other.mValue.find (mValue);
|
std::string::size_type sub = find (other.mValue, mValue);
|
||||||
if (sub != std::string::npos &&
|
if (sub != std::string::npos &&
|
||||||
isWordStart (other.mValue, sub) &&
|
isWordStart (other.mValue, sub) &&
|
||||||
isWordEnd (other.mValue, sub + mValue.length () - 1))
|
isWordEnd (other.mValue, sub + mValue.length () - 1))
|
||||||
|
|
|
@ -42,17 +42,22 @@ bool Filter::pass (const Record& record) const
|
||||||
{
|
{
|
||||||
// Descriptions have special handling such that they are linked to
|
// Descriptions have special handling such that they are linked to
|
||||||
// annotations, and filtering on description implies identical filtering
|
// annotations, and filtering on description implies identical filtering
|
||||||
// on annotations, and that both filter matches must succeed for the filter
|
// on annotations.
|
||||||
// to succeed overall.
|
//
|
||||||
|
// For positive modifiers (has, is ...) either the description or the
|
||||||
|
// annotation filter must succeed.
|
||||||
|
//
|
||||||
|
// For negative modifiers (hasnt, isnt ...) both the description and the
|
||||||
|
// annotation filter must succeed.
|
||||||
if (att->name () == "description")
|
if (att->name () == "description")
|
||||||
{
|
{
|
||||||
bool description_result = true;
|
bool pass = true;
|
||||||
int annotation_pass_count = 0;
|
int annotation_pass_count = 0;
|
||||||
int annotation_fail_count = 0;
|
int annotation_fail_count = 0;
|
||||||
|
|
||||||
if ((r = record.find (att->name ())) != record.end ())
|
if ((r = record.find (att->name ())) != record.end ())
|
||||||
{
|
{
|
||||||
description_result = att->match (r->second);
|
pass = att->match (r->second);
|
||||||
|
|
||||||
foreach (ra, record)
|
foreach (ra, record)
|
||||||
{
|
{
|
||||||
|
@ -66,6 +71,8 @@ bool Filter::pass (const Record& record) const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Missing attribute implies failure.
|
||||||
else if (! att->match (Att ()))
|
else if (! att->match (Att ()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -74,12 +81,12 @@ bool Filter::pass (const Record& record) const
|
||||||
// are willing to invest a week understanding and testing it.
|
// are willing to invest a week understanding and testing it.
|
||||||
if (att->modType (att->mod ()) == "positive")
|
if (att->modType (att->mod ()) == "positive")
|
||||||
{
|
{
|
||||||
if (! (description_result || annotation_pass_count > 0))
|
if (! (pass || annotation_pass_count))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!description_result || annotation_fail_count > 0)
|
if (! (pass && annotation_fail_count == 0))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,7 @@ if (open my $fh, '>', 'hasnt.rc')
|
||||||
}
|
}
|
||||||
|
|
||||||
# 1
|
# 1
|
||||||
|
diag ("Initialization taskes 6 seconds");
|
||||||
qx{../task rc:hasnt.rc add foo};
|
qx{../task rc:hasnt.rc add foo};
|
||||||
|
|
||||||
# 2
|
# 2
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue