- Added feature #415, which supports displaying just a single page of tasks,
  by specifying either 'limit:page' to a command, or 'report.xxx.limit:page'
  in a report specification (thanks to T. Charles Yun).
- Modified the 'next' report to only display a page, by default.
This commit is contained in:
Paul Beckingham 2010-06-20 17:32:11 -04:00
parent 2f85941d37
commit 916b8641b3
13 changed files with 239 additions and 28 deletions

View file

@ -20,7 +20,11 @@
list all used projects/tags, not just the ones used in current pending tasks. list all used projects/tags, not just the ones used in current pending tasks.
Controlled by the 'list.all.projects' and 'list.all.tags' configuration Controlled by the 'list.all.projects' and 'list.all.tags' configuration
variables (thanks to Dirk Deimeke). variables (thanks to Dirk Deimeke).
+ Added feature #415, which supports displaying just a single page of tasks,
by specifying either 'limit:page' to a command, or 'report.xxx.limit:page'
in a report specification (thanks to T. Charles Yun).
+ Improvements to the man pages (thanks to T. Charles Yun). + Improvements to the man pages (thanks to T. Charles Yun).
+ Modified the 'next' report to only display a page, by default.
+ Added feature #408, making it possible to delete annotations with the new + Added feature #408, making it possible to delete annotations with the new
denotate command and the provided description (thanks to Dirk Deimeke). denotate command and the provided description (thanks to Dirk Deimeke).
+ Fixed bug #406 so that task now includes command aliases in the _commands + Fixed bug #406 so that task now includes command aliases in the _commands

View file

@ -302,7 +302,10 @@ Specifies background color.
.TP .TP
.B limit:<number-of-rows> .B limit:<number-of-rows>
Specifies the desired number of rows a report should have. Specifies the desired number of tasks a report should show, if a positive
integer is given. The value 'page' may also be used, and will limit the
report output to as many lines of text as will fit on screen. This defaults
to 25 lines, if ncurses is not installed or enabled.
.TP .TP
.B wait:<wait-date> .B wait:<wait-date>

View file

@ -344,8 +344,8 @@ bool Att::validNameValue (
else if (name == "limit") else if (name == "limit")
{ {
if (value == "" || !digitsOnly (value)) if (value == "" || (value != "page" && !digitsOnly (value)))
throw std::string ("The '") + name + "' attribute must be an integer."; throw std::string ("The '") + name + "' attribute must be an integer, or the value 'page'.";
} }
else if (name == "status") else if (name == "status")

View file

@ -293,7 +293,7 @@ std::string Config::defaults =
"report.next.columns=id,project,priority,due,active,age,description\n" "report.next.columns=id,project,priority,due,active,age,description\n"
"report.next.labels=ID,Project,Pri,Due,Active,Age,Description\n" "report.next.labels=ID,Project,Pri,Due,Active,Age,Description\n"
"report.next.sort=due+,priority-,project+\n" "report.next.sort=due+,priority-,project+\n"
"report.next.filter=status:pending\n" "report.next.filter=status:pending limit:page\n"
"#report.next.dateformat=m/d/Y\n" "#report.next.dateformat=m/d/Y\n"
"#report.next.annotations=full\n" "#report.next.annotations=full\n"
"\n"; "\n";

View file

@ -55,6 +55,7 @@ public:
void shadow (); // shadow file update void shadow (); // shadow file update
int getWidth (); // determine terminal width int getWidth (); // determine terminal width
int getHeight (); // determine terminal height
void header (const std::string&); // Header message sink void header (const std::string&); // Header message sink
void footnote (const std::string&); // Footnote message sink void footnote (const std::string&); // Footnote message sink
@ -93,7 +94,6 @@ public:
std::vector <std::string> tagRemovals; std::vector <std::string> tagRemovals;
Hooks hooks; Hooks hooks;
private:
std::vector <std::string> headers; std::vector <std::string> headers;
std::vector <std::string> footnotes; std::vector <std::string> footnotes;
std::vector <std::string> debugMessages; std::vector <std::string> debugMessages;

View file

@ -1024,10 +1024,13 @@ void Table::clean (std::string& value)
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
const std::string Table::render (int maximum /* = 0 */) const std::string Table::render (int maxrows /* = 0 */, int maxlines /* = 0 */)
{ {
Timer t ("Table::render"); Timer t ("Table::render");
// May not exceed maxlines, if non-zero.
int renderedlines = 0;
calculateColumnWidths (); calculateColumnWidths ();
// Print column headers in column order. // Print column headers in column order.
@ -1048,8 +1051,12 @@ const std::string Table::render (int maximum /* = 0 */)
} }
output += "\n"; output += "\n";
++renderedlines;
if (underline.length ()) if (underline.length ())
{
output += underline + "\n"; output += underline + "\n";
++renderedlines;
}
// Determine row order, according to sort options. // Determine row order, according to sort options.
std::vector <int> order; std::vector <int> order;
@ -1060,14 +1067,17 @@ const std::string Table::render (int maximum /* = 0 */)
if (mSortColumns.size ()) if (mSortColumns.size ())
sort (order); sort (order);
// If a non-zero maximum is specified, then it limits the number of rows of // If a non-zero maxrows is specified, then it limits the number of rows of
// the table that are rendered. // the table that are rendered.
int limit = mRows; int limitrows = mRows;
if (maximum != 0) if (maxrows != 0)
limit = min (maximum, mRows); limitrows = min (maxrows, mRows);
// If a non-zero maxlines is specified, then it limits the number of lines
// of output from the table that are rendered.
// Print all rows. // Print all rows.
for (int row = 0; row < limit; ++row) for (int row = 0; row < limitrows; ++row)
{ {
std::vector <std::vector <std::string> > columns; std::vector <std::vector <std::string> > columns;
std::vector <std::string> blanks; std::vector <std::string> blanks;
@ -1105,6 +1115,11 @@ const std::string Table::render (int maximum /* = 0 */)
// Trim right. // Trim right.
output.erase (output.find_last_not_of (" ") + 1); output.erase (output.find_last_not_of (" ") + 1);
output += "\n"; output += "\n";
++renderedlines;
if (maxlines != 0 && renderedlines >= maxlines)
break;
} }
} }
else else
@ -1113,6 +1128,9 @@ const std::string Table::render (int maximum /* = 0 */)
output.erase (output.find_last_not_of (" ") + 1); output.erase (output.find_last_not_of (" ") + 1);
output += "\n"; output += "\n";
} }
if (maxlines != 0 && renderedlines >= maxlines)
break;
} }
optimize (output); optimize (output);

View file

@ -92,7 +92,7 @@ public:
int rowCount (); int rowCount ();
int columnCount (); int columnCount ();
const std::string render (int maximum = 0); const std::string render (int maxrows = 0, int maxlines = 0);
private: private:
std::string getCell (const int, const int); std::string getCell (const int, const int);

View file

@ -647,7 +647,7 @@ int handleShow (std::string &outs)
"next project shadow.command shadow.file shadow.notify weekstart editor " "next project shadow.command shadow.file shadow.notify weekstart editor "
"import.synonym.id import.synonym.uuid complete.all.projects " "import.synonym.id import.synonym.uuid complete.all.projects "
"complete.all.tags search.case.sensitive hooks active.indicator tag.indicator " "complete.all.tags search.case.sensitive hooks active.indicator tag.indicator "
"recurrence.indicator recurrence.limit " "recurrence.indicator recurrence.limit list.all.projects list.all.tags "
#ifdef FEATURE_SHELL #ifdef FEATURE_SHELL
"shell.prompt " "shell.prompt "
#endif #endif
@ -735,7 +735,7 @@ int handleShow (std::string &outs)
out << std::endl out << std::endl
<< table.render () << table.render ()
<< (table.rowCount () == 0 ? "No matching configuration variables\n" : "") << (table.rowCount () == 0 ? "No matching configuration variables\n" : "")
<< std::endl; << std::endl;
// Display the unrecognized variables. // Display the unrecognized variables.

View file

@ -715,23 +715,37 @@ int runCustomReport (
table.setTableAlternateColor (alternate); table.setTableAlternateColor (alternate);
} }
// Limit the number of rows according to the report definition. // Report output can be limited by rows or lines.
int maximum = context.config.getInteger (std::string ("report.") + report + ".limit"); int maxrows = 0;
int maxlines = 0;
getLimits (report, maxrows, maxlines);
// If the custom report has a defined limit, then allow a numeric override. // Adjust for fluff in the output.
// This is an integer specified as a filter (limit:10). if (maxlines)
if (context.task.has ("limit")) maxlines -= (context.config.getBoolean ("blanklines") ? 2 : 0)
maximum = atoi (context.task.get ("limit").c_str ()); + 1
+ context.headers.size ()
+ context.footnotes.size ();
std::stringstream out; std::stringstream out;
if (table.rowCount ()) if (table.rowCount ())
{
out << optionalBlankLine () out << optionalBlankLine ()
<< table.render (maximum) << table.render (maxrows, maxlines)
<< optionalBlankLine () << optionalBlankLine ()
<< table.rowCount () << table.rowCount ()
<< (table.rowCount () == 1 ? " task" : " tasks") << (table.rowCount () == 1 ? " task" : " tasks");
<< std::endl;
else { if (maxrows)
out << ", " << maxrows << " shown";
if (maxlines)
out << ", truncated to " << maxlines-1 << " lines";
out << std::endl;
}
else
{
out << "No matches." out << "No matches."
<< std::endl; << std::endl;
rc = 1; rc = 1;
@ -813,3 +827,44 @@ void validSortColumns (
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// A value of zero mean unlimited.
// A value of 'page' means however many screen lines there are.
// A value of a positive integer is a row limit.
void getLimits (const std::string& report, int& rows, int& lines)
{
rows = 0;
lines = 0;
int screenheight = 0;
// If a report has a stated limit, use it.
if (report != "")
{
std::string name = "report." + report + ".limit";
if (context.config.get (name) == "page")
lines = screenheight = context.getHeight ();
else
rows = context.config.getInteger (name);
}
// If the custom report has a defined limit, then allow a numeric override.
// This is an integer specified as a filter (limit:10).
if (context.task.has ("limit"))
{
if (context.task.get ("limit") == "page")
{
if (screenheight == 0)
screenheight = context.getHeight ();
rows = 0;
lines = screenheight;
}
else
{
rows = atoi (context.task.get ("limit").c_str ());
lines = 0;
}
}
}
////////////////////////////////////////////////////////////////////////////////

View file

@ -167,3 +167,36 @@ int Context::getWidth ()
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
int Context::getHeight ()
{
// Determine window size, and set table accordingly.
int height = 25; // TODO Is there a better number?
#ifdef HAVE_LIBNCURSES
if (config.getBoolean ("curses"))
{
#ifdef FEATURE_NCURSES_COLS
initscr ();
height = LINES;
#else
WINDOW* w = initscr ();
height = w->_maxy + 1;
#endif
endwin ();
std::stringstream out;
out << "Context::getHeight: ncurses determined height of " << height << " characters";
debug (out.str ());
}
else
debug ("Context::getHeight: ncurses available but disabled.");
#else
std::stringstream out;
out << "Context::getHeight: no ncurses, using height of " << height << " characters";
debug (out.str ());
#endif
return height;
}
////////////////////////////////////////////////////////////////////////////////

View file

@ -117,6 +117,7 @@ int runCustomReport (const std::string&, const std::string&,
std::string&); std::string&);
void validReportColumns (const std::vector <std::string>&); void validReportColumns (const std::vector <std::string>&);
void validSortColumns (const std::vector <std::string>&, const std::vector <std::string>&); void validSortColumns (const std::vector <std::string>&, const std::vector <std::string>&);
void getLimits (const std::string&, int&, int&);
// rules.cpp // rules.cpp
void initializeColorRules (); void initializeColorRules ();

View file

@ -283,7 +283,7 @@ int longUsage (std::string &outs)
<< " until: Recurrence end date" << "\n" << " until: Recurrence end date" << "\n"
<< " fg: Foreground color" << "\n" << " fg: Foreground color" << "\n"
<< " bg: Background color" << "\n" << " bg: Background color" << "\n"
<< " limit: Desired number of rows in report" << "\n" << " limit: Desired number of rows in report, or 'page'" << "\n"
<< " wait: Date until task becomes pending" << "\n" << " wait: Date until task becomes pending" << "\n"
<< "\n" << "\n"
<< "Attribute modifiers improve filters. Supported modifiers are:" << "\n" << "Attribute modifiers improve filters. Supported modifiers are:" << "\n"
@ -2318,6 +2318,7 @@ int handleReportCalendar (std::string &outs)
holTable.addCell (row, 1, holName); holTable.addCell (row, 1, holName);
} }
} }
out << optionalBlankLine () out << optionalBlankLine ()
<< holTable.render () << holTable.render ()
<< std::endl; << std::endl;

96
src/tests/limit.t Executable file
View file

@ -0,0 +1,96 @@
#! /usr/bin/perl
################################################################################
## task - a command line task list manager.
##
## Copyright 2006 - 2010, Paul Beckingham.
## 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
##
################################################################################
use strict;
use warnings;
use Test::More tests => 8;
# Create the rc file.
if (open my $fh, '>', 'limit.rc')
{
print $fh "data.location=.\n";
close $fh;
ok (-r 'limit.rc', 'Created limit.rc');
}
# Add a large number of tasks (> 25).
qx{../task rc:limit.rc add one};
qx{../task rc:limit.rc add two};
qx{../task rc:limit.rc add three};
qx{../task rc:limit.rc add four};
qx{../task rc:limit.rc add five};
qx{../task rc:limit.rc add six};
qx{../task rc:limit.rc add seven};
qx{../task rc:limit.rc add eight};
qx{../task rc:limit.rc add nine};
qx{../task rc:limit.rc add ten};
qx{../task rc:limit.rc add eleven};
qx{../task rc:limit.rc add twelve};
qx{../task rc:limit.rc add thirteen};
qx{../task rc:limit.rc add fourteen};
qx{../task rc:limit.rc add fifteen};
qx{../task rc:limit.rc add sixteen};
qx{../task rc:limit.rc add seventeen};
qx{../task rc:limit.rc add eighteen};
qx{../task rc:limit.rc add nineteen};
qx{../task rc:limit.rc add twenty};
qx{../task rc:limit.rc add twenty one};
qx{../task rc:limit.rc add twenty two};
qx{../task rc:limit.rc add twenty three};
qx{../task rc:limit.rc add twenty four};
qx{../task rc:limit.rc add twenty five};
qx{../task rc:limit.rc add twenty six};
qx{../task rc:limit.rc add twenty seven};
qx{../task rc:limit.rc add twenty eight};
qx{../task rc:limit.rc add twenty nine};
qx{../task rc:limit.rc add thirty};
my $output = qx{../task rc:limit.rc ls};
like ($output, qr/^30 tasks$/ms, 'unlimited');
$output = qx{../task rc:limit.rc ls limit:0};
like ($output, qr/^30 tasks$/ms, 'limited to 0 - unlimited');
$output = qx{../task rc:limit.rc ls limit:3};
like ($output, qr/^30 tasks, 3 shown$/ms, 'limited to 3');
$output = qx{../task rc:limit.rc ls limit:page};
like ($output, qr/^30 tasks, truncated to 20 lines$/ms, 'limited to page');
# Cleanup.
unlink 'pending.data';
ok (!-r 'pending.data', 'Removed pending.data');
unlink 'undo.data';
ok (!-r 'undo.data', 'Removed undo.data');
unlink 'limit.rc';
ok (!-r 'limit.rc', 'Removed limit.rc');
exit 0;