doing 2.1.0pre → 2.1.1pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.yardoc/checksums +3 -2
- data/.yardoc/object_types +0 -0
- data/.yardoc/objects/root.dat +0 -0
- data/CHANGELOG.md +14 -11
- data/Gemfile.lock +1 -1
- data/README.md +1 -1
- data/bin/doing +112 -87
- data/doc/Array.html +1 -1
- data/doc/Doing/Color.html +1 -1
- data/doc/Doing/Completion.html +1 -1
- data/doc/Doing/Configuration.html +1 -1
- data/doc/Doing/Errors/DoingNoTraceError.html +1 -1
- data/doc/Doing/Errors/DoingRuntimeError.html +1 -1
- data/doc/Doing/Errors/DoingStandardError.html +1 -1
- data/doc/Doing/Errors/EmptyInput.html +1 -1
- data/doc/Doing/Errors/NoResults.html +1 -1
- data/doc/Doing/Errors/PluginException.html +1 -1
- data/doc/Doing/Errors/UserCancelled.html +1 -1
- data/doc/Doing/Errors/WrongCommand.html +1 -1
- data/doc/Doing/Errors.html +1 -1
- data/doc/Doing/Hooks.html +1 -1
- data/doc/Doing/Item.html +1 -1
- data/doc/Doing/Items.html +1 -1
- data/doc/Doing/LogAdapter.html +1 -1
- data/doc/Doing/Note.html +1 -1
- data/doc/Doing/Pager.html +1 -1
- data/doc/Doing/Plugins.html +1 -1
- data/doc/Doing/Prompt.html +1 -1
- data/doc/Doing/Section.html +1 -1
- data/doc/Doing/Util.html +1 -1
- data/doc/Doing/WWID.html +1 -181
- data/doc/Doing.html +3 -3
- data/doc/GLI/Commands/MarkdownDocumentListener.html +1 -1
- data/doc/GLI/Commands.html +1 -1
- data/doc/GLI.html +1 -1
- data/doc/Hash.html +1 -1
- data/doc/Status.html +1 -1
- data/doc/String.html +207 -3
- data/doc/Symbol.html +1 -1
- data/doc/Time.html +1 -1
- data/doc/_index.html +1 -1
- data/doc/file.README.html +2 -2
- data/doc/index.html +2 -2
- data/doc/method_list.html +84 -84
- data/doc/top-level-namespace.html +1 -1
- data/doing.rdoc +85 -18
- data/lib/completion/doing.bash +11 -11
- data/lib/doing/string_chronify.rb +81 -0
- data/lib/doing/version.rb +1 -1
- data/lib/doing/wwid.rb +87 -75
- data/lib/doing.rb +1 -0
- metadata +2 -1
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.1pre
|
9
9
|
|
10
10
|
=== Global Options
|
11
11
|
=== --config_file arg
|
@@ -145,7 +145,7 @@ Repeat last entry matching tags. Combine multiple tags with a comma.
|
|
145
145
|
|
146
146
|
|
147
147
|
===== -e|--editor
|
148
|
-
Edit duplicated entry with
|
148
|
+
Edit duplicated entry with emacs -nw before adding
|
149
149
|
|
150
150
|
|
151
151
|
|
@@ -541,7 +541,7 @@ Include date
|
|
541
541
|
|
542
542
|
|
543
543
|
===== -e|--editor
|
544
|
-
Edit entry with
|
544
|
+
Edit entry with emacs -nw (with no arguments, edits the last entry)
|
545
545
|
|
546
546
|
|
547
547
|
|
@@ -670,14 +670,14 @@ To search with regular expressions, single quote the string and surround with sl
|
|
670
670
|
===== Options
|
671
671
|
===== --after DATE_STRING
|
672
672
|
|
673
|
-
|
673
|
+
Search entries newer than date. If this is only a time (8am, 1:30pm, 15:00), all dates will be included, but entries will be filtered by time of day.
|
674
674
|
|
675
675
|
[Default Value] None
|
676
676
|
|
677
677
|
|
678
678
|
===== --before DATE_STRING
|
679
679
|
|
680
|
-
|
680
|
+
Search entries older than date. If this is only a time (8am, 1:30pm, 15:00), all dates will be included, but entries will be filtered by time of day.
|
681
681
|
|
682
682
|
[Default Value] None
|
683
683
|
|
@@ -690,6 +690,18 @@ Case sensitivity for search string matching [(c)ase-sensitive, (i)gnore, (s)mart
|
|
690
690
|
[Must Match] (?-mix:^[csi])
|
691
691
|
|
692
692
|
|
693
|
+
===== --from DATE_OR_RANGE
|
694
|
+
|
695
|
+
Date range to show, or a single day to filter date on.
|
696
|
+
Date range argument should be quoted. Date specifications can be natural language.
|
697
|
+
To specify a range, use "to" or "through": `doing search --from "monday 8am to friday 5pm"`.
|
698
|
+
|
699
|
+
If values are only time(s) (6am to noon) all dates will be included, but entries will be filtered
|
700
|
+
by time of day.
|
701
|
+
|
702
|
+
[Default Value] None
|
703
|
+
|
704
|
+
|
693
705
|
===== -o|--output FORMAT
|
694
706
|
|
695
707
|
Output to export format (csv|doing|html|markdown|say|taskpaper|template|timeline|wiki)
|
@@ -891,7 +903,7 @@ Tag filter, combine multiple tags with a comma.
|
|
891
903
|
|
892
904
|
|
893
905
|
===== -e|--editor
|
894
|
-
Edit entry with
|
906
|
+
Edit entry with emacs -nw
|
895
907
|
|
896
908
|
|
897
909
|
|
@@ -925,7 +937,7 @@ Note
|
|
925
937
|
|
926
938
|
|
927
939
|
===== -e|--editor
|
928
|
-
Edit entry with
|
940
|
+
Edit entry with emacs -nw
|
929
941
|
|
930
942
|
|
931
943
|
|
@@ -1047,7 +1059,7 @@ Archive previous @meanwhile entry
|
|
1047
1059
|
|
1048
1060
|
|
1049
1061
|
===== -e|--editor
|
1050
|
-
Edit entry with
|
1062
|
+
Edit entry with emacs -nw
|
1051
1063
|
|
1052
1064
|
|
1053
1065
|
|
@@ -1098,7 +1110,7 @@ Add/remove note from last entry matching tag
|
|
1098
1110
|
|
1099
1111
|
|
1100
1112
|
===== -e|--editor
|
1101
|
-
Edit entry with
|
1113
|
+
Edit entry with emacs -nw
|
1102
1114
|
|
1103
1115
|
|
1104
1116
|
|
@@ -1129,7 +1141,7 @@ Record what you're starting now, or backdate the start time using natural langua
|
|
1129
1141
|
|
1130
1142
|
A parenthetical at the end of the entry will be converted to a note.
|
1131
1143
|
|
1132
|
-
Run with no argument to create a new entry using
|
1144
|
+
Run with no argument to create a new entry using emacs -nw.
|
1133
1145
|
===== Options
|
1134
1146
|
===== -b|--back|--started DATE_STRING
|
1135
1147
|
|
@@ -1153,7 +1165,7 @@ Section
|
|
1153
1165
|
|
1154
1166
|
|
1155
1167
|
===== -e|--editor
|
1156
|
-
Edit entry with
|
1168
|
+
Edit entry with emacs -nw
|
1157
1169
|
|
1158
1170
|
|
1159
1171
|
|
@@ -1433,6 +1445,20 @@ Multiple selections are allowed, hit tab to add the highlighted entry to the
|
|
1433
1445
|
selection, and use ctrl-a to select all visible items. Return processes the
|
1434
1446
|
selected entries.
|
1435
1447
|
===== Options
|
1448
|
+
===== --after DATE_STRING
|
1449
|
+
|
1450
|
+
Select from entries newer than date. If this is only a time (8am, 1:30pm, 15:00), all dates will be included, but entries will be filtered by time of day.
|
1451
|
+
|
1452
|
+
[Default Value] None
|
1453
|
+
|
1454
|
+
|
1455
|
+
===== --before DATE_STRING
|
1456
|
+
|
1457
|
+
Select from entries older than date. If this is only a time (8am, 1:30pm, 15:00), all dates will be included, but entries will be filtered by time of day.
|
1458
|
+
|
1459
|
+
[Default Value] None
|
1460
|
+
|
1461
|
+
|
1436
1462
|
===== --case TYPE
|
1437
1463
|
|
1438
1464
|
Case sensitivity for search string matching [(c)ase-sensitive, (i)gnore, (s)mart]
|
@@ -1441,6 +1467,18 @@ Case sensitivity for search string matching [(c)ase-sensitive, (i)gnore, (s)mart
|
|
1441
1467
|
[Must Match] (?-mix:^[csi])
|
1442
1468
|
|
1443
1469
|
|
1470
|
+
===== --from DATE_OR_RANGE
|
1471
|
+
|
1472
|
+
Date range to show, or a single day to filter date on.
|
1473
|
+
Date range argument should be quoted. Date specifications can be natural language.
|
1474
|
+
To specify a range, use "to" or "through": `doing select --from "monday 8am to friday 5pm"`.
|
1475
|
+
|
1476
|
+
If values are only time(s) (6am to noon) all dates will be included, but entries will be filtered
|
1477
|
+
by time of day.
|
1478
|
+
|
1479
|
+
[Default Value] None
|
1480
|
+
|
1481
|
+
|
1444
1482
|
===== -m|--move SECTION
|
1445
1483
|
|
1446
1484
|
Move selected items to section
|
@@ -1558,7 +1596,7 @@ Age (oldest|newest)
|
|
1558
1596
|
|
1559
1597
|
===== --after DATE_STRING
|
1560
1598
|
|
1561
|
-
|
1599
|
+
Show entries newer than date. If this is only a time (8am, 1:30pm, 15:00), all dates will be included, but entries will be filtered by time of day.
|
1562
1600
|
|
1563
1601
|
[Default Value] None
|
1564
1602
|
|
@@ -1573,7 +1611,7 @@ Tag boolean (AND,OR,NOT)
|
|
1573
1611
|
|
1574
1612
|
===== --before DATE_STRING
|
1575
1613
|
|
1576
|
-
|
1614
|
+
Show entries older than date. If this is only a time (8am, 1:30pm, 15:00), all dates will be included, but entries will be filtered by time of day.
|
1577
1615
|
|
1578
1616
|
[Default Value] None
|
1579
1617
|
|
@@ -1593,11 +1631,14 @@ Case sensitivity for search string matching [(c)ase-sensitive, (i)gnore, (s)mart
|
|
1593
1631
|
[Must Match] (?-mix:^[csi])
|
1594
1632
|
|
1595
1633
|
|
1596
|
-
=====
|
1634
|
+
===== --from DATE_OR_RANGE
|
1597
1635
|
|
1598
1636
|
Date range to show, or a single day to filter date on.
|
1599
|
-
|
1600
|
-
|
1637
|
+
Date range argument should be quoted. Date specifications can be natural language.
|
1638
|
+
To specify a range, use "to" or "through": `doing show --from "monday 8am to friday 5pm"`.
|
1639
|
+
|
1640
|
+
If values are only time(s) (6am to noon) all dates will be included, but entries will be filtered
|
1641
|
+
by time of day.
|
1601
1642
|
|
1602
1643
|
[Default Value] None
|
1603
1644
|
|
@@ -1869,6 +1910,13 @@ View entries before specified time (e.g. 8am, 12:30pm, 15:00)
|
|
1869
1910
|
[Default Value] None
|
1870
1911
|
|
1871
1912
|
|
1913
|
+
===== --from DATE_OR_RANGE
|
1914
|
+
|
1915
|
+
Time range to show `doing today --from "12pm to 4pm"`
|
1916
|
+
|
1917
|
+
[Default Value] None
|
1918
|
+
|
1919
|
+
|
1872
1920
|
===== -o|--output FORMAT
|
1873
1921
|
|
1874
1922
|
Output to export format (csv|doing|html|markdown|say|taskpaper|template|timeline|wiki)
|
@@ -1920,7 +1968,7 @@ Command line options override view configuration
|
|
1920
1968
|
===== Options
|
1921
1969
|
===== --after DATE_STRING
|
1922
1970
|
|
1923
|
-
View entries newer than date
|
1971
|
+
View entries newer than date. If this is only a time (8am, 1:30pm, 15:00), all dates will be included, but entries will be filtered by time of day.
|
1924
1972
|
|
1925
1973
|
[Default Value] None
|
1926
1974
|
|
@@ -1935,7 +1983,7 @@ Tag boolean (AND,OR,NOT)
|
|
1935
1983
|
|
1936
1984
|
===== --before DATE_STRING
|
1937
1985
|
|
1938
|
-
View entries older than date
|
1986
|
+
View entries older than date. If this is only a time (8am, 1:30pm, 15:00), all dates will be included, but entries will be filtered by time of day.
|
1939
1987
|
|
1940
1988
|
[Default Value] None
|
1941
1989
|
|
@@ -1956,6 +2004,18 @@ Case sensitivity for search string matching [(c)ase-sensitive, (i)gnore, (s)mart
|
|
1956
2004
|
[Must Match] (?-mix:^[csi])
|
1957
2005
|
|
1958
2006
|
|
2007
|
+
===== --from DATE_OR_RANGE
|
2008
|
+
|
2009
|
+
Date range to show, or a single day to filter date on.
|
2010
|
+
Date range argument should be quoted. Date specifications can be natural language.
|
2011
|
+
To specify a range, use "to" or "through": `doing view --from "monday 8am to friday 5pm" view_name`.
|
2012
|
+
|
2013
|
+
If values are only time(s) (6am to noon) all dates will be included, but entries will be filtered
|
2014
|
+
by time of day.
|
2015
|
+
|
2016
|
+
[Default Value] None
|
2017
|
+
|
2018
|
+
|
1959
2019
|
===== -o|--output FORMAT
|
1960
2020
|
|
1961
2021
|
Output to export format (csv|doing|html|markdown|say|taskpaper|template|timeline|wiki)
|
@@ -2126,6 +2186,13 @@ View entries before specified time (e.g. 8am, 12:30pm, 15:00)
|
|
2126
2186
|
[Default Value] None
|
2127
2187
|
|
2128
2188
|
|
2189
|
+
===== --from TIME_RANGE
|
2190
|
+
|
2191
|
+
Time range to show, e.g. `doing yesterday --from "1am to 8am"`
|
2192
|
+
|
2193
|
+
[Default Value] None
|
2194
|
+
|
2195
|
+
|
2129
2196
|
===== -o|--output FORMAT
|
2130
2197
|
|
2131
2198
|
Output to export format (csv|doing|html|markdown|say|taskpaper|template|timeline|wiki)
|
data/lib/completion/doing.bash
CHANGED
@@ -101,9 +101,9 @@ _doing_finish() {
|
|
101
101
|
_doing_grep() {
|
102
102
|
|
103
103
|
if [[ "$token" == --* ]]; then
|
104
|
-
COMPREPLY=( $( compgen -W '--after --before --case --interactive --not --output --only_timed --section --times --tag_sort --totals --exact' -- $token ) )
|
104
|
+
COMPREPLY=( $( compgen -W '--after --before --case --from --interactive --not --output --only_timed --section --times --tag_sort --totals --exact' -- $token ) )
|
105
105
|
elif [[ "$token" == -* ]]; then
|
106
|
-
COMPREPLY=( $( compgen -W '-i -o -s -t -x --after --before --case --interactive --not --output --only_timed --section --times --tag_sort --totals --exact' -- $token ) )
|
106
|
+
COMPREPLY=( $( compgen -W '-i -o -s -t -x --after --before --case --from --interactive --not --output --only_timed --section --times --tag_sort --totals --exact' -- $token ) )
|
107
107
|
|
108
108
|
fi
|
109
109
|
}
|
@@ -261,9 +261,9 @@ _doing_sections() {
|
|
261
261
|
_doing_select() {
|
262
262
|
|
263
263
|
if [[ "$token" == --* ]]; then
|
264
|
-
COMPREPLY=( $( compgen -W '--archive --resume --cancel --case --delete --editor --finish --flag --force --move --menu --not --output --search --remove --section --save_to --tag --exact' -- $token ) )
|
264
|
+
COMPREPLY=( $( compgen -W '--archive --after --resume --before --cancel --case --delete --editor --finish --flag --force --from --move --menu --not --output --search --remove --section --save_to --tag --exact' -- $token ) )
|
265
265
|
elif [[ "$token" == -* ]]; then
|
266
|
-
COMPREPLY=( $( compgen -W '-a -c -d -e -f -m -o -r -s -t -x --archive --resume --cancel --case --delete --editor --finish --flag --force --move --menu --not --output --search --remove --section --save_to --tag --exact' -- $token ) )
|
266
|
+
COMPREPLY=( $( compgen -W '-a -c -d -e -f -m -o -r -s -t -x --archive --after --resume --before --cancel --case --delete --editor --finish --flag --force --from --move --menu --not --output --search --remove --section --save_to --tag --exact' -- $token ) )
|
267
267
|
|
268
268
|
fi
|
269
269
|
}
|
@@ -278,7 +278,7 @@ IFS="$OLD_IFS"
|
|
278
278
|
if [[ "$token" == --* ]]; then
|
279
279
|
COMPREPLY=( $( compgen -W '--age --after --bool --before --count --case --from --interactive --not --output --only_timed --sort --search --times --tag --tag_order --tag_sort --totals --exact' -- $token ) )
|
280
280
|
elif [[ "$token" == -* ]]; then
|
281
|
-
COMPREPLY=( $( compgen -W '-a -b -c -
|
281
|
+
COMPREPLY=( $( compgen -W '-a -b -c -i -o -s -t -x --age --after --bool --before --count --case --from --interactive --not --output --only_timed --sort --search --times --tag --tag_order --tag_sort --totals --exact' -- $token ) )
|
282
282
|
else
|
283
283
|
local nocasematchWasOff=0
|
284
284
|
shopt nocasematch >/dev/null || nocasematchWasOff=1
|
@@ -331,9 +331,9 @@ _doing_template() {
|
|
331
331
|
_doing_today() {
|
332
332
|
|
333
333
|
if [[ "$token" == --* ]]; then
|
334
|
-
COMPREPLY=( $( compgen -W '--after --before --output --section --times --tag_sort --totals' -- $token ) )
|
334
|
+
COMPREPLY=( $( compgen -W '--after --before --from --output --section --times --tag_sort --totals' -- $token ) )
|
335
335
|
elif [[ "$token" == -* ]]; then
|
336
|
-
COMPREPLY=( $( compgen -W '-o -s -t --after --before --output --section --times --tag_sort --totals' -- $token ) )
|
336
|
+
COMPREPLY=( $( compgen -W '-o -s -t --after --before --from --output --section --times --tag_sort --totals' -- $token ) )
|
337
337
|
|
338
338
|
fi
|
339
339
|
}
|
@@ -356,9 +356,9 @@ local words=$(doing views)
|
|
356
356
|
IFS="$OLD_IFS"
|
357
357
|
|
358
358
|
if [[ "$token" == --* ]]; then
|
359
|
-
COMPREPLY=( $( compgen -W '--after --bool --before --count --case --color --interactive --not --output --only_timed --section --search --times --tag --tag_order --tag_sort --totals --exact' -- $token ) )
|
359
|
+
COMPREPLY=( $( compgen -W '--after --bool --before --count --case --color --from --interactive --not --output --only_timed --section --search --times --tag --tag_order --tag_sort --totals --exact' -- $token ) )
|
360
360
|
elif [[ "$token" == -* ]]; then
|
361
|
-
COMPREPLY=( $( compgen -W '-b -c -i -o -s -t -x --after --bool --before --count --case --color --interactive --not --output --only_timed --section --search --times --tag --tag_order --tag_sort --totals --exact' -- $token ) )
|
361
|
+
COMPREPLY=( $( compgen -W '-b -c -i -o -s -t -x --after --bool --before --count --case --color --from --interactive --not --output --only_timed --section --search --times --tag --tag_order --tag_sort --totals --exact' -- $token ) )
|
362
362
|
else
|
363
363
|
local nocasematchWasOff=0
|
364
364
|
shopt nocasematch >/dev/null || nocasematchWasOff=1
|
@@ -401,9 +401,9 @@ _doing_wiki() {
|
|
401
401
|
_doing_yesterday() {
|
402
402
|
|
403
403
|
if [[ "$token" == --* ]]; then
|
404
|
-
COMPREPLY=( $( compgen -W '--after --before --output --section --times --tag_order --tag_sort --totals' -- $token ) )
|
404
|
+
COMPREPLY=( $( compgen -W '--after --before --from --output --section --times --tag_order --tag_sort --totals' -- $token ) )
|
405
405
|
elif [[ "$token" == -* ]]; then
|
406
|
-
COMPREPLY=( $( compgen -W '-o -s -t --after --before --output --section --times --tag_order --tag_sort --totals' -- $token ) )
|
406
|
+
COMPREPLY=( $( compgen -W '-o -s -t --after --before --from --output --section --times --tag_order --tag_sort --totals' -- $token ) )
|
407
407
|
|
408
408
|
fi
|
409
409
|
}
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Doing
|
4
|
+
# Chronify methods for strings
|
5
|
+
class ::String
|
6
|
+
##
|
7
|
+
## Converts input string into a Time object when input
|
8
|
+
## takes on the following formats:
|
9
|
+
## - interval format e.g. '1d2h30m', '45m'
|
10
|
+
## etc.
|
11
|
+
## - a semantic phrase e.g. 'yesterday
|
12
|
+
## 5:30pm'
|
13
|
+
## - a strftime e.g. '2016-03-15 15:32:04
|
14
|
+
## PDT'
|
15
|
+
##
|
16
|
+
## @param options Additional options
|
17
|
+
##
|
18
|
+
## @option options :future [Boolean] assume future date
|
19
|
+
## (default: false)
|
20
|
+
##
|
21
|
+
## @option options :guess [Symbol] :begin or :end to
|
22
|
+
## assume beginning or end of
|
23
|
+
## arbitrary time range
|
24
|
+
##
|
25
|
+
## @return [DateTime] result
|
26
|
+
##
|
27
|
+
def chronify(**options)
|
28
|
+
now = Time.now
|
29
|
+
raise InvalidTimeExpression, "Invalid time expression #{inspect}" if to_s.strip == ''
|
30
|
+
|
31
|
+
secs_ago = if match(/^(\d+)$/)
|
32
|
+
# plain number, assume minutes
|
33
|
+
Regexp.last_match(1).to_i * 60
|
34
|
+
elsif (m = match(/^(?:(?<day>\d+)d)?(?:(?<hour>\d+)h)?(?:(?<min>\d+)m)?$/i))
|
35
|
+
# day/hour/minute format e.g. 1d2h30m
|
36
|
+
[[m['day'], 24 * 3600],
|
37
|
+
[m['hour'], 3600],
|
38
|
+
[m['min'], 60]].map { |qty, secs| qty ? (qty.to_i * secs) : 0 }.reduce(0, :+)
|
39
|
+
end
|
40
|
+
|
41
|
+
if secs_ago
|
42
|
+
now - secs_ago
|
43
|
+
else
|
44
|
+
Chronic.parse(self, { guess: options.fetch(:guess, :begin), context: options.fetch(:future, false) ? :future : :past, ambiguous_time_range: 8 })
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
##
|
49
|
+
## Converts simple strings into seconds that can be
|
50
|
+
## added to a Time object
|
51
|
+
##
|
52
|
+
## Input string can be HH:MM or XX[dhm][[XXhm][XXm]]
|
53
|
+
## (1d2h30m, 45m, 1.5d, 1h20m, etc.)
|
54
|
+
##
|
55
|
+
## @return [Integer] seconds
|
56
|
+
##
|
57
|
+
def chronify_qty
|
58
|
+
minutes = 0
|
59
|
+
case self.strip
|
60
|
+
when /^(\d+):(\d\d)$/
|
61
|
+
minutes += Regexp.last_match(1).to_i * 60
|
62
|
+
minutes += Regexp.last_match(2).to_i
|
63
|
+
when /^(\d+(?:\.\d+)?)([hmd])?$/
|
64
|
+
amt = Regexp.last_match(1)
|
65
|
+
type = Regexp.last_match(2).nil? ? 'm' : Regexp.last_match(2)
|
66
|
+
|
67
|
+
minutes = case type.downcase
|
68
|
+
when 'm'
|
69
|
+
amt.to_i
|
70
|
+
when 'h'
|
71
|
+
(amt.to_f * 60).round
|
72
|
+
when 'd'
|
73
|
+
(amt.to_f * 60 * 24).round
|
74
|
+
else
|
75
|
+
minutes
|
76
|
+
end
|
77
|
+
end
|
78
|
+
minutes * 60
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
data/lib/doing/version.rb
CHANGED
data/lib/doing/wwid.rb
CHANGED
@@ -190,7 +190,7 @@ module Doing
|
|
190
190
|
m = Regexp.last_match
|
191
191
|
t = m['tag']
|
192
192
|
d = m['date']
|
193
|
-
parsed_date = d =~ date_rx ? Time.parse(d) : chronify(
|
193
|
+
parsed_date = d =~ date_rx ? Time.parse(d) : d.chronify(guess: :begin)
|
194
194
|
parsed_date.nil? ? m[0] : "@#{t}(#{parsed_date.strftime('%F %R')})"
|
195
195
|
end
|
196
196
|
|
@@ -200,7 +200,7 @@ module Doing
|
|
200
200
|
date = if d =~ iso_rx
|
201
201
|
Time.parse(d)
|
202
202
|
else
|
203
|
-
chronify(
|
203
|
+
d.chronify(guess: :begin)
|
204
204
|
end
|
205
205
|
title.sub!(date_rx, '').strip!
|
206
206
|
end
|
@@ -222,71 +222,6 @@ module Doing
|
|
222
222
|
[date, title, note]
|
223
223
|
end
|
224
224
|
|
225
|
-
##
|
226
|
-
## Converts input string into a Time object when input takes on the
|
227
|
-
## following formats:
|
228
|
-
## - interval format e.g. '1d2h30m', '45m' etc.
|
229
|
-
## - a semantic phrase e.g. 'yesterday 5:30pm'
|
230
|
-
## - a strftime e.g. '2016-03-15 15:32:04 PDT'
|
231
|
-
##
|
232
|
-
## @param input [String] String to chronify
|
233
|
-
##
|
234
|
-
## @return [DateTime] result
|
235
|
-
##
|
236
|
-
def chronify(input, future: false, guess: :begin)
|
237
|
-
now = Time.now
|
238
|
-
raise InvalidTimeExpression, "Invalid time expression #{input.inspect}" if input.to_s.strip == ''
|
239
|
-
|
240
|
-
secs_ago = if input.match(/^(\d+)$/)
|
241
|
-
# plain number, assume minutes
|
242
|
-
Regexp.last_match(1).to_i * 60
|
243
|
-
elsif (m = input.match(/^(?:(?<day>\d+)d)?(?:(?<hour>\d+)h)?(?:(?<min>\d+)m)?$/i))
|
244
|
-
# day/hour/minute format e.g. 1d2h30m
|
245
|
-
[[m['day'], 24 * 3600],
|
246
|
-
[m['hour'], 3600],
|
247
|
-
[m['min'], 60]].map { |qty, secs| qty ? (qty.to_i * secs) : 0 }.reduce(0, :+)
|
248
|
-
end
|
249
|
-
|
250
|
-
if secs_ago
|
251
|
-
now - secs_ago
|
252
|
-
else
|
253
|
-
Chronic.parse(input, { guess: guess, context: future ? :future : :past, ambiguous_time_range: 8 })
|
254
|
-
end
|
255
|
-
end
|
256
|
-
|
257
|
-
##
|
258
|
-
## Converts simple strings into seconds that can be added to a Time
|
259
|
-
## object
|
260
|
-
##
|
261
|
-
## @param qty [String] HH:MM or XX[dhm][[XXhm][XXm]] (1d2h30m, 45m,
|
262
|
-
## 1.5d, 1h20m, etc.)
|
263
|
-
##
|
264
|
-
## @return [Integer] seconds
|
265
|
-
##
|
266
|
-
def chronify_qty(qty)
|
267
|
-
minutes = 0
|
268
|
-
case qty.strip
|
269
|
-
when /^(\d+):(\d\d)$/
|
270
|
-
minutes += Regexp.last_match(1).to_i * 60
|
271
|
-
minutes += Regexp.last_match(2).to_i
|
272
|
-
when /^(\d+(?:\.\d+)?)([hmd])?$/
|
273
|
-
amt = Regexp.last_match(1)
|
274
|
-
type = Regexp.last_match(2).nil? ? 'm' : Regexp.last_match(2)
|
275
|
-
|
276
|
-
minutes = case type.downcase
|
277
|
-
when 'm'
|
278
|
-
amt.to_i
|
279
|
-
when 'h'
|
280
|
-
(amt.to_f * 60).round
|
281
|
-
when 'd'
|
282
|
-
(amt.to_f * 60 * 24).round
|
283
|
-
else
|
284
|
-
minutes
|
285
|
-
end
|
286
|
-
end
|
287
|
-
minutes * 60
|
288
|
-
end
|
289
|
-
|
290
225
|
##
|
291
226
|
## List sections
|
292
227
|
##
|
@@ -661,12 +596,55 @@ module Doing
|
|
661
596
|
## @option opt [String] :age ('old' or 'new')
|
662
597
|
##
|
663
598
|
def filter_items(items = Items.new, opt: {})
|
599
|
+
time_rx = /^(\d{1,2}+(:\d{1,2}+)?( *(am|pm))?|midnight|noon)$/
|
600
|
+
|
664
601
|
if items.nil? || items.empty?
|
665
602
|
section = opt[:section] ? guess_section(opt[:section]) : 'All'
|
666
|
-
|
667
603
|
items = section =~ /^all$/i ? @content.dup : @content.in_section(section)
|
668
604
|
end
|
669
605
|
|
606
|
+
opt[:time_filter] = [nil, nil]
|
607
|
+
if opt[:from] && !opt[:date_filter]
|
608
|
+
date_string = opt[:from]
|
609
|
+
case date_string
|
610
|
+
when / (to|through|thru|(un)?til|-+) /
|
611
|
+
dates = date_string.split(/ (?:to|through|thru|(?:un)?til|-+) /)
|
612
|
+
if dates[0].strip =~ time_rx && dates[-1].strip =~ time_rx
|
613
|
+
time_start = dates[0].strip
|
614
|
+
time_end = dates[-1].strip
|
615
|
+
else
|
616
|
+
start = dates[0].chronify(guess: :begin)
|
617
|
+
finish = dates[-1].chronify(guess: :end)
|
618
|
+
end
|
619
|
+
when time_rx
|
620
|
+
time_start = date_string
|
621
|
+
time_end = nil
|
622
|
+
else
|
623
|
+
start = date_string.chronify(guess: :begin)
|
624
|
+
finish = false
|
625
|
+
end
|
626
|
+
|
627
|
+
if time_start
|
628
|
+
opt[:time_filter] = [time_start, time_end]
|
629
|
+
Doing.logger.debug('Parser:', "--from string interpreted as time span, from #{time_start ? time_start : '12am'} to #{time_end ? time_end : '11:59pm'}")
|
630
|
+
else
|
631
|
+
raise InvalidTimeExpression, 'Unrecognized date string' unless start
|
632
|
+
|
633
|
+
opt[:date_filter] = [start, finish]
|
634
|
+
Doing.logger.debug('Parser:', "--from string interpreted as #{start.strftime('%F %R')} -- #{finish ? finish.strftime('%F %R') : 'now'}")
|
635
|
+
end
|
636
|
+
end
|
637
|
+
|
638
|
+
if opt[:before] =~ time_rx
|
639
|
+
opt[:time_filter][1] = opt[:before]
|
640
|
+
opt[:before] = nil
|
641
|
+
end
|
642
|
+
|
643
|
+
if opt[:after] =~ time_rx
|
644
|
+
opt[:time_filter][0] = opt[:after]
|
645
|
+
opt[:after] = nil
|
646
|
+
end
|
647
|
+
|
670
648
|
items.sort_by! { |item| [item.date, item.title.downcase] }.reverse
|
671
649
|
|
672
650
|
filtered_items = items.select do |item|
|
@@ -708,6 +686,26 @@ module Doing
|
|
708
686
|
keep = opt[:not] ? !keep : keep
|
709
687
|
end
|
710
688
|
|
689
|
+
if keep && opt[:time_filter][0] || opt[:time_filter][1]
|
690
|
+
start_string = if opt[:time_filter][0].nil?
|
691
|
+
"#{item.date.strftime('%Y-%m-%d')} 12am"
|
692
|
+
else
|
693
|
+
"#{item.date.strftime('%Y-%m-%d')} #{opt[:time_filter][0]}"
|
694
|
+
end
|
695
|
+
start_time = start_string.chronify(guess: :begin)
|
696
|
+
|
697
|
+
end_string = if opt[:time_filter][1].nil?
|
698
|
+
"#{item.date.next_day.strftime('%Y-%m-%d')} 12am"
|
699
|
+
else
|
700
|
+
"#{item.date.strftime('%Y-%m-%d')} #{opt[:time_filter][1]}"
|
701
|
+
end
|
702
|
+
end_time = end_string.chronify(guess: :end)
|
703
|
+
|
704
|
+
in_time_range = item.date >= start_time && item.date <= end_time
|
705
|
+
keep = false unless in_time_range
|
706
|
+
keep = opt[:not] ? !keep : keep
|
707
|
+
end
|
708
|
+
|
711
709
|
keep = false if keep && opt[:only_timed] && !item.interval
|
712
710
|
|
713
711
|
if keep && opt[:tag_filter] && !opt[:tag_filter]['tags'].empty?
|
@@ -717,14 +715,22 @@ module Doing
|
|
717
715
|
|
718
716
|
if keep && opt[:before]
|
719
717
|
time_string = opt[:before]
|
720
|
-
|
718
|
+
if time_string =~ time_rx
|
719
|
+
cutoff = "#{item.date.strftime('%Y-%m-%d')} #{time_string}".chronify(guess: :begin)
|
720
|
+
else
|
721
|
+
cutoff = time_string.chronify(guess: :begin)
|
722
|
+
end
|
721
723
|
keep = cutoff && item.date <= cutoff
|
722
724
|
keep = opt[:not] ? !keep : keep
|
723
725
|
end
|
724
726
|
|
725
727
|
if keep && opt[:after]
|
726
728
|
time_string = opt[:after]
|
727
|
-
|
729
|
+
if time_string =~ time_rx
|
730
|
+
cutoff = "#{item.date.strftime('%Y-%m-%d')} #{time_string}".chronify(guess: :end)
|
731
|
+
else
|
732
|
+
cutoff = time_string.chronify(guess: :end)
|
733
|
+
end
|
728
734
|
keep = cutoff && item.date >= cutoff
|
729
735
|
keep = opt[:not] ? !keep : keep
|
730
736
|
end
|
@@ -760,7 +766,7 @@ module Doing
|
|
760
766
|
## Options hash is shared with #filter_items and #act_on
|
761
767
|
##
|
762
768
|
def interactive(opt = {})
|
763
|
-
section = opt[:section] ? guess_section(opt[:section]) : 'All'
|
769
|
+
opt[:section] = opt[:section] ? guess_section(opt[:section]) : 'All'
|
764
770
|
|
765
771
|
search = nil
|
766
772
|
|
@@ -774,9 +780,12 @@ module Doing
|
|
774
780
|
opt[:query] = "!#{opt[:query]}" if opt[:not]
|
775
781
|
opt[:multiple] = true
|
776
782
|
opt[:show_if_single] = true
|
777
|
-
|
783
|
+
filter_options = %i[after before case date_filter from fuzzy not search section].each_with_object({}) {
|
784
|
+
|k, hsh| hsh[k] = opt[k]
|
785
|
+
}
|
786
|
+
items = filter_items(Items.new, opt: filter_options)
|
778
787
|
|
779
|
-
selection = Prompt.choose_from_items(items, include_section: section =~ /^all$/i, **opt)
|
788
|
+
selection = Prompt.choose_from_items(items, include_section: opt[:section] =~ /^all$/i, **opt)
|
780
789
|
|
781
790
|
raise NoResults, 'no items selected' if selection.nil? || selection.empty?
|
782
791
|
|
@@ -1336,7 +1345,7 @@ module Doing
|
|
1336
1345
|
break if counter >= max
|
1337
1346
|
if opt[:before]
|
1338
1347
|
time_string = opt[:before]
|
1339
|
-
cutoff = chronify(
|
1348
|
+
cutoff = time_string.chronify(guess: :begin)
|
1340
1349
|
end
|
1341
1350
|
|
1342
1351
|
unless ((!tags.empty? && !item.tags?(tags, bool)) || (opt[:search] && !item.search(opt[:search].to_s)) || (opt[:before] && item.date >= cutoff))
|
@@ -1524,10 +1533,12 @@ module Doing
|
|
1524
1533
|
'order' => @config['order'] || 'asc',
|
1525
1534
|
'tags_color' => @config['tags_color']
|
1526
1535
|
})
|
1536
|
+
|
1527
1537
|
options = {
|
1528
1538
|
after: opt[:after],
|
1529
1539
|
before: opt[:before],
|
1530
1540
|
count: 0,
|
1541
|
+
from: opt[:from],
|
1531
1542
|
format: cfg['date_format'],
|
1532
1543
|
order: cfg['order'] || 'asc',
|
1533
1544
|
output: output,
|
@@ -1584,6 +1595,7 @@ module Doing
|
|
1584
1595
|
after: opt[:after],
|
1585
1596
|
before: opt[:before],
|
1586
1597
|
count: 0,
|
1598
|
+
from: opt[:from],
|
1587
1599
|
order: opt[:order],
|
1588
1600
|
output: output,
|
1589
1601
|
section: section,
|
@@ -2044,7 +2056,7 @@ EOS
|
|
2044
2056
|
break if counter >= max
|
2045
2057
|
if opt[:before]
|
2046
2058
|
time_string = opt[:before]
|
2047
|
-
cutoff = chronify(
|
2059
|
+
cutoff = time_string.chronify(guess: :begin)
|
2048
2060
|
end
|
2049
2061
|
|
2050
2062
|
if (item.section.downcase != section.downcase && section != /^all$/i) || item.section.downcase == destination.downcase
|
data/lib/doing.rb
CHANGED
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.1pre
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brett Terpstra
|
@@ -444,6 +444,7 @@ files:
|
|
444
444
|
- lib/doing/prompt.rb
|
445
445
|
- lib/doing/section.rb
|
446
446
|
- lib/doing/string.rb
|
447
|
+
- lib/doing/string_chronify.rb
|
447
448
|
- lib/doing/symbol.rb
|
448
449
|
- lib/doing/time.rb
|
449
450
|
- lib/doing/util.rb
|