Enhancement - substitutions /from/to/g

- Added support for the "g" modifier to the substitution command,
  that replace every occurrence of "from" with "to", in the task
  description and any annotations.
This commit is contained in:
Paul Beckingham
2009-04-12 02:01:08 -04:00
parent a39261f82d
commit e0fd39db7b
8 changed files with 106 additions and 55 deletions

View File

@@ -40,6 +40,8 @@
date, because of an assumption of 365 days per year, which failed to date, because of an assumption of 365 days per year, which failed to
consider leap years (thanks to T. Charles Yun). consider leap years (thanks to T. Charles Yun).
+ Annotations can now be modified with the substitution commands /from/to/. + Annotations can now be modified with the substitution commands /from/to/.
+ Substitutions can now be made global with /from/to/g and all occurrences
of "from" will be replaced with "to".
------ old releases ------------------------------ ------ old releases ------------------------------

View File

@@ -289,7 +289,7 @@ ID Project Pri Description
set via the "newest" configuration variable. set via the "newest" configuration variable.
</p> </p>
<strong>% task /from/to/</strong> <strong>% task &lt;id&gt; /from/to/</strong>
<p> <p>
If a task has been entered with a typo, it can be easily corrected If a task has been entered with a typo, it can be easily corrected
by this command. For example: by this command. For example:
@@ -309,14 +309,22 @@ ID Project Pri Description
...</code></pre> ...</code></pre>
<p> <p>
This command makes single corrections to a task description. This command makes a single correction to the first occurrence of
"from" in a task description.
</p> </p>
<p> <p>
If a task is annotated, the annotation can be modified using If a task is annotated, the annotation can also be modified using
this command. this command.
</p> </p>
<strong>% task &lt;id&gt; /from/to/g</strong>
<p>
The "g" modifier to the substitution command causes every occurrence
of "from" to be replaced with "to", in both the description and any
annotations.
</p>
<strong>% task tags</strong> <strong>% task tags</strong>
<p> <p>
This command will generate a list of all the tags that are currently This command will generate a list of all the tags that are currently

View File

@@ -145,6 +145,8 @@
date, because of an assumption of 365 days per year, which failed to date, because of an assumption of 365 days per year, which failed to
consider leap years (thanks to T. Charles Yun). consider leap years (thanks to T. Charles Yun).
<li>Annotations can now be modified with the substitution commands /from/to/. <li>Annotations can now be modified with the substitution commands /from/to/.
<li>Substitutions can now be made global with /from/to/g and all occurrences
of "from" will be replaced with "to".
</ul> </ul>
<p> <p>

View File

@@ -40,6 +40,9 @@ T::T ()
mTags.clear (); mTags.clear ();
mAttributes.clear (); mAttributes.clear ();
mDescription = ""; mDescription = "";
mFrom = "";
mTo = "";
mGlobal = false;
mAnnotations.clear (); mAnnotations.clear ();
} }
@@ -240,17 +243,25 @@ void T::removeAttribute (const std::string& name)
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void T::getSubstitution (std::string& from, std::string& to) const void T::getSubstitution (
std::string& from,
std::string& to,
bool& global) const
{ {
from = mFrom; from = mFrom;
to = mTo; to = mTo;
global = mGlobal;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void T::setSubstitution (const std::string& from, const std::string& to) void T::setSubstitution (
const std::string& from,
const std::string& to,
bool global)
{ {
mFrom = from; mFrom = from;
mTo = to; mTo = to;
mGlobal = global;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@@ -58,8 +58,8 @@ public:
void setDescription (const std::string& description) { mDescription = description; } void setDescription (const std::string& description) { mDescription = description; }
int getAnnotationCount () const { return mAnnotations.size (); } int getAnnotationCount () const { return mAnnotations.size (); }
void getSubstitution (std::string&, std::string&) const; void getSubstitution (std::string&, std::string&, bool&) const;
void setSubstitution (const std::string&, const std::string&); void setSubstitution (const std::string&, const std::string&, bool);
bool hasTag (const std::string&) const; bool hasTag (const std::string&) const;
@@ -101,6 +101,7 @@ private:
std::map<std::string, std::string> mAttributes; std::map<std::string, std::string> mAttributes;
std::string mFrom; std::string mFrom;
std::string mTo; std::string mTo;
bool mGlobal;
std::map <time_t, std::string> mAnnotations; std::map <time_t, std::string> mAnnotations;
}; };

View File

@@ -756,40 +756,67 @@ std::string handleModify (TDB& tdb, T& task, Config& conf)
std::string from; std::string from;
std::string to; std::string to;
task.getSubstitution (from, to); bool global;
task.getSubstitution (from, to, global);
if (from != "") if (from != "")
{ {
std::string description = original.getDescription (); std::string description = original.getDescription ();
size_t pattern = description.find (from); size_t pattern;
if (pattern != std::string::npos)
if (global)
{ {
description = description.substr (0, pattern) + // Perform all subs on description.
to + while ((pattern = description.find (from)) != std::string::npos)
description.substr (pattern + from.length (), std::string::npos); {
description.replace (pattern, from.length (), to);
++changes;
}
original.setDescription (description); original.setDescription (description);
++changes;
} // Perform all subs on annotations.
else
{
std::map <time_t, std::string> annotations; std::map <time_t, std::string> annotations;
original.getAnnotations (annotations); original.getAnnotations (annotations);
std::map <time_t, std::string>::iterator it; std::map <time_t, std::string>::iterator it;
for (it = annotations.begin (); it != annotations.end (); ++it) for (it = annotations.begin (); it != annotations.end (); ++it)
{ {
size_t pattern = it->second.find (from); while ((pattern = it->second.find (from)) != std::string::npos)
if (pattern != std::string::npos)
{ {
it->second = it->second.substr (0, pattern) + it->second.replace (pattern, from.length (), to);
to +
it->second.substr (pattern + from.length (), std::string::npos);
++changes; ++changes;
break;
} }
} }
if (changes) original.setAnnotations (annotations);
}
else
{
// Perform first description substitution.
if ((pattern = description.find (from)) != std::string::npos)
{
description.replace (pattern, from.length (), to);
original.setDescription (description);
++changes;
}
// Failing that, perform the first annotation substitution.
else
{
std::map <time_t, std::string> annotations;
original.getAnnotations (annotations);
std::map <time_t, std::string>::iterator it;
for (it = annotations.begin (); it != annotations.end (); ++it)
{
if ((pattern = it->second.find (from)) != std::string::npos)
{
it->second.replace (pattern, from.length (), to);
++changes;
break;
}
}
original.setAnnotations (annotations); original.setAnnotations (annotations);
}
} }
} }
@@ -879,23 +906,6 @@ std::string handleAppend (TDB& tdb, T& task, Config& conf)
++changes; ++changes;
} }
std::string from;
std::string to;
task.getSubstitution (from, to);
if (from != "")
{
std::string description = original.getDescription ();
size_t pattern = description.find (from);
if (pattern != std::string::npos)
{
description = description.substr (0, pattern) +
to +
description.substr (pattern + from.length (), std::string::npos);
original.setDescription (description);
++changes;
}
}
if (changes) if (changes)
{ {
tdb.modifyT (original); tdb.modifyT (original);

View File

@@ -348,7 +348,8 @@ static bool validCommand (std::string& input)
static bool validSubstitution ( static bool validSubstitution (
std::string& input, std::string& input,
std::string& from, std::string& from,
std::string& to) std::string& to,
bool& global)
{ {
size_t first = input.find ('/'); size_t first = input.find ('/');
if (first != std::string::npos) if (first != std::string::npos)
@@ -362,10 +363,17 @@ static bool validSubstitution (
if (first == 0 && if (first == 0 &&
first < second && first < second &&
second < third && second < third &&
third == input.length () - 1) (third == input.length () - 1 ||
third == input.length () - 2))
{ {
from = input.substr (first + 1, second - first - 1); from = input.substr (first + 1, second - first - 1);
to = input.substr (second + 1, third - second - 1); to = input.substr (second + 1, third - second - 1);
global = false;
if (third == input.length () - 2 &&
input.find ('g', third + 1) != std::string::npos)
global = true;
return true; return true;
} }
} }
@@ -411,6 +419,7 @@ void parse (
size_t colon; // Pointer to colon in argument. size_t colon; // Pointer to colon in argument.
std::string from; std::string from;
std::string to; std::string to;
bool global;
// An id is the first argument found that contains all digits. // An id is the first argument found that contains all digits.
if (lowerCase (command) != "add" && // "add" doesn't require an ID if (lowerCase (command) != "add" && // "add" doesn't require an ID
@@ -451,9 +460,9 @@ void parse (
} }
// Substitution of description text. // Substitution of description text.
else if (validSubstitution (arg, from, to)) else if (validSubstitution (arg, from, to, global))
{ {
task.setSubstitution (from, to); task.setSubstitution (from, to, global);
} }
// Command. // Command.

View File

@@ -28,7 +28,7 @@
use strict; use strict;
use warnings; use warnings;
use Test::More tests => 5; use Test::More tests => 7;
# Create the rc file. # Create the rc file.
if (open my $fh, '>', 'subst.rc') if (open my $fh, '>', 'subst.rc')
@@ -39,16 +39,24 @@ if (open my $fh, '>', 'subst.rc')
} }
# Test the substitution command. # Test the substitution command.
qx{../task rc:subst.rc add foo}; qx{../task rc:subst.rc add foo foo foo};
qx{../task rc:subst.rc 1 /foo/FOO/}; qx{../task rc:subst.rc 1 /foo/FOO/};
my $output = qx{../task rc:subst.rc info 1}; my $output = qx{../task rc:subst.rc info 1};
like ($output, qr/FOO/, 'substitution in description'); like ($output, qr/FOO foo foo/, 'substitution in description');
qx{../task rc:subst.rc 1 /foo/FOO/g};
my $output = qx{../task rc:subst.rc info 1};
like ($output, qr/FOO FOO FOO/, 'global substitution in description');
# Test the substitution command on annotations. # Test the substitution command on annotations.
qx{../task rc:subst.rc annotate 1 bar}; qx{../task rc:subst.rc annotate 1 bar bar bar};
qx{../task rc:subst.rc 1 /bar/BAR/}; qx{../task rc:subst.rc 1 /bar/BAR/};
$output = qx{../task rc:subst.rc info 1}; $output = qx{../task rc:subst.rc info 1};
like ($output, qr/BAR/, 'substitution in annotation'); like ($output, qr/BAR bar bar/, 'substitution in annotation');
qx{../task rc:subst.rc 1 /bar/BAR/g};
$output = qx{../task rc:subst.rc info 1};
like ($output, qr/BAR BAR BAR/, 'global substitution in annotation');
# Cleanup. # Cleanup.
unlink 'pending.data'; unlink 'pending.data';