Use the tag-related Task methods in the Tags column type (#3911)

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 22:08:30 -04:00 committed by GitHub
parent 236b57f321
commit 0282ea8b8a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 55 additions and 16 deletions

View file

@ -15,8 +15,11 @@ jobs:
path: ~/.cargo/registry
key: ${{ runner.os }}-cargo-registry-${{ steps.toolchain.outputs.cachekey }}-${{ hashFiles('**/Cargo.lock') }}
- name: Update apt repos
run: sudo apt-get update -y
- name: Install uuid-dev
run: sudo apt install uuid-dev
run: sudo apt-get install -y uuid-dev
- name: make a release tarball and build from it
run: |

View file

@ -7,6 +7,9 @@ jobs:
coverage:
runs-on: ubuntu-latest
steps:
- name: Update apt repos
run: sudo apt-get update -y
- name: Install apt packages
run: sudo apt-get install -y build-essential cmake git uuid-dev faketime locales python3 curl gcovr ninja-build

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) {
// 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;
}
maximum = utf8_width(tags);
}
// 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
@ -114,6 +115,21 @@ class TestInfoCommand(TestCase):
self.assertRegex(out, r"U_ONE\s+\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}")
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):