Compare commits

...

454 Commits

Author SHA1 Message Date
Federico Hernandez
499044b9b6 Version number and release date for 2.4.3 2015-04-19 23:47:18 +02:00
Wilhelm Schuermann
f66c706370 Portability: FreeBSD 9.3 x86 needs limits.h 2015-04-18 11:38:01 +02:00
Paul Beckingham
e3e72e32f1 Build: More changes to address 32-bit builds 2015-04-18 10:26:37 +01:00
Paul Beckingham
39fb45447b Lexer: Migrated ispunct to Lexer::isPunctuation 2015-04-16 23:52:43 -04:00
Paul Beckingham
3cbb2bb20f Lexer: Migrated isdigit to Lexer::isDigit 2015-04-16 23:33:25 -04:00
Paul Beckingham
c6dbdf87a4 Lexer: Migrated isalpha to Lexer::isAlpha 2015-04-16 23:24:17 -04:00
Paul Beckingham
fe8d235a6b UTF8: Moved mk_wcwidth definition to utf8.h 2015-04-16 23:11:17 -04:00
Paul Beckingham
1371b6595e Build: Disambiguated Variant instantiation that causes 32-bit problems 2015-04-16 22:53:38 -04:00
Paul Beckingham
21407e0ca2 TW-1595: info command doesn't print urgency details, if urgency is negative
- Thanks to Peter Rochen.
2015-04-12 10:13:15 -04:00
Paul Beckingham
c0efa0f4d7 L10N: Removed unused strings, add string missing from fra-FRA.h 2015-04-06 20:19:25 -04:00
Paul Beckingham
236e7898b9 Vim: Modified 'priority' syntax elements. 2015-04-06 20:02:33 -04:00
Paul Beckingham
33738af3e4 Show: Removed obsolete settings. 2015-04-06 19:59:54 -04:00
Fidel Mato
f684917c3c L10N esp-ESP
- Added two missing strings and its translation.
- Order changed to follow eng-USA.h
2015-04-06 19:54:44 -04:00
Fidel Mato
876a7d29b9 L10N esp-ESP
- Newly translated string from eng-USA to esp-ESP, and some corrections.
2015-04-06 19:54:38 -04:00
Paul Beckingham
119c19b519 TW-1591: add and option to see non-pending project with command task summary
- Setting 'summary.all.projects' shows all projects, not just those with pending
  tasks.
2015-04-06 15:47:02 -04:00
Paul Beckingham
935b2993f3 C++11: Took advantage of N1757. 2015-04-06 15:30:39 -04:00
Renato Alves
afec6d451f Tests: Force stopping taskd on 5mins timeout 2015-04-06 13:46:08 +01:00
Renato Alves
fd17a68930 Tests: expose 'args' passed to hook scripts
- Useful to validate that all the expected arguments were passed with
  correct escaping.
- Update test/template.t with one use-case for 'args'
2015-04-06 13:43:41 +01:00
Renato Alves
09d86eb165 Tests: Better diagnostics on taskd start failure. 2015-04-06 13:08:17 +01:00
Wilhelm Schuermann
8d5a77f490 Tests: Adjust wrapper to make hook env test work 2015-04-06 10:01:09 +02:00
Paul Beckingham
e9b54ea74f TW-1556: task hangs when modifying uda field with percent-encoded (url-encoded)
- This bug іs fixed by the new Lexer, which has allows unrecognized lexemes
  to fall through to becomre 'word' or 'dom' types (thanks to Stefan Frühwirth).
2015-04-05 23:45:27 -04:00
Paul Beckingham
3a035a7d1d TW-1590: syntax of rcfile not documented (whitespace, line continuation)
- Updated top of the taskrc(5) man page with a description of the file syntax.
  (thanks to Scott M).
2015-04-05 23:37:43 -04:00
Paul Beckingham
d405a5f3b5 Tests: Changed hook test to use a command that actually produces output 2015-04-05 16:14:20 -04:00
Paul Beckingham
0cba34268c Build: Fixed broken compile 2015-04-05 16:08:33 -04:00
Paul Beckingham
61e1401073 Performance: Only measures the first fixed-width column row 2015-04-05 16:05:43 -04:00
Paul Beckingham
a883c5ca41 Themes: Updated the built-in default theme regarding priority 2015-04-05 15:17:33 -04:00
Paul Beckingham
d98b6e5c0b Tests: Converted priority urgency coefficients to UDAs 2015-04-05 14:58:07 -04:00
Paul Beckingham
6b0d288da7 Themes: Converted color rules for 'priority'. 2015-04-05 14:09:40 -04:00
Paul Beckingham
e8654c49b5 Tests: Modified tests to accomodate new 'prioirity' color rules 2015-04-05 14:05:29 -04:00
Paul Beckingham
c28932ebbe Tests: Removed diag output from test. Still doens't work. 2015-04-05 13:29:00 -04:00
Paul Beckingham
826769bcd9 Tests: Removed unused code 2015-04-05 13:21:20 -04:00
Paul Beckingham
d31227d2e2 Tests: Add testing for hook args. Doesn't work. 2015-04-05 13:04:51 -04:00
Paul Beckingham
0a54d46589 Hooks: With rc.debug.hooks=2, shows hook arguments 2015-04-05 12:19:55 -04:00
Paul Beckingham
3f457dc744 Info: Don't print an urgency detail table is the value is zero 2015-04-05 12:00:08 -04:00
Paul Beckingham
f9c99624b2 Info: Properly breaks out urgency values for UDA coefficients 2015-04-05 11:56:55 -04:00
Paul Beckingham
b19b0f47e5 TW-1541: Priority should be converted to UDA (in default taskrc)
- The 'priority' core attribute is removed, replaced by a UDA configured to
  behave in the same manner.
2015-04-05 10:41:23 -04:00
Paul Beckingham
fbf9a8be8d TW-1539: user-defined urgency coefficients for priority values
- The 'urgency.uda.<uda>.coefficient', and 'urgency.uda.<uda>.<value>.coefficient'
  settings allow the setting of an urgency coefficient for the presence of data,
  or a specific value (takes precedence).
2015-04-05 10:38:13 -04:00
Paul Beckingham
9476069b41 TW-111 User-defined priorities
- The 'priority' attribute is no longer a core attribute, and is instead defined
  by default as a UDA that behaves in the same way. It is therefore
  reconfigurable with defined values, sort order, and urgency coefficients.
2015-04-05 10:35:21 -04:00
Paul Beckingham
35b77f93c4 TW-57: user defined attribute sort order
- When a UDA of type 'string' specifies allowable values, that list is used to
  indicate the sort order, from high to low (thanks to Max Muller).
2015-04-05 10:33:36 -04:00
Paul Beckingham
5da3416b79 TW-70: urgency.user.keyword.<keyword>.coefficient=...
- The 'urgency.user.keyword.<keywrod>.coefficient' setting allows for urgency
  adjustments based on description keywords.
2015-04-05 10:25:42 -04:00
Paul Beckingham
79207a8e42 Info: A '+' symbol was shown instead of a '*', for urgency details 2015-04-05 10:22:56 -04:00
Paul Beckingham
5af4579741 Priority: Converted 'priority' attribute to a UDA
- 'priority.long' and 'priority.short' formats now map to 'priority', and
  generate a warning.
- Man page updated.
- Converted priority urgency coefficients to UDA equivalents.
- Converted priority color rules to UDA color rules.
- Removed 'default.priority' support.
- Removed special sort handling for 'priority' field in Variant, added special
  UDA sort handling.
- Removed ColPriority.{h,cpp} source files.
- Removed asorted newly-unused variables.
- The 'show' command now highlights unused priority settings.
- Removed unused localized priority-relateѕ strings.
- Added legacy mapping for columns and sort columns for 'priority.long' and
  'priority.short' columns in report definitions.
- Removed priority color rules implementation.
- Removed obsolete tests for #860, #990, custom.priority_long.t.
- Updated various tests that set priority default, colors.
2015-04-04 19:30:33 -04:00
Paul Beckingham
7a9d314016 Variant: Temporarily put back the priority string handling
- In order to test that the UDA sorting is good, the priority processing must
  be in place, as many test rely on priority values. Will be removed later.
2015-04-04 16:16:51 -04:00
Paul Beckingham
07a18f4fae Tests: UDA sort tests 2015-04-04 15:48:01 -04:00
Paul Beckingham
29a09707f3 Variant: Implemented custom UDA sorting 2015-04-04 15:47:41 -04:00
Paul Beckingham
743cb92958 Context: Static initialize UDA sort order values 2015-04-04 14:50:38 -04:00
Paul Beckingham
370afa0d26 Sort: Delegate UDA string sorting to Variant::operator< 2015-04-04 12:53:06 -04:00
Paul Beckingham
8c0f46309b Variant: Return const refs rather than string copies 2015-04-04 12:51:56 -04:00
Paul Beckingham
fa7d4352cd Sort: All attributes are now sortable 2015-04-04 11:15:43 -04:00
Paul Beckingham
f1ef53bea0 Column: Return const refs rather than string copies 2015-04-04 10:58:55 -04:00
Paul Beckingham
21dc2ec100 UTF8: Moved variable into lower scope 2015-04-04 09:55:54 -04:00
Wilhelm Schuermann
5b01abc27f Fix problem with special chars in descriptions
Fix 'task add a; task add "\n"; task next' returning "Unknown error."
The problem can be reproduced with any report including "description"
as a column.

For task descriptions that had more special characters than regular
ones, "length" in utf8_width() underflowed, leading to problems
elsewhere in the code.
2015-04-03 22:22:44 +02:00
Wilhelm Schuermann
c830b4b669 TW-1287: default.* values applied to synced tasks
- Tasks that arrive through "task sync" no longer have defaults applied
  to them.
2015-04-02 13:39:20 +02:00
Wilhelm Schuermann
70d5f595c7 TW-1584: attr.{isnt,not} use partial matching
- Change attr.{isnt,not} to be the exact opposite of attr{:is,:},
  i.e. exact matching.
- Fix tests that assumed the old behavior was intended.
2015-04-02 13:38:04 +02:00
Wilhelm Schuermann
c1815b8277 TW-1279: default.* applies to recurring tasks
- Recurring tasks inherit attributes from their parent.  default.*
  is no longer applied to them.
2015-04-02 13:37:59 +02:00
Wilhelm Schuermann
2eea7805c6 Lexer: Fix "task calc now+3days" error
- Fix isDuration() consuming operators, leading to evaluation errors.
  Previously only "task calc now + 3days" worked, now the spaces are
  entirely optional.
2015-04-02 13:29:57 +02:00
Wilhelm Schuermann
5487414003 Hooks: Add "version:" to hooks v2 arguments
- Saves a call to "task --version", potentially saving precious hook
  execution time.  Useful for writing backwards compatible hooks in
  case of buggy Taskwarrior behavior.
2015-04-02 09:18:34 +02:00
Wilhelm Schuermann
229078ffed Scripts: Disable hooks in bash completion script
- Stop on-launch and on-exit hooks with output from interfering with
  bash completion.
2015-04-01 16:23:42 +02:00
Wilhelm Schuermann
04d0c52a43 Hooks: Implement "Hooks v2" API
- All hook scripts now receive key:value pair command line arguments.
- Add only the basics: api, args, command, rc, data.  Possibly more to
  come.
2015-04-01 11:44:37 +02:00
Wilhelm Schuermann
5d4859c44d Tests: Fix bash_completion.t problem
- bash_completion.t rewrites task.sh during setup in order to be able
  to test it.  704eb1eab3 broke that
  rewrite mechanism.
2015-03-31 11:28:12 +02:00
Wilhelm Schuermann
3f394fa164 TW-1588: Fix broken export scripts 2015-03-31 07:26:10 +02:00
Wilhelm Schuermann
df209b9b8b TW-1587: Further improve example on-exit hook
- Initial patch was not quite right.  on-exit _does_ receive added and
  modified tasks on STDIN, but is not allowed to return JSON.
2015-03-31 07:25:23 +02:00
Jochen Sprickerhof
c27f5d23b6 Hooks: Remove read loop from example on-exit hook
There is no input to the on-exit script. Prevents error:
"Hook Error: Expected 0 JSON task(s), found 1"

Signed-off-by: Wilhelm Schuermann <wimschuermann@googlemail.com>
2015-03-30 21:38:25 +02:00
Wilhelm Schuermann
df3f8ead11 TW-1579: Add INSTALL note for OpenBSD 2015-03-30 21:17:36 +02:00
Wilhelm Schuermann
bc335e8075 Add-Ons: Fix export-html.pl for new export format 2015-03-30 09:41:45 +02:00
Paul Beckingham
e0f3e6d328 Tests: Corrected test that assumes lines are longer than they are 2015-03-29 23:52:34 -04:00
Paul Beckingham
eb35386d76 Tests: Recategorized lexemes that are only dates in the presence of Eval 2015-03-29 23:14:59 -04:00
Paul Beckingham
96c448ca1e Lexer: Corrected off-by-one error in ::isTripleCharOperator 2015-03-29 23:07:18 -04:00
Paul Beckingham
71fea510bb Tests: Corrected lexer test "\"" 2015-03-29 23:02:59 -04:00
Paul Beckingham
1e411ed4b8 Tests: Corrected lexer text '\'' 2015-03-29 23:01:33 -04:00
Paul Beckingham
cfbdd4bc05 Tests: Corrected lexer test "\"three\"" 2015-03-29 22:59:27 -04:00
Paul Beckingham
5af3f71ac5 Lexer: Fixed /from/to/g detection at EOS 2015-03-29 22:56:34 -04:00
Paul Beckingham
8b863c9764 Lexer: Fixed /pattern/ detection at EOS 2015-03-29 22:53:38 -04:00
Paul Beckingham
9630b76990 Build: Resolve warnings from flod build 2015-03-29 18:21:33 -04:00
Paul Beckingham
690d9493f0 Build: Resolve some more compiler warnings 2015-03-29 18:03:36 -04:00
Paul Beckingham
73f4f55e0a Merge branch '2.4.3' of ssh://git.tasktools.org/tm/task into 2.4.3 2015-03-29 17:30:06 -04:00
Paul Beckingham
267f054449 Duration: Some units were not marked as standalone 2015-03-29 17:25:08 -04:00
Wilhelm Schuermann
7a64c19641 Resolve most compiler warnings 2015-03-29 22:05:38 +02:00
Wilhelm Schuermann
b8105812fc CMake: Enable compiler warnings 2015-03-29 22:04:48 +02:00
Paul Beckingham
f454a02224 Tests: Added more Lexer::Type::date tests 2015-03-28 16:57:34 -04:00
Paul Beckingham
1bbe709e38 Tests: Removed redundant LExer::Type::tag tests 2015-03-28 16:53:32 -04:00
Paul Beckingham
a4b96a3191 Tests: Added Lexer::Type::op tests, a full set 2015-03-28 16:49:57 -04:00
Paul Beckingham
ddb6014358 Tests: Converted Lexer::Type::duration tests 2015-03-28 16:38:17 -04:00
Paul Beckingham
31a7a3343b Tests: Converted ordinal tests 2015-03-28 16:26:50 -04:00
Paul Beckingham
3a0971f290 Tests: Added Lexer::Type::tag test for +@tag 2015-03-28 16:23:02 -04:00
Paul Beckingham
fee58b0eb6 Tests: Added Lexer::Type::operator tests, removed old ones 2015-03-28 16:21:25 -04:00
Paul Beckingham
9adf3fc0fa Tests: Added Lexer compound token expression tests 2015-03-28 12:05:45 -04:00
Paul Beckingham
39d330631d Tests: Added Lexer::Type::date tests 2015-03-28 12:05:17 -04:00
Paul Beckingham
b41d7c4582 Tests: Added Lexer::Type::duration tests 2015-03-28 11:40:31 -04:00
Paul Beckingham
12eca4b2cc Tests: Added Lexer::Type::op tests 2015-03-28 11:38:03 -04:00
Paul Beckingham
f26f790e74 Tests: Added Lexer::Type::separator test 2015-03-28 11:33:57 -04:00
Paul Beckingham
7ac0a919aa Tests: Added Lexer::Type::uuid tests 2015-03-28 11:32:17 -04:00
Paul Beckingham
e9e91ce55e Tests: Added Lexer::type::pair tests 2015-03-28 11:05:19 -04:00
Paul Beckingham
880fb5bcc6 Tests: Added Lexer::Type::number and Lexer::Type::hex tests 2015-03-28 10:58:27 -04:00
Paul Beckingham
84eb75c705 Tests: Added Lexer::Type::string tests 2015-03-28 10:45:19 -04:00
Paul Beckingham
f697e4df73 Tests: Added Lexer::Type::url tests 2015-03-28 10:26:14 -04:00
Paul Beckingham
0eaa061efe Tests: Added more Lexer::Type::dom tests 2015-03-28 10:19:54 -04:00
Paul Beckingham
b02d518b02 Tests: Lexer::Type::dom and Lexer::Type::path tests 2015-03-28 10:06:56 -04:00
Paul Beckingham
0973e6566b Tests: Added Lexer::Type::tag tests 2015-03-28 09:41:04 -04:00
Paul Beckingham
d0a9e9a253 Tests: Added Lexer::Type::substitution tests 2015-03-28 09:32:36 -04:00
Paul Beckingham
ff445c567e Unit Tests: Lexer now tests embedded and isolated tokens
- The tests are now defined in a static structure, simplifying maintenance of
  these tests.
- Instead of simply lexing large strings and checking the lexemes, the tests
  now take a single token and test it as-is ("token"), and surrounded by spaces
  (" token "), which yields surprising results.
2015-03-28 09:22:33 -04:00
Paul Beckingham
254b1eb49c Merge branch '2.4.3' of ssh://git.tasktools.org/tm/task into 2.4.3 2015-03-28 08:28:56 -04:00
Paul Beckingham
15005afd1e Unit Tests: Added tests for Lexer::Type::pattern 2015-03-28 08:24:49 -04:00
Wilhelm Schuermann
9e6c6ecb93 TW-1583: Invalid ID displayed after done/delete
- Fix completed/deleted tasks getting an ID when GC is going to be run,
  previously resulting in invalid IDs being displayed in reports that
  show non-pending tasks.

A side effect of this fix is that it is sometimes not possible to
filter by ID when running a report right after calling done/delete.
This problem existed before; this change makes it happen on the first
report instead of the second, so it is more consistently broken.
Commands that modify tasks are not affected, making this an annoying
yet harmless defect.
2015-03-28 10:36:58 +01:00
Tomas Babej
20eaa312e6 localization: Fix inconsisitent case in the legend of History commands 2015-03-27 19:23:22 -04:00
Renato Alves
fdb22ad341 Tests: Merge bug.299 with project.t and add more scenarios
* Currently one of the new scenarios fails. Looks like a bug.
2015-03-27 11:15:53 +00:00
Renato Alves
d76d5c3587 Tests: Make sleeptime in wait_conditional an argument 2015-03-27 10:53:18 +00:00
Renato Alves
526665d4ec Tests: Configurable check for taskd readyness
* Also increase the frequency from 1/s to 2/s
2015-03-27 10:51:38 +00:00
Renato Alves
834b4ddab6 Tests: Use faketime instead of time.sleep. 2015-03-27 10:14:44 +00:00
Renato Alves
357850177d Tests: common/reusable variables live in basetest.utils 2015-03-27 10:14:44 +00:00
Wilhelm Schuermann
d2e6c90446 TW-1581: Wrong urgency after done/delete command
- TDB2::dependency_scan() is run before GC, so we need to check both
  tasks in a dependency chain for their status before setting the
  blocking/blocked flag to true.
2015-03-26 18:43:58 +01:00
Paul Beckingham
96922231b8 Merge branch '2.4.3' into lexer2 2015-03-25 07:52:36 -04:00
Wilhelm Schuermann
55db1239bd Info: Hide "modified" attribute changes
- The info report shows modifications already.  "modified" attribute
  updates are implied by other attributes being changed, so there
  is no need to clutter the report with them.
2015-03-23 16:28:53 +01:00
Wilhelm Schuermann
4f75652ccb TW-1580: "modified" attribute not updated
- Make all local modifications update the "modified" attribute.
- As per design, the user cannot overwrite this attribute; neither
  can hooks.
2015-03-23 15:55:47 +01:00
Paul Beckingham
052a5c607a Documentation: 'rc.bulk=0' notes.
- Added ChangeLog and NEWS entries for the rc.bulk=0 change, where zero is
  considered infinity.
2015-03-22 11:43:00 -04:00
Tomas Babej
c306d458e0 Doc: Document bulk=0 in taskrc manpage 2015-03-22 11:41:28 -04:00
Tomas Babej
7babc9c5b1 Command: Let zero bulk denote infinite value 2015-03-22 10:10:08 -04:00
Paul Beckingham
24a1cbefe9 Merge branch '2.4.3' into lexer2 2015-03-20 17:16:48 -04:00
Paul Beckingham
289780c8cc Lexer: Added comments 2015-03-20 17:16:00 -04:00
Tomas Babej
f5af3368a9 Changelog: Record TW-1578 in the changelog 2015-03-19 21:51:09 -04:00
Tomas Babej
a2ed996102 Man: Add task calc command to the task manpage 2015-03-18 14:15:15 -04:00
Tomas Babej
704eb1eab3 scripts: Enforce disabled confirmation in bash completion
https://bug.tasktools.org/browse/TW-1578
2015-03-18 14:14:59 -04:00
Tomas Babej
484979b4e0 Man: Fix typo in allow.empty.filter description 2015-03-18 14:14:32 -04:00
Paul Beckingham
928e94a6e1 Documentation
- Updated for 2.4.3.
2015-03-15 20:19:29 -04:00
Federico Hernandez
e2c9752bc4 Bumped version number in documentation 2015-03-16 00:38:56 +01:00
Federico Hernandez
8386b702dd Bumped version number to 2.4.3 2015-03-16 00:25:55 +01:00
Federico Hernandez
48e9c0518e Added SHA1 of tagged release commit 2015-03-16 00:19:25 +01:00
Federico Hernandez
b9dc0813d9 Version number and release date for 2.4.2 2015-03-16 00:01:52 +01:00
Paul Beckingham
25c687e1ec Themes
- Updated default theme to match dark-16 or dark-256.
2015-03-15 12:16:40 -04:00
Paul Beckingham
6e98bbbd85 Themes
- Consistency tweaks across all themes.
2015-03-15 12:06:02 -04:00
Paul Beckingham
06c8976be5 Themes
- Changed color.error to white on red.
- Changed color.debug to blue.
- Made color.active more yellow.
- Added color.scheduled.
- Changed due, due.today and overdue to be more yellow.
- Changed color.summary.background to match theme.
- Changed color.undo to match theme.
2015-03-15 11:55:32 -04:00
Paul Beckingham
d13a5a3f47 Themes
- Changed color.error to white on red.
- Changed color.debug to blue.
- Added color.scheduled.
- Changed color.due, color.overdue to match theme.
- Added color.summary.background.
2015-03-15 11:46:00 -04:00
Paul Beckingham
b662afa1ff Themes
- Removed color.completed, color.deleted.
- Darkened color.alternate.
- Changed color.debug to blue.
- Changed color.error to white on red.
- Added fg to color.scheduled.
- Changed color.tag.next to match theme.
- Changed color.undo to match theme.
2015-03-15 11:37:57 -04:00
Paul Beckingham
bde83d3195 Themes
- Darkened color.alternate.
- Changed color.debug to blue.
- Changed color.error to white on red.
- Changed color.warning to match theme.
- Removed color.completed, color.deleted.
- Added fg to color.scheduled.
- Added color.summary.background.
- Darkened color.undo.before.
2015-03-15 11:32:51 -04:00
Paul Beckingham
0edb7e57f6 Themes
- Changed color.debug to blue.
- Change color.error to white on red.
- Added fg to burndown and history.
- Added color.summary.bckground.
- Changed undo colors to match theme.
2015-03-15 11:27:55 -04:00
Paul Beckingham
4569d2da24 Themes
- Changed color.debug to a common blue.
- Changed color.error to white on red.
- Added fg to color.calendar.due.
- Fixed typo for color.synd.added.
2015-03-15 11:21:13 -04:00
Paul Beckingham
6b00f08794 Themes
- Removed color.completed, color.deleted.
- Made color.scheduled more blue than green.
- Made summary light on dark blue.
2015-03-15 11:16:14 -04:00
Paul Beckingham
e83809c6aa Themes
- Lightened color.alternate.
- Changed color.debug to a common blue.
2015-03-15 11:12:26 -04:00
Paul Beckingham
39b736d23e Themes
- Changed color.debug to a readable color.
- Added fg to color.scheduled.
- Removed color.completed, color.deleted.
2015-03-15 11:08:34 -04:00
Paul Beckingham
3e90554f40 Themes
- Removed background color from debug.
- Removed color.completed, color.deleted.
- Darkened color.active, for contrast.
- Removed color.pri.none, which was the same as color.pri.M.
- Inverted color.calendar.holiday.
- Changed burndown and history to use red, yellow, green.

+color.history.delete=color0 on rgb550

 # Report: summary
 color.summary.bar=on rgb141
@@ -82,7 +82,7 @@ color.summary.background=on gray20
 # Command: calendar
 color.calendar.due=on rgb343
 color.calendar.due.today=on rgb353
-color.calendar.holiday=rgb420
+color.calendar.holiday=color0 on rgb530
 color.calendar.overdue=on rgb533
 color.calendar.today=rgb005
 color.calendar.weekend=on gray21
2015-03-15 11:02:27 -04:00
Paul Beckingham
4690b1a6cb Themes
- Removed background color from debug messages.
- Removed color.completed and color.deleted.
- Swapped the overdue magenta for red.
- Changed burndown and history to use red, yellow, green.
- Inverted the calendar holiday colors.
- Removed background from color.calendar.weeknumber.
2015-03-15 10:52:28 -04:00
Paul Beckingham
ce1a7b921c Themes
- Removed all the 16-color names, which do not apply to the solarized palette.
- Modified color.blocking and color.blocked so that both fg and bg are specified.
- Modified color.burndown.* and color.history.* to use red, yellow, green.
2015-03-15 10:40:11 -04:00
Paul Beckingham
7a1645bba3 Themes
- Fixed color.burndown.done and color.burndown.started, which were two different
  shades of red.
- Fixed color.history.done, which was dark gray and should be closer to green.
2015-03-15 10:13:08 -04:00
Paul Beckingham
9f1b3e0d56 Themes
- Added run.default to test the default theme.
2015-03-15 10:08:47 -04:00
Paul Beckingham
0af8b1dbab Themes
- Added 'task color legend' to the scripts to make it easier to spot broken
  themes.
- Removed the 'run' script, which is no longer used.
2015-03-15 09:09:54 -04:00
Tomas Babej
1465bcb918 Context: Do not apply context for the export command 2015-03-14 17:28:20 -04:00
Paul Beckingham
4eb70e68b7 TW-63
- TW-63 indicators for UDAs (thanks to Datvid Patrick).
- Note that this was implemented in 2.4.0 and tracked via the duplicate
  issue TW-161.
2015-03-14 11:57:22 -04:00
Renato Alves
cd216bd4fd simpletap - Add color to test state when running on tty 2015-03-12 20:49:50 +00:00
Renato Alves
ac4a5c6d62 simpletap - Include scenario for expected failures
* Expected failures are treated as "ok" followed by a report of what went wrong
2015-03-12 20:00:02 +00:00
Renato Alves
a67593decf Tests - merge bug.1110.t with filter.t and convert to python 2015-03-12 19:34:58 +00:00
Renato Alves
7be294267b Tests - rename bug.1065.t to custom.config.t and convert to python 2015-03-11 15:32:54 +00:00
Renato Alves
920cdcca10 Tests - convert bug.1063.t to python 2015-03-11 15:32:54 +00:00
Paul Beckingham
7cc410f58e Documentation
- Fixed obsolete wiki reference in the main man page (thanks to David Patrick).
2015-03-11 08:46:05 -04:00
Paul Beckingham
5d42c0f6b6 Themes
- Updated the README for this version, not the previous one.
2015-03-10 19:17:17 -04:00
Renato Alves
cff680d074 Tests - merge bug.1056.t with project.t and convert to python 2015-03-10 19:18:55 +00:00
Renato Alves
dda190703e Tests - merge basic.t with version.t and convert to python
* _version now outputs "2.4.2 (git-ref)" instead of only "git-ref" when building from git.
* Changelog updated accordingly.
2015-03-09 16:02:26 +00:00
Wilhelm Schuermann
281edcc38f Unit Tests
- The Sith must never be allowed to regain their power over us.
2015-03-08 09:04:39 -04:00
Paul Beckingham
11dab68fce Unit Tests
- Longer test script names need wider padding.
- Added counts for skipped tests.
2015-03-07 17:55:42 -05:00
Paul Beckingham
29dff399bd Merge branch '2.4.2' into lexer2 2015-03-07 17:36:10 -05:00
Paul Beckingham
a75c293286 Merge branch '2.4.2' into lexer2 2015-03-07 17:33:23 -05:00
Paul Beckingham
dbf31bfce8 Unit Tests
- Added new cli.t to contain asorted command line tests.
2015-03-07 17:31:15 -05:00
Paul Beckingham
7826c7b988 CLI
- Added Lexer::Type arg to ::addArg, for future capture of type.
- Cleaned up code formatting from patch.
2015-03-07 17:29:08 -05:00
Paul Beckingham
87d00698db Unit Tests
- Successful hook tests now query the task to make sure processing continued.
  Could be better.
2015-03-07 16:18:44 -05:00
Paul Beckingham
19674ee339 Unit Tests
- Specifically set certain color rules to '' to override default theme.
2015-03-07 16:11:38 -05:00
Paul Beckingham
9f654a5c95 Unit Tests
- Test was wrong.
2015-03-07 16:06:18 -05:00
Paul Beckingham
87e9578666 Unit Tests
- Combined all the separate color rule tests into one script, and converted
  it to Python.
2015-03-07 13:29:59 -05:00
Paul Beckingham
e3ec52f6ca Unit Tests
- Cleaned up color.t.cpp.
2015-03-07 12:14:39 -05:00
Paul Beckingham
70c3d9845c Unіt Tests
- Merged width.t.cpp into utf8.t.cpp.
2015-03-07 11:59:37 -05:00
Paul Beckingham
9d1c4101c2 Unit Tests
- Renamed feature.exit.t to shell.t so that this test script is known to
  contain shell-interaction tests.
- Converted to Python.
2015-03-07 11:48:42 -05:00
Paul Beckingham
44ed974fa2 Unit Tests
- Fixed all the test descriptions.
2015-03-07 11:44:44 -05:00
Paul Beckingham
3cf7a701c8 Conversion
- Updated script for BSD.
2015-03-07 11:38:52 -05:00
Paul Beckingham
d390f99fc5 Unit Tests
- Simplified the msg.t.cpp tests.
2015-03-07 11:35:48 -05:00
Paul Beckingham
a2cae67644 Unit Tests
- Converted filter-prefix.t to Python.  Still needs a rename and possible merge
  with other tests.
2015-03-07 11:11:18 -05:00
Paul Beckingham
d202691638 Lexer
- Improved comment.
2015-03-07 10:09:58 -05:00
Paul Beckingham
c9d61ff71b Duration
- Made list of units static.
2015-03-06 21:15:50 -05:00
Renato Alves
ebd6977480 Tests - merge bug.1044 into project.t and convert to python 2015-03-06 16:44:28 +00:00
Renato Alves
276050ce0b Tests - rename bug.1043.t to completion.t and convert to python 2015-03-06 15:19:04 +00:00
Renato Alves
197815a3a7 Tests - convert bug.1036.t to python 2015-03-06 15:10:07 +00:00
Paul Beckingham
4118fe70ae Unit Test
- Added quotes, which is a reasonable workaround for a misinterpreted argument.
2015-03-05 21:29:38 -05:00
Paul Beckingham
e11b333a0b Lexer
- ::isPath was not observing the null character string terminator.
2015-03-05 21:22:40 -05:00
Paul Beckingham
5e8426f0cc Config
- The 'next' report was not sㄡrted by urgency, which is wrong (thanks
  to Adam Coddington).
2015-03-05 18:26:43 -05:00
Wilhelm Schuermann
138360b7bc Hooks
- Fixed on-modify hook regression which stopped hooks from modifying
  tasks.  On a related note, more hooks tests are definitely needed.
2015-03-05 07:21:30 -05:00
Renato Alves
7fa3c5ac84 Tests - convert bug.1031.t to python 2015-03-04 00:54:40 +00:00
Renato Alves
b5da4acab9 Tests - run_all script now exports TASKD_USE_PATH=1 2015-03-03 01:50:57 +00:00
Renato Alves
180c382de2 Tests - Finer control on which binaries to look for on PATH
* It is now possible to control whether taskw and/or taskd are looked up
on the PATH by setting TASK_USE_PATH/TASKD_USE_PATH to "1"
2015-03-03 01:46:53 +00:00
Renato Alves
23d4e2b3c9 Tests - rename bug.1023.t to feature.default.project.t and convert to python
Also add testcase from TW-1287 and TW-1279 which are part of the same feature
2015-03-03 01:25:45 +00:00
Paul Beckingham
d1f7e44811 Unit Tests
- Removed unit test for a bug that is a deliberate attempt to generate an
  ambiguous command line, to break the parser. The '--' terminator is meant
  to resolve these issues, and we don't need more tests proving that the
  grammar is ambiguous.
2015-03-02 16:44:28 -05:00
Paul Beckingham
96185cbd61 Merge branch '2.4.2' of ssh://git.tasktools.org/tm/task into 2.4.2 2015-03-02 16:17:09 -05:00
Paul Beckingham
7665d13d42 Duration
- Removed legacy duration value mapping, which causes much performance
  degradation.
2015-03-02 16:16:39 -05:00
Paul Beckingham
a40967a324 Unit Tests
- Fixed project.t errors.
2015-03-02 11:35:19 -05:00
Paul Beckingham
01feec568b Merge branch '2.4.2' of ssh://git.tasktools.org/tm/task into 2.4.2 2015-03-02 09:35:45 -05:00
Paul Beckingham
104aeb3905 TDB2
- Commented and reorganized for threading.
2015-03-02 09:33:57 -05:00
Paul Beckingham
53e9bd0cbd Lexer
- Words cannot contain single-char operators.
2015-03-02 00:03:01 -05:00
Paul Beckingham
234e4d7308 Unit Tests
- Modified Lexer types to reflect DOM recognition.
2015-03-01 23:55:10 -05:00
Paul Beckingham
237d932ff9 Lexer
- Improved ::isIdentifier, ::isUUID and ::isDOM.
2015-03-01 23:54:45 -05:00
Paul Beckingham
2af470bb90 Lexer
- Lexer::Type::identifier now includes DOM references.
2015-03-01 22:08:19 -05:00
Paul Beckingham
cefc129e9a Task
- Delegated the modifiability check to Column::modifiable.
2015-03-01 21:36:56 -05:00
Paul Beckingham
7aa55a8a71 Unit Tests
- Corrected test so it doesn't include '--' separator token.
2015-03-01 21:19:16 -05:00
Paul Beckingham
565987a177 Unit Tests
- Corrected token types for '3rd', which is Lexer::Type::word, not
  LExer::Type::identifier.
2015-03-01 21:14:28 -05:00
Paul Beckingham
309b607672 Lexer
- Number digits followed by non-whitespace, non-operators are not numbers, ie
  "2nd" is not "2","nd".
2015-03-01 16:03:10 -05:00
Paul Beckingham
abe8819f2f Unit Tests
- Restored 'name=value' tests that expect three tokens.
2015-03-01 15:53:24 -05:00
Tomas Babej
f0cc0151b7 TDB2: Explicitly mark updates done as addition to optimize performance
We are able to avoid loading the completed.data in certain scenarios.
2015-02-28 16:54:09 -05:00
Paul Beckingham
3f2d377fef Lexer
- Allow '=' in rc.<name>[:=]<value>, but not in non-rc Lexer::Type::pair
  combinations. That means 'name=value' is not a Lexer::Type::pair.
2015-02-28 12:05:24 -05:00
Paul Beckingham
c849cc9bfe Unit Tests
- Added tests with spaces around the operators, for better coverage.
2015-02-28 12:00:36 -05:00
Tomas Babej
48be6986c2 Make tasks affect statistics of super-projects 2015-02-28 09:34:10 -05:00
Tomas Babej
ffd6465661 CmdProjects: Enforce garbage collector 2015-02-28 09:33:36 -05:00
Paul Beckingham
7e890c084f Report
- Removed bad filter term.
2015-02-27 00:18:19 -05:00
Paul Beckingham
0f1a46e6d3 Lex
- Changed output to reflect current usage.
2015-02-27 00:17:56 -05:00
Paul Beckingham
2811b9a571 Unit Tests
- Fixed tests regarding quoted strings coming out of the Lexer.
2015-02-27 00:16:14 -05:00
Paul Beckingham
58e62711f3 Virtual Tags
- The 'info' command now shows virtual tags.
2015-02-26 21:09:50 -05:00
Tomas Babej
3b9d88a87b completion: Add bash completion for contexts 2015-02-26 20:51:39 -05:00
Paul Beckingham
9dad0c7eb6 Merge branch '2.4.2' into lexer2 2015-02-24 23:14:58 -05:00
Paul Beckingham
e67c6c45cf Lexer
- Strings now retain their quotes, for compatibility with 2.4.1.
2015-02-24 23:01:12 -05:00
Paul Beckingham
aa8cd54142 TW-1535
- TW-1535 move default listing-break from list to ls (thanks to David Patrick).
2015-02-24 22:57:56 -05:00
Paul Beckingham
651ac3f174 Tw-1551
- TW-1551 Unable to get a UDA value from DOM (thanks to Tomas Babej).
2015-02-24 22:35:18 -05:00
Paul Beckingham
ea78200ae3 TW-1549
- TW-1549 task annotate hangs with specific text pattern (thanks to Alexandre
          de Verteuil).
2015-02-24 22:04:05 -05:00
Paul Beckingham
ad89a90d12 TW-1550
- TW-1550 _contexts helper-command (thanks to David Patrick).
2015-02-24 20:43:28 -05:00
Paul Beckingham
8c6892fed6 Merge branch '2.4.2' into lexer2 2015-02-24 17:03:11 -05:00
Tomas Babej
85d0e1789a CmdContext: Validate context's filter upon definition 2015-02-24 16:55:28 -05:00
Tomas Babej
e19c99ce1e CmdContext: Fix incorrect exception handling 2015-02-24 16:53:23 -05:00
Tomas Babej
0868ba757c CmdConfig: Do not leave empty line upon variable removal 2015-02-24 16:52:55 -05:00
Tomas Babej
bf5f246168 man: Fix copy-paste error in task.1 2015-02-24 16:51:28 -05:00
Tomas Babej
9748fa2ab1 tests: Fix asserted strings in context tests 2015-02-24 16:51:20 -05:00
Tomas Babej
1a0f479394 CmdContext: Add helpful hint to output when setting context 2015-02-24 16:51:05 -05:00
Wilhelm Schuermann
e2f35a1a06 Hooks
- onAdd, onExit, onLaunch and onModify hooks now output feedback messages
  as footnotes when hooks exit with status 0, as per hooks documentation.
2015-02-24 16:45:23 -05:00
Paul Beckingham
0548fca88f Merge branch '2.4.2' into lexer2 2015-02-23 20:22:34 -05:00
Paul Beckingham
394acae790 CmdContext
- Trivіal code edits.
2015-02-23 20:07:53 -05:00
Tomas Babej
e8d385119a Update NEWS and ChangeLog files with the info about the context 2015-02-23 19:25:40 -05:00
Tomas Babej
070fdf60fa localization: Update other locale files 2015-02-23 19:22:06 -05:00
Tomas Babej
1278226c16 CmdContext: Localize the hardcoded strings 2015-02-23 19:22:01 -05:00
Tomas Babej
98410cff42 CmdShow: Add context related variables 2015-02-23 19:21:57 -05:00
Tomas Babej
ee23a099f2 CmdContext: Minor style and message changes 2015-02-23 19:21:50 -05:00
Tomas Babej
0ab1dc0c9c man: Update task and taskrc manpages with the information about context 2015-02-23 19:21:46 -05:00
Tomas Babej
c2a9bb65e6 tests: Add tests for the context feature 2015-02-23 19:20:00 -05:00
Tomas Babej
75cf742a55 tests: Add taskrc_content property 2015-02-23 19:19:55 -05:00
Tomas Babej
dc0502dd9f CLI: Apply context filter directly in getFilter method 2015-02-23 19:19:51 -05:00
Tomas Babej
3a77a5f291 CLI: Add addContextFilter method 2015-02-23 19:19:47 -05:00
Tomas Babej
1abda3900b CLI: Abstract adding new raw filter to a new helper method 2015-02-23 19:19:44 -05:00
Tomas Babej
09fe5be086 CmdContext: Add show subcommand 2015-02-23 19:19:41 -05:00
Tomas Babej
1a3550541b CmdContext: Add support for unsetting context 2015-02-23 19:19:38 -05:00
Tomas Babej
371ca27da0 CmdContext: Add support for setting context 2015-02-23 19:19:35 -05:00
Tomas Babej
5f3dd43893 CmdCompletionContext: Implement the context completion list 2015-02-23 19:19:32 -05:00
Tomas Babej
edb54a51b3 CmdContext: Add list subcommand 2015-02-23 19:19:29 -05:00
Tomas Babej
636f6bfd96 CmdContext: Add initial implementation of the delete subcommand 2015-02-23 19:19:24 -05:00
Tomas Babej
e91063426a CmdContext: Add initial implementation of the context define subcommand 2015-02-23 19:19:18 -05:00
Tomas Babej
a4d5ab07e9 CmdConfig: Abstract saving and removal of the configuration values into separate methods 2015-02-23 19:19:15 -05:00
Tomas Babej
49a7e46eaf CmdContext: Add initial plumbing 2015-02-23 19:19:08 -05:00
Paul Beckingham
26aff348d2 Lexer
- Replaced old digitsOnly() function with Lexer::isAllDigits.
2015-02-22 20:23:00 -05:00
Paul Beckingham
9f82926c65 Text
- Removed obsolete noSpaces() function.
2015-02-22 18:27:19 -05:00
Paul Beckingham
8791c0a921 Lexer
- Migrated old noSpaces() function into Lexer::isOneWord.
2015-02-22 18:23:26 -05:00
Paul Beckingham
745aad0d27 Lexer
- Renamed Lexer2 to Lexer, it looks good enough to assume control.
2015-02-22 18:23:03 -05:00
Paul Beckingham
e1c0d5b130 Merge branch '2.4.2' into lexer2 2015-02-22 15:26:22 -05:00
Wilhelm Schuermann
0891ed4b57 Util
- Closed dangling pipes in execute (), resolving problems when a hook script
  forks (thanks to Jens Erat).
2015-02-22 14:43:16 -05:00
Paul Beckingham
9898aa15b5 Merge branch '2.4.2' into lexer2 2015-02-22 14:06:01 -05:00
Wilhelm Schuermann
af772f4c49 Util
- Closed dangling pipes in execute (), resolving problems when a hook script
  forks.
2015-02-22 14:03:30 -05:00
Wilhelm Schuermann
3484e44c7d CMake
- Added workaround for Cygwin build errors.
2015-02-22 13:53:53 -05:00
Paul Beckingham
0cf18f3b16 Lexer2
- Integrated Lexer2 in place of Lexer. Tests fail.
2015-02-22 13:52:14 -05:00
Paul Beckingham
2155bd3969 Lexer2
- Implemented ::isAllDigits to distinguish between integers and real numbers.
2015-02-21 10:58:07 -08:00
Paul Beckingham
ae6024ef8b Lex
- Added a 'lex' binary to test Lexer2.
2015-02-21 10:03:17 -08:00
Paul Beckingham
21553d9044 Lexer2
- Implemented ::isDate and ::isDuration.
2015-02-21 10:01:59 -08:00
Paul Beckingham
d6e3430e0d Lexer2
- Implemented ::split for typeless tokenization.
2015-02-21 09:28:56 -08:00
Paul Beckingham
0d23511cee Lexer2
- Stubbed ::isDate and ::isDuration methods.
2015-02-21 09:19:24 -08:00
Paul Beckingham
1128ad8259 Lexer2
- Improved ::isUUID lexing.
2015-02-20 12:13:11 -08:00
Paul Beckingham
66d5a8ba3d Lexer2
- Migrated new ::isURL and ::isPath methods.
- Migrated new ::tokens method to access all tokens at once.
2015-02-20 12:05:03 -08:00
Paul Beckingham
aab93b2cda Lexer2
- Migrated ::dequote method from Lexer.
2015-02-19 21:43:22 -08:00
Paul Beckingham
be80bc4ea3 Converted to Lexer2. 2015-02-19 21:36:04 -08:00
Paul Beckingham
15bec27e03 Converted to Lexer2. 2015-02-19 21:32:51 -08:00
Paul Beckingham
6683cc7e83 Converted to Lexer2. 2015-02-19 21:31:47 -08:00
Paul Beckingham
044ca40bb7 Merge branch '2.4.2' into lexer2 2015-02-19 21:00:27 -08:00
Wilhelm Schuermann
be855af8c4 CMake
- Custom compile flags and flags for c++11 ignored even less on OSX now...
2015-02-19 19:17:14 -08:00
Paul Beckingham
d10ad5c7af Lexer2
- Copied in the Lexer2 object.
2015-02-19 08:54:20 -08:00
Paul Beckingham
1ae4ea2ea3 I18N
- Removed unnecessary string termination and exceptions.
- Removed associated localized errors.
2015-02-19 08:51:36 -08:00
Renato Alves
d37937d970 Tests - add total number of tests to conversion script 2015-02-19 14:11:19 +00:00
Renato Alves
51e947064f Tests - merge args.[2-5].t with enpassant.t and convert to python 2015-02-19 13:38:27 +00:00
Renato Alves
606a7505d3 Tests - convert args.5.t to python 2015-02-19 13:02:46 +00:00
Renato Alves
846dd14a9f Tests - convert args.4.t to python 2015-02-19 12:56:19 +00:00
Renato Alves
d8f436cca9 Tests - convert args.3.t to python 2015-02-19 12:54:45 +00:00
Renato Alves
ac9c726782 Tests - convert args.2.t to python 2015-02-19 12:50:40 +00:00
Paul Beckingham
88d65b23c1 CMake Feedback Messages
- Reworded the '-std=c++11' feedback messages to user fewer '!' characters,
  and be more emphatic about upgrading the compiler.
2015-02-18 21:15:58 -08:00
Paul Beckingham
f2998aba74 TW-1547
- TW-1547 Recur column is always shown even if no recurring task is displayed
          (thanks to Renato Alves).
2015-02-18 20:53:56 -08:00
Paul Beckingham
d2b2631db7 TW-1546
- TW-1546 column type due.remaining breaks colors on due tasks (thanks to Renato
          Alves).
2015-02-18 20:18:31 -08:00
Renato Alves
4c491d8a0d Tests - convert args.1.t to python 2015-02-18 19:54:45 +00:00
Renato Alves
917a152a1e Tests - convert args.t to python 2015-02-18 19:32:58 +00:00
Renato Alves
289e45499a Tests - convert append.t to python 2015-02-18 16:26:59 +00:00
Renato Alves
2efb9594d9 Tests - convert annotate.t to python 2015-02-18 14:05:53 +00:00
Wilhelm Schuermann
0d096a5a42 CMake
- Fixed the C++11 config to actually check for availability of C++11 before trying to use it.
- Custom CXX flags no longer ignored on some platforms.
2015-02-17 16:16:02 -08:00
Paul Beckingham
cc1c063925 Merge branch '2.4.2' of ssh://git.tasktools.org/tm/task into 2.4.2 2015-02-17 18:46:41 -05:00
Paul Beckingham
dd31a15001 Portability
- Removed pthreads linkage.
2015-02-17 12:49:58 -05:00
Paul Beckingham
c11601e30e Cleanup
- Removed unused Nibbler::getHex.
2015-02-17 10:58:45 -05:00
Paul Beckingham
520067f522 Cleanup
- Eliminated text.cpp upperCase(), as it was not UTF8-safe, and is no longer
  used.
2015-02-17 10:44:31 -05:00
Paul Beckingham
693fe9b8fd ColPriority
- Made the acceptance of lower-case priorities explicitly either lower or upper
  case, but converted to upper case. This eliminates the dependency on text.cpp
  upperCase(), which is not UTF8-safe.
2015-02-17 10:40:51 -05:00
Renato Alves
fa249bfedd Tests - Convert alias.t to python 2015-02-17 13:46:00 +00:00
Paul Beckingham
952d743218 Cleanup
- Removed duplicate legacy value map entry.
- Added version number to legacy mapping.
- Added TODO for later clarification.
2015-02-17 08:39:53 -05:00
Paul Beckingham
91f6980d2f Documents
- Updated INSTALL instructions with dependencies.
2015-02-17 08:39:17 -05:00
Renato Alves
d211969236 Tests - clarify that USE_PATH alone is not enough. 2015-02-17 13:37:39 +00:00
Renato Alves
bd320b6729 Tests - Remove commented code 2015-02-17 12:24:13 +00:00
Renato Alves
3f004f547e Tests - Don't hang if spawning taskw fails 2015-02-17 12:24:13 +00:00
Paul Beckingham
3b192cddcf Merge branch '2.4.2' of ssh://git.tasktools.org/tm/task into 2.4.2 2015-02-16 19:33:14 -05:00
Paul Beckingham
ca6061a987 Documentation
- Updated for 2.4.2.
2015-02-16 19:32:02 -05:00
Federico Hernandez
2787ee960b Cleanup NEWS file 2015-02-17 00:22:07 +01:00
Federico Hernandez
c27ad4dc27 Bumped version number in ref page 2015-02-17 00:18:45 +01:00
Federico Hernandez
a36eb974ee Bumped version number to 2.4.2 2015-02-17 00:04:04 +01:00
Federico Hernandez
a55a7bf1a2 Added SHA1 of tagged release commit 2015-02-17 00:00:20 +01:00
Federico Hernandez
82e019a4a8 Version number and release date for 2.4.1 2015-02-16 23:38:16 +01:00
Renato Alves
9180aa6e33 Tests - minor formatting changes 2015-02-16 15:37:31 +00:00
Renato Alves
32d837fb25 Tests - Convert add.t to python
* Also added UUID_regex to basetest.utils, likely to be reused
2015-02-16 15:36:38 +00:00
Renato Alves
395b08385b Tests - Rename testname of abbreviation.t 2015-02-16 15:36:09 +00:00
Renato Alves
9cc9e19757 Tests - conversion script was failing due to binaries matching 2015-02-16 14:50:35 +00:00
Renato Alves
c07d74574d Tests - Silence "vramsteg not found" message 2015-02-16 14:36:47 +00:00
Renato Alves
73401664b6 Tests - Convert abbreviation.t to Python and add one more testcase 2015-02-16 14:30:11 +00:00
Renato Alves
d052b8752d Tests - remove unused imports 2015-02-16 14:09:04 +00:00
Renato Alves
68d9e3bb5e Tests - List perl tests to migrate with ./conversion -v 2015-02-16 03:58:49 +00:00
Renato Alves
86837022d3 Tests - conversion script was giving false positives for python 2015-02-16 03:47:12 +00:00
Renato Alves
e831f85823 Tests - Migrate bug.1006.t to python 2015-02-16 03:47:12 +00:00
Renato Alves
4b839f2c83 Tests - Significant speed improvement at cost of CPU cycles 2015-02-16 01:59:47 +00:00
Renato Alves
1e1bd32c42 Tests - Rework how taskw is launched from python
* Should avoid some odd race conditions
2015-02-16 01:59:32 +00:00
Renato Alves
880ab5d665 Tests - Correct use of non-existent exception 2015-02-15 19:43:09 +00:00
Renato Alves
ef583dedea Tests - Update template.t to include assertValidJSONOutput 2015-02-15 18:13:49 +00:00
Renato Alves
1bd26fe9ab Tests - Default to having Python objects as "hook log" 2015-02-15 18:10:36 +00:00
Renato Alves
80c01f3ffb Tests - Remove assertTriggered, redundant with assertTriggeredCount 2015-02-15 18:10:36 +00:00
Renato Alves
7cd42b5d2f Tests - Less verbose hooks.on-*.t tests 2015-02-15 18:10:36 +00:00
Renato Alves
1b218d68f5 Tests - Invalid JSON replies in hooks no longer fail the test framework 2015-02-15 18:10:36 +00:00
Paul Beckingham
e6031183dd Documentation
- Added 'taskd.trust' setting warning in the NEWS file.
2015-02-15 12:10:08 -05:00
Paul Beckingham
f9bcb8cfc8 Unit Tests
- Added unit tests for TW-1542.
2015-02-15 11:45:31 -05:00
Paul Beckingham
2ef0214248 TW-1542
- TW-1542 Large numeric UDA values get rendered in scientific notation on export
          (thanks to Ralph Bean).
2015-02-15 11:24:24 -05:00
Paul Beckingham
0a62061ca8 L10N
- Localized the new strings in Hooks.cpp.
2015-02-15 02:03:24 -05:00
Paul Beckingham
5d96547c07 Documentation
- Prepared more docs for the 2.4.1 release.
2015-02-14 21:12:16 -05:00
Paul Beckingham
eb3afa1fdc Documentation
- Updated ChangeLog.
2015-02-14 20:44:56 -05:00
Paul Beckingham
885ff46066 Unit Tests
- Renamed tw-1452.t to take it out of play, because the bug is not going to be
  fixed for 2.4.1.
2015-02-14 19:10:57 -05:00
Paul Beckingham
5a060802e7 Hooks
- It is now an error for a failing hook script to fail to also generate some
  feedback.
- Updated NEWS file to mention stricter hook script controls.
2015-02-14 18:16:32 -05:00
Paul Beckingham
acce2d5a68 Unit Tests
- Corrected expected output from on-modify-misbehave6 test hook script.
2015-02-14 17:55:02 -05:00
Paul Beckingham
adac566665 Unit Tests
- Corrected expected output from the on-modify-misbehave4 test hook script.
- Moved JSON syntax error details to debug output.
2015-02-14 17:50:28 -05:00
Paul Beckingham
f454eecafb Unit Tests
- Corrected expected output.
- Added 'complete' task to the on-modify-misbehave4 test hook script.
2015-02-14 17:46:24 -05:00
Paul Beckingham
f83702076b Unit Tests
- Updated expected error for on-modify-misbehave3 test hook script.
2015-02-14 17:44:21 -05:00
Paul Beckingham
83519a44c6 Unit Tests
- Added feedback to the on-modify-misbehave2 test hook script.
2015-02-14 17:42:03 -05:00
Paul Beckingham
a2e980c6b8 Hooks
- Deferred JSON checking until after we know whether the hooks voluntarily
  failed the event.
2015-02-14 17:33:48 -05:00
Paul Beckingham
e00704a375 Hooks
- Cleaned up logic for crude JSON detection.
2015-02-14 17:31:37 -05:00
Paul Beckingham
8fb3161a02 Hooks
- Modified expected output from an on-modify hooks script, because it's 1 line
  of JSON, not 2.
2015-02-14 17:19:44 -05:00
Tomas Babej
fa528beb58 Tests: Add test for TW-1510 2015-02-14 17:10:22 -05:00
Paul Beckingham
c5a2b7f759 Unit Tests
- Fixed exected error for on-add-misbehave5. This highlights a potential
  error in the test suite with the one failing test.
2015-02-14 17:03:43 -05:00
Paul Beckingham
4213afd408 Unit Tests
- Fixed on-add-misbehave6 error.
2015-02-14 16:52:47 -05:00
Paul Beckingham
8ad0c1172e Unit Tests
- on-add now detects JSON emitted by hooks for different tasks.
2015-02-14 16:46:52 -05:00
Paul Beckingham
f887173bf7 TW-1510
- TW-1510 Task can save empty value in the data backlog (thanks to Tomas Babej).
2015-02-14 16:33:45 -05:00
Tomas Babej
328b7ec260 Task: Do not export empty attributes in composeJSON 2015-02-14 16:32:41 -05:00
Paul Beckingham
ab41e005c9 Unit Tests
- Added on-exit test for unexpected JSON output.
2015-02-14 16:19:36 -05:00
Paul Beckingham
bd785ffad5 Hooks
- Added a new on-launch-misbehave2 script that emits unexpected JSON.
2015-02-14 16:14:20 -05:00
Paul Beckingham
3cce6c23f5 Hooks
- Hooks now verify the expected number of JSON lines emitted by hook scripts.
2015-02-14 15:50:51 -05:00
Paul Beckingham
ec919a8677 TW-1534
- TW-1534 Urgency coefficient for user project disables 'info' output(thanks to
          Martin).
- Reimplemented 'information' command urgency details as a ViewText.
- Fixed math bug in rightJustify.
2015-02-14 12:50:10 -05:00
Paul Beckingham
5911286218 Documentation
- Relocated Tomas to a better seat.
2015-02-14 11:52:07 -05:00
Paul Beckingham
9e49325d6e TW-1531
- TW-1531 'task export' should handle recurrence (thanks to Tomas Babej).
2015-02-13 20:35:04 -05:00
Tomas Babej
c483c8dda9 CmdExport: Generate recurrent tasks 2015-02-13 20:31:46 -05:00
Paul Beckingham
2d2579d801 Unit Tests
- Fixed bug in test where the rc path name contains a number, which is found by
  the test in place of an unexpected week number.
2015-02-08 14:04:22 -05:00
Paul Beckingham
4f6c51e7ae Unit Tests
- Updated README with suggested work.
- Removed obsolete TAP.py.
- Added 'conversion' script to check on the Perl -> Python progress.
2015-02-08 13:51:27 -05:00
Paul Beckingham
cd6808ab15 Unit Tests
- Removed color.duetoday.t, because of two reasons. First, the test is flawed,
  and second the test is duplicated in tw-285.t.
2015-02-08 13:22:05 -05:00
Paul Beckingham
8b648bc690 Unit Tests
- Reduced hh from 99:00:00 to 95:00:00, because with the +01:00 it becomes
  100:00:00, and '100' exceeds Nibbler::getDigit2.
2015-02-08 13:18:23 -05:00
Paul Beckingham
9a0a6188b1 Documentation
- Added email address to which patches should be sent (thanks to mathstuf).
2015-02-07 18:50:03 -05:00
Paul Beckingham
b79453afa7 TW-1532
- TW-1532 Hooks does not execute any script on Cygwin (thanks to Taisuke
          Hachimura).
2015-02-07 14:45:06 -05:00
Paul Beckingham
60dddffda3 TW-1524
- TW-1524 Build Broken (thanks to Jack).
2015-02-07 14:39:25 -05:00
Paul Beckingham
e8dc5a16a1 Portability
- Clarified the requirements regarding C++11 support, libuuid and gnutls.
2015-02-07 14:37:51 -05:00
Paul Beckingham
528e72062b Hooks
- Fixed the ::execute function used by Hooks and CmdCalendar to run commands,
  by following the convention:

    "The first argument, by convention, should point to the file name associated
     with the file being executed."
     (man execvp)
2015-02-07 14:08:58 -05:00
Paul Beckingham
876820ee1a Unit Tests
- Restored bash as the interpreter for wrapper.sh.
2015-02-07 10:39:16 -05:00
Paul Beckingham
8e730f2da3 Hooks
- Updated examples to not rely on bash being in /bin.
2015-02-07 10:25:50 -05:00
Paul Beckingham
8b28c36485 Unit Tests
- Modified the test_hooks/* scripts to use /bin/sh, which is portable. When
  using /bin/bash, Cygwin and FreeBSD silently fail.
2015-02-07 10:22:07 -05:00
Paul Beckingham
04be198281 Unit Tests
- Added on-modify hook tests.
2015-02-07 08:58:47 -05:00
Paul Beckingham
1b69ac6ad2 Unit Tests
- The on-modify-misbehave1 test is not useful.
2015-02-07 08:57:27 -05:00
Paul Beckingham
b1081bd510 Unit Tests
- Added hook tests for on-add.
2015-02-07 08:41:02 -05:00
Paul Beckingham
a72322b9db Unit Test
- Added tests for on-exit hooks.
2015-02-02 12:37:39 -05:00
Paul Beckingham
82e6574d30 Unit Tests
- Added tests for on-launch hooks.
2015-02-01 18:37:18 -05:00
Paul Beckingham
1f14454be7 Merge branch '2.4.1' of ssh://git.tasktools.org/tm/task into 2.4.1
Conflicts:
	test/template.t
2015-02-01 17:01:58 -05:00
Paul Beckingham
384ca82292 Unit Tests
- Hooks snapshot prior to pull.
2015-02-01 17:00:39 -05:00
Renato Alves
066143d939 Unittest - template.t use docstring for proper test description on execution 2015-02-01 20:39:38 +00:00
Renato Alves
e845a25ea6 Unittest - Replace the timestamp function on wrapper.sh
* %N is not supported by /bin/date on BSD systems
2015-02-01 20:35:11 +00:00
Renato Alves
75a2b11638 Unittest - Update template.t to use Hook.assert* functions 2015-02-01 19:33:08 +00:00
Renato Alves
6a3edc88a1 Unittest - properly implement Hook.assert* functions 2015-02-01 19:33:08 +00:00
Renato Alves
bb3555f217 Unittest - Bad exception formatting 2015-02-01 19:33:08 +00:00
Paul Beckingham
5de80eedeb Unit Tests
- Typos in template.t.
2015-02-01 12:36:54 -05:00
Paul Beckingham
10c1b5f902 Merge branch '2.4.1' of ssh://git.tasktools.org/tm/task into 2.4.1 2015-02-01 11:53:11 -05:00
Renato Alves
049ed04471 Unittest - Document the magic line in wrapper.sh 2015-02-01 06:23:34 +00:00
Paul Beckingham
3ff42af9c7 Unit Tests
- Added sample hooks that behave/misbehave in various ways.
2015-01-31 18:34:25 -05:00
Paul Beckingham
1cfdfbae52 Hooks
- Removed the ability for hooks to add tasks, or modify tasks that are outside
  the context of the current event. This makes hooks a local mechanism that
  operates only on local changes. Modifications/additions coming in via sync
  command are not processed by hooks.
2015-01-31 17:47:58 -05:00
Paul Beckingham
61291e4d1e C++
- ColumnDate::~ColumnDate should be virtual.
2015-01-31 13:23:45 -05:00
Tomas Babej
77ec56dcd0 Tests: Add test case for disabled recurrence mechanism 2015-01-25 16:06:26 -05:00
Paul Beckingham
d0f74c4a80 Unit Tests
- Fixed a broken test that returns different values are different times of the
  year/month.
- Removed silly tests that were looking at color values, which is a bad idea.
2015-01-25 16:00:43 -05:00
Paul Beckingham
4bdee56fa7 Configuration
- New 'recurrence' configuration setting can disable recurring task generation.
2015-01-25 15:41:31 -05:00
Paul Beckingham
8323407242 TW-1504
- TW-1504 On-modify hook does not accept correct JSON format (thanks to Tomas
          Babej).
2015-01-25 14:51:07 -05:00
Paul Beckingham
6626207ad1 TW-1522
- TW-1522 Date format doesn't like hyphens (thanks to Scott Carter).
2015-01-25 14:49:02 -05:00
Paul Beckingham
4865269630 Hooks
- Modified loop so it no longer walks the whole list unnecessarily.
- Minor style tweaks.
2015-01-25 10:33:03 -05:00
Tomas Babej
8683574b18 Hooks: Make sure that original task is properly detected
On modify event uses UUID of the original task being modified
to determine which line should be interpreted as modification
of the task.

This was achieved by searching by a substring in a specific JSON
format, which, consenquently, failed on JSON strings of other (valid)
formats.
2015-01-25 10:28:04 -05:00
Paul Beckingham
5e90510530 Merge branch '2.4.1' of ssh://git.tasktools.org/tm/task into 2.4.1 2015-01-24 10:12:43 -05:00
Tomas Babej
a8bab90c34 Documentation
- Update the man page with the correct description of behaviour of
  rc.confirmation and rc.bulk variables
2015-01-24 10:12:32 -05:00
Renato Alves
87c2a9b5a5 Unittest - README clarifications
* Add one line command to run all tests
* Clarify that only tests ending with .t and execute bit set are run
2015-01-21 12:13:19 +00:00
Paul Beckingham
cba5948a1d Unit Tests
- Took the tw-1481.t test offline, because the bug will not be fixed in the
  next release.
2015-01-20 22:06:46 -05:00
Paul Beckingham
f4a6ec6f97 Documentation
- Updated README.
2015-01-20 22:05:11 -05:00
Paul Beckingham
a3490966c0 Documentation
- Added README to explain the test suite.
2015-01-20 21:52:17 -05:00
Paul Beckingham
855537b975 TW-1517
- TW-1517 Hook performance should be measured individually for each hook (thanks
          to Tomas Babej).
2015-01-20 20:33:31 -05:00
Paul Beckingham
473019c1f0 TW-1519
- TW-1519 Testing suite forces taskd.trust="ignore hostname" (thanks to Renato
          Alves).
2015-01-20 20:18:13 -05:00
Renato Alves
d1698eab2d Fix usage of taskd.trust=strict on the test suite
* This reverts commit 67cb30fdce.
* Code also now tests if taskd is listening on IPv4 or IPv6 interfaces.
2015-01-20 11:56:53 +00:00
Paul Beckingham
73b7b21b81 Certs
- Replaced all the certs for testing.  This may not help, but it does mean
  there were recreated using the current PKI scripts, and are all consistent.
2015-01-19 18:25:21 -05:00
Paul Beckingham
44f2c0c98c Unit Tests
- Added JSON test which includes spaces.
2015-01-19 18:09:54 -05:00
Tomas Babej
654159b2fd Hooks: Add per-hook time measurement 2015-01-19 16:07:34 -05:00
Renato Alves
67cb30fdce Unittest - default to taskd.trust="ignore hostname"
* The currently available taskd testing certificates fail if
taskd.trust is set to strict. See also: TW-1519
2015-01-19 15:07:06 +00:00
Renato Alves
7d75547f44 Ignore taskd in src folder (for running taskd tests) 2015-01-19 15:00:02 +00:00
Renato Alves
d68fa7ea8a Unittest - Template updated to include hook test examples 2015-01-19 14:58:06 +00:00
Renato Alves
db78851b40 Unittest - Hook testing, fixed a few mistakes 2015-01-19 14:54:11 +00:00
Tomas Babej
6fdf0738a7 Hooks: Improve debugging messages for on-exit hooks 2015-01-18 13:27:32 -05:00
Paul Beckingham
41003fc78b Code Cleanup
- Removed whitespace at EOL.
2015-01-17 19:05:09 -05:00
Wilhelm Schuermann
d2928dc4e4 TW-1516 Hook input not ended with EOF
- Made execute() send EOF when all input is written.
- Minor code cleanup.
2015-01-17 19:04:31 -05:00
Wilhelm Schuermann
61deb7ce7e CMake
- Fix CMAKE_CXX_FLAGS change to preserve previous flags.
2015-01-17 18:58:38 -05:00
Renato Alves
0311ea6689 GnuTLS optionally compiled - defaults to yes
* Allow optionally disabling GnuTLS via config var (USE_GNUTLS)
2015-01-14 13:17:25 +00:00
Paul Beckingham
0b0ddbfbc4 TW-1491
- TW-1491 Regression in deleting due dates (thanks to Jens Erat).
2015-01-12 15:52:15 -05:00
Jens Erat
cd31335742 TW-1491 - TW-1491 Regression in deleting due dates 2015-01-12 15:42:58 -05:00
Tomas Babej
5a3037f257 Tests: Fix error in export.t and add test for urgency 2015-01-12 15:38:44 -05:00
Paul Beckingham
a22d173b9b TW-1487
- TW-1487 Task export exports some numeric attributes as strings (thanks to
          Tomas Babej).
2015-01-11 09:07:04 -05:00
Paul Beckingham
134d201cb0 TW-1457
- TW-1457 Non-existant attributes are not properly handled (thanks to Tomas
          Babej).
2015-01-10 15:53:55 -05:00
Paul Beckingham
547d9edb55 TW-1484
- TW-1484 The 'history' and 'ghistory' reports do not obey rc.color.label.
2015-01-10 15:49:42 -05:00
Paul Beckingham
cdde747733 TW-1498
- TW-1498 Filtering for presence of UDA matches all tasks (thanks to Ralph Bean).
2015-01-10 14:36:21 -05:00
Paul Beckingham
588009d679 TW-1501
- TW-1501 Calc can't handle multi-digit numbers in some expressions (thanks to
          Jeremy John Reeder).
2015-01-08 20:40:29 -05:00
Paul Beckingham
adf9f25289 Unit Tests
- Removed failing tests, as there is no plan to address all these now.
2015-01-07 23:43:11 -05:00
Paul Beckingham
ea0397f542 TW-1495
- TW-1495 German translation for taskwarrior (thanks to Jens Erat).
2015-01-07 23:23:25 -05:00
Paul Beckingham
411fab3a27 Hooks
- Added debug output on startup, if a hook script is found and does not have
  a valid name.
2015-01-07 22:54:55 -05:00
Paul Beckingham
da6a57b203 Hooks
- The 'diag' command should indicate misnamed hook scripts (thanks to Tomas
  Babej).
2015-01-07 18:43:14 -05:00
Tomas Babej
235e4ef047 Tests: Add negative tests for parsing time 2015-01-07 17:14:49 -05:00
Tomas Babej
1dbb0a57a8 Tests: Add negative tests for parsing dates 2015-01-06 19:14:04 -05:00
Paul Beckingham
5a7117630e Unit Tests
- Typo.
2015-01-05 20:04:26 -05:00
Paul Beckingham
e9f9f6a86e Unit Tests
- Added copyright and removed whitespace at EOL.
2015-01-05 19:52:17 -05:00
Tomas Babej
8ea1563944 Tests: Add basic coverage for the types of attributes in the export command 2015-01-05 19:48:52 -05:00
Wilhelm Schuermann
e522c1980a TW-1424
- Fixed tw-1424.t (mostly).
2015-01-05 06:53:51 -05:00
Paul Beckingham
845fe047cc C++11
- Converted one source file to use an actual C++11 feature. If no one notices
  we can move forward and start using supported featues. If it's a problem,
  this commit gets rolled back.

  GCC 4.6 (released March 2011), and Clang 3.0 (releasd November 2011) both
  support N2930 (range-based for) which is the feature being tried here.
2015-01-04 22:01:22 -05:00
Paul Beckingham
5932d9f90b Portability
- More STDOUT_FILENO conversions.
2015-01-04 22:00:08 -05:00
Paul Beckingham
a8ff7655ef Portability
- Properly detects Cygwin.  version 2.
2015-01-04 21:37:47 -05:00
Paul Beckingham
79cc9d8ed9 Portability
- Properly detects Cygwin.
2015-01-04 21:29:46 -05:00
Paul Beckingham
b83dc8a29e Portability
- Added '-std=gnu++0x' for Cygwin.
2015-01-04 20:39:42 -05:00
Paul Beckingham
99ebf82c3a Context
- Eliminated fileno() by using unistd.h STDOUT_FILENO. This is because fileno()
  is not visible with '--std=c++11'.
2015-01-04 20:25:25 -05:00
Paul Beckingham
231e8ca913 Portability
- Missing include on Cygwin only.  Odd.
2015-01-04 20:17:56 -05:00
Paul Beckingham
9bd9a3b285 C++
- Enabling C++11, as a test.  Let's see if anything blows up.
2015-01-04 20:11:32 -05:00
Paul Beckingham
b882cd151e Build
- Added code to set poicy CMP0037 to "OLD", which eliminates the warnings about
  the 'test' target.
2015-01-04 18:59:09 -05:00
Paul Beckingham
a3b0d5c6a6 Unit Tests
- Added copyright to tests that were missing it.
2015-01-04 10:55:01 -05:00
Paul Beckingham
d1664b3f55 Unit Tests
- Re-enabled bug.982.t and fixed the regex so it is right. This test was created
  for a parsing bug which is now fixed.
2015-01-04 10:40:21 -05:00
Paul Beckingham
1cac14f3f8 Unit Tests
- This disabled test is verifying 'Total Active Time' functionality that has
  itself been removed.
2015-01-04 10:35:57 -05:00
Paul Beckingham
507e2d5807 Unit Tests
- Added copyright, fixed test name.
2015-01-04 10:30:10 -05:00
Tomas Babej
c664d62c2f Tests: Add coverage for TW-1452 2015-01-04 10:23:38 -05:00
Paul Beckingham
f32c4d1f73 TW-1492
- TW-1492 compiling v2.4.0 using musl(libc) (thanks to V.Krishn).
2015-01-04 10:14:15 -05:00
Paul Beckingham
9bf7dfda4f AUTHORS
- Upgrade Tomas to the dev section.
2015-01-03 20:25:30 -05:00
Tomas Babej
2a782b8f01 Tests: Add coverage for TW-1481 2015-01-03 20:21:46 -05:00
Paul Beckingham
3c07510a43 Unit Tests
- Added copyright to template.
2015-01-03 20:20:30 -05:00
Paul Beckingham
07eff0f3c1 Bug
- Changed assorted reports so they do not use '.age' format for dates that are
  in the future, because those are never shown with this format.
2015-01-03 17:56:20 -05:00
Paul Beckingham
dad0ac0c27 TW-1486
- TW-1486 task wait shows completed tasks which has a wait attribute (thanks to
          Sujeevan Vijayakumaran).
2015-01-03 09:31:54 -05:00
Paul Beckingham
2c986d6e6b Unit Tests
- Fixed typo in comment.
2015-01-03 09:24:07 -05:00
Paul Beckingham
1e8f83cab9 Themes
- Removed 'bold' from a 256-color definition.
2015-01-02 08:55:18 -05:00
Paul Beckingham
15f00452c2 Themes
- Increased contrast for 'color.blocking'.
2015-01-02 08:54:50 -05:00
Paul Beckingham
6a3b13dbfc Documentation
- Initial project version doc updates.
2015-01-02 00:01:32 -05:00
Federico Hernandez
8180b94b1b Bumped version number in task reference pdf 2015-01-02 01:05:03 +01:00
Federico Hernandez
6aafa9f04e Bumped version number to 2.4.1 2015-01-02 00:44:49 +01:00
Federico Hernandez
fe07f62214 Added SHA1 of tagged release commit 2015-01-02 00:39:40 +01:00
305 changed files with 13553 additions and 7544 deletions

1
.gitignore vendored
View File

@@ -2,6 +2,7 @@ cmake.h
commit.h
Makefile
src/task
src/taskd
src/libtask.a
src/commands/libcommands.a
src/columns/libcolumns.a

14
AUTHORS
View File

@@ -13,6 +13,7 @@ contributions of the following people:
Louis-Claude Canon (Contributing Author)
Scott Kostyshak (Contributing Author)
Renato Alves (Contributing Author)
Tomas Babej (Contributing Author)
The following submitted code, packages or analysis, and deserve special thanks:
@@ -112,6 +113,9 @@ The following submitted code, packages or analysis, and deserve special thanks:
Jeremy John Reeder
Roman Inflianskas
Łukasz Panek
V.Krishn
Jens Erat
Peter Rochen
Thanks to the following, who submitted detailed bug reports and excellent
suggestions:
@@ -233,10 +237,16 @@ suggestions:
dev-zero
Petteri
Black Ops Testing
Jens Erat
Leon Feng
Tomas Babej
Nick Person
Arnoud K
Ozgur Akgun
David Costa
Sujeevan Vijayakumaran
Scott Carter
Taisuke Hachimura
Martin
Alexandre de Verteuil
Scott M
Stefan Frühwirth
Pierre Campet

View File

@@ -1,5 +1,8 @@
cmake_minimum_required (VERSION 2.8)
set(CMAKE_LEGACY_CYGWIN_WIN32 0) # Remove when CMake >= 2.8.4 is required
if(POLICY CMP0037)
cmake_policy(SET CMP0037 OLD)
endif()
include (CheckFunctionExists)
include (CheckStructHasMember)
@@ -7,12 +10,40 @@ include (CheckStructHasMember)
set (HAVE_CMAKE true)
project (task)
set (PROJECT_VERSION "2.4.0")
set (PROJECT_VERSION "2.4.3")
OPTION(USE_GNUTLS "Build gnutls support." ON)
message ("CMAKE_SYSTEM_NAME ${CMAKE_SYSTEM_NAME}")
include (CheckCXXCompilerFlag)
# NOTE: If we are to actually use C++11 features, we should either require
# a compiler that supports the -std=c++11 flag or check for the
# features used.
# Relying on -std=c++0x or even -std=gnu++0x is highly volatile.
CHECK_CXX_COMPILER_FLAG("-std=c++11" _HAS_CXX11)
CHECK_CXX_COMPILER_FLAG("-std=c++0x" _HAS_CXX0X)
CHECK_CXX_COMPILER_FLAG("-std=gnu++0x" _HAS_GNU0X)
if (_HAS_CXX11)
set (_CXX11_FLAGS "-std=c++11")
elseif (_HAS_CXX0X)
message (WARNING "Enabling -std=c++0x draft compile flag. Your compiler does not support the standard '-std=c++11' option. Consider upgrading.")
set (_CXX11_FLAGS "-std=c++0x")
elseif (_HAS_GNU0X)
message (WARNING "Enabling -std=gnu++0x draft compile flag. Your compiler does not support the standard '-std=c++11' option. Consider upgrading.")
set (_CXX11_FLAGS "-std=gnu++0x")
else (_HAS_CXX11)
message (FATAL_ERROR "C++11 support missing. Try upgrading your C++ compiler. If you have a good reason for using an outdated compiler, please let us know at support@taskwarrior.org.")
endif (_HAS_CXX11)
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
set (LINUX true)
elseif (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
set (DARWIN true)
set (_CXX11_FLAGS "${_CXX11_FLAGS} -stdlib=libc++")
elseif (${CMAKE_SYSTEM_NAME} MATCHES "kFreeBSD")
set (KFREEBSD true)
elseif (${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
@@ -25,10 +56,20 @@ elseif (${CMAKE_SYSTEM_NAME} MATCHES "SunOS")
set (SOLARIS true)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "GNU")
set (GNUHURD true)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "CYGWIN")
set (CYGWIN true)
# NOTE: Not setting -std=gnu++0x leads to compile errors even with
# GCC 4.8.3, and debugging those leads to insanity. Adding this
# workaround instead of fixing Cygwin.
set (_CXX11_FLAGS "-std=gnu++0x")
else (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
set (UNKNOWN true)
endif (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
set (CMAKE_CXX_FLAGS "${_CXX11_FLAGS} ${CMAKE_CXX_FLAGS}")
set (CMAKE_CXX_FLAGS "-Wall -Wsign-compare -Wreturn-type ${CMAKE_CXX_FLAGS}")
if (NETBSD)
# Since readline, etc likely to be in /usr/pkg/lib, not standard library
# Otherwise will remove links during install
@@ -65,23 +106,15 @@ set (PACKAGE_TARNAME "${PACKAGE}")
set (PACKAGE_VERSION "${VERSION}")
set (PACKAGE_STRING "${PACKAGE} ${VERSION}")
message ("-- Looking for GnuTLS")
find_package (GnuTLS)
if (GNUTLS_FOUND)
set (HAVE_LIBGNUTLS true)
set (TASK_INCLUDE_DIRS ${TASK_INCLUDE_DIRS} ${GNUTLS_INCLUDE_DIR})
set (TASK_LIBRARIES ${TASK_LIBRARIES} ${GNUTLS_LIBRARIES})
endif (GNUTLS_FOUND)
#message ("-- Looking for pthread")
#find_path (PTHREAD_INCLUDE_DIR pthread.h)
#find_library (PTHREAD_LIBRARY NAMES pthread)
#if (PTHREAD_INCLUDE_DIR AND PTHREAD_LIBRARY)
# message ("-- Found pthread: ${PTHREAD_LIBRARY}")
# set (HAVE_LIBPTHREAD true)
# set (TASK_INCLUDE_DIRS ${TASK_INCLUDE_DIRS} ${PTHREAD_INCLUDE_DIR})
# set (TASK_LIBRARIES ${TASK_LIBRARIES} ${PTHREAD_LIBRARIES})
#endif (PTHREAD_INCLUDE_DIR AND PTHREAD_LIBRARY)
if (USE_GNUTLS)
message ("-- Looking for GnuTLS")
find_package (GnuTLS)
if (GNUTLS_FOUND)
set (HAVE_LIBGNUTLS true)
set (TASK_INCLUDE_DIRS ${TASK_INCLUDE_DIRS} ${GNUTLS_INCLUDE_DIR})
set (TASK_LIBRARIES ${TASK_LIBRARIES} ${GNUTLS_LIBRARIES})
endif (GNUTLS_FOUND)
endif (USE_GNUTLS)
check_function_exists (timegm HAVE_TIMEGM)
check_function_exists (get_current_dir_name HAVE_GET_CURRENT_DIR_NAME)

128
ChangeLog
View File

@@ -1,10 +1,129 @@
2.4.0 (2015-01-01) -
2.4.3 (2015-04-19) -
- TW-57 user defined attribute sort order (thanks to Max Muller).
- TW-70 urgency.user.keyword.<keyword>.coefficient=...
- TW-111 User-defined priorities.
- TW-1279 Make default.* not apply to recurring tasks.
- TW-1287 Make default.* not apply to synced tasks.
- TW-1539 user-defined urgency coefficients for priority values.
- TW-1541 Priority should be converted to UDA (in default taskrc) (thanks to
Tomas Babej).
- TW-1556 task hangs when modifying uda field with percent-encoded (url-encoded)
value (thanks to Stefan Frühwirth).
- TW-1578 Bash tab completion problems on first run
(thanks to Renato Alves and Ptolemarch).
- TW-1580 "modified" attribute no longer updated (thanks to David Patrick).
- TW-1581 Tasks with dependencies show wrong urgency values for the first
report run after a task in the dependency chain is completed/deleted (thanks
to Ulf Eliasson).
- TW-1583 Invalid ID displayed for first report after done/delete (thanks to
Ulf Eliasson).
- TW-1584 attr.{isnt,not} use partial matching.
- TW-1587 Fix and improve example on-exit hook, adjust to new hooks API
(thanks to Jochen Sprickerhof).
- TW-1588 Most Export scripts cannot deal with new export format (thanks to
Scott Carter).
- TW-1590 syntax of rcfile not documented (whitespace, line continuation)
(thanks to Scott M).
- TW-1591 add an option to see non-pending project with command task summary
(thanks to Pierre Campet).
- TW-1595 info command doesn't print urgency details, if urgency is negative
(thanks to Peter Rochen).
- Setting 'bulk' to zero is interpreted as infinity, which means there is no
amount of changes that is considered dangerous (thanks to Tomas Babej).
- Disable hooks in bash completion script. Hooks were previously able to
abort processing or output interfering data, breaking completion.
- Fix "task add due:tomorrow+3days" failing to work without spaces.
- Performance improvements:
+ Stops after measuring a fixed-width column format.
+ Reduced number of std::string copies.
------ current release ---------------------------
2.4.2 (2015-03-15) b9dc0813d9a8922b4cef9595033f133f9fbabf44
- TW-41 Tasks in subprojects are not counted in project completion (thanks
to Renato Alves).
- TW-1450 Projects command should trigger running garbage collector (thanks to
Tomas Babej).
- TW-1535 move default listing-break from list to ls (thanks to David Patrick).
- TW-1545 cc1plus: error: unrecognized command line option '-std=c++11' (thanks
to Petteri).
- TW-1546 column type due.remaining breaks colors on due tasks (thanks to
Renato Alves).
- TW-1547 Recur column is always shown even if no recurring task is displayed
(thanks to Renato Alves).
- TW-1549 task annotate hangs with specific text pattern (thanks to Alexandre
de Verteuil).
- TW-1550 _contexts helper-command (thanks to David Patrick).
- TW-1551 Unable to get a UDA value from DOM (thanks to Tomas Babej).
- Eliminated some code that is not UTF8-safe.
- Removed pthreads linkage.
- Implemented the context feature.
- Closed dangling pipes in execute (), resolving problems when a hook script
forks (thanks to Jens Erat).
- Re-enabled hook script feedback when exiting with 0 exit status.
- The 'info' command now shows virtual tags.
- Fixed major on-modify hooks regression where hooks could no longer modify
the tasks handed to them.
- 'task _version' now outputs "2.4.2 (git-ref)" when built from git. "2.4.2"
when built from release tarballs (thanks to Renato Alves).
2.4.1 (2015-02-16) 82e019a4a8b20de63d53b51d59b8d1c89d3c05b2
- TW-1457 Non-existent attributes are not properly handled (thanks to Tomas
Babej).
- TW-1484 The 'history' and 'ghistory' reports do not obey rc.color.label.
- TW-1486 task wait shows completed tasks which has a wait attribute (thanks to
Sujeevan Vijayakumaran).
- TW-1487 Task export exports some numeric attributes as strings (thanks to
Tomas Babej).
- TW-1491 Regression in deleting due dates (thanks to Jens Erat).
- TW-1492 compiling v2.4.0 using musl(libc) (thanks to V.Krishn).
- TW-1495 German translation for taskwarrior (thanks to Jens Erat).
- TW-1498 Filtering for presence of UDA matches all tasks (thanks to Ralph
Bean).
- TW-1501 Calc can't handle multi-digit numbers in some expressions (thanks to
Jeremy John Reeder).
- TW-1502 Successful on-add hook with no output confuses TW (thanks to Tomas
Babej).
- TW-1504 On-modify hook does not accept correct JSON format (thanks to Tomas
Babej).
- TW-1505 completely ignore mis-named hook scripts (thanks to Tomas Babej,
David Patrick).
- TW-1509 Hooks modifications performed on sync not sycning back (thanks to
Tomas Babej).
- TW-1510 Task can save empty value in the data backlog (thanks to Tomas Babej).
- TW-1517 Hook performance should be measured individually for each hook (thanks
to Tomas Babej).
- TW-1518 Misbehaving hooks silently break task processing (thanks to Wim
Schuermann).
- TW-1519 Testing suite forces taskd.trust="ignore hostname" (thanks to Renato
Alves).
- TW-1522 Date format doesn't like hyphens (thanks to Scott Carter).
- TW-1524 Build Broken (thanks to Jack).
- TW-1530 Multiple on-add hooks generating new tasks are ignored (thanks to
Tomas Babej).
- TW-1531 'task export' should handle recurrence (thanks to Tomas Babej).
- TW-1532 Hooks does not execute any script on Cygwin (thanks to Taisuke
Hachimura).
- TW-1534 Urgency coefficient for user project disables 'info' output (thanks to
Martin).
- TW-1542 Large numeric UDA values get rendered in scientific notation on export
(thanks to Ralph Bean).
- Fixed assorted color theme problems.
- Changed assorted reports so they do not use '.age' format for dates that are
in the future, because those are never shown with this format (thanks to
Sujeevan Vijayakumaran).
- New 'recurrence' configuration setting can disable recurring task generation.
2.4.0 (2015-01-01) 670102842c39bdc62ef84ae4b679a8f5a2d89523
- TD-42 Cannot compile taskd - GNUTLS_VERSION undefined in diag.cpp (thanks
to Michele Vetturi).
- TD-45 Fix preprocessor define (thanks to Jochen Sprickerhof).
- TD-55 TLSServer/Client need to include <errno.h> on Solaris (thanks to Tatjana
Heuser). Also applied to NetBSD.
- TD-55 TLSServer/Client need to include <errno.h> on Solaris (thanks to
Tatjana Heuser). Also applied to NetBSD.
- TD-56 File.cpp needs to include <string.h> on Solaris (thanks to Tatjana
Heuѕer).
- TD-57 taskdctl script assumes /bin/sh is /bin/bash (thanks to Tatjana Heuser).
@@ -45,6 +164,7 @@
- TW-52 "task add ... recur:2 months" interpreted as "2s" (thanks to jwhisnant).
- TW-55 Bulk edit recurring tasks without answering yes/no for each? (thanks to
Max Muller).
- TW-63 indicators for UDAs (thanks to David Patrick).
- TW-71 task ls/list/long/etc. should match contents of projects too (thanks to
Cory Donnelly).
- TW-72 extend info report with urgency column.
@@ -286,8 +406,6 @@
- Supports 'debug.parser' configuration setting.
- Supports 'color.label.sort' for column labels of sort columns.
------ current release ---------------------------
2.3.0 (2014-01-15) c4eb46507031b7dee839dcb932bb2a22b2f0d3a2
Features

View File

@@ -1,9 +1,16 @@
How to Build Taskwarrior
Satisfy the Requirements:
- gcc 4.4 or later, clang 2.9 or later or equivalent. This is because 2.4.1
marks the first step towards using modern C++11. Initially this involves
only feature N1984.
- libuuid
- gnutls (optional)
Obtain and build code:
$ git clone https://git.tasktools.org/scm/tm/task.git task.git
$ cd task.git
$ git checkout 2.4.0 # Latest dev branch
$ git checkout 2.4.3 # Latest dev branch
$ cmake -DCMAKE_BUILD_TYPE=debug . # debug or release. Default: neither.
$ make VERBOSE=1 # Shows details
@@ -18,8 +25,9 @@ How to Build Taskwarrior
current development branch. The source tarballs do not reflect HEAD, and do
not contain the test suite.
If you send us a patch, make sure that patch is made against git HEAD. We
cannot apply patches made against the tarball source.
If you send a patch (support@taskwarrior.org), make sure that patch is made
against git HEAD on the development branch. We cannot apply patches made
against the tarball source, or master.
General Statement
This file is intended to convey the current efforts, priorities and needs of
@@ -181,12 +189,11 @@ Work in Progress
Current Codebase Condition
'master' branch:
- 2.3.0 Current release, locked.
- 2.4.2 Current release, locked.
'2.4.0' branch:
- Current development branch with new command line parser, expressions,
and heading for beta soon.
'2.4.3' branch:
- Current development branch no plans yet.
---
2014-10-12 Updated for 2.4.0
2015-03-15 Updated for 2.4.3

View File

@@ -24,7 +24,7 @@ Command Line Parsing
determines whether subsequent arguments are interpreted as part of a filter or
set of modifications.
The Parser object is fed command line arguments, then through a succession of
The CLI object is fed command line arguments, then through a succession of
calls builds and annotates a parse tree. To help with this, the Lexer is
used to break up strings into tokens.

32
INSTALL
View File

@@ -8,9 +8,17 @@ Pre-requisites
--------------
You will need the CMake build system installed in order to build taskwarrior
from source.
from source. More information on cmake can be obtained at http://cmake.org
More information on cmake can be obtained at http://cmake.org
You will need a C++ compiler that supports C++11 N1984, which includes:
- gcc 4.4 (released 2009-04-21)
- clang 2.9 (released 2011-04-06)
In addition:
- uuid lib
- gnutls (optional - for syncing)
It is HIGHLY RECOMMENDED that you build with a library that provides uuid_*
functions, such as libuuid.
@@ -92,10 +100,12 @@ Currently the defined languages are:
eng_USA 1
esp_ESP 2
deu_DEU 3
fra_FRA 4
ita_ITA 5
por_PRT 6
epo_RUS 7
pol_POL 8
Uninstallation
--------------
@@ -131,9 +141,7 @@ Cygwin
Darwin, 32bit
The taskwarrior packages will not work on a 32-bit OSX installation on Core
Duo hardware. You will need to build Taskwarrior from source, and use this
configure command:
Taskwarrior needs to be built from source, and use this configure command:
cmake -DCMAKE_C_FLAGS=-m32 -DCMAKE_CXX_FLAGS=-m32 -DCMAKE_EXE_LINKER_FLAGS=-m32 .
@@ -144,6 +152,18 @@ Darwin, 32bit
http://stackoverflow.com/questions/6077414/cmake-how-to-set-the-ldflags-in-cmakelists-txt
OpenBSD
In order to build Taskwarrior 2.4.2+, you might need to install a newer GCC
version from ports or packages.
Afterwards, run
cmake -DCMAKE_CXX_COMPILER=eg++ .
and build normally.
See: https://bug.tasktools.org/browse/TW-1579
Troubleshooting
---------------

105
NEWS
View File

@@ -1,98 +1,45 @@
New Features in taskwarrior 2.4.0
New Features in taskwarrior 2.4.3
- The 'show' command displays default configuration values, when appropriate.
- Removed deprecated commands 'push', 'pull' and 'merge'.
- Portuguese (por-PRT), French (fra-FRA), Esperanto (epo-RUS) and Polish
(pol-POL) localizations.
- Better handling for deletion of recurring tasks.
- New virtual tags: YESTERDAY, TOMORROW, READY, PENDING, COMPLETED, DELETED,
TAGGED.
- The '_get' command properly uses exit codes.
- Regular expressions are now enabled by default.
- The 'filter' verbosity token shows the complete filter used for the last
command.
- The 'new-uuid' verbosity token shows the UUID of newly created tasks.
- The 'info' report now breaks down urgency values.
- New 'color.until' color rule.
- Using a non-zero 'urgency.inherit.coefficient' value means a task inherits
the urgency values of the dependency chain.
- Listing breaks now supported. See 'man taskrc'.
- New fish shell completion script.
- The filter form 'name:value' now maps to the partial match operator '=',
rather than the exact match operator, '=='. This means that dates now
match on the day by default, not the time also.
- UDA indicator format.
- Hooks.
- New and updated holidays.rc files.
- UDA string attributes with allowable values can now define their sort order.
- The 'priority' attribute is now a UDA, configured by default.
New commands in taskwarrior 2.4.0
New commands in taskwarrior 2.4.3
- New 'calc' command (and standalone utility) for quick command line
calculations.
-
New configuration options in taskwarrior 2.4.0
New configuration options in taskwarrior 2.4.3
- The 'taskd.trust' setting is now a tri-state, supporting values 'strict',
'ignore hostname' and 'allow all', for server certificate validation.
- New theme: dark-gray-blue-256.theme
- The 'allow.empty.filter' setting can be set to 'no' to disallow the
potentially dangerous combination of write commands and empty filters.
- New truncated_count column style for the description field which as the
name says is a combination of the existing truncated and count styles.
- New 'hooks' setting is a master control switch for hook processing.
- New 'debug.hooks' for debugging hook scripts.
- New 'debug.parser' for debugging parser issues scripts.
- New 'color.label.sort' is used to color the column label of sort columns.
- New 'urgency.uda.<name>.<value>' allows specific UDA values to affect
urgency.
- New 'recurrence.confirmation' which allows bypassing confirmation for
changes to recurring tasks, by accepting, rejecting or prompting.
- New 'uda.<name>.indicator' to override the UDA indicator.
- Setting 'bulk' to zero is interpreted as infinity, which means there is no
amount of changes that is considered dangerous.
- The 'urgency.user.keyword.<keyword>.coefficient' setting allows tasks with
specific words in the description to have adjusted urgency.
- The 'summary.all.projects' setting shows all projects in the 'summary'
reportş instead of just those with pending tasks.
Newly deprecated features in taskwarrior 2.4.0
Newly deprecated features in taskwarrior 2.4.3
- The alias '_query' is deprecated.
- Bare word search terms are deprecated, so 'task pattern list' will not
be supported in future releases, instead requiring 'task /pattern/ list'.
- The 'limit' pseudo attribute will change to 'rc.limit' in a future release.
-
Removed features in 2.4.0
Removed features in 2.4.3
- Version 1.x column names no longer supported.
- Version 1.x sort columns no longer supported.
- Old-style color names including underscores are no longer supported.
- Removed priority counts from the 'projects' commands.
- Removed the unused 'locale' configuration variable.
- Removed the unused 'patterns' confguration variable.
- Removed the obsolete 'task-faq.5' man page.
- Removed the obsolete 'task-tutorial.5' man page.
- Removed the obsolete 'tasksh.1' man page.
- Removed the 'tasksh' program from the Taskwarrior distribution. It is now
a separate project.
- Removed the 'complete.all.projects' setting that was not used.
- Removed support for 'report.X.limit', which can now be set in the report
filter 'limit:N'.
- The 'total active time' information is removed from the 'info' report.
This was being misinterpreted as support for time tracking.
- The 'shadow file' feature is removed, but replaced with an example hook
script that performs the same function..
- The 'priority.long' and 'priority.short' column formats are no longer
supported.
- The 'default.priority' setting is now 'uda.priority.default'.
Known Issues
- On Cygwin, case-insensitive regex searches are broken. The default is
now case-sensitive for Cygwin only.
- https://bug.tasktools.org
- https://bug.tasktools.org/
Taskwarrior has been built and tested on the following configurations:
* OS X 10.10 Yosemite, 10.9 Mavericks
* Fedora 20 Heisenbug
* Ubuntu 14.04 Trusty Tahr
* Debian 7.0 Wheezy (stable)
* Arch 2014.05
* FreeBSD 10
* Cygwin 1.7.29, 1.7.32
* OS X
* Fedora
* Ubuntu
* Debian
* Arch
* FreeBSD
* Cygwin
---

View File

@@ -1,4 +1,4 @@
.TH task-color 5 2015-01-01 "${PACKAGE_STRING}" "User Manuals"
.TH task-color 5 2015-04-19 "${PACKAGE_STRING}" "User Manuals"
.SH NAME
task-color \- A color tutorial for the taskwarrior command line todo manager.

View File

@@ -1,4 +1,4 @@
.TH task-sync 5 2015-01-01 "${PACKAGE_STRING}" "User Manuals"
.TH task-sync 5 2015-04-19 "${PACKAGE_STRING}" "User Manuals"
.SH NAME
task-sync \- A discussion and tutorial for the various task(1) data

View File

@@ -1,4 +1,4 @@
.TH task 1 2015-01-01 "${PACKAGE_STRING}" "User Manuals"
.TH task 1 2015-04-19 "${PACKAGE_STRING}" "User Manuals"
.SH NAME
task \- A command line todo manager.
@@ -379,6 +379,22 @@ time from the specified task.
Miscellaneous subcommands either accept no command line arguments, or accept
non-standard arguments.
.TP
.B task calc <expression>
Evaluates an algebraic expression. Can be used to test how TaskWarrior
parses and evaluates the expression given on the command line.
Examples:
task calc 1 + 1
2
task calc now + 8d
2015-03-26T18:06:57
task calc eom
2015-03-31T23:59:59
.TP
.B task config [name [value | '']]
Add, modify and remove settings directly in the taskwarrior configuration.
@@ -396,6 +412,46 @@ Finally, this command removes any 'name=...' entry from the .taskrc file:
task config name
.TP
.B task context <name>
Sets the currectly active context. See the CONTEXT section.
Example:
task context work
.TP
.B task context delete <name>
Deletes the context with the name <name>. If the context being deleted is currently
set as active, it will be unset.
Example:
task context delete work
.TP
.B task context define <name> <filter>
Defines a new context with name <name> and definition <filter>. This command
does not affect the currently set context, just adds a new context definition.
Examples:
task context define work project:Work
task context define home project:Home or +home
task context define superurgent due:today and +urgent
.TP
.B task context list
Outputs a list of available contexts along with their definitions.
.TP
.B task context none
Unsets the currently active context, if any was set.
.TP
.B task context show
Shows the currently active context, along with its definition.
.TP
.B task diagnostics
Shows diagnostic information, of the kind needed when reporting a problem.
@@ -474,6 +530,10 @@ Generates a list of all commands, for autocompletion purposes.
.B task _config
Lists all supported configuration variables, for completion purposes.
.TP
.B task _context
Lists all available context variables, for completion purposes.
.TP
.B task <filter> _ids
Shows only the IDs of matching tasks, in the form of a list.
@@ -962,6 +1022,66 @@ biannual, biyearly, 2yr
Every two years.
.RE
.SH CONTEXT
Context is a user-defined filter, which is automatically applied to all commands
that filter the task list. In particular, any report command will have its
result affected by the current active context.
$ task list
ID Age Project Description Urg
1 2d Sport Run 5 miles 1.42
2 1d Home Clean the dishes 1.14
$ task context home
Context 'home' set. Use 'task context none' to remove.
$ task list
ID Age Project Description Urg
2 1d Home Clean the dishes 1.14
Context 'home' set. Use 'task context none' to remove.
As seen in the example above, context is applied by specifying its name to the
"context" command. To change the currently applied context, just pass the
new context's name to the 'context' command.
To unset any context, use the 'none' subcommand.
$ task context none
Context unset.
$ task list
ID Age Project Description Urg
1 2d Sport Run 5 miles 1.42
2 1d Home Clean the dishes 1.14
Context can be defined using the 'define' subcommand, specifying both the name
of the new context, and it's assigned filter.
$ task context define home
Are you sure you want to add 'context.home' with a value of 'project:Home'? (yes/no) yes
Context 'home' successfully defined.
To remove the definition, use the 'delete' subcommand.
$ task context delete home
Are you sure you want to remove 'context.home'? (yes/no) yes
Context 'home' successfully undefined.
To check what is the currently active context, use the 'show' subcommand.
$ task context show
Context 'home' with filter 'project:Home' is currently applied.
Contexts can store arbitrarily complex filters.
$ task context define family project:Family or +paul or +nancy
Are you sure you want to add 'context.home' with a value of 'project:Family or +paul or +nancy'? (yes/no) yes
Context 'family' successfully defined.
Contexts are permanent, and the currently set context name is stored in the
"context" configuration variable. The context definition is stored in the
"context.<name>" configuration variable.
.SH COMMAND ABBREVIATION
All taskwarrior commands may be abbreviated as long as a unique prefix is used,
for example:
@@ -1057,11 +1177,11 @@ the 'data.location' configuration setting of the task data directory.
For examples please see the online documentation starting at
.RS
<http://taskwarrior.org/projects/taskwarrior/wiki>
<http://taskwarrior.org/docs>
.RE
Note that the online documentation is more detailed and more current than this
man page.
Note that the online documentation can be more detailed and more current than
this man page.
.SH FILES

View File

@@ -1,4 +1,4 @@
.TH taskrc 5 2015-01-01 "${PACKAGE_STRING}" "User Manuals"
.TH taskrc 5 2015-04-19 "${PACKAGE_STRING}" "User Manuals"
.SH NAME
taskrc \- Configuration details for the task(1) command
@@ -11,7 +11,7 @@ taskrc \- Configuration details for the task(1) command
.B TASKRC=<directory-path>/.taskrc task ...
.SH DESCRIPTION
.B taskwarrior
.B Taskwarrior
obtains its configuration data from a file called
.I .taskrc
\&. This file is normally located in the user's home directory:
@@ -55,37 +55,50 @@ default, sample
.I .taskrc
file in the user's home directory.
The taskwarrior configuration file consists of a series of assignments in each
line. The assignments have the syntax:
The .taskrc file follows a very simply syntax defining name/value pairs:
.RS
<name-of-configuration-variable>=<value-to-be-set>
<name> = <value>
.RE
where:
.RS
.TP
<name-of-configuration-variable>
is one of the variables described below
.TP
<value-to-be-set>
is the value the variable is to be set to.
.RE
and set a configuration variable to a certain value. The equal sign ("=") is
used to separate the variable name from the value to be set.
The hash mark, or pound sign ("#") is used as a comment character. It can be
used to annotate the configuration file. All text after the character to the end
of the line is ignored.
The configuration file supports UTF8 as well as JSON encoding, such as \\uNNNN.
There may be whitespace around <name>, '=' and <value>, and it is ignored.
Whitespace within the <value> is left intact.
Whitespace is not permitted in comma-separated lists.
The entry must be on a single line, no continuations.
Values support UTF8 as well as JSON encoding, such as \\uNNNN.
Note that taskwarrior is flexible about the values used to represent Boolean
items. You can use "on", "yes", "y", "1" and "true".
Anything else means "off".
.RS
include <file>
.RE
There may be whitespace around 'include' and <file>. The file may be an
absolute or relative path, and the special character '~' is expanded to mean
$HOME.
The entry must be on a single line, no continuations.
.RS
# <comment>
.RE
A comment consists of the character '#', and extends from the '#' to the end
of the line. There is no way to comment a multi-line block. There may be
blank lines.
Almost every value has a default setting, and an empty .taskrc file is one
that makes use of every default. The contents of the .taskrc file therefore
represent overrides of the default values. To remove a default value completely
there must be an entry like this:
.RS
<name> =
.RE
This entry overrides the default value with a blank value.
.SH EDITING
You can edit your .taskrc file by hand if you wish, or you can use the 'config'
command. To permanently set a value in your .taskrc file, use this command:
@@ -282,14 +295,13 @@ and project. The others are sent to standard output.
.TP
.B confirmation=yes
May be "yes" or "no", and determines whether taskwarrior will ask for
confirmation before deleting a task, performing bulk changes, or the undo
command. The default value is "yes". Consider leaving this setting as "yes",
for safety.
confirmation before deleting a task or performing the undo command. The default
value is "yes". Consider leaving this setting as "yes", for safety.
.TP
.B allow.empty.filter=yes
An empty filter combined with a write command is potentially a way to modify
all tasks by mistkae, and when this is detected, confirmation is required.
all tasks by mistake, and when this is detected, confirmation is required.
Setting this to 'no' means that it is an error to use a write command with no
filter.
@@ -313,25 +325,30 @@ Controls padding between columns of the report output. Default is "1".
.TP
.B bulk=3
Is a number, defaulting to 3. When this number or greater of tasks are modified
in a single command, confirmation will be required, unless the
in a single command, confirmation will be required, regardless of the value of
.B confirmation
variable is "no".
variable. The special value bulk=0 is treated as an infinity.
This is useful for preventing large-scale unintended changes.
.TP
.B nag=You have more urgent tasks.
This may be a string of text, or blank. It is used as a prompt when a task is
started or completed that is not considered high priority. Default value is:
You have more urgent tasks. It is a gentle reminder that you are contradicting
your own priority settings.
This may be a string of text, or blank. It is used as a prompt when a task is
started of completed, when there are other tasks with a higher urgency.
Default value is: 'You have more urgent tasks'.
It is a gentle reminder that you are contradicting your own urgency settings.
.TP
.B list.all.projects=yes
.B list.all.projects=no
May be yes or no, and determines whether the 'projects' command lists all the project
names you have used, or just the ones used in active tasks. The default value is
"no".
.TP
.B summary.all.projects=no
If set to yes, shows all projects in the summary report, even if there are no
pending tasks. The default value is "no".
.TP
.B complete.all.tags=yes
May be yes or no, and determines whether the tab completion scripts consider all
@@ -412,6 +429,11 @@ The character or string to show in the depends.indicator column. Defaults to +.
.B uda.<name>.indicator=U
The character or string to show in the <uda>.indicator column. Defaults to U.
.TP
.B recurrence=yes
Controls whether recurrence is enable, and whether recurring tasks continue to
generate new task instances. Defaults to 'yes'.
.TP
.B recurrence.confirmation=prompt
Controls whether changes to recurring tasks propagates to other child tasks with
@@ -616,6 +638,10 @@ field that is set. Otherwise, they are set to the corresponding values of
.RE
.RE
.TP
.B date.iso=yes
Enables ISO-8601 date support. The default value is "yes".
.TP
.B weekstart=Sunday
Determines the day a week starts. Valid values are Sunday or Monday only. The
@@ -820,18 +846,6 @@ Task has at least one tag.
.B color.recurring
Task is recurring.
.br
.B color.pri.H
Task has priority H.
.br
.B color.pri.M
Task has priority M.
.br
.B color.pri.L
Task has priority L.
.br
.B color.pri.none
Task has no priority.
.br
.B color.completed
Task is completed.
.br
@@ -1050,10 +1064,6 @@ Urgency inherited from dependency chain
.RS
Urgency coefficient for due dates
.RE
.B urgency.priority.coefficient=6.0
.RS
Urgency coefficient for priorities
.RE
.B urgency.waiting.coefficient=-3.0
.RS
Urgency coefficient for waiting status
@@ -1094,6 +1104,10 @@ Specific tag coefficient.
.RS
Specific project coefficient.
.RE
.B urgency.user.keyword.<keyword>.coefficient=...
.RS
Specific description keyword coefficient.
.RE
.B urgency.uda.<name>.coefficient=...
.RS
Presence/absence of UDA data.
@@ -1116,13 +1130,6 @@ Provides a default project name for the
.I task add
command, if you don't specify one. The default is blank.
.TP
.B
default.priority=M
Provides a default priority for the
.I task add
command, if you don't specify one. The default is blank.
.TP
.B
default.due=...
@@ -1328,19 +1335,56 @@ Provides a default report label for the UDA called '<name>'.
For type 'string' UDAs only, this provides a comma-separated list of acceptable
values. In this example, the '<name>' UDA may only contain values 'A', 'B',
or 'C', but may also contain no value.
Note that the order of the value is important, and denotes the sort order from
highest ('A') to lowest ('C').
Note that a blank value is permitted.
.RE
.TP
.B uda.<name>.default=...
.RS
Provides a default value for the UDA called '<name>'.
.RE
.TP
.B Example 'estimate' UDA
This example shows an 'estimate' UDA that stores specific values for the size
of a task.
of a task. Note the blank value after 'trivial'.
.RS
.B uda.estimate.type=string
.br
.B uda.estimate.label=Size Estimate
.br
.B uda.estimate.values=trivial,small,medium,large,huge
.B uda.estimate.values=huge,large,medium,small,trivial,
.RE
.RS
Note that the value are sorted
huge > large > medium > small > trivial > ''
.RE
.SS CONTEXT
Context setting is a mechanism which allows the user to set a permanent filter,
thus avoiding the need to specify one filter repeatedly. More details on usage
can be found in the task(1) manpage.
The current context is stored in the taskrc file, along with definitions for
all user provided contexts.
.TP
.B context=<name>
.RS
Stores the value of the currently active context.
.RE
.TP
.B context.<name>=<filter>
.RS
Stores the definition of the context with the name <name>.
.RE
.SS SYNC

View File

@@ -1,15 +1,24 @@
Themes
To generate samples of themes, first execute the 'run' script to generate the
To generate samples of themes, first execute the 'setup' script to generate the
sample data. Note that this data may need to be tweaked to include qualities
that need to be illustrated in theme sample.
Then edit the 'rc' file to include the desired theme file.
Using a dark-background terminal (black recommended), run the following:
Then run 'per' to run a few commands for each theme.
run.dark
Note that this will require that the terminal window be switched between a black
and white background to properly show the light and dark themes.
Using a light-background terminal, run the following:
run.light
Using a solarized dark terminal, run the following:
run.solar.dark
Using a solarized light terminal, run the following:
run.solar.light
Note that for the solarized themes, the terminal color palette needs to be set
to specific colors.

View File

@@ -1,23 +0,0 @@
for theme in $PWD/../../rc/*.theme
do
cat <<EOF >>x
data.location=.
confirmation=off
_forcecolor=on
include $theme
EOF
echo "--- $theme -----------------------------------------------------"
echo '$ task list'
task rc:x list
echo '$ task summary'
task rc:x summary
echo '$ task ghistory'
task rc:x ghistory
echo '$ task calendar'
task rc:x calendar
echo '$ task burndown.daily'
task rc:x burndown.daily
done

View File

@@ -21,6 +21,8 @@ include $theme
EOF
echo "--- $theme -----------------------------------------------------"
echo '$ task color legend'
task rc:x color legend
echo '$ task list'
task rc:x list
echo '$ task summary'

24
doc/misc/themes/run.default Executable file
View File

@@ -0,0 +1,24 @@
#!/bin/bash
cat <<EOF >x
data.location=.
confirmation=off
detection=off
_forcecolor=on
default.height=24
verbose=off
EOF
echo "--- DEFAULT -----------------------------------------------------"
echo '$ task color legend'
task rc:x color legend
echo '$ task list'
task rc:x list
echo '$ task summary'
task rc:x summary
echo '$ task ghistory'
task rc:x ghistory
echo '$ task calendar'
task rc:x calendar
echo '$ task burndown.daily'
task rc:x burndown.daily

View File

@@ -14,6 +14,8 @@ include $theme
EOF
echo "--- $theme -----------------------------------------------------"
echo '$ task color legend'
task rc:x color legend
echo '$ task list'
task rc:x list
echo '$ task summary'

View File

@@ -13,6 +13,8 @@ include $theme
EOF
echo "--- $theme -----------------------------------------------------"
echo '$ task color legend'
task rc:x color legend
echo '$ task list'
task rc:x list
echo '$ task summary'

View File

@@ -13,6 +13,8 @@ include $theme
EOF
echo "--- $theme -----------------------------------------------------"
echo '$ task color legend'
task rc:x color legend
echo '$ task list'
task rc:x list
echo '$ task summary'

View File

@@ -33,15 +33,15 @@ color.alternate=
color.header=yellow
color.footnote=yellow
color.warning=bold red
color.error=yellow
color.debug=yellow
color.error=white on red
color.debug=blue
# Task state
color.completed=green on white
color.deleted=red on white
color.completed=
color.deleted=
color.active=black on bright green
color.recurring=magenta
color.scheduled=on green
color.scheduled=white on green
color.until=
color.blocked=black on white
color.blocking=black on bright white
@@ -50,10 +50,9 @@ color.blocking=black on bright white
color.project.none=
# Priority
color.pri.H=bold white
color.pri.M=white
color.pri.L=
color.pri.none=
color.uda.priority.H=bold white
color.uda.priority.M=white
color.uda.priority.L=
# Tags
color.tag.next=bold yellow

View File

@@ -29,31 +29,30 @@ rule.precedence.color=deleted,completed,active,keyword.,tag.,uda.,project.,overd
# General decoration
color.label=
color.label.sort=
color.alternate=on gray1
color.alternate=on gray2
color.header=color3
color.footnote=color3
color.warning=bold red
color.error=color3
color.debug=color3
color.error=white on red
color.debug=color4
# Task state
color.completed=rgb010 on white
color.deleted=rgb100 on white
color.completed=
color.deleted=
color.active=rgb555 on rgb410
color.recurring=rgb013
color.scheduled=on rgb001
color.until=
color.blocked=white on color8
color.blocking=white on color15
color.blocking=black on color15
# Project
color.project.none=
# Priority
color.pri.H=color255
color.pri.L=color245
color.pri.M=color250
color.pri.none=
color.uda.priority.H=color255
color.uda.priority.L=color245
color.uda.priority.M=color250
# Tags
color.tag.next=rgb440

View File

@@ -29,19 +29,19 @@ rule.precedence.color=deleted,completed,active,keyword.,tag.,uda.,project.,overd
# General decoration
color.label=
color.label.sort=
color.alternate=on gray1
color.alternate=on gray2
color.header=rgb013
color.footnote=rgb013
color.warning=bold red
color.error=rgb013
color.error=white on red
color.debug=rgb013
# Task state
color.completed=rgb001 on white
color.deleted=rgb100 on white
color.completed=
color.deleted=
color.active=rgb045 on rgb015
color.recurring=rgb115
color.scheduled=on rgb011
color.scheduled=on rgb012
color.until=
color.blocked=white on rgb001
color.blocking=white on rgb002
@@ -50,10 +50,9 @@ color.blocking=white on rgb002
color.project.none=
# Priority
color.pri.H=rgb035
color.pri.L=rgb015
color.pri.M=rgb025
color.pri.none=
color.uda.priority.H=rgb035
color.uda.priority.L=rgb015
color.uda.priority.M=rgb025
# Tags
color.tag.next=rgb550
@@ -76,8 +75,8 @@ color.history.delete=color0 on rgb035
color.history.done=color0 on rgb025
# Report: summary
color.summary.background=white on color0
color.summary.bar=white on rgb003
color.summary.background=on rgb001
color.summary.bar=on rgb114
# Command: calendar
color.calendar.due.today=color0 on color252

View File

@@ -29,16 +29,16 @@ rule.precedence.color=deleted,completed,active,keyword.,tag.,uda.,project.,overd
# General decoration
color.label=
color.label.sort=
color.alternate=on gray1
color.alternate=on gray2
color.header=color0 on gray11
color.footnote=on gray5
color.warning=bold red
color.error=red on white
color.debug=black on white
color.error=white on red
color.debug=blue
# Task state
color.completed=black on white
color.deleted=black on white
color.completed=
color.deleted=
color.active=black on gray18
color.recurring=
color.scheduled=on gray8
@@ -50,10 +50,9 @@ color.blocking=bold white
color.project.none=
# Priority
color.pri.H=gray21
color.pri.M=gray18
color.pri.L=gray12
color.pri.none=gray18
color.uda.priority.H=gray21
color.uda.priority.M=gray18
color.uda.priority.L=gray12
# Tags
color.tag.next=bold white
@@ -61,7 +60,7 @@ color.tag.none=
color.tagged=
# Due
color.due=on gray2
color.due=on gray3
color.due.today=on gray4
color.overdue=on gray6
@@ -76,12 +75,12 @@ color.history.delete=black on gray10
color.history.done=gray5 on gray23
# Report: summary
color.summary.bar=on gray15
color.summary.background=on black
color.summary.bar=on gray12
color.summary.background=on gray5
# Command: calendar
color.calendar.due=on gray8
color.calendar.due.today=on gray15
color.calendar.due.today=black on gray15
color.calendar.holiday=black on gray20
color.calendar.overdue=gray2 on gray10
color.calendar.today=bold white
@@ -89,7 +88,7 @@ color.calendar.weekend=on gray2
color.calendar.weeknumber=gray6
# Command: sync
color.sync.add=gray15 on gray5
color.sync.added=gray15 on gray5
color.sync.changed=black on gray10
color.sync.rejected=gray5 on gray23

View File

@@ -29,12 +29,12 @@ rule.precedence.color=deleted,completed,active,keyword.,tag.,uda.,project.,overd
# General decoration
color.label=
color.label.sort=
color.alternate=on gray0
color.alternate=on gray2
color.header=gray10
color.footnote=gray10
color.warning=
color.error=rgb500
color.debug=rgb500
color.error=white on red
color.debug=blue
# Task state
color.completed=
@@ -44,16 +44,15 @@ color.recurring=
color.scheduled=
color.until=
color.blocked=bold gray10 on gray4
color.blocking=bold gray18 on gray6
color.blocking=gray18 on gray6
# Project
color.project.none=
# Priority
color.pri.H=
color.pri.L=
color.pri.M=
color.pri.none=
color.uda.priority.H=
color.uda.priority.L=
color.uda.priority.M=
# Tags
color.tag.next=
@@ -66,18 +65,18 @@ color.due.today=color0 on rgb024
color.overdue=color0 on rgb035
# Report: burndown
color.burndown.pending=on gray9
color.burndown.started=on gray16
color.burndown.done=on rgb013
color.burndown.pending=white on gray9
color.burndown.started=black on gray16
color.burndown.done=white on rgb013
# Report: history
color.history.add=on gray9
color.history.delete=black on gray23
color.history.done=black on rgb013
color.history.add=white on gray6
color.history.delete=black on gray18
color.history.done=black on rgb024
# Report: summary
color.summary.bar=on rgb012
color.summary.background=on color0
color.summary.background=on gray2
# Command: calendar
color.calendar.due=color0 on gray10
@@ -94,6 +93,6 @@ color.sync.changed=gray15
color.sync.rejected=gray23
# Command: undo
color.undo.before=green
color.undo.after=red
color.undo.before=rgb013
color.undo.after=rgb035

View File

@@ -29,19 +29,19 @@ rule.precedence.color=deleted,completed,active,keyword.,tag.,uda.,project.,overd
# General decoration
color.label=
color.label.sort=
color.alternate=on gray1
color.alternate=on gray2
color.header=rgb031
color.footnote=rgb031
color.warning=bold red
color.error=rgb031
color.debug=rgb031
color.warning=rgb020
color.error=white on red
color.debug=blue
# Task state
color.completed=rgb020 on white
color.deleted=rgb200 on white
color.completed=
color.deleted=
color.active=rgb050 on rgb010
color.recurring=rgb151
color.scheduled=on rgb011
color.scheduled=black on rgb031
color.until=
color.blocked=white on rgb010
color.blocking=white on rgb020
@@ -50,10 +50,9 @@ color.blocking=white on rgb020
color.project.none=
# Priority
color.pri.H=rgb050
color.pri.L=rgb010
color.pri.M=rgb030
color.pri.none=
color.uda.priority.H=rgb050
color.uda.priority.L=rgb010
color.uda.priority.M=rgb030
# Tags
color.tag.next=rgb440
@@ -76,7 +75,7 @@ color.history.delete=color0 on rgb050
color.history.done=color0 on rgb030
# Report: summary
color.summary.background=white on color0
color.summary.background=white on gray3
color.summary.bar=white on rgb030
# Command: calendar
@@ -95,4 +94,4 @@ color.sync.rejected=rgb010
# Command: undo
color.undo.after=rgb053
color.undo.before=rgb031
color.undo.before=rgb021

View File

@@ -29,19 +29,19 @@ rule.precedence.color=deleted,completed,active,keyword.,tag.,uda.,project.,overd
# General decoration
color.label=
color.label.sort=
color.alternate=on gray1
color.alternate=on gray2
color.header=rgb100
color.footnote=rgb100
color.warning=bold red
color.error=rgb100
color.debug=rgb100
color.warning=red
color.error=white on red
color.debug=blue
# Task state
color.completed=rgb020 on white
color.deleted=rgb200 on white
color.completed=
color.deleted=
color.active=rgb500 on rgb100
color.recurring=rgb511
color.scheduled=on rgb201
color.scheduled=white on rgb311
color.until=
color.blocked=white on rgb100
color.blocking=white on rgb200
@@ -50,13 +50,12 @@ color.blocking=white on rgb200
color.project.none=
# Priority
color.pri.H=rgb500
color.pri.L=rgb300
color.pri.M=rgb400
color.pri.none=
color.uda.priority.H=rgb500
color.uda.priority.L=rgb300
color.uda.priority.M=rgb400
# Tags
color.tag.next=rgb440
color.tag.next=rgb511
color.tag.none=
color.tagged=color246
@@ -94,6 +93,6 @@ color.sync.changed=rgb411
color.sync.rejected=rgb200
# Command: undo
color.undo.after=rgb503
color.undo.before=rgb301
color.undo.after=rgb511
color.undo.before=rgb200

View File

@@ -33,15 +33,15 @@ color.alternate=on gray2
color.header=rgb013
color.footnote=rgb013
color.warning=
color.error=rgb013
color.debug=rgb013
color.error=white on red
color.debug=blue
# Task state
color.completed=
color.deleted=
color.active=rgb445 on rgb213
color.recurring=rgb115
color.scheduled=
color.scheduled=white on rgb113
color.until=
color.blocked=white on rgb101
color.blocking=white on rgb202
@@ -50,10 +50,9 @@ color.blocking=white on rgb202
color.project.none=
# Priority
color.pri.H=rgb404
color.pri.M=rgb304
color.pri.L=rgb325
color.pri.none=
color.uda.priority.H=rgb404
color.uda.priority.M=rgb304
color.uda.priority.L=rgb325
# Tags
color.tag.next=
@@ -61,9 +60,9 @@ color.tag.none=
color.tagged=rgb334
# Due
color.due=rgb055
color.due.today=rgb533
color.overdue=color9
color.due=rgb015
color.due.today=rgb125
color.overdue=color5
# Report: burndown
color.burndown.pending=on rgb103
@@ -76,14 +75,14 @@ color.history.done=color0 on rgb205
color.history.delete=color0 on rgb305
# Report: summary
color.summary.bar=white on rgb103
color.summary.background=white on color0
color.summary.bar=white on rgb104
color.summary.background=white on rgb001
# Command: calendar
color.calendar.due=color0 on rgb325
color.calendar.due.today=color0 on rgb404
color.calendar.holiday=color15 on rgb022
color.calendar.overdue=color0 on color9
color.calendar.holiday=color15 on rgb102
color.calendar.overdue=color0 on color5
color.calendar.today=color15 on rgb103
color.calendar.weekend=gray12 on gray3
color.calendar.weeknumber=rgb104

View File

@@ -33,15 +33,15 @@ color.alternate=on gray2
color.header=rgb031
color.footnote=rgb031
color.warning=
color.error=rgb031
color.debug=rgb031
color.error=white on red
color.debug=blue
# Task state
color.completed=
color.deleted=
color.active=rgb451 on rgb310
color.active=rgb451 on rgb320
color.recurring=rgb343
color.scheduled=
color.scheduled=black on rgb441
color.until=
color.blocked=white on rgb110
color.blocking=white on rgb220
@@ -50,10 +50,9 @@ color.blocking=white on rgb220
color.project.none=
# Priority
color.pri.H=rgb450
color.pri.M=rgb030
color.pri.L=rgb010
color.pri.none=
color.uda.priority.H=rgb450
color.uda.priority.M=rgb030
color.uda.priority.L=rgb010
# Tags
color.tag.next=
@@ -61,9 +60,9 @@ color.tag.none=
color.tagged=rgb342
# Due
color.due=rgb420
color.due.today=rgb410
color.overdue=rgb400
color.due=rgb440
color.due.today=rgb430
color.overdue=rgb420
# Report: burndown
color.burndown.pending=on rgb110
@@ -71,19 +70,19 @@ color.burndown.started=on rgb430
color.burndown.done=on gray4
# Report: history
color.history.add=color0 on rgb010
color.history.done=color0 on rgb030
color.history.delete=color0 on rgb050
color.history.add=color0 on rgb110
color.history.done=color0 on rgb430
color.history.delete=white on gray4
# Report: summary
color.summary.bar=white on rgb030
color.summary.background=white on color0
color.summary.bar=white on rgb330
color.summary.background=white on rgb110
# Command: calendar
color.calendar.due=color0 on rgb430
color.calendar.due.today=color0 on rgb410
color.calendar.due=color0 on rgb440
color.calendar.due.today=color0 on rgb430
color.calendar.holiday=rgb151 on rgb020
color.calendar.overdue=color0 on rgb400
color.calendar.overdue=color0 on rgb420
color.calendar.today=color15 on rgb110
color.calendar.weekend=on color235
color.calendar.weeknumber=rgb110
@@ -94,6 +93,6 @@ color.sync.changed=rgb430
color.sync.rejected=rgb110
# Command: undo
color.undo.before=rgb031
color.undo.after=rgb053
color.undo.before=rgb021
color.undo.after=rgb042

View File

@@ -33,12 +33,12 @@ color.alternate=
color.header=bold white on bright black
color.footnote=bold cyan on bright black
color.warning=bold red
color.error=red on white
color.debug=white on black
color.error=white on red
color.debug=blue
# Task state
color.completed=green on black
color.deleted=red on black
color.completed=
color.deleted=
color.active=bold yellow on bright black
color.recurring=
color.scheduled=on bright cyan
@@ -50,10 +50,9 @@ color.blocked=blue
color.project.none=
# Priority
color.pri.H=
color.pri.M=
color.pri.L=bright black
color.pri.none=
color.uda.priority.H=
color.uda.priority.M=
color.uda.priority.L=bright black
# Tags
color.tag.next=bold yellow on bright black
@@ -63,17 +62,17 @@ color.tagged=
# Due
color.due=on bright green
color.due.today=on bright yellow
color.overdue=on bright magenta
color.overdue=on bright red
# Report: burndown
color.burndown.pending=on bright green
color.burndown.pending=on bright red
color.burndown.started=on bright yellow
color.burndown.done=on green
# Report: history
color.history.add=blue on bright yellow
color.history.done=green on bright green
color.history.delete=black on red
color.history.add=black on bright red
color.history.done=black on bright green
color.history.delete=black on yellow
# Report: summary
color.summary.bar=on bright green
@@ -82,11 +81,11 @@ color.summary.background=on white
# Command: calendar
color.calendar.due=on bright green
color.calendar.due.today=blue on bright yellow
color.calendar.holiday=yellow
color.calendar.overdue=on bright magenta
color.calendar.holiday=on yellow
color.calendar.overdue=on bright red
color.calendar.today=blue
color.calendar.weekend=on white
color.calendar.weeknumber=white on bright black
color.calendar.weeknumber=blue
# Command: sync
color.sync.added=green

View File

@@ -33,13 +33,13 @@ color.alternate=on gray22
color.header=color15 on gray8
color.footnote=on gray18
color.warning=color9
color.error=red on white
color.debug=color7 on color0
color.error=white on red
color.debug=rgb025
# Task state
color.completed=rgb353 on rgb000
color.deleted=rgb533 on rgb000
color.active=rgb420
color.completed=
color.deleted=
color.active=rgb510
color.recurring=
color.scheduled=on rgb345
color.until=
@@ -50,10 +50,9 @@ color.blocked=rgb003
color.project.none=
# Priority
color.pri.H=gray0
color.pri.M=gray5
color.pri.L=gray10
color.pri.none=gray5
color.uda.priority.H=gray0
color.uda.priority.M=gray5
color.uda.priority.L=gray10
# Tags
color.tag.next=rgb420
@@ -66,14 +65,14 @@ color.due.today=on rgb353
color.overdue=on rgb544
# Report: burndown
color.burndown.pending=on rgb141
color.burndown.started=on rgb440
color.burndown.pending=on rgb411
color.burndown.started=on rgb550
color.burndown.done=on rgb151
# Report: history
color.history.add=rgb005 on rgb440
color.history.done=rgb020 on rgb343
color.history.delete=rgb300 on rgb533
color.history.add=color0 on rgb411
color.history.done=color0 on rgb151
color.history.delete=color0 on rgb550
# Report: summary
color.summary.bar=on rgb141
@@ -82,7 +81,7 @@ color.summary.background=on gray20
# Command: calendar
color.calendar.due=on rgb343
color.calendar.due.today=on rgb353
color.calendar.holiday=rgb420
color.calendar.holiday=color0 on rgb530
color.calendar.overdue=on rgb533
color.calendar.today=rgb005
color.calendar.weekend=on gray21

View File

@@ -53,10 +53,9 @@ color.blocking=
color.project.none=
# Priority
color.pri.H=
color.pri.L=
color.pri.M=
color.pri.none=
color.uda.priority.H=
color.uda.priority.L=
color.uda.priority.M=
# Tags
color.tag.next=

View File

@@ -67,10 +67,9 @@ color.blocking=color15 on color10
color.project.none=
# Priority
color.pri.H=bold blue #color14
color.pri.M=bold yellow #color12
color.pri.L=bold green #color11
color.pri.none=
color.uda.priority.H=bold blue #color14
color.uda.priority.M=bold yellow #color12
color.uda.priority.L=bold green #color11
# Tags
color.tag.next=
@@ -83,14 +82,14 @@ color.due.today=color1
color.overdue=color5
# Report: burndown
color.burndown.done=color0 on color4
color.burndown.done=color0 on color6
color.burndown.pending=color0 on color1
color.burndown.started=color0 on color9
color.burndown.started=color0 on color3
# Report: history
color.history.add=color0 on color1
color.history.delete=color0 on color3
color.history.done=color0 on color10
color.history.done=color0 on color6
# Report: summary
color.summary.background=on color0

View File

@@ -46,7 +46,7 @@ rule.precedence.color=deleted,completed,active,keyword.,tag.,uda.,project.,overd
# General decoration
color.label=
color.label.sort=
color.alternate=on white #color7 (allows bold for alternate rows)
color.alternate=on color7
color.header=color2
color.footnote=color2
color.warning=
@@ -56,21 +56,20 @@ color.debug=color3
# Task state
color.completed=
color.deleted=
color.active=bold red #color9
color.active=color9
color.recurring=color4
color.scheduled=
color.until=
color.blocked=on color14
color.blocking=on color14
color.blocked=color0 on color14
color.blocking=color15 on color0
# Project
color.project.none=
# Priority
color.pri.H=bold black #color0
color.pri.M=bold yellow #color11
color.pri.L=bold cyan #color14
color.pri.none=
color.uda.priority.H=bold color0
color.uda.priority.M=bold color11
color.uda.priority.L=bold color14
# Tags
color.tag.next=
@@ -83,14 +82,14 @@ color.due.today=color1
color.overdue=color5
# Report: burndown
color.burndown.done=color0 on color4
color.burndown.done=color0 on color6
color.burndown.pending=color0 on color1
color.burndown.started=color0 on color9
color.burndown.started=color0 on color3
# Report: history
color.history.add=color0 on color1
color.history.delete=color0 on color3
color.history.done=color14 on color0
color.history.done=color0 on color6
# Report: summary
color.summary.background=on color7

Binary file not shown.

Binary file not shown.

View File

@@ -48,7 +48,7 @@ if ($command =~ /No matches/)
print "'uuid','status','tags','entry','start','due','recur','end','project',",
"'priority','fg','bg','description'\n";
for my $task (split /,$/ms, qx{$command})
for my $task (split "\n", qx{$command})
{
my $data = from_json ($task);

View File

@@ -60,7 +60,7 @@ print "<html>\n",
" <tbody>\n";
my $count = 0;
for my $task (split /,$/ms, qx{$command})
for my $task (split "\n", qx{$command})
{
++$count;
my $data = from_json ($task);

View File

@@ -49,7 +49,7 @@ print "BEGIN:VCALENDAR\n",
"VERSION:2.0\n",
"PRODID:=//GBF/taskwarrior 1.9.4//EN\n";
for my $task (split /,$/ms, qx{$command})
for my $task (split "\n", qx{$command})
{
my $data = from_json ($task);

View File

@@ -140,10 +140,10 @@ def main():
""" Return a list of SQL statements. """
# Use the taskwarrior 2.0+ export command to filter and return JSON
command = "task rc.verbose=nothing rc.json.array=no export " + " ".join(sys.argv[1:])
command = "task rc.verbose=nothing rc.json.array=yes " + " ".join(sys.argv[1:]) + " export"
# Load each task from json to a python dict
tasks = map(json.loads, commands.getoutput(command).split(",\n"))
tasks = json.loads(commands.getoutput(command))
# Mangle datetime strings into python datetime objects
tasks = map(parse_datetime, tasks)

View File

@@ -48,7 +48,7 @@ if ($command =~ /No matches/)
print "uuid\tstatus\ttags\tentry\tstart\tdue\trecur\tend\tproject\t",
"priority\tfg\tbg\tdescription\n";
for my $task (split /,$/ms, qx{$command})
for my $task (split "\n", qx{$command})
{
my $data = from_json ($task);

View File

@@ -46,7 +46,7 @@ if ($command =~ /No matches/)
# Generate output.
print "<tasks>\n";
for my $task (split /,$/ms, qx{$command})
for my $task (split "\n", qx{$command})
{
my $data = from_json ($task);

View File

@@ -48,7 +48,7 @@ if ($command =~ /No matches/)
print "'uuid','status','tags','entry','start','due','recur','end','project',",
"'priority','fg','bg','description'\n";
for my $task (split /,$/ms, qx{$command})
for my $task (split "\n", qx{$command})
{
my $data = from_json ($task);

View File

@@ -50,7 +50,7 @@
#
################################################################################
#the following variable is substituted for by ../../test/bash_completion.t
taskcommand='task rc.verbose:nothing'
taskcommand='task rc.verbose:nothing rc.confirmation:no rc.hooks:off'
_task_get_tags() {
$taskcommand _tags
@@ -68,7 +68,13 @@ _task_offer_projects() {
COMPREPLY=( $(compgen -W "$($taskcommand _projects)" -- ${cur/*:/}) )
}
_task()
_task_offer_contexts() {
COMPREPLY=( $(compgen -W "$($taskcommand _context) define delete list none show" -- $cur) )
}
_task_context_alias=$($taskcommand show | grep alias.*context | cut -d' ' -f1 | cut -d. -f2)
_task()
{
local cur prev opts base
@@ -91,6 +97,10 @@ _task()
opts="$commands_aliases $($taskcommand _columns)"
case "${prev}" in
$_task_context_alias|cont|conte|contex|context)
_task_offer_contexts
return 0
;;
:)
case "${prev2}" in
pri|prior|priori|priorit|priority)

View File

@@ -1,20 +1,19 @@
#!/bin/bash
#!/bin/sh
# The on-add event is triggered separately for each task added
# The on-add event is triggered separately for each task added. This hook
# script can accept/reject the addition. Processing will continue.
# Input:
# - line of JSON for the task added
# Output:
# - all emitted JSON lines are added/modified as tasks, if the exit code is
# zero, otherwise ignored.
# - minimal new task: {"description":"Buy milk"}
# - to modify a task include complete JSON
# - all emitted non-JSON lines are considered feedback messages if the exit
# code is zero, otherwise they are considered errors.
# - Line of JSON for proposed new task.
read new_task
# Output:
# - JSON, modified or unmodified.
# - Optional feedback/error.
echo $new_task
echo 'on-add'
# Status:
# - 0: JSON accepted, non-JSON is feedback.
# - non-0: JSON ignored, non-JSON is error.
exit 0

View File

@@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
read new_task

View File

@@ -1,20 +1,20 @@
#!/bin/bash
#!/bin/sh
# The on-exit event is triggered once, after all processing is complete, i.e.
# last
# Input:
# - read-only line of JSON for each task added/modified
# The on-exit event is triggered once, after all processing is complete.
# This hooks script has no effect on processing.
# Output:
# - any emitted JSON is ignored
# - all emitted non-JSON lines are considered feedback messages if the exit
# code is zero, otherwise they are considered errors.
# - Optional feedback/error.
while read -t 1 modified_task
n=0
while read modified_task
do
# Scan task
n=$(($n + 1))
done
echo 'on-exit'
echo "on-exit: Counted $n added/modified tasks."
# Status:
# - 0: Non-JSON is feedback.
# - non-0: Non-JSON is error.
exit 0

View File

@@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
# This hook script replaces the shadow file feature, found in Taskwarrior
# prior to version 2.4.0.

View File

@@ -1,18 +1,17 @@
#!/bin/bash
#!/bin/sh
# The on-launch event is triggered once, after initialization, before any
# processing occurs, i.e first
# processing occurs. This hooks script has no effect on processing.
# Input:
# - none
# - None
# Output:
# - all emitted JSON lines are added/modified as tasks, if the exit code is
# zero, otherwise ignored.
# - minimal new task: {"description":"Buy milk"}
# - to modify a task include complete JSON
# - all emitted non-JSON lines are considered feedback messages if the exit
# code is zero, otherwise they are considered errors.
# - Optional feedback/error.
echo 'on-launch'
# Status:
# - 0: JSON ignored, non-JSON is feedback.
# - non-0: JSON ignored, non-JSON is error.
exit 0

View File

@@ -1,22 +1,21 @@
#!/bin/bash
#!/bin/sh
# The on-modify event is triggered separately for each task added or modified
# The on-modify event is triggered separately for each task modified. This hook
# script can accept/reject the modification. Processing will continue.
# Input:
# - line of JSON for the original task
# - line of JSON for the modified task, the diff being the modification
# Output:
# - all emitted JSON lines are added/modified as tasks, if the exit code is
# zero, otherwise ignored.
# - minimal new task: {"description":"Buy milk"}
# - to modify a task include complete JSON
# - all emitted non-JSON lines are considered feedback messages if the exit
# code is zero, otherwise they are considered errors.
read original_task
read modified_task
# Output:
# - JSON, modified or unmodified.
# - Optional feedback/error.
echo $modified_task
echo 'on-modify'
# Status:
# - 0: JSON accepted, non-JSON is feedback.
# - non-0: JSON ignored, non-JSON is error.
exit 0

View File

@@ -72,10 +72,9 @@ syn match taskrcGoodKey '^\s*\Vcolor.history.add='he=e-1
syn match taskrcGoodKey '^\s*\Vcolor.history.delete='he=e-1
syn match taskrcGoodKey '^\s*\Vcolor.history.done='he=e-1
syn match taskrcGoodKey '^\s*\Vcolor.overdue='he=e-1
syn match taskrcGoodKey '^\s*\Vcolor.pri.H='he=e-1
syn match taskrcGoodKey '^\s*\Vcolor.pri.L='he=e-1
syn match taskrcGoodKey '^\s*\Vcolor.pri.M='he=e-1
syn match taskrcGoodKey '^\s*\Vcolor.pri.none='he=e-1
syn match taskrcGoodKey '^\s*\Vcolor.uda.priority.H='he=e-1
syn match taskrcGoodKey '^\s*\Vcolor.uda.priority.L='he=e-1
syn match taskrcGoodKey '^\s*\Vcolor.uda.priority.M='he=e-1
syn match taskrcGoodKey '^\s*\Vcolor.recurring='he=e-1
syn match taskrcGoodKey '^\s*\Vcolor.scheduled='he=e-1
syn match taskrcGoodKey '^\s*\Vcolor.summary.background='he=e-1
@@ -154,7 +153,9 @@ syn match taskrcGoodKey '^\s*\Vurgency.blocking.coefficient='he=e-1
syn match taskrcGoodKey '^\s*\Vurgency.due.coefficient='he=e-1
syn match taskrcGoodKey '^\s*\Vurgency.inherit.coefficient='he=e-1
syn match taskrcGoodKey '^\s*\Vurgency.next.coefficient='he=e-1
syn match taskrcGoodKey '^\s*\Vurgency.priority.coefficient='he=e-1
syn match taskrcGoodKey '^\s*\Vurgency.uda.priority.H.coefficient='he=e-1
syn match taskrcGoodKey '^\s*\Vurgency.uda.priority.M.coefficient='he=e-1
syn match taskrcGoodKey '^\s*\Vurgency.uda.priority.L.coefficient='he=e-1
syn match taskrcGoodKey '^\s*\Vurgency.project.coefficient='he=e-1
syn match taskrcGoodKey '^\s*\Vurgency.scheduled.coefficient='he=e-1
syn match taskrcGoodKey '^\s*\Vurgency.tags.coefficient='he=e-1

1
src/.gitignore vendored
View File

@@ -3,3 +3,4 @@ Makefile.in
tw-*
args
calc
lex

View File

@@ -220,16 +220,14 @@ const std::string A::dump () const
// Static method.
void CLI::getOverride (int argc, const char** argv, std::string& home, File& rc)
{
bool terminated = false;
for (int i = 0; i < argc; ++i)
{
std::string raw = argv[i];
if (raw == "--")
terminated = true;
return;
if (! terminated &&
raw.length () > 3 &&
if (raw.length () > 3 &&
raw.substr (0, 3) == "rc:")
{
rc = raw.substr (3);
@@ -351,7 +349,6 @@ void CLI::initialize (int argc, const char** argv)
_terminated = false;
_original_args.push_back (argv[0]);
bool terminated = false;
for (int i = 1; i < argc; ++i)
{
if (isTerminator (argv[i]))
@@ -374,6 +371,63 @@ void CLI::add (const std::string& arg)
analyze ();
}
////////////////////////////////////////////////////////////////////////////////
void CLI::addContextFilter ()
{
// Detect if any context is set, and bail out if not
std::string contextName = context.config.get ("context");
if (contextName == "")
{
context.debug ("No context applied.");
return;
}
// Detect if UUID or ID is set, and bail out
if (_args.size ())
{
std::vector <A>::const_iterator a;
for (a = _args.begin (); a != _args.end (); ++a)
{
// TODO This looks wrong.
if (a->hasTag ("FILTER") &&
a->hasTag ("ATTRIBUTE") &&
! a->hasTag ("TERMINATED") &&
! a->hasTag ("WORD") &&
(a->attribute ("raw") == "id" || a->attribute ("raw") == "uuid"))
{
context.debug (format ("UUID/ID lexeme found '{1}', not applying context.", a->attribute ("raw")));
return;
}
}
}
// Apply context
context.debug ("Applying context: " + contextName);
std::string contextFilter = context.config.get ("context." + contextName);
if (contextFilter == "")
context.debug ("Context '" + contextName + "' not defined.");
else
{
addRawFilter ("( " + contextFilter + " )");
if (context.verbose ("context"))
context.footnote (format ("Context '{1}' set. Use 'task context none' to remove.", contextName));
}
}
////////////////////////////////////////////////////////////////////////////////
// Process raw string into parsed filter.
void CLI::addRawFilter (const std::string& arg)
{
std::string lexeme;
Lexer::Type type;
Lexer lex (arg);
lex.ambiguity (false);
while (lex.token (lexeme, type))
add (lexeme);
}
////////////////////////////////////////////////////////////////////////////////
// Intended to be called after ::initialize() and ::add(), to perform the final
// analysis. Analysis is also performed directly after the above, because there
@@ -388,7 +442,7 @@ void CLI::analyze (bool parse /* = true */, bool strict /* = false */)
// For propagation.
_strict = strict;
for (int i = 0; i < _original_args.size (); ++i)
for (unsigned int i = 0; i < _original_args.size (); ++i)
{
std::string raw = _original_args[i];
A a ("arg", raw);
@@ -474,8 +528,14 @@ void CLI::applyOverrides ()
////////////////////////////////////////////////////////////////////////////////
// Extract all the FILTER-tagged items.
const std::string CLI::getFilter ()
const std::string CLI::getFilter (bool applyContext /* = true */)
{
// Handle context setting
// Commands that don't want to respect current context should leverage
// the applyContext argument
if (applyContext)
addContextFilter ();
std::string filter = "";
if (_args.size ())
{
@@ -500,6 +560,7 @@ const std::string CLI::getFilter ()
filter = "( " + filter + " )";
}
context.debug("Derived filter: '" + filter + "'");
return filter;
}
@@ -627,7 +688,7 @@ const std::string CLI::dump (const std::string& title /* = "CLI Parser" */) cons
// be lexed from those that need to be left alone.
//
// Either the arg is appended to _original_args intact, or the lexemes are.
void CLI::addArg (const std::string& arg)
void CLI::addArg (const std::string& arg, Lexer::Type type /* = Lexer::Type::word */)
{
std::string raw = trim (arg);
@@ -653,8 +714,8 @@ void CLI::addArg (const std::string& arg)
_original_args.push_back (raw);
}
// The argument may require lexing. Lex anyway, and analyze before comitting
// that.
// The argument may require lexing. Lex anyway, and analyze before comitting
// to that.
else
{
// Lex each remaining argument. The apply a series of disqualifying tests
@@ -665,7 +726,7 @@ void CLI::addArg (const std::string& arg)
Lexer lex (raw);
lex.ambiguity (false);
std::vector <std::pair <std::string, Lexer::Type> > lexemes;
std::vector <std::pair <std::string, Lexer::Type>> lexemes;
while (lex.token (lexeme, type))
lexemes.push_back (std::pair <std::string, Lexer::Type> (lexeme, type));
@@ -681,7 +742,7 @@ void CLI::addArg (const std::string& arg)
{
// How often have I said to you that when you have eliminated the
// impossible, whatever remains, however improbable, must be the truth?
std::vector <std::pair <std::string, Lexer::Type> >::iterator l;
std::vector <std::pair <std::string, Lexer::Type>>::iterator l;
for (l = lexemes.begin (); l != lexemes.end (); ++l)
_original_args.push_back (l->first);
}
@@ -713,9 +774,7 @@ void CLI::aliasExpansion ()
{
if (_aliases.find (raw) != _aliases.end ())
{
std::vector <std::string> lexed;
Lexer::token_split (lexed, _aliases[raw]);
std::vector <std::string> lexed = Lexer::split (_aliases[raw]);
std::vector <std::string>::iterator l;
for (l = lexed.begin (); l != lexed.end (); ++l)
{
@@ -851,7 +910,7 @@ void CLI::categorize ()
a->tag ("MODIFICATION");
// If the argument contains a space, it was quoted. Record that.
if (! noSpaces (raw))
if (! Lexer::isOneWord (raw))
a->tag ("QUOTED");
changes = true;
@@ -861,7 +920,7 @@ void CLI::categorize ()
a->tag ("FILTER");
// If the argument contains a space, it was quoted. Record that.
if (! noSpaces (raw))
if (! Lexer::isOneWord (raw))
a->tag ("QUOTED");
changes = true;
@@ -1175,7 +1234,7 @@ void CLI::desugarFilterAttributeModifiers ()
}
else if (modifier == "isnt" || modifier == "not")
{
op.attribute ("raw", "!=");
op.attribute ("raw", "!==");
rhs.attribute ("raw", "'" + value + "'");
rhs.tag ("LITERAL");
}
@@ -1324,14 +1383,12 @@ void CLI::findIDs ()
{
if (a->hasTag ("FILTER"))
{
bool found = false;
// IDs have a limited character set.
std::string raw = a->attribute ("raw");
if (raw.find_first_not_of ("0123456789,-") == std::string::npos)
{
// Container for min/max ID ranges.
std::vector <std::pair <int, int> > ranges;
std::vector <std::pair <int, int>> ranges;
// Split the ID list into elements.
std::vector <std::string> elements;
@@ -1347,7 +1404,7 @@ void CLI::findIDs ()
if (terms.size () == 1)
{
if (! digitsOnly (terms[0]))
if (! Lexer::isAllDigits (terms[0]))
{
is_an_id = false;
break;
@@ -1368,8 +1425,8 @@ void CLI::findIDs ()
}
else if (terms.size () == 2)
{
if (! digitsOnly (terms[0]) ||
! digitsOnly (terms[1]))
if (! Lexer::isAllDigits (terms[0]) ||
! Lexer::isAllDigits (terms[1]))
{
is_an_id = false;
break;
@@ -1410,7 +1467,7 @@ void CLI::findIDs ()
a->tag ("ID");
// Save the ranges.
std::vector <std::pair <int, int> >::iterator r;
std::vector <std::pair <int, int>>::iterator r;
for (r = ranges.begin (); r != ranges.end (); ++r)
_id_ranges.push_back (*r);
}
@@ -1519,7 +1576,7 @@ void CLI::insertIDExpr ()
reconstructed.push_back (openParen);
// Add all ID ranges.
std::vector <std::pair <int, int> >::iterator r;
std::vector <std::pair <int, int>>::iterator r;
for (r = _id_ranges.begin (); r != _id_ranges.end (); ++r)
{
if (r != _id_ranges.begin ())
@@ -1814,8 +1871,7 @@ void CLI::injectDefaults ()
if (defaultCommand != "")
{
// Split the defaultCommand into separate args.
std::vector <std::string> tokens;
Lexer::token_split (tokens, defaultCommand);
std::vector <std::string> tokens = Lexer::split (defaultCommand);
// Modify _args to be: <args0> [<def0> ...] <args1> [...]
std::vector <A> reconstructed;
@@ -2209,7 +2265,7 @@ bool CLI::isIDSequence (const std::string& raw) const
////////////////////////////////////////////////////////////////////////////////
bool CLI::isID (const std::string& raw) const
{
return digitsOnly (raw);
return Lexer::isAllDigits (raw);
}
////////////////////////////////////////////////////////////////////////////////
@@ -2303,11 +2359,11 @@ bool CLI::isName (const std::string& raw) const
{
if (raw != "")
{
for (int i = 0; i < raw.length (); ++i)
for (unsigned int i = 0; i < raw.length (); ++i)
{
if (i == 0 && ! Lexer::is_ident_start (raw[i]))
if (i == 0 && ! Lexer::isIdentifierStart (raw[i]))
return false;
else if (! Lexer::is_ident (raw[i]))
else if (! Lexer::isIdentifierNext (raw[i]))
return false;
}
@@ -2319,19 +2375,19 @@ bool CLI::isName (const std::string& raw) const
////////////////////////////////////////////////////////////////////////////////
bool CLI::disqualifyInsufficientTerms (
const std::vector <std::pair <std::string, Lexer::Type> >& lexemes) const
const std::vector <std::pair <std::string, Lexer::Type>>& lexemes) const
{
return lexemes.size () < 3 ? true : false;
}
////////////////////////////////////////////////////////////////////////////////
bool CLI::disqualifyNoOps (
const std::vector <std::pair <std::string, Lexer::Type> >& lexemes) const
const std::vector <std::pair <std::string, Lexer::Type>>& lexemes) const
{
bool foundOP = false;
std::vector <std::pair <std::string, Lexer::Type> >::const_iterator l;
std::vector <std::pair <std::string, Lexer::Type>>::const_iterator l;
for (l = lexemes.begin (); l != lexemes.end (); ++l)
if (l->second == Lexer::typeOperator)
if (l->second == Lexer::Type::op)
foundOP = true;
return ! foundOP;
@@ -2339,16 +2395,16 @@ bool CLI::disqualifyNoOps (
////////////////////////////////////////////////////////////////////////////////
bool CLI::disqualifyOnlyParenOps (
const std::vector <std::pair <std::string, Lexer::Type> >& lexemes) const
const std::vector <std::pair <std::string, Lexer::Type>>& lexemes) const
{
int opCount = 0;
int opSugarCount = 0;
int opParenCount = 0;
std::vector <std::pair <std::string, Lexer::Type> >::const_iterator l;
std::vector <std::pair <std::string, Lexer::Type>>::const_iterator l;
for (l = lexemes.begin (); l != lexemes.end (); ++l)
{
if (l->second == Lexer::typeOperator)
if (l->second == Lexer::Type::op)
{
++opCount;
@@ -2375,7 +2431,7 @@ bool CLI::disqualifyOnlyParenOps (
// as there are no operators in between, which includes syntactic sugar that
// hides operators.
bool CLI::disqualifyFirstLastBinary (
const std::vector <std::pair <std::string, Lexer::Type> >& lexemes) const
const std::vector <std::pair <std::string, Lexer::Type>>& lexemes) const
{
bool firstBinary = false;
bool lastBinary = false;
@@ -2394,7 +2450,7 @@ bool CLI::disqualifyFirstLastBinary (
////////////////////////////////////////////////////////////////////////////////
// Disqualify terms when there operators hidden by syntactic sugar.
bool CLI::disqualifySugarFree (
const std::vector <std::pair <std::string, Lexer::Type> >& lexemes) const
const std::vector <std::pair <std::string, Lexer::Type>>& lexemes) const
{
bool sugared = true;
for (unsigned int i = 1; i < lexemes.size () - 1; ++i)

View File

@@ -77,9 +77,11 @@ public:
void entity (const std::string&, const std::string&);
void initialize (int, const char**);
void add (const std::string&);
void addContextFilter ();
void addRawFilter (const std::string& arg);
void analyze (bool parse = true, bool strict = false);
void applyOverrides ();
const std::string getFilter ();
const std::string getFilter (bool applyContext = true);
const std::vector <std::string> getWords ();
bool canonicalize (std::string&, const std::string&, const std::string&) const;
std::string getBinary () const;
@@ -88,7 +90,7 @@ public:
const std::string dump (const std::string& title = "CLI Parser") const;
private:
void addArg (const std::string&);
void addArg (const std::string&, Lexer::Type type = Lexer::Type::word);
void aliasExpansion ();
void findOverrides ();
void categorize ();
@@ -126,11 +128,11 @@ private:
bool isOperator (const std::string&) const;
bool isName (const std::string&) const;
bool disqualifyInsufficientTerms (const std::vector <std::pair <std::string, Lexer::Type> >&) const;
bool disqualifyNoOps (const std::vector <std::pair <std::string, Lexer::Type> >&) const;
bool disqualifyOnlyParenOps (const std::vector <std::pair <std::string, Lexer::Type> >&) const;
bool disqualifyFirstLastBinary (const std::vector <std::pair <std::string, Lexer::Type> >&) const;
bool disqualifySugarFree (const std::vector <std::pair <std::string, Lexer::Type> >&) const;
bool disqualifyInsufficientTerms (const std::vector <std::pair <std::string, Lexer::Type>>&) const;
bool disqualifyNoOps (const std::vector <std::pair <std::string, Lexer::Type>>&) const;
bool disqualifyOnlyParenOps (const std::vector <std::pair <std::string, Lexer::Type>>&) const;
bool disqualifyFirstLastBinary (const std::vector <std::pair <std::string, Lexer::Type>>&) const;
bool disqualifySugarFree (const std::vector <std::pair <std::string, Lexer::Type>>&) const;
public:
std::multimap <std::string, std::string> _entities;
@@ -138,7 +140,7 @@ public:
std::vector <std::string> _original_args;
std::vector <A> _args;
std::vector <std::pair <int, int> > _id_ranges;
std::vector <std::pair <int, int>> _id_ranges;
std::vector <std::string> _uuid_list;
bool _strict;
bool _terminated;

View File

@@ -48,16 +48,19 @@ set (task_SRCS CLI.cpp CLI.h
add_library (task STATIC ${task_SRCS})
add_executable (task_executable main.cpp)
add_executable (calc_executable calc.cpp)
add_executable (lex_executable lex.cpp)
# Yes, 'task' is included twice, otherwise linking fails on assorted OSes.
target_link_libraries (task_executable task commands columns task ${TASK_LIBRARIES})
target_link_libraries (calc_executable task commands columns task ${TASK_LIBRARIES})
target_link_libraries (lex_executable task commands columns task ${TASK_LIBRARIES})
set_property (TARGET task_executable PROPERTY OUTPUT_NAME "task")
install (TARGETS task_executable DESTINATION ${TASK_BINDIR})
set_property (TARGET calc_executable PROPERTY OUTPUT_NAME "calc")
set_property (TARGET lex_executable PROPERTY OUTPUT_NAME "lex")
#SET(CMAKE_BUILD_TYPE gcov)
#SET(CMAKE_CXX_FLAGS_GCOV "--coverage")

View File

@@ -82,6 +82,7 @@ std::string Config::_defaults =
"# # Comma-separated list. May contain any subset of:\n"
"#verbose=blank,header,footnote,label,new-id,new-uuid,affected,edit,special,project,sync,filter\n"
"confirmation=yes # Confirmation on delete, big changes\n"
"recurrence=yes # Enable recurrence\n"
"recurrence.confirmation=prompt # Confirmation for propagating changes among recurring tasks (yes/no/prompt)\n"
"allow.empty.filter=yes # An empty filter gets a warning and requires confirmation\n"
"indent.annotation=2 # Indent spaces for annotations\n"
@@ -116,6 +117,7 @@ std::string Config::_defaults =
"dateformat.info=Y-M-D H:N:S # Preferred display date format for information\n"
"dateformat.report= # Preferred display date format for reports\n"
"dateformat.annotation= # Preferred display date format for annotations\n"
"date.iso=yes # Enable ISO date support\n"
"weekstart="
STRING_DATE_SUNDAY_LONG
" # Sunday or Monday only\n"
@@ -145,7 +147,6 @@ std::string Config::_defaults =
"urgency.next.coefficient=15.0 # Urgency coefficient for 'next' special tag\n"
"urgency.due.coefficient=12.0 # Urgency coefficient for due dates\n"
"urgency.blocking.coefficient=8.0 # Urgency coefficient for blocking tasks\n"
"urgency.priority.coefficient=6.0 # Urgency coefficient for priorities\n"
"urgency.active.coefficient=4.0 # Urgency coefficient for active tasks\n"
"urgency.scheduled.coefficient=5.0 # Urgency coefficient for scheduled tasks\n"
"urgency.age.coefficient=2.0 # Urgency coefficient for age\n"
@@ -163,38 +164,37 @@ std::string Config::_defaults =
"\n"
"# Color controls.\n"
"color=on # Enable color\n"
#ifdef LINUX
#if defined(LINUX) || defined(DARWIN)
"\n"
"rule.precedence.color=deleted,completed,active,keyword.,tag.,uda.,project.,overdue,scheduled,due.today,due,blocked,blocking,recurring,tagged,pri.\n"
"\n"
"# General decoration\n"
"color.label=\n"
"color.label.sort=\n"
"color.alternate=on gray1\n"
"color.alternate=on gray2\n"
"color.header=color3\n"
"color.footnote=color3\n"
"color.warning=bold red\n"
"color.error=color3\n"
"color.debug=color3\n"
"color.error=white on red\n"
"color.debug=color4\n"
"\n"
"# Task state\n"
"color.completed=rgb010 on white\n"
"color.deleted=rgb100 on white\n"
"color.completed=\n"
"color.deleted=\n"
"color.active=rgb555 on rgb410\n"
"color.recurring=rgb013\n"
"color.scheduled=on rgb001\n"
"color.until=\n"
"color.blocked=white on color8\n"
"color.blocking=white on color15\n"
"color.blocking=black on color15\n"
"\n"
"# Project\n"
"color.project.none=\n"
"\n"
"# Priority\n"
"color.pri.H=color255\n"
"color.pri.L=color245\n"
"color.pri.M=color250\n"
"color.pri.none=\n"
"# Priority UDA\n"
"color.uda.priority.H=color255\n"
"color.uda.priority.L=color245\n"
"color.uda.priority.M=color250\n"
"\n"
"# Tags\n"
"color.tag.next=rgb440\n"
@@ -249,15 +249,15 @@ std::string Config::_defaults =
"color.header=yellow\n"
"color.footnote=yellow\n"
"color.warning=bold red\n"
"color.error=yellow\n"
"color.debug=yellow\n"
"color.error=white on red\n"
"color.debug=blue\n"
"\n"
"# Task state\n"
"color.completed=green on white\n"
"color.deleted=red on white\n"
"color.completed=\n"
"color.deleted=\n"
"color.active=black on bright green\n"
"color.recurring=magenta\n"
"color.scheduled=on green\n"
"color.scheduled=white on green\n"
"color.until=\n"
"color.blocked=black on white\n"
"color.blocking=black on bright white\n"
@@ -265,11 +265,10 @@ std::string Config::_defaults =
"# Project\n"
"color.project.none=\n"
"\n"
"# Priority\n"
"color.pri.H=bold white\n"
"color.pri.M=white\n"
"color.pri.L=\n"
"color.pri.none=\n"
"# Priority UDA\n"
"color.uda.priority.H=bold white\n"
"color.uda.priority.M=white\n"
"color.uda.priority.L=\n"
"\n"
"# Tags\n"
"color.tag.next=bold yellow\n"
@@ -317,19 +316,29 @@ std::string Config::_defaults =
"color.undo.before=red\n"
"\n"
#endif
"# UDA priority\n"
"uda.priority.type=string # UDA priority is a string type\n"
"uda.priority.label=Priority # UDA priority has a display label'\n"
"uda.priority.values=H,M,L, # UDA priority values are 'H', 'M', 'L' or ''\n"
" # UDA priority sorting is 'H' > 'M' > 'L' > '' (highest to lowest)\n"
"#uda.priority.default=M # UDA priority default value of 'M'\n"
"urgency.uda.priority.H.coefficient=6.0 # UDA priority coefficient for value 'H'\n"
"urgency.uda.priority.M.coefficient=3.9 # UDA priority coefficient for value 'M'\n"
"urgency.uda.priority.L.coefficient=1.8 # UDA priority coefficient for value 'L'\n"
"\n"
"# Here is the rule precedence order, highest to lowest.\n"
"# Note that these are just the color rule names, without the leading 'color.'\n"
"# and any trailing '.value'.\n"
"rule.precedence.color=deleted,completed,active,keyword.,tag.,uda.,project.,overdue,scheduled,due.today,due,blocked,blocking,recurring,tagged,pri.\n"
"\n"
"#default.project=foo # Default project for 'add' command\n"
"#default.priority=M # Default priority for 'add' command\n"
"#default.due=eom # Default due date for 'add' command\n"
"default.command=next # When no arguments are specified\n"
"\n"
"_forcecolor=no # Forces color to be on, even for non TTY output\n"
"complete.all.tags=no # Include old tag names in '_ags' command\n"
"list.all.projects=no # Include old project names in 'projects' command\n"
"summary.all.projects=no # Include old project names in 'summary' command\n"
"list.all.tags=no # Include old tag names in 'tags' command\n"
"print.empty.columns=no # Print columns which have no data for any task\n"
"debug=no # Display diagnostics\n"
@@ -358,19 +367,19 @@ std::string Config::_defaults =
"\n"
"report.long.description=All details of tasks\n"
"report.long.labels=ID,A,Created,Mod,Deps,P,Project,Tags,Recur,Wait,Sched,Due,Until,Description\n"
"report.long.columns=id,start.active,entry,modified.age,depends,priority,project,tags,recur,wait.age,scheduled,due,until,description\n"
"report.long.columns=id,start.active,entry,modified.age,depends,priority,project,tags,recur,wait.remaining,scheduled,due,until,description\n"
"report.long.filter=status:pending\n"
"report.long.sort=modified-\n"
"\n"
"report.list.description=Most details of tasks\n"
"report.list.labels=ID,Active,Age,D,P,Project,Tags,R,Sch,Due,Until,Description,Urg\n"
"report.list.columns=id,start.age,entry.age,depends.indicator,priority,project,tags,recur.indicator,scheduled.countdown,due,until.age,description.count,urgency\n"
"report.list.columns=id,start.age,entry.age,depends.indicator,priority,project,tags,recur.indicator,scheduled.countdown,due,until.remaining,description.count,urgency\n"
"report.list.filter=status:pending\n"
"report.list.sort=start-,due+,project+/,urgency-\n"
"report.list.sort=start-,due+,project+,urgency-\n"
"\n"
"report.ls.description=Few details of tasks\n"
"report.ls.labels=ID,A,D,Project,Tags,R,Wait,S,Due,Until,Description\n"
"report.ls.columns=id,start.active,depends.indicator,project,tags,recur.indicator,wait.age,scheduled.countdown,due.countdown,until.countdown,description.count\n"
"report.ls.columns=id,start.active,depends.indicator,project,tags,recur.indicator,wait.remaining,scheduled.countdown,due.countdown,until.countdown,description.count\n"
"report.ls.filter=status:pending\n"
"report.ls.sort=start-,description+\n"
"\n"
@@ -382,13 +391,13 @@ std::string Config::_defaults =
"\n"
"report.newest.description=Newest tasks\n"
"report.newest.labels=ID,Active,Created,Age,Mod,D,P,Project,Tags,R,Wait,Sch,Due,Until,Description\n"
"report.newest.columns=id,start.age,entry,entry.age,modified.age,depends.indicator,priority,project,tags,recur.indicator,wait.age,scheduled.countdown,due,until.age,description\n"
"report.newest.filter=(status:pending or status:waiting) and recur.none:\n"
"report.newest.columns=id,start.age,entry,entry.age,modified.age,depends.indicator,priority,project,tags,recur.indicator,wait.remaining,scheduled.countdown,due,until.age,description\n"
"report.newest.filter=(status:pending or status:waiting)\n"
"report.newest.sort=entry-\n"
"\n"
"report.oldest.description=Oldest tasks\n"
"report.oldest.labels=ID,Active,Created,Age,Mod,D,P,Project,Tags,R,Wait,Sch,Due,Until,Description\n"
"report.oldest.columns=id,start.age,entry,entry.age,modified.age,depends.indicator,priority,project,tags,recur.indicator,wait.age,scheduled.countdown,due,until.age,description\n"
"report.oldest.columns=id,start.age,entry,entry.age,modified.age,depends.indicator,priority,project,tags,recur.indicator,wait.remaining,scheduled.countdown,due,until.age,description\n"
"report.oldest.filter=(status:pending or status:waiting)\n"
"report.oldest.sort=entry+\n"
"\n"
@@ -400,7 +409,7 @@ std::string Config::_defaults =
"\n"
"report.active.description=Active tasks\n"
"report.active.labels=ID,Started,Active,Age,D,P,Project,Tags,Recur,W,Sch,Due,Until,Description\n"
"report.active.columns=id,start,start.age,entry.age,depends.indicator,priority,project,tags,recur,wait.indicator,scheduled.age,due,until,description\n"
"report.active.columns=id,start,start.age,entry.age,depends.indicator,priority,project,tags,recur,wait.indicator,scheduled.remaining,due,until,description\n"
"report.active.filter=status:pending and +ACTIVE\n"
"report.active.sort=project+,start+\n"
"\n"
@@ -412,30 +421,30 @@ std::string Config::_defaults =
"\n"
"report.recurring.description=Recurring Tasks\n"
"report.recurring.labels=ID,Active,Age,D,P,Project,Tags,Recur,Sch,Due,Until,Description,Urg\n"
"report.recurring.columns=id,start.age,entry.age,depends.indicator,priority,project,tags,recur,scheduled.countdown,due,until.age,description,urgency\n"
"report.recurring.columns=id,start.age,entry.age,depends.indicator,priority,project,tags,recur,scheduled.countdown,due,until.remaining,description,urgency\n"
"report.recurring.filter=(status:pending or status:waiting) and (+PARENT or +CHILD)\n"
"report.recurring.sort=due+,urgency-,entry+\n"
"\n"
"report.waiting.description=Waiting (hidden) tasks\n"
"report.waiting.labels=ID,A,Age,D,P,Project,Tags,R,Wait,for,Sched,Due,Until,Description\n"
"report.waiting.columns=id,start.active,entry.age,depends.indicator,priority,project,tags,recur.indicator,wait,wait.age,scheduled,due,until,description\n"
"report.waiting.columns=id,start.active,entry.age,depends.indicator,priority,project,tags,recur.indicator,wait,wait.remaining,scheduled,due,until,description\n"
"report.waiting.filter=+WAITING\n"
"report.waiting.sort=due+,wait+,entry+\n"
"\n"
"report.all.description=All tasks\n"
"report.all.labels=ID,St,UUID,A,Age,Done,D,P,Project,Tags,R,Wait,Sch,Due,Until,Description\n"
"report.all.columns=id,status.short,uuid.short,start.active,entry.age,end.age,depends.indicator,priority,project.parent,tags.count,recur.indicator,wait.age,scheduled.age,due,until.age,description\n"
"report.all.columns=id,status.short,uuid.short,start.active,entry.age,end.age,depends.indicator,priority,project.parent,tags.count,recur.indicator,wait.remaining,scheduled.remaining,due,until.remaining,description\n"
"report.all.sort=entry-\n"
"\n"
"report.next.description=Most urgent tasks\n"
"report.next.labels=ID,Active,Age,Deps,P,Project,Tag,Recur,S,Due,Until,Description,Urg\n"
"report.next.columns=id,start.age,entry.age,depends,priority,project,tags,recur,scheduled.countdown,due.age,until.age,description,urgency\n"
"report.next.columns=id,start.age,entry.age,depends,priority,project,tags,recur,scheduled.countdown,due.remaining,until.remaining,description,urgency\n"
"report.next.filter=status:pending limit:page\n"
"report.next.sort=start-,urgency-\n"
"report.next.sort=urgency-\n"
"\n"
"report.ready.description=Most urgent actionable tasks\n"
"report.ready.labels=ID,Active,Age,D,P,Project,Tags,R,S,Due,Until,Description,Urg\n"
"report.ready.columns=id,start.age,entry.age,depends.indicator,priority,project,tags,recur.indicator,scheduled.countdown,due.countdown,until.age,description,urgency\n"
"report.ready.columns=id,start.age,entry.age,depends.indicator,priority,project,tags,recur.indicator,scheduled.countdown,due.countdown,until.remaining,description,urgency\n"
"report.ready.filter=+READY\n"
"report.ready.sort=start-,urgency-\n"
"\n"
@@ -453,7 +462,7 @@ std::string Config::_defaults =
"\n"
"report.blocking.description=Blocking tasks\n"
"report.blocking.labels=ID,UUID,A,Deps,Project,Tags,R,W,Sch,Due,Until,Description,Urg\n"
"report.blocking.columns=id,uuid.short,start.active,depends,project,tags,recur,wait.indicator,scheduled.age,due.age,until.age,description.count,urgency\n"
"report.blocking.columns=id,uuid.short,start.active,depends,project,tags,recur,wait.indicator,scheduled.remaining,due.remaining,until.remaining,description.count,urgency\n"
"report.blocking.sort=urgency-,due+,entry+\n"
"report.blocking.filter= status:pending +BLOCKING\n"
"\n";

View File

@@ -65,33 +65,6 @@ static const char* modifierNames[] =
#define NUM_MODIFIER_NAMES (sizeof (modifierNames) / sizeof (modifierNames[0]))
static const char* attributeNames[] =
{
"depends",
"description",
"due",
"end",
"entry",
"id",
"imask",
"mask",
"modified",
"parent",
"priority",
"project",
"recur",
"scheduled",
"start",
"status",
"tags",
"until",
"urgency",
"uuid",
"wait"
};
#define NUM_ATTRIBUTE_NAMES (sizeof (attributeNames) / sizeof (attributeNames[0]))
////////////////////////////////////////////////////////////////////////////////
Context::Context ()
: rc_file ("~/.taskrc")
@@ -101,6 +74,7 @@ Context::Context ()
, dom ()
, determine_color_use (true)
, use_color (true)
, run_gc (true)
, verbosity_legacy (false)
, terminal_width (0)
, terminal_height (0)
@@ -275,7 +249,7 @@ int Context::initialize (int argc, const char** argv)
////////////////////////////////////////////////////////////////////////////
//
// [8] Run on.launch hooks.
// [8] Initialize hooks.
//
////////////////////////////////////////////////////////////////////////////
@@ -478,7 +452,14 @@ int Context::dispatch (std::string &out)
// GC is invoked prior to running any command that displays task IDs, if
// possible.
if (c->displays_id () && !tdb2.read_only ())
{
run_gc = true;
tdb2.gc ();
}
else
{
run_gc = false;
}
/*
// Only read-only commands can be run when TDB2 is read-only.
@@ -504,7 +485,7 @@ bool Context::color ()
use_color = config.getBoolean ("color");
// Only tty's support color.
if (! isatty (fileno (stdout)))
if (! isatty (STDOUT_FILENO))
{
// No ioctl.
config.set ("detection", "off");
@@ -652,18 +633,32 @@ void Context::staticInitialization ()
CLI::minimumMatchLength = config.getInteger ("abbreviation.minimum");
Task::defaultProject = config.get ("default.project");
Task::defaultPriority = config.get ("default.priority");
Task::defaultDue = config.get ("default.due");
Task::searchCaseSensitive = Variant::searchCaseSensitive = config.getBoolean ("search.case.sensitive");
Task::regex = Variant::searchUsingRegex = config.getBoolean ("regex");
Lexer::dateFormat = Variant::dateFormat = config.get ("dateformat");
Lexer::isoEnabled = Variant::isoEnabled = config.getBoolean ("date.iso");
Config::const_iterator rc;
for (rc = config.begin (); rc != config.end (); ++rc)
{
if (rc->first.substr (0, 4) == "uda." &&
rc->first.substr (rc->first.length () - 7, 7) == ".values")
{
std::string name = rc->first.substr (4, rc->first.length () - 7 - 4);
std::vector <std::string> values;
split (values, rc->second, ',');
for (auto r = values.rbegin(); r != values.rend (); ++r)
Task::customOrder[name].push_back (*r);
}
}
std::map <std::string, Column*>::iterator i;
for (i = columns.begin (); i != columns.end (); ++i)
Task::attributes[i->first] = i->second->type ();
Task::urgencyPriorityCoefficient = config.getReal ("urgency.priority.coefficient");
Task::urgencyProjectCoefficient = config.getReal ("urgency.project.coefficient");
Task::urgencyActiveCoefficient = config.getReal ("urgency.active.coefficient");
Task::urgencyScheduledCoefficient = config.getReal ("urgency.scheduled.coefficient");
@@ -770,7 +765,7 @@ void Context::clear ()
// this output?'.
void Context::updateXtermTitle ()
{
if (config.getBoolean ("xterm.title") && isatty (fileno (stdout)))
if (config.getBoolean ("xterm.title") && isatty (STDOUT_FILENO))
{
std::string command = cli.getCommand ();
std::string title;

View File

@@ -94,6 +94,8 @@ public:
bool determine_color_use;
bool use_color;
bool run_gc;
bool verbosity_legacy;
std::vector <std::string> verbosity;
std::vector <std::string> headers;

View File

@@ -116,16 +116,16 @@ bool DOM::get (const std::string& name, Variant& value)
}
else if (name == "context.width")
{
value = Variant (context.terminal_width
? context.terminal_width
: context.getWidth ());
value = Variant (static_cast<int> (context.terminal_width
? context.terminal_width
: context.getWidth ()));
return true;
}
else if (name == "context.height")
{
value = Variant (context.terminal_height
? context.terminal_height
: context.getHeight ());
value = Variant (static_cast<int> (context.terminal_height
? context.terminal_height
: context.getHeight ()));
return true;
}
else
@@ -218,7 +218,7 @@ bool DOM::get (const std::string& name, const Task& task, Variant& value)
// <attr>
if (task.size () && name == "id")
{
value = Variant (task.id);
value = Variant (static_cast<int> (task.id));
return true;
}
@@ -240,6 +240,12 @@ bool DOM::get (const std::string& name, const Task& task, Variant& value)
Column* column = context.columns[canonical];
if (column)
{
if (column->is_uda () && ! task.has (canonical))
{
value = Variant ("''");
return true;
}
if (column->type () == "date")
value = Variant (task.get_date (canonical), Variant::type_date);
else if (column->type () == "duration" || canonical == "recur")
@@ -290,7 +296,7 @@ bool DOM::get (const std::string& name, const Task& task, Variant& value)
{
if (elements[1] == "id")
{
value = Variant (ref.id);
value = Variant (static_cast<int> (ref.id));
return true;
}
else if (elements[1] == "urgency")
@@ -307,6 +313,12 @@ bool DOM::get (const std::string& name, const Task& task, Variant& value)
Column* column = context.columns[canonical];
if (column)
{
if (column->is_uda () && ! ref.has (canonical))
{
value = Variant ("''");
return true;
}
if (column->type () == "date")
value = Variant (ref.get_date (canonical), Variant::type_date);
else if (column->type () == "duration")
@@ -341,15 +353,15 @@ bool DOM::get (const std::string& name, const Task& task, Variant& value)
// <date>.minute
// <date>.second
Date date (ref.get_date (canonical));
if (elements[2] == "year") { value = Variant (date.year ()); return true; }
else if (elements[2] == "month") { value = Variant (date.month ()); return true; }
else if (elements[2] == "day") { value = Variant (date.day ()); return true; }
else if (elements[2] == "week") { value = Variant (date.week ()); return true; }
else if (elements[2] == "weekday") { value = Variant (date.dayOfWeek ()); return true; }
else if (elements[2] == "julian") { value = Variant (date.dayOfYear ()); return true; }
else if (elements[2] == "hour") { value = Variant (date.hour ()); return true; }
else if (elements[2] == "minute") { value = Variant (date.minute ()); return true; }
else if (elements[2] == "second") { value = Variant (date.second ()); return true; }
if (elements[2] == "year") { value = Variant (static_cast<int> (date.year ())); return true; }
else if (elements[2] == "month") { value = Variant (static_cast<int> (date.month ())); return true; }
else if (elements[2] == "day") { value = Variant (static_cast<int> (date.day ())); return true; }
else if (elements[2] == "week") { value = Variant (static_cast<int> (date.week ())); return true; }
else if (elements[2] == "weekday") { value = Variant (static_cast<int> (date.dayOfWeek ())); return true; }
else if (elements[2] == "julian") { value = Variant (static_cast<int> (date.dayOfYear ())); return true; }
else if (elements[2] == "hour") { value = Variant (static_cast<int> (date.hour ())); return true; }
else if (elements[2] == "minute") { value = Variant (static_cast<int> (date.minute ())); return true; }
else if (elements[2] == "second") { value = Variant (static_cast<int> (date.second ())); return true; }
}
}
}
@@ -408,15 +420,15 @@ bool DOM::get (const std::string& name, const Task& task, Variant& value)
// <annotations>.<N>.entry.minute
// <annotations>.<N>.entry.second
Date date (i->first.substr (11));
if (elements[4] == "year") { value = Variant (date.year ()); return true; }
else if (elements[4] == "month") { value = Variant (date.month ()); return true; }
else if (elements[4] == "day") { value = Variant (date.day ()); return true; }
else if (elements[4] == "week") { value = Variant (date.week ()); return true; }
else if (elements[4] == "weekday") { value = Variant (date.dayOfWeek ()); return true; }
else if (elements[4] == "julian") { value = Variant (date.dayOfYear ()); return true; }
else if (elements[4] == "hour") { value = Variant (date.hour ()); return true; }
else if (elements[4] == "minute") { value = Variant (date.minute ()); return true; }
else if (elements[4] == "second") { value = Variant (date.second ()); return true; }
if (elements[4] == "year") { value = Variant (static_cast<int> (date.year ())); return true; }
else if (elements[4] == "month") { value = Variant (static_cast<int> (date.month ())); return true; }
else if (elements[4] == "day") { value = Variant (static_cast<int> (date.day ())); return true; }
else if (elements[4] == "week") { value = Variant (static_cast<int> (date.week ())); return true; }
else if (elements[4] == "weekday") { value = Variant (static_cast<int> (date.dayOfWeek ())); return true; }
else if (elements[4] == "julian") { value = Variant (static_cast<int> (date.dayOfYear ())); return true; }
else if (elements[4] == "hour") { value = Variant (static_cast<int> (date.hour ())); return true; }
else if (elements[4] == "minute") { value = Variant (static_cast<int> (date.minute ())); return true; }
else if (elements[4] == "second") { value = Variant (static_cast<int> (date.second ())); return true; }
}
}
}

View File

@@ -31,13 +31,13 @@
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <Nibbler.h>
#include <Date.h>
#include <Variant.h>
#include <Dates.h>
#include <text.h>
#include <util.h>
#include <utf8.h>
#include <i18n.h>
#include <Context.h>
@@ -823,7 +823,7 @@ void Date::operator++ (int)
////////////////////////////////////////////////////////////////////////////////
bool Date::isEpoch (const std::string& input)
{
if (digitsOnly (input) &&
if (Lexer::isAllDigits (input) &&
input.length () <= 10 )
{
_t = (time_t) atoi (input.c_str ());

View File

@@ -30,6 +30,7 @@
#include <text.h>
#include <Dates.h>
#include <Date.h>
#include <Lexer.h>
#include <i18n.h>
////////////////////////////////////////////////////////////////////////////////
@@ -354,7 +355,7 @@ bool namedDates (const std::string& name, Variant& value)
// 4th
else if ((
name.length () == 3 &&
isdigit (name[0]) &&
Lexer::isDigit (name[0]) &&
((name[1] == 's' && name[2] == 't') ||
(name[1] == 'n' && name[2] == 'd') ||
(name[1] == 'r' && name[2] == 'd') ||
@@ -363,8 +364,8 @@ bool namedDates (const std::string& name, Variant& value)
||
(
name.length () == 4 &&
isdigit (name[0]) &&
isdigit (name[1]) &&
Lexer::isDigit (name[0]) &&
Lexer::isDigit (name[1]) &&
((name[2] == 's' && name[3] == 't') ||
(name[2] == 'n' && name[3] == 'd') ||
(name[2] == 'r' && name[3] == 'd') ||
@@ -375,7 +376,7 @@ bool namedDates (const std::string& name, Variant& value)
int number;
std::string ordinal;
if (isdigit (name[1]))
if (Lexer::isDigit (name[1]))
{
number = strtol (name.substr (0, 2).c_str (), NULL, 10);
ordinal = lowerCase (name.substr (2));

View File

@@ -34,7 +34,7 @@
#include <stdlib.h>
#include <Directory.h>
#if defined SOLARIS || defined NETBSD
#if defined SOLARIS || defined NETBSD || defined FREEBSD
#include <limits.h>
#endif

View File

@@ -61,31 +61,49 @@ static struct
{"fortnight", 14 * DAY, true},
{"hours", 1 * HOUR, false},
{"hour", 1 * HOUR, true},
{"hrs", 1 * HOUR, false},
{"hr", 1 * HOUR, true},
{"h", 1 * HOUR, false},
{"minutes", 1 * MINUTE, false},
{"minute", 1 * MINUTE, false},
{"min", 1 * MINUTE, false},
{"minute", 1 * MINUTE, true},
{"mins", 1 * MINUTE, false},
{"min", 1 * MINUTE, true},
{"monthly", 30 * DAY, true},
{"months", 30 * DAY, false},
{"month", 30 * DAY, true},
{"mo", 30 * DAY, false},
{"mnths", 30 * DAY, false},
{"mths", 30 * DAY, false},
{"mth", 30 * DAY, true},
{"mos", 30 * DAY, false},
{"mo", 30 * DAY, true},
{"m", 30 * DAY, false},
{"quarterly", 91 * DAY, true},
{"quarters", 91 * DAY, false},
{"quarter", 91 * DAY, true},
{"qrtrs", 91 * DAY, false},
{"qrtr", 91 * DAY, true},
{"qtrs", 91 * DAY, false},
{"qtr", 91 * DAY, true},
{"q", 91 * DAY, false},
{"semiannual", 183 * DAY, true},
{"sennight", 14 * DAY, false},
{"seconds", 1 * SECOND, false},
{"second", 1 * SECOND, true},
{"secs", 1 * SECOND, false},
{"sec", 1 * SECOND, true},
{"s", 1 * SECOND, false},
{"weekdays", 1 * DAY, true},
{"weekly", 7 * DAY, true},
{"weeks", 7 * DAY, false},
{"week", 7 * DAY, true},
{"wks", 7 * DAY, false},
{"wk", 7 * DAY, true},
{"w", 7 * DAY, false},
{"yearly", 365 * DAY, true},
{"years", 365 * DAY, false},
{"year", 365 * DAY, true},
{"yrs", 365 * DAY, false},
{"yr", 365 * DAY, true},
{"y", 365 * DAY, false},
};
@@ -107,7 +125,7 @@ Duration::Duration (time_t input)
Duration::Duration (const std::string& input)
: _secs (0)
{
if (digitsOnly (input))
if (Lexer::isAllDigits (input))
{
time_t value = (time_t) strtol (input.c_str (), NULL, 10);
if (value == 0 || value > 60)
@@ -284,10 +302,11 @@ bool Duration::parse (const std::string& input, std::string::size_type& start)
std::string::size_type original_start = start;
Nibbler n (input.substr (start));
// TODO This can be made static, and so preserved between calls.
std::vector <std::string> units;
for (int i = 0; i < NUM_DURATIONS; i++)
units.push_back (durations[i].unit);
// Static and so preserved between calls.
static std::vector <std::string> units;
if (units.size () == 0)
for (unsigned int i = 0; i < NUM_DURATIONS; i++)
units.push_back (durations[i].unit);
std::string number;
std::string unit;
@@ -295,13 +314,12 @@ bool Duration::parse (const std::string& input, std::string::size_type& start)
if (n.getOneOf (units, unit))
{
if (n.depleted () ||
Lexer::is_ws (n.next ()))
Lexer::isWhitespace (n.next ()))
{
start = original_start + n.cursor ();
// Linear lookup - should be logarithmic.
double seconds = 1;
for (int i = 0; i < NUM_DURATIONS; i++)
for (unsigned int i = 0; i < NUM_DURATIONS; i++)
{
if (durations[i].unit == unit &&
durations[i].standalone == true)
@@ -319,14 +337,14 @@ bool Duration::parse (const std::string& input, std::string::size_type& start)
if (n.getOneOf (units, unit))
{
if (n.depleted () ||
Lexer::is_ws (n.next ()))
Lexer::isWhitespace (n.next ()))
{
start = original_start + n.cursor ();
double quantity = strtod (number.c_str (), NULL);
// Linear lookup - should be logarithmic.
double seconds = 1;
for (int i = 0; i < NUM_DURATIONS; i++)
for (unsigned int i = 0; i < NUM_DURATIONS; i++)
{
if (durations[i].unit == unit)
{

View File

@@ -127,7 +127,7 @@ void Eval::evaluateInfixExpression (const std::string& e, Variant& v) const
// Reduce e to a vector of tokens.
Lexer l (e);
l.ambiguity (_ambiguity);
std::vector <std::pair <std::string, Lexer::Type> > tokens;
std::vector <std::pair <std::string, Lexer::Type>> tokens;
std::string token;
Lexer::Type type;
while (l.token (token, type))
@@ -155,7 +155,7 @@ void Eval::evaluatePostfixExpression (const std::string& e, Variant& v) const
// Reduce e to a vector of tokens.
Lexer l (e);
l.ambiguity (_ambiguity);
std::vector <std::pair <std::string, Lexer::Type> > tokens;
std::vector <std::pair <std::string, Lexer::Type>> tokens;
std::string token;
Lexer::Type type;
while (l.token (token, type))
@@ -179,7 +179,7 @@ void Eval::compileExpression (const std::string& e)
while (l.token (token, type))
{
if (_debug)
context.debug ("Lexer '" + token + "' " + Lexer::type_name (type));
context.debug ("Lexer '" + token + "' " + Lexer::typeToString (type));
_compiled.push_back (std::pair <std::string, Lexer::Type> (token, type));
}
@@ -236,7 +236,7 @@ void Eval::getBinaryOperators (std::vector <std::string>& all)
////////////////////////////////////////////////////////////////////////////////
void Eval::evaluatePostfixStack (
const std::vector <std::pair <std::string, Lexer::Type> >& tokens,
const std::vector <std::pair <std::string, Lexer::Type>>& tokens,
Variant& result) const
{
if (tokens.size () == 0)
@@ -245,11 +245,11 @@ void Eval::evaluatePostfixStack (
// This is stack used by the postfix evaluator.
std::vector <Variant> values;
std::vector <std::pair <std::string, Lexer::Type> >::const_iterator token;
std::vector <std::pair <std::string, Lexer::Type>>::const_iterator token;
for (token = tokens.begin (); token != tokens.end (); ++token)
{
// Unary operators.
if (token->second == Lexer::typeOperator &&
if (token->second == Lexer::Type::op &&
token->first == "!")
{
if (values.size () < 1)
@@ -262,7 +262,7 @@ void Eval::evaluatePostfixStack (
if (_debug)
context.debug (format ("Eval {1} ↓'{2}' → ↑'{3}'", token->first, (std::string) right, (std::string) result));
}
else if (token->second == Lexer::typeOperator &&
else if (token->second == Lexer::Type::op &&
token->first == "_neg_")
{
if (values.size () < 1)
@@ -278,7 +278,7 @@ void Eval::evaluatePostfixStack (
if (_debug)
context.debug (format ("Eval {1} ↓'{2}' → ↑'{3}'", token->first, (std::string) right, (std::string) result));
}
else if (token->second == Lexer::typeOperator &&
else if (token->second == Lexer::Type::op &&
token->first == "_pos_")
{
// The _pos_ operator is a NOP.
@@ -287,7 +287,7 @@ void Eval::evaluatePostfixStack (
}
// Binary operators.
else if (token->second == Lexer::typeOperator)
else if (token->second == Lexer::Type::op)
{
if (values.size () < 2)
throw std::string (STRING_EVAL_NO_EVAL);
@@ -338,24 +338,28 @@ void Eval::evaluatePostfixStack (
Variant v (token->first);
switch (token->second)
{
case Lexer::typeNumber:
case Lexer::typeHex:
v.cast (Variant::type_integer);
if (_debug)
context.debug (format ("Eval literal number ↑'{1}'", (std::string) v));
case Lexer::Type::number:
if (Lexer::isAllDigits (token->first))
{
v.cast (Variant::type_integer);
if (_debug)
context.debug (format ("Eval literal number ↑'{1}'", (std::string) v));
}
else
{
v.cast (Variant::type_real);
if (_debug)
context.debug (format ("Eval literal decimal ↑'{1}'", (std::string) v));
}
break;
case Lexer::typeDecimal:
v.cast (Variant::type_real);
if (_debug)
context.debug (format ("Eval literal decimal ↑'{1}'", (std::string) v));
break;
case Lexer::typeOperator:
case Lexer::Type::op:
throw std::string (STRING_EVAL_OP_EXPECTED);
break;
case Lexer::typeIdentifier:
case Lexer::Type::dom:
case Lexer::Type::identifier:
{
bool found = false;
std::vector <bool (*)(const std::string&, Variant&)>::const_iterator source;
@@ -380,20 +384,33 @@ void Eval::evaluatePostfixStack (
}
break;
case Lexer::typeDate:
case Lexer::Type::date:
v.cast (Variant::type_date);
if (_debug)
context.debug (format ("Eval literal date ↑'{1}'", (std::string) v));
break;
case Lexer::typeDuration:
case Lexer::Type::duration:
v.cast (Variant::type_duration);
if (_debug)
context.debug (format ("Eval literal duration ↑'{1}'", (std::string) v));
break;
// Nothing to do.
case Lexer::typeString:
/*
case Lexer::Type::uuid:
case Lexer::Type::hex:
case Lexer::Type::list:
case Lexer::Type::url:
case Lexer::Type::pair:
case Lexer::Type::separator:
case Lexer::Type::tag:
case Lexer::Type::path:
case Lexer::Type::substitution:
case Lexer::Type::pattern:
case Lexer::Type::word:
*/
case Lexer::Type::string:
default:
if (_debug)
context.debug (format ("Eval literal string ↑'{1}'", (std::string) v));
@@ -427,26 +444,26 @@ void Eval::evaluatePostfixStack (
// Primitive --> "(" Logical ")" | Variant
//
void Eval::infixParse (
std::vector <std::pair <std::string, Lexer::Type> >& infix) const
std::vector <std::pair <std::string, Lexer::Type>>& infix) const
{
int i = 0;
unsigned int i = 0;
parseLogical (infix, i);
}
////////////////////////////////////////////////////////////////////////////////
// Logical --> Regex {( "and" | "or" | "xor" ) Regex}
bool Eval::parseLogical (
std::vector <std::pair <std::string, Lexer::Type> >& infix,
int &i) const
std::vector <std::pair <std::string, Lexer::Type>>& infix,
unsigned int &i) const
{
if (i < infix.size () &&
parseRegex (infix, i))
{
while (i < infix.size () &&
infix[i].second == Lexer::Type::op &&
(infix[i].first == "and" ||
infix[i].first == "or" ||
infix[i].first == "xor") &&
infix[i].second == Lexer::typeOperator)
infix[i].first == "xor"))
{
++i;
if (! parseRegex (infix, i))
@@ -462,16 +479,16 @@ bool Eval::parseLogical (
////////////////////////////////////////////////////////////////////////////////
// Regex --> Equality {( "~" | "!~" ) Equality}
bool Eval::parseRegex (
std::vector <std::pair <std::string, Lexer::Type> >& infix,
int &i) const
std::vector <std::pair <std::string, Lexer::Type>>& infix,
unsigned int &i) const
{
if (i < infix.size () &&
parseEquality (infix, i))
{
while (i < infix.size () &&
infix[i].second == Lexer::Type::op &&
(infix[i].first == "~" ||
infix[i].first == "!~") &&
infix[i].second == Lexer::typeOperator)
infix[i].first == "!~"))
{
++i;
if (! parseEquality (infix, i))
@@ -487,18 +504,18 @@ bool Eval::parseRegex (
////////////////////////////////////////////////////////////////////////////////
// Equality --> Comparative {( "==" | "=" | "!==" | "!=" ) Comparative}
bool Eval::parseEquality (
std::vector <std::pair <std::string, Lexer::Type> >& infix,
int &i) const
std::vector <std::pair <std::string, Lexer::Type>>& infix,
unsigned int &i) const
{
if (i < infix.size () &&
parseComparative (infix, i))
{
while (i < infix.size () &&
infix[i].second == Lexer::Type::op &&
(infix[i].first == "==" ||
infix[i].first == "=" ||
infix[i].first == "!==" ||
infix[i].first == "!=") &&
infix[i].second == Lexer::typeOperator)
infix[i].first == "!="))
{
++i;
if (! parseComparative (infix, i))
@@ -514,18 +531,18 @@ bool Eval::parseEquality (
////////////////////////////////////////////////////////////////////////////////
// Comparative --> Arithmetic {( "<=" | "<" | ">=" | ">" ) Arithmetic}
bool Eval::parseComparative (
std::vector <std::pair <std::string, Lexer::Type> >& infix,
int &i) const
std::vector <std::pair <std::string, Lexer::Type>>& infix,
unsigned int &i) const
{
if (i < infix.size () &&
parseArithmetic (infix, i))
{
while (i < infix.size () &&
infix[i].second == Lexer::Type::op &&
(infix[i].first == "<=" ||
infix[i].first == "<" ||
infix[i].first == ">=" ||
infix[i].first == ">") &&
infix[i].second == Lexer::typeOperator)
infix[i].first == ">"))
{
++i;
if (! parseArithmetic (infix, i))
@@ -541,16 +558,16 @@ bool Eval::parseComparative (
////////////////////////////////////////////////////////////////////////////////
// Arithmetic --> Geometric {( "+" | "-" ) Geometric}
bool Eval::parseArithmetic (
std::vector <std::pair <std::string, Lexer::Type> >& infix,
int &i) const
std::vector <std::pair <std::string, Lexer::Type>>& infix,
unsigned int &i) const
{
if (i < infix.size () &&
parseGeometric (infix, i))
{
while (i < infix.size () &&
infix[i].second == Lexer::Type::op &&
(infix[i].first == "+" ||
infix[i].first == "-") &&
infix[i].second == Lexer::typeOperator)
infix[i].first == "-"))
{
++i;
if (! parseGeometric (infix, i))
@@ -566,17 +583,17 @@ bool Eval::parseArithmetic (
////////////////////////////////////////////////////////////////////////////////
// Geometric --> Tag {( "*" | "/" | "%" ) Tag}
bool Eval::parseGeometric (
std::vector <std::pair <std::string, Lexer::Type> >& infix,
int &i) const
std::vector <std::pair <std::string, Lexer::Type>>& infix,
unsigned int &i) const
{
if (i < infix.size () &&
parseTag (infix, i))
{
while (i < infix.size () &&
infix[i].second == Lexer::Type::op &&
(infix[i].first == "*" ||
infix[i].first == "/" ||
infix[i].first == "%") &&
infix[i].second == Lexer::typeOperator)
infix[i].first == "%"))
{
++i;
if (! parseTag (infix, i))
@@ -592,16 +609,16 @@ bool Eval::parseGeometric (
////////////////////////////////////////////////////////////////////////////////
// Tag --> Unary {( "_hastag_" | "_notag_" ) Unary}
bool Eval::parseTag (
std::vector <std::pair <std::string, Lexer::Type> >& infix,
int &i) const
std::vector <std::pair <std::string, Lexer::Type>>& infix,
unsigned int &i) const
{
if (i < infix.size () &&
parseUnary (infix, i))
{
while (i < infix.size () &&
infix[i].second == Lexer::Type::op &&
(infix[i].first == "_hastag_" ||
infix[i].first == "_notag_") &&
infix[i].second == Lexer::typeOperator)
infix[i].first == "_notag_"))
{
++i;
if (! parseUnary (infix, i))
@@ -617,8 +634,8 @@ bool Eval::parseTag (
////////////////////////////////////////////////////////////////////////////////
// Unary --> [( "-" | "+" | "!" )] Exponent
bool Eval::parseUnary (
std::vector <std::pair <std::string, Lexer::Type> >& infix,
int &i) const
std::vector <std::pair <std::string, Lexer::Type>>& infix,
unsigned int &i) const
{
if (i < infix.size ())
{
@@ -644,15 +661,15 @@ bool Eval::parseUnary (
////////////////////////////////////////////////////////////////////////////////
// Exponent --> Primitive ["^" Primitive]
bool Eval::parseExponent (
std::vector <std::pair <std::string, Lexer::Type> >& infix,
int &i) const
std::vector <std::pair <std::string, Lexer::Type>>& infix,
unsigned int& i) const
{
if (i < infix.size () &&
parsePrimitive (infix, i))
{
while (i < infix.size () &&
infix[i].first == "^" &&
infix[i].second == Lexer::typeOperator)
infix[i].second == Lexer::Type::op &&
infix[i].first == "^")
{
++i;
if (! parsePrimitive (infix, i))
@@ -668,8 +685,8 @@ bool Eval::parseExponent (
////////////////////////////////////////////////////////////////////////////////
// Primitive --> "(" Logical ")" | Variant
bool Eval::parsePrimitive (
std::vector <std::pair <std::string, Lexer::Type> >& infix,
int &i) const
std::vector <std::pair <std::string, Lexer::Type>>& infix,
unsigned int &i) const
{
if (i < infix.size ())
{
@@ -706,7 +723,7 @@ bool Eval::parsePrimitive (
++i;
return true;
}
else if (infix[i].second != Lexer::typeOperator)
else if (infix[i].second != Lexer::Type::op)
{
++i;
return true;
@@ -750,32 +767,32 @@ bool Eval::parsePrimitive (
// Exit.
//
void Eval::infixToPostfix (
std::vector <std::pair <std::string, Lexer::Type> >& infix) const
std::vector <std::pair <std::string, Lexer::Type>>& infix) const
{
// Short circuit.
if (infix.size () == 1)
return;
// Result.
std::vector <std::pair <std::string, Lexer::Type> > postfix;
std::vector <std::pair <std::string, Lexer::Type>> postfix;
// Shunting yard.
std::vector <std::pair <std::string, Lexer::Type> > op_stack;
std::vector <std::pair <std::string, Lexer::Type>> op_stack;
// Operator characteristics.
char type;
int precedence;
unsigned int precedence;
char associativity;
std::vector <std::pair <std::string, Lexer::Type> >::iterator token;
std::vector <std::pair <std::string, Lexer::Type>>::iterator token;
for (token = infix.begin (); token != infix.end (); ++token)
{
if (token->second == Lexer::typeOperator &&
if (token->second == Lexer::Type::op &&
token->first == "(")
{
op_stack.push_back (*token);
}
else if (token->second == Lexer::typeOperator &&
else if (token->second == Lexer::Type::op &&
token->first == ")")
{
while (op_stack.size () &&
@@ -790,11 +807,11 @@ void Eval::infixToPostfix (
else
throw std::string ("Mismatched parentheses in expression");
}
else if (token->second == Lexer::typeOperator &&
else if (token->second == Lexer::Type::op &&
identifyOperator (token->first, type, precedence, associativity))
{
char type2;
int precedence2;
unsigned int precedence2;
char associativity2;
while (op_stack.size () > 0 &&
identifyOperator (op_stack.back ().first, type2, precedence2, associativity2) &&
@@ -830,7 +847,7 @@ void Eval::infixToPostfix (
bool Eval::identifyOperator (
const std::string& op,
char& type,
int& precedence,
unsigned int& precedence,
char& associativity) const
{
for (unsigned int i = 0; i < NUM_OPERATORS; ++i)
@@ -849,22 +866,21 @@ bool Eval::identifyOperator (
////////////////////////////////////////////////////////////////////////////////
std::string Eval::dump (
std::vector <std::pair <std::string, Lexer::Type> >& tokens) const
std::vector <std::pair <std::string, Lexer::Type>>& tokens) const
{
// Set up a color mapping.
std::map <Lexer::Type, Color> color_map;
color_map[Lexer::typeNone] = Color ("rgb000 on gray6");
color_map[Lexer::typeOperator] = Color ("gray14 on gray6");
color_map[Lexer::typeNumber] = Color ("rgb530 on gray6");
color_map[Lexer::typeHex] = Color ("rgb303 on gray6");
color_map[Lexer::typeDecimal] = Color ("rgb530 on gray6");
color_map[Lexer::typeString] = Color ("rgb550 on gray6");
color_map[Lexer::typeIdentifier] = Color ("rgb035 on gray6");
color_map[Lexer::typeDate] = Color ("rgb150 on gray6");
color_map[Lexer::typeDuration] = Color ("rgb531 on gray6");
color_map[Lexer::Type::op] = Color ("gray14 on gray6");
color_map[Lexer::Type::number] = Color ("rgb530 on gray6");
color_map[Lexer::Type::hex] = Color ("rgb303 on gray6");
color_map[Lexer::Type::string] = Color ("rgb550 on gray6");
color_map[Lexer::Type::dom] = Color ("rgb045 on gray6");
color_map[Lexer::Type::identifier] = Color ("rgb035 on gray6");
color_map[Lexer::Type::date] = Color ("rgb150 on gray6");
color_map[Lexer::Type::duration] = Color ("rgb531 on gray6");
std::string output;
std::vector <std::pair <std::string, Lexer::Type> >::const_iterator i;
std::vector <std::pair <std::string, Lexer::Type>>::const_iterator i;
for (i = tokens.begin (); i != tokens.end (); ++i)
{
if (i != tokens.begin ())
@@ -874,7 +890,7 @@ std::string Eval::dump (
if (color_map[i->second].nontrivial ())
c = color_map[i->second];
else
c = color_map[Lexer::typeNone];
c = Color ("rgb000 on gray6");
output += c.colorize (i->first);
}

View File

@@ -53,28 +53,28 @@ public:
static void getBinaryOperators (std::vector <std::string>&);
private:
void evaluatePostfixStack (const std::vector <std::pair <std::string, Lexer::Type> >&, Variant&) const;
void infixToPostfix (std::vector <std::pair <std::string, Lexer::Type> >&) const;
void infixParse (std::vector <std::pair <std::string, Lexer::Type> >&) const;
bool parseLogical (std::vector <std::pair <std::string, Lexer::Type> >&, int &) const;
bool parseRegex (std::vector <std::pair <std::string, Lexer::Type> >&, int &) const;
bool parseEquality (std::vector <std::pair <std::string, Lexer::Type> >&, int &) const;
bool parseComparative (std::vector <std::pair <std::string, Lexer::Type> >&, int &) const;
bool parseArithmetic (std::vector <std::pair <std::string, Lexer::Type> >&, int &) const;
bool parseGeometric (std::vector <std::pair <std::string, Lexer::Type> >&, int &) const;
bool parseTag (std::vector <std::pair <std::string, Lexer::Type> >&, int &) const;
bool parseUnary (std::vector <std::pair <std::string, Lexer::Type> >&, int &) const;
bool parseExponent (std::vector <std::pair <std::string, Lexer::Type> >&, int &) const;
bool parsePrimitive (std::vector <std::pair <std::string, Lexer::Type> >&, int &) const;
bool identifyOperator (const std::string&, char&, int&, char&) const;
void evaluatePostfixStack (const std::vector <std::pair <std::string, Lexer::Type>>&, Variant&) const;
void infixToPostfix (std::vector <std::pair <std::string, Lexer::Type>>&) const;
void infixParse (std::vector <std::pair <std::string, Lexer::Type>>&) const;
bool parseLogical (std::vector <std::pair <std::string, Lexer::Type>>&, unsigned int &) const;
bool parseRegex (std::vector <std::pair <std::string, Lexer::Type>>&, unsigned int &) const;
bool parseEquality (std::vector <std::pair <std::string, Lexer::Type>>&, unsigned int &) const;
bool parseComparative (std::vector <std::pair <std::string, Lexer::Type>>&, unsigned int &) const;
bool parseArithmetic (std::vector <std::pair <std::string, Lexer::Type>>&, unsigned int &) const;
bool parseGeometric (std::vector <std::pair <std::string, Lexer::Type>>&, unsigned int &) const;
bool parseTag (std::vector <std::pair <std::string, Lexer::Type>>&, unsigned int &) const;
bool parseUnary (std::vector <std::pair <std::string, Lexer::Type>>&, unsigned int &) const;
bool parseExponent (std::vector <std::pair <std::string, Lexer::Type>>&, unsigned int &) const;
bool parsePrimitive (std::vector <std::pair <std::string, Lexer::Type>>&, unsigned int &) const;
bool identifyOperator (const std::string&, char&, unsigned int&, char&) const;
std::string dump (std::vector <std::pair <std::string, Lexer::Type> >&) const;
std::string dump (std::vector <std::pair <std::string, Lexer::Type>>&) const;
private:
std::vector <bool (*)(const std::string&, Variant&)> _sources;
bool _ambiguity;
bool _debug;
std::vector <std::pair <std::string, Lexer::Type> > _compiled;
std::vector <std::pair <std::string, Lexer::Type>> _compiled;
};

View File

@@ -67,7 +67,7 @@ Filter::~Filter ()
////////////////////////////////////////////////////////////////////////////////
// Take an input set of tasks and filter into a subset.
void Filter::subset (const std::vector <Task>& input, std::vector <Task>& output)
void Filter::subset (const std::vector <Task>& input, std::vector <Task>& output, bool applyContext /* = true */)
{
context.timer_filter.start ();
_startCount = (int) input.size ();
@@ -75,7 +75,7 @@ void Filter::subset (const std::vector <Task>& input, std::vector <Task>& output
if (context.config.getInteger ("debug.parser") >= 1)
context.debug (context.cli.dump ("Filter::subset"));
std::string filterExpr = context.cli.getFilter ();
std::string filterExpr = context.cli.getFilter (applyContext);
if (filterExpr.length ())
{
Eval eval;
@@ -111,7 +111,7 @@ void Filter::subset (const std::vector <Task>& input, std::vector <Task>& output
////////////////////////////////////////////////////////////////////////////////
// Take the set of all tasks and filter into a subset.
void Filter::subset (std::vector <Task>& output)
void Filter::subset (std::vector <Task>& output, bool applyContext /* = true */)
{
context.timer_filter.start ();
@@ -119,7 +119,7 @@ void Filter::subset (std::vector <Task>& output)
context.debug (context.cli.dump ("Filter::subset"));
bool shortcut = false;
std::string filterExpr = context.cli.getFilter ();
std::string filterExpr = context.cli.getFilter (applyContext);
if (filterExpr.length ())
{
context.timer_filter.stop ();

View File

@@ -40,8 +40,8 @@ public:
Filter ();
~Filter ();
void subset (const std::vector <Task>&, std::vector <Task>&);
void subset (std::vector <Task>&);
void subset (const std::vector <Task>&, std::vector <Task>&, bool applyContext = true);
void subset (std::vector <Task>&, bool applyContext = true);
bool pendingOnly ();
void safety ();

View File

@@ -38,8 +38,10 @@
#include <Context.h>
#include <JSON.h>
#include <Hooks.h>
#include <Timer.h>
#include <text.h>
#include <util.h>
#include <i18n.h>
extern Context context;
@@ -73,7 +75,17 @@ void Hooks::initialize ()
{
std::vector <std::string>::iterator i;
for (i = _scripts.begin (); i != _scripts.end (); ++i)
context.debug ("Found hook script " + *i);
{
Path p (*i);
std::string name = p.name ();
if (name.substr (0, 6) == "on-add" ||
name.substr (0, 9) == "on-modify" ||
name.substr (0, 9) == "on-launch" ||
name.substr (0, 7) == "on-exit")
context.debug ("Found hook script " + *i);
else
context.debug ("Found misnamed hook script " + *i);
}
}
}
else if (_debug >= 1)
@@ -98,8 +110,7 @@ bool Hooks::enable (bool value)
// - none
//
// Output:
// - all emitted JSON lines are added/modified as tasks, if the exit code is
// zero, otherwise ignored.
// - JSON not allowed.
// - all emitted non-JSON lines are considered feedback or error messages
// depending on the status code.
//
@@ -120,29 +131,28 @@ void Hooks::onLaunch ()
std::vector <std::string> output;
int status = callHookScript (*script, input, output);
std::vector <std::string>::iterator line;
for (line = output.begin (); line != output.end (); ++line)
{
if (isJSON (*line))
{
if (status == 0)
{
// Only 'add' is possible.
Task newTask (*line);
context.tdb2.add (newTask);
}
}
else
{
if (status == 0)
context.header (*line);
else
context.error (*line);
}
}
std::vector <std::string> outputJSON;
std::vector <std::string> outputFeedback;
separateOutput (output, outputJSON, outputFeedback);
assertNTasks (outputJSON, 0);
if (status == 0)
{
std::vector <std::string>::iterator message;
for (message = outputFeedback.begin (); message != outputFeedback.end (); ++message)
context.footnote (*message);
}
else
{
assertFeedback (outputFeedback);
std::vector <std::string>::iterator message;
for (message = outputFeedback.begin (); message != outputFeedback.end (); ++message)
context.error (*message);
if (status)
throw 0; // This is how hooks silently terminate processing.
}
}
}
@@ -157,7 +167,7 @@ void Hooks::onLaunch ()
// - read-only line of JSON for each task added/modified
//
// Output:
// - any emitted JSON is ignored
// - all emitted JSON is ignored
// - all emitted non-JSON lines are considered feedback or error messages
// depending on the status code.
//
@@ -188,24 +198,28 @@ void Hooks::onExit ()
std::vector <std::string> output;
int status = callHookScript (*script, input, output);
std::vector <std::string>::iterator line;
for (line = output.begin (); line != output.end (); ++line)
{
if (isJSON (*line))
{
context.error ("JSON output ignored: {1}");
}
else
{
if (status == 0)
context.footnote (*line);
else
context.error (*line);
}
}
std::vector <std::string> outputJSON;
std::vector <std::string> outputFeedback;
separateOutput (output, outputJSON, outputFeedback);
assertNTasks (outputJSON, 0);
if (status == 0)
{
std::vector <std::string>::iterator message;
for (message = outputFeedback.begin (); message != outputFeedback.end (); ++message)
context.footnote (*message);
}
else
{
assertFeedback (outputFeedback);
std::vector <std::string>::iterator message;
for (message = outputFeedback.begin (); message != outputFeedback.end (); ++message)
context.error (*message);
if (status)
throw 0; // This is how hooks silently terminate processing.
}
}
}
@@ -219,14 +233,14 @@ void Hooks::onExit ()
// - line of JSON for the task added
//
// Output:
// - all emitted JSON lines are added/modified as tasks, if the exit code is
// zero, otherwise ignored.
// - emitted JSON for the input task is added, if the exit code is zero,
// otherwise ignored.
// - all emitted non-JSON lines are considered feedback or error messages
// depending on the status code.
//
void Hooks::onAdd (std::vector <Task>& tasks)
void Hooks::onAdd (Task& task)
{
if (! _enabled || tasks.size () < 1)
if (! _enabled)
return;
context.timer_hooks.start ();
@@ -234,9 +248,9 @@ void Hooks::onAdd (std::vector <Task>& tasks)
std::vector <std::string> matchingScripts = scripts ("on-add");
if (matchingScripts.size ())
{
// Convert vector of tasks to a vector of strings.
// Convert task to a vector of strings.
std::vector <std::string> input;
input.push_back (tasks[0].composeJSON ());
input.push_back (task.composeJSON ());
// Call the hook scripts.
std::vector <std::string>::iterator script;
@@ -245,33 +259,37 @@ void Hooks::onAdd (std::vector <Task>& tasks)
std::vector <std::string> output;
int status = callHookScript (*script, input, output);
input.clear ();
std::vector <std::string>::iterator line;
for (line = output.begin (); line != output.end (); ++line)
{
if (isJSON (*line))
{
if (status == 0)
input.push_back (*line);
}
else
{
if (status == 0)
context.footnote (*line);
else
context.error (*line);
}
}
std::vector <std::string> outputJSON;
std::vector <std::string> outputFeedback;
separateOutput (output, outputJSON, outputFeedback);
if (status == 0)
{
assertNTasks (outputJSON, 1);
assertValidJSON (outputJSON);
assertSameTask (outputJSON, task);
// Propagate forward to the next script.
input[0] = outputJSON[0];
std::vector <std::string>::iterator message;
for (message = outputFeedback.begin (); message != outputFeedback.end (); ++message)
context.footnote (*message);
}
else
{
assertFeedback (outputFeedback);
std::vector <std::string>::iterator message;
for (message = outputFeedback.begin (); message != outputFeedback.end (); ++message)
context.error (*message);
if (status)
throw 0; // This is how hooks silently terminate processing.
}
}
// Transfer the modified task lines back to the original task list.
tasks.clear ();
std::vector <std::string>::iterator i;
for (i = input.begin (); i != input.end (); ++i)
tasks.push_back (Task (*i));
// Transfer the modified task back to the original task.
task = Task (input[0]);
}
context.timer_hooks.stop ();
@@ -285,14 +303,14 @@ void Hooks::onAdd (std::vector <Task>& tasks)
// - line of JSON for the modified task, the diff being the modification
//
// Output:
// - all emitted JSON lines are added/modified as tasks, if the exit code is
// zero, otherwise ignored.
// - emitted JSON for the input task is saved, if the exit code is zero,
// otherwise ignored.
// - all emitted non-JSON lines are considered feedback or error messages
// depending on the status code.
//
void Hooks::onModify (const Task& before, std::vector <Task>& tasks)
void Hooks::onModify (const Task& before, Task& after)
{
if (! _enabled || tasks.size () < 1)
if (! _enabled)
return;
context.timer_hooks.start ();
@@ -300,61 +318,48 @@ void Hooks::onModify (const Task& before, std::vector <Task>& tasks)
std::vector <std::string> matchingScripts = scripts ("on-modify");
if (matchingScripts.size ())
{
// Prepare invariants.
std::string beforeJSON = before.composeJSON ();
std::string uuidPattern = "\"uuid\":\"" + before.get ("uuid") + "\"";
// Convert vector of tasks to a vector of strings.
std::vector <std::string> input;
input.push_back (beforeJSON); // [0] original, never changes
input.push_back (tasks[0].composeJSON ()); // [1] original'
input.push_back (before.composeJSON ()); // [line 0] original, never changes
input.push_back (after.composeJSON ()); // [line 1] modified
// Call the hook scripts.
std::vector <std::string>::iterator script;
for (script = matchingScripts.begin (); script != matchingScripts.end (); ++script)
{
std::vector <std::string> firstTwoOnly;
firstTwoOnly.push_back (input[0]);
firstTwoOnly.push_back (input[1]);
std::vector <std::string> output;
int status = callHookScript (*script, firstTwoOnly, output);
int status = callHookScript (*script, input, output);
// Start from scratch.
input[1] = ""; // [1] placeholder for original'
std::vector <std::string> outputJSON;
std::vector <std::string> outputFeedback;
separateOutput (output, outputJSON, outputFeedback);
std::vector <std::string>::iterator line;
for (line = output.begin (); line != output.end (); ++line)
if (status == 0)
{
if (isJSON (*line))
{
if (status == 0)
{
if (line->find (uuidPattern) != std::string::npos)
input[1] = *line; // [1] original'
else
input.push_back (*line); // [n > 1] extras
}
}
else
{
if (status == 0)
context.footnote (*line);
else
context.error (*line);
}
}
assertNTasks (outputJSON, 1);
assertValidJSON (outputJSON);
assertSameTask (outputJSON, before);
// Propagate accepted changes forward to the next script.
input[1] = outputJSON[0];
std::vector <std::string>::iterator message;
for (message = outputFeedback.begin (); message != outputFeedback.end (); ++message)
context.footnote (*message);
}
else
{
assertFeedback (outputFeedback);
std::vector <std::string>::iterator message;
for (message = outputFeedback.begin (); message != outputFeedback.end (); ++message)
context.error (*message);
if (status)
throw 0; // This is how hooks silently terminate processing.
}
}
// Transfer the modified task lines back to the original task list.
tasks.clear ();
std::vector <std::string>::iterator i;
for (i = input.begin (); i != input.end (); ++i)
if (i != input.begin ())
tasks.push_back (Task (*i));
after = Task (input[1]);
}
context.timer_hooks.stop ();
@@ -384,65 +389,162 @@ std::vector <std::string> Hooks::scripts (const std::string& event)
return matching;
}
////////////////////////////////////////////////////////////////////////////////
void Hooks::separateOutput (
const std::vector <std::string>& output,
std::vector <std::string>& json,
std::vector <std::string>& feedback) const
{
std::vector <std::string>::const_iterator i;
for (i = output.begin (); i != output.end (); ++i)
{
if (isJSON (*i))
json.push_back (*i);
else
feedback.push_back (*i);
}
}
////////////////////////////////////////////////////////////////////////////////
bool Hooks::isJSON (const std::string& input) const
{
// Does it even look like JSON? {...}
if (input.length () > 2 &&
input[0] == '{' &&
input[input.length () - 1] == '}')
return input.length () > 2 &&
input[0] == '{' &&
input[input.length () - 1] == '}';
}
////////////////////////////////////////////////////////////////////////////////
void Hooks::assertValidJSON (const std::vector <std::string>& input) const
{
std::vector <std::string>::const_iterator i;
for (i = input.begin (); i != input.end (); i++)
{
if (i->length () < 3 ||
(*i)[0] != '{' ||
(*i)[i->length () - 1] != '}')
{
context.error (STRING_HOOK_ERROR_OBJECT);
throw 0;
}
try
{
// The absolute minimum a task needs is:
bool foundDescription = false;
// Parse the whole thing.
json::value* root = json::parse (input);
if (root->type () == json::j_object)
json::value* root = json::parse (*i);
if (root->type () != json::j_object)
{
json::object* root_obj = (json::object*)root;
// For each object element...
json_object_iter i;
for (i = root_obj->_data.begin ();
i != root_obj->_data.end ();
++i)
{
// If the attribute is a recognized column.
std::string type = Task::attributes[i->first];
if (type == "string" && i->first == "description")
foundDescription = true;
}
context.error (STRING_HOOK_ERROR_OBJECT);
throw 0;
}
else
throw std::string ("Object expected.");
// It's JSON, but is it a task?
if (! foundDescription)
throw std::string ("Missing 'description' attribute, of type 'string'.");
if (((json::object*)root)->_data.find ("description") == ((json::object*)root)->_data.end ())
{
context.error (STRING_HOOK_ERROR_NODESC);
throw 0;
}
// Yep, looks like a JSON task.
return true;
if (((json::object*)root)->_data.find ("uuid") == ((json::object*)root)->_data.end ())
{
context.error (STRING_HOOK_ERROR_NOUUID);
throw 0;
}
}
catch (const std::string& e)
{
if (_debug >= 1)
context.error ("Hook output looks like JSON, but is not a valid task.");
if (_debug >= 2)
context.error ("JSON " + e);
context.error (format (STRING_HOOK_ERROR_SYNTAX, *i));
if (_debug)
context.error (STRING_HOOK_ERROR_JSON + e);
throw 0;
}
catch (...)
{
if (_debug >= 1)
context.error ("Hook output looks like JSON, but fails to parse.");
context.error (STRING_HOOK_ERROR_NOPARSE + *i);
throw 0;
}
}
}
return false;
////////////////////////////////////////////////////////////////////////////////
void Hooks::assertNTasks (const std::vector <std::string>& input, unsigned int n) const
{
if (input.size () != n)
{
context.error (format (STRING_HOOK_ERROR_BAD_NUM, n, (int) input.size ()));
throw 0;
}
}
////////////////////////////////////////////////////////////////////////////////
void Hooks::assertSameTask (const std::vector <std::string>& input, const Task& task) const
{
std::string uuid = task.get ("uuid");
std::vector <std::string>::const_iterator i;
for (i = input.begin (); i != input.end (); i++)
{
json::object* root_obj = (json::object*)json::parse (*i);
// If there is no UUID at all.
json_object_iter u = root_obj->_data.find ("uuid");
if (u == root_obj->_data.end () ||
u->second->type () != json::j_string)
{
context.error (format (STRING_HOOK_ERROR_SAME1, uuid));
throw 0;
}
json::string* up = (json::string*) u->second;
std::string json_uuid = json::decode (unquoteText (up->dump ()));
if (json_uuid != uuid)
{
context.error (format (STRING_HOOK_ERROR_SAME2, uuid, json_uuid));
throw 0;
}
}
}
////////////////////////////////////////////////////////////////////////////////
void Hooks::assertFeedback (const std::vector <std::string>& input) const
{
bool foundSomething = false;
std::vector <std::string>::const_iterator i;
for (i = input.begin (); i != input.end (); ++i)
if (nontrivial (*i))
foundSomething = true;
if (! foundSomething)
{
context.error (STRING_HOOK_ERROR_NOFEEDBACK);
throw 0;
}
}
////////////////////////////////////////////////////////////////////////////////
std::vector <std::string>& Hooks::buildHookScriptArgs (std::vector <std::string>& args)
{
Variant v;
// Hooks API version.
args.push_back ("api:2");
// Command line Taskwarrior was called with.
context.dom.get ("context.args", v);
args.push_back ("args:" + std::string (v));
// Command to be executed.
args.push_back ("command:" + context.cli.getCommand ());
// rc file used after applying all overrides.
args.push_back ("rc:" + context.rc_file._data);
// Directory containing *.data files.
args.push_back ("data:" + context.data_dir._data);
// Taskwarrior version, same as returned by "task --version"
args.push_back ("version:" + std::string(VERSION));
return args;
}
////////////////////////////////////////////////////////////////////////////////
@@ -452,11 +554,11 @@ int Hooks::callHookScript (
std::vector <std::string>& output)
{
if (_debug >= 1)
context.debug ("Hooks: Calling " + script);
context.debug ("Hook: Calling " + script);
if (_debug >= 2)
{
context.debug ("Hooks: input");
context.debug ("Hook: input");
std::vector <std::string>::const_iterator i;
for (i = input.begin (); i != input.end (); ++i)
context.debug (" " + *i);
@@ -467,21 +569,39 @@ int Hooks::callHookScript (
for (i = input.begin (); i != input.end (); ++i)
inputStr += *i + "\n";
std::string outputStr;
std::vector <std::string> args;
int status = execute (script, args, inputStr, outputStr);
buildHookScriptArgs (args);
if (_debug >= 2)
{
context.debug ("Hooks: args");
for (auto arg: args)
context.debug (" " + arg);
}
// Measure time for each hook if running in debug
int status;
std::string outputStr;
if (_debug >= 2)
{
Timer timer_per_hook("Hooks::execute (" + script + ")");
timer_per_hook.start();
status = execute (script, args, inputStr, outputStr);
}
else
status = execute (script, args, inputStr, outputStr);
split (output, outputStr, '\n');
if (_debug >= 2)
{
context.debug ("Hooks: output");
context.debug ("Hook: output");
std::vector <std::string>::iterator i;
for (i = output.begin (); i != output.end (); ++i)
if (*i != "")
context.debug (" " + *i);
context.debug (format ("Hooks: Completed with status {1}", status));
context.debug (format ("Hook: Completed with status {1}", status));
context.debug (" "); // Blank line
}

View File

@@ -44,14 +44,20 @@ public:
void onLaunch ();
void onExit ();
void onAdd (std::vector <Task>&);
void onModify (const Task&, std::vector <Task>&);
void onAdd (Task&);
void onModify (const Task&, Task&);
std::vector <std::string> list ();
private:
std::vector <std::string> scripts (const std::string&);
void separateOutput (const std::vector <std::string>&, std::vector <std::string>&, std::vector <std::string>&) const;
bool isJSON (const std::string&) const;
void assertValidJSON (const std::vector <std::string>&) const;
void assertNTasks (const std::vector <std::string>&, unsigned int) const;
void assertSameTask (const std::vector <std::string>&, const Task&) const;
void assertFeedback (const std::vector <std::string>&) const;
std::vector <std::string>& buildHookScriptArgs (std::vector <std::string>&);
int callHookScript (const std::string&, const std::vector <std::string>&, std::vector <std::string>&);
private:

View File

@@ -25,6 +25,7 @@
////////////////////////////////////////////////////////////////////////////////
#include <cmake.h>
#include <Lexer.h>
#include <ISO8601.h>
////////////////////////////////////////////////////////////////////////////////
@@ -196,7 +197,7 @@ bool ISO8601d::parse_date_time_ext (Nibbler& n)
else if (parse_off_ext (n))
;
if (! isdigit (n.next ()))
if (! Lexer::isDigit (n.next ()))
return true;
}
@@ -228,16 +229,16 @@ bool ISO8601d::parse_date_time (Nibbler& n)
if (n.skip ('Z'))
{
_utc = true;
if (!isdigit (n.next ()))
if (!Lexer::isDigit (n.next ()))
return true;
}
else if (parse_off (n))
{
if (!isdigit (n.next ()))
if (!Lexer::isDigit (n.next ()))
return true;
}
if (!isdigit (n.next ()))
if (!Lexer::isDigit (n.next ()))
return true;
}
@@ -277,13 +278,13 @@ bool ISO8601d::parse_date_ext (Nibbler& n)
}
_year = year;
if (!isdigit (n.next ()))
if (!Lexer::isDigit (n.next ()))
return true;
}
else if (n.getDigit3 (_julian))
{
_year = year;
if (!isdigit (n.next ()))
if (!Lexer::isDigit (n.next ()))
return true;
}
else if (n.getDigit2 (month) &&
@@ -293,7 +294,7 @@ bool ISO8601d::parse_date_ext (Nibbler& n)
_year = year;
_month = month;
_day = day;
if (!isdigit (n.next ()))
if (!Lexer::isDigit (n.next ()))
return true;
}
}
@@ -327,7 +328,7 @@ bool ISO8601d::parse_date (Nibbler& n, bool ambiguous)
_weekday = day;
_year = year;
if (!isdigit (n.next ()))
if (!Lexer::isDigit (n.next ()))
return true;
}
}
@@ -336,7 +337,7 @@ bool ISO8601d::parse_date (Nibbler& n, bool ambiguous)
if (n.getDigit2 (_month))
{
_year = year;
if (!isdigit (n.next ()))
if (!Lexer::isDigit (n.next ()))
return true;
}
}
@@ -345,13 +346,13 @@ bool ISO8601d::parse_date (Nibbler& n, bool ambiguous)
_year = year;
_month = month / 100;
_day = month % 100;
if (!isdigit (n.next ()))
if (!Lexer::isDigit (n.next ()))
return true;
}
else if (ambiguous && n.getDigit3 (_julian))
{
_year = year;
if (!isdigit (n.next ()))
if (!Lexer::isDigit (n.next ()))
return true;
}
}
@@ -382,7 +383,7 @@ bool ISO8601d::parse_off_ext (Nibbler& n)
offset += mm * 60;
_offset = (sign == "-") ? -offset : offset;
if (!isdigit (n.next ()))
if (!Lexer::isDigit (n.next ()))
return true;
}
}
@@ -412,7 +413,7 @@ bool ISO8601d::parse_off (Nibbler& n)
offset += mm * 60;
_offset = (sign == "-") ? -offset : offset;
if (!isdigit (n.next ()))
if (!Lexer::isDigit (n.next ()))
return true;
}
}
@@ -453,7 +454,7 @@ bool ISO8601d::parse_time_ext (Nibbler& n)
if (_ambiguity)
{
_seconds = seconds;
if (!isdigit (n.next ()))
if (!Lexer::isDigit (n.next ()))
return true;
}
}
@@ -487,7 +488,7 @@ bool ISO8601d::parse_time (Nibbler& n, bool ambiguous)
}
_seconds = seconds;
if (!isdigit (n.next ()))
if (!Lexer::isDigit (n.next ()))
return true;
}
@@ -504,7 +505,7 @@ bool ISO8601d::parse_time_utc_ext (Nibbler& n)
n.skip ('Z'))
{
_utc = true;
if (!isdigit (n.next ()))
if (!Lexer::isDigit (n.next ()))
return true;
}
@@ -521,7 +522,7 @@ bool ISO8601d::parse_time_utc (Nibbler& n)
n.skip ('Z'))
{
_utc = true;
if (!isdigit (n.next ()))
if (!Lexer::isDigit (n.next ()))
return true;
}
@@ -537,7 +538,7 @@ bool ISO8601d::parse_time_off_ext (Nibbler& n)
if (parse_time_ext (n) &&
parse_off_ext (n))
{
if (!isdigit (n.next ()))
if (!Lexer::isDigit (n.next ()))
return true;
}
@@ -553,7 +554,7 @@ bool ISO8601d::parse_time_off (Nibbler& n)
if (parse_time (n, true) &&
parse_off (n))
{
if (!isdigit (n.next ()))
if (!Lexer::isDigit (n.next ()))
return true;
}
@@ -702,7 +703,7 @@ void ISO8601d::resolve ()
t.tm_min = (seconds % 3600) / 60;
t.tm_sec = seconds % 60;
_value = utc ? timegm (&t) : timelocal (&t);
_value = utc ? timegm (&t) : mktime (&t);
}
////////////////////////////////////////////////////////////////////////////////

File diff suppressed because it is too large Load Diff

View File

@@ -27,91 +27,86 @@
#ifndef INCLUDED_LEXER
#define INCLUDED_LEXER
#include <vector>
#include <string>
#include <vector>
#include <cstddef>
// Lexer: A UTF8 lexical analyzer for every construct used on the Taskwarrior
// command line, with additional recognized types for disambiguation.
class Lexer
{
public:
// These are overridable.
static std::string dateFormat;
static bool isoEnabled;
enum Type
{
typeNone = 0,
typeString,
typeIdentifier,
typeIdentifierEscape, // Intermediate
typeEscape, // Intermediate
typeEscapeHex, // Intermediate
typeEscapeUnicode, // Intermediate
typeNumber,
typeDecimal,
typeExponentIndicator, // Intermediate
typeExponent, // Intermediate
typeHex,
typeOperator,
typeDate,
typeDuration,
typeTag,
/*
Recognizing more types means that Lexer::*_split and Lexer::token approach
the ideal form, whereby the command line becomes just one string that is
lexed into tokens. Those tokens are then simply dissected by type..
typeUUID,
typePattern,
typeSubstitution,
typeNameValue,
*/
};
enum class Type { uuid, number, hex,
string,
list, url, pair, separator,
tag,
path,
substitution, pattern,
op,
dom, identifier, word,
date, duration };
Lexer (const std::string&);
virtual ~Lexer ();
Lexer (const Lexer&); // Not implemented.
Lexer& operator= (const Lexer&); // Not implemented.
bool operator== (const Lexer&); // Not implemented.
bool token (std::string&, Type&);
bool word (std::string&, Type&);
~Lexer ();
void ambiguity (bool);
bool token (std::string&, Lexer::Type&);
static std::vector <std::pair <std::string, Lexer::Type>> tokens (const std::string&);
static std::vector <std::string> split (const std::string&);
static std::string typeToString (Lexer::Type);
static bool isAllDigits (const std::string&);
static bool isOneWord (const std::string&);
static const std::string type_name (const Type&);
static bool is_ws (int);
static bool is_ident_start (int);
static bool is_ident (int);
static bool is_single_op (int);
static bool is_dec_digit (int);
static bool boundary (int, int);
static void word_split (std::vector <std::string>&, const std::string&);
static void token_split (std::vector <std::string>&, const std::string&);
static void token_split (std::vector <std::pair <std::string, Lexer::Type> >&, const std::string&);
// Static helpers.
static const std::string typeName (const Lexer::Type&);
static bool isWhitespace (int);
static bool isAlpha (int);
static bool isDigit (int);
static bool isHexDigit (int);
static bool isIdentifierStart (int);
static bool isIdentifierNext (int);
static bool isSingleCharOperator (int);
static bool isDoubleCharOperator (int, int, int);
static bool isTripleCharOperator (int, int, int, int);
static bool isBoundary (int, int);
static bool isPunctuation (int);
static void dequote (std::string&);
private:
bool is_date (std::string&);
bool is_duration (std::string&);
bool is_punct (int) const;
bool is_num (int) const;
bool is_triple_op (int, int, int) const;
bool is_double_op (int, int, int) const;
bool is_hex_digit (int) const;
int decode_escape (int) const;
int hex_to_int (int) const;
int hex_to_int (int, int) const;
int hex_to_int (int, int, int, int) const;
void shift ();
// Helpers.
bool isEOS () const;
int hexToInt (int) const;
int hexToInt (int, int) const;
int hexToInt (int, int, int, int) const;
// Classifiers.
bool isString (std::string&, Lexer::Type&, int quote);
bool isDate (std::string&, Lexer::Type&);
bool isDuration (std::string&, Lexer::Type&);
bool isUUID (std::string&, Lexer::Type&);
bool isNumber (std::string&, Lexer::Type&);
bool isHexNumber (std::string&, Lexer::Type&);
bool isSeparator (std::string&, Lexer::Type&);
bool isList (std::string&, Lexer::Type&);
bool isURL (std::string&, Lexer::Type&);
bool isPair (std::string&, Lexer::Type&);
bool isTag (std::string&, Lexer::Type&);
bool isPath (std::string&, Lexer::Type&);
bool isSubstitution (std::string&, Lexer::Type&);
bool isPattern (std::string&, Lexer::Type&);
bool isOperator (std::string&, Lexer::Type&);
bool isDOM (std::string&, Lexer::Type&);
bool isIdentifier (std::string&, Lexer::Type&);
bool isWord (std::string&, Lexer::Type&);
private:
const std::string _input;
std::string::size_type _i;
std::string::size_type _shift_counter;
int _n0;
int _n1;
int _n2;
int _n3;
bool _boundary01;
bool _boundary12;
bool _boundary23;
bool _ambiguity;
std::string _text;
std::size_t _cursor;
std::size_t _eos;
bool _ambiguity;
};
#endif

View File

@@ -27,9 +27,9 @@
#include <cmake.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <inttypes.h>
#include <Lexer.h>
#include <Nibbler.h>
#ifdef NIBBLER_FEATURE_DATE
#include <Date.h>
@@ -284,7 +284,7 @@ bool Nibbler::getQuoted (
bool Nibbler::getDigit (int& result)
{
if (_cursor < _length &&
isdigit (_input[_cursor]))
Lexer::isDigit (_input[_cursor]))
{
result = _input[_cursor++] - '0';
return true;
@@ -300,12 +300,12 @@ bool Nibbler::getDigit6 (int& result)
if (i < _length &&
_length - i >= 6)
{
if (isdigit (_input[i + 0]) &&
isdigit (_input[i + 1]) &&
isdigit (_input[i + 2]) &&
isdigit (_input[i + 3]) &&
isdigit (_input[i + 4]) &&
isdigit (_input[i + 5]))
if (Lexer::isDigit (_input[i + 0]) &&
Lexer::isDigit (_input[i + 1]) &&
Lexer::isDigit (_input[i + 2]) &&
Lexer::isDigit (_input[i + 3]) &&
Lexer::isDigit (_input[i + 4]) &&
Lexer::isDigit (_input[i + 5]))
{
result = strtoimax (_input.substr (_cursor, 6).c_str (), NULL, 10);
_cursor += 6;
@@ -323,10 +323,10 @@ bool Nibbler::getDigit4 (int& result)
if (i < _length &&
_length - i >= 4)
{
if (isdigit (_input[i + 0]) &&
isdigit (_input[i + 1]) &&
isdigit (_input[i + 2]) &&
isdigit (_input[i + 3]))
if (Lexer::isDigit (_input[i + 0]) &&
Lexer::isDigit (_input[i + 1]) &&
Lexer::isDigit (_input[i + 2]) &&
Lexer::isDigit (_input[i + 3]))
{
result = strtoimax (_input.substr (_cursor, 4).c_str (), NULL, 10);
_cursor += 4;
@@ -344,9 +344,9 @@ bool Nibbler::getDigit3 (int& result)
if (i < _length &&
_length - i >= 3)
{
if (isdigit (_input[i + 0]) &&
isdigit (_input[i + 1]) &&
isdigit (_input[i + 2]))
if (Lexer::isDigit (_input[i + 0]) &&
Lexer::isDigit (_input[i + 1]) &&
Lexer::isDigit (_input[i + 2]))
{
result = strtoimax (_input.substr (_cursor, 3).c_str (), NULL, 10);
_cursor += 3;
@@ -364,8 +364,8 @@ bool Nibbler::getDigit2 (int& result)
if (i < _length &&
_length - i >= 2)
{
if (isdigit (_input[i + 0]) &&
isdigit (_input[i + 1]))
if (Lexer::isDigit (_input[i + 0]) &&
Lexer::isDigit (_input[i + 1]))
{
result = strtoimax (_input.substr (_cursor, 2).c_str (), NULL, 10);
_cursor += 2;
@@ -390,7 +390,7 @@ bool Nibbler::getInt (int& result)
}
// TODO Potential for use of find_first_not_of
while (i < _length && isdigit (_input[i]))
while (i < _length && Lexer::isDigit (_input[i]))
++i;
if (i > _cursor)
@@ -403,39 +403,12 @@ bool Nibbler::getInt (int& result)
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool Nibbler::getHex (int& result)
{
std::string::size_type i = _cursor;
if (i < _length)
{
if (_input[i] == '-')
++i;
else if (_input[i] == '+')
++i;
}
// TODO Potential for use of find_first_not_of
while (i < _length && isxdigit (_input[i]))
++i;
if (i > _cursor)
{
result = strtoimax (_input.substr (_cursor, i - _cursor).c_str (), NULL, 16);
_cursor = i;
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool Nibbler::getUnsignedInt (int& result)
{
std::string::size_type i = _cursor;
// TODO Potential for use of find_first_not_of
while (i < _length && isdigit (_input[i]))
while (i < _length && Lexer::isDigit (_input[i]))
++i;
if (i > _cursor)
@@ -473,11 +446,11 @@ bool Nibbler::getNumber (std::string& result)
++i;
// digit+
if (i < _length && isdigit (_input[i]))
if (i < _length && Lexer::isDigit (_input[i]))
{
++i;
while (i < _length && isdigit (_input[i]))
while (i < _length && Lexer::isDigit (_input[i]))
++i;
// ( . digit+ )?
@@ -485,7 +458,7 @@ bool Nibbler::getNumber (std::string& result)
{
++i;
while (i < _length && isdigit (_input[i]))
while (i < _length && Lexer::isDigit (_input[i]))
++i;
}
@@ -497,11 +470,11 @@ bool Nibbler::getNumber (std::string& result)
if (i < _length && (_input[i] == '+' || _input[i] == '-'))
++i;
if (i < _length && isdigit (_input[i]))
if (i < _length && Lexer::isDigit (_input[i]))
{
++i;
while (i < _length && isdigit (_input[i]))
while (i < _length && Lexer::isDigit (_input[i]))
++i;
result = _input.substr (_cursor, i - _cursor);
@@ -555,11 +528,11 @@ bool Nibbler::getUnsignedNumber (double& result)
std::string::size_type i = _cursor;
// digit+
if (i < _length && isdigit (_input[i]))
if (i < _length && Lexer::isDigit (_input[i]))
{
++i;
while (i < _length && isdigit (_input[i]))
while (i < _length && Lexer::isDigit (_input[i]))
++i;
// ( . digit+ )?
@@ -567,7 +540,7 @@ bool Nibbler::getUnsignedNumber (double& result)
{
++i;
while (i < _length && isdigit (_input[i]))
while (i < _length && Lexer::isDigit (_input[i]))
++i;
}
@@ -579,11 +552,11 @@ bool Nibbler::getUnsignedNumber (double& result)
if (i < _length && (_input[i] == '+' || _input[i] == '-'))
++i;
if (i < _length && isdigit (_input[i]))
if (i < _length && Lexer::isDigit (_input[i]))
{
++i;
while (i < _length && isdigit (_input[i]))
while (i < _length && Lexer::isDigit (_input[i]))
++i;
result = strtof (_input.substr (_cursor, i - _cursor).c_str (), NULL);
@@ -737,21 +710,21 @@ bool Nibbler::getDateISO (time_t& t)
if (i < _length &&
_length - i >= 16)
{
if (isdigit (_input[i + 0]) &&
isdigit (_input[i + 1]) &&
isdigit (_input[i + 2]) &&
isdigit (_input[i + 3]) &&
isdigit (_input[i + 4]) &&
isdigit (_input[i + 5]) &&
isdigit (_input[i + 6]) &&
isdigit (_input[i + 7]) &&
if (Lexer::isDigit (_input[i + 0]) &&
Lexer::isDigit (_input[i + 1]) &&
Lexer::isDigit (_input[i + 2]) &&
Lexer::isDigit (_input[i + 3]) &&
Lexer::isDigit (_input[i + 4]) &&
Lexer::isDigit (_input[i + 5]) &&
Lexer::isDigit (_input[i + 6]) &&
Lexer::isDigit (_input[i + 7]) &&
_input[i + 8] == 'T' &&
isdigit (_input[i + 9]) &&
isdigit (_input[i + 10]) &&
isdigit (_input[i + 11]) &&
isdigit (_input[i + 12]) &&
isdigit (_input[i + 13]) &&
isdigit (_input[i + 14]) &&
Lexer::isDigit (_input[i + 9]) &&
Lexer::isDigit (_input[i + 10]) &&
Lexer::isDigit (_input[i + 11]) &&
Lexer::isDigit (_input[i + 12]) &&
Lexer::isDigit (_input[i + 13]) &&
Lexer::isDigit (_input[i + 14]) &&
_input[i + 15] == 'Z')
{
_cursor += 16;
@@ -817,7 +790,7 @@ bool Nibbler::parseDigits(std::string::size_type& i,
// Check that 'f' of them are digits
unsigned int g;
for (g = 0; g < f; g++)
if (! isdigit (_input[i + g]))
if (! Lexer::isDigit (_input[i + g]))
break;
// Parse the integer when it is the case
if (g == f)
@@ -908,9 +881,9 @@ bool Nibbler::getDate (const std::string& format, time_t& t)
case 'a':
case 'A':
if (i + 3 <= _length &&
! isdigit (_input[i + 0]) &&
! isdigit (_input[i + 1]) &&
! isdigit (_input[i + 2]))
! Lexer::isDigit (_input[i + 0]) &&
! Lexer::isDigit (_input[i + 1]) &&
! Lexer::isDigit (_input[i + 2]))
{
wday = Date::dayOfWeek (_input.substr (i, 3).c_str ());
i += (format[f] == 'a') ? 3 : Date::dayName (wday).size ();
@@ -922,9 +895,9 @@ bool Nibbler::getDate (const std::string& format, time_t& t)
case 'b':
case 'B':
if (i + 3 <= _length &&
! isdigit (_input[i + 0]) &&
! isdigit (_input[i + 1]) &&
! isdigit (_input[i + 2]))
! Lexer::isDigit (_input[i + 0]) &&
! Lexer::isDigit (_input[i + 1]) &&
! Lexer::isDigit (_input[i + 2]))
{
if (month != -1)
return false;
@@ -1030,14 +1003,14 @@ bool Nibbler::getName (std::string& result)
if (i < _length)
{
if (! isdigit (_input[i]) &&
if (! Lexer::isDigit (_input[i]) &&
! ispunct (_input[i]) &&
! Lexer::is_ws (_input[i]))
! Lexer::isWhitespace (_input[i]))
{
++i;
while (i < _length &&
(_input[i] == '_' || ! ispunct (_input[i])) &&
! Lexer::is_ws (_input[i]))
! Lexer::isWhitespace (_input[i]))
{
++i;
}
@@ -1062,9 +1035,9 @@ bool Nibbler::getWord (std::string& result)
if (i < _length)
{
while (!isdigit (_input[i]) &&
!isPunctuation (_input[i]) &&
!Lexer::is_ws (_input[i]))
while (!Lexer::isDigit (_input[i]) &&
!Lexer::isPunctuation (_input[i]) &&
!Lexer::isWhitespace (_input[i]))
{
++i;
}
@@ -1254,21 +1227,6 @@ bool Nibbler::depleted ()
return false;
}
////////////////////////////////////////////////////////////////////////////////
// Override of ispunct, that considers #, $, _ and @ not to be punctuation.
//
// ispunct: ! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~
// Punctuation: ! " % & ' ( ) * + , - . / : ; < = > ? [ \ ] ^ _ ` { | } ~
// delta: # $ @
//
bool Nibbler::isPunctuation (char c)
{
if (c == '@' || c == '#' || c == '$' || c == '_')
return false;
return ispunct (c);
}
////////////////////////////////////////////////////////////////////////////////
std::string Nibbler::dump ()
{

View File

@@ -68,7 +68,6 @@ public:
bool getDigit3 (int&);
bool getDigit2 (int&);
bool getInt (int&);
bool getHex (int&);
bool getUnsignedInt (int&);
bool getNumber (std::string&);
bool getNumber (double&);
@@ -111,7 +110,6 @@ public:
bool depleted ();
static bool isPunctuation (char);
std::string dump ();
private:

View File

@@ -102,7 +102,7 @@ bool TF2::get (int id, Task& task)
// pending.data file, the task in question cannot appear earlier than line
// (id - 1) in the file. It can, however, appear significantly later because
// it is not known how recent a GC operation was run.
for (int i = id - 1; i < _tasks.size (); ++i)
for (unsigned int i = id - 1; i < _tasks.size (); ++i)
{
if (_tasks[i].id == id)
{
@@ -316,9 +316,15 @@ void TF2::load_tasks ()
++line_number;
Task task (*i);
// Some tasks gets an ID.
// Some tasks get an ID.
if (_has_ids)
task.id = context.tdb2.next_id ();
{
Task::status status = task.getStatus ();
// Completed / deleted tasks in pending.data get an ID if GC is off.
if (!context.run_gc ||
(status != Task::completed && status != Task::deleted))
task.id = context.tdb2.next_id ();
}
_tasks.push_back (task);
@@ -462,9 +468,13 @@ void TF2::dependency_scan ()
{
if (right->get ("uuid") == *d)
{
Task::status status = right->getStatus ();
if (status != Task::completed &&
status != Task::deleted)
// GC hasn't run yet, check both tasks for their current status
Task::status lstatus = left->getStatus ();
Task::status rstatus = right->getStatus ();
if (lstatus != Task::completed &&
lstatus != Task::deleted &&
rstatus != Task::completed &&
rstatus != Task::deleted)
{
left->is_blocked = true;
right->is_blocking = true;
@@ -559,7 +569,9 @@ void TDB2::set_location (const std::string& location)
void TDB2::add (Task& task, bool add_to_backlog /* = true */)
{
// Ensure the task is consistent, and provide defaults if necessary.
task.validate ();
// bool argument to validate() is "applyDefault". Pass add_to_backlog through
// in order to not apply defaults to synchronized tasks.
task.validate (add_to_backlog);
std::string uuid = task.get ("uuid");
// If the tasks are loaded, then verify that this uuid is not already in
@@ -567,32 +579,30 @@ void TDB2::add (Task& task, bool add_to_backlog /* = true */)
if (!verifyUniqueUUID (uuid))
throw format (STRING_TDB2_UUID_NOT_UNIQUE, uuid);
// Create a vector tasks, as hooks can cause them to multiply.
std::vector <Task> changes;
changes.push_back (task);
context.hooks.onAdd (changes);
// Only locally-added tasks trigger hooks. This means that tasks introduced
// via 'sync' do not trigger hooks.
if (add_to_backlog)
context.hooks.onAdd (task);
update (uuid, task, add_to_backlog, changes);
update (uuid, task, add_to_backlog, true);
}
////////////////////////////////////////////////////////////////////////////////
void TDB2::modify (Task& task, bool add_to_backlog /* = true */)
{
// Ensure the task is consistent, and provide defaults if necessary.
// Ensure the task is consistent.
task.validate (false);
task.upgradeLegacyValues ();
std::string uuid = task.get ("uuid");
// Get the unmodified task as reference, so the hook can compare.
Task original;
get (uuid, original);
if (add_to_backlog)
{
Task original;
get (uuid, original);
context.hooks.onModify (original, task);
}
// Create a vector tasks, as hooks can cause them to multiply.
std::vector <Task> changes;
changes.push_back (task);
context.hooks.onModify (original, changes);
update (uuid, task, add_to_backlog, changes);
update (uuid, task, add_to_backlog);
}
////////////////////////////////////////////////////////////////////////////////
@@ -600,61 +610,57 @@ void TDB2::update (
const std::string& uuid,
Task& task,
const bool add_to_backlog,
std::vector <Task>& changes)
const bool addition /* = false */)
{
std::vector <Task>::iterator i;
for (i = changes.begin (); i != changes.end (); ++i)
// Validate to add metadata.
task.validate (false);
// If the task already exists, it is a modification, else addition.
Task original;
if (not addition && get (task.get ("uuid"), original))
{
// Validate to add metadata.
i->validate (false);
// If the task already exists, it is a modification, else addition.
Task original;
if (get (i->get ("uuid"), original))
{
// Update the task, wherever it is.
if (!pending.modify_task (*i))
completed.modify_task (*i);
// time <time>
// old <task>
// new <task>
// ---
undo.add_line ("time " + Date ().toEpochString () + "\n");
undo.add_line ("old " + original.composeF4 () + "\n");
undo.add_line ("new " + i->composeF4 () + "\n");
undo.add_line ("---\n");
}
else
{
// Re-validate to add default values.
i->validate ();
// Add new task to either pending or completed.
std::string status = i->get ("status");
if (status == "completed" ||
status == "deleted")
completed.add_task (*i);
else
pending.add_task (*i);
// Add undo data lines:
// time <time>
// new <task>
// ---
undo.add_line ("time " + Date ().toEpochString () + "\n");
undo.add_line ("new " + i->composeF4 () + "\n");
undo.add_line ("---\n");
}
// Add task to backlog.
if (add_to_backlog)
backlog.add_line (i->composeJSON () + "\n");
{
// All locally modified tasks are timestamped, implicitly overwriting any
// changes the user or hooks tried to apply to the "modified" attribute.
task.setAsNow ("modified");
}
// The original task may be further referenced by the caller.
if (i->get ("uuid") == uuid)
task = *i;
// Update the task, wherever it is.
if (!pending.modify_task (task))
completed.modify_task (task);
// time <time>
// old <task>
// new <task>
// ---
undo.add_line ("time " + Date ().toEpochString () + "\n");
undo.add_line ("old " + original.composeF4 () + "\n");
undo.add_line ("new " + task.composeF4 () + "\n");
undo.add_line ("---\n");
}
else
{
// Add new task to either pending or completed.
std::string status = task.get ("status");
if (status == "completed" ||
status == "deleted")
completed.add_task (task);
else
pending.add_task (task);
// Add undo data lines:
// time <time>
// new <task>
// ---
undo.add_line ("time " + Date ().toEpochString () + "\n");
undo.add_line ("new " + task.composeF4 () + "\n");
undo.add_line ("---\n");
}
// Add task to backlog.
if (add_to_backlog)
backlog.add_line (task.composeJSON () + "\n");
}
////////////////////////////////////////////////////////////////////////////////
@@ -979,6 +985,9 @@ void TDB2::show_diff (
view.add (Column::factory ("string", STRING_TDB2_UNDO_PRIOR));
view.add (Column::factory ("string", STRING_TDB2_UNDO_CURRENT));
Color label (context.config.get ("color.label"));
view.colorHeader (label);
Task after (current);
if (prior != "")
@@ -1189,18 +1198,21 @@ int TDB2::gc ()
// Allowed as an override, but not recommended.
if (context.config.getBoolean ("gc"))
{
std::vector <Task> pending_tasks = pending.get_tasks ();
std::vector <Task> pending_tasks = pending.get_tasks ();
// TODO Thread.
std::vector <Task> completed_tasks = completed.get_tasks ();
// TODO Assume pending < completed, therefore there is room here to process
// data before joining with the completed.data thread.
bool pending_changes = false;
bool completed_changes = false;
std::vector <Task> pending_tasks_after;
std::vector <Task> completed_tasks_after;
// Reduce unnecessary allocation/copies.
pending_tasks_after.reserve (pending_tasks.size ());
completed_tasks_after.reserve (completed_tasks.size ());
// Scan all pending tasks, looking for any that need to be relocated to
// completed, or need to be 'woken'.
@@ -1237,6 +1249,11 @@ int TDB2::gc ()
}
}
// TODO Join completed.data thread.
// Reduce unnecessary allocation/copies.
completed_tasks_after.reserve (completed_tasks.size ());
// Scan all completed tasks, looking for any that need to be relocated to
// pending.
for (task = completed_tasks.begin ();

View File

@@ -128,7 +128,7 @@ public:
private:
void gather_changes ();
void update (const std::string&, Task&, const bool, std::vector <Task>&);
void update (const std::string&, Task&, const bool, const bool addition = false);
bool verifyUniqueUUID (const std::string&);
void show_diff (const std::string&, const std::string&, const std::string&);
void revert_undo (std::vector <std::string>&, std::string&, std::string&, std::string&, std::string&);

View File

@@ -65,14 +65,12 @@ static const float epsilon = 0.000001;
#endif
std::string Task::defaultProject = "";
std::string Task::defaultPriority = "";
std::string Task::defaultDue = "";
bool Task::searchCaseSensitive = true;
bool Task::regex = false;
std::map <std::string, std::string> Task::attributes;
std::map <std::string, float> Task::coefficients;
float Task::urgencyPriorityCoefficient = 0.0;
float Task::urgencyProjectCoefficient = 0.0;
float Task::urgencyActiveCoefficient = 0.0;
float Task::urgencyScheduledCoefficient = 0.0;
@@ -87,6 +85,8 @@ float Task::urgencyBlockingCoefficient = 0.0;
float Task::urgencyAgeCoefficient = 0.0;
float Task::urgencyAgeMax = 0.0;
std::map <std::string, std::vector <std::string>> Task::customOrder;
static const std::string dummy ("");
////////////////////////////////////////////////////////////////////////////////
@@ -565,7 +565,6 @@ void Task::parse (const std::string& input)
nl.getQuoted ('"', value))
{
legacyAttributeMap (name);
legacyValueMap (name, value);
if (name.substr (0, 11) == "annotation_")
++annotation_count;
@@ -586,8 +585,6 @@ void Task::parse (const std::string& input)
parseJSON (copy);
else
throw std::string (STRING_RECORD_NOT_FF4);
upgradeLegacyValues ();
}
catch (const std::string&)
@@ -636,8 +633,9 @@ void Task::parseJSON (const std::string& line)
// Dates are converted from ISO to epoch.
else if (type == "date")
{
Date d (unquoteText (i->second->dump ()));
set (i->first, d.toEpochString ());
std::string text = unquoteText (i->second->dump ());
Date d (text);
set (i->first, text == "" ? "" : d.toEpochString ());
}
// Tags are an array of JSON strings.
@@ -718,8 +716,6 @@ void Task::parseJSON (const std::string& line)
}
}
}
upgradeLegacyValues ();
}
}
@@ -804,6 +800,10 @@ std::string Task::composeJSON (bool decorate /*= false*/) const
if (i->first.substr (0, 11) == "annotation_")
continue;
// If value is an empty string, do not ever output it
if (i->second == "")
continue;
if (attributes_written)
out << ",";
@@ -815,16 +815,22 @@ std::string Task::composeJSON (bool decorate /*= false*/) const
if (type == "date")
{
Date d (i->second);
if (i->first == "modification")
out << "\"modified\":\""
<< d.toISO ()
<< "\"";
else
out << "\""
<< i->first
<< "\":\""
<< d.toISO ()
<< "\"";
out << "\""
<< (i->first == "modification" ? "modified" : i->first)
<< "\":\""
// Date was deleted, do not export parsed empty string
<< (i->second == "" ? "" : d.toISO ())
<< "\"";
++attributes_written;
}
else if (type == "numeric")
{
out << "\""
<< i->first
<< "\":"
<< i->second;
++attributes_written;
}
@@ -847,6 +853,8 @@ std::string Task::composeJSON (bool decorate /*= false*/) const
}
out << "]";
++attributes_written;
}
// Everything else is a quoted value.
@@ -894,9 +902,8 @@ std::string Task::composeJSON (bool decorate /*= false*/) const
// Include urgency.
if (decorate)
out << ","
<< "\"urgency\":\""
<< urgency_c ()
<<"\"";
<< "\"urgency\":"
<< urgency_c ();
#endif
out << "}";
@@ -1093,6 +1100,7 @@ int Task::getTagCount () const
bool Task::hasTag (const std::string& tag) const
{
// Synthetic tags - dynamically generated, but do not occupy storage space.
// Note: This list must match that in CmdInfo::execute.
if (tag == "BLOCKED") return is_blocked;
if (tag == "UNBLOCKED") return !is_blocked;
if (tag == "BLOCKING") return is_blocking;
@@ -1112,10 +1120,10 @@ bool Task::hasTag (const std::string& tag) const
if (tag == "SCHEDULED") return has ("scheduled");
if (tag == "CHILD") return has ("parent");
if (tag == "UNTIL") return has ("until");
if (tag == "WAITING") return has ("wait");
if (tag == "ANNOTATED") return hasAnnotations ();
if (tag == "TAGGED") return has ("tags");
if (tag == "PARENT") return has ("mask");
if (tag == "WAITING") return get ("status") == "waiting";
if (tag == "PENDING") return get ("status") == "pending";
if (tag == "COMPLETED") return get ("status") == "completed";
if (tag == "DELETED") return get ("status") == "deleted";
@@ -1376,7 +1384,7 @@ void Task::validate (bool applyDefault /* = true */)
if (!has ("modified") || get ("modified") == "")
setAsNow ("modified");
if (applyDefault)
if (applyDefault && (! has ("parent") || get ("parent") == ""))
{
// Override with default.project, if not specified.
if (Task::defaultProject != "" &&
@@ -1386,14 +1394,6 @@ void Task::validate (bool applyDefault /* = true */)
set ("project", Task::defaultProject);
}
// Override with default.priority, if not specified.
if (Task::defaultPriority != "" &&
! has ("priority"))
{
if (context.columns["priority"]->validate (Task::defaultPriority))
set ("priority", Task::defaultPriority);
}
// Override with default.due, if not specified.
if (Task::defaultDue != "" &&
! has ("due"))
@@ -1479,17 +1479,6 @@ void Task::validate (bool applyDefault /* = true */)
throw std::string (format (STRING_TASK_VALID_RECUR, value));
}
}
// Priorities must be valid.
if (has ("priority"))
{
std::string priority = get ("priority");
if (priority != "H" &&
priority != "M" &&
priority != "L" &&
priority != "")
throw format (STRING_TASK_VALID_PRIORITY, priority);
}
}
////////////////////////////////////////////////////////////////////////////////
@@ -1647,7 +1636,6 @@ float Task::urgency_c () const
{
float value = 0.0;
#ifdef PRODUCT_TASKWARRIOR
value += fabsf (Task::urgencyPriorityCoefficient) > epsilon ? (urgency_priority () * Task::urgencyPriorityCoefficient) : 0.0;
value += fabsf (Task::urgencyProjectCoefficient) > epsilon ? (urgency_project () * Task::urgencyProjectCoefficient) : 0.0;
value += fabsf (Task::urgencyActiveCoefficient) > epsilon ? (urgency_active () * Task::urgencyActiveCoefficient) : 0.0;
value += fabsf (Task::urgencyScheduledCoefficient) > epsilon ? (urgency_scheduled () * Task::urgencyScheduledCoefficient) : 0.0;
@@ -1689,6 +1677,16 @@ float Task::urgency_c () const
if (hasTag (tag))
value += var->second;
}
// urgency.user.keyword.<keyword>.coefficient
if (var->first.substr (13, 8) == "keyword." &&
(end = var->first.find (".coefficient")) != std::string::npos)
{
std::string keyword = var->first.substr (21, end - 21);
if (get ("description").find (keyword) != std::string::npos)
value += var->second;
}
}
else if (var->first.substr (0, 12) == "urgency.uda.")
{
@@ -1734,18 +1732,6 @@ float Task::urgency ()
return urgency_value;
}
////////////////////////////////////////////////////////////////////////////////
float Task::urgency_priority () const
{
const std::string& value = get_ref ("priority");
if (value == "H") return 1.0;
else if (value == "M") return 0.65;
else if (value == "L") return 0.3;
return 0.0;
}
////////////////////////////////////////////////////////////////////////////////
float Task::urgency_inherit () const
{
@@ -1767,7 +1753,6 @@ float Task::urgency_inherit () const
v += it->urgency_annotations ();
v += it->urgency_due ();
v += it->urgency_next ();
v += it->urgency_priority ();
v += it->urgency_scheduled ();
v += it->urgency_waiting ();
@@ -1948,17 +1933,13 @@ void Task::modify (modType type, bool text_required /* = false */)
}
else
{
// Some columns are not modifiable.
if (name == "uuid" ||
name == "id" ||
name == "mask" ||
name == "imask" ||
name == "parent")
throw format (STRING_INVALID_MOD, name, value);
// Get the column info.
Column* column = context.columns[name];
// Some columns are not modifiable.
if (! column->modifiable ())
throw format (STRING_INVALID_MOD, name, value);
// Dependencies are specified as IDs.
if (name == "depends")
{
@@ -2068,9 +2049,6 @@ void Task::modify (modType type, bool text_required /* = false */)
if (v.type () == Variant::type_string)
throw format (STRING_UDA_NUMERIC, v.get_string ());
v.cast (Variant::type_real);
v.cast (Variant::type_string);
set (name, v);
++modCount;
}
@@ -2209,78 +2187,3 @@ void Task::modify (modType type, bool text_required /* = false */)
}
////////////////////////////////////////////////////////////////////////////////
void Task::upgradeLegacyValues ()
{
// 2.4.0 Update recurrence values.
if (has ("recur"))
{
std::string value = get ("recur");
if (value != "")
{
std::string new_value = value;
upgradeLegacyValue (new_value);
if (new_value != value)
{
set ("recur", new_value);
context.debug (format ("Legacy upgrade: recur {1} --> {2}", value, new_value));
}
}
}
// 2.4.0 Update UDA duration values.
Config::const_iterator name;
for (name = context.config.begin (); name != context.config.end (); ++name)
{
if (name->first.substr (0, 4) == "uda." &&
name->first.find (".type") != std::string::npos)
{
if (name->second == "duration")
{
std::string::size_type period = name->first.find ('.', 4);
if (period != std::string::npos)
{
std::string uda = name->first.substr (4, period - 4);
std::string value = get (uda);
std::string new_value = value;
upgradeLegacyValue (new_value);
if (new_value != value)
{
set ("recur", new_value);
context.debug (format ("Legacy upgrade: UDA {1}, {2} --> {3}", uda, value, new_value));
}
}
}
}
}
}
////////////////////////////////////////////////////////////////////////////////
void Task::upgradeLegacyValue (std::string& value)
{
std::string::size_type len = value.length ();
std::string::size_type p;
if (value == "-") value = "0s";
else if ((p = value.find ("hr")) != std::string::npos && p == len - 2) value = value.substr (0, p) + "h";
else if ((p = value.find ("hrs")) != std::string::npos && p == len - 3) value = value.substr (0, p) + "h";
else if ((p = value.find ("mins")) != std::string::npos && p == len - 4) value = value.substr (0, p) + "min";
else if ((p = value.find ("mnths")) != std::string::npos && p == len - 5) value = value.substr (0, p) + "mo";
else if ((p = value.find ("mos")) != std::string::npos && p == len - 3) value = value.substr (0, p) + "mo";
else if ((p = value.find ("mth")) != std::string::npos && p == len - 3) value = value.substr (0, p) + "mo";
else if ((p = value.find ("mths")) != std::string::npos && p == len - 4) value = value.substr (0, p) + "mo";
else if ((p = value.find ("qrtrs")) != std::string::npos && p == len - 5) value = value.substr (0, p) + "q";
else if ((p = value.find ("qtr")) != std::string::npos && p == len - 3) value = value.substr (0, p) + "q";
else if ((p = value.find ("qtrs")) != std::string::npos && p == len - 4) value = value.substr (0, p) + "q";
else if ((p = value.find ("sec")) != std::string::npos && p == len - 3) value = value.substr (0, p) + "s";
else if ((p = value.find ("secs")) != std::string::npos && p == len - 4) value = value.substr (0, p) + "s";
else if ((p = value.find ("wk")) != std::string::npos && p == len - 2) value = value.substr (0, p) + "w";
else if ((p = value.find ("wks")) != std::string::npos && p == len - 3) value = value.substr (0, p) + "w";
else if ((p = value.find ("yr")) != std::string::npos && p == len - 2) value = value.substr (0, p) + "y";
else if ((p = value.find ("yrs")) != std::string::npos && p == len - 3) value = value.substr (0, p) + "y";
// It is not an error to have a non-legacy value.
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -37,13 +37,12 @@ class Task : public std::map <std::string, std::string>
{
public:
static std::string defaultProject;
static std::string defaultPriority;
static std::string defaultDue;
static bool searchCaseSensitive;
static bool regex;
static std::map <std::string, std::string> attributes; // name -> type
static std::map <std::string, float> coefficients;
static float urgencyPriorityCoefficient;
static std::map <std::string, std::vector <std::string>> customOrder;
static float urgencyProjectCoefficient;
static float urgencyActiveCoefficient;
static float urgencyScheduledCoefficient;
@@ -154,7 +153,6 @@ public:
enum modType {modReplace, modPrepend, modAppend, modAnnotate};
void modify (modType, bool text_required = false);
void upgradeLegacyValues ();
private:
int determineVersion (const std::string&);
@@ -163,10 +161,8 @@ private:
void validate_before (const std::string&, const std::string&);
const std::string encode (const std::string&) const;
const std::string decode (const std::string&) const;
void upgradeLegacyValue (std::string&);
public:
float urgency_priority () const;
float urgency_project () const;
float urgency_active () const;
float urgency_scheduled () const;

View File

@@ -26,6 +26,7 @@
#include <cmake.h>
#include <sstream>
#include <algorithm>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
@@ -41,6 +42,7 @@
std::string Variant::dateFormat = "";
bool Variant::searchCaseSensitive = true;
bool Variant::searchUsingRegex = true;
bool Variant::isoEnabled = true;
////////////////////////////////////////////////////////////////////////////////
Variant::Variant ()
@@ -134,7 +136,7 @@ Variant::Variant (const char* value)
}
////////////////////////////////////////////////////////////////////////////////
Variant::Variant (const time_t value, const enum type new_type /*=type_date*/)
Variant::Variant (const time_t value, const enum type new_type)
: _type (new_type)
, _bool (false)
, _integer (0)
@@ -165,7 +167,7 @@ void Variant::source (const std::string& input)
}
////////////////////////////////////////////////////////////////////////////////
std::string Variant::source () const
const std::string& Variant::source () const
{
return _source;
}
@@ -313,19 +315,25 @@ bool Variant::operator< (const Variant& other) const
return left._string < right._string;
case type_string:
if (left.source () == "priority" || right.source () == "priority")
{
if (left._string != "H" && right._string == "H") return true;
else if (left._string == "L" && right._string == "M") return true;
else if (left._string == "" && right._string != "") return true;
else return false;
}
else
{
if (left.trivial () || right.trivial ())
if (left._string == right._string)
return false;
return left._string < right._string;
auto order = Task::customOrder.find (left.source ());
if (order != Task::customOrder.end ())
{
// Guaranteed to be found, because of ColUDA::validate ().
auto posLeft = std::find (order->second.begin (), order->second.end (), left._string);
auto posRight = std::find (order->second.begin (), order->second.end (), right._string);
return posLeft < posRight;
}
else
{
if (left.trivial () || right.trivial ())
return false;
return left._string < right._string;
}
}
case type_date:
@@ -458,20 +466,25 @@ bool Variant::operator<= (const Variant& other) const
return left._string <= right._string;
case type_string:
if (left.source () == "priority" || right.source () == "priority")
{
if (left._string == right._string ) return true;
else if ( right._string == "H") return true;
else if (left._string == "L" && right._string == "M") return true;
else if (left._string == "" ) return true;
else return false;
}
else
{
if (left.trivial () || right.trivial ())
return false;
if (left._string == right._string)
return true;
return left._string <= right._string;
auto order = Task::customOrder.find (left.source ());
if (order != Task::customOrder.end ())
{
// Guaranteed to be found, because of ColUDA::validate ().
auto posLeft = std::find (order->second.begin (), order->second.end (), left._string);
auto posRight = std::find (order->second.begin (), order->second.end (), right._string);
return posLeft <= posRight;
}
else
{
if (left.trivial () || right.trivial ())
return false;
return left._string <= right._string;
}
}
case type_date:
@@ -604,20 +617,27 @@ bool Variant::operator> (const Variant& other) const
return left._string > right._string;
case type_string:
if (left.source () == "priority" || right.source () == "priority")
{
if (left._string == "H" && right._string != "H") return true;
else if (left._string == "M" && right._string == "L") return true;
else if (left._string != "" && right._string == "") return true;
else return false;
}
else
{
if (left.trivial () || right.trivial ())
if (left._string == right._string)
return false;
return left._string> right._string;
auto order = Task::customOrder.find (left.source ());
if (order != Task::customOrder.end ())
{
// Guaranteed to be found, because of ColUDA::validate ().
auto posLeft = std::find (order->second.begin (), order->second.end (), left._string);
auto posRight = std::find (order->second.begin (), order->second.end (), right._string);
return posLeft > posRight;
}
else
{
if (left.trivial () || right.trivial ())
return false;
return left._string > right._string;
}
}
case type_date:
if (left.trivial () || right.trivial ())
return false;
@@ -748,20 +768,25 @@ bool Variant::operator>= (const Variant& other) const
return left._string >= right._string;
case type_string:
if (left.source () == "priority" || right.source () == "priority")
{
if (left._string == right._string ) return true;
else if (left._string == "H" ) return true;
else if (left._string == "M" && right._string == "L") return true;
else if ( right._string == "" ) return true;
else return false;
}
else
{
if (left.trivial () || right.trivial ())
return false;
if (left._string == right._string)
return true;
return left._string >= right._string;
auto order = Task::customOrder.find (left.source ());
if (order != Task::customOrder.end ())
{
// Guaranteed to be found, because of ColUDA::validate ().
auto posLeft = std::find (order->second.begin (), order->second.end (), left._string);
auto posRight = std::find (order->second.begin (), order->second.end (), right._string);
return posLeft >= posRight;
}
else
{
if (left.trivial () || right.trivial ())
return false;
return left._string >= right._string;
}
}
case type_date:
@@ -1963,6 +1988,8 @@ Variant::operator std::string () const
case type_unknown:
throw std::string (STRING_VARIANT_RENDER_UNK);
}
return "";
}
////////////////////////////////////////////////////////////////////////////////
@@ -2065,7 +2092,8 @@ void Variant::cast (const enum type new_type)
ISO8601d iso;
std::string::size_type pos = 0;
if (iso.parse (_string, pos) &&
if (isoEnabled &&
iso.parse (_string, pos) &&
pos == _string.length ())
{
_date = (time_t) iso;
@@ -2170,7 +2198,7 @@ double Variant::get_real () const
}
////////////////////////////////////////////////////////////////////////////////
std::string Variant::get_string () const
const std::string& Variant::get_string () const
{
return _string;
}

View File

@@ -27,6 +27,7 @@
#ifndef INCLUDED_VARIANT
#define INCLUDED_VARIANT
#include <map>
#include <string>
#include <time.h>
#include <Task.h>
@@ -37,6 +38,7 @@ public:
static std::string dateFormat;
static bool searchCaseSensitive;
static bool searchUsingRegex;
static bool isoEnabled;
enum type {type_unknown, type_boolean, type_integer, type_real, type_string,
type_date, type_duration};
@@ -48,11 +50,11 @@ public:
Variant (const double);
Variant (const std::string&);
Variant (const char*);
Variant (const time_t, const enum type new_type = type_date);
Variant (const time_t, const enum type);
~Variant ();
void source (const std::string&);
std::string source () const;
const std::string& source () const;
Variant& operator= (const Variant&);
@@ -98,12 +100,12 @@ public:
int type ();
bool trivial () const;
bool get_bool () const;
int get_integer () const;
double get_real () const;
std::string get_string () const;
time_t get_date () const;
time_t get_duration () const;
bool get_bool () const;
int get_integer () const;
double get_real () const;
const std::string& get_string () const;
time_t get_date () const;
time_t get_duration () const;
private:
enum type _type;

View File

@@ -143,6 +143,11 @@ std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& seque
if (min > global_min) global_min = min;
if (ideal > global_ideal) global_ideal = ideal;
// If a fixed-width column was just measured, there is no point repeating
// the measurement for all tasks.
if (_columns[i]->is_fixed_width ())
break;
}
if (print_empty_columns || global_min != 0)
@@ -237,7 +242,7 @@ std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& seque
// Compose column headers.
unsigned int max_lines = 0;
std::vector <std::vector <std::string> > headers;
std::vector <std::vector <std::string>> headers;
for (unsigned int c = 0; c < _columns.size (); ++c)
{
headers.push_back (std::vector <std::string> ());
@@ -290,7 +295,7 @@ std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& seque
// Compose, render columns, in sequence.
_rows = 0;
std::vector <std::vector <std::string> > cells;
std::vector <std::vector <std::string>> cells;
std::vector <int>::iterator s;
for (unsigned int s = 0; s < sequence.size (); ++s)
{

View File

@@ -132,6 +132,11 @@ std::string ViewText::render ()
if (min > global_min) global_min = min;
if (ideal > global_ideal) global_ideal = ideal;
// If a fixed-width column was just measured, there is no point repeating
// the measurement for all tasks.
if (_columns[col]->is_fixed_width ())
break;
}
minimal.push_back (global_min);
@@ -181,7 +186,7 @@ std::string ViewText::render ()
// Compose column headers.
unsigned int max_lines = 0;
std::vector <std::vector <std::string> > headers;
std::vector <std::vector <std::string>> headers;
for (unsigned int c = 0; c < _columns.size (); ++c)
{
headers.push_back (std::vector <std::string> ());
@@ -233,7 +238,7 @@ std::string ViewText::render ()
// Compose, render columns, in sequence.
_rows = 0;
std::vector <std::vector <std::string> > cells;
std::vector <std::vector <std::string>> cells;
for (unsigned int row = 0; row < _data.size (); ++row)
{
max_lines = 0;

View File

@@ -68,24 +68,24 @@ public:
std::string render ();
private:
std::vector <std::vector <std::string> > _data;
std::vector <std::vector <Color> > _color;
std::vector <Column*> _columns;
int _width;
int _left_margin;
Color _header;
Color _odd;
Color _even;
int _intra_padding;
Color _intra_odd;
Color _intra_even;
int _extra_padding;
Color _extra_odd;
Color _extra_even;
int _truncate_lines;
int _truncate_rows;
int _lines;
int _rows;
std::vector <std::vector <std::string>> _data;
std::vector <std::vector <Color>> _color;
std::vector <Column*> _columns;
int _width;
int _left_margin;
Color _header;
Color _odd;
Color _even;
int _intra_padding;
Color _intra_odd;
Color _intra_even;
int _extra_padding;
Color _extra_odd;
Color _extra_even;
int _truncate_lines;
int _truncate_rows;
int _lines;
int _rows;
};
#endif

View File

@@ -135,7 +135,6 @@ int main (int argc, const char** argv)
cli.entity ("attribute", "mask");
cli.entity ("attribute", "modified");
cli.entity ("attribute", "parent");
cli.entity ("attribute", "priority");
cli.entity ("attribute", "project");
cli.entity ("attribute", "recur");
cli.entity ("attribute", "scheduled");

View File

@@ -57,7 +57,7 @@ int main (int argc, char** argv)
try
{
bool infix = true;
bool ambiguous = true;
bool ambiguous = false;
// Add a source for constants.
Eval e;
@@ -77,8 +77,8 @@ int main (int argc, char** argv)
<< " -d|--debug Debug mode\n"
<< " -i|--infix Infix expression (default)\n"
<< " -p|--postfix Postfix expression\n"
<< " -a|--ambiguous Choose dates over numbers when ambiguous (default)\n"
<< " -n|--noambiguous Choose numbers over dates when ambiguous\n"
<< " -a|--ambiguous Choose dates over numbers when ambiguous\n"
<< " -n|--noambiguous Choose numbers over dates when ambiguous (default)\n"
<< "\n";
exit (1);
}

View File

@@ -17,7 +17,6 @@ set (columns_SRCS Column.cpp Column.h
ColMask.cpp ColMask.h
ColModified.cpp ColModified.h
ColParent.cpp ColParent.h
ColPriority.cpp ColPriority.h
ColProject.cpp ColProject.h
ColRecur.cpp ColRecur.h
ColScheduled.cpp ColScheduled.h

View File

@@ -207,8 +207,6 @@ void ColumnDate::render (
color.colorize (
rightJustify (
Duration (date - now).format (), width)));
else
lines.push_back (rightJustify ("", width));
}
}
}

View File

@@ -37,7 +37,7 @@ class ColumnDate : public Column
{
public:
ColumnDate ();
~ColumnDate ();
virtual ~ColumnDate ();
virtual bool validate (std::string&);
virtual void measure (Task&, unsigned int&, unsigned int&);

View File

@@ -83,8 +83,15 @@ void ColumnDepends::measure (Task& task, unsigned int& minimum, unsigned int& ma
std::vector <Task> blocking;
dependencyGetBlocking (task, blocking);
if (_style == "indicator") minimum = maximum = utf8_width (context.config.get ("dependency.indicator"));
else if (_style == "count") minimum = maximum = 2 + format ((int) blocking.size ()).length ();
if (_style == "indicator")
{
minimum = maximum = utf8_width (context.config.get ("dependency.indicator"));
_fixed_width = true;
}
else if (_style == "count")
{
minimum = maximum = 2 + format ((int) blocking.size ()).length ();
}
else if (_style == "default" ||
_style == "list")
{

View File

@@ -62,11 +62,16 @@ bool ColumnIMask::validate (std::string& value)
// Set the minimum and maximum widths for the value.
void ColumnIMask::measure (Task& task, unsigned int& minimum, unsigned int& maximum)
{
minimum = maximum = task.get ("imask").length ();
minimum = maximum = 0;
if (_style != "default" &&
_style != "number")
throw format (STRING_COLUMN_BAD_FORMAT, _name, _style);
if (task.has (_name))
{
minimum = maximum = task.get ("imask").length ();
if (_style != "default" &&
_style != "number")
throw format (STRING_COLUMN_BAD_FORMAT, _name, _style);
}
}
////////////////////////////////////////////////////////////////////////////////
@@ -76,7 +81,8 @@ void ColumnIMask::render (
int width,
Color& color)
{
lines.push_back (color.colorize (rightJustify (task.get ("imask"), width)));
if (task.has (_name))
lines.push_back (color.colorize (rightJustify (task.get ("imask"), width)));
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -62,10 +62,14 @@ bool ColumnMask::validate (std::string& value)
// Set the minimum and maximum widths for the value.
void ColumnMask::measure (Task& task, unsigned int& minimum, unsigned int& maximum)
{
minimum = maximum = task.get ("mask").length ();
minimum = maximum = 0;
if (task.has (_name))
{
minimum = maximum = task.get ("mask").length ();
if (_style != "default")
throw format (STRING_COLUMN_BAD_FORMAT, _name, _style);
if (_style != "default")
throw format (STRING_COLUMN_BAD_FORMAT, _name, _style);
}
}
////////////////////////////////////////////////////////////////////////////////
@@ -75,7 +79,8 @@ void ColumnMask::render (
int width,
Color& color)
{
lines.push_back (color.colorize (task.get ("mask")));
if (task.has (_name))
lines.push_back (color.colorize (task.get ("mask")));
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -62,12 +62,17 @@ bool ColumnParent::validate (std::string& value)
////////////////////////////////////////////////////////////////////////////////
// Set the minimum and maximum widths for the value.
void ColumnParent::measure (Task&, unsigned int& minimum, unsigned int& maximum)
void ColumnParent::measure (Task& task, unsigned int& minimum, unsigned int& maximum)
{
if (_style == "default" || _style == "long") minimum = maximum = 36;
else if (_style == "short") minimum = maximum = 8;
else
throw format (STRING_COLUMN_BAD_FORMAT, _name, _style);
minimum = maximum = 0;
if (task.has (_name))
{
if (_style == "default" || _style == "long") minimum = maximum = 36;
else if (_style == "short") minimum = maximum = 8;
else
throw format (STRING_COLUMN_BAD_FORMAT, _name, _style);
}
}
////////////////////////////////////////////////////////////////////////////////
@@ -77,20 +82,23 @@ void ColumnParent::render (
int width,
Color& color)
{
// f30cb9c3-3fc0-483f-bfb2-3bf134f00694 default
// 34f00694 short
if (_style == "default" ||
_style == "long")
if (task.has (_name))
{
lines.push_back (color.colorize (leftJustify (task.get (_name), width)));
}
// f30cb9c3-3fc0-483f-bfb2-3bf134f00694 default
// 34f00694 short
if (_style == "default" ||
_style == "long")
{
lines.push_back (color.colorize (leftJustify (task.get (_name), width)));
}
else if (_style == "short")
{
if (task.has (_name))
lines.push_back (color.colorize (leftJustify (task.get (_name).substr (28), width)));
else
lines.push_back (color.colorize (leftJustify ("", width)));
else if (_style == "short")
{
if (task.has (_name))
lines.push_back (color.colorize (leftJustify (task.get (_name).substr (28), width)));
else
lines.push_back (color.colorize (leftJustify ("", width)));
}
}
}

View File

@@ -1,133 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2015, Paul Beckingham, Federico Hernandez.
//
// 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.
//
// http://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
#include <cmake.h>
#include <Context.h>
#include <ColPriority.h>
#include <text.h>
#include <i18n.h>
extern Context context;
////////////////////////////////////////////////////////////////////////////////
ColumnPriority::ColumnPriority ()
{
_name = "priority";
_type = "string";
_style = "short";
_label = STRING_COLUMN_LABEL_PRI;
_styles.push_back ("short");
_styles.push_back ("long");
_examples.push_back ("H");
_examples.push_back ("High");
}
////////////////////////////////////////////////////////////////////////////////
ColumnPriority::~ColumnPriority ()
{
}
////////////////////////////////////////////////////////////////////////////////
bool ColumnPriority::validate (std::string& value)
{
value = upperCase (value);
if (value == "H" ||
value == "M" ||
value == "L" ||
value == "")
return true;
return false;
}
////////////////////////////////////////////////////////////////////////////////
// Overriden so that style <----> label are linked.
// Note that you can not determine which gets called first.
void ColumnPriority::setStyle (const std::string& value)
{
_style = value;
if (_style == "long" && _label == STRING_COLUMN_LABEL_PRI)
_label = STRING_COLUMN_LABEL_PRIORITY;
}
////////////////////////////////////////////////////////////////////////////////
// Set the minimum and maximum widths for the value.
void ColumnPriority::measure (Task& task, unsigned int& minimum, unsigned int& maximum)
{
std::string priority = task.get (_name);
if (priority == "")
minimum = maximum = 0;
else
minimum = maximum = 1;
if (_style == "long")
{
if (priority == "H") minimum = maximum = 4;
else if (priority == "M") minimum = maximum = 6;
else if (priority == "L") minimum = maximum = 3;
}
else if (_style != "default" &&
_style != "short")
throw format (STRING_COLUMN_BAD_FORMAT, "priority", _style);
}
////////////////////////////////////////////////////////////////////////////////
void ColumnPriority::render (
std::vector <std::string>& lines,
Task& task,
int width,
Color& color)
{
std::string priority = task.get (_name);
if (_style == "long")
{
if (priority == "H") priority = "High";
else if (priority == "M") priority = "Medium";
else if (priority == "L") priority = "Low";
}
lines.push_back (color.colorize (leftJustify (priority, width)));
}
////////////////////////////////////////////////////////////////////////////////
std::string ColumnPriority::modify (std::string& value)
{
return upperCase (value);
}
////////////////////////////////////////////////////////////////////////////////
bool ColumnPriority::can_modify ()
{
return true;
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -68,25 +68,30 @@ bool ColumnProject::validate (std::string& value)
// Set the minimum and maximum widths for the value.
void ColumnProject::measure (Task& task, unsigned int& minimum, unsigned int& maximum)
{
std::string project = task.get (_name);
minimum = maximum = 0;
if (_style == "parent")
if (task.has (_name))
{
std::string::size_type period = project.find ('.');
if (period != std::string::npos)
project = project.substr (0, period);
}
else if (_style == "indented")
{
project = indentProject (project, " ", '.');
}
else if (_style != "default" &&
_style != "full" &&
_style != "indented")
throw format (STRING_COLUMN_BAD_FORMAT, _name, _style);
std::string project = task.get (_name);
minimum = longestWord (project);
maximum = utf8_width (project);
if (_style == "parent")
{
std::string::size_type period = project.find ('.');
if (period != std::string::npos)
project = project.substr (0, period);
}
else if (_style == "indented")
{
project = indentProject (project, " ", '.');
}
else if (_style != "default" &&
_style != "full" &&
_style != "indented")
throw format (STRING_COLUMN_BAD_FORMAT, _name, _style);
minimum = longestWord (project);
maximum = utf8_width (project);
}
}
////////////////////////////////////////////////////////////////////////////////
@@ -96,24 +101,27 @@ void ColumnProject::render (
int width,
Color& color)
{
std::string project = task.get (_name);
if (_style == "parent")
if (task.has (_name))
{
std::string::size_type period = project.find ('.');
if (period != std::string::npos)
project = project.substr (0, period);
}
else if (_style == "indented")
{
project = indentProject (project, " ", '.');
}
std::string project = task.get (_name);
if (_style == "parent")
{
std::string::size_type period = project.find ('.');
if (period != std::string::npos)
project = project.substr (0, period);
}
else if (_style == "indented")
{
project = indentProject (project, " ", '.');
}
std::vector <std::string> raw;
wrapText (raw, project, width, _hyphenate);
std::vector <std::string> raw;
wrapText (raw, project, width, _hyphenate);
std::vector <std::string>::iterator i;
for (i = raw.begin (); i != raw.end (); ++i)
lines.push_back (color.colorize (leftJustify (*i, width)));
std::vector <std::string>::iterator i;
for (i = raw.begin (); i != raw.end (); ++i)
lines.push_back (color.colorize (leftJustify (*i, width)));
}
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -79,18 +79,23 @@ void ColumnRecur::setStyle (const std::string& value)
// Set the minimum and maximum widths for the value.
void ColumnRecur::measure (Task& task, unsigned int& minimum, unsigned int& maximum)
{
if (_style == "default" ||
_style == "duration")
minimum = maximum = 0;
if (task.has (_name))
{
minimum = maximum = Duration (task.get ("recur")).formatISO ().length ();
}
else if (_style == "indicator")
{
if (task.has (_name))
if (_style == "default" ||
_style == "duration")
{
minimum = maximum = Duration (task.get ("recur")).formatISO ().length ();
}
else if (_style == "indicator")
{
minimum = maximum = utf8_width (context.config.get ("recurrence.indicator"));
_fixed_width = true;
}
else
throw format (STRING_COLUMN_BAD_FORMAT, _name, _style);
}
else
throw format (STRING_COLUMN_BAD_FORMAT, _name, _style);
}
////////////////////////////////////////////////////////////////////////////////
@@ -108,8 +113,7 @@ void ColumnRecur::render (
lines.push_back (
color.colorize (
rightJustify (
Duration (task.get ("recur")).formatISO (),
width)));
Duration (task.get ("recur")).formatISO (), width)));
}
else if (_style == "indicator")
{

View File

@@ -76,8 +76,8 @@ void ColumnStart::measure (Task& task, unsigned int& minimum, unsigned int& maxi
{
if (_style == "active")
{
if (! task.has ("end"))
minimum = maximum = utf8_width (context.config.get ("active.indicator"));
minimum = maximum = utf8_width (context.config.get ("active.indicator"));
_fixed_width = true;
}
else
ColumnDate::measure (task, minimum, maximum);

View File

@@ -71,6 +71,8 @@ void ColumnString::setReport (const std::string& value)
//
void ColumnString::measure (const std::string& value, unsigned int& minimum, unsigned int& maximum)
{
minimum = maximum = 0;
if (_style == "left" ||
_style == "right" ||
_style == "default")

View File

@@ -84,30 +84,41 @@ 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)
{
if (_style == "indicator") minimum = maximum = utf8_width (context.config.get ("tag.indicator"));
else if (_style == "count") minimum = maximum = 3;
else if (_style == "default" ||
_style == "list")
{
std::string tags = task.get (_name);
minimum = 0;
maximum = utf8_width (tags);
minimum = maximum = 0;
if (maximum)
if (task.has (_name))
{
if (_style == "indicator")
{
std::vector <std::string> all;
split (all, tags, ',');
std::vector <std::string>::iterator i;
for (i = all.begin (); i != all.end (); ++i)
minimum = maximum = utf8_width (context.config.get ("tag.indicator"));
_fixed_width = true;
}
else if (_style == "count")
{
minimum = maximum = 3;
}
else if (_style == "default" ||
_style == "list")
{
std::string tags = task.get (_name);
maximum = utf8_width (tags);
if (maximum)
{
unsigned int length = utf8_width (*i);
if (length > minimum)
minimum = length;
std::vector <std::string> all;
split (all, tags, ',');
std::vector <std::string>::iterator i;
for (i = all.begin (); i != all.end (); ++i)
{
unsigned int length = utf8_width (*i);
if (length > minimum)
minimum = length;
}
}
}
else
throw format (STRING_COLUMN_BAD_FORMAT, _name, _style);
}
else
throw format (STRING_COLUMN_BAD_FORMAT, _name, _style);
}
////////////////////////////////////////////////////////////////////////////////
@@ -117,9 +128,9 @@ void ColumnTags::render (
int width,
Color& color)
{
std::string tags = task.get (_name);
if (tags != "")
if (task.has (_name))
{
std::string tags = task.get (_name);
if (_style == "default" ||
_style == "list")
{

View File

@@ -80,49 +80,52 @@ void ColumnUDA::measure (Task& task, unsigned int& minimum, unsigned int& maximu
{
minimum = maximum = 0;
if (_style == "default")
if (task.has (_name))
{
std::string value = task.get (_name);
if (value != "")
if (_style == "default")
{
if (_type == "date")
std::string value = task.get (_name);
if (value != "")
{
// Determine the output date format, which uses a hierarchy of definitions.
// rc.report.<report>.dateformat
// rc.dateformat.report
// rc.dateformat
Date date ((time_t) strtol (value.c_str (), NULL, 10));
std::string format = context.config.get ("report." + _report + ".dateformat");
if (format == "")
format = context.config.get ("dateformat.report");
if (format == "")
format = context.config.get ("dateformat");
if (_type == "date")
{
// Determine the output date format, which uses a hierarchy of definitions.
// rc.report.<report>.dateformat
// rc.dateformat.report
// rc.dateformat
Date date ((time_t) strtol (value.c_str (), NULL, 10));
std::string format = context.config.get ("report." + _report + ".dateformat");
if (format == "")
format = context.config.get ("dateformat.report");
if (format == "")
format = context.config.get ("dateformat");
minimum = maximum = Date::length (format);
}
else if (_type == "duration")
{
minimum = maximum = utf8_width (Duration (value).formatISO ());
}
else if (_type == "string")
{
std::string stripped = Color::strip (value);
maximum = longestLine (stripped);
minimum = longestWord (stripped);
}
else if (_type == "numeric")
{
minimum = maximum = utf8_width (value);
minimum = maximum = Date::length (format);
}
else if (_type == "duration")
{
minimum = maximum = utf8_width (Duration (value).formatISO ());
}
else if (_type == "string")
{
std::string stripped = Color::strip (value);
maximum = longestLine (stripped);
minimum = longestWord (stripped);
}
else if (_type == "numeric")
{
minimum = maximum = utf8_width (value);
}
}
}
}
else if (_style == "indicator")
{
if (task.has (_name))
else if (_style == "indicator")
{
minimum = maximum = utf8_width (context.config.get ("uda." + _name + ".indicator"));
_fixed_width = true;
}
else
throw format (STRING_COLUMN_BAD_FORMAT, _name, _style);
}
else
throw format (STRING_COLUMN_BAD_FORMAT, _name, _style);
}
////////////////////////////////////////////////////////////////////////////////
@@ -132,11 +135,11 @@ void ColumnUDA::render (
int width,
Color& color)
{
if (_style == "default")
if (task.has (_name))
{
std::string value = task.get (_name);
if (value != "")
if (_style == "default")
{
std::string value = task.get (_name);
if (_type == "date")
{
// Determine the output date format, which uses a hierarchy of definitions.
@@ -177,13 +180,13 @@ void ColumnUDA::render (
lines.push_back (color.colorize (rightJustify (value, width)));
}
}
}
else if (_style == "indicator")
{
if (task.has (_name))
lines.push_back (
color.colorize (
rightJustify (context.config.get ("uda." + _name + ".indicator"), width)));
else if (_style == "indicator")
{
if (task.has (_name))
lines.push_back (
color.colorize (
rightJustify (context.config.get ("uda." + _name + ".indicator"), width)));
}
}
}

View File

@@ -37,7 +37,6 @@
#include <ColMask.h>
#include <ColModified.h>
#include <ColParent.h>
#include <ColPriority.h>
#include <ColProject.h>
#include <ColRecur.h>
#include <ColScheduled.h>
@@ -88,7 +87,6 @@ Column* Column::factory (const std::string& name, const std::string& report)
else if (column_name == "mask") c = new ColumnMask ();
else if (column_name == "modified") c = new ColumnModified ();
else if (column_name == "parent") c = new ColumnParent ();
else if (column_name == "priority") c = new ColumnPriority ();
else if (column_name == "project") c = new ColumnProject ();
else if (column_name == "recur") c = new ColumnRecur ();
else if (column_name == "scheduled") c = new ColumnScheduled ();
@@ -131,7 +129,6 @@ void Column::factory (std::map <std::string, Column*>& all)
c = new ColumnMask (); all[c->_name] = c;
c = new ColumnModified (); all[c->_name] = c;
c = new ColumnParent (); all[c->_name] = c;
c = new ColumnPriority (); all[c->_name] = c;
c = new ColumnProject (); all[c->_name] = c;
c = new ColumnRecur (); all[c->_name] = c;
c = new ColumnScheduled (); all[c->_name] = c;
@@ -211,19 +208,21 @@ Column::Column ()
, _report ("")
, _modifiable (true)
, _uda (false)
, _fixed_width (false)
{
}
////////////////////////////////////////////////////////////////////////////////
Column::Column (const Column& other)
{
_name = other._name;
_type = other._type;
_style = other._style;
_label = other._label;
_label = other._report;
_modifiable = other._modifiable;
_uda = other._uda;
_name = other._name;
_type = other._type;
_style = other._style;
_label = other._label;
_label = other._report;
_modifiable = other._modifiable;
_uda = other._uda;
_fixed_width = other._fixed_width;
}
////////////////////////////////////////////////////////////////////////////////
@@ -238,6 +237,7 @@ Column& Column::operator= (const Column& other)
_report = other._report;
_modifiable = other._modifiable;
_uda = other._uda;
_fixed_width = other._fixed_width;
}
return *this;

View File

@@ -46,12 +46,13 @@ public:
bool operator== (const Column&) const; // TODO Is this necessary?
virtual ~Column ();
std::string name () const { return _name; }
std::string style () const { return _style; }
std::string label () const { return _label; }
std::string type () const { return _type; }
const std::string& name () const { return _name; }
const std::string& style () const { return _style; }
const std::string& label () const { return _label; }
const std::string& type () const { return _type; }
bool modifiable () const { return _modifiable; }
bool is_uda () const { return _uda; }
bool is_fixed_width () const { return _fixed_width;}
std::vector <std::string> styles () const { return _styles; }
std::vector <std::string> examples () const { return _examples; }
@@ -76,6 +77,7 @@ protected:
std::string _report;
bool _modifiable;
bool _uda;
bool _fixed_width;
std::vector <std::string> _styles;
std::vector <std::string> _examples;
};

Some files were not shown because too many files have changed in this diff Show More