doing 2.1.0pre → 2.1.1pre

Sign up to get free protection for your applications and to get access to all the features.
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