Use the tag-related Task methods in the Tags column type

This column type was taking a shortcut by assuming the `tags` property
contains a comma-separated list of tags. This property is now deprecated
and tags are stored in individual properties. The tag-related Task
methods correctly access these new properties.

Includes a test for the fixed behavior and a test demonstrating that
`task info` already does the right thing.
This commit is contained in:
Dustin J. Mitchell 2025-07-10 16:11:53 -04:00
parent 236b57f321
commit b4578692ed
No known key found for this signature in database
3 changed files with 49 additions and 15 deletions

View file

@ -66,28 +66,21 @@ void ColumnTags::setStyle(const std::string& value) {
// Set the minimum and maximum widths for the value.
void ColumnTags::measure(Task& task, unsigned int& minimum, unsigned int& maximum) {
minimum = maximum = 0;
if (task.has(_name)) {
if (task.getTagCount() > 0) {
if (_style == "indicator") {
minimum = maximum = utf8_width(Context::getContext().config.get("tag.indicator"));
} else if (_style == "count") {
minimum = maximum = 3;
} else if (_style == "default" || _style == "list") {
std::string tags = task.get(_name);
auto tags = task.getTags();
// Find the widest tag.
if (tags.find(',') != std::string::npos) {
auto all = split(tags, ',');
for (const auto& tag : all) {
auto length = utf8_width(tag);
if (length > minimum) minimum = length;
}
maximum = utf8_width(tags);
// Find the widest tag (minimum) and width of all tags together (maximum)
maximum = tags.size() - 1;
for (const auto& tag : tags) {
auto length = utf8_width(tag);
maximum += length;
if (length > minimum) minimum = length;
}
// No need to split a single tag.
else
minimum = maximum = utf8_width(tags);
}
}
}

View file

@ -74,6 +74,30 @@ class TestCustomReports(TestCase):
self.assertIn("[44m", out)
class TestReportTags(TestCase):
def setUp(self):
"""Executed before each test in the class"""
self.t = Task()
self.t.config("report.foo.description", "DESC")
self.t.config("report.foo.labels", "ID,TAGS")
self.t.config("report.foo.columns", "id,tags")
self.t.config("report.foo.sort", "id+")
def test_report_with_tags(self):
"""Verify custom report includes tags even when `tags` property is not included"""
# Create a task directly with TC, avoiding TaskWarrior's creation of the deprecated
# `tags` property.
uuid = self.t.make_tc_task(
status="pending",
description="task with tags",
tag_tag1="x",
tag_tag2="x",
)
code, out, err = self.t("foo")
self.assertIn("tag1", out)
self.assertIn("tag2", out)
class TestCustomErrorHandling(TestCase):
def setUp(self):
self.t = Task()

View file

@ -26,6 +26,7 @@
###############################################################################
import sys
import time
import os
import unittest
@ -115,6 +116,22 @@ class TestInfoCommand(TestCase):
self.assertRegex(out, r"U_TWO\s+P1D")
def test_tags_without_tags_attribute(self):
"""Verify info command shows tags, even if the `tags` property is not present"""
# Create a task directly with TC, avoiding TaskWarrior's creation of the deprecated
# `tags` property.
uuid = self.t.make_tc_task(
status="pending",
description="task with tags",
due=str(int(time.time())),
tag_foo="x",
tag_bar="x",
)
code, out, err = self.t("1 info")
# Tags can occur in any order
self.assertRegex(out, r"Tags\s+(bar|foo)\s(foo|bar)")
class TestBug425(TestCase):
def setUp(self):
self.t = Task()