diff --git a/NEWS b/NEWS index 328d95ce7..b0dd065f1 100644 --- a/NEWS +++ b/NEWS @@ -56,6 +56,8 @@ New configuration options in taskwarrior 2.0.0 - New 'exit.on.missing.db' control causes an exit if the ~/.task directory (or override) is missing. - New 'color.completed' and 'color.deleted' color rules. + - New 'abbreviation.minimum' setting controls how short an abbreviated + command or value may be. Newly deprecated features in taskwarrior 2.0.0 diff --git a/doc/man/taskrc.5.in b/doc/man/taskrc.5.in index 64cfecc76..e1a2f70a0 100644 --- a/doc/man/taskrc.5.in +++ b/doc/man/taskrc.5.in @@ -387,6 +387,11 @@ of the longer-term rate. The calculation is as follows: rate = (long-term-rate * (1 - bias)) + (short-term-rate * bias) +.TP +.B abbreviation.minimum=2 +Minimum length of any abbreviated command/value. This means that "ve", "ver", +"vers", "versi", "versio" will all equate to "version", but "v" will not. +Default is 2. .TP .B debug=off Taskwarrior has a debug mode that causes diagnostic output to be displayed. diff --git a/src/Arguments.cpp b/src/Arguments.cpp index b793021f3..cbc7de062 100644 --- a/src/Arguments.cpp +++ b/src/Arguments.cpp @@ -798,7 +798,10 @@ bool Arguments::is_command ( std::string& command) { std::vector matches; - if (autoComplete (command, keywords, matches) == 1) + if (autoComplete (command, + keywords, + matches, + context.config.getInteger ("abbreviation.minimum")) == 1) { command = matches[0]; return true; @@ -1090,7 +1093,10 @@ bool Arguments::is_attribute (const std::string& input, std::string& canonical) } std::vector matches; - autoComplete (input, candidates, matches); + autoComplete (input, + candidates, + matches, + context.config.getInteger ("abbreviation.minimum")); if (matches.size () == 1) { @@ -1116,7 +1122,10 @@ bool Arguments::is_modifier (const std::string& input) } std::vector matches; - autoComplete (input, candidates, matches); + autoComplete (input, + candidates, + matches, + context.config.getInteger ("abbreviation.minimum")); if (matches.size () == 1) return true; diff --git a/src/Att.cpp b/src/Att.cpp index 3f81dc633..536b2e632 100644 --- a/src/Att.cpp +++ b/src/Att.cpp @@ -301,7 +301,10 @@ bool Att::validNameValue ( candidates.push_back (modifiableNames[i]); std::vector matches; - autoComplete (name, candidates, matches); + autoComplete (name, + candidates, + matches, + context.config.getInteger ("abbreviation.minimum")); if (matches.size () == 0) return false; @@ -327,7 +330,10 @@ bool Att::validNameValue ( candidates.push_back (modifierNames[i]); matches.clear (); - autoComplete (mod, candidates, matches); + autoComplete (mod, + candidates, + matches, + context.config.getInteger ("abbreviation.minimum")); if (matches.size () == 0) throw std::string ("Unrecognized modifier '") + mod + "'."; @@ -454,7 +460,10 @@ bool Att::validNameValue ( candidates.push_back ("deleted"); candidates.push_back ("recurring"); candidates.push_back ("waiting"); - autoComplete (value, candidates, matches); + autoComplete (value, + candidates, + matches, + context.config.getInteger ("abbreviation.minimum")); if (matches.size () == 1) value = matches[0]; diff --git a/src/Config.cpp b/src/Config.cpp index 1edd2328b..ccdb8beb1 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -102,6 +102,7 @@ std::string Config::defaults = "expressions=on # Support for algebraic expressions\n" "patterns=on # Support for regex patterns\n" "json.array=off # Enclose JSON output in [ ]\n" + "abbreviation.minimum=2 # Shortest allowed abbreviation\n" "\n" "# Dates\n" "dateformat=m/d/Y # Preferred input and display date format\n" diff --git a/src/DOM.cpp b/src/DOM.cpp index ff3600ed7..bb4f89a1e 100644 --- a/src/DOM.cpp +++ b/src/DOM.cpp @@ -27,7 +27,6 @@ #define L10N // Localization complete. -#include // TODO Remove #include #include #include @@ -192,16 +191,16 @@ const std::string DOM::get (const std::string& name) // TODO .recur // TODO .depends // -// {.entry,.start,.end,.due,.until,.wait} -// .description -// .project -// .priority -// .parent -// .status -// .tags -// .urgency -// .recur -// .depends +// {entry,start,end,due,until,wait} +// description +// project +// priority +// parent +// status +// tags +// urgency +// recur +// depends // const std::string DOM::get (const std::string& name, const Task& task) { @@ -295,7 +294,6 @@ bool DOM::is_literal (std::string& input) if (Date::valid (input, context.config.get ("dateformat"))) { input = Date (input).toEpochString (); - std::cout << "# DOM::is_literal '" << input << "' --> date\n"; return true; } @@ -323,7 +321,6 @@ bool DOM::is_literal (std::string& input) if (n.getInt (i) && n.depleted ()) return true; -// std::cout << "# DOM::is_literal '" << input << "' --> unknown\n"; return false; } diff --git a/src/Date.cpp b/src/Date.cpp index 47024f1a4..a50ac70fa 100644 --- a/src/Date.cpp +++ b/src/Date.cpp @@ -27,6 +27,7 @@ #define L10N // Localization complete. +#include // TODO Remove. #include #include #include @@ -175,17 +176,18 @@ void Date::toMDY (int& m, int& d, int& y) } //////////////////////////////////////////////////////////////////////////////// -const std::string Date::toString (const std::string& format /*= "m/d/Y" */) const +const std::string Date::toString ( + const std::string& format /*= "m/d/Y" */) const { - // Making this local copy seems to fix a bug. Remove the local copy and you'll - // see segmentation faults and all kinds of gibberish. + // Making this local copy seems to fix a bug. Remove the local copy and + // you'll see segmentation faults and all kinds of gibberish. std::string localFormat = format; char buffer[12]; std::string formatted; for (unsigned int i = 0; i < localFormat.length (); ++i) { - char c = localFormat[i]; + int c = localFormat[i]; switch (c) { case 'm': sprintf (buffer, "%d", this->month ()); break; @@ -794,8 +796,9 @@ bool Date::isRelativeDate (const std::string& input) supported.push_back ("later"); supported.push_back ("someday"); + // Hard-coded 3, despite rc.abbreviation.minimum. std::vector matches; - if (autoComplete (in, supported, matches) == 1) + if (autoComplete (in, supported, matches, 3) == 1) { std::string found = matches[0]; diff --git a/src/Duration.cpp b/src/Duration.cpp index 15e91c749..0d427fa12 100644 --- a/src/Duration.cpp +++ b/src/Duration.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -91,6 +92,8 @@ static const char* durations[] = #define NUM_DURATIONS (sizeof (durations) / sizeof (durations[0])) +extern Context context; + //////////////////////////////////////////////////////////////////////////////// Duration::Duration () : mSecs (0) @@ -339,7 +342,10 @@ bool Duration::valid (const std::string& input) supported.push_back (durations[i]); std::vector matches; - if (autoComplete (units, supported, matches) == 1) + if (autoComplete (units, + supported, + matches, + context.config.getInteger ("abbreviation.minimum")) == 1) return true; return false; @@ -376,7 +382,10 @@ void Duration::parse (const std::string& input) mSecs = 0; std::vector matches; - if (autoComplete (units, supported, matches) == 1) + if (autoComplete (units, + supported, + matches, + context.config.getInteger ("abbreviation.minimum")) == 1) { std::string match = matches[0]; diff --git a/src/Expression.cpp b/src/Expression.cpp index 9d3b3b075..fffb98b52 100644 --- a/src/Expression.cpp +++ b/src/Expression.cpp @@ -174,7 +174,7 @@ void Expression::eval (const Task& task, std::vector & value_stack) right._raw_type = value_stack.back ()._raw_type; } value_stack.pop_back (); -// std::cout << "# right raw=" << right._raw << " type=" << right._type << " value=" << right._string << "\n"; +// std::cout << "# right variant " << right.dump () << "\n"; // lvalue (dom). Variant left (value_stack.back ()); @@ -185,7 +185,7 @@ void Expression::eval (const Task& task, std::vector & value_stack) left._raw_type = value_stack.back ()._raw_type; } value_stack.pop_back (); -// std::cout << "# left raw=" << left._raw << " type=" << left._type << " value=" << left._string << "\n"; +// std::cout << "# left variant " << left.dump () << "\n"; // Now the binary operators. if (arg->_first == "and") @@ -463,7 +463,7 @@ void Expression::create_variant ( const std::string& value, const std::string& type) { -// std::cout << "# operand '" << value << "' as " << type << "\n"; +// std::cout << "# create_variant " << value << "/" << type << "\n"; // DOM references are not resolved until the operator is processed. This // preserves the original name, which helps determine how to apply the diff --git a/src/Variant.cpp b/src/Variant.cpp index 5c186f126..fae6cf587 100644 --- a/src/Variant.cpp +++ b/src/Variant.cpp @@ -41,6 +41,12 @@ Variant::Variant () : _type (v_unknown) , _raw ("") , _raw_type ("") +, _bool (false) +, _integer (0) +, _double (0.0) +, _string ("") +, _date (0) +, _duration (0) { } @@ -66,44 +72,86 @@ Variant::Variant (const Variant& other) //////////////////////////////////////////////////////////////////////////////// Variant::Variant (const bool input) +: _type (v_boolean) +, _raw ("") +, _raw_type ("") +, _bool (input) +, _integer (0) +, _double (0.0) +, _string ("") +, _date (0) +, _duration (0) { - _type = v_boolean; - _bool = input; } //////////////////////////////////////////////////////////////////////////////// Variant::Variant (const int input) +: _type (v_integer) +, _raw ("") +, _raw_type ("") +, _bool (false) +, _integer (input) +, _double (0.0) +, _string ("") +, _date (0) +, _duration (0) { - _type = v_integer; - _integer = input; } //////////////////////////////////////////////////////////////////////////////// Variant::Variant (const double& input) +: _type (v_double) +, _raw ("") +, _raw_type ("") +, _bool (false) +, _integer (0) +, _double (input) +, _string ("") +, _date (0) +, _duration (0) { - _type = v_double; - _double = input; } //////////////////////////////////////////////////////////////////////////////// Variant::Variant (const std::string& input) +: _type (v_string) +, _raw ("") +, _raw_type ("") +, _bool (false) +, _integer (0) +, _double (0.0) +, _string (input) +, _date (0) +, _duration (0) { - _type = v_string; - _string = input; } //////////////////////////////////////////////////////////////////////////////// Variant::Variant (const Date& input) +: _type (v_date) +, _raw ("") +, _raw_type ("") +, _bool (false) +, _integer (0) +, _double (0.0) +, _string ("") +, _date (input) +, _duration (0) { - _type = v_date; - _date = input; } //////////////////////////////////////////////////////////////////////////////// Variant::Variant (const Duration& input) +: _type (v_duration) +, _raw ("") +, _raw_type ("") +, _bool (false) +, _integer (0) +, _double (0.0) +, _string ("") +, _date (0) +, _duration (input) { - _type = v_duration; - _duration = input; } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/commands/CmdCalendar.cpp b/src/commands/CmdCalendar.cpp index 101437fd3..e216ce728 100644 --- a/src/commands/CmdCalendar.cpp +++ b/src/commands/CmdCalendar.cpp @@ -123,11 +123,11 @@ int CmdCalendar::execute (std::string& output) for (arg = args.begin (); arg != args.end (); ++arg) { // Some version of "calendar". - if (autoComplete (lowerCase (*arg), commandNames, matches) == 1) + if (autoComplete (lowerCase (*arg), commandNames, matches, context.config.getInteger ("abbreviation.minimum")) == 1) continue; // "due". - else if (autoComplete (lowerCase (*arg), keywordNames, matches) == 1) + else if (autoComplete (lowerCase (*arg), keywordNames, matches, context.config.getInteger ("abbreviation.minimum")) == 1) getpendingdate = true; // "y". @@ -147,7 +147,7 @@ int CmdCalendar::execute (std::string& output) } // "January" etc. - else if (autoComplete (lowerCase (*arg), monthNames, matches) == 1) + else if (autoComplete (lowerCase (*arg), monthNames, matches, context.config.getInteger ("abbreviation.minimum")) == 1) { argMonth = Date::monthOfYear (matches[0]); if (argMonth == -1) diff --git a/src/commands/CmdShow.cpp b/src/commands/CmdShow.cpp index a028e14d7..8eecf0f5f 100644 --- a/src/commands/CmdShow.cpp +++ b/src/commands/CmdShow.cpp @@ -67,6 +67,7 @@ int CmdShow::execute (std::string& output) // Note that there is a leading and trailing space, to make it easier to // search for whole words. std::string recognized = + " abbreviation.minimum" " active.indicator" " annotations" " avoidlastcolumn" diff --git a/src/rules.cpp b/src/rules.cpp index 0fdc7e570..fca01babb 100644 --- a/src/rules.cpp +++ b/src/rules.cpp @@ -74,7 +74,7 @@ void initializeColorRules () { // Add the leading "color." string. std::string rule = "color." + *p; - autoComplete (rule, rules, results); + autoComplete (rule, rules, results, 3); // Hard-coded 3. std::vector ::iterator r; for (r = results.begin (); r != results.end (); ++r) diff --git a/src/text.cpp b/src/text.cpp index e53ddab88..a2b06eb58 100644 --- a/src/text.cpp +++ b/src/text.cpp @@ -482,7 +482,8 @@ void guess ( std::string& candidate) { std::vector matches; - autoComplete (candidate, options, matches); + autoComplete (candidate, options, matches, + context.config.getInteger ("abbreviation.minimum")); if (1 == matches.size ()) candidate = matches[0]; diff --git a/src/util.cpp b/src/util.cpp index beb374d11..72a4a1818 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -75,7 +75,7 @@ bool confirm (const std::string& question) std::getline (std::cin, answer); answer = std::cin.eof() ? STRING_UTIL_CONFIRM_NO : lowerCase (trim (answer)); - autoComplete (answer, options, matches); + autoComplete (answer, options, matches, 1); // Hard-coded 1. } while (matches.size () != 1); @@ -109,7 +109,7 @@ int confirm3 (const std::string& question) std::getline (std::cin, answer); answer = trim (answer); - autoComplete (answer, options, matches); + autoComplete (answer, options, matches, 1); // Hard-coded 1. } while (matches.size () != 1); @@ -150,7 +150,7 @@ int confirm4 (const std::string& question) std::getline (std::cin, answer); answer = trim (answer); - autoComplete (answer, options, matches); + autoComplete (answer, options, matches, 1); // Hard-coded 1. } while (matches.size () != 1); @@ -190,7 +190,8 @@ std::string formatBytes (size_t bytes) int autoComplete ( const std::string& partial, const std::vector& list, - std::vector& matches) + std::vector& matches, + int minimum/* = 2*/) { matches.clear (); @@ -211,7 +212,8 @@ int autoComplete ( } // Maintain a list of partial matches. - else if (length <= item->length () && + else if (length >= (unsigned) minimum && + length <= item->length () && partial == item->substr (0, length)) matches.push_back (*item); } diff --git a/src/util.h b/src/util.h index 95db8e912..776d9479f 100644 --- a/src/util.h +++ b/src/util.h @@ -62,7 +62,7 @@ int confirm3 (const std::string&); int confirm4 (const std::string&); void delay (float); std::string formatBytes (size_t); -int autoComplete (const std::string&, const std::vector&, std::vector&); +int autoComplete (const std::string&, const std::vector&, std::vector&, int minimum = 1); #if defined(HAVE_UUID) && !defined(HAVE_UUID_UNPARSE_LOWER) void uuid_unparse_lower (uuid_t uu, char *out);