diff --git a/AUTHORS b/AUTHORS index 1776ef143..6e55fde6e 100644 --- a/AUTHORS +++ b/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 + diff --git a/ChangeLog b/ChangeLog index 7a2f56a67..c43c91078 100644 --- a/ChangeLog +++ b/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. diff --git a/NEWS b/NEWS index 6a358f0b1..160ab2fde 100644 --- a/NEWS +++ b/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: diff --git a/README b/README index de698bba5..08cd2dec8 100644 --- a/README +++ b/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: diff --git a/configure.ac b/configure.ac index 7b8b02e46..448519e00 100644 --- a/configure.ac +++ b/configure.ac @@ -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. diff --git a/doc/man/taskrc.5 b/doc/man/taskrc.5 index 1490c766b..4fc4da4f5 100644 --- a/doc/man/taskrc.5 +++ b/doc/man/taskrc.5 @@ -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 diff --git a/src/Config.cpp b/src/Config.cpp index f168b035c..7b0657bcb 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -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" diff --git a/src/Context.cpp b/src/Context.cpp index 734af52b4..911d98203 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -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 (); diff --git a/src/Context.h b/src/Context.h index e903bb8a7..bf56675bc 100644 --- a/src/Context.h +++ b/src/Context.h @@ -83,7 +83,8 @@ public: StringTable stringtable; std::string program; std::vector args; - std::string overrides; + std::string file_override; + std::string var_overrides; Cmd cmd; std::map aliases; std::vector tagAdditions; diff --git a/src/Permission.cpp b/src/Permission.cpp index d70c22b1d..2ea8cff7a 100644 --- a/src/Permission.cpp +++ b/src/Permission.cpp @@ -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; } diff --git a/src/Permission.h b/src/Permission.h index e3755add2..0d94946a7 100644 --- a/src/Permission.h +++ b/src/Permission.h @@ -44,6 +44,7 @@ public: private: bool needConfirmation; bool allConfirmed; + bool quit; }; #endif diff --git a/src/Task.cpp b/src/Task.cpp index c8c9d6e07..2f49c6ab1 100644 --- a/src/Task.cpp +++ b/src/Task.cpp @@ -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 or characters."); // TODO i18n diff --git a/src/command.cpp b/src/command.cpp index 0ed918d3b..6066ef4c1 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -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 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 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 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); diff --git a/src/tests/bug.327.t b/src/tests/bug.327.t new file mode 100755 index 000000000..de8340e0a --- /dev/null +++ b/src/tests/bug.327.t @@ -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; + diff --git a/src/tests/bug.bulk.t b/src/tests/bug.bulk.t index 2016abc13..5c8d9602c 100755 --- a/src/tests/bug.bulk.t +++ b/src/tests/bug.bulk.t @@ -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'); diff --git a/src/tests/confirmation.t b/src/tests/confirmation.t index dd9c6bc9e..9ebf21921 100755 --- a/src/tests/confirmation.t +++ b/src/tests/confirmation.t @@ -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'); diff --git a/src/tests/default.t b/src/tests/default.t index b80e81a0e..d452a5a4e 100755 --- a/src/tests/default.t +++ b/src/tests/default.t @@ -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; diff --git a/src/tests/duplicate.t b/src/tests/duplicate.t index 19ea26911..f148e7b03 100755 --- a/src/tests/duplicate.t +++ b/src/tests/duplicate.t @@ -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'); diff --git a/src/tests/text.t.cpp b/src/tests/text.t.cpp index e1813aa1b..b0437c1d6 100644 --- a/src/tests/text.t.cpp +++ b/src/tests/text.t.cpp @@ -34,7 +34,7 @@ Context context; //////////////////////////////////////////////////////////////////////////////// int main (int argc, char** argv) { - UnitTest t (109); + UnitTest t (117); // void wrapText (std::vector & 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& 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"); diff --git a/src/tests/util.t.cpp b/src/tests/util.t.cpp index 56038cef8..cabdc59ec 100644 --- a/src/tests/util.t.cpp +++ b/src/tests/util.t.cpp @@ -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; } diff --git a/src/text.cpp b/src/text.cpp index d980b7c34..727bfd811 100644 --- a/src/text.cpp +++ b/src/text.cpp @@ -72,6 +72,26 @@ void split ( results.push_back (input.substr (start, std::string::npos)); } +//////////////////////////////////////////////////////////////////////////////// +void split_minimal ( + std::vector& 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& results, @@ -93,6 +113,28 @@ void split ( results.push_back (input.substr (start, std::string::npos)); } +//////////////////////////////////////////////////////////////////////////////// +void split_minimal ( + std::vector& 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, diff --git a/src/text.h b/src/text.h index 95eb718a7..e23864cd8 100644 --- a/src/text.h +++ b/src/text.h @@ -40,6 +40,8 @@ std::string unquoteText (const std::string&); void extractLine (std::string&, std::string&, int); void split (std::vector&, const std::string&, const char); void split (std::vector&, const std::string&, const std::string&); +void split_minimal (std::vector&, const std::string&, const char); +void split_minimal (std::vector&, const std::string&, const std::string&); void join (std::string&, const std::string&, const std::vector&); std::string commify (const std::string&); std::string lowerCase (const std::string&); diff --git a/src/util.cpp b/src/util.cpp index 75938a318..a61ac0fd5 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -82,7 +82,7 @@ bool confirm (const std::string& question) int confirm3 (const std::string& question) { std::vector 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 options; + options.push_back ("Yes"); + options.push_back ("no"); + options.push_back ("all"); + options.push_back ("quit"); + + std::string answer; + std::vector 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 (); } diff --git a/src/util.h b/src/util.h index 2b0b69b01..0a2102abf 100644 --- a/src/util.h +++ b/src/util.h @@ -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);