diff --git a/AUTHORS b/AUTHORS index a1b07d53..555eefbb 100644 --- a/AUTHORS +++ b/AUTHORS @@ -131,3 +131,5 @@ Thanks to the following, who submitted detailed bug reports and excellent sugges Sebastian Carlos ftambara Tobias Predel + Alex Rogers + diff --git a/ChangeLog b/ChangeLog index e87225b4..556c4197 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +- #727 :color hint does not work with the summary report + (thanks to Alex Rogers) + ------ current release --------------------------- 1.9.1 (2025-08-27) - 8c14b6d44adc75b7031eb0c25237db842aba37f9 diff --git a/src/SummaryTable.cpp b/src/SummaryTable.cpp index c0310bc5..6ce4fa05 100644 --- a/src/SummaryTable.cpp +++ b/src/SummaryTable.cpp @@ -110,6 +110,13 @@ SummaryTable::Builder & SummaryTable::Builder::withIntervals (const std::vector return *this; } +//////////////////////////////////////////////////////////////////////////////// +SummaryTable::Builder& SummaryTable::Builder::withColor (bool withColor) +{ + _with_color = withColor; + return *this; +} + //////////////////////////////////////////////////////////////////////////////// Table SummaryTable::Builder::build () { @@ -164,6 +171,8 @@ Table SummaryTable::Builder::build () table.add ("Annotation"); } + table.withColor(_with_color); + table.add ("Start", false); table.add ("End", false); table.add ("Time", false); diff --git a/src/SummaryTable.h b/src/SummaryTable.h index 3c58b21a..9ba98a1d 100644 --- a/src/SummaryTable.h +++ b/src/SummaryTable.h @@ -51,6 +51,8 @@ class SummaryTable Builder& withRange (const Range&); Builder& withIntervals (const std::vector &); + Builder& withColor (bool); + Table build (); private: @@ -63,6 +65,7 @@ class SummaryTable bool _show_tags; bool _show_weekdays; bool _show_weeks; + bool _with_color; Range _range; std::vector _tracked; diff --git a/src/TagsTable.cpp b/src/TagsTable.cpp index 53b62f58..15f931b4 100644 --- a/src/TagsTable.cpp +++ b/src/TagsTable.cpp @@ -40,6 +40,13 @@ TagsTable::Builder& TagsTable::Builder::withTagDescriptions (std::vector &); + Builder& withColor (bool); Table build (); private: std::vector _tagDescriptions {}; + bool _with_color {false}; }; public: diff --git a/src/commands/CmdSummary.cpp b/src/commands/CmdSummary.cpp index dcbf6b50..3edac0c1 100644 --- a/src/commands/CmdSummary.cpp +++ b/src/commands/CmdSummary.cpp @@ -154,6 +154,7 @@ int CmdSummary ( .withAnnotations (show_annotations) .withRange (range) .withIntervals (tracked) + .withColor (rules.getBoolean ("color")) .build (); std::cout << '\n' diff --git a/src/commands/CmdTags.cpp b/src/commands/CmdTags.cpp index 1f7272ea..d80e71db 100644 --- a/src/commands/CmdTags.cpp +++ b/src/commands/CmdTags.cpp @@ -80,6 +80,7 @@ int CmdTags ( auto table = TagsTable::builder() .withTagDescriptions (tagDescriptions) + .withColor (rules.getBoolean ("color")) .build (); std::cout << '\n' diff --git a/src/init.cpp b/src/init.cpp index 2bdfe4de..ae2d0943 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -158,6 +158,18 @@ void initializeDataJournalAndRules ( enableDebugMode (rules.getBoolean ("debug")); paths::initializeDirs (rules); + for (auto& arg : cli._args) + { + if (arg.hasTag ("HINT")) + { + if (arg.attribute ("canonical") == ":debug") rules.set ("debug", "on"); + if (arg.attribute ("canonical") == ":quiet") rules.set ("verbose", "off"); + if (arg.attribute ("canonical") == ":color") rules.set ("color", "on"); + if (arg.attribute ("canonical") == ":nocolor") rules.set ("color", "off"); + if (arg.attribute ("canonical") == ":yes") rules.set ("confirmation", "off"); + } + } + if (rules.has ("debug.indicator")) setDebugIndicator (rules.get ("debug.indicator")); diff --git a/src/libshared b/src/libshared index 15cb6f90..3da15345 160000 --- a/src/libshared +++ b/src/libshared @@ -1 +1 @@ -Subproject commit 15cb6f90f25758612d79f788095a4c3a9c090b89 +Subproject commit 3da15345b1bdd2bc69de7231e578bd5d530fc92c diff --git a/test/color_hint.t b/test/color_hint.t new file mode 100755 index 00000000..19cbb3d6 --- /dev/null +++ b/test/color_hint.t @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 + +############################################################################### +# +# Copyright 2025, Gothenburg Bit Factory. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +# https://opensource.org/license/mit +# +############################################################################### + +import os +import sys +import unittest + +# Ensure python finds the local simpletap module +sys.path.append(os.path.dirname(os.path.abspath(__file__))) + +from basetest import Timew, TestCase + + +class TestColorHint(TestCase): + def setUp(self): + """Executed before each test in the class""" + self.t = Timew() + + def test_color_hint_overrides_config_and_works_when_piped(self): + """:color hint should override color=off config and work with piped output""" + # Set color to off in config file + try: + self.t.config("color", "off") + except: + pass + + # Track some time + self.t("start 1h ago foo") + self.t("stop") + + # Test with day command - output is naturally piped in test framework + code, out, err = self.t(":color day") + self.assertIn("\033[", out, "Expected colors with :color hint for day command") + + # Test with summary command + code, out, err = self.t(":color summary") + self.assertIn("\033[", out, "Expected colors with :color hint for summary command") + + # Test with tags command + code, out, err = self.t(":color tags") + self.assertIn("\033[", out, "Expected colors with :color hint for tags command") + + def test_nocolor_hint_disables_color(self): + """:nocolor hint should disable color output""" + # Set color to on in config file + self.t.config("color", "on") + + # Track some time + self.t("start 1h ago bar") + self.t("stop") + + # Use :nocolor hint + code, out, err = self.t(":nocolor day") + + # Check that there are no ANSI color codes + self.assertNotIn("\033[", out, "Expected no ANSI color codes when using :nocolor hint") + + def test_no_color_by_default_when_piped(self): + """By default, no color should be used when output is piped""" + # Don't set any color config (should default to off for pipes) + + # Track some time + self.t("start 2h ago qux") + self.t("stop 1h ago") + + # Without :color hint, output should not have colors when piped + # (test framework pipes output by default) + code, out, err = self.t("day") + self.assertNotIn("\033[", out, "Expected no colors by default when piped") + + code, out, err = self.t("summary") + self.assertNotIn("\033[", out, "Expected no colors in summary by default when piped") + + +if __name__ == "__main__": + from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner())