• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

GothenburgBitFactory / taskwarrior / 29693910695

19 Jul 2026 03:58PM UTC coverage: 84.782% (-0.2%) from 84.997%
29693910695

Pull #4117

github

web-flow
Merge 2a56ced1a into bbbf3f9fd
Pull Request #4117: Add a (mostly) full Task FFI from TaskChampion

19599 of 23117 relevant lines covered (84.78%)

24198.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.54
/src/commands/Command.cpp
1
////////////////////////////////////////////////////////////////////////////////
2
//
3
// Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez.
4
//
5
// Permission is hereby granted, free of charge, to any person obtaining a copy
6
// of this software and associated documentation files (the "Software"), to deal
7
// in the Software without restriction, including without limitation the rights
8
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
// copies of the Software, and to permit persons to whom the Software is
10
// furnished to do so, subject to the following conditions:
11
//
12
// The above copyright notice and this permission notice shall be included
13
// in all copies or substantial portions of the Software.
14
//
15
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
// SOFTWARE.
22
//
23
// https://www.opensource.org/licenses/mit-license.php
24
//
25
////////////////////////////////////////////////////////////////////////////////
26

27
#include <cmake.h>
28
// cmake.h include header must come first
29

30
#include <CmdAdd.h>
31
#include <CmdAliases.h>
32
#include <CmdAnnotate.h>
33
#include <CmdAppend.h>
34
#include <CmdAttributes.h>
35
#include <CmdBurndown.h>
36
#include <CmdCalc.h>
37
#include <CmdCalendar.h>
38
#include <CmdColor.h>
39
#include <CmdColumns.h>
40
#include <CmdCommands.h>
41
#include <CmdConfig.h>
42
#include <CmdContext.h>
43
#include <CmdCount.h>
44
#include <CmdCustom.h>
45
#include <CmdDelete.h>
46
#include <CmdDenotate.h>
47
#include <CmdDiagnostics.h>
48
#include <CmdDone.h>
49
#include <CmdDuplicate.h>
50
#include <CmdEdit.h>
51
#include <Command.h>
52
#include <format.h>
53
#include <shared.h>
54
#include <util.h>
55

56
#include <iostream>
57
#include <vector>
58
#ifdef HAVE_EXECUTE
59
#include <CmdExec.h>
60
#endif
61
#include <CmdExport.h>
62
#include <CmdGet.h>
63
#include <CmdHelp.h>
64
#include <CmdHistory.h>
65
#include <CmdIDs.h>
66
#include <CmdImport.h>
67
#include <CmdImportV2.h>
68
#include <CmdInfo.h>
69
#include <CmdLog.h>
70
#include <CmdLogo.h>
71
#include <CmdModify.h>
72
#include <CmdNews.h>
73
#include <CmdPrepend.h>
74
#include <CmdProjects.h>
75
#include <CmdPurge.h>
76
#include <CmdReports.h>
77
#include <CmdShow.h>
78
#include <CmdStart.h>
79
#include <CmdStats.h>
80
#include <CmdStop.h>
81
#include <CmdSummary.h>
82
#include <CmdSync.h>
83
#include <CmdTags.h>
84
#include <CmdTimesheet.h>
85
#include <CmdUDAs.h>
86
#include <CmdUndo.h>
87
#include <CmdUnique.h>
88
#include <CmdUrgency.h>
89
#include <CmdVersion.h>
90
#include <ColDue.h>
91
#include <ColProject.h>
92
#include <Context.h>
93

94
////////////////////////////////////////////////////////////////////////////////
95
void Command::factory(std::map<std::string, Command*>& all) {
4,621✔
96
  Command* c;
97

98
  c = new CmdAdd();
4,621✔
99
  all[c->keyword()] = c;
4,621✔
100
  c = new CmdAnnotate();
4,621✔
101
  all[c->keyword()] = c;
4,621✔
102
  c = new CmdAppend();
4,621✔
103
  all[c->keyword()] = c;
4,621✔
104
  c = new CmdBurndownAnnual();
4,621✔
105
  all[c->keyword()] = c;
4,621✔
106
  c = new CmdBurndownDaily();
4,621✔
107
  all[c->keyword()] = c;
4,621✔
108
  c = new CmdBurndownMonthly();
4,621✔
109
  all[c->keyword()] = c;
4,621✔
110
  c = new CmdBurndownWeekly();
4,621✔
111
  all[c->keyword()] = c;
4,621✔
112
  c = new CmdCalc();
4,621✔
113
  all[c->keyword()] = c;
4,621✔
114
  c = new CmdCalendar();
4,621✔
115
  all[c->keyword()] = c;
4,621✔
116
  c = new CmdColor();
4,621✔
117
  all[c->keyword()] = c;
4,621✔
118
  c = new CmdColumns();
4,621✔
119
  all[c->keyword()] = c;
4,621✔
120
  c = new CmdCommands();
4,621✔
121
  all[c->keyword()] = c;
4,621✔
122
  c = new CmdCompletionAliases();
4,621✔
123
  all[c->keyword()] = c;
4,621✔
124
  c = new CmdCompletionColumns();
4,621✔
125
  all[c->keyword()] = c;
4,621✔
126
  c = new CmdCompletionCommands();
4,621✔
127
  all[c->keyword()] = c;
4,621✔
128
  c = new CmdCompletionConfig();
4,621✔
129
  all[c->keyword()] = c;
4,621✔
130
  c = new CmdCompletionContext();
4,621✔
131
  all[c->keyword()] = c;
4,621✔
132
  c = new CmdCompletionIds();
4,621✔
133
  all[c->keyword()] = c;
4,621✔
134
  c = new CmdCompletionUDAs();
4,621✔
135
  all[c->keyword()] = c;
4,621✔
136
  c = new CmdCompletionUuids();
4,621✔
137
  all[c->keyword()] = c;
4,621✔
138
  c = new CmdCompletionProjects();
4,621✔
139
  all[c->keyword()] = c;
4,621✔
140
  c = new CmdCompletionTags();
4,621✔
141
  all[c->keyword()] = c;
4,621✔
142
  c = new CmdCompletionVersion();
4,621✔
143
  all[c->keyword()] = c;
4,621✔
144
  c = new CmdConfig();
4,621✔
145
  all[c->keyword()] = c;
4,621✔
146
  c = new CmdContext();
4,621✔
147
  all[c->keyword()] = c;
4,621✔
148
  c = new CmdCount();
4,621✔
149
  all[c->keyword()] = c;
4,621✔
150
  c = new CmdDelete();
4,621✔
151
  all[c->keyword()] = c;
4,621✔
152
  c = new CmdDenotate();
4,621✔
153
  all[c->keyword()] = c;
4,621✔
154
  c = new CmdDiagnostics();
4,621✔
155
  all[c->keyword()] = c;
4,621✔
156
  c = new CmdDone();
4,621✔
157
  all[c->keyword()] = c;
4,621✔
158
  c = new CmdDuplicate();
4,621✔
159
  all[c->keyword()] = c;
4,621✔
160
  c = new CmdEdit();
4,621✔
161
  all[c->keyword()] = c;
4,621✔
162
#ifdef HAVE_EXECUTE
163
  c = new CmdExec();
4,621✔
164
  all[c->keyword()] = c;
4,621✔
165
#endif
166
  c = new CmdExport();
4,621✔
167
  all[c->keyword()] = c;
4,621✔
168
  c = new CmdGet();
4,621✔
169
  all[c->keyword()] = c;
4,621✔
170
  c = new CmdGHistoryDaily();
4,621✔
171
  all[c->keyword()] = c;
4,621✔
172
  c = new CmdGHistoryWeekly();
4,621✔
173
  all[c->keyword()] = c;
4,621✔
174
  c = new CmdGHistoryMonthly();
4,621✔
175
  all[c->keyword()] = c;
4,621✔
176
  c = new CmdGHistoryAnnual();
4,621✔
177
  all[c->keyword()] = c;
4,621✔
178
  c = new CmdHelp();
4,621✔
179
  all[c->keyword()] = c;
4,621✔
180
  c = new CmdHistoryDaily();
4,621✔
181
  all[c->keyword()] = c;
4,621✔
182
  c = new CmdHistoryWeekly();
4,621✔
183
  all[c->keyword()] = c;
4,621✔
184
  c = new CmdHistoryMonthly();
4,621✔
185
  all[c->keyword()] = c;
4,621✔
186
  c = new CmdHistoryAnnual();
4,621✔
187
  all[c->keyword()] = c;
4,621✔
188
  c = new CmdIDs();
4,621✔
189
  all[c->keyword()] = c;
4,621✔
190
  c = new CmdImport();
4,621✔
191
  all[c->keyword()] = c;
4,621✔
192
  c = new CmdImportV2();
4,621✔
193
  all[c->keyword()] = c;
4,621✔
194
  c = new CmdInfo();
4,621✔
195
  all[c->keyword()] = c;
4,621✔
196
  c = new CmdLog();
4,621✔
197
  all[c->keyword()] = c;
4,621✔
198
  c = new CmdLogo();
4,621✔
199
  all[c->keyword()] = c;
4,621✔
200
  c = new CmdModify();
4,621✔
201
  all[c->keyword()] = c;
4,621✔
202
  c = new CmdNews();
4,621✔
203
  all[c->keyword()] = c;
4,621✔
204
  c = new CmdPrepend();
4,621✔
205
  all[c->keyword()] = c;
4,621✔
206
  c = new CmdProjects();
4,621✔
207
  all[c->keyword()] = c;
4,621✔
208
  c = new CmdPurge();
4,621✔
209
  all[c->keyword()] = c;
4,621✔
210
  c = new CmdReports();
4,621✔
211
  all[c->keyword()] = c;
4,621✔
212
  c = new CmdShow();
4,621✔
213
  all[c->keyword()] = c;
4,621✔
214
  c = new CmdShowRaw();
4,621✔
215
  all[c->keyword()] = c;
4,621✔
216
  c = new CmdStart();
4,621✔
217
  all[c->keyword()] = c;
4,621✔
218
  c = new CmdStats();
4,621✔
219
  all[c->keyword()] = c;
4,621✔
220
  c = new CmdStop();
4,621✔
221
  all[c->keyword()] = c;
4,621✔
222
  c = new CmdSummary();
4,621✔
223
  all[c->keyword()] = c;
4,621✔
224
  c = new CmdSync();
4,621✔
225
  all[c->keyword()] = c;
4,621✔
226
  c = new CmdTags();
4,621✔
227
  all[c->keyword()] = c;
4,621✔
228
  c = new CmdTimesheet();
4,621✔
229
  all[c->keyword()] = c;
4,621✔
230
  c = new CmdUDAs();
4,621✔
231
  all[c->keyword()] = c;
4,621✔
232
  c = new CmdUndo();
4,621✔
233
  all[c->keyword()] = c;
4,621✔
234
  c = new CmdUnique();
4,621✔
235
  all[c->keyword()] = c;
4,621✔
236
  c = new CmdUrgency();
4,621✔
237
  all[c->keyword()] = c;
4,621✔
238
  c = new CmdUUIDs();
4,621✔
239
  all[c->keyword()] = c;
4,621✔
240
  c = new CmdVersion();
4,621✔
241
  all[c->keyword()] = c;
4,621✔
242
  c = new CmdZshAttributes();
4,621✔
243
  all[c->keyword()] = c;
4,621✔
244
  c = new CmdZshCommands();
4,621✔
245
  all[c->keyword()] = c;
4,621✔
246
  c = new CmdZshCompletionIds();
4,621✔
247
  all[c->keyword()] = c;
4,621✔
248
  c = new CmdZshCompletionUuids();
4,621✔
249
  all[c->keyword()] = c;
4,621✔
250

251
  // Instantiate a command object for each custom report.
252
  std::vector<std::string> reports;
4,621✔
253
  for (auto& i : Context::getContext().config) {
1,136,065✔
254
    if (i.first.substr(0, 7) == "report.") {
1,131,444✔
255
      std::string report = i.first.substr(7);
477,287✔
256
      auto columns = report.find(".columns");
477,287✔
257
      if (columns != std::string::npos) reports.push_back(report.substr(0, columns));
477,287✔
258
    }
477,287✔
259
  }
260

261
  for (auto& report : reports) {
83,650✔
262
    // Make sure a custom report does not clash with a built-in command.
263
    if (all.find(report) != all.end())
79,029✔
264
      throw format("Custom report '{1}' conflicts with built-in task command.", report);
×
265

266
    c = new CmdCustom(report, "task <filter> " + report,
158,058✔
267
                      Context::getContext().config.get("report." + report + ".description"));
237,087✔
268

269
    all[c->keyword()] = c;
79,029✔
270
  }
271
}
4,621✔
272

273
////////////////////////////////////////////////////////////////////////////////
274
const std::map<Command::Category, std::string> Command::categoryNames = {
275
    // These strings are intentionally not l10n'd: they are used as identifiers.
276
    {Command::Category::unassigned, "unassigned"}  // should never happen
277
    ,
278
    {Command::Category::metadata, "metadata"},
279
    {Command::Category::report, "report"},
280
    {Command::Category::operation, "operation"},
281
    {Command::Category::context, "context"},
282
    {Command::Category::graphs, "graphs"},
283
    {Command::Category::config, "config"},
284
    {Command::Category::migration, "migration"},
285
    {Command::Category::misc, "misc"},
286
    {Command::Category::internal, "internal"},
287
    {Command::Category::UNDOCUMENTED, "undocumented"}};
288

289
////////////////////////////////////////////////////////////////////////////////
290
Command::Command()
425,611✔
291
    : _keyword(""),
425,611✔
292
      _usage(""),
851,222✔
293
      _description(""),
851,222✔
294
      _read_only(true),
425,611✔
295
      _displays_id(true),
425,611✔
296
      _needs_confirm(false),
425,611✔
297
      _needs_gc(true),
425,611✔
298
      _needs_recur_update(false),
425,611✔
299
      _uses_context(false),
425,611✔
300
      _accepts_filter(false),
425,611✔
301
      _accepts_modifications(false),
425,611✔
302
      _accepts_miscellaneous(false),
425,611✔
303
      _category(Category::unassigned),
425,611✔
304
      _permission_quit(false),
425,611✔
305
      _permission_all(false),
425,611✔
306
      _first_iteration(true) {}
851,222✔
307

308
////////////////////////////////////////////////////////////////////////////////
309
std::string Command::keyword() const { return _keyword; }
425,611✔
310

311
////////////////////////////////////////////////////////////////////////////////
312
std::string Command::usage() const { return _usage; }
277✔
313

314
////////////////////////////////////////////////////////////////////////////////
315
std::string Command::description() const { return _description; }
733✔
316

317
////////////////////////////////////////////////////////////////////////////////
318
bool Command::read_only() const { return _read_only; }
9,059✔
319

320
////////////////////////////////////////////////////////////////////////////////
321
bool Command::displays_id() const { return _displays_id; }
5,390✔
322

323
////////////////////////////////////////////////////////////////////////////////
324
bool Command::needs_gc() const { return _needs_gc; }
14,616✔
325

326
////////////////////////////////////////////////////////////////////////////////
327
bool Command::needs_recur_update() const { return _needs_recur_update; }
8,476✔
328

329
////////////////////////////////////////////////////////////////////////////////
330
bool Command::uses_context() const { return _uses_context; }
8,150✔
331

332
////////////////////////////////////////////////////////////////////////////////
333
bool Command::accepts_filter() const { return _accepts_filter; }
48,900✔
334

335
////////////////////////////////////////////////////////////////////////////////
336
bool Command::accepts_modifications() const { return _accepts_modifications; }
34,323✔
337

338
////////////////////////////////////////////////////////////////////////////////
339
bool Command::accepts_miscellaneous() const { return _accepts_miscellaneous; }
18,305✔
340

341
////////////////////////////////////////////////////////////////////////////////
342
Command::Category Command::category() const { return _category; }
417✔
343

344
////////////////////////////////////////////////////////////////////////////////
345
// Returns true or false indicating whether to proceed with a write command, on
346
// a per-task basis, after (potentially) asking for permission.
347
//
348
// Factors:
349
//   filtered.size ()
350
//   rc.bulk
351
//   rc.confirmation
352
//   this->_read_only
353
bool Command::permission(const std::string& question, unsigned int quantity) {
470✔
354
  // Read-only commands do not need to seek permission.  Write commands are
355
  // granted permission automatically if the 'all' selection was made in an
356
  // earlier call.  Or if the 'all' option has already been made.
357
  if (_read_only || _permission_all) return true;
470✔
358

359
  // If the 'quit' selection has already been made.
360
  if (_permission_quit) return false;
458✔
361

362
  // What remains are write commands that have not yet selected 'all' or 'quit'.
363
  // Describe the task.
364
  bool confirmation = Context::getContext().config.getBoolean("confirmation");
916✔
365
  unsigned int bulk = Context::getContext().config.getInteger("bulk");
916✔
366

367
  // Quantity 1 modifications have optional confirmation, and only (y/n).
368
  if (quantity == 1) {
458✔
369
    if (!_needs_confirm || !confirmation) return true;
400✔
370

371
    bool answer = confirm(question);
55✔
372
    return answer;
55✔
373
  }
374

375
  // 1 < Quantity < bulk modifications have optional confirmation, in the (y/n/a/q)
376
  // style. Bulk = 0 denotes infinite bulk.
377
  if ((bulk == 0 || quantity < bulk) && (!_needs_confirm || !confirmation)) return true;
58✔
378

379
  if (Context::getContext().verbose("blank") && !_first_iteration) std::cout << '\n';
81✔
380
  int answer = confirm4(question);
27✔
381
  _first_iteration = false;
27✔
382
  switch (answer) {
27✔
383
    case 1:
13✔
384
      return true;  // yes
13✔
385
    case 2:
5✔
386
      _permission_all = true;
5✔
387
      return true;  // all
5✔
388
    case 3:
4✔
389
      _permission_quit = true;
4✔
390
      return false;  // quit
4✔
391
  }
392

393
  return false;  // This line keeps the compiler happy.
5✔
394
}
395

396
////////////////////////////////////////////////////////////////////////////////
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc