mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-06-26 10:54:26 +02:00
Merge branch 'master' into 1.9.0
due to release of 1.8.5 Conflicts: ChangeLog configure.ac doc/man/task-tutorial.5 doc/man/task.1 doc/man/taskrc.5 src/Config.cpp src/rules.cpp
This commit is contained in:
commit
f3d31834ee
24 changed files with 317 additions and 72 deletions
2
AUTHORS
2
AUTHORS
|
@ -42,3 +42,5 @@ Thanks to the following, who submitted detailed bug reports and excellent sugges
|
|||
Zach Frazier
|
||||
Ivo Jimenez
|
||||
Joe Pulliam
|
||||
Juergen Daubert
|
||||
|
||||
|
|
30
ChangeLog
30
ChangeLog
|
@ -21,6 +21,36 @@
|
|||
|
||||
------ old releases ------------------------------
|
||||
|
||||
1.8.5 (12/05/2009) a6c7236ff34e5eee3ef1693b97cb1367e6e3c607
|
||||
+ Added feature to allow the user to quit when asked to confirm multiple
|
||||
changes. Now task asks "Proceed with change? (Yes/no/all/quit)".
|
||||
+ Added feature #341 that makes explicit references to the task and taskrc
|
||||
man pages, both in the auto-generated .taskrc file and the version command
|
||||
output (thanks to Cory Donnelly).
|
||||
+ Added feature - #310 that simplified and make clearer an error message
|
||||
that complained about things that were beyond user control (thanks to
|
||||
John Florian).
|
||||
+ Fixed bug that was causing the 'completed' report to sort incorrectly.
|
||||
+ Fixed bug #321 where all shell input was converted to lower case (thanks
|
||||
to Juergen Daubert).
|
||||
+ Fixed bug #327 that allowed the removal of a due date from a recurring
|
||||
task.
|
||||
+ Fixed bug #317 which colored tasks in the 'completed' report according
|
||||
to due dates, which are no longer relevant to a completed task (thanks
|
||||
to Cory Donnelly).
|
||||
+ Fixed bug that was causing the 'completed' report to sort incorrectly.
|
||||
+ Fixed bug #322 which failed to propagate rc overrides to shell commands.
|
||||
+ Fixed redundant messages when exiting shell mode.
|
||||
+ Fixed bug #333 which failed to display the ID of a duplicated task (thanks
|
||||
to Cory Donnelly).
|
||||
+ Fixed bug #332 where task complained that the 'recur_ind' custom report
|
||||
column was invalid. It was misnamed in the documentation, which should
|
||||
have read 'recurrence_indicator'. Also, the 'tag_indicator' column was
|
||||
not mentioned anywhere (thanks to T. Charles Yun).
|
||||
+ Fixed bug #319 that caused task to not properly detect the removal of a
|
||||
tag when obtaining confirmation from the user fora bulk modification
|
||||
(thanks to Cory Donnelly).
|
||||
|
||||
1.8.4 (11/17/2009) 12c4983936d27317df100f05da8244139dd06a3f
|
||||
+ Fixed bug that caused wait: dates to not be properly rendered in a
|
||||
readable and preferred format with the "edit" command.
|
||||
|
|
24
NEWS
24
NEWS
|
@ -16,17 +16,19 @@ New Features in task 1.8
|
|||
|
||||
Task has been built and tested on the following configurations:
|
||||
|
||||
- OS X 10.6 Snow Leopard and 10.5 Leopard
|
||||
- Fedora 11 Leonidas and 10 Cambridge
|
||||
- Ubuntu 9.04 Jaunty Jackalope and 8.10 Intrepid Ibex
|
||||
- Slackware 12.2
|
||||
- Arch Linux
|
||||
- Gentoo Linux
|
||||
- Solaris 10 and 8
|
||||
- OpenBSD 4.5
|
||||
- FreeBSD
|
||||
- Cygwin 1.5
|
||||
- Haiku R1/alpha1
|
||||
* OS X 10.6 Snow Leopard and 10.5 Leopard
|
||||
* Fedora 12 Constantine and 11 Leonidas
|
||||
* Ubuntu 9.10 Karmic Koala and 9.04 Jaunty Jackalope
|
||||
* Slackware 12.2
|
||||
* Arch Linux
|
||||
* Gentoo Linux
|
||||
* SliTaz Linux
|
||||
* CRUX Linux
|
||||
* Solaris 10 and 8
|
||||
* OpenBSD 4.5
|
||||
* FreeBSD
|
||||
* Cygwin 1.5
|
||||
* Haiku R1/alpha1
|
||||
|
||||
While Task has undergone testing, bugs are sure to remain. If you encounter a
|
||||
bug, please enter a new issue at:
|
||||
|
|
4
README
4
README
|
@ -12,8 +12,8 @@ At the site you'll find a wiki, discussion forums, downloads, news and more.
|
|||
|
||||
|
||||
Your contributions are especially welcome. Whether it comes in the form of
|
||||
code patches, ideas, discussion, bug reports or just encouragement, your input
|
||||
is needed.
|
||||
code patches, ideas, discussion, bug reports, encouragement or criticism, your
|
||||
input is needed.
|
||||
|
||||
Please send your support questions and code patches to:
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ AC_INIT(task, 1.9.0, support@taskwarrior.org)
|
|||
|
||||
CFLAGS="${CFLAGS=}"
|
||||
CXXFLAGS="${CXXFLAGS=}"
|
||||
|
||||
# this macro is used to get the arguments supplied
|
||||
# to the configure script (./configure --enable-debug)
|
||||
# Check if we have enable debug support.
|
||||
|
|
|
@ -384,8 +384,9 @@ The description for report X when running the "task help" command.
|
|||
.TP
|
||||
.B report.X.columns
|
||||
The columns that will be used when generating the report X. Valid columns are:
|
||||
id, uuid, project, priority, entry, start, due, recur, recur_ind, age, age_compact,
|
||||
active, tags, description, description_only. The IDs are separated by commas.
|
||||
id, uuid, project, priority, entry, start, due, recur, recur_indicator, age,
|
||||
age_compact, active, tags, tag_indicator, description, description_only.
|
||||
The IDs are separated by commas.
|
||||
|
||||
.TP
|
||||
.B report.X.labels
|
||||
|
|
|
@ -152,8 +152,8 @@ void Config::createDefaultRC (const std::string& rc, const std::string& data)
|
|||
<< "\n"
|
||||
<< "alias.rm=delete\n"
|
||||
<< "\n"
|
||||
<< "# Fields: id,uuid,project,priority,entry,start,due,recur,recur_ind,age,\n"
|
||||
<< "# age_compact,active,tags,description,description_only\n"
|
||||
<< "# Fields: id,uuid,project,priority,entry,start,due,recur,recurrence_indicator,age,\n"
|
||||
<< "# age_compact,active,tags,tag_indicator,description,description_only\n"
|
||||
<< "# Description: This report is ...\n"
|
||||
<< "# Sort: due+,priority-,project+\n"
|
||||
<< "# Filter: pro:x pri:H +bug limit:10\n"
|
||||
|
|
|
@ -50,7 +50,8 @@ Context::Context ()
|
|||
, tdb ()
|
||||
, stringtable ()
|
||||
, program ("")
|
||||
, overrides ("")
|
||||
, file_override ("")
|
||||
, var_overrides ("")
|
||||
, cmd ()
|
||||
, inShadow (false)
|
||||
{
|
||||
|
@ -66,6 +67,7 @@ void Context::initialize (int argc, char** argv)
|
|||
{
|
||||
// Capture the args.
|
||||
for (int i = 0; i < argc; ++i)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
program = argv[i];
|
||||
|
@ -76,6 +78,7 @@ void Context::initialize (int argc, char** argv)
|
|||
}
|
||||
else
|
||||
args.push_back (argv[i]);
|
||||
}
|
||||
|
||||
initialize ();
|
||||
}
|
||||
|
@ -351,13 +354,14 @@ void Context::loadCorrectConfigFile ()
|
|||
std::string rc = home + "/.taskrc";
|
||||
std::string data = home + "/.task";
|
||||
|
||||
// Is there an override for rc?
|
||||
// Is there an file_override for rc:?
|
||||
foreach (arg, args)
|
||||
{
|
||||
if (*arg == "--")
|
||||
break;
|
||||
else if (arg->substr (0, 3) == "rc:")
|
||||
{
|
||||
file_override = *arg;
|
||||
rc = arg->substr (3, std::string::npos);
|
||||
|
||||
home = rc;
|
||||
|
@ -381,7 +385,7 @@ void Context::loadCorrectConfigFile ()
|
|||
if (config.get ("data.location") != "")
|
||||
data = config.get ("data.location");
|
||||
|
||||
// Is there an override for data?
|
||||
// Are there any var_overrides for data.location?
|
||||
foreach (arg, args)
|
||||
{
|
||||
if (*arg == "--")
|
||||
|
@ -442,7 +446,7 @@ void Context::loadCorrectConfigFile ()
|
|||
n.getUntilEOS (value))
|
||||
{
|
||||
config.set (name, value);
|
||||
overrides += " " + *arg;
|
||||
var_overrides += " " + *arg;
|
||||
footnote (std::string ("Configuration override ") + // TODO i18n
|
||||
arg->substr (3, std::string::npos));
|
||||
}
|
||||
|
@ -660,15 +664,21 @@ void Context::parse (
|
|||
if (parseCmd.command == "" && parseArgs.size () == 0)
|
||||
{
|
||||
// Apply overrides, if any.
|
||||
std::string defaultCommand = config.get ("default.command") + overrides;
|
||||
std::string defaultCommand = config.get ("default.command");
|
||||
if (defaultCommand != "")
|
||||
{
|
||||
// Add on the overrides.
|
||||
defaultCommand += " " + file_override + " " + var_overrides;
|
||||
|
||||
// Stuff the command line.
|
||||
args.clear ();
|
||||
split (args, defaultCommand, ' ');
|
||||
header ("[task " + defaultCommand + "]");
|
||||
|
||||
// Reinitialize the context and recurse.
|
||||
file_override = "";
|
||||
var_overrides = "";
|
||||
footnotes.clear ();
|
||||
initialize ();
|
||||
parse (args, cmd, task, sequence, subst, filter);
|
||||
}
|
||||
|
@ -693,6 +703,8 @@ void Context::clear ()
|
|||
// stringtable.clear ();
|
||||
program = "";
|
||||
args.clear ();
|
||||
file_override = "";
|
||||
var_overrides = "";
|
||||
cmd.command = "";
|
||||
tagAdditions.clear ();
|
||||
tagRemovals.clear ();
|
||||
|
|
|
@ -83,7 +83,8 @@ public:
|
|||
StringTable stringtable;
|
||||
std::string program;
|
||||
std::vector <std::string> args;
|
||||
std::string overrides;
|
||||
std::string file_override;
|
||||
std::string var_overrides;
|
||||
Cmd cmd;
|
||||
std::map <std::string, std::string> aliases;
|
||||
std::vector <std::string> tagAdditions;
|
||||
|
|
|
@ -37,6 +37,7 @@ extern Context context;
|
|||
Permission::Permission ()
|
||||
: needConfirmation (false)
|
||||
, allConfirmed (false)
|
||||
, quit (false)
|
||||
{
|
||||
// Turning confirmations off is the same as entering "all".
|
||||
if (context.config.get ("confirmation", true) == false)
|
||||
|
@ -46,6 +47,9 @@ Permission::Permission ()
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
bool Permission::confirmed (const Task& task, const std::string& question)
|
||||
{
|
||||
if (quit)
|
||||
return false;
|
||||
|
||||
if (!needConfirmation)
|
||||
return true;
|
||||
|
||||
|
@ -67,13 +71,15 @@ bool Permission::confirmed (const Task& task, const std::string& question)
|
|||
|
||||
std::cout << std::endl;
|
||||
|
||||
int answer = confirm3 (question);
|
||||
int answer = confirm4 (question);
|
||||
if (answer == 2)
|
||||
allConfirmed = true;
|
||||
|
||||
if (answer > 0)
|
||||
if (answer == 1 || answer == 2)
|
||||
return true;
|
||||
|
||||
if (answer == 3)
|
||||
quit = true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ public:
|
|||
private:
|
||||
bool needConfirmation;
|
||||
bool allConfirmed;
|
||||
bool quit;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -527,7 +527,7 @@ void Task::validate () const
|
|||
if (!has ("uuid") ||
|
||||
!has ("entry") ||
|
||||
!has ("description"))
|
||||
throw std::string ("A task must have a uuid, entry date and description in order to be valid."); // TODO i18n
|
||||
throw std::string ("A task must have a description in order to be valid."); // TODO i18n
|
||||
|
||||
if (get ("description") == "") // No i18n
|
||||
throw std::string ("Cannot add a task that is blank, or contains <CR> or <LF> characters."); // TODO i18n
|
||||
|
|
|
@ -858,7 +858,7 @@ int handleDone (std::string &outs)
|
|||
|
||||
if (taskDiff (before, *task))
|
||||
{
|
||||
if (permission.confirmed (before, taskDifferences (before, *task) + "Are you sure?"))
|
||||
if (permission.confirmed (before, taskDifferences (before, *task) + "Proceed with change?"))
|
||||
{
|
||||
context.tdb.update (*task);
|
||||
|
||||
|
@ -979,6 +979,15 @@ int handleModify (std::string &outs)
|
|||
!task->has ("recur"))
|
||||
throw std::string ("You cannot specify an until date for a non-recurring task.");
|
||||
|
||||
if (task->has ("due") &&
|
||||
!context.task.has ("due") &&
|
||||
context.task.has ("recur"))
|
||||
throw std::string ("You cannot remove the due date from a recurring task.");
|
||||
|
||||
if (task->has ("recur") &&
|
||||
!context.task.has ("recur"))
|
||||
throw std::string ("You cannot remove the recurrence from a recurring task.");
|
||||
|
||||
// Make all changes.
|
||||
foreach (other, all)
|
||||
{
|
||||
|
@ -1012,7 +1021,7 @@ int handleModify (std::string &outs)
|
|||
|
||||
if (taskDiff (before, *other))
|
||||
{
|
||||
if (changes && permission.confirmed (before, taskDifferences (before, *other) + "Are you sure?"))
|
||||
if (changes && permission.confirmed (before, taskDifferences (before, *other) + "Proceed with change?"))
|
||||
{
|
||||
context.tdb.update (*other);
|
||||
++count;
|
||||
|
@ -1072,7 +1081,7 @@ int handleAppend (std::string &outs)
|
|||
|
||||
if (taskDiff (before, *other))
|
||||
{
|
||||
if (changes && permission.confirmed (before, taskDifferences (before, *other) + "Are you sure?"))
|
||||
if (changes && permission.confirmed (before, taskDifferences (before, *other) + "Proceed with change?"))
|
||||
{
|
||||
context.tdb.update (*other);
|
||||
|
||||
|
@ -1228,12 +1237,21 @@ int handleDuplicate (std::string &outs)
|
|||
++count;
|
||||
}
|
||||
|
||||
if (context.config.get ("echo.command", true))
|
||||
{
|
||||
out << "Duplicated " << count << " task" << (count == 1 ? "" : "s") << std::endl;
|
||||
#ifdef FEATURE_NEW_ID
|
||||
// All this, just for an id number.
|
||||
std::vector <Task> all;
|
||||
Filter none;
|
||||
context.tdb.loadPending (all, none);
|
||||
out << "Created task " << context.tdb.nextId () << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
context.tdb.commit ();
|
||||
context.tdb.unlock ();
|
||||
|
||||
if (context.config.get ("echo.command", true))
|
||||
out << "Duplicated " << count << " task" << (count == 1 ? "" : "s") << std::endl;
|
||||
|
||||
outs = out.str ();
|
||||
return 0;
|
||||
}
|
||||
|
@ -1258,13 +1276,9 @@ void handleShell ()
|
|||
<< std::endl
|
||||
<< std::endl;
|
||||
|
||||
// Preserve any special override arguments, and reapply them for each
|
||||
// shell command.
|
||||
std::vector <std::string> special;
|
||||
foreach (arg, context.args)
|
||||
if (arg->substr (0, 3) == "rc." ||
|
||||
arg->substr (0, 3) == "rc:")
|
||||
special.push_back (*arg);
|
||||
// Make a copy because context.clear will delete them.
|
||||
std::string permanentOverrides = " " + context.file_override
|
||||
+ " " + context.var_overrides;
|
||||
|
||||
std::string quit = "quit"; // TODO i18n
|
||||
std::string command;
|
||||
|
@ -1276,11 +1290,13 @@ void handleShell ()
|
|||
|
||||
command = "";
|
||||
std::getline (std::cin, command);
|
||||
command = lowerCase (trim (command));
|
||||
std::string decoratedCommand = trim (command + permanentOverrides);
|
||||
|
||||
if (command.length () > 0 &&
|
||||
command.length () <= quit.length () &&
|
||||
command == quit.substr (0, command.length ()))
|
||||
// When looking for the 'quit' command, use 'command', not
|
||||
// 'decoratedCommand'.
|
||||
if (command.length () > 0 &&
|
||||
command.length () <= quit.length () &&
|
||||
lowerCase (command) == quit.substr (0, command.length ()))
|
||||
{
|
||||
keepGoing = false;
|
||||
}
|
||||
|
@ -1291,8 +1307,7 @@ void handleShell ()
|
|||
context.clear ();
|
||||
|
||||
std::vector <std::string> args;
|
||||
split (args, command, ' ');
|
||||
foreach (arg, special) context.args.push_back (*arg);
|
||||
split (args, decoratedCommand, ' ');
|
||||
foreach (arg, args) context.args.push_back (*arg);
|
||||
|
||||
context.initialize ();
|
||||
|
@ -1311,6 +1326,9 @@ void handleShell ()
|
|||
}
|
||||
}
|
||||
while (keepGoing && !std::cin.eof ());
|
||||
|
||||
// No need to repeat any overrides after the shell quits.
|
||||
context.clearMessages ();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -1443,7 +1461,7 @@ int handleAnnotate (std::string &outs)
|
|||
|
||||
if (taskDiff (before, *task))
|
||||
{
|
||||
if (permission.confirmed (before, taskDifferences (before, *task) + "Are you sure?"))
|
||||
if (permission.confirmed (before, taskDifferences (before, *task) + "Proceed with change?"))
|
||||
{
|
||||
context.tdb.update (*task);
|
||||
|
||||
|
|
66
src/tests/bug.327.t
Executable file
66
src/tests/bug.327.t
Executable file
|
@ -0,0 +1,66 @@
|
|||
#! /usr/bin/perl
|
||||
################################################################################
|
||||
## task - a command line task list manager.
|
||||
##
|
||||
## Copyright 2006 - 2009, Paul Beckingham.
|
||||
## All rights reserved.
|
||||
##
|
||||
## This program is free software; you can redistribute it and/or modify it under
|
||||
## the terms of the GNU General Public License as published by the Free Software
|
||||
## Foundation; either version 2 of the License, or (at your option) any later
|
||||
## version.
|
||||
##
|
||||
## This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
## FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
## details.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License along with
|
||||
## this program; if not, write to the
|
||||
##
|
||||
## Free Software Foundation, Inc.,
|
||||
## 51 Franklin Street, Fifth Floor,
|
||||
## Boston, MA
|
||||
## 02110-1301
|
||||
## USA
|
||||
##
|
||||
################################################################################
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Test::More tests => 6;
|
||||
|
||||
# Create the rc file.
|
||||
if (open my $fh, '>', 'bug.rc')
|
||||
{
|
||||
print $fh "data.location=.\n",
|
||||
"confirmation=no\n";
|
||||
close $fh;
|
||||
ok (-r 'bug.rc', 'Created bug.rc');
|
||||
}
|
||||
|
||||
# Setup: Add a recurring task then remove the due date.
|
||||
qx{../task rc:bug.rc add foo recur:yearly due:eoy};
|
||||
qx{../task rc:bug.rc li};
|
||||
qx{../task rc:bug.rc 2 due:};
|
||||
|
||||
# Result: Somehow the due date is incremented and wraps around to 12/31/1969,
|
||||
# then keeps going back to today.
|
||||
my $output = qx{../task rc:bug.rc li};
|
||||
like ($output, qr/^1 task$/ms, 'Should only be one task');
|
||||
|
||||
# Cleanup.
|
||||
unlink 'pending.data';
|
||||
ok (!-r 'pending.data', 'Removed pending.data');
|
||||
|
||||
unlink 'completed.data';
|
||||
ok (!-r 'completed.data', 'Removed completed.data');
|
||||
|
||||
unlink 'undo.data';
|
||||
ok (!-r 'undo.data', 'Removed undo.data');
|
||||
|
||||
unlink 'bug.rc';
|
||||
ok (!-r 'bug.rc', 'Removed bug.rc');
|
||||
|
||||
exit 0;
|
||||
|
|
@ -28,7 +28,7 @@
|
|||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Test::More tests => 14;
|
||||
use Test::More tests => 15;
|
||||
|
||||
# Create the rc file.
|
||||
if (open my $fh, '>', 'bulk.rc')
|
||||
|
@ -49,13 +49,13 @@ qx{../task rc:bulk.rc add t4 due:thursday};
|
|||
qx{../task rc:bulk.rc add t5 due:friday};
|
||||
qx{../task rc:bulk.rc add t6 due:saturday};
|
||||
|
||||
my $output = qx{yes|../task rc:bulk.rc pro:p1 pri:M 4 5 6};
|
||||
my $output = qx{echo "quit"|../task rc:bulk.rc pro:p1 pri:M 4 5 6};
|
||||
like ($output, qr/Modified 0 tasks/, '"quit" prevents any further modifications');
|
||||
|
||||
my $output = qx{echo "all"|../task rc:bulk.rc pro:p1 pri:M 4 5 6};
|
||||
unlike ($output, qr/Task 4 "t4"\n - No changes were made/, 'Task 4 modified');
|
||||
unlike ($output, qr/Task 5 "t5"\n - No changes were made/, 'Task 5 modified');
|
||||
unlike ($output, qr/Task 6 "t6"\n - No changes were made/, 'Task 6 modified');
|
||||
#diag ("---");
|
||||
#diag ($output);
|
||||
#diag ("---");
|
||||
|
||||
$output = qx{../task rc:bulk.rc info 4};
|
||||
like ($output, qr/Project\s+p1/, 'project applied to 4');
|
||||
|
|
|
@ -49,10 +49,10 @@ if (open my $fh, '>', 'response.txt')
|
|||
|
||||
qx{../task rc:confirm.rc add foo} for 1 .. 10;
|
||||
|
||||
# Test the various forms of "yes".
|
||||
my $output = qx{echo "yes" | ../task rc:confirm.rc del 1};
|
||||
like ($output, qr/Permanently delete task 1 'foo'\? \(y\/n\)/, 'confirmation - yes works');
|
||||
unlike ($output, qr/Task not deleted\./, 'confirmation - yes works');
|
||||
# Test the various forms of "Yes".
|
||||
my $output = qx{echo "Yes" | ../task rc:confirm.rc del 1};
|
||||
like ($output, qr/Permanently delete task 1 'foo'\? \(y\/n\)/, 'confirmation - Yes works');
|
||||
unlike ($output, qr/Task not deleted\./, 'confirmation - Yes works');
|
||||
|
||||
$output = qx{echo "ye" | ../task rc:confirm.rc del 2};
|
||||
like ($output, qr/Permanently delete task 2 'foo'\? \(y\/n\)/, 'confirmation - ye works');
|
||||
|
|
|
@ -34,7 +34,7 @@ use Test::More tests => 17;
|
|||
if (open my $fh, '>', 'default.rc')
|
||||
{
|
||||
print $fh "data.location=.\n",
|
||||
"default.command=rc:default.rc list\n",
|
||||
"default.command=list\n",
|
||||
"default.project=PROJECT\n",
|
||||
"default.priority=M\n";
|
||||
close $fh;
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Test::More tests => 12;
|
||||
use Test::More tests => 15;
|
||||
|
||||
# Create the rc file.
|
||||
if (open my $fh, '>', 'dup.rc')
|
||||
|
@ -55,6 +55,12 @@ like ($output, qr/Description\s+FOO/, 'duplicate modified description');
|
|||
like ($output, qr/Priority\s+H/, 'duplicate added priority');
|
||||
like ($output, qr/Tags\s+tag/, 'duplicate added tag');
|
||||
|
||||
# Test the output of the duplicate command - returning id of duplicated task
|
||||
$output = qx{../task rc:dup.rc duplicate 1};
|
||||
like ($output, qr/Duplicated\s+1\s+'foo'/, 'duplicate output task id and description');
|
||||
like ($output, qr/Duplicated\s+1\s+task/, 'duplicate output number of tasks duplicated');
|
||||
like ($output, qr/Created\s+task\s+4/, 'duplicate output of new task id');
|
||||
|
||||
# Cleanup.
|
||||
unlink 'pending.data';
|
||||
ok (!-r 'pending.data', 'Removed pending.data');
|
||||
|
|
|
@ -34,7 +34,7 @@ Context context;
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
int main (int argc, char** argv)
|
||||
{
|
||||
UnitTest t (109);
|
||||
UnitTest t (117);
|
||||
|
||||
// void wrapText (std::vector <std::string>& lines, const std::string& text, const int width)
|
||||
std::string text = "This is a test of the line wrapping code.";
|
||||
|
@ -104,12 +104,19 @@ int main (int argc, char** argv)
|
|||
t.is (items.size (), (size_t) 1, "split 'a' '-' -> 1 item");
|
||||
t.is (items[0], "a", "split 'a' '-' -> 'a'");
|
||||
|
||||
split (items, unsplit, '-');
|
||||
t.is (items.size (), (size_t) 1, "split 'a' '-' -> 1 item");
|
||||
t.is (items[0], "a", "split 'a' '-' -> 'a'");
|
||||
|
||||
unsplit = "-";
|
||||
split (items, unsplit, '-');
|
||||
t.is (items.size (), (size_t) 2, "split '-' '-' -> '' ''");
|
||||
t.is (items[0], "", "split '-' '-' -> [0] ''");
|
||||
t.is (items[1], "", "split '-' '-' -> [1] ''");
|
||||
|
||||
split_minimal (items, unsplit, '-');
|
||||
t.is (items.size (), (size_t) 1, "split '-' '-' -> '-'");
|
||||
|
||||
unsplit = "-a-bc-def";
|
||||
split (items, unsplit, '-');
|
||||
t.is (items.size (), (size_t) 4, "split '-a-bc-def' '-' -> '' 'a' 'bc' 'def'");
|
||||
|
@ -118,11 +125,20 @@ int main (int argc, char** argv)
|
|||
t.is (items[2], "bc", "split '-a-bc-def' '-' -> [2] 'bc'");
|
||||
t.is (items[3], "def", "split '-a-bc-def' '-' -> [3] 'def'");
|
||||
|
||||
split_minimal (items, unsplit, '-');
|
||||
t.is (items.size (), (size_t) 3, "split '-a-bc-def' '-' -> 'a' 'bc' 'def'");
|
||||
t.is (items[0], "a", "split '-a-bc-def' '-' -> [1] 'a'");
|
||||
t.is (items[1], "bc", "split '-a-bc-def' '-' -> [2] 'bc'");
|
||||
t.is (items[2], "def", "split '-a-bc-def' '-' -> [3] 'def'");
|
||||
|
||||
// void split (std::vector<std::string>& results, const std::string& input, const std::string& delimiter)
|
||||
unsplit = "";
|
||||
split (items, unsplit, "--");
|
||||
t.is (items.size (), (size_t) 0, "split '' '--' -> 0 items");
|
||||
|
||||
split_minimal (items, unsplit, "--");
|
||||
t.is (items.size (), (size_t) 0, "split '' '--' -> 0");
|
||||
|
||||
unsplit = "a";
|
||||
split (items, unsplit, "--");
|
||||
t.is (items.size (), (size_t) 1, "split 'a' '--' -> 1 item");
|
||||
|
|
|
@ -38,6 +38,7 @@ int main (int argc, char** argv)
|
|||
|
||||
// TODO bool confirm (const std::string&);
|
||||
// TODO int confirm3 (const std::string&);
|
||||
// TODO int confirm4 (const std::string&);
|
||||
// TODO void delay (float);
|
||||
// TODO std::string formatSeconds (time_t);
|
||||
// TODO std::string formatSecondsCompact (time_t);
|
||||
|
@ -83,15 +84,15 @@ int main (int argc, char** argv)
|
|||
Task rightAgain (right);
|
||||
|
||||
std::string output = taskDifferences (left, right);
|
||||
t.ok (taskDiff (left, right), "Detected changes");
|
||||
t.ok (output.find ("zero was changed from '0' to '00'") != std::string::npos, "Detected change zero:0 -> zero:00");
|
||||
t.ok (output.find ("one was deleted") != std::string::npos, "Detected deletion one:1 ->");
|
||||
t.ok (output.find ("two") == std::string::npos, "Detected no change two:2 -> two:2");
|
||||
t.ok (output.find ("three was set to '3'") != std::string::npos, "Detected addition -> three:3");
|
||||
t.ok (taskDiff (left, right), "Detected changes");
|
||||
t.ok (output.find ("zero will be changed from '0' to '00'") != std::string::npos, "Detected change zero:0 -> zero:00");
|
||||
t.ok (output.find ("one will be deleted") != std::string::npos, "Detected deletion one:1 ->");
|
||||
t.ok (output.find ("two") == std::string::npos, "Detected no change two:2 -> two:2");
|
||||
t.ok (output.find ("three will be set to '3'") != std::string::npos, "Detected addition -> three:3");
|
||||
|
||||
t.notok (taskDiff (right, rightAgain), "No changes detected");
|
||||
t.notok (taskDiff (right, rightAgain), "No changes detected");
|
||||
output = taskDifferences (right, rightAgain);
|
||||
t.ok (output.find ("No changes were made") != std::string::npos, "No changes detected");
|
||||
t.ok (output.find ("No changes will be made") != std::string::npos, "No changes detected");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
42
src/text.cpp
42
src/text.cpp
|
@ -72,6 +72,26 @@ void split (
|
|||
results.push_back (input.substr (start, std::string::npos));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void split_minimal (
|
||||
std::vector<std::string>& results,
|
||||
const std::string& input,
|
||||
const char delimiter)
|
||||
{
|
||||
results.clear ();
|
||||
std::string::size_type start = 0;
|
||||
std::string::size_type i;
|
||||
while ((i = input.find (delimiter, start)) != std::string::npos)
|
||||
{
|
||||
if (i != start)
|
||||
results.push_back (input.substr (start, i - start));
|
||||
start = i + 1;
|
||||
}
|
||||
|
||||
if (input.length ())
|
||||
results.push_back (input.substr (start, std::string::npos));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void split (
|
||||
std::vector<std::string>& results,
|
||||
|
@ -93,6 +113,28 @@ void split (
|
|||
results.push_back (input.substr (start, std::string::npos));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void split_minimal (
|
||||
std::vector<std::string>& results,
|
||||
const std::string& input,
|
||||
const std::string& delimiter)
|
||||
{
|
||||
results.clear ();
|
||||
std::string::size_type length = delimiter.length ();
|
||||
|
||||
std::string::size_type start = 0;
|
||||
std::string::size_type i;
|
||||
while ((i = input.find (delimiter, start)) != std::string::npos)
|
||||
{
|
||||
if (i != start)
|
||||
results.push_back (input.substr (start, i - start));
|
||||
start = i + length;
|
||||
}
|
||||
|
||||
if (input.length ())
|
||||
results.push_back (input.substr (start, std::string::npos));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void join (
|
||||
std::string& result,
|
||||
|
|
|
@ -40,6 +40,8 @@ std::string unquoteText (const std::string&);
|
|||
void extractLine (std::string&, std::string&, int);
|
||||
void split (std::vector<std::string>&, const std::string&, const char);
|
||||
void split (std::vector<std::string>&, const std::string&, const std::string&);
|
||||
void split_minimal (std::vector<std::string>&, const std::string&, const char);
|
||||
void split_minimal (std::vector<std::string>&, const std::string&, const std::string&);
|
||||
void join (std::string&, const std::string&, const std::vector<std::string>&);
|
||||
std::string commify (const std::string&);
|
||||
std::string lowerCase (const std::string&);
|
||||
|
|
51
src/util.cpp
51
src/util.cpp
|
@ -82,7 +82,7 @@ bool confirm (const std::string& question)
|
|||
int confirm3 (const std::string& question)
|
||||
{
|
||||
std::vector <std::string> options;
|
||||
options.push_back ("yes");
|
||||
options.push_back ("Yes");
|
||||
options.push_back ("no");
|
||||
options.push_back ("all");
|
||||
|
||||
|
@ -104,11 +104,49 @@ int confirm3 (const std::string& question)
|
|||
}
|
||||
while (matches.size () != 1);
|
||||
|
||||
if (matches[0] == "yes") return 1;
|
||||
if (matches[0] == "Yes") return 1;
|
||||
else if (matches[0] == "all") return 2;
|
||||
else return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 0 = no
|
||||
// 1 = yes
|
||||
// 2 = all
|
||||
// 3 = quit
|
||||
int confirm4 (const std::string& question)
|
||||
{
|
||||
std::vector <std::string> options;
|
||||
options.push_back ("Yes");
|
||||
options.push_back ("no");
|
||||
options.push_back ("all");
|
||||
options.push_back ("quit");
|
||||
|
||||
std::string answer;
|
||||
std::vector <std::string> matches;
|
||||
|
||||
do
|
||||
{
|
||||
std::cout << question
|
||||
<< " ("
|
||||
<< options[0] << "/"
|
||||
<< options[1] << "/"
|
||||
<< options[2] << "/"
|
||||
<< options[3]
|
||||
<< ") ";
|
||||
|
||||
std::getline (std::cin, answer);
|
||||
answer = trim (answer);
|
||||
autoComplete (answer, options, matches);
|
||||
}
|
||||
while (matches.size () != 1);
|
||||
|
||||
if (matches[0] == "Yes") return 1;
|
||||
else if (matches[0] == "all") return 2;
|
||||
else if (matches[0] == "quit") return 3;
|
||||
else return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void delay (float f)
|
||||
{
|
||||
|
@ -500,22 +538,21 @@ std::string taskDifferences (const Task& before, const Task& after)
|
|||
foreach (name, beforeOnly)
|
||||
out << " - "
|
||||
<< *name
|
||||
<< " was deleted\n";
|
||||
<< " will be deleted\n";
|
||||
|
||||
foreach (name, afterOnly)
|
||||
out << " - "
|
||||
<< *name
|
||||
<< " was set to '"
|
||||
<< " will be set to '"
|
||||
<< renderAttribute (*name, after.get (*name))
|
||||
<< "'\n";
|
||||
|
||||
foreach (name, beforeAtts)
|
||||
if (*name != "uuid" &&
|
||||
after.get (*name) != "" &&
|
||||
before.get (*name) != after.get (*name))
|
||||
out << " - "
|
||||
<< *name
|
||||
<< " was changed from '"
|
||||
<< " will be changed from '"
|
||||
<< renderAttribute (*name, before.get (*name))
|
||||
<< "' to '"
|
||||
<< renderAttribute (*name, after.get (*name))
|
||||
|
@ -523,7 +560,7 @@ std::string taskDifferences (const Task& before, const Task& after)
|
|||
|
||||
// Shouldn't just say nothing.
|
||||
if (out.str ().length () == 0)
|
||||
out << " - No changes were made\n";
|
||||
out << " - No changes will be made\n";
|
||||
|
||||
return out.str ();
|
||||
}
|
||||
|
|
|
@ -53,6 +53,7 @@ for (typeof (c) *foreach_p = & (c); \
|
|||
// util.cpp
|
||||
bool confirm (const std::string&);
|
||||
int confirm3 (const std::string&);
|
||||
int confirm4 (const std::string&);
|
||||
void delay (float);
|
||||
std::string formatSeconds (time_t);
|
||||
std::string formatSecondsCompact (time_t);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue