taskwarrior/src/commands/CmdShow.cpp
Paul Beckingham 78f91ffa91 Verbosity
- Added 'affected' verbosity token.
- Deprecated 'rc.echo.command'.
- Updated config defaults.
- Labelled all deprecated 'echo.command' references with '// Deprecated 2.0'
- Added unit tests to start the verbosity testing.  Not complete.
- Updated taskrc.5 man page.
2011-10-01 11:16:12 -04:00

411 lines
12 KiB
C++

////////////////////////////////////////////////////////////////////////////////
// taskwarrior - a command line task list manager.
//
// Copyright 2006 - 2011, Paul Beckingham, Federico Hernandez.
// All rights reserved.
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the
//
// Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor,
// Boston, MA
// 02110-1301
// USA
//
////////////////////////////////////////////////////////////////////////////////
#define L10N // Localization complete.
#include <vector>
#include <sstream>
#include <algorithm>
#include <text.h>
#include <i18n.h>
#include <Context.h>
#include <Directory.h>
#include <ViewText.h>
#include <CmdShow.h>
extern Context context;
////////////////////////////////////////////////////////////////////////////////
CmdShow::CmdShow ()
{
_keyword = "show";
_usage = "task show [all | substring]";
_description = STRING_CMD_SHOW;
_read_only = true;
_displays_id = false;
}
////////////////////////////////////////////////////////////////////////////////
int CmdShow::execute (std::string& output)
{
int rc = 0;
std::stringstream out;
// Obtain the arguments from the description. That way, things like '--'
// have already been handled.
std::vector <std::string> words = context.a3.extract_words ();
if (words.size () > 2)
throw std::string (STRING_CMD_SHOW_ARGS);
int width = context.getWidth ();
// Complain about configuration variables that are not recognized.
// These are the regular configuration variables.
// Note that there is a leading and trailing space, to make it easier to
// search for whole words.
std::string recognized =
" abbreviation.minimum"
" active.indicator"
" annotations"
" avoidlastcolumn"
" bulk"
" burndown.bias"
" calendar.details"
" calendar.details.report"
" calendar.holidays"
" calendar.legend"
" calendar.offset"
" calendar.offset.value"
" color"
" color.active"
" color.alternate"
" color.blocked"
" color.burndown.done"
" color.burndown.pending"
" color.burndown.started"
" color.calendar.due"
" color.calendar.due.today"
" color.calendar.holiday"
" color.calendar.overdue"
" color.calendar.today"
" color.calendar.weekend"
" color.calendar.weeknumber"
" color.completed"
" color.debug"
" color.deleted"
" color.due"
" color.due.today"
" color.footnote"
" color.header"
" color.history.add"
" color.history.delete"
" color.history.done"
" color.label"
" color.overdue"
" color.pri.H"
" color.pri.L"
" color.pri.M"
" color.pri.none"
" color.recurring"
" color.summary.background"
" color.summary.bar"
" color.sync.added"
" color.sync.changed"
" color.sync.rejected"
" color.tagged"
" color.undo.after"
" color.undo.before"
" column.padding"
" column.spacing"
" complete.all.projects"
" complete.all.tags"
" confirmation"
" data.location"
" dateformat"
" dateformat.annotation"
" dateformat.holiday"
" dateformat.report"
" debug"
" default.command"
" default.due"
" default.priority"
" default.project"
" defaultheight"
" defaultwidth"
" dependency.confirmation"
" dependency.indicator"
" dependency.reminder"
" detection"
" displayweeknumber"
" dom"
" due"
" echo.command" // Deprecated 2.0
" edit.verbose"
" editor"
" exit.on.missing.db"
" export.ical.class"
" expressions"
" extensions"
" fontunderline"
" gc"
" hyphenate"
" indent.annotation"
" indent.report"
" journal.info"
" journal.time"
" journal.time.start.annotation"
" journal.time.stop.annotation"
" json.array"
" list.all.projects"
" list.all.tags"
" locale"
" locking"
" merge.autopush"
" merge.default.uri"
" monthsperline"
" nag"
" patterns"
" project"
" pull.default.uri"
" push.default.uri"
" recurrence.indicator"
" recurrence.limit"
" regex"
" row.padding"
" rule.precedence.color"
" search.case.sensitive"
" shadow.command"
" shadow.file"
" shadow.notify"
" shell.prompt"
" tag.indicator"
" undo.style"
" urgency.active.coefficient"
" urgency.annotations.coefficient"
" urgency.blocked.coefficient"
" urgency.blocking.coefficient"
" urgency.due.coefficient"
" urgency.next.coefficient"
" urgency.priority.coefficient"
" urgency.project.coefficient"
" urgency.tags.coefficient"
" urgency.waiting.coefficient"
" verbose"
" weekstart"
" xterm.title"
" ";
// This configuration variable is supported, but not documented. It exists
// so that unit tests can force color to be on even when the output from task
// is redirected to a file, or stdout is not a tty.
recognized += "_forcecolor ";
std::vector <std::string> all;
context.config.all (all);
std::vector <std::string> unrecognized;
std::vector <std::string>::iterator i;
for (i = all.begin (); i != all.end (); ++i)
{
// Disallow partial matches by tacking a leading and trailing space on each
// variable name.
std::string pattern = " " + *i + " ";
if (recognized.find (pattern) == std::string::npos)
{
// These are special configuration variables, because their name is
// dynamic.
if (i->substr (0, 14) != "color.keyword." &&
i->substr (0, 14) != "color.project." &&
i->substr (0, 10) != "color.tag." &&
i->substr (0, 8) != "holiday." &&
i->substr (0, 7) != "report." &&
i->substr (0, 6) != "alias." &&
i->substr (0, 5) != "hook." &&
i->substr (0, 21) != "urgency.user.project." &&
i->substr (0, 17) != "urgency.user.tag.")
{
unrecognized.push_back (*i);
}
}
}
// Find all the values that match the defaults, for highlighting.
std::vector <std::string> default_values;
Config default_config;
default_config.setDefaults ();
for (i = all.begin (); i != all.end (); ++i)
if (context.config.get (*i) != default_config.get (*i))
default_values.push_back (*i);
// Create output view.
ViewText view;
view.width (width);
view.add (Column::factory ("string", STRING_CMD_SHOW_CONF_VAR));
view.add (Column::factory ("string", STRING_CMD_SHOW_CONF_VALUE));
Color error ("bold white on red");
Color warning ("black on yellow");
std::string section;
// Look for the first plausible argument which could be a pattern
if (words.size ())
section = words[0];
if (section == "all")
section = "";
for (i = all.begin (); i != all.end (); ++i)
{
std::string::size_type loc = i->find (section, 0);
if (loc != std::string::npos)
{
// Look for unrecognized.
Color color;
if (std::find (unrecognized.begin (), unrecognized.end (), *i) != unrecognized.end ())
color = error;
else if (std::find (default_values.begin (), default_values.end (), *i) != default_values.end ())
color = warning;
int row = view.addRow ();
view.set (row, 0, *i, color);
view.set (row, 1, context.config.get (*i), color);
}
}
out << "\n"
<< view.render ()
<< (view.rows () == 0 ? STRING_CMD_SHOW_NONE : "")
<< (view.rows () == 0 ? "\n\n" : "\n");
// Display the unrecognized variables.
if (unrecognized.size ())
{
out << STRING_CMD_SHOW_UNREC << "\n";
for (i = unrecognized.begin (); i != unrecognized.end (); ++i)
out << " " << *i << "\n";
if (context.color ())
out << "\n " << format (STRING_CMD_SHOW_DIFFER_COLOR, error.colorize ("color"));
out << "\n\n";
}
if (default_values.size ())
{
out << STRING_CMD_SHOW_DIFFER;
if (context.color ())
out << " " << format (STRING_CMD_SHOW_DIFFER_COLOR, warning.colorize ("color"));
}
out << context.config.checkForDeprecatedColor ();
out << context.config.checkForDeprecatedColumns ();
// TODO Check for referenced but missing theme files.
// TODO Check for referenced but missing string files.
// TODO Check for referenced but missing tips files.
// Check for referenced but missing hook scripts.
#ifdef HAVE_LIBLUA
std::vector <std::string> missing_scripts;
for (i = all.begin (); i != all.end (); ++i)
{
if (i->substr (0, 5) == "hook.")
{
std::string value = context.config.get (*i);
Nibbler n (value);
// <path>:<function> [, ...]
while (!n.depleted ())
{
std::string file;
std::string function;
if (n.getUntil (':', file) &&
n.skip (':') &&
n.getUntil (',', function))
{
Path script (file);
if (!script.exists () || !script.readable ())
missing_scripts.push_back (file);
(void) n.skip (',');
}
}
}
}
if (missing_scripts.size ())
{
out << STRING_CMD_SHOW_HOOKS << "\n";
for (i = missing_scripts.begin (); i != missing_scripts.end (); ++i)
out << " " << *i << "\n";
out << "\n";
}
#endif
// Check for bad values in rc.annotations.
// TODO Reconsider this.
std::string annotations = context.config.get ("annotations");
if (annotations != "full" &&
annotations != "sparse" &&
annotations != "none")
out << format (STRING_CMD_SHOW_CONFIG_ERROR, "annotations", annotations)
<< "\n";
// Check for bad values in rc.calendar.details.
std::string calendardetails = context.config.get ("calendar.details");
if (calendardetails != "full" &&
calendardetails != "sparse" &&
calendardetails != "none")
out << format (STRING_CMD_SHOW_CONFIG_ERROR, "calendar.details", calendardetails)
<< "\n";
// Check for bad values in rc.calendar.holidays.
std::string calendarholidays = context.config.get ("calendar.holidays");
if (calendarholidays != "full" &&
calendarholidays != "sparse" &&
calendarholidays != "none")
out << format (STRING_CMD_SHOW_CONFIG_ERROR, "calendar.holidays", calendarholidays)
<< "\n";
// Check for bad values in rc.default.priority.
std::string defaultPriority = context.config.get ("default.priority");
if (defaultPriority != "H" &&
defaultPriority != "M" &&
defaultPriority != "L" &&
defaultPriority != "")
out << format (STRING_CMD_SHOW_CONFIG_ERROR, "default.priority", defaultPriority)
<< "\n";
// Verify installation. This is mentioned in the documentation as the way
// to ensure everything is properly installed.
if (all.size () == 0)
{
out << STRING_CMD_SHOW_EMPTY << "\n";
rc = 1;
}
else
{
Directory location (context.config.get ("data.location"));
if (location._data == "")
out << STRING_CMD_SHOW_NO_LOCATION << "\n";
if (! location.exists ())
out << STRING_CMD_SHOW_LOC_EXIST << "\n";
}
output = out.str ();
return rc;
}
////////////////////////////////////////////////////////////////////////////////