mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-07-07 20:06:36 +02:00
Enhancements - Cmd object
- New Cmd object to handle localized commands, customReports and general command parsing. - Localized new Subst methods. - Relocate guess method from parse.cpp to text.cpp. - Converted Att object to use new valid/parse scheme. - Unit tests for Cmd object. - Fixed att.t.cpp unit tests.
This commit is contained in:
parent
ffa0c6e758
commit
24f31eeb00
17 changed files with 416 additions and 52 deletions
|
@ -26,7 +26,7 @@
|
|||
113 Unrecognized character(s) at end of substitution
|
||||
114 Malformed substitution
|
||||
|
||||
# 2xx Commands
|
||||
# 2xx Commands - must be sequential
|
||||
200 active
|
||||
201 add
|
||||
202 append
|
||||
|
@ -57,7 +57,7 @@
|
|||
227 undo
|
||||
228 version
|
||||
|
||||
# 3xx Attributes
|
||||
# 3xx Attributes - must be sequential
|
||||
300 project
|
||||
301 priority
|
||||
302 fg
|
||||
|
@ -71,7 +71,7 @@
|
|||
310 mask
|
||||
311 imask
|
||||
|
||||
# 35x Attribute modifiers
|
||||
# 35x Attribute modifiers - must be sequential
|
||||
350 before
|
||||
351 after
|
||||
352 not
|
||||
|
|
31
src/Att.cpp
31
src/Att.cpp
|
@ -86,13 +86,39 @@ Att::~Att ()
|
|||
{
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
bool Att::valid (const std::string& input) const
|
||||
{
|
||||
Nibbler n (input);
|
||||
std::string ignored;
|
||||
if (n.getUntilOneOf (".:", ignored))
|
||||
{
|
||||
if (ignored.length () == 0)
|
||||
return false;
|
||||
|
||||
while (n.skip ('.'))
|
||||
if (!n.getUntilOneOf (".:", ignored))
|
||||
return false;
|
||||
|
||||
if (n.skip (':') &&
|
||||
(n.getQuoted ('"', ignored) ||
|
||||
n.getUntil (' ', ignored) ||
|
||||
n.getUntilEOS (ignored)))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// start --> name --> . --> mod --> : --> " --> value --> " --> end
|
||||
// ^ |
|
||||
// |__________|
|
||||
//
|
||||
bool Att::parse (Nibbler& n)
|
||||
void Att::parse (Nibbler& n)
|
||||
{
|
||||
// Ensure a clean object first.
|
||||
mName = "";
|
||||
|
@ -126,7 +152,6 @@ bool Att::parse (Nibbler& n)
|
|||
n.getUntil (' ', mValue))
|
||||
{
|
||||
decode (mValue);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
throw std::string ("Missing attribute value"); // TODO i18n
|
||||
|
@ -136,8 +161,6 @@ bool Att::parse (Nibbler& n)
|
|||
}
|
||||
else
|
||||
throw std::string ("Missing : after attribute name"); // TODO i18n
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -41,7 +41,8 @@ public:
|
|||
Att& operator= (const Att&); // Assignment operator
|
||||
~Att (); // Destructor
|
||||
|
||||
bool parse (Nibbler&);
|
||||
bool valid (const std::string&) const;
|
||||
void parse (Nibbler&);
|
||||
bool validMod (const std::string&) const;
|
||||
bool match (const Att&) const;
|
||||
|
||||
|
|
175
src/Cmd.cpp
Normal file
175
src/Cmd.cpp
Normal file
|
@ -0,0 +1,175 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <iostream>
|
||||
#include "Cmd.h"
|
||||
#include "Context.h"
|
||||
#include "util.h"
|
||||
#include "text.h"
|
||||
#include "i18n.h"
|
||||
|
||||
extern Context context;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
Cmd::Cmd ()
|
||||
: command ("")
|
||||
{
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
Cmd::Cmd (const std::string& input)
|
||||
{
|
||||
parse (input);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
Cmd::~Cmd ()
|
||||
{
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Determines whether the string represents a unique command name or custom
|
||||
// report name.
|
||||
bool Cmd::valid (const std::string& input)
|
||||
{
|
||||
loadCommands ();
|
||||
loadCustomReports ();
|
||||
|
||||
std::string candidate = lowerCase (input);
|
||||
|
||||
std::vector <std::string> matches;
|
||||
autoComplete (candidate, commands, matches);
|
||||
if (0 == matches.size ())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Determines whether the string represents a valid custom report name.
|
||||
bool Cmd::validCustom (const std::string& input)
|
||||
{
|
||||
loadCustomReports ();
|
||||
|
||||
std::vector <std::string> matches;
|
||||
autoComplete (lowerCase (input), customReports, matches);
|
||||
return matches.size () == 1 ? true : false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void Cmd::parse (const std::string& input)
|
||||
{
|
||||
loadCommands ();
|
||||
loadCustomReports ();
|
||||
|
||||
std::string candidate = lowerCase (input);
|
||||
|
||||
std::vector <std::string> matches;
|
||||
autoComplete (candidate, commands, matches);
|
||||
if (1 == matches.size ())
|
||||
command = matches[0];
|
||||
|
||||
else if (0 == matches.size ())
|
||||
command = "";
|
||||
|
||||
else
|
||||
{
|
||||
std::string error = "Ambiguous command '" + candidate + "' - could be either of "; // TODO i18n
|
||||
|
||||
std::string combined;
|
||||
join (combined, ", ", matches);
|
||||
error += combined;
|
||||
|
||||
throw error + combined;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void Cmd::loadCommands ()
|
||||
{
|
||||
if (commands.size () == 0)
|
||||
{
|
||||
commands.push_back (context.stringtable.get (CMD_ACTIVE, "active"));
|
||||
commands.push_back (context.stringtable.get (CMD_ADD, "add"));
|
||||
commands.push_back (context.stringtable.get (CMD_APPEND, "append"));
|
||||
commands.push_back (context.stringtable.get (CMD_ANNOTATE, "annotate"));
|
||||
commands.push_back (context.stringtable.get (CMD_CALENDAR, "calendar"));
|
||||
commands.push_back (context.stringtable.get (CMD_COLORS, "colors"));
|
||||
commands.push_back (context.stringtable.get (CMD_COMPLETED, "completed"));
|
||||
commands.push_back (context.stringtable.get (CMD_DELETE, "delete"));
|
||||
commands.push_back (context.stringtable.get (CMD_DONE, "done"));
|
||||
commands.push_back (context.stringtable.get (CMD_DUPLICATE, "duplicate"));
|
||||
commands.push_back (context.stringtable.get (CMD_EDIT, "edit"));
|
||||
commands.push_back (context.stringtable.get (CMD_EXPORT, "export"));
|
||||
commands.push_back (context.stringtable.get (CMD_HELP, "help"));
|
||||
commands.push_back (context.stringtable.get (CMD_HISTORY, "history"));
|
||||
commands.push_back (context.stringtable.get (CMD_GHISTORY, "ghistory"));
|
||||
commands.push_back (context.stringtable.get (CMD_IMPORT, "import"));
|
||||
commands.push_back (context.stringtable.get (CMD_INFO, "info"));
|
||||
commands.push_back (context.stringtable.get (CMD_NEXT, "next"));
|
||||
commands.push_back (context.stringtable.get (CMD_OVERDUE, "overdue"));
|
||||
commands.push_back (context.stringtable.get (CMD_PROJECTS, "projects"));
|
||||
commands.push_back (context.stringtable.get (CMD_START, "start"));
|
||||
commands.push_back (context.stringtable.get (CMD_STATS, "statistics"));
|
||||
commands.push_back (context.stringtable.get (CMD_STOP, "stop"));
|
||||
commands.push_back (context.stringtable.get (CMD_SUMMARY, "summary"));
|
||||
commands.push_back (context.stringtable.get (CMD_TAGS, "tags"));
|
||||
commands.push_back (context.stringtable.get (CMD_TIMESHEET, "timesheet"));
|
||||
commands.push_back (context.stringtable.get (CMD_UNDELETE, "undelete"));
|
||||
commands.push_back (context.stringtable.get (CMD_UNDO, "undo"));
|
||||
commands.push_back (context.stringtable.get (CMD_VERSION, "version"));
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void Cmd::loadCustomReports ()
|
||||
{
|
||||
if (customReports.size () == 0)
|
||||
{
|
||||
std::vector <std::string> all;
|
||||
context.config.all (all);
|
||||
|
||||
foreach (i, all)
|
||||
{
|
||||
if (i->substr (0, 7) == "report.")
|
||||
{
|
||||
std::string report = i->substr (7, std::string::npos);
|
||||
std::string::size_type columns = report.find (".columns");
|
||||
if (columns != std::string::npos)
|
||||
{
|
||||
report = report.substr (0, columns);
|
||||
|
||||
// A custom report is also a command.
|
||||
customReports.push_back (report);
|
||||
commands.push_back (report);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
58
src/Cmd.h
Normal file
58
src/Cmd.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef INCLUDED_CMD
|
||||
#define INCLUDED_CMD
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "Cmd.h"
|
||||
|
||||
class Cmd
|
||||
{
|
||||
public:
|
||||
Cmd (); // Default constructor
|
||||
Cmd (const std::string&); // Default constructor
|
||||
~Cmd (); // Destructor
|
||||
|
||||
bool valid (const std::string&);
|
||||
bool validCustom (const std::string&);
|
||||
void parse (const std::string&);
|
||||
|
||||
public:
|
||||
std::string command;
|
||||
|
||||
private:
|
||||
void loadCommands ();
|
||||
void loadCustomReports ();
|
||||
|
||||
private:
|
||||
std::vector <std::string> commands;
|
||||
std::vector <std::string> customReports;
|
||||
};
|
||||
|
||||
#endif
|
||||
////////////////////////////////////////////////////////////////////////////////
|
|
@ -275,7 +275,8 @@ void Context::parse ()
|
|||
else if (arg[0] == '-')
|
||||
task.addRemoveTag (arg->substr (1, std::string::npos));
|
||||
}
|
||||
|
||||
*/
|
||||
/*
|
||||
// Attributes contain a constant string followed by a colon, followed by a
|
||||
// value.
|
||||
else if ((colon = arg->find (":")) != std::string::npos)
|
||||
|
@ -312,7 +313,6 @@ void Context::parse ()
|
|||
std::cout << "# found subst" << std::endl;
|
||||
subst.parse (*arg);
|
||||
}
|
||||
|
||||
/*
|
||||
// Command.
|
||||
else if (command == "")
|
||||
|
@ -331,7 +331,6 @@ void Context::parse ()
|
|||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// Anything else is just considered description.
|
||||
else
|
||||
{
|
||||
|
|
|
@ -2,10 +2,10 @@ bin_PROGRAMS = task
|
|||
task_SOURCES = Config.cpp Date.cpp Record.cpp T.cpp T2.cpp TDB.cpp TDB2.cpp \
|
||||
Att.cpp Filter.cpp Sequence.cpp Table.cpp Grid.cpp Timer.cpp \
|
||||
Duration.cpp StringTable.cpp Location.cpp Subst.cpp Keymap.cpp \
|
||||
Nibbler.cpp Context.cpp color.cpp parse.cpp task.cpp edit.cpp \
|
||||
command.cpp report.cpp util.cpp text.cpp rules.cpp import.cpp \
|
||||
interactive.cpp \
|
||||
Nibbler.cpp Context.cpp Cmd.cpp color.cpp parse.cpp task.cpp \
|
||||
edit.cpp command.cpp report.cpp util.cpp text.cpp rules.cpp \
|
||||
import.cpp interactive.cpp \
|
||||
Config.h Date.h Record.h T.h TDB.h Att.h Filter.h Sequence.h \
|
||||
Table.h Grid.h Timer.h Duration.h StringTable.h Location.h \
|
||||
Subst.h Keymap.h Nibbler.h Context.h color.h task.h
|
||||
Subst.h Keymap.h Nibbler.h Context.h Cmd.h color.h task.h
|
||||
|
||||
|
|
|
@ -95,8 +95,9 @@ void Record::parse (const std::string& input)
|
|||
|
||||
Nibbler nl (line);
|
||||
Att a;
|
||||
while (!nl.depleted () && a.parse (nl))
|
||||
while (!nl.depleted ())
|
||||
{
|
||||
a.parse (nl);
|
||||
(*this)[a.name ()] = a;
|
||||
nl.skip (' ');
|
||||
}
|
||||
|
|
11
src/i18n.h
11
src/i18n.h
|
@ -25,6 +25,17 @@
|
|||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Strings that should be localized:
|
||||
// - All text output that the user sees or types
|
||||
//
|
||||
// Strings that should NOT be localized:
|
||||
// - ./taskrc configuration variable names
|
||||
// - certain literals associated with parsing
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef INCLUDED_I18N
|
||||
#define INCLUDED_I18N
|
||||
|
||||
|
|
|
@ -158,38 +158,6 @@ static const char* commands[] =
|
|||
|
||||
static std::vector <std::string> customReports;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void guess (
|
||||
const std::string& type,
|
||||
std::vector<std::string>& options,
|
||||
std::string& candidate)
|
||||
{
|
||||
std::vector <std::string> matches;
|
||||
autoComplete (candidate, options, matches);
|
||||
if (1 == matches.size ())
|
||||
candidate = matches[0];
|
||||
|
||||
else if (0 == matches.size ())
|
||||
candidate = "";
|
||||
|
||||
else
|
||||
{
|
||||
std::string error = "Ambiguous ";
|
||||
error += type;
|
||||
error += " '";
|
||||
error += candidate;
|
||||
error += "' - could be either of ";
|
||||
for (size_t i = 0; i < matches.size (); ++i)
|
||||
{
|
||||
if (i)
|
||||
error += ", ";
|
||||
error += matches[i];
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void guess (
|
||||
const std::string& type,
|
||||
|
@ -375,7 +343,7 @@ bool validDescription (const std::string& input)
|
|||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
static bool validCommand (std::string& input)
|
||||
bool validCommand (std::string& input)
|
||||
{
|
||||
std::string copy = input;
|
||||
guess ("command", commands, copy);
|
||||
|
|
|
@ -43,6 +43,7 @@ bool validPriority (const std::string&);
|
|||
bool validDate (std::string&);
|
||||
bool validDuration (std::string&);
|
||||
bool validDescription (const std::string&);
|
||||
bool validCommand (std::string&);
|
||||
void loadCustomReports ();
|
||||
bool isCustomReport (const std::string&);
|
||||
void allCustomReports (std::vector <std::string>&);
|
||||
|
|
1
src/tests/.gitignore
vendored
1
src/tests/.gitignore
vendored
|
@ -14,3 +14,4 @@ stringtable.t
|
|||
nibbler.t
|
||||
subst.t
|
||||
filt.t
|
||||
cmd.t
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
PROJECT = t.t t2.t tdb.t date.t duration.t t.benchmark.t text.t autocomplete.t \
|
||||
parse.t seq.t att.t stringtable.t record.t nibbler.t subst.t filt.t
|
||||
parse.t seq.t att.t stringtable.t record.t nibbler.t subst.t filt.t \
|
||||
cmd.t
|
||||
CFLAGS = -I. -I.. -Wall -pedantic -ggdb3 -fno-rtti
|
||||
LFLAGS = -L/usr/local/lib
|
||||
OBJECTS = ../TDB.o ../TDB2.o ../T.o ../T2.o ../parse.o ../text.o ../Date.o \
|
||||
../Duration.o ../util.o ../Config.o ../Sequence.o ../Att.o \
|
||||
../Record.o ../StringTable.o ../Subst.o ../Nibbler.o ../Location.o \
|
||||
../Filter.o ../Context.o ../Keymap.o
|
||||
../Filter.o ../Context.o ../Keymap.o ../Cmd.o
|
||||
|
||||
all: $(PROJECT)
|
||||
|
||||
|
@ -69,3 +70,6 @@ nibbler.t: nibbler.t.o $(OBJECTS) test.o
|
|||
filt.t: filt.t.o $(OBJECTS) test.o
|
||||
g++ filt.t.o $(OBJECTS) test.o $(LFLAGS) -o filt.t
|
||||
|
||||
cmd.t: cmd.t.o $(OBJECTS) test.o
|
||||
g++ cmd.t.o $(OBJECTS) test.o $(LFLAGS) -o cmd.t
|
||||
|
||||
|
|
|
@ -33,7 +33,25 @@ Context context;
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
int main (int argc, char** argv)
|
||||
{
|
||||
UnitTest t (59);
|
||||
UnitTest t (74);
|
||||
|
||||
Att a;
|
||||
t.notok (a.valid ("name"), "Att::valid name -> fail");
|
||||
t.notok (a.valid (":"), "Att::valid : -> fail");
|
||||
t.notok (a.valid (":value"), "Att::valid :value -> fail");
|
||||
|
||||
t.ok (a.valid ("name:value"), "Att::valid name:value");
|
||||
t.ok (a.valid ("name:value "), "Att::valid name:value\\s");
|
||||
t.ok (a.valid ("name:'value'"), "Att::valid name:'value'");
|
||||
t.ok (a.valid ("name:'one two'"), "Att::valid name:'one two'");
|
||||
t.ok (a.valid ("name:\"value\""), "Att::valid name:\"value\"");
|
||||
t.ok (a.valid ("name:\"one two\""), "Att::valid name:\"one two\"");
|
||||
t.ok (a.valid ("name:"), "Att::valid name:");
|
||||
t.ok (a.valid ("name:""), "Att::valid "");
|
||||
t.ok (a.valid ("name.one:value"), "Att::valid name.one.value");
|
||||
t.ok (a.valid ("name.one.two:value"), "Att::valid name.one.two:value");
|
||||
t.ok (a.valid ("name.:value"), "Att::valid name.:value");
|
||||
t.ok (a.valid ("name..:value"), "Att::valid name..:value");
|
||||
|
||||
Att a1 ("name", "value");
|
||||
t.is (a1.name (), "name", "Att::Att (name, value), Att.name");
|
||||
|
|
71
src/tests/cmd.t.cpp
Normal file
71
src/tests/cmd.t.cpp
Normal file
|
@ -0,0 +1,71 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
#include <Context.h>
|
||||
#include <Cmd.h>
|
||||
#include <test.h>
|
||||
|
||||
Context context;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
int main (int argc, char** argv)
|
||||
{
|
||||
UnitTest t (18);
|
||||
|
||||
context.config.set ("report.foo.columns", "id");
|
||||
|
||||
Cmd cmd;
|
||||
t.ok (cmd.valid ("annotate"), "Cmd::valid annotate");
|
||||
t.ok (cmd.valid ("annotat"), "Cmd::valid annotat");
|
||||
t.ok (cmd.valid ("annota"), "Cmd::valid annota");
|
||||
t.ok (cmd.valid ("annot"), "Cmd::valid annot");
|
||||
t.ok (cmd.valid ("anno"), "Cmd::valid anno");
|
||||
t.ok (cmd.valid ("ann"), "Cmd::valid ann");
|
||||
t.ok (cmd.valid ("an"), "Cmd::valid an");
|
||||
|
||||
t.ok (cmd.valid ("ANNOTATE"), "Cmd::valid ANNOTATE");
|
||||
t.ok (cmd.valid ("ANNOTAT"), "Cmd::valid ANNOTAT");
|
||||
t.ok (cmd.valid ("ANNOTA"), "Cmd::valid ANNOTA");
|
||||
t.ok (cmd.valid ("ANNOT"), "Cmd::valid ANNOT");
|
||||
t.ok (cmd.valid ("ANNO"), "Cmd::valid ANNO");
|
||||
t.ok (cmd.valid ("ANN"), "Cmd::valid ANN");
|
||||
t.ok (cmd.valid ("AN"), "Cmd::valid AN");
|
||||
|
||||
t.ok (cmd.validCustom ("foo"), "Cmd::validCustom foo");
|
||||
t.notok (cmd.validCustom ("bar"), "Cmd::validCustom bar -> fail");
|
||||
|
||||
bool good = true;
|
||||
try { cmd.parse ("a"); } catch (...) { good = false; }
|
||||
t.notok (good, "Cmd::parse a -> fail");
|
||||
|
||||
good = true;
|
||||
try { cmd.parse ("add"); } catch (...) { good = false; }
|
||||
t.ok (good, "Cmd::parse add");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
32
src/text.cpp
32
src/text.cpp
|
@ -305,3 +305,35 @@ const char* optionalBlankLine ()
|
|||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void guess (
|
||||
const std::string& type,
|
||||
std::vector<std::string>& options,
|
||||
std::string& candidate)
|
||||
{
|
||||
std::vector <std::string> matches;
|
||||
autoComplete (candidate, options, matches);
|
||||
if (1 == matches.size ())
|
||||
candidate = matches[0];
|
||||
|
||||
else if (0 == matches.size ())
|
||||
candidate = "";
|
||||
|
||||
else
|
||||
{
|
||||
std::string error = "Ambiguous "; // TODO i18n
|
||||
error += type;
|
||||
error += " '";
|
||||
error += candidate;
|
||||
error += "' - could be either of "; // TODO i18n
|
||||
for (size_t i = 0; i < matches.size (); ++i)
|
||||
{
|
||||
if (i)
|
||||
error += ", ";
|
||||
error += matches[i];
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -45,6 +45,7 @@ std::string commify (const std::string&);
|
|||
std::string lowerCase (const std::string&);
|
||||
std::string upperCase (const std::string&);
|
||||
const char* optionalBlankLine ();
|
||||
void guess (const std::string&, std::vector<std::string>&, std::string&);
|
||||
|
||||
#endif
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue