Refactor to store tags as individual attributes

Each tag is stored as `tag_<tagname>: x`.  The `x` is required because
empty attributes are treated as nonexistent.

For compatibility, the `tags` attribute is updated in sync with the
per-tag attributes.  This compatibility support may be dropped in later
versions.

Note that synchronization _updates_ use JSON format, which does not
change with this patch, and thus no compatibility issues exist.  The
synchronization _initialization_, however, uses FF4, meaning that a
sync server initialized from a version of `task` with this patch will
contain `tag_<tagname>` attributes, which will look like orphaned UDAs
to older versions.  However, as updates to tasks are synchronized via
the sync server, the updates will not contain these attributes and they
will show as "deleted" in the `task info` display on the older version.
Aside from the noise in the `task info` output, this is harmless.
This commit is contained in:
Dustin J. Mitchell 2021-08-02 01:25:16 +00:00 committed by Tomas Babej
parent 17e6257e07
commit 20041c120e
6 changed files with 163 additions and 91 deletions

View file

@ -115,17 +115,16 @@ void ColumnTags::render (
int width,
Color& color)
{
if (task.has (_name))
auto all = task.getTags ();
if (all.size() > 0)
{
std::string tags = task.get (_name);
if (_style == "default" ||
_style == "list")
{
if (tags.find (',') != std::string::npos)
if (all.size () > 1)
{
auto all = split (tags, ',');
std::sort (all.begin (), all.end ());
tags = join (" ", all);
auto tags = join (" ", all);
all.clear ();
wrapText (all, tags, width, _hyphenate);
@ -134,7 +133,7 @@ void ColumnTags::render (
renderStringLeft (lines, width, color, i);
}
else
renderStringLeft (lines, width, color, tags);
renderStringLeft (lines, width, color, all[0]);
}
else if (_style == "indicator")
{
@ -142,7 +141,6 @@ void ColumnTags::render (
}
else if (_style == "count")
{
auto all = split (tags, ',');
renderStringRight (lines, width, color, '[' + format (static_cast <int> (all.size ())) + ']');
}
}
@ -152,36 +150,36 @@ void ColumnTags::render (
void ColumnTags::modify (Task& task, const std::string& value)
{
std::string label = " MODIFICATION ";
std::string commasep;
std::vector <std::string> tags;
// TW-1701
task.set (_name, "");
for (auto& tag : split (value, ','))
// If it's a DOM ref, eval it first.
Lexer lexer (value);
std::string domRef;
Lexer::Type type;
if (lexer.token (domRef, type) &&
type == Lexer::Type::dom)
{
// If it's a DOM ref, eval it first.
Lexer lexer (tag);
std::string domRef;
Lexer::Type type;
if (lexer.token (domRef, type) &&
type == Lexer::Type::dom)
{
Eval e;
e.addSource (domSource);
contextTask = task;
Eval e;
e.addSource (domSource);
contextTask = task;
Variant v;
e.evaluateInfixExpression (value, v);
task.addTag ((std::string) v);
Context::getContext ().debug (label + "tags <-- '" + (std::string) v + "' <-- '" + tag + '\'');
}
else
{
task.addTag (tag);
Context::getContext ().debug (label + "tags <-- '" + tag + '\'');
}
Variant v;
e.evaluateInfixExpression (value, v);
commasep = (std::string) v;
} else {
commasep = (std::string) value;
}
for (auto& tag : split (commasep, ','))
{
tags.push_back ((std::string) tag);
Context::getContext ().debug (label + "tags <-- '" + tag + '\'');
feedback_special_tags (task, tag);
}
task.setTags(tags);
}
////////////////////////////////////////////////////////////////////////////////