doing 2.1.91 → 2.1.94
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +30 -0
- data/Gemfile.lock +1 -1
- data/README.md +1 -1
- data/bin/commands/budget.rb +85 -0
- data/bin/doing +1 -1
- data/docs/doc/Array.html +3 -3
- data/docs/doc/BooleanTermParser/Clause.html +3 -3
- data/docs/doc/BooleanTermParser/Operator.html +3 -3
- data/docs/doc/BooleanTermParser/Query.html +3 -3
- data/docs/doc/BooleanTermParser/QueryParser.html +3 -3
- data/docs/doc/BooleanTermParser/QueryTransformer.html +3 -3
- data/docs/doc/BooleanTermParser.html +3 -3
- data/docs/doc/Doing/ArrayCleanup.html +3 -3
- data/docs/doc/Doing/ArrayNestedHash.html +3 -3
- data/docs/doc/Doing/ArrayTags.html +3 -3
- data/docs/doc/Doing/ByDayExport.html +3 -3
- data/docs/doc/Doing/CSVExport.html +3 -3
- data/docs/doc/Doing/CalendarImport.html +3 -3
- data/docs/doc/Doing/Change.html +3 -3
- data/docs/doc/Doing/Changes.html +3 -3
- data/docs/doc/Doing/ChronifyArray.html +3 -3
- data/docs/doc/Doing/ChronifyNumeric.html +3 -3
- data/docs/doc/Doing/ChronifyString.html +3 -3
- data/docs/doc/Doing/Color.html +3 -3
- data/docs/doc/Doing/Completion/BashCompletions.html +3 -3
- data/docs/doc/Doing/Completion/FigCompletions.html +3 -3
- data/docs/doc/Doing/Completion/FishCompletions.html +3 -3
- data/docs/doc/Doing/Completion/StringUtils.html +3 -3
- data/docs/doc/Doing/Completion/ZshCompletions.html +3 -3
- data/docs/doc/Doing/Completion.html +3 -3
- data/docs/doc/Doing/Configuration.html +5 -3
- data/docs/doc/Doing/DayOneRenderer.html +3 -3
- data/docs/doc/Doing/DayoneExport.html +3 -3
- data/docs/doc/Doing/DoingExport.html +3 -3
- data/docs/doc/Doing/DoingImport.html +3 -3
- data/docs/doc/Doing/Entry.html +3 -3
- data/docs/doc/Doing/Errors/DoingNoTraceError.html +3 -3
- data/docs/doc/Doing/Errors/DoingRuntimeError.html +3 -3
- data/docs/doc/Doing/Errors/DoingStandardError.html +3 -3
- data/docs/doc/Doing/Errors/EmptyInput.html +3 -3
- data/docs/doc/Doing/Errors/HistoryLimitError.html +3 -3
- data/docs/doc/Doing/Errors/InvalidPlugin.html +3 -3
- data/docs/doc/Doing/Errors/MissingBackupFile.html +3 -3
- data/docs/doc/Doing/Errors/NoResults.html +3 -3
- data/docs/doc/Doing/Errors/PluginException.html +3 -3
- data/docs/doc/Doing/Errors/UserCancelled.html +3 -3
- data/docs/doc/Doing/Errors/WrongCommand.html +3 -3
- data/docs/doc/Doing/Errors.html +3 -3
- data/docs/doc/Doing/HTMLExport.html +3 -3
- data/docs/doc/Doing/Hooks.html +3 -3
- data/docs/doc/Doing/Item.html +3 -3
- data/docs/doc/Doing/ItemDates.html +3 -3
- data/docs/doc/Doing/ItemQuery.html +3 -3
- data/docs/doc/Doing/ItemState.html +3 -3
- data/docs/doc/Doing/ItemTags.html +3 -3
- data/docs/doc/Doing/Items.html +3 -3
- data/docs/doc/Doing/JSONExport.html +3 -3
- data/docs/doc/Doing/JSONImport.html +3 -3
- data/docs/doc/Doing/Logger.html +3 -3
- data/docs/doc/Doing/MarkdownExport.html +3 -3
- data/docs/doc/Doing/Note.html +3 -3
- data/docs/doc/Doing/Pager.html +3 -3
- data/docs/doc/Doing/Plugins.html +3 -3
- data/docs/doc/Doing/Prompt.html +3 -3
- data/docs/doc/Doing/PromptChoose.html +3 -3
- data/docs/doc/Doing/PromptFZF.html +3 -3
- data/docs/doc/Doing/PromptInput.html +3 -3
- data/docs/doc/Doing/PromptSTD.html +3 -3
- data/docs/doc/Doing/PromptYN.html +3 -3
- data/docs/doc/Doing/Section.html +3 -3
- data/docs/doc/Doing/StringHighlight.html +3 -3
- data/docs/doc/Doing/StringNormalize.html +3 -3
- data/docs/doc/Doing/StringQuery.html +3 -3
- data/docs/doc/Doing/StringTags.html +3 -3
- data/docs/doc/Doing/StringTransform.html +3 -3
- data/docs/doc/Doing/StringTruncate.html +3 -3
- data/docs/doc/Doing/StringURL.html +3 -3
- data/docs/doc/Doing/SymbolNormalize.html +3 -3
- data/docs/doc/Doing/TaskPaperExport.html +3 -3
- data/docs/doc/Doing/TemplateExport.html +3 -3
- data/docs/doc/Doing/TemplateString.html +3 -3
- data/docs/doc/Doing/TimingImport.html +3 -3
- data/docs/doc/Doing/Types.html +3 -3
- data/docs/doc/Doing/Util/Backup.html +3 -3
- data/docs/doc/Doing/Util.html +3 -3
- data/docs/doc/Doing/Version.html +3 -3
- data/docs/doc/Doing/WWID.html +3 -3
- data/docs/doc/Doing.html +4 -4
- data/docs/doc/FalseClass.html +3 -3
- data/docs/doc/GLI/Commands/Help.html +3 -3
- data/docs/doc/GLI/Commands/MarkdownDocumentListener.html +3 -3
- data/docs/doc/GLI/Commands.html +3 -3
- data/docs/doc/GLI.html +3 -3
- data/docs/doc/Hash.html +3 -3
- data/docs/doc/Numeric.html +3 -3
- data/docs/doc/Object.html +3 -3
- data/docs/doc/PhraseParser/Operator.html +3 -3
- data/docs/doc/PhraseParser/PhraseClause.html +3 -3
- data/docs/doc/PhraseParser/Query.html +3 -3
- data/docs/doc/PhraseParser/QueryParser.html +3 -3
- data/docs/doc/PhraseParser/QueryTransformer.html +3 -3
- data/docs/doc/PhraseParser/TermClause.html +3 -3
- data/docs/doc/PhraseParser.html +3 -3
- data/docs/doc/Status.html +3 -3
- data/docs/doc/String.html +3 -3
- data/docs/doc/Symbol.html +3 -3
- data/docs/doc/Time.html +3 -3
- data/docs/doc/TrueClass.html +3 -3
- data/docs/doc/_index.html +4 -4
- data/docs/doc/css/style.css +2 -15
- data/docs/doc/file.README.html +4 -4
- data/docs/doc/frames.html +1 -1
- data/docs/doc/index.html +4 -4
- data/docs/doc/js/app.js +91 -40
- data/docs/doc/js/full_list.js +22 -20
- data/docs/doc/top-level-namespace.html +3 -3
- data/doing.rdoc +17 -1
- data/lib/completion/_doing.zsh +4 -0
- data/lib/completion/doing.bash +11 -0
- data/lib/completion/doing.fish +2 -0
- data/lib/completion/doing.ts +14 -0
- data/lib/doing/configuration.rb +4 -2
- data/lib/doing/plugins/export/byday.rb +39 -0
- data/lib/doing/version.rb +1 -1
- data/lib/doing/wwid/modify.rb +2 -1
- data/lib/doing/wwid/timers.rb +65 -8
- metadata +3 -2
data/docs/doc/js/app.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
window.__app = function () {
|
|
2
2
|
var localStorage = {},
|
|
3
3
|
sessionStorage = {};
|
|
4
4
|
try {
|
|
@@ -153,7 +153,6 @@
|
|
|
153
153
|
});
|
|
154
154
|
// Add the value of the constant as "Tooltip" to the summary object
|
|
155
155
|
list.find("pre.code").each(function () {
|
|
156
|
-
console.log($(this).parent());
|
|
157
156
|
var dt_element = $(this).parent().prev();
|
|
158
157
|
var tooltip = $(this).text();
|
|
159
158
|
if (dt_element.hasClass("deprecated")) {
|
|
@@ -250,37 +249,46 @@
|
|
|
250
249
|
);
|
|
251
250
|
}
|
|
252
251
|
|
|
253
|
-
function navResizeFn(e) {
|
|
254
|
-
if (e.which !== 1) {
|
|
255
|
-
navResizeFnStop();
|
|
256
|
-
return;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
sessionStorage.navWidth = e.pageX.toString();
|
|
260
|
-
$(".nav_wrap").css("width", e.pageX);
|
|
261
|
-
$(".nav_wrap").css("-ms-flex", "inherit");
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
function navResizeFnStop() {
|
|
265
|
-
$(window).unbind("mousemove", navResizeFn);
|
|
266
|
-
window.removeEventListener("message", navMessageFn, false);
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
function navMessageFn(e) {
|
|
270
|
-
if (e.data.action === "mousemove") navResizeFn(e.data.event);
|
|
271
|
-
if (e.data.action === "mouseup") navResizeFnStop();
|
|
272
|
-
}
|
|
273
|
-
|
|
274
252
|
function navResizer() {
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
253
|
+
const resizer = document.getElementById("resizer");
|
|
254
|
+
resizer.addEventListener(
|
|
255
|
+
"pointerdown",
|
|
256
|
+
function (e) {
|
|
257
|
+
resizer.setPointerCapture(e.pointerId);
|
|
258
|
+
e.preventDefault();
|
|
259
|
+
e.stopPropagation();
|
|
260
|
+
},
|
|
261
|
+
false
|
|
262
|
+
);
|
|
263
|
+
resizer.addEventListener(
|
|
264
|
+
"pointerup",
|
|
265
|
+
function (e) {
|
|
266
|
+
resizer.releasePointerCapture(e.pointerId);
|
|
267
|
+
e.preventDefault();
|
|
268
|
+
e.stopPropagation();
|
|
269
|
+
},
|
|
270
|
+
false
|
|
271
|
+
);
|
|
272
|
+
resizer.addEventListener(
|
|
273
|
+
"pointermove",
|
|
274
|
+
function (e) {
|
|
275
|
+
if ((e.buttons & 1) === 0) {
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
sessionStorage.navWidth = e.pageX.toString();
|
|
280
|
+
$(".nav_wrap").css("width", Math.max(200, e.pageX));
|
|
281
|
+
e.preventDefault();
|
|
282
|
+
e.stopPropagation();
|
|
283
|
+
},
|
|
284
|
+
false
|
|
285
|
+
);
|
|
281
286
|
|
|
282
287
|
if (sessionStorage.navWidth) {
|
|
283
|
-
|
|
288
|
+
$(".nav_wrap").css(
|
|
289
|
+
"width",
|
|
290
|
+
Math.max(200, parseInt(sessionStorage.navWidth, 10))
|
|
291
|
+
);
|
|
284
292
|
}
|
|
285
293
|
}
|
|
286
294
|
|
|
@@ -295,15 +303,6 @@
|
|
|
295
303
|
document.getElementById("nav").contentWindow.postMessage(opts, "*");
|
|
296
304
|
done = true;
|
|
297
305
|
}
|
|
298
|
-
|
|
299
|
-
window.addEventListener(
|
|
300
|
-
"message",
|
|
301
|
-
function (event) {
|
|
302
|
-
if (event.data === "navReady") postMessage();
|
|
303
|
-
return false;
|
|
304
|
-
},
|
|
305
|
-
false
|
|
306
|
-
);
|
|
307
306
|
}
|
|
308
307
|
|
|
309
308
|
function mainFocus() {
|
|
@@ -341,4 +340,56 @@
|
|
|
341
340
|
mainFocus();
|
|
342
341
|
navigationChange();
|
|
343
342
|
});
|
|
344
|
-
}
|
|
343
|
+
};
|
|
344
|
+
window.__app();
|
|
345
|
+
|
|
346
|
+
window.addEventListener(
|
|
347
|
+
"message",
|
|
348
|
+
async (e) => {
|
|
349
|
+
if (e.data.action === "navigate") {
|
|
350
|
+
const response = await fetch(e.data.url);
|
|
351
|
+
const text = await response.text();
|
|
352
|
+
const parser = new DOMParser();
|
|
353
|
+
const doc = parser.parseFromString(text, "text/html");
|
|
354
|
+
|
|
355
|
+
const classListLink =
|
|
356
|
+
document.getElementById("class_list_link").classList;
|
|
357
|
+
|
|
358
|
+
const content = doc.querySelector("#main").innerHTML;
|
|
359
|
+
document.querySelector("#main").innerHTML = content;
|
|
360
|
+
document.title = doc.head.querySelector("title").innerText;
|
|
361
|
+
document.head.querySelectorAll("script").forEach((script) => {
|
|
362
|
+
if (
|
|
363
|
+
!script.type ||
|
|
364
|
+
(script.type.includes("text/javascript") && !script.src)
|
|
365
|
+
) {
|
|
366
|
+
script.remove();
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
doc.head.querySelectorAll("script").forEach((script) => {
|
|
371
|
+
if (
|
|
372
|
+
!script.type ||
|
|
373
|
+
(script.type.includes("text/javascript") && !script.src)
|
|
374
|
+
) {
|
|
375
|
+
const newScript = document.createElement("script");
|
|
376
|
+
newScript.type = "text/javascript";
|
|
377
|
+
newScript.textContent = script.textContent;
|
|
378
|
+
document.head.appendChild(newScript);
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
window.__app();
|
|
383
|
+
|
|
384
|
+
document.getElementById("class_list_link").classList = classListLink;
|
|
385
|
+
|
|
386
|
+
const url = new URL(e.data.url, "https://localhost");
|
|
387
|
+
const hash = decodeURIComponent(url.hash ?? "");
|
|
388
|
+
if (hash) {
|
|
389
|
+
document.getElementById(hash.substring(1)).scrollIntoView();
|
|
390
|
+
}
|
|
391
|
+
history.pushState({}, document.title, e.data.url);
|
|
392
|
+
}
|
|
393
|
+
},
|
|
394
|
+
false
|
|
395
|
+
);
|
data/docs/doc/js/full_list.js
CHANGED
|
@@ -20,17 +20,6 @@ function escapeShortcut() {
|
|
|
20
20
|
});
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
function navResizer() {
|
|
24
|
-
$(window).mousemove(function(e) {
|
|
25
|
-
window.parent.postMessage({
|
|
26
|
-
action: 'mousemove', event: {pageX: e.pageX, which: e.which}
|
|
27
|
-
}, '*');
|
|
28
|
-
}).mouseup(function(e) {
|
|
29
|
-
window.parent.postMessage({action: 'mouseup'}, '*');
|
|
30
|
-
});
|
|
31
|
-
window.parent.postMessage("navReady", "*");
|
|
32
|
-
}
|
|
33
|
-
|
|
34
23
|
function clearSearchTimeout() {
|
|
35
24
|
clearTimeout(searchTimeout);
|
|
36
25
|
searchTimeout = null;
|
|
@@ -44,14 +33,21 @@ function enableLinks() {
|
|
|
44
33
|
$clicked.addClass('clicked');
|
|
45
34
|
evt.stopPropagation();
|
|
46
35
|
|
|
47
|
-
if (
|
|
36
|
+
if (window.origin === "null") {
|
|
37
|
+
if (evt.target.tagName === 'A') return true;
|
|
48
38
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
39
|
+
var elem = $clicked.find('> .item .object_link a')[0];
|
|
40
|
+
var e = evt.originalEvent;
|
|
41
|
+
var newEvent = new MouseEvent(evt.originalEvent.type);
|
|
42
|
+
newEvent.initMouseEvent(e.type, e.canBubble, e.cancelable, e.view, e.detail, e.screenX, e.screenY, e.clientX, e.clientY, e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, e.button, e.relatedTarget);
|
|
43
|
+
elem.dispatchEvent(newEvent);
|
|
44
|
+
evt.preventDefault();
|
|
45
|
+
} else {
|
|
46
|
+
window.top.postMessage({
|
|
47
|
+
action: "navigate",
|
|
48
|
+
url: $clicked.find('.object_link a').attr('href'),
|
|
49
|
+
}, "*");
|
|
50
|
+
}
|
|
55
51
|
return false;
|
|
56
52
|
});
|
|
57
53
|
}
|
|
@@ -199,6 +195,13 @@ function highlight() {
|
|
|
199
195
|
});
|
|
200
196
|
}
|
|
201
197
|
|
|
198
|
+
function isInView(element) {
|
|
199
|
+
const rect = element.getBoundingClientRect();
|
|
200
|
+
const windowHeight =
|
|
201
|
+
window.innerHeight || document.documentElement.clientHeight;
|
|
202
|
+
return rect.left >= 0 && rect.bottom <= windowHeight;
|
|
203
|
+
}
|
|
204
|
+
|
|
202
205
|
/**
|
|
203
206
|
* Expands the tree to the target element and its immediate
|
|
204
207
|
* children.
|
|
@@ -214,7 +217,7 @@ function expandTo(path) {
|
|
|
214
217
|
$(el).find('> div > a.toggle').attr('aria-expanded', 'true');
|
|
215
218
|
});
|
|
216
219
|
|
|
217
|
-
if($target[0]) {
|
|
220
|
+
if($target[0] && !isInView($target[0])) {
|
|
218
221
|
window.scrollTo(window.scrollX, $target.offset().top - 250);
|
|
219
222
|
highlight();
|
|
220
223
|
}
|
|
@@ -232,7 +235,6 @@ window.addEventListener("message", windowEvents, false);
|
|
|
232
235
|
|
|
233
236
|
$(document).ready(function() {
|
|
234
237
|
escapeShortcut();
|
|
235
|
-
navResizer();
|
|
236
238
|
enableLinks();
|
|
237
239
|
enableToggles();
|
|
238
240
|
populateSearchCache();
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<title>
|
|
7
7
|
Top Level Namespace
|
|
8
8
|
|
|
9
|
-
— Documentation by YARD 0.9.
|
|
9
|
+
— Documentation by YARD 0.9.38
|
|
10
10
|
|
|
11
11
|
</title>
|
|
12
12
|
|
|
@@ -216,9 +216,9 @@
|
|
|
216
216
|
</div>
|
|
217
217
|
|
|
218
218
|
<div id="footer">
|
|
219
|
-
Generated on Fri
|
|
219
|
+
Generated on Fri Feb 13 07:19:08 2026 by
|
|
220
220
|
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
|
221
|
-
0.9.
|
|
221
|
+
0.9.38 (ruby-3.4.4).
|
|
222
222
|
</div>
|
|
223
223
|
|
|
224
224
|
</div>
|
data/doing.rdoc
CHANGED
|
@@ -5,7 +5,7 @@ record of what you've been doing, complete with tag-based time tracking. The
|
|
|
5
5
|
command line tool allows you to add entries, annotate with tags and notes, and
|
|
6
6
|
view your entries with myriad options, with a focus on a "natural" language syntax.
|
|
7
7
|
|
|
8
|
-
v2.1.
|
|
8
|
+
v2.1.94
|
|
9
9
|
|
|
10
10
|
=== Global Options
|
|
11
11
|
=== --config_file arg
|
|
@@ -346,6 +346,22 @@ Autotag last entry (or entries) not marked @done
|
|
|
346
346
|
|
|
347
347
|
|
|
348
348
|
|
|
349
|
+
==== Command: <tt>budget [TAG [AMOUNT]]</tt>
|
|
350
|
+
Set, list, and remove tag time budgets
|
|
351
|
+
|
|
352
|
+
Manage simple time budgets for tags.
|
|
353
|
+
|
|
354
|
+
Run without arguments to list configured budgets.
|
|
355
|
+
|
|
356
|
+
Use `doing budget TAG AMOUNT` to set a budget (e.g. `doing budget dev 100h`).
|
|
357
|
+
|
|
358
|
+
Use `doing budget TAG --remove` to delete a budget.
|
|
359
|
+
===== Options
|
|
360
|
+
===== -r|--remove
|
|
361
|
+
Delete specified tag budget
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
|
|
349
365
|
==== Command: <tt>cancel COUNT</tt>
|
|
350
366
|
End last X entries with no time tracked
|
|
351
367
|
|
data/lib/completion/_doing.zsh
CHANGED
|
@@ -12,6 +12,7 @@ function _doing() {
|
|
|
12
12
|
'archive:Move entries between sections'
|
|
13
13
|
'move:Move entries between sections'
|
|
14
14
|
'autotag:Autotag last entry or filtered entries'
|
|
15
|
+
'budget:Set'
|
|
15
16
|
'cancel:End last X entries with no time tracked'
|
|
16
17
|
'changes:List recent changes in Doing'
|
|
17
18
|
'changelog:List recent changes in Doing'
|
|
@@ -82,6 +83,9 @@ function _doing() {
|
|
|
82
83
|
autotag)
|
|
83
84
|
args=( "--bool[Boolean]:BOOLEAN:" {'(--count)-c','(-c)--count'}"[How many recent entries to autotag]:COUNT:" "--force[Don't ask permission to autotag all entries when count is 0]" {'(--interactive)-i','(-i)--interactive'}"[Select item(s) to tag from a menu of matching entries]" {'(--section)-s','(-s)--section'}"[Section]:SECTION_NAME:" "--search[Autotag entries matching search filter]:QUERY:" "--tag[Autotag the last X entries containing TAG]:TAG:" {'(--unfinished)-u','(-u)--unfinished'}"[Autotag last entry]" )
|
|
84
85
|
;;
|
|
86
|
+
budget)
|
|
87
|
+
args=( {'(--remove)-r','(-r)--remove'}"[Delete specified tag budget]" )
|
|
88
|
+
;;
|
|
85
89
|
cancel)
|
|
86
90
|
args=( {'(--archive)-a','(-a)--archive'}"[Archive entries]" "--bool[Boolean used to combine multiple tags]:BOOLEAN:" "--case[Case sensitivity for search string matching [(c)ase-sensitive]:TYPE:" {'(--interactive)-i','(-i)--interactive'}"[Select item(s) to cancel from a menu of matching entries]" "--not[Cancel items that *don't* match search/tag filters]" {'(--section)-s','(-s)--section'}"[Section]:NAME:" "--search[Filter entries using a search query]:QUERY:" "--tag[Filter entries by tag]:TAG:" {'(--unfinished)-u','(-u)--unfinished'}"[Cancel last entry]" "--val[Perform a tag value query]:QUERY:" {'(--exact)-x','(-x)--exact'}"[Force exact search string matching]" )
|
|
87
91
|
;;
|
data/lib/completion/doing.bash
CHANGED
|
@@ -48,6 +48,16 @@ _doing_autotag() {
|
|
|
48
48
|
fi
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
_doing_budget() {
|
|
52
|
+
|
|
53
|
+
if [[ "$token" == --* ]]; then
|
|
54
|
+
COMPREPLY=( $( compgen -W '--remove' -- $token ) )
|
|
55
|
+
elif [[ "$token" == -* ]]; then
|
|
56
|
+
COMPREPLY=( $( compgen -W '-r --remove' -- $token ) )
|
|
57
|
+
|
|
58
|
+
fi
|
|
59
|
+
}
|
|
60
|
+
|
|
51
61
|
_doing_cancel() {
|
|
52
62
|
|
|
53
63
|
if [[ "$token" == --* ]]; then
|
|
@@ -456,6 +466,7 @@ _doing()
|
|
|
456
466
|
if [[ $last =~ (again|resume) ]]; then _doing_again
|
|
457
467
|
elif [[ $last =~ (archive|move) ]]; then _doing_archive
|
|
458
468
|
elif [[ $last =~ (autotag) ]]; then _doing_autotag
|
|
469
|
+
elif [[ $last =~ (budget) ]]; then _doing_budget
|
|
459
470
|
elif [[ $last =~ (cancel) ]]; then _doing_cancel
|
|
460
471
|
elif [[ $last =~ (changes|changelog) ]]; then _doing_changes
|
|
461
472
|
elif [[ $last =~ (completion) ]]; then _doing_completion
|
data/lib/completion/doing.fish
CHANGED
|
@@ -138,6 +138,7 @@ __fish_doing_complete_args tag
|
|
|
138
138
|
complete -xc doing -n '__fish_doing_needs_command' -a 'again resume' -d Repeat\ last\ entry\ as\ new\ entry
|
|
139
139
|
complete -xc doing -n '__fish_doing_needs_command' -a 'archive move' -d Move\ entries\ between\ sections
|
|
140
140
|
complete -xc doing -n '__fish_doing_needs_command' -a 'autotag' -d Autotag\ last\ entry\ or\ filtered\ entries
|
|
141
|
+
complete -xc doing -n '__fish_doing_needs_command' -a 'budget' -d Set
|
|
141
142
|
complete -xc doing -n '__fish_doing_needs_command' -a 'cancel' -d End\ last\ X\ entries\ with\ no\ time\ tracked
|
|
142
143
|
complete -xc doing -n '__fish_doing_needs_command' -a 'changes changelog' -d List\ recent\ changes\ in\ Doing
|
|
143
144
|
complete -xc doing -n '__fish_doing_needs_command' -a 'colors' -d List\ available\ color\ variables\ for\ configuration\ templates\ and\ views
|
|
@@ -214,6 +215,7 @@ complete -c doing -l section -s s -f -r -n '__fish_doing_using_command autotag'
|
|
|
214
215
|
complete -c doing -l search -f -r -n '__fish_doing_using_command autotag' -d Autotag\ entries\ matching\ search\ filter
|
|
215
216
|
complete -c doing -l tag -f -r -n '__fish_doing_using_command autotag' -d Autotag\ the\ last\ X\ entries\ containing\ TAG
|
|
216
217
|
complete -c doing -l unfinished -s u -f -n '__fish_doing_using_command autotag' -d Autotag\ last\ entry
|
|
218
|
+
complete -c doing -l remove -s r -f -n '__fish_doing_using_command budget' -d Delete\ specified\ tag\ budget
|
|
217
219
|
complete -c doing -l archive -s a -f -n '__fish_doing_using_command cancel' -d Archive\ entries
|
|
218
220
|
complete -c doing -l bool -f -r -n '__fish_doing_using_command cancel' -d Boolean\ used\ to\ combine\ multiple\ tags
|
|
219
221
|
complete -c doing -l case -f -r -n '__fish_doing_using_command cancel' -d Case\ sensitivity\ for\ search\ string\ matching\ \[\(c\)ase-sensitive
|
data/lib/completion/doing.ts
CHANGED
|
@@ -598,6 +598,20 @@ const completionSpec: Fig.Spec = {
|
|
|
598
598
|
|
|
599
599
|
},
|
|
600
600
|
|
|
601
|
+
{
|
|
602
|
+
name: "budget",
|
|
603
|
+
description: "Set",
|
|
604
|
+
options: [
|
|
605
|
+
{
|
|
606
|
+
name: ["-r", "--remove"],
|
|
607
|
+
description: "Delete specified tag budget",
|
|
608
|
+
|
|
609
|
+
},
|
|
610
|
+
|
|
611
|
+
],
|
|
612
|
+
|
|
613
|
+
},
|
|
614
|
+
|
|
601
615
|
{
|
|
602
616
|
name: "cancel",
|
|
603
617
|
description: "End last X entries with no time tracked",
|
data/lib/doing/configuration.rb
CHANGED
|
@@ -41,6 +41,8 @@ module Doing
|
|
|
41
41
|
'never_finish' => [],
|
|
42
42
|
'date_tags' => ['done', 'defer(?:red)?', 'waiting'],
|
|
43
43
|
|
|
44
|
+
'budgets' => {},
|
|
45
|
+
|
|
44
46
|
'timer_format' => 'text',
|
|
45
47
|
'interval_format' => 'text',
|
|
46
48
|
|
|
@@ -175,8 +177,8 @@ module Doing
|
|
|
175
177
|
|
|
176
178
|
return @config_file if @force_answer
|
|
177
179
|
|
|
178
|
-
if
|
|
179
|
-
choices = [@config_file].concat(
|
|
180
|
+
if additional_configs&.count&.positive? || create
|
|
181
|
+
choices = [@config_file].concat(additional_configs)
|
|
180
182
|
choices.push('Create a new .doingrc in the current directory') if create && !File.exist?('.doingrc')
|
|
181
183
|
res = Doing::Prompt.choose_from(choices.uniq.sort.reverse,
|
|
182
184
|
sorted: false,
|
|
@@ -28,6 +28,7 @@ module Doing
|
|
|
28
28
|
|
|
29
29
|
totals = {}
|
|
30
30
|
total = 0
|
|
31
|
+
tag_totals = Hash.new(0)
|
|
31
32
|
|
|
32
33
|
days.each do |day, day_items|
|
|
33
34
|
day_items.each do |item|
|
|
@@ -35,8 +36,40 @@ module Doing
|
|
|
35
36
|
duration = item.interval || 0
|
|
36
37
|
totals[day] += duration
|
|
37
38
|
total += duration
|
|
39
|
+
|
|
40
|
+
item.title.scan(/(?mi)@(\S+?)(\(.*\))?(?=\s|$)/).each do |m|
|
|
41
|
+
tag = m[0].downcase
|
|
42
|
+
next if tag == 'done'
|
|
43
|
+
|
|
44
|
+
tag_totals[tag] += duration
|
|
45
|
+
end
|
|
38
46
|
end
|
|
39
47
|
end
|
|
48
|
+
|
|
49
|
+
budgets = Doing.setting('budgets', {}) || {}
|
|
50
|
+
budgets = budgets.transform_keys { |k| k.to_s.downcase }
|
|
51
|
+
budgets_total = 0
|
|
52
|
+
|
|
53
|
+
budget_fmt = lambda do |secs|
|
|
54
|
+
secs = secs.to_i
|
|
55
|
+
return '0h' if secs <= 0
|
|
56
|
+
|
|
57
|
+
minutes = (secs / 60).to_i
|
|
58
|
+
hours = (minutes / 60).to_i
|
|
59
|
+
mins = (minutes % 60).to_i
|
|
60
|
+
return format('%<h>dh', h: hours) if mins.zero?
|
|
61
|
+
return format('%<m>dm', m: mins) if hours.zero?
|
|
62
|
+
|
|
63
|
+
format('%<h>dh%<m>dm', h: hours, m: mins)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
budgets.each do |tag, budget_secs|
|
|
67
|
+
used = tag_totals[tag].to_i
|
|
68
|
+
remaining = budget_secs.to_i - used
|
|
69
|
+
remaining = 0 if remaining.negative?
|
|
70
|
+
budgets_total += remaining
|
|
71
|
+
end
|
|
72
|
+
|
|
40
73
|
width = wwid.config['plugins']['byday']['item_width'].to_i || 60
|
|
41
74
|
divider = "{wd}+{xk}#{'-' * 10}{wd}+{xk}#{'-' * width}{wd}+{xk}#{'-' * 8}{wd}+{x}"
|
|
42
75
|
out = []
|
|
@@ -54,11 +87,17 @@ module Doing
|
|
|
54
87
|
out << "{wd}| |{xbw}#{title}{wd}|{xy}#{interval}{wd}|{x}"
|
|
55
88
|
end
|
|
56
89
|
day_total = "Total: #{totals[day].time_string(format: :clock)}"
|
|
90
|
+
if budgets_total.positive?
|
|
91
|
+
day_total += " (total budgets left #{budget_fmt.call(budgets_total)})"
|
|
92
|
+
end
|
|
57
93
|
out << divider
|
|
58
94
|
out << "{wd}|{xg}#{day_total.rjust(width + 20)}{wd}|{x}"
|
|
59
95
|
out << divider
|
|
60
96
|
end
|
|
61
97
|
all_total = "Grand Total: #{total.time_string(format: :clock)}"
|
|
98
|
+
if budgets_total.positive?
|
|
99
|
+
all_total += " (total budgets left #{budget_fmt.call(budgets_total)})"
|
|
100
|
+
end
|
|
62
101
|
out << "{wd}|{xrb}#{all_total.rjust(width + 20)}{wd}|{x}"
|
|
63
102
|
out << divider
|
|
64
103
|
Doing::Color.template(out.join("\n"))
|
data/lib/doing/version.rb
CHANGED
data/lib/doing/wwid/modify.rb
CHANGED
|
@@ -186,7 +186,8 @@ module Doing
|
|
|
186
186
|
items = filter_items(Items.new, opt: opt)
|
|
187
187
|
|
|
188
188
|
if opt[:interactive]
|
|
189
|
-
|
|
189
|
+
include_section = Array(opt[:section]).any? { |sec| sec.to_s =~ /^all$/i }
|
|
190
|
+
items = Prompt.choose_from_items(items, include_section: include_section, menu: true,
|
|
190
191
|
header: '',
|
|
191
192
|
prompt: 'Select entries to tag > ',
|
|
192
193
|
multiple: true,
|
data/lib/doing/wwid/timers.rb
CHANGED
|
@@ -16,6 +16,34 @@ module Doing
|
|
|
16
16
|
|
|
17
17
|
@timers.delete('meanwhile')
|
|
18
18
|
|
|
19
|
+
timers_snapshot = @timers.dup
|
|
20
|
+
|
|
21
|
+
budgets = Doing.setting('budgets', {}) || {}
|
|
22
|
+
budgets = budgets.transform_keys { |k| k.to_s.downcase }
|
|
23
|
+
remaining_map = {}
|
|
24
|
+
budgets_total = 0
|
|
25
|
+
|
|
26
|
+
budget_fmt = lambda do |secs|
|
|
27
|
+
secs = secs.to_i
|
|
28
|
+
return '0h' if secs <= 0
|
|
29
|
+
|
|
30
|
+
minutes = (secs / 60).to_i
|
|
31
|
+
hours = (minutes / 60).to_i
|
|
32
|
+
mins = (minutes % 60).to_i
|
|
33
|
+
return format('%dh', hours) if mins.zero?
|
|
34
|
+
return format('%dm', mins) if hours.zero?
|
|
35
|
+
|
|
36
|
+
format('%dh%dm', hours, mins)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
budgets.each do |tag, budget_secs|
|
|
40
|
+
used = timers_snapshot[tag].to_i
|
|
41
|
+
remaining = budget_secs.to_i - used
|
|
42
|
+
remaining = 0 if remaining.negative?
|
|
43
|
+
remaining_map[tag] = remaining
|
|
44
|
+
budgets_total += remaining
|
|
45
|
+
end
|
|
46
|
+
|
|
19
47
|
max = @timers.keys.sort_by(&:length).reverse[0].length + 1
|
|
20
48
|
|
|
21
49
|
total = @timers.delete('All')
|
|
@@ -47,9 +75,13 @@ module Doing
|
|
|
47
75
|
<tbody>
|
|
48
76
|
EOHEAD
|
|
49
77
|
sorted_tags_data.reverse.each do |k, v|
|
|
50
|
-
|
|
51
|
-
|
|
78
|
+
next unless v.positive?
|
|
79
|
+
|
|
80
|
+
budget_str = ''
|
|
81
|
+
if remaining_map.key?(k) && remaining_map[k].positive?
|
|
82
|
+
budget_str = " (budget left #{budget_fmt.call(remaining_map[k])})"
|
|
52
83
|
end
|
|
84
|
+
output += "<tr><td style='text-align:left;'>#{k}</td><td style='text-align:left;'>#{v.time_string(format: :clock)}#{budget_str}</td></tr>\n"
|
|
53
85
|
end
|
|
54
86
|
tail = <<EOTAIL
|
|
55
87
|
<tr>
|
|
@@ -59,7 +91,7 @@ EOHEAD
|
|
|
59
91
|
<tfoot>
|
|
60
92
|
<tr>
|
|
61
93
|
<td style="text-align:left;"><strong>Total</strong></td>
|
|
62
|
-
<td style="text-align:left;">#{total.time_string(format: :clock)}</td>
|
|
94
|
+
<td style="text-align:left;">#{total.time_string(format: :clock)}#{" (total budgets left #{budget_fmt.call(budgets_total)})" if budgets_total.positive?}</td>
|
|
63
95
|
</tr>
|
|
64
96
|
</tfoot>
|
|
65
97
|
</table>
|
|
@@ -73,7 +105,13 @@ EOTAIL
|
|
|
73
105
|
| #{'-' * (pad - 1)}: | :------- |
|
|
74
106
|
EOHEADER
|
|
75
107
|
sorted_tags_data.reverse.each do |k, v|
|
|
76
|
-
|
|
108
|
+
next unless v.positive?
|
|
109
|
+
|
|
110
|
+
budget_str = ''
|
|
111
|
+
if remaining_map.key?(k) && remaining_map[k].positive?
|
|
112
|
+
budget_str = " (budget left #{budget_fmt.call(remaining_map[k])})"
|
|
113
|
+
end
|
|
114
|
+
output += "| #{' ' * (pad - k.length)}#{k} | #{v.time_string(format: :clock)}#{budget_str} |\n"
|
|
77
115
|
end
|
|
78
116
|
tail = '[Tag Totals]'
|
|
79
117
|
output + tail
|
|
@@ -83,7 +121,10 @@ EOTAIL
|
|
|
83
121
|
output << {
|
|
84
122
|
'tag' => k,
|
|
85
123
|
'seconds' => v,
|
|
86
|
-
'formatted' => v.time_string(format: :clock)
|
|
124
|
+
'formatted' => v.time_string(format: :clock),
|
|
125
|
+
'budget' => budgets[k],
|
|
126
|
+
'remaining' => remaining_map[k],
|
|
127
|
+
'remaining_formatted' => (remaining_map[k] && remaining_map[k].positive? ? budget_fmt.call(remaining_map[k]) : nil)
|
|
87
128
|
}
|
|
88
129
|
end
|
|
89
130
|
output
|
|
@@ -94,7 +135,12 @@ EOTAIL
|
|
|
94
135
|
(max - k.length).times do
|
|
95
136
|
spacer += ' '
|
|
96
137
|
end
|
|
97
|
-
|
|
138
|
+
line = "┃ #{spacer}#{k}:#{v.time_string(format: :hm)}"
|
|
139
|
+
if remaining_map.key?(k) && remaining_map[k].positive?
|
|
140
|
+
line += " (budget left #{budget_fmt.call(remaining_map[k])})"
|
|
141
|
+
end
|
|
142
|
+
line += ' ┃'
|
|
143
|
+
output.push(line)
|
|
98
144
|
end
|
|
99
145
|
|
|
100
146
|
header = '┏━━ Tag Totals '
|
|
@@ -115,6 +161,9 @@ EOTAIL
|
|
|
115
161
|
total_time = total.time_string(format: :hm)
|
|
116
162
|
total = "┃ #{spacer}total: "
|
|
117
163
|
total += total_time
|
|
164
|
+
if budgets_total.positive?
|
|
165
|
+
total += " (total budgets left #{budget_fmt.call(budgets_total)})"
|
|
166
|
+
end
|
|
118
167
|
total += ' ┃'
|
|
119
168
|
output += "\n#{total}"
|
|
120
169
|
output += "\n#{footer}"
|
|
@@ -126,11 +175,19 @@ EOTAIL
|
|
|
126
175
|
(max - k.length).times do
|
|
127
176
|
spacer += ' '
|
|
128
177
|
end
|
|
129
|
-
|
|
178
|
+
line = "#{k}:#{spacer}#{v.time_string(format: :clock)}"
|
|
179
|
+
if remaining_map.key?(k) && remaining_map[k].positive?
|
|
180
|
+
line += " (budget left #{budget_fmt.call(remaining_map[k])})"
|
|
181
|
+
end
|
|
182
|
+
output.push(line)
|
|
130
183
|
end
|
|
131
184
|
|
|
132
185
|
output = output.empty? ? '' : "\n--- Tag Totals ---\n#{output.join("\n")}"
|
|
133
|
-
output += "\n\nTotal tracked: #{total.time_string(format: :clock)}
|
|
186
|
+
output += "\n\nTotal tracked: #{total.time_string(format: :clock)}"
|
|
187
|
+
if budgets_total.positive?
|
|
188
|
+
output += " (total budgets left #{budget_fmt.call(budgets_total)})"
|
|
189
|
+
end
|
|
190
|
+
output += "\n"
|
|
134
191
|
output
|
|
135
192
|
end
|
|
136
193
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: doing
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.1.
|
|
4
|
+
version: 2.1.94
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Brett Terpstra
|
|
@@ -514,6 +514,7 @@ files:
|
|
|
514
514
|
- _config.yml
|
|
515
515
|
- bin/commands/again.rb
|
|
516
516
|
- bin/commands/archive.rb
|
|
517
|
+
- bin/commands/budget.rb
|
|
517
518
|
- bin/commands/cancel.rb
|
|
518
519
|
- bin/commands/changes.rb
|
|
519
520
|
- bin/commands/choose.rb
|
|
@@ -943,7 +944,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
943
944
|
- !ruby/object:Gem::Version
|
|
944
945
|
version: '0'
|
|
945
946
|
requirements: []
|
|
946
|
-
rubygems_version:
|
|
947
|
+
rubygems_version: 4.0.3
|
|
947
948
|
specification_version: 4
|
|
948
949
|
summary: A command line tool for managing What Was I Doing reminders
|
|
949
950
|
test_files: []
|