Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,5 @@ Thanks to the following, who submitted detailed bug reports and excellent sugges
Sebastian Carlos
ftambara
Tobias Predel
Alex Rogers

3 changes: 3 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -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
Expand Down
9 changes: 9 additions & 0 deletions src/SummaryTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 ()
{
Expand Down Expand Up @@ -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);
Expand Down
3 changes: 3 additions & 0 deletions src/SummaryTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ class SummaryTable
Builder& withRange (const Range&);
Builder& withIntervals (const std::vector <Interval>&);

Builder& withColor (bool);

Table build ();

private:
Expand All @@ -63,6 +65,7 @@ class SummaryTable
bool _show_tags;
bool _show_weekdays;
bool _show_weeks;
bool _with_color;

Range _range;
std::vector <Interval> _tracked;
Expand Down
9 changes: 9 additions & 0 deletions src/TagsTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ TagsTable::Builder& TagsTable::Builder::withTagDescriptions (std::vector <TagDes
return *this;
}

////////////////////////////////////////////////////////////////////////////////
TagsTable::Builder& TagsTable::Builder::withColor (bool forceColor)
{
_with_color = forceColor;
return *this;
}

////////////////////////////////////////////////////////////////////////////////
Table TagsTable::Builder::build ()
{
Expand All @@ -51,6 +58,8 @@ Table TagsTable::Builder::build ()
table.add ("Tag");
table.add ("Description");

table.withColor (_with_color);

for (const auto& tagDescription : _tagDescriptions)
{
auto row = table.addRow ();
Expand Down
2 changes: 2 additions & 0 deletions src/TagsTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,13 @@ class TagsTable
{
public:
Builder& withTagDescriptions (std::vector <TagDescription>&);
Builder& withColor (bool);

Table build ();

private:
std::vector <TagDescription> _tagDescriptions {};
bool _with_color {false};
};

public:
Expand Down
1 change: 1 addition & 0 deletions src/commands/CmdSummary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ int CmdSummary (
.withAnnotations (show_annotations)
.withRange (range)
.withIntervals (tracked)
.withColor (rules.getBoolean ("color"))
.build ();

std::cout << '\n'
Expand Down
1 change: 1 addition & 0 deletions src/commands/CmdTags.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ int CmdTags (

auto table = TagsTable::builder()
.withTagDescriptions (tagDescriptions)
.withColor (rules.getBoolean ("color"))
.build ();

std::cout << '\n'
Expand Down
12 changes: 12 additions & 0 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"));

Expand Down
102 changes: 102 additions & 0 deletions test/color_hint.t
Original file line number Diff line number Diff line change
@@ -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())