doing 2.1.0pre → 2.1.1pre

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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.yardoc/checksums +3 -2
  3. data/.yardoc/object_types +0 -0
  4. data/.yardoc/objects/root.dat +0 -0
  5. data/CHANGELOG.md +14 -11
  6. data/Gemfile.lock +1 -1
  7. data/README.md +1 -1
  8. data/bin/doing +112 -87
  9. data/doc/Array.html +1 -1
  10. data/doc/Doing/Color.html +1 -1
  11. data/doc/Doing/Completion.html +1 -1
  12. data/doc/Doing/Configuration.html +1 -1
  13. data/doc/Doing/Errors/DoingNoTraceError.html +1 -1
  14. data/doc/Doing/Errors/DoingRuntimeError.html +1 -1
  15. data/doc/Doing/Errors/DoingStandardError.html +1 -1
  16. data/doc/Doing/Errors/EmptyInput.html +1 -1
  17. data/doc/Doing/Errors/NoResults.html +1 -1
  18. data/doc/Doing/Errors/PluginException.html +1 -1
  19. data/doc/Doing/Errors/UserCancelled.html +1 -1
  20. data/doc/Doing/Errors/WrongCommand.html +1 -1
  21. data/doc/Doing/Errors.html +1 -1
  22. data/doc/Doing/Hooks.html +1 -1
  23. data/doc/Doing/Item.html +1 -1
  24. data/doc/Doing/Items.html +1 -1
  25. data/doc/Doing/LogAdapter.html +1 -1
  26. data/doc/Doing/Note.html +1 -1
  27. data/doc/Doing/Pager.html +1 -1
  28. data/doc/Doing/Plugins.html +1 -1
  29. data/doc/Doing/Prompt.html +1 -1
  30. data/doc/Doing/Section.html +1 -1
  31. data/doc/Doing/Util.html +1 -1
  32. data/doc/Doing/WWID.html +1 -181
  33. data/doc/Doing.html +3 -3
  34. data/doc/GLI/Commands/MarkdownDocumentListener.html +1 -1
  35. data/doc/GLI/Commands.html +1 -1
  36. data/doc/GLI.html +1 -1
  37. data/doc/Hash.html +1 -1
  38. data/doc/Status.html +1 -1
  39. data/doc/String.html +207 -3
  40. data/doc/Symbol.html +1 -1
  41. data/doc/Time.html +1 -1
  42. data/doc/_index.html +1 -1
  43. data/doc/file.README.html +2 -2
  44. data/doc/index.html +2 -2
  45. data/doc/method_list.html +84 -84
  46. data/doc/top-level-namespace.html +1 -1
  47. data/doing.rdoc +85 -18
  48. data/lib/completion/doing.bash +11 -11
  49. data/lib/doing/string_chronify.rb +81 -0
  50. data/lib/doing/version.rb +1 -1
  51. data/lib/doing/wwid.rb +87 -75
  52. data/lib/doing.rb +1 -0
  53. 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.0pre
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 vim before adding
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 vim (with no arguments, edits the last entry)
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
- Constrain search to entries newer than date
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
- Constrain search to entries older than date
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 vim
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 vim
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 vim
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 vim
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 vim.
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 vim
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
- View entries newer than date
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
- View entries older than date
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
- ===== -f|--from DATE_OR_RANGE
1634
+ ===== --from DATE_OR_RANGE
1597
1635
 
1598
1636
  Date range to show, or a single day to filter date on.
1599
- Date range argument should be quoted. Date specifications can be natural language.
1600
- To specify a range, use "to" or "through": `doing show --from "monday to friday"`
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)
@@ -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 -f -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 ) )
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
@@ -1,3 +1,3 @@
1
1
  module Doing
2
- VERSION = '2.1.0pre'
2
+ VERSION = '2.1.1pre'
3
3
  end
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(d, guess: :begin)
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(d, guess: :begin)
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
- cutoff = chronify(time_string, guess: :begin)
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
- cutoff = chronify(time_string, guess: :end)
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
- items = filter_items(Items.new, opt: { section: section, search: opt[:search], fuzzy: opt[:fuzzy], case: opt[:case], not: opt[:not] })
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(time_string, guess: :begin)
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(time_string, guess: :begin)
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
@@ -17,6 +17,7 @@ require 'safe_yaml/load'
17
17
  require 'doing/hash'
18
18
  require 'doing/colors'
19
19
  require 'doing/string'
20
+ require 'doing/string_chronify'
20
21
  require 'doing/time'
21
22
  require 'doing/array'
22
23
  require 'doing/symbol'
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.0pre
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