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
consider leap years (thanks to T. Charles Yun).
+ 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 ------------------------------

View file

@ -289,7 +289,7 @@ ID Project Pri Description
set via the "newest" configuration variable.
</p>
<strong>% task /from/to/</strong>
<strong>% task &lt;id&gt; /from/to/</strong>
<p>
If a task has been entered with a typo, it can be easily corrected
by this command. For example:
@ -309,14 +309,22 @@ ID Project Pri Description
...</code></pre>
<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>
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.
</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>
<p>
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
consider leap years (thanks to T. Charles Yun).
<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>
<p>

View file

@ -40,6 +40,9 @@ T::T ()
mTags.clear ();
mAttributes.clear ();
mDescription = "";
mFrom = "";
mTo = "";
mGlobal = false;
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;
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;
mTo = to;
mGlobal = global;
}
////////////////////////////////////////////////////////////////////////////////

View file

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

View file

@ -756,19 +756,49 @@ std::string handleModify (TDB& tdb, T& task, Config& conf)
std::string from;
std::string to;
task.getSubstitution (from, to);
bool global;
task.getSubstitution (from, to, global);
if (from != "")
{
std::string description = original.getDescription ();
size_t pattern = description.find (from);
if (pattern != std::string::npos)
size_t pattern;
if (global)
{
description = description.substr (0, pattern) +
to +
description.substr (pattern + from.length (), std::string::npos);
// Perform all subs on description.
while ((pattern = description.find (from)) != std::string::npos)
{
description.replace (pattern, from.length (), to);
++changes;
}
original.setDescription (description);
// Perform all subs on annotations.
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)
{
while ((pattern = it->second.find (from)) != std::string::npos)
{
it->second.replace (pattern, from.length (), to);
++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;
@ -777,21 +807,18 @@ std::string handleModify (TDB& tdb, T& task, Config& conf)
std::map <time_t, std::string>::iterator it;
for (it = annotations.begin (); it != annotations.end (); ++it)
{
size_t pattern = it->second.find (from);
if (pattern != std::string::npos)
if ((pattern = it->second.find (from)) != std::string::npos)
{
it->second = it->second.substr (0, pattern) +
to +
it->second.substr (pattern + from.length (), std::string::npos);
it->second.replace (pattern, from.length (), to);
++changes;
break;
}
}
if (changes)
original.setAnnotations (annotations);
}
}
}
if (changes)
tdb.modifyT (original);
@ -879,23 +906,6 @@ std::string handleAppend (TDB& tdb, T& task, Config& conf)
++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)
{
tdb.modifyT (original);

View file

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

View file

@ -28,7 +28,7 @@
use strict;
use warnings;
use Test::More tests => 5;
use Test::More tests => 7;
# Create the rc file.
if (open my $fh, '>', 'subst.rc')
@ -39,16 +39,24 @@ if (open my $fh, '>', 'subst.rc')
}
# 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/};
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.
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/};
$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.
unlink 'pending.data';