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

GothenburgBitFactory / taskwarrior / 11420355627

19 Oct 2024 08:00PM UTC coverage: 84.853% (+0.6%) from 84.223%
11420355627

push

github

web-flow
Pass rc.weekstart to libshared for ISO8601 weeknum parsing if "monday" (#3654)

* libshared: bump for weekstart, epoch defines, eopww fix

mainly those visible changes, and miscellaneous others

see GothenburgBitFactory/taskwarrior#3623 (weekstart)
see GothenburgBitFactory/taskwarrior#3651 (epoch limit defines)
see GothenburgBitFactory/libshared#73 (eopww fix)

* Initialize libshared's weekstart from user's rc.weekstart config

This enables use of newer libshared code that can parse week numbers
according to ISO8601 instead of existing code which is always using
Sunday-based weeks.  To get ISO behavior, set rc.weekstart=monday.
Default is still Sunday / old algorithm, as before, since Sunday is in
the hardcoded default rcfile.

Weekstart does not yet fix week-relative shortcuts, which will still
always use Monday.

See #3623 for further details.

4 of 6 new or added lines in 2 files covered. (66.67%)

993 existing lines in 25 files now uncovered.

19019 of 22414 relevant lines covered (84.85%)

23067.98 hits per line

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

96.45
/src/columns/Column.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 <ColDepends.h>
31
#include <ColDescription.h>
32
#include <ColDue.h>
33
#include <ColEnd.h>
34
#include <ColEntry.h>
35
#include <ColID.h>
36
#include <ColIMask.h>
37
#include <ColLast.h>
38
#include <ColMask.h>
39
#include <ColModified.h>
40
#include <ColParent.h>
41
#include <ColProject.h>
42
#include <ColRType.h>
43
#include <ColRecur.h>
44
#include <ColScheduled.h>
45
#include <ColStart.h>
46
#include <ColStatus.h>
47
#include <ColTags.h>
48
#include <ColTemplate.h>
49
#include <ColUDA.h>
50
#include <ColUUID.h>
51
#include <ColUntil.h>
52
#include <ColUrgency.h>
53
#include <ColWait.h>
54
#include <Column.h>
55
#include <Context.h>
56
#include <format.h>
57
#include <shared.h>
58

59
#include <algorithm>
60
#include <set>
61

62
////////////////////////////////////////////////////////////////////////////////
63
// Supports the complete column definition:
64
//
65
//   <type>[.<format>]
66
//
67
Column* Column::factory(const std::string& name, const std::string& report) {
7,527✔
68
  // Decompose name into type and style.
69
  auto dot = name.find('.');
7,527✔
70
  std::string column_name;
7,527✔
71
  std::string column_style;
7,527✔
72
  if (dot != std::string::npos) {
7,527✔
73
    column_name = name.substr(0, dot);
4,218✔
74
    column_style = name.substr(dot + 1);
4,218✔
75
  } else {
76
    column_name = name;
3,309✔
77
    column_style = "default";
3,309✔
78
  }
79

80
  Column* c;
81
  if (column_name == "depends")
7,527✔
82
    c = new ColumnDepends();
558✔
83
  else if (column_name == "description")
6,969✔
84
    c = new ColumnDescription();
618✔
85
  else if (column_name == "due")
6,351✔
86
    c = new ColumnDue();
577✔
87
  else if (column_name == "end")
5,774✔
88
    c = new ColumnEnd();
90✔
89
  else if (column_name == "entry")
5,684✔
90
    c = new ColumnEntry();
467✔
91
  else if (column_name == "id")
5,217✔
92
    c = new ColumnID();
651✔
93
  else if (column_name == "imask")
4,566✔
94
    c = new ColumnIMask();
3✔
95
  else if (column_name == "last")
4,563✔
96
    c = new ColumnLast();
×
97
  else if (column_name == "mask")
4,563✔
98
    c = new ColumnMask();
3✔
99
  else if (column_name == "modified")
4,560✔
100
    c = new ColumnModified();
15✔
101
  else if (column_name == "parent")
4,545✔
102
    c = new ColumnParent();
3✔
103
  else if (column_name == "project")
4,542✔
104
    c = new ColumnProject();
571✔
105
  else if (column_name == "recur")
3,971✔
106
    c = new ColumnRecur();
564✔
107
  else if (column_name == "rtype")
3,407✔
108
    c = new ColumnRType();
×
109
  else if (column_name == "scheduled")
3,407✔
110
    c = new ColumnScheduled();
541✔
111
  else if (column_name == "start")
2,866✔
112
    c = new ColumnStart();
548✔
113
  else if (column_name == "status")
2,318✔
114
    c = new ColumnStatus();
84✔
115
  else if (column_name == "tags")
2,234✔
116
    c = new ColumnTags();
572✔
117
  else if (column_name == "template")
1,662✔
118
    c = new ColumnTemplate();
×
119
  else if (column_name == "until")
1,662✔
120
    c = new ColumnUntil();
541✔
121
  else if (column_name == "urgency")
1,121✔
122
    c = new ColumnUrgency();
342✔
123
  else if (column_name == "uuid")
779✔
124
    c = new ColumnUUID();
95✔
125
  else if (column_name == "wait")
684✔
126
    c = new ColumnWait();
205✔
127

128
  // UDA.
129
  else if (Context::getContext().config.has("uda." + column_name + ".type"))
479✔
130
    c = Column::uda(column_name);
477✔
131

132
  else
133
    throw format("Unrecognized column name '{1}'.", column_name);
2✔
134

135
  c->setReport(report);
7,525✔
136
  c->setStyle(column_style);
7,525✔
137
  return c;
7,514✔
138
}
7,540✔
139

140
////////////////////////////////////////////////////////////////////////////////
141
// Bulk column instantiation.
142
void Column::factory(std::map<std::string, Column*>& all) {
4,497✔
143
  Column* c;
144

145
  c = new ColumnDepends();
4,497✔
146
  all[c->_name] = c;
4,497✔
147
  c = new ColumnDescription();
4,497✔
148
  all[c->_name] = c;
4,497✔
149
  c = new ColumnDue();
4,497✔
150
  all[c->_name] = c;
4,497✔
151
  c = new ColumnEnd();
4,497✔
152
  all[c->_name] = c;
4,497✔
153
  c = new ColumnEntry();
4,497✔
154
  all[c->_name] = c;
4,497✔
155
  c = new ColumnID();
4,497✔
156
  all[c->_name] = c;
4,497✔
157
  c = new ColumnIMask();
4,497✔
158
  all[c->_name] = c;
4,497✔
159
  c = new ColumnLast();
4,497✔
160
  all[c->_name] = c;
4,497✔
161
  c = new ColumnMask();
4,497✔
162
  all[c->_name] = c;
4,497✔
163
  c = new ColumnModified();
4,497✔
164
  all[c->_name] = c;
4,497✔
165
  c = new ColumnParent();
4,497✔
166
  all[c->_name] = c;
4,497✔
167
  c = new ColumnProject();
4,497✔
168
  all[c->_name] = c;
4,497✔
169
  c = new ColumnRecur();
4,497✔
170
  all[c->_name] = c;
4,497✔
171
  c = new ColumnRType();
4,497✔
172
  all[c->_name] = c;
4,497✔
173
  c = new ColumnScheduled();
4,497✔
174
  all[c->_name] = c;
4,497✔
175
  c = new ColumnStart();
4,497✔
176
  all[c->_name] = c;
4,497✔
177
  c = new ColumnStatus();
4,497✔
178
  all[c->_name] = c;
4,497✔
179
  c = new ColumnTags();
4,497✔
180
  all[c->_name] = c;
4,497✔
181
  c = new ColumnTemplate();
4,497✔
182
  all[c->_name] = c;
4,497✔
183
  c = new ColumnUntil();
4,497✔
184
  all[c->_name] = c;
4,497✔
185
  c = new ColumnUrgency();
4,497✔
186
  all[c->_name] = c;
4,497✔
187
  c = new ColumnUUID();
4,497✔
188
  all[c->_name] = c;
4,497✔
189
  c = new ColumnWait();
4,497✔
190
  all[c->_name] = c;
4,497✔
191

192
  Column::uda(all);
4,497✔
193
}
4,497✔
194

195
////////////////////////////////////////////////////////////////////////////////
196
void Column::uda(std::map<std::string, Column*>& all) {
4,497✔
197
  // For each UDA, instantiate and initialize ColumnUDA.
198
  std::set<std::string> udas;
4,497✔
199

200
  for (const auto& i : Context::getContext().config) {
1,110,096✔
201
    if (i.first.substr(0, 4) == "uda.") {
1,105,599✔
202
      std::string::size_type period = 4;  // One byte after the first '.'.
14,646✔
203

204
      if ((period = i.first.find('.', period)) != std::string::npos)
14,646✔
205
        udas.insert(i.first.substr(4, period - 4));
14,646✔
206
    }
207
  }
208

209
  for (const auto& uda : udas) {
9,770✔
210
    if (all.find(uda) != all.end())
5,273✔
UNCOV
211
      throw format("The UDA named '{1}' is the same as a core attribute, and is not permitted.",
×
212
                   uda);
×
213

214
    Column* c = Column::uda(uda);
5,273✔
215
    if (c) all[c->_name] = c;
5,273✔
216
  }
217
}
4,497✔
218

219
////////////////////////////////////////////////////////////////////////////////
220
Column* Column::uda(const std::string& name) {
5,750✔
221
  auto type = Context::getContext().config.get("uda." + name + ".type");
11,500✔
222
  auto label = Context::getContext().config.get("uda." + name + ".label");
11,500✔
223
  auto values = Context::getContext().config.get("uda." + name + ".values");
11,500✔
224

225
  if (type == "string") {
5,750✔
226
    auto c = new ColumnUDAString();
5,218✔
227
    c->_name = name;
5,218✔
228
    c->_label = label;
5,218✔
229
    if (values != "") c->_values = split(values, ',');
5,218✔
230
    return c;
5,218✔
231
  } else if (type == "numeric") {
532✔
232
    auto c = new ColumnUDANumeric();
214✔
233
    c->_name = name;
214✔
234
    c->_label = label;
214✔
235
    if (values != "") c->_values = split(values, ',');
214✔
236
    return c;
214✔
237
  } else if (type == "date") {
318✔
238
    auto c = new ColumnUDADate();
125✔
239
    c->_name = name;
125✔
240
    c->_label = label;
125✔
241
    if (values != "") c->_values = split(values, ',');
125✔
242
    return c;
125✔
243
  } else if (type == "duration") {
193✔
244
    auto c = new ColumnUDADuration();
119✔
245
    c->_name = name;
119✔
246
    c->_label = label;
119✔
247
    if (values != "") c->_values = split(values, ',');
119✔
248
    return c;
119✔
249
  } else if (type != "")
74✔
UNCOV
250
    throw std::string(
×
251
        "User defined attributes may only be of type 'string', 'date', 'duration' or 'numeric'.");
×
252

253
  return nullptr;
74✔
254
}
5,750✔
255

256
////////////////////////////////////////////////////////////////////////////////
257
Column::Column()
116,156✔
258
    : _name(""),
116,156✔
259
      _type("string"),
116,156✔
260
      _style("default"),
116,156✔
261
      _label(""),
116,156✔
262
      _report(""),
116,156✔
263
      _modifiable(true),
116,156✔
264
      _uda(false),
116,156✔
265
      _fixed_width(false) {}
232,312✔
266

267
////////////////////////////////////////////////////////////////////////////////
268
void Column::renderHeader(std::vector<std::string>& lines, int width, Color& color) {
2,736✔
269
  if (Context::getContext().verbose("label") && _label != "") {
2,736✔
270
    // Create a basic label.
271
    std::string header;
1,989✔
272
    header.reserve(width);
1,989✔
273
    header = _label;
1,989✔
274

275
    // Create a fungible copy.
276
    Color c = color;
1,989✔
277

278
    // Now underline the header, or add a dashed line.
279
    if (Context::getContext().color() && Context::getContext().config.getBoolean("fontunderline")) {
1,989✔
280
      c.blend(Color(Color::nocolor, Color::nocolor, true, false, false));
89✔
281
      lines.push_back(c.colorize(leftJustify(header, width)));
89✔
282
    } else {
283
      lines.push_back(c.colorize(leftJustify(header, width)));
1,900✔
284
      lines.push_back(c.colorize(std::string(width, '-')));
1,900✔
285
    }
286
  }
1,989✔
287
}
2,736✔
288

289
////////////////////////////////////////////////////////////////////////////////
290
void Column::setStyle(const std::string& style) {
7,525✔
291
  if (style != "default" && std::find(_styles.begin(), _styles.end(), style) == _styles.end())
7,525✔
292
    throw format("Unrecognized column format '{1}.{2}'", _name, style);
11✔
293

294
  _style = style;
7,514✔
295
}
7,514✔
296

297
////////////////////////////////////////////////////////////////////////////////
298
// All integer values are right-justified.
299
void Column::renderInteger(std::vector<std::string>& lines, int width, Color& color, int value) {
1,196✔
300
  lines.push_back(color.colorize(rightJustify(value, width)));
1,196✔
301
}
1,196✔
302

303
////////////////////////////////////////////////////////////////////////////////
304
// All floating point values are right-justified.
305
void Column::renderDouble(std::vector<std::string>& lines, int width, Color& color, double value) {
472✔
306
  lines.push_back(color.colorize(rightJustify(format(value, 4, 3), width)));
472✔
307
}
472✔
308

309
////////////////////////////////////////////////////////////////////////////////
310
void Column::renderStringLeft(std::vector<std::string>& lines, int width, Color& color,
2,608✔
311
                              const std::string& value) {
312
  lines.push_back(color.colorize(leftJustify(value, width)));
2,608✔
313
}
2,608✔
314

315
////////////////////////////////////////////////////////////////////////////////
316
void Column::renderStringRight(std::vector<std::string>& lines, int width, Color& color,
1,291✔
317
                               const std::string& value) {
318
  lines.push_back(color.colorize(rightJustify(value, width)));
1,291✔
319
}
1,291✔
320

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

© 2026 Coveralls, Inc