File: EXPOSITION

package info (click to toggle)
task 2.5.1%2Bdfsg-7
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 6,576 kB
  • sloc: cpp: 36,161; python: 11,324; perl: 8,697; ansic: 7,400; sh: 673; makefile: 23
file content (210 lines) | stat: -rw-r--r-- 8,975 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
Startup
  On startup, main creates a global Context object, then calls the
  Context::initialize and Context::run methods.

  Context is a large object that holds all task information, both in terms of
  the task data, and intermediate run-time data. Having one global Context
  object means we don't have 50 global variables. Context is therefore just a
  big global bucket of data.

  Context::initialize sets up all the data and processes the command line. The
  initialization process is a big chicken-and-egg problem, because the command
  line depends on configuration (aliases) and the command line can force a
  reload of configuration (rc:foo). This is solved by look-ahead: the command
  line is scanned for 'rc:xxx' and 'rc.data.location:xxx' arguments, then later
  for overrides.

  The Context::run method handles all the debug output and exceptions. Its
  main purpose is to set up exception handling and call Context::dispatch.


Command Line Parsing
  Command line parsing is difficult because of all the ambiguity. The solution
  is to make multiple passes over the command line. For example, the command
  determines whether subsequent arguments are interpreted as part of a filter or
  set of modifications.

  The CLI2 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.


Dispatch
  Dispatch is simple: once the command line is parsed, the command is used to
  look up a command object, then a call is made to the Command::execute method.

  Context stores an associative map of command object pointers indexed by a
  string. This means the 'done' string is an index to the CmdDone object that
  implements the functionality.


Command Objects
  Every task command is implemented by a command object. The command object
  provides metadata, usage and one-line help in addition to the ::execute method
  that implements the command. The Command base class implements common
  functionality.


Column Objects
  There is a 1:1 correspondence between attributes stored in the data files and
  the columns that may be reported. These are represented by column objects,
  which are responsible for validating input, measuring space needed according
  to various formats, and for rendering data for reports. There is a
  ColDescription object that inherits from a Column base class.


TDB2
  The TDB2 object is a layered, transactioned I/O manager. Its purpose is to
  isolate code from file I/O, locking and parsing details. It is also
  responsible for minimizing reads, writes and parsing of data files.

  All input is assumed to be UTF8. All stored data is UTF8.


GC
  Garbage Collection is the process that moves tasks between the pending.data
  and completed.data files. It is also responsible for waking tasks out of the
  wait state.

  Every command that displays task IDs will cause a GC to be run first, which
  minimizes the number of changes necessary to the task IDs. This means that
  when a report shows task IDs, those IDs will remain valid while subsequent
  write commands are issued. The next report run may show different IDs.

  Minimizing the size of pending.data is important for performance, because it
  is the file that is accessed most.


Files
  The data files used are all kept in the rc.data.location directory, which
  defaults to ~/.task. The files are:

    pending.data
    completed.data
    undo.data
    backlog.data

  The pending.data file aspires to contain only pending, waiting and recurring
  tasks, but this is only correct after a GC, and before any tasks are modified.
  This file tends to be relatively stable in size, reflecting the length of the
  task list.

  The completed.data file accumulates data over time, and grows unbounded.

  The undo.data file accumulates changes over time, and grows unbounded. It
  provides all the necessary metadata to support the 'undo' command.

  The backlog.data file contains an accumulated set of changes that have not
  been transmitted to Taskserver. It grows unbounded between 'sync' commands.


Filter
  A filter is simply a set of command line arguments, but is only a subset of
  the complete command line. These arguments are extracted from the parse tree
  according to whether the command found is a read or write command.

  There is a Filter::subset method for applying a filter to a set of tasks,
  yielding a result set. It does this by creating an expression from the
  parse tree using the Eval object, then evaluating the expression for each task,
  such that the result set contains only tasks for which the expression evaluates
  to Boolean true.


Eval & Variant
  The Eval class evaluates expressions, provided in string form, using the
  Variant class to represent data elements. Variant implements all operators
  for all Variant types.


Sorting
  Sorting is performed on a set of tasks. More specifically, the list that is
  sorted is a set of numeric indexes to tasks that are stored in a separate
  list. This minimizes the amount of data copying involved to just integers
  rather than Task objects, but at the expense of one level of indirection.
  Memory fragmentation is a bigger problem than the performance of vector
  indexing.

  The actual sorting is performed by std::stable_sort, but the compare function
  is custom.


Render
  There are two rendering objects, ViewTask and ViewText. These both have the
  same tabular grid rendering capabilities. ViewText maintains a 2D vector of
  strings to contain the data to be rendered, so it is used for things like the
  help command output. ViewTask does not copy data, but assumes all data is
  stored externally in a vector of Tasks, which minimizes data copying.

  ViewTask contains projection data in the form of a set of Column objects that
  represent the X axis. The Y axis is represented by a vector of tasks.

  The rendering process is complex. It involves dynamically setting column
  widths based on (1) available terminal width, (2) the columns to be included
  in the output, (3) ability to wrap text for certain columns and (4) the size
  of the data to be rendered, which involves added complexity when UTF8 is used.

  The Column objects determine minimum width for a column and the maximum width
  which then allows ViewT* to make choices.


Test Suite
  A strong and diverse test suite is critical to the successful release of any
  software. With the complex command set and its myriad permutations, a test
  suite is the only way to ensure quality levels, and guarantee that big changes
  are robust.

  It is intended that the test suite continues growing, mostly feature tests.
  The test are mostly written in Python. Some tests are written in C++ and all
  tests generate TAP output.

  There are currently about 8,000 unit tests, that take only a few seconds to
  run on a multi-core machine.

  Taskwarrior uses flod software to automate continuous integration across many
  platforms. Code changes are automatically detected, propagated, built and
  tested on a variety of participating platforms. Grid testing results are here:

    http://central.tasktools.org/

  When making code changes, it is important that the test suite be run to verify
  that functionality was not broken.


Debugging
  The 'rc.debug=on' override provides the following additional information which
  is useful during debugging:

    - Timing of various components (used to generate the data for the charts at
      http://tasktools.org/performance).
    - Data load times.
    - Terminal size, color capabilities.
    - Command line parse tree.
    - TDB2 layer and I/O information.

  Additionally, there are other settings (see 'man taskrc' for full details) that
  may also be helpful: 'rc.hooks=on|off', 'rc.debug.parser=0|1|2|3',
  'rc.debug.hooks=0|1|2', 'rc.debug.tls=0|1|2|3...'.


Patches
  Patches are encouraged and welcomed. Either attach them to the appropriate
  Jira issue, or send them to support@taskwarrior.org. A good patch:

    - Maintains the MIT license, and does not contain code lifted from other
      sources. You will have written 100% of the code in the patch, otherwise
      we cannot maintain the license.
    - Precisely addresses one issue only.
    - Doesn't break unit tests.
    - Doesn't introduce dependencies.
    - Is accompanied by unit tests, where appropriate, written in Python.
    - Is accompanied by documentation changes, where appropriate.
    - Conforms to the prevailing coding standards - in other words, it should
      fit right in with the existing code.

  A patch may be rejected for violating any of the above rules, and more.
  Bad patches may be accepted and modified depending on work load and mood. It
  is possible that a patch may be rejected because it conflicts in some way with
  plans or upcoming changes. Check with us first, before sinking time and effort
  into a patch.

---