mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-08-28 22:47:20 +02:00
Merge branch '2.4.5' of ssh://git.tasktools.org/tm/task into 2.4.5
This commit is contained in:
commit
5d02291077
36 changed files with 324 additions and 750 deletions
155
src/CLI2.cpp
155
src/CLI2.cpp
|
@ -340,15 +340,42 @@ void CLI2::lexArguments ()
|
|||
{
|
||||
// Note: Starts interating at index 1, because ::handleArg0 has already
|
||||
// processed it.
|
||||
bool terminated = false;
|
||||
for (unsigned int i = 1; i < _original_args.size (); ++i)
|
||||
{
|
||||
std::string lexeme;
|
||||
Lexer::Type type;
|
||||
Lexer lex (_original_args[i]);
|
||||
lex.ambiguity (false);
|
||||
// The terminator itself is captured.
|
||||
if (_original_args[i] == "--")
|
||||
{
|
||||
terminated = true;
|
||||
_args.push_back (A2 (_original_args[i], Lexer::Type::separator));
|
||||
}
|
||||
|
||||
while (lex.token (lexeme, type))
|
||||
_args.push_back (A2 (lexeme, type));
|
||||
// Any arguments that are after the terminator are captured as words.
|
||||
else if (terminated)
|
||||
{
|
||||
A2 word (_original_args[i], Lexer::Type::word);
|
||||
word.tag ("TERMINATED");
|
||||
_args.push_back (word);
|
||||
}
|
||||
|
||||
// rc:<file> and rc.<name>[:=]<value> argumenst are captured whole.
|
||||
else if (_original_args[i].substr (0, 3) == "rc:" ||
|
||||
_original_args[i].substr (0, 3) == "rc.")
|
||||
{
|
||||
_args.push_back (A2 (_original_args[i], Lexer::Type::pair));
|
||||
}
|
||||
|
||||
// Everything else gets lexed.
|
||||
else
|
||||
{
|
||||
std::string lexeme;
|
||||
Lexer::Type type;
|
||||
Lexer lex (_original_args[i]);
|
||||
lex.ambiguity (false);
|
||||
|
||||
while (lex.token (lexeme, type))
|
||||
_args.push_back (A2 (lexeme, type));
|
||||
}
|
||||
}
|
||||
|
||||
if (context.config.getInteger ("debug.parser") >= 3)
|
||||
|
@ -414,51 +441,6 @@ void CLI2::analyze ()
|
|||
context.debug (dump ("CLI2::analyze end"));
|
||||
}
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// There are situations where a context filter is applied. This method
|
||||
// determines whether one applies, and if so, applies it. Disqualifiers include:
|
||||
// - filter contains ID or UUID
|
||||
void CLI2::addContextFilter ()
|
||||
{
|
||||
// Detect if any context is set, and bail out if not
|
||||
std::string contextName = context.config.get ("context");
|
||||
if (contextName == "")
|
||||
{
|
||||
context.debug ("No context applied.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Detect if UUID or ID is set, and bail out
|
||||
for (auto& a : _args)
|
||||
{
|
||||
// TODO This looks wrong.
|
||||
if (a.hasTag ("FILTER") &&
|
||||
a.hasTag ("ATTRIBUTE") &&
|
||||
! a.hasTag ("TERMINATED") &&
|
||||
! a.hasTag ("WORD") &&
|
||||
(a.attribute ("raw") == "id" || a.attribute ("raw") == "uuid"))
|
||||
{
|
||||
context.debug (format ("UUID/ID lexeme found '{1}', not applying context.", a.attribute ("raw")));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply context
|
||||
context.debug ("Applying context: " + contextName);
|
||||
std::string contextFilter = context.config.get ("context." + contextName);
|
||||
|
||||
if (contextFilter == "")
|
||||
context.debug ("Context '" + contextName + "' not defined.");
|
||||
else
|
||||
{
|
||||
addRawFilter ("( " + contextFilter + " )");
|
||||
if (context.verbose ("context"))
|
||||
context.footnote (format ("Context '{1}' set. Use 'task context none' to remove.", contextName));
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Process raw string.
|
||||
void CLI2::addFilter (const std::string& arg)
|
||||
|
@ -480,6 +462,52 @@ void CLI2::addFilter (const std::string& arg)
|
|||
analyze ();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// There are situations where a context filter is applied. This method
|
||||
// determines whether one applies, and if so, applies it. Disqualifiers include:
|
||||
// - filter contains ID or UUID
|
||||
void CLI2::addContextFilter ()
|
||||
{
|
||||
// Detect if any context is set, and bail out if not
|
||||
std::string contextName = context.config.get ("context");
|
||||
if (contextName == "")
|
||||
{
|
||||
context.debug ("No context applied.");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
// Detect if UUID or ID is set, and bail out
|
||||
for (auto& a : _args)
|
||||
{
|
||||
// TODO This is needed, but the parsing is not yet complete, so the logic
|
||||
// below is not valid.
|
||||
if (a.hasTag ("FILTER") &&
|
||||
a.hasTag ("ATTRIBUTE") &&
|
||||
! a.hasTag ("TERMINATED") &&
|
||||
! a.hasTag ("WORD") &&
|
||||
(a.attribute ("raw") == "id" || a.attribute ("raw") == "uuid"))
|
||||
{
|
||||
context.debug (format ("UUID/ID lexeme found '{1}', not applying context.", a.attribute ("raw")));
|
||||
return;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// Apply context
|
||||
context.debug ("Applying context: " + contextName);
|
||||
std::string contextFilter = context.config.get ("context." + contextName);
|
||||
|
||||
if (contextFilter == "")
|
||||
context.debug ("Context '" + contextName + "' not defined.");
|
||||
else
|
||||
{
|
||||
addFilter (contextFilter);
|
||||
if (context.verbose ("context"))
|
||||
context.footnote (format ("Context '{1}' set. Use 'task context none' to remove.", contextName));
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Parse the command line, identifiying filter components, expanding syntactic
|
||||
// sugar as necessary.
|
||||
|
@ -489,6 +517,9 @@ void CLI2::prepareFilter (bool applyContext)
|
|||
_id_ranges.clear ();
|
||||
_uuid_list.clear ();
|
||||
|
||||
if (applyContext)
|
||||
addContextFilter ();
|
||||
|
||||
// Classify FILTER and MODIFICATION args, based on CMD and READCMD/WRITECMD.
|
||||
bool changes = false;
|
||||
bool foundCommand = false;
|
||||
|
@ -711,13 +742,15 @@ void CLI2::aliasExpansion ()
|
|||
for (auto& i : _args)
|
||||
{
|
||||
raw = i.attribute ("raw");
|
||||
if (_aliases.find (raw) != _aliases.end ())
|
||||
if (i.hasTag ("TERMINATED"))
|
||||
{
|
||||
reconstructed.push_back (i);
|
||||
}
|
||||
else if (_aliases.find (raw) != _aliases.end ())
|
||||
{
|
||||
for (auto& l : Lexer::split (_aliases[raw]))
|
||||
{
|
||||
A2 a (l, Lexer::Type::word);
|
||||
a.tag ("ALIAS");
|
||||
a.tag ("LEX");
|
||||
reconstructed.push_back (a);
|
||||
}
|
||||
|
||||
|
@ -725,15 +758,25 @@ void CLI2::aliasExpansion ()
|
|||
changes = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
reconstructed.push_back (i);
|
||||
}
|
||||
}
|
||||
|
||||
_args = reconstructed;
|
||||
|
||||
std::vector <std::string> reconstructedOriginals;
|
||||
bool terminated = false;
|
||||
for (auto& i : _original_args)
|
||||
{
|
||||
if (_aliases.find (i) != _aliases.end ())
|
||||
if (i == "--")
|
||||
terminated = true;
|
||||
|
||||
if (terminated)
|
||||
{
|
||||
reconstructedOriginals.push_back (i);
|
||||
}
|
||||
else if (_aliases.find (i) != _aliases.end ())
|
||||
{
|
||||
for (auto& l : Lexer::split (_aliases[i]))
|
||||
reconstructedOriginals.push_back (l);
|
||||
|
@ -742,7 +785,9 @@ void CLI2::aliasExpansion ()
|
|||
changes = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
reconstructedOriginals.push_back (i);
|
||||
}
|
||||
}
|
||||
|
||||
_original_args = reconstructedOriginals;
|
||||
|
|
|
@ -73,10 +73,8 @@ public:
|
|||
|
||||
void add (const std::string&);
|
||||
void analyze ();
|
||||
/*
|
||||
void addContextFilter ();
|
||||
*/
|
||||
void addFilter (const std::string& arg);
|
||||
void addContextFilter ();
|
||||
void prepareFilter (bool applyContext = true);
|
||||
const std::vector <std::string> getWords (bool filtered = true);
|
||||
bool canonicalize (std::string&, const std::string&, const std::string&) const;
|
||||
|
|
|
@ -73,7 +73,8 @@ void Filter::subset (const std::vector <Task>& input, std::vector <Task>& output
|
|||
context.timer_filter.start ();
|
||||
_startCount = (int) input.size ();
|
||||
|
||||
// context.cli2.prepareFilter (applyContext);
|
||||
context.cli2.prepareFilter (applyContext);
|
||||
|
||||
std::vector <std::pair <std::string, Lexer::Type>> precompiled;
|
||||
for (auto& a : context.cli2._args)
|
||||
if (a.hasTag ("FILTER"))
|
||||
|
@ -118,6 +119,8 @@ void Filter::subset (std::vector <Task>& output, bool applyContext /* = true */)
|
|||
{
|
||||
context.timer_filter.start ();
|
||||
|
||||
context.cli2.prepareFilter (applyContext);
|
||||
|
||||
std::vector <std::pair <std::string, Lexer::Type>> precompiled;
|
||||
for (auto& a : context.cli2._args)
|
||||
if (a.hasTag ("FILTER"))
|
||||
|
@ -179,7 +182,7 @@ void Filter::subset (std::vector <Task>& output, bool applyContext /* = true */)
|
|||
}
|
||||
else
|
||||
{
|
||||
safety (precompiled.size ());
|
||||
safety ();
|
||||
context.timer_filter.stop ();
|
||||
|
||||
for (auto& task : context.tdb2.pending.get_tasks ())
|
||||
|
@ -258,7 +261,7 @@ bool Filter::pendingOnly ()
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Disaster avoidance mechanism. If a WRITECMD has no filter, then it can cause
|
||||
// all tasks to be modified. This is usually not intended.
|
||||
void Filter::safety (unsigned int terms)
|
||||
void Filter::safety ()
|
||||
{
|
||||
for (auto& a : context.cli2._args)
|
||||
{
|
||||
|
@ -266,19 +269,16 @@ void Filter::safety (unsigned int terms)
|
|||
{
|
||||
if (a.hasTag ("WRITECMD"))
|
||||
{
|
||||
if (terms)
|
||||
{
|
||||
if (! context.config.getBoolean ("allow.empty.filter"))
|
||||
throw std::string (STRING_TASK_SAFETY_ALLOW);
|
||||
if (! context.config.getBoolean ("allow.empty.filter"))
|
||||
throw std::string (STRING_TASK_SAFETY_ALLOW);
|
||||
|
||||
// If user is willing to be asked, this can be avoided.
|
||||
if (context.config.getBoolean ("confirmation") &&
|
||||
confirm (STRING_TASK_SAFETY_VALVE))
|
||||
return;
|
||||
// If user is willing to be asked, this can be avoided.
|
||||
if (context.config.getBoolean ("confirmation") &&
|
||||
confirm (STRING_TASK_SAFETY_VALVE))
|
||||
return;
|
||||
|
||||
// Sounds the alarm.
|
||||
throw std::string (STRING_TASK_SAFETY_FAIL);
|
||||
}
|
||||
// Sounds the alarm.
|
||||
throw std::string (STRING_TASK_SAFETY_FAIL);
|
||||
}
|
||||
|
||||
// CMD was found.
|
||||
|
|
|
@ -43,7 +43,7 @@ public:
|
|||
void subset (const std::vector <Task>&, std::vector <Task>&, bool applyContext = true);
|
||||
void subset (std::vector <Task>&, bool applyContext = true);
|
||||
bool pendingOnly ();
|
||||
void safety (unsigned int);
|
||||
void safety ();
|
||||
|
||||
private:
|
||||
int _startCount;
|
||||
|
|
245
src/ISO8601.cpp
245
src/ISO8601.cpp
|
@ -61,16 +61,9 @@ void ISO8601d::ambiguity (bool value)
|
|||
// | date-ext 'T' time-ext offset-ext # Specified TZ
|
||||
// | date-ext 'T' time-ext # Local
|
||||
// | date-ext # Local
|
||||
// | date 'T' time 'Z'
|
||||
// | date 'T' time offset-ext
|
||||
// | date 'T' time
|
||||
// | date
|
||||
// | time-ext 'Z'
|
||||
// | time-ext offset-ext Not needed
|
||||
// | time-ext
|
||||
// | time 'Z'
|
||||
// | time offset
|
||||
// | time
|
||||
// ;
|
||||
//
|
||||
// date-ext ::= ±YYYYY-MM-DD Νot needed
|
||||
|
@ -83,37 +76,14 @@ void ISO8601d::ambiguity (bool value)
|
|||
// | YYYY-Www
|
||||
// ;
|
||||
//
|
||||
// date ::= ±YYYYYMMDD Νot needed
|
||||
// | ±YYYYYWwwD Νot needed
|
||||
// | ±YYYYYWww Νot needed
|
||||
// | ±YYYYYDDD Νot needed
|
||||
// | ±YYYYYMM Νot needed
|
||||
// | ±YYYYY Νot needed
|
||||
// | ±YYY Νot needed
|
||||
// | YYYYMMDD Ambiguous (number)
|
||||
// | YYYYWwwD
|
||||
// | YYYYWww
|
||||
// | YYYYDDD Ambiguous (number)
|
||||
// | YYYY-MM
|
||||
// | YYYY Ambiguous (number)
|
||||
// | YY Ambiguous (number)
|
||||
// ;
|
||||
//
|
||||
// time-ext ::= hh:mm:ss[,ss]
|
||||
// | hh:mm[,mm]
|
||||
// | hh[,hh] Ambiguous (number)
|
||||
// ;
|
||||
//
|
||||
// time ::= hhmmss[,ss] Ambiguous (number)
|
||||
// | hhmm[,mm] Ambiguous (number)
|
||||
// | hh[,hh] Ambiguous (number)
|
||||
// ;
|
||||
//
|
||||
// time-utc-ext ::= hh:mm[:ss] 'Z' ;
|
||||
// time-utc ::= hh[mm[ss]] 'Z' ;
|
||||
//
|
||||
// offset-ext ::= ±hh[:mm] ;
|
||||
// offset ::= ±hh[mm] ;
|
||||
//
|
||||
// Not yet supported:
|
||||
//
|
||||
|
@ -136,12 +106,7 @@ bool ISO8601d::parse (const std::string& input, std::string::size_type& start)
|
|||
parse_date_ext (n) ||
|
||||
parse_time_utc_ext (n) ||
|
||||
parse_time_off_ext (n) ||
|
||||
parse_date_time (n) ||
|
||||
parse_date (n, _ambiguity) ||
|
||||
parse_time_utc (n) ||
|
||||
parse_time_off (n) ||
|
||||
parse_time_ext (n) || // Time last, as it is the most permissive.
|
||||
parse_time (n, _ambiguity))
|
||||
parse_time_ext (n)) // Time last, as it is the most permissive.
|
||||
{
|
||||
// Check the values and determine time_t.
|
||||
if (validate ())
|
||||
|
@ -214,47 +179,6 @@ bool ISO8601d::parse_date_time_ext (Nibbler& n)
|
|||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// date 'T' time 'Z'
|
||||
// date 'T' time offset
|
||||
// date 'T' time
|
||||
bool ISO8601d::parse_date_time (Nibbler& n)
|
||||
{
|
||||
Nibbler backup (n);
|
||||
if (parse_date (n, true))
|
||||
{
|
||||
if (n.skip ('T') &&
|
||||
parse_time (n, true))
|
||||
{
|
||||
if (n.skip ('Z'))
|
||||
{
|
||||
_utc = true;
|
||||
if (!Lexer::isDigit (n.next ()))
|
||||
return true;
|
||||
}
|
||||
else if (parse_off (n))
|
||||
{
|
||||
if (!Lexer::isDigit (n.next ()))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!Lexer::isDigit (n.next ()))
|
||||
return true;
|
||||
}
|
||||
|
||||
// Restore date
|
||||
_year = 0;
|
||||
_month = 0;
|
||||
_week = 0;
|
||||
_weekday = 0;
|
||||
_julian = 0;
|
||||
_day = 0;
|
||||
}
|
||||
|
||||
n = backup;
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// YYYY-MM-DD
|
||||
// YYYY-DDD
|
||||
|
@ -303,64 +227,6 @@ bool ISO8601d::parse_date_ext (Nibbler& n)
|
|||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// YYYYMMDD Ambiguous (number)
|
||||
// YYYYWwwD
|
||||
// YYYYWww
|
||||
// YYYYDDD Ambiguous (number)
|
||||
// YYYY-MM
|
||||
bool ISO8601d::parse_date (Nibbler& n, bool ambiguous)
|
||||
{
|
||||
Nibbler backup (n);
|
||||
int year;
|
||||
if (n.getDigit4 (year))
|
||||
{
|
||||
int month;
|
||||
if (n.skip ('W'))
|
||||
{
|
||||
int week;
|
||||
if (n.getDigit2 (week))
|
||||
{
|
||||
_week = week;
|
||||
|
||||
int day;
|
||||
if (n.getDigit (day))
|
||||
_weekday = day;
|
||||
|
||||
_year = year;
|
||||
if (!Lexer::isDigit (n.next ()))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (n.skip ('-'))
|
||||
{
|
||||
if (n.getDigit2 (_month))
|
||||
{
|
||||
_year = year;
|
||||
if (!Lexer::isDigit (n.next ()))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (n.getDigit4 (month))
|
||||
{
|
||||
_year = year;
|
||||
_month = month / 100;
|
||||
_day = month % 100;
|
||||
if (!Lexer::isDigit (n.next ()))
|
||||
return true;
|
||||
}
|
||||
else if (ambiguous && n.getDigit3 (_julian))
|
||||
{
|
||||
_year = year;
|
||||
if (!Lexer::isDigit (n.next ()))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
n = backup;
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// ±hh[:mm]
|
||||
bool ISO8601d::parse_off_ext (Nibbler& n)
|
||||
|
@ -394,37 +260,7 @@ bool ISO8601d::parse_off_ext (Nibbler& n)
|
|||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// ±hh[mm]
|
||||
bool ISO8601d::parse_off (Nibbler& n)
|
||||
{
|
||||
Nibbler backup (n);
|
||||
std::string sign;
|
||||
if (n.getN (1, sign))
|
||||
{
|
||||
if (sign == "+" || sign == "-")
|
||||
{
|
||||
int offset;
|
||||
int hh;
|
||||
if (n.getDigit2 (hh))
|
||||
{
|
||||
offset = hh * 3600;
|
||||
int mm;
|
||||
if (n.getDigit2 (mm))
|
||||
offset += mm * 60;
|
||||
|
||||
_offset = (sign == "-") ? -offset : offset;
|
||||
if (!Lexer::isDigit (n.next ()))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
n = backup;
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// hh[:mm[:ss]]
|
||||
// hh:mm[:ss]
|
||||
bool ISO8601d::parse_time_ext (Nibbler& n)
|
||||
{
|
||||
Nibbler backup (n);
|
||||
|
@ -433,20 +269,15 @@ bool ISO8601d::parse_time_ext (Nibbler& n)
|
|||
int mm;
|
||||
int ss;
|
||||
if (n.getDigit2 (hh) &&
|
||||
!n.getDigit (mm))
|
||||
n.skip (':') &&
|
||||
n.getDigit2 (mm))
|
||||
{
|
||||
seconds = hh * 3600;
|
||||
seconds = (hh * 3600) + (mm * 60);
|
||||
|
||||
if (n.skip (':') &&
|
||||
n.getDigit2 (mm) &&
|
||||
!n.getDigit (ss))
|
||||
n.getDigit2 (ss))
|
||||
{
|
||||
seconds += mm * 60;
|
||||
|
||||
if (n.skip (':') &&
|
||||
n.getDigit2 (ss))
|
||||
seconds += ss;
|
||||
|
||||
seconds += ss;
|
||||
_seconds = seconds;
|
||||
return true;
|
||||
}
|
||||
|
@ -463,35 +294,6 @@ bool ISO8601d::parse_time_ext (Nibbler& n)
|
|||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// hhmm[ss]
|
||||
bool ISO8601d::parse_time (Nibbler& n, bool ambiguous)
|
||||
{
|
||||
if (!ambiguous)
|
||||
return false;
|
||||
|
||||
Nibbler backup (n);
|
||||
int seconds = 0;
|
||||
int hh;
|
||||
int mm;
|
||||
if (n.getDigit2 (hh) &&
|
||||
n.getDigit2 (mm))
|
||||
{
|
||||
seconds = hh * 3600 + mm * 60;
|
||||
|
||||
int ss;
|
||||
if (n.getDigit2 (ss))
|
||||
seconds += ss;
|
||||
|
||||
_seconds = seconds;
|
||||
if (!Lexer::isDigit (n.next ()))
|
||||
return true;
|
||||
}
|
||||
|
||||
n = backup;
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// time-ext 'Z'
|
||||
bool ISO8601d::parse_time_utc_ext (Nibbler& n)
|
||||
|
@ -509,23 +311,6 @@ bool ISO8601d::parse_time_utc_ext (Nibbler& n)
|
|||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// time 'Z'
|
||||
bool ISO8601d::parse_time_utc (Nibbler& n)
|
||||
{
|
||||
n.save ();
|
||||
if (parse_time (n, true) &&
|
||||
n.skip ('Z'))
|
||||
{
|
||||
_utc = true;
|
||||
if (!Lexer::isDigit (n.next ()))
|
||||
return true;
|
||||
}
|
||||
|
||||
n.restore ();
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// time-ext offset-ext
|
||||
bool ISO8601d::parse_time_off_ext (Nibbler& n)
|
||||
|
@ -542,22 +327,6 @@ bool ISO8601d::parse_time_off_ext (Nibbler& n)
|
|||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// time offset
|
||||
bool ISO8601d::parse_time_off (Nibbler& n)
|
||||
{
|
||||
Nibbler backup (n);
|
||||
if (parse_time (n, true) &&
|
||||
parse_off (n))
|
||||
{
|
||||
if (!Lexer::isDigit (n.next ()))
|
||||
return true;
|
||||
}
|
||||
|
||||
n = backup;
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Using Zeller's Congruence.
|
||||
int ISO8601d::dayOfWeek (int year, int month, int day)
|
||||
|
|
|
@ -45,17 +45,11 @@ public:
|
|||
|
||||
private:
|
||||
bool parse_date_time_ext (Nibbler&);
|
||||
bool parse_date_time (Nibbler&);
|
||||
bool parse_date_ext (Nibbler&);
|
||||
bool parse_date (Nibbler&, bool);
|
||||
bool parse_off_ext (Nibbler&);
|
||||
bool parse_off (Nibbler&);
|
||||
bool parse_time_ext (Nibbler&);
|
||||
bool parse_time (Nibbler&, bool);
|
||||
bool parse_time_utc_ext (Nibbler&);
|
||||
bool parse_time_utc (Nibbler&);
|
||||
bool parse_time_off_ext (Nibbler&);
|
||||
bool parse_time_off (Nibbler&);
|
||||
int dayOfWeek (int, int, int);
|
||||
bool validate ();
|
||||
void resolve ();
|
||||
|
|
|
@ -324,6 +324,15 @@ void Lexer::dequote (std::string& input)
|
|||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
bool Lexer::wasQuoted (const std::string& input)
|
||||
{
|
||||
if (input.find_first_of (" \t()") != std::string::npos)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
bool Lexer::isEOS () const
|
||||
{
|
||||
|
|
25
src/Lexer.h
25
src/Lexer.h
|
@ -63,18 +63,19 @@ public:
|
|||
|
||||
// Static helpers.
|
||||
static const std::string typeName (const Lexer::Type&);
|
||||
static bool isWhitespace (int);
|
||||
static bool isAlpha (int);
|
||||
static bool isDigit (int);
|
||||
static bool isHexDigit (int);
|
||||
static bool isIdentifierStart (int);
|
||||
static bool isIdentifierNext (int);
|
||||
static bool isSingleCharOperator (int);
|
||||
static bool isDoubleCharOperator (int, int, int);
|
||||
static bool isTripleCharOperator (int, int, int, int);
|
||||
static bool isBoundary (int, int);
|
||||
static bool isPunctuation (int);
|
||||
static void dequote (std::string&);
|
||||
static bool isWhitespace (int);
|
||||
static bool isAlpha (int);
|
||||
static bool isDigit (int);
|
||||
static bool isHexDigit (int);
|
||||
static bool isIdentifierStart (int);
|
||||
static bool isIdentifierNext (int);
|
||||
static bool isSingleCharOperator (int);
|
||||
static bool isDoubleCharOperator (int, int, int);
|
||||
static bool isTripleCharOperator (int, int, int, int);
|
||||
static bool isBoundary (int, int);
|
||||
static bool isPunctuation (int);
|
||||
static void dequote (std::string&);
|
||||
static bool wasQuoted (const std::string&);
|
||||
|
||||
// Helpers.
|
||||
bool isEOS () const;
|
||||
|
|
|
@ -1092,6 +1092,7 @@ bool Task::hasTag (const std::string& tag) const
|
|||
{
|
||||
// Synthetic tags - dynamically generated, but do not occupy storage space.
|
||||
// Note: This list must match that in CmdInfo::execute.
|
||||
// Note: This list must match that in ::feedback_reserved_tags.
|
||||
if (tag == "BLOCKED") return is_blocked;
|
||||
if (tag == "UNBLOCKED") return !is_blocked;
|
||||
if (tag == "BLOCKING") return is_blocking;
|
||||
|
@ -2094,11 +2095,13 @@ void Task::modify (modType type, bool text_required /* = false */)
|
|||
else if (a._lextype == Lexer::Type::tag)
|
||||
{
|
||||
std::string tag = a.attribute ("name");
|
||||
feedback_reserved_tags (tag);
|
||||
|
||||
if (a.attribute ("sign") == "+")
|
||||
{
|
||||
context.debug (label + "tags <-- add '" + tag + "'");
|
||||
addTag (tag);
|
||||
feedback_special_tags ((*this), tag);
|
||||
feedback_special_tags (*this, tag);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -63,8 +63,7 @@ int CmdCalc::execute (std::string& output)
|
|||
|
||||
// Compile all the args into one expression.
|
||||
std::string expression;
|
||||
std::vector <std::string> words = context.cli2.getWords ();
|
||||
for (auto& word : words)
|
||||
for (auto& word : context.cli2.getWords ())
|
||||
expression += word + " ";
|
||||
|
||||
// Evaluate according to preference.
|
||||
|
|
|
@ -81,10 +81,7 @@ int CmdCustom::execute (std::string& output)
|
|||
|
||||
// Add the report filter to any existing filter.
|
||||
if (reportFilter != "")
|
||||
{
|
||||
context.cli2.addFilter (reportFilter);
|
||||
context.cli2.prepareFilter ();
|
||||
}
|
||||
|
||||
// Apply filter.
|
||||
handleRecurrence ();
|
||||
|
|
|
@ -303,6 +303,7 @@ int CmdInfo::execute (std::string& output)
|
|||
// Virtual tags.
|
||||
{
|
||||
// Note: This list must match that in Task::hasTag.
|
||||
// Note: This list must match that in ::feedback_reserved_tags.
|
||||
std::string virtualTags = "";
|
||||
if (task.hasTag ("ACTIVE")) virtualTags += "ACTIVE ";
|
||||
if (task.hasTag ("ANNOTATED")) virtualTags += "ANNOTATED ";
|
||||
|
|
|
@ -351,6 +351,41 @@ void feedback_affected (const std::string& effect, const Task& task)
|
|||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Implements feedback and error when adding a reserved tag name.
|
||||
void feedback_reserved_tags (const std::string& tag)
|
||||
{
|
||||
// Note: This list must match that in Task::hasTag.
|
||||
// Note: This list must match that in CmdInfo::execute.
|
||||
if (tag == "BLOCKED" ||
|
||||
tag == "UNBLOCKED" ||
|
||||
tag == "BLOCKING" ||
|
||||
tag == "READY" ||
|
||||
tag == "DUE" ||
|
||||
tag == "DUETODAY" ||
|
||||
tag == "TODAY" ||
|
||||
tag == "YESTERDAY" ||
|
||||
tag == "TOMORROW" ||
|
||||
tag == "OVERDUE" ||
|
||||
tag == "WEEK" ||
|
||||
tag == "MONTH" ||
|
||||
tag == "YEAR" ||
|
||||
tag == "ACTIVE" ||
|
||||
tag == "SCHEDULED" ||
|
||||
tag == "CHILD" ||
|
||||
tag == "UNTIL" ||
|
||||
tag == "ANNOTATED" ||
|
||||
tag == "TAGGED" ||
|
||||
tag == "PARENT" ||
|
||||
tag == "WAITING" ||
|
||||
tag == "PENDING" ||
|
||||
tag == "COMPLETED" ||
|
||||
tag == "DELETED")
|
||||
{
|
||||
throw format (STRING_FEEDBACK_TAG_VIRTUAL, tag);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Implements feedback when adding special tags to a task.
|
||||
void feedback_special_tags (const Task& task, const std::string& tag)
|
||||
|
|
|
@ -792,6 +792,7 @@
|
|||
#define STRING_FEEDBACK_TAG_NONAG "Das besondere Schlagwort 'nonag' verhindert Nachfragen, wenn diese Aufgabe geändert wird."
|
||||
#define STRING_FEEDBACK_TAG_NOCAL "Das besondere Schlagwort 'nocal' verhindert, dass diese Aufgabe im 'calendar'-Report erscheint."
|
||||
#define STRING_FEEDBACK_TAG_NEXT "Das besondere Schlagwort 'next' erhöht die Dringlichkeit dieser Aufgabe, sodass sie im 'next'-Report erscheint."
|
||||
#define STRING_FEEDBACK_TAG_VIRTUAL "Virtual tags (including '{1}') are reserved and may not be added or removed."
|
||||
#define STRING_FEEDBACK_UNBLOCKED "Aufgabe {1} '{2}' entsperrt."
|
||||
#define STRING_FEEDBACK_EXPIRED "Aufgabe {1} '{2}' ist abgelaufen und wurde gelöscht."
|
||||
#define STRING_FEEDBACK_BACKLOG "Lokale Änderungen. Datenabgleich erforderlich."
|
||||
|
|
|
@ -372,7 +372,7 @@
|
|||
#define STRING_CMD_UDAS_ORPHANS "{1} Orphan UDAs"
|
||||
|
||||
#define STRING_CMD_DELETE_USAGE "Deletes the specified task"
|
||||
#define STRING_CMD_DELETE_CONFIRM "Permanently delete task {1} '{2}'?"
|
||||
#define STRING_CMD_DELETE_CONFIRM "Delete task {1} '{2}'?"
|
||||
#define STRING_CMD_DELETE_TASK "Deleting task {1} '{2}'."
|
||||
#define STRING_CMD_DELETE_TASK_R "Deleting recurring task {1} '{2}'."
|
||||
#define STRING_CMD_DELETE_CONFIRM_R "This is a recurring task. Do you want to delete all pending recurrences of this same task?"
|
||||
|
@ -792,6 +792,7 @@
|
|||
#define STRING_FEEDBACK_TAG_NONAG "The 'nonag' special tag will prevent nagging when this task is modified."
|
||||
#define STRING_FEEDBACK_TAG_NOCAL "The 'nocal' special tag will keep this task off the 'calendar' report."
|
||||
#define STRING_FEEDBACK_TAG_NEXT "The 'next' special tag will boost the urgency of this task so it appears on the 'next' report."
|
||||
#define STRING_FEEDBACK_TAG_VIRTUAL "Virtual tags (including '{1}') are reserved and may not be added or removed."
|
||||
#define STRING_FEEDBACK_UNBLOCKED "Unblocked {1} '{2}'."
|
||||
#define STRING_FEEDBACK_EXPIRED "Task {1} '{2}' expired and was deleted."
|
||||
#define STRING_FEEDBACK_BACKLOG "There are local changes. Sync required."
|
||||
|
|
|
@ -792,6 +792,7 @@
|
|||
#define STRING_FEEDBACK_TAG_NONAG "Speciala etikedo 'nonag' antaŭmalebligitos molestojn, kiam oni modifus tiun taskon."
|
||||
#define STRING_FEEDBACK_TAG_NOCAL "Speciala etikedo 'nocal' ekskluzivos tiun taskon ĉe raporto 'calendar'."
|
||||
#define STRING_FEEDBACK_TAG_NEXT "Speciala etikedo 'next' pligrandigos la urĝecon de tiu tasko por ke ĝi aperus ĉe raporto 'next'."
|
||||
#define STRING_FEEDBACK_TAG_VIRTUAL "Virtual tags (including '{1}') are reserved and may not be added or removed."
|
||||
#define STRING_FEEDBACK_UNBLOCKED "Malblokis {1} '{2}'."
|
||||
#define STRING_FEEDBACK_EXPIRED "Tasko {1} '{2}' fortempiĝis do estis viŝata."
|
||||
#define STRING_FEEDBACK_BACKLOG "Estas lokaj ŝanĝoj. Sinkronigo devita."
|
||||
|
|
|
@ -804,6 +804,7 @@
|
|||
#define STRING_FEEDBACK_TAG_NONAG "La marca especial 'nonag' evitará el recuerdo fastidioso cuando la tarea sea modificada."
|
||||
#define STRING_FEEDBACK_TAG_NOCAL "La marca especial 'nocal' mantendrá esta tarea fuera del informe 'calendar'."
|
||||
#define STRING_FEEDBACK_TAG_NEXT "La etiqueta especial 'next' aumentará la urgencia de esta tarea para que aparezca en el informe 'next'."
|
||||
#define STRING_FEEDBACK_TAG_VIRTUAL "Virtual tags (including '{1}') are reserved and may not be added or removed."
|
||||
#define STRING_FEEDBACK_UNBLOCKED "Desbloqueada {1} '{2}'."
|
||||
#define STRING_FEEDBACK_EXPIRED "La tarea {1} '{2}' caducó y fue eliminada."
|
||||
#define STRING_FEEDBACK_BACKLOG "Hay modificaciones locales. Se require una sincronización."
|
||||
|
|
|
@ -792,6 +792,7 @@
|
|||
#define STRING_FEEDBACK_TAG_NONAG "The 'nonag' special tag will prevent nagging when this task is modified."
|
||||
#define STRING_FEEDBACK_TAG_NOCAL "The 'nocal' special tag will keep this task off the 'calendar' report."
|
||||
#define STRING_FEEDBACK_TAG_NEXT "The 'next' special tag will boost the urgency of this task so it appears on the 'next' report."
|
||||
#define STRING_FEEDBACK_TAG_VIRTUAL "Virtual tags (including '{1}') are reserved and may not be added or removed."
|
||||
#define STRING_FEEDBACK_UNBLOCKED "Unblocked {1} '{2}'."
|
||||
#define STRING_FEEDBACK_EXPIRED "Tâche {1} '{2}' a expiré et a été supprimée."
|
||||
#define STRING_FEEDBACK_BACKLOG "Il y a des changements locaux. Synchronisation requise."
|
||||
|
|
|
@ -791,6 +791,7 @@
|
|||
#define STRING_FEEDBACK_TAG_NONAG "Il tag speciale 'nonag' eviterà problemi quando il task è modificato."
|
||||
#define STRING_FEEDBACK_TAG_NOCAL "Il tag speciale 'nocal' manterrà il task fuori dal report 'calendar'."
|
||||
#define STRING_FEEDBACK_TAG_NEXT "Il tag speciale 'next' aumenterà l'urgenza di questo task in modo che appaia nel report 'next'."
|
||||
#define STRING_FEEDBACK_TAG_VIRTUAL "Virtual tags (including '{1}') are reserved and may not be added or removed."
|
||||
#define STRING_FEEDBACK_UNBLOCKED "Sbloccato {1} '{2}'."
|
||||
#define STRING_FEEDBACK_EXPIRED "Il task {1} '{2}' è scaduto ed è stato eliminato"
|
||||
#define STRING_FEEDBACK_BACKLOG "There are local changes. Sync required."
|
||||
|
|
|
@ -792,6 +792,7 @@
|
|||
#define STRING_FEEDBACK_TAG_NONAG "The 'nonag' special tag will prevent nagging when this task is modified."
|
||||
#define STRING_FEEDBACK_TAG_NOCAL "The 'nocal' special tag will keep this task off the 'calendar' report."
|
||||
#define STRING_FEEDBACK_TAG_NEXT "The 'next' special tag will boost the urgency of this task so it appears on the 'next' report."
|
||||
#define STRING_FEEDBACK_TAG_VIRTUAL "Virtual tags (including '{1}') are reserved and may not be added or removed."
|
||||
#define STRING_FEEDBACK_UNBLOCKED "Unblocked {1} '{2}'."
|
||||
#define STRING_FEEDBACK_EXPIRED "Task {1} '{2}' expired and was deleted."
|
||||
#define STRING_FEEDBACK_BACKLOG "There are local changes. Sync required."
|
||||
|
|
|
@ -792,6 +792,7 @@
|
|||
#define STRING_FEEDBACK_TAG_NONAG "Specjalny tag 'nonag' uchroni przed upierdliwością kiedy zadanie jest modyfikowane."
|
||||
#define STRING_FEEDBACK_TAG_NOCAL "Specjalny tag 'nocal' spowoduje nie dodawanie zadania do kalendarza."
|
||||
#define STRING_FEEDBACK_TAG_NEXT "Specjalny tag 'next' podniesie pilność tego zadania co spowoduje wyświetlenie go w raporcie 'next'."
|
||||
#define STRING_FEEDBACK_TAG_VIRTUAL "Virtual tags (including '{1}') are reserved and may not be added or removed."
|
||||
#define STRING_FEEDBACK_UNBLOCKED "Odblokowane {1} '{2}'."
|
||||
#define STRING_FEEDBACK_EXPIRED "Zadanie {1} '{2}' jest przedawnione i zostało usunięte."
|
||||
#define STRING_FEEDBACK_BACKLOG "Wykryto lokalne zmiany. Wymagana synchronizacja."
|
||||
|
|
|
@ -792,6 +792,7 @@
|
|||
#define STRING_FEEDBACK_TAG_NONAG "A marca especial 'nonag' irá prevenir avisos quando a tarefa é modificada."
|
||||
#define STRING_FEEDBACK_TAG_NOCAL "A marca especial 'nocal' irá manter esta tarefa ausente do relatório de 'calendário'."
|
||||
#define STRING_FEEDBACK_TAG_NEXT "A marca especial 'next' irá aumentar a urgência desta tarefa de modo a que apareça no relatório 'next'."
|
||||
#define STRING_FEEDBACK_TAG_VIRTUAL "Virtual tags (including '{1}') are reserved and may not be added or removed."
|
||||
#define STRING_FEEDBACK_UNBLOCKED "Desbloqueada {1} '{2}'."
|
||||
#define STRING_FEEDBACK_EXPIRED "Tarefa {1} '{2}' expirou e foi eliminada."
|
||||
#define STRING_FEEDBACK_BACKLOG "Há modificações locais. Necessário sincronizar (sync)."
|
||||
|
|
|
@ -67,6 +67,7 @@ std::string renderAttribute (const std::string&, const std::string&, const std::
|
|||
void feedback_affected (const std::string&);
|
||||
void feedback_affected (const std::string&, int);
|
||||
void feedback_affected (const std::string&, const Task&);
|
||||
void feedback_reserved_tags (const std::string&);
|
||||
void feedback_special_tags (const Task&, const std::string&);
|
||||
void feedback_unblocked (const Task&);
|
||||
void feedback_backlog ();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue