doing 1.0.73 → 1.0.74
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/bin/doing +162 -61
- data/lib/doing/version.rb +1 -1
- data/lib/doing/wwid.rb +29 -21
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 652cc285b42ce068d4640a6aff95d2058d1fba60715e41ce4599bfcbe7051507
|
4
|
+
data.tar.gz: 7a940801f8fa9d78b27688b6c92f4fcdb4b5765c28888cb6f1218245d4535376
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: df8f8c529b18238a5d069b79c9e43382f2751d18f1dd022e7d918467f31dadd6b6a50780034b516d4d9b321ddcc4d3ebb7a40db9b96a4059adf49eda119663f9
|
7
|
+
data.tar.gz: b67333df19e976fe0cbde7645c6cc8a9098813a023090f7e52bd60052283202f2dff8406beacb28d921d7c947dac2d885caab7fe3388c578a17b73181a4cbbc5
|
data/README.md
CHANGED
@@ -27,7 +27,7 @@ If there's something I want to look at later but doesn't need to be added to a t
|
|
27
27
|
|
28
28
|
## Installation
|
29
29
|
|
30
|
-
The current version of `doing` is <!--VER-->1.0.
|
30
|
+
The current version of `doing` is <!--VER-->1.0.73<!--END VER-->.
|
31
31
|
|
32
32
|
$ [sudo] gem install doing
|
33
33
|
|
@@ -536,7 +536,7 @@ If you have a use for it, you can use `-o csv` on the show or view commands to o
|
|
536
536
|
|
537
537
|
`doing yesterday` is great for stand-ups (thanks to [Sean Collins](https://github.com/sc68cal) for that!). Note that you can show yesterday's activity from an alternate section by using the section name as an argument (e.g. `doing yesterday archive`).
|
538
538
|
|
539
|
-
`doing on` allows for full date ranges and filtering. `doing on saturday`, or `doing on one month to today` will give you ranges. You can use the same terms with the `show` command by adding the `-f` or `--from` flag. `doing show @done --from "monday to friday"` will give you all of your completed items for the last week (assuming it's the weekend).
|
539
|
+
`doing on` allows for full date ranges and filtering. `doing on saturday`, or `doing on one month to today` will give you ranges. You can use the same terms with the `show` command by adding the `-f` or `--from` flag. `doing show @done --from "monday to friday"` will give you all of your completed items for the last week (assuming it's the weekend). There's also `doing since` a simple alias for `doing on PAST_DATE to now`, e.g. `doing since monday`.
|
540
540
|
|
541
541
|
You can also show entries matching a search string with `doing grep` (synonym `doing search`). If you want to search with regular expressions or for an exact match, surround your search query with forward slashes, e.g. `doing search /project name/`. If you pass a search string without slashes, it's treated as a fuzzy search string, meaning matches can be found as long as the characters in the search string are in order and with no more than three other characters between each. By default searches are across all sections, but you can limit it to one with the `-s SECTION_NAME` flag. Searches can be displayed with the default template, or output as HTML, CSV, or JSON.
|
542
542
|
|
data/bin/doing
CHANGED
@@ -79,7 +79,7 @@ command %i[now next] do |c|
|
|
79
79
|
if options[:back]
|
80
80
|
date = wwid.chronify(options[:back])
|
81
81
|
|
82
|
-
|
82
|
+
exit_now! 'Unable to parse date string' if date.nil?
|
83
83
|
else
|
84
84
|
date = Time.now
|
85
85
|
end
|
@@ -87,13 +87,13 @@ command %i[now next] do |c|
|
|
87
87
|
section = wwid.guess_section(options[:s]) || options[:s].cap_first
|
88
88
|
|
89
89
|
if options[:e] || (args.empty? && $stdin.stat.size.zero?)
|
90
|
-
|
90
|
+
exit_now! 'No EDITOR variable defined in environment' if ENV['EDITOR'].nil?
|
91
91
|
|
92
92
|
input = ''
|
93
93
|
input += args.join(' ') unless args.empty?
|
94
94
|
input = wwid.fork_editor(input).strip
|
95
95
|
|
96
|
-
|
96
|
+
exit_now! 'No content' if input.empty?
|
97
97
|
|
98
98
|
title, note = wwid.format_input(input)
|
99
99
|
note.push(options[:n]) if options[:n]
|
@@ -111,7 +111,7 @@ command %i[now next] do |c|
|
|
111
111
|
wwid.add_item(title.cap_first, section, { note: note, back: date, timed: options[:f] })
|
112
112
|
wwid.write(wwid.doing_file)
|
113
113
|
else
|
114
|
-
|
114
|
+
exit_now! 'You must provide content when creating a new entry'
|
115
115
|
end
|
116
116
|
end
|
117
117
|
end
|
@@ -138,7 +138,7 @@ command :note do |c|
|
|
138
138
|
section = wwid.guess_section(options[:s]) || options[:s].cap_first
|
139
139
|
|
140
140
|
if options[:e] || (args.empty? && $stdin.stat.size.zero? && !options[:r])
|
141
|
-
|
141
|
+
exit_now! 'No EDITOR variable defined in environment' if ENV['EDITOR'].nil?
|
142
142
|
|
143
143
|
input = !args.empty? ? args.join(' ') : ''
|
144
144
|
|
@@ -147,11 +147,11 @@ command :note do |c|
|
|
147
147
|
input = prev_input + input
|
148
148
|
|
149
149
|
input = wwid.fork_editor(input).strip
|
150
|
-
|
150
|
+
exit_now! 'No content, cancelled' unless input
|
151
151
|
|
152
152
|
_title, note = wwid.format_input(input)
|
153
153
|
|
154
|
-
|
154
|
+
exit_now! 'No note content' unless note
|
155
155
|
|
156
156
|
wwid.note_last(section, note, replace: true)
|
157
157
|
elsif !args.empty?
|
@@ -165,7 +165,7 @@ command :note do |c|
|
|
165
165
|
elsif options[:r]
|
166
166
|
wwid.note_last(section, [], replace: true)
|
167
167
|
else
|
168
|
-
|
168
|
+
exit_now! 'You must provide content when adding a note'
|
169
169
|
end
|
170
170
|
wwid.write(wwid.doing_file)
|
171
171
|
end
|
@@ -196,7 +196,7 @@ command :meanwhile do |c|
|
|
196
196
|
if options[:back]
|
197
197
|
date = wwid.chronify(options[:back])
|
198
198
|
|
199
|
-
|
199
|
+
exit_now! 'Unable to parse date string' if date.nil?
|
200
200
|
else
|
201
201
|
date = Time.now
|
202
202
|
end
|
@@ -205,7 +205,7 @@ command :meanwhile do |c|
|
|
205
205
|
input = ''
|
206
206
|
|
207
207
|
if options[:e]
|
208
|
-
|
208
|
+
exit_now! 'No EDITOR variable defined in environment' if ENV['EDITOR'].nil?
|
209
209
|
|
210
210
|
input += args.join(' ') unless args.empty?
|
211
211
|
input = wwid.fork_editor(input).strip
|
@@ -243,7 +243,7 @@ long_desc %(
|
|
243
243
|
arg_name 'TYPE', must_match: /^(?:html|haml|css)/i
|
244
244
|
command :template do |c|
|
245
245
|
c.action do |_global_options, options, args|
|
246
|
-
|
246
|
+
exit_now! 'No type specified, use `doing template [HAML|CSS]`' if args.empty?
|
247
247
|
|
248
248
|
case args[0]
|
249
249
|
when /html|haml/i
|
@@ -251,7 +251,7 @@ command :template do |c|
|
|
251
251
|
when /css/i
|
252
252
|
$stdout.puts wwid.css_template
|
253
253
|
else
|
254
|
-
|
254
|
+
exit_now! 'Invalid type specified, must be HAML or CSS'
|
255
255
|
end
|
256
256
|
end
|
257
257
|
end
|
@@ -322,17 +322,17 @@ command :later do |c|
|
|
322
322
|
c.action do |_global_options, options, args|
|
323
323
|
if options[:back]
|
324
324
|
date = wwid.chronify(options[:back])
|
325
|
-
|
325
|
+
exit_now! 'Unable to parse date string' if date.nil?
|
326
326
|
else
|
327
327
|
date = Time.now
|
328
328
|
end
|
329
329
|
|
330
330
|
if options[:e] || (args.empty? && $stdin.stat.size.zero?)
|
331
|
-
|
331
|
+
exit_now! 'No EDITOR variable defined in environment' if ENV['EDITOR'].nil?
|
332
332
|
|
333
333
|
input = args.empty? ? '' : args.join(' ')
|
334
334
|
input = wwid.fork_editor(input).strip
|
335
|
-
|
335
|
+
exit_now! 'No content' unless input && !input.empty?
|
336
336
|
|
337
337
|
title, note = wwid.format_input(input)
|
338
338
|
note.push(options[:n]) if options[:n]
|
@@ -349,7 +349,7 @@ command :later do |c|
|
|
349
349
|
wwid.add_item(title.cap_first, 'Later', { note: note, back: date })
|
350
350
|
wwid.write(wwid.doing_file)
|
351
351
|
else
|
352
|
-
|
352
|
+
exit_now! 'You must provide content when creating a new entry'
|
353
353
|
end
|
354
354
|
end
|
355
355
|
end
|
@@ -397,19 +397,19 @@ command %i[done did] do |c|
|
|
397
397
|
|
398
398
|
if options[:took]
|
399
399
|
took = wwid.chronify_qty(options[:took])
|
400
|
-
|
400
|
+
exit_now! 'Unable to parse date string for --took' if took.nil?
|
401
401
|
end
|
402
402
|
|
403
403
|
if options[:back]
|
404
404
|
date = wwid.chronify(options[:back])
|
405
|
-
|
405
|
+
exit_now! 'Unable to parse date string for --back' if date.nil?
|
406
406
|
else
|
407
407
|
date = options[:took] ? Time.now - took : Time.now
|
408
408
|
end
|
409
409
|
|
410
410
|
if options[:at]
|
411
411
|
finish_date = wwid.chronify(options[:at])
|
412
|
-
|
412
|
+
exit_now! 'Unable to parse date string for --at' if finish_date.nil?
|
413
413
|
|
414
414
|
date = options[:took] ? finish_date - took : finish_date
|
415
415
|
elsif options[:took]
|
@@ -427,12 +427,12 @@ command %i[done did] do |c|
|
|
427
427
|
section = wwid.guess_section(options[:s]) || options[:s].cap_first
|
428
428
|
|
429
429
|
if options[:e]
|
430
|
-
|
430
|
+
exit_now! 'No EDITOR variable defined in environment' if ENV['EDITOR'].nil?
|
431
431
|
|
432
432
|
input = ''
|
433
433
|
input += args.join(' ') unless args.empty?
|
434
434
|
input = wwid.fork_editor(input).strip
|
435
|
-
|
435
|
+
exit_now! 'No content' unless input && !input.empty?
|
436
436
|
|
437
437
|
title, note = wwid.format_input(input)
|
438
438
|
title += " @done#{donedate}"
|
@@ -467,7 +467,7 @@ command %i[done did] do |c|
|
|
467
467
|
wwid.add_item(title.cap_first, section.cap_first, { note: note, back: date })
|
468
468
|
wwid.write(wwid.doing_file)
|
469
469
|
else
|
470
|
-
|
470
|
+
exit_now! 'You must provide content when creating a new entry'
|
471
471
|
end
|
472
472
|
end
|
473
473
|
end
|
@@ -513,9 +513,9 @@ command :cancel do |c|
|
|
513
513
|
end
|
514
514
|
end
|
515
515
|
|
516
|
-
|
516
|
+
exit_now! 'Only one argument allowed' if args.length > 1
|
517
517
|
|
518
|
-
|
518
|
+
exit_now! 'Invalid argument (specify number of recent items to mark @done)' unless args.empty? || args[0] =~ /\d+/
|
519
519
|
|
520
520
|
count = args[0] ? args[0].to_i : 1
|
521
521
|
opts = {
|
@@ -580,14 +580,14 @@ command :finish do |c|
|
|
580
580
|
section = wwid.guess_section(options[:s]) || options[:s].cap_first
|
581
581
|
|
582
582
|
unless options[:auto]
|
583
|
-
|
583
|
+
exit_now! '--back and --took cannot be used together' if options[:back] && options[:took]
|
584
584
|
|
585
|
-
|
585
|
+
exit_now! '--search and --tag cannot be used together' if options[:search] && options[:tag]
|
586
586
|
|
587
587
|
if options[:back]
|
588
588
|
date = wwid.chronify(options[:back])
|
589
589
|
|
590
|
-
|
590
|
+
exit_now! 'Unable to parse date string' if date.nil?
|
591
591
|
elsif options[:took]
|
592
592
|
date = wwid.chronify_qty(options[:took])
|
593
593
|
else
|
@@ -611,9 +611,9 @@ command :finish do |c|
|
|
611
611
|
end
|
612
612
|
end
|
613
613
|
|
614
|
-
|
614
|
+
exit_now! 'Only one argument allowed' if args.length > 1
|
615
615
|
|
616
|
-
|
616
|
+
exit_now! 'Invalid argument (specify number of recent items to mark @done)' unless args.length == 0 || args[0] =~ /\d+/
|
617
617
|
|
618
618
|
count = args[0] ? args[0].to_i : 1
|
619
619
|
opts = {
|
@@ -695,6 +695,9 @@ command :tag do |c|
|
|
695
695
|
c.arg_name 'COUNT'
|
696
696
|
c.flag %i[c count], default_value: 1
|
697
697
|
|
698
|
+
c.desc 'Don\'t ask permission to tag all entries when count is 0'
|
699
|
+
c.switch %i[force], negatable: false, default_value: false
|
700
|
+
|
698
701
|
c.desc 'Include current date/time with tag'
|
699
702
|
c.switch %i[d date], negatable: false, default_value: false
|
700
703
|
|
@@ -707,12 +710,48 @@ command :tag do |c|
|
|
707
710
|
c.desc 'Autotag entries based on autotag configuration in ~/.doingrc'
|
708
711
|
c.switch %i[a autotag], negatable: false, default_value: false
|
709
712
|
|
713
|
+
c.desc 'Tag the last X entries containing TAG.
|
714
|
+
Separate multiple tags with comma (--tag=tag1,tag2), combine with --bool'
|
715
|
+
c.arg_name 'TAG'
|
716
|
+
c.flag [:tag]
|
717
|
+
|
718
|
+
c.desc 'Tag entries matching search filter, surround with slashes for regex (e.g. "/query.*/")'
|
719
|
+
c.arg_name 'QUERY'
|
720
|
+
c.flag [:search]
|
721
|
+
|
722
|
+
c.desc 'Boolean (AND|OR|NOT) with which to combine multiple tag filters'
|
723
|
+
c.arg_name 'BOOLEAN'
|
724
|
+
c.flag [:bool], must_match: /(?:and|all|any|or|not|none)/i, default_value: 'AND'
|
725
|
+
|
710
726
|
c.action do |_global_options, options, args|
|
711
|
-
|
727
|
+
exit_now! 'You must specify at least one tag' if args.empty? && !options[:a]
|
712
728
|
|
713
|
-
|
729
|
+
exit_now! '--search and --tag cannot be used together' if options[:search] && options[:tag]
|
730
|
+
|
731
|
+
section = 'All'
|
732
|
+
|
733
|
+
if options[:section]
|
734
|
+
section = wwid.guess_section(options[:section]) || options[:section].cap_first
|
735
|
+
end
|
736
|
+
|
737
|
+
|
738
|
+
if options[:tag].nil?
|
739
|
+
search_tags = []
|
740
|
+
else
|
741
|
+
search_tags = options[:tag].split(/ *, */).map { |t| t.strip.sub(/^@/, '') }
|
742
|
+
options[:bool] = case options[:bool]
|
743
|
+
when /(and|all)/i
|
744
|
+
'AND'
|
745
|
+
when /(any|or)/i
|
746
|
+
'OR'
|
747
|
+
when /(not|none)/i
|
748
|
+
'NOT'
|
749
|
+
else
|
750
|
+
'AND'
|
751
|
+
end
|
752
|
+
end
|
714
753
|
|
715
|
-
if options[:
|
754
|
+
if options[:autotag]
|
716
755
|
tags = []
|
717
756
|
else
|
718
757
|
tags = if args.join('') =~ /,/
|
@@ -724,10 +763,19 @@ command :tag do |c|
|
|
724
763
|
tags.map! { |tag| tag.sub(/^@/, '').strip }
|
725
764
|
end
|
726
765
|
|
727
|
-
count = options[:
|
766
|
+
count = options[:count].to_i
|
767
|
+
|
768
|
+
if count.zero? && !options[:force]
|
769
|
+
if options[:search]
|
770
|
+
section_q = ' matching your search terms'
|
771
|
+
elsif options[:tag]
|
772
|
+
section_q = ' matching your tag search'
|
773
|
+
elsif section == 'All'
|
774
|
+
section_q = ''
|
775
|
+
else
|
776
|
+
section_q = " in section #{section}"
|
777
|
+
end
|
728
778
|
|
729
|
-
if count.zero?
|
730
|
-
section_q = section == 'All' ? '' : " in section #{section}"
|
731
779
|
|
732
780
|
question = if options[:a]
|
733
781
|
"Are you sure you want to autotag all records#{section_q}"
|
@@ -739,14 +787,18 @@ command :tag do |c|
|
|
739
787
|
|
740
788
|
res = wwid.yn(question, default_response: false)
|
741
789
|
|
742
|
-
|
790
|
+
exit_now! 'Cancelled' unless res
|
743
791
|
end
|
792
|
+
|
744
793
|
opts = {
|
745
794
|
autotag: options[:a],
|
746
795
|
count: count,
|
747
796
|
date: options[:date],
|
748
797
|
remove: options[:r],
|
798
|
+
search: options[:search],
|
749
799
|
section: section,
|
800
|
+
tag: search_tags,
|
801
|
+
tag_bool: options[:bool],
|
750
802
|
tags: tags,
|
751
803
|
unfinished: options[:unfinished]
|
752
804
|
}
|
@@ -813,10 +865,10 @@ command :show do |c|
|
|
813
865
|
c.flag %i[f from]
|
814
866
|
|
815
867
|
c.desc 'Show time intervals on @done tasks'
|
816
|
-
c.switch %i[t times], default_value: true
|
868
|
+
c.switch %i[t times], default_value: true, negatable: true
|
817
869
|
|
818
870
|
c.desc 'Show intervals with totals at the end of output'
|
819
|
-
c.switch [:totals], default_value: false, negatable:
|
871
|
+
c.switch [:totals], default_value: false, negatable: false
|
820
872
|
|
821
873
|
c.desc 'Sort tags by (name|time)'
|
822
874
|
default = 'time'
|
@@ -845,7 +897,7 @@ command :show do |c|
|
|
845
897
|
section = 'All'
|
846
898
|
else
|
847
899
|
section = wwid.guess_section(args[0])
|
848
|
-
|
900
|
+
exit_now! "No such section: #{args[0]}" unless section
|
849
901
|
|
850
902
|
args.shift
|
851
903
|
end
|
@@ -935,10 +987,10 @@ command [:grep, :search] do |c|
|
|
935
987
|
c.flag %i[o output], must_match: /^(?:template|html|csv|json|timeline)$/i
|
936
988
|
|
937
989
|
c.desc 'Show time intervals on @done tasks'
|
938
|
-
c.switch %i[t times], default_value: true
|
990
|
+
c.switch %i[t times], default_value: true, negatable: true
|
939
991
|
|
940
992
|
c.desc 'Show intervals with totals at the end of output'
|
941
|
-
c.switch [:totals], default_value: false, negatable:
|
993
|
+
c.switch [:totals], default_value: false, negatable: false
|
942
994
|
|
943
995
|
c.desc 'Sort tags by (name|time)'
|
944
996
|
default = 'time'
|
@@ -982,10 +1034,10 @@ command :recent do |c|
|
|
982
1034
|
c.flag %i[s section], default_value: 'All'
|
983
1035
|
|
984
1036
|
c.desc 'Show time intervals on @done tasks'
|
985
|
-
c.switch %i[t times], default_value: true
|
1037
|
+
c.switch %i[t times], default_value: true, negatable: true
|
986
1038
|
|
987
1039
|
c.desc 'Show intervals with totals at the end of output'
|
988
|
-
c.switch [:totals], default_value: false, negatable:
|
1040
|
+
c.switch [:totals], default_value: false, negatable: false
|
989
1041
|
|
990
1042
|
c.desc 'Sort tags by (name|time)'
|
991
1043
|
default = 'time'
|
@@ -1022,10 +1074,10 @@ command :today do |c|
|
|
1022
1074
|
c.flag %i[s section], default_value: 'All'
|
1023
1075
|
|
1024
1076
|
c.desc 'Show time intervals on @done tasks'
|
1025
|
-
c.switch %i[t times], default_value: true
|
1077
|
+
c.switch %i[t times], default_value: true, negatable: true
|
1026
1078
|
|
1027
1079
|
c.desc 'Show time totals at the end of output'
|
1028
|
-
c.switch [:totals], default_value: false, negatable:
|
1080
|
+
c.switch [:totals], default_value: false, negatable: false
|
1029
1081
|
|
1030
1082
|
c.desc 'Sort tags by (name|time)'
|
1031
1083
|
default = 'time'
|
@@ -1057,10 +1109,10 @@ command :on do |c|
|
|
1057
1109
|
c.flag %i[s section], default_value: 'All'
|
1058
1110
|
|
1059
1111
|
c.desc 'Show time intervals on @done tasks'
|
1060
|
-
c.switch %i[t times], default_value: true
|
1112
|
+
c.switch %i[t times], default_value: true, negatable: true
|
1061
1113
|
|
1062
1114
|
c.desc 'Show time totals at the end of output'
|
1063
|
-
c.switch [:totals], default_value: false, negatable:
|
1115
|
+
c.switch [:totals], default_value: false, negatable: false
|
1064
1116
|
|
1065
1117
|
c.desc 'Sort tags by (name|time)'
|
1066
1118
|
default = 'time'
|
@@ -1100,6 +1152,55 @@ command :on do |c|
|
|
1100
1152
|
end
|
1101
1153
|
end
|
1102
1154
|
|
1155
|
+
desc 'List entries since a date'
|
1156
|
+
long_desc %(Date argument can be natural language and are always interpreted as being in the past. "thursday" would be interpreted as "last thursday,"
|
1157
|
+
and "2d" would be interpreted as "two days ago.")
|
1158
|
+
arg_name 'DATE_STRING'
|
1159
|
+
command :since do |c|
|
1160
|
+
c.desc 'Section'
|
1161
|
+
c.arg_name 'NAME'
|
1162
|
+
c.flag %i[s section], default_value: 'All'
|
1163
|
+
|
1164
|
+
c.desc 'Show time intervals on @done tasks'
|
1165
|
+
c.switch %i[t times], default_value: true, negatable: true
|
1166
|
+
|
1167
|
+
c.desc 'Show time totals at the end of output'
|
1168
|
+
c.switch [:totals], default_value: false, negatable: false
|
1169
|
+
|
1170
|
+
c.desc 'Sort tags by (name|time)'
|
1171
|
+
default = 'time'
|
1172
|
+
default = wwid.config['tag_sort'] if wwid.config.key?('tag_sort')
|
1173
|
+
c.arg_name 'KEY'
|
1174
|
+
c.flag [:tag_sort], must_match: /^(?:name|time)$/i, default_value: default
|
1175
|
+
|
1176
|
+
c.desc 'Output to export format (csv|html|json|template|timeline)'
|
1177
|
+
c.arg_name 'FORMAT'
|
1178
|
+
c.flag %i[o output], must_match: /^(?:template|html|csv|json|timeline)$/i
|
1179
|
+
|
1180
|
+
c.action do |_global_options, options, args|
|
1181
|
+
exit_now! 'Missing date argument' if args.empty?
|
1182
|
+
|
1183
|
+
date_string = args.join(' ')
|
1184
|
+
|
1185
|
+
date_string += ' at midnight' unless date_string =~ /(\d:|\d *[ap]m?|midnight|noon)/i
|
1186
|
+
date_string.sub!(/(day) (\d)/, '\1 at \2') if date_string =~ /day \d/
|
1187
|
+
|
1188
|
+
start = wwid.chronify(date_string)
|
1189
|
+
finish = Time.now
|
1190
|
+
|
1191
|
+
exit_now! 'Unrecognized date string' unless start
|
1192
|
+
|
1193
|
+
message = "Date interpreted as #{start} through the current time"
|
1194
|
+
wwid.results.push(message)
|
1195
|
+
|
1196
|
+
options[:t] = true if options[:totals]
|
1197
|
+
options[:sort_tags] = options[:tag_sort] =~ /^n/i
|
1198
|
+
|
1199
|
+
puts wwid.list_date([start, finish], options[:s], options[:t], options[:output],
|
1200
|
+
{ totals: options[:totals], sort_tags: options[:sort_tags] }).chomp
|
1201
|
+
end
|
1202
|
+
end
|
1203
|
+
|
1103
1204
|
desc 'List entries from yesterday'
|
1104
1205
|
command :yesterday do |c|
|
1105
1206
|
c.desc 'Specify a section'
|
@@ -1111,10 +1212,10 @@ command :yesterday do |c|
|
|
1111
1212
|
c.flag %i[o output], must_match: /^(?:template|html|csv|json|timeline)$/i
|
1112
1213
|
|
1113
1214
|
c.desc 'Show time intervals on @done tasks'
|
1114
|
-
c.switch %i[t times], default_value: true
|
1215
|
+
c.switch %i[t times], default_value: true, negatable: true
|
1115
1216
|
|
1116
1217
|
c.desc 'Show time totals at the end of output'
|
1117
|
-
c.switch [:totals], default_value: false, negatable:
|
1218
|
+
c.switch [:totals], default_value: false, negatable: false
|
1118
1219
|
|
1119
1220
|
c.desc 'Sort tags by (name|time)'
|
1120
1221
|
default = 'time'
|
@@ -1151,7 +1252,7 @@ command :last do |c|
|
|
1151
1252
|
c.flag [:search]
|
1152
1253
|
|
1153
1254
|
c.action do |_global_options, options, _args|
|
1154
|
-
|
1255
|
+
exit_now! '--tag and --search cannot be used together' if options[:tag] && options[:search]
|
1155
1256
|
|
1156
1257
|
if options[:tag].nil?
|
1157
1258
|
tags = []
|
@@ -1200,7 +1301,7 @@ desc 'Add a new section to the "doing" file'
|
|
1200
1301
|
arg_name 'SECTION_NAME'
|
1201
1302
|
command :add_section do |c|
|
1202
1303
|
c.action do |_global_options, _options, args|
|
1203
|
-
|
1304
|
+
exit_now! "Section #{args[0]} already exists" if wwid.sections.include?(args[0])
|
1204
1305
|
|
1205
1306
|
wwid.add_section(args[0].cap_first)
|
1206
1307
|
wwid.write(wwid.doing_file)
|
@@ -1241,10 +1342,10 @@ command :view do |c|
|
|
1241
1342
|
c.flag %i[o output], must_match: /^(?:template|html|csv|json|timeline)$/i
|
1242
1343
|
|
1243
1344
|
c.desc 'Show time intervals on @done tasks'
|
1244
|
-
c.switch %i[t times], default_value: true
|
1345
|
+
c.switch %i[t times], default_value: true, negatable: true
|
1245
1346
|
|
1246
1347
|
c.desc 'Show intervals with totals at the end of output'
|
1247
|
-
c.switch [:totals], default_value: false, negatable:
|
1348
|
+
c.switch [:totals], default_value: false, negatable: false
|
1248
1349
|
|
1249
1350
|
c.desc 'Include colors in output'
|
1250
1351
|
c.switch [:color], default_value: true, negatable: true
|
@@ -1256,7 +1357,7 @@ command :view do |c|
|
|
1256
1357
|
c.flag [:tag_sort], must_match: /^(?:name|time)$/i, default_value: default
|
1257
1358
|
|
1258
1359
|
c.desc 'Only show items with recorded time intervals'
|
1259
|
-
c.switch [:only_timed], default_value: false, negatable:
|
1360
|
+
c.switch [:only_timed], default_value: false, negatable: false
|
1260
1361
|
|
1261
1362
|
c.action do |_global_options, options, args|
|
1262
1363
|
title = if args.empty?
|
@@ -1328,7 +1429,7 @@ command :view do |c|
|
|
1328
1429
|
elsif title.instance_of?(FalseClass)
|
1329
1430
|
exit_now! 'Cancelled'
|
1330
1431
|
else
|
1331
|
-
|
1432
|
+
exit_now! "View #{title} not found in config"
|
1332
1433
|
end
|
1333
1434
|
end
|
1334
1435
|
end
|
@@ -1385,7 +1486,7 @@ command :archive do |c|
|
|
1385
1486
|
tags = args.length > 1 ? args[1..].map { |t| t.sub(/^@/, '').strip } : []
|
1386
1487
|
end
|
1387
1488
|
|
1388
|
-
|
1489
|
+
exit_now! '--keep and --count can\'t be used together' if options[:keep] && options[:count]
|
1389
1490
|
|
1390
1491
|
tags.concat(options[:tag].split(/ *, */).map { |t| t.sub(/^@/, '').strip }) if options[:tag]
|
1391
1492
|
|
@@ -1436,7 +1537,7 @@ command :open do |c|
|
|
1436
1537
|
elsif options[:b]
|
1437
1538
|
system %(open -b "#{options[:b]}" "#{File.expand_path(wwid.doing_file)}")
|
1438
1539
|
elsif options[:e]
|
1439
|
-
|
1540
|
+
exit_now! 'No EDITOR variable defined in environment' if ENV['EDITOR'].nil?
|
1440
1541
|
|
1441
1542
|
system %($EDITOR "#{File.expand_path(wwid.doing_file)}")
|
1442
1543
|
elsif wwid.config.key?('editor_app') && !wwid.config['editor_app'].nil?
|
@@ -1446,7 +1547,7 @@ command :open do |c|
|
|
1446
1547
|
end
|
1447
1548
|
|
1448
1549
|
else
|
1449
|
-
|
1550
|
+
exit_now! 'No EDITOR variable defined in environment' if ENV['EDITOR'].nil?
|
1450
1551
|
|
1451
1552
|
system %($EDITOR "#{File.expand_path(wwid.doing_file)}")
|
1452
1553
|
end
|
@@ -1483,13 +1584,13 @@ command :config do |c|
|
|
1483
1584
|
`open -b #{options[:b]} "#{wwid.config_file}"`
|
1484
1585
|
end
|
1485
1586
|
else
|
1486
|
-
|
1587
|
+
exit_now! 'No EDITOR variable defined in environment' if options[:e].nil? && ENV['EDITOR'].nil?
|
1487
1588
|
|
1488
1589
|
editor = options[:e].nil? ? ENV['EDITOR'] : options[:e]
|
1489
1590
|
system %(#{editor} "#{wwid.config_file}")
|
1490
1591
|
end
|
1491
1592
|
else
|
1492
|
-
|
1593
|
+
exit_now! 'No EDITOR variable defined in environment' if options[:e].nil? && ENV['EDITOR'].nil?
|
1493
1594
|
|
1494
1595
|
editor = options[:e].nil? ? ENV['EDITOR'] : options[:e]
|
1495
1596
|
system %(#{editor} "#{wwid.config_file}")
|
@@ -1542,7 +1643,7 @@ command :import do |c|
|
|
1542
1643
|
wwid.write(wwid.doing_file)
|
1543
1644
|
end
|
1544
1645
|
else
|
1545
|
-
|
1646
|
+
exit_now! 'Invalid import type'
|
1546
1647
|
end
|
1547
1648
|
end
|
1548
1649
|
end
|
data/lib/doing/version.rb
CHANGED
data/lib/doing/wwid.rb
CHANGED
@@ -73,7 +73,7 @@ class WWID
|
|
73
73
|
rescue StandardError
|
74
74
|
@config = {}
|
75
75
|
@local_config = {}
|
76
|
-
#
|
76
|
+
# exit_now! "error reading config"
|
77
77
|
end
|
78
78
|
end
|
79
79
|
|
@@ -158,7 +158,7 @@ class WWID
|
|
158
158
|
|
159
159
|
# if ENV['DOING_DEBUG'].to_i == 3
|
160
160
|
# if @config['default_tags'].length > 0
|
161
|
-
#
|
161
|
+
# exit_now! "DEFAULT CONFIG CHANGED"
|
162
162
|
# end
|
163
163
|
# end
|
164
164
|
|
@@ -293,7 +293,7 @@ class WWID
|
|
293
293
|
if $?.exitstatus == 0
|
294
294
|
input = IO.read(tmpfile.path)
|
295
295
|
else
|
296
|
-
|
296
|
+
exit_now! 'Cancelled'
|
297
297
|
end
|
298
298
|
ensure
|
299
299
|
tmpfile.close
|
@@ -311,11 +311,11 @@ class WWID
|
|
311
311
|
# @param input (String) The string to parse
|
312
312
|
#
|
313
313
|
def format_input(input)
|
314
|
-
|
314
|
+
exit_now! 'No content in entry' if input.nil? || input.strip.empty?
|
315
315
|
|
316
316
|
input_lines = input.split(/[\n\r]+/).delete_if {|line| line =~ /^#/ || line =~ /^\s*$/ }
|
317
317
|
title = input_lines[0]&.strip
|
318
|
-
|
318
|
+
exit_now! 'No content in first line' if title.nil? || title.strip.empty?
|
319
319
|
|
320
320
|
note = input_lines.length > 1 ? input_lines[1..-1] : []
|
321
321
|
# If title line ends in a parenthetical, use that as the note
|
@@ -346,7 +346,7 @@ class WWID
|
|
346
346
|
#
|
347
347
|
def chronify(input)
|
348
348
|
now = Time.now
|
349
|
-
|
349
|
+
exit_now! "Invalid time expression #{input.inspect}" if input.to_s.strip == ''
|
350
350
|
|
351
351
|
secs_ago = if input.match(/^(\d+)$/)
|
352
352
|
# plain number, assume minutes
|
@@ -438,7 +438,7 @@ class WWID
|
|
438
438
|
end
|
439
439
|
unless section || guessed
|
440
440
|
alt = guess_view(frag, true)
|
441
|
-
|
441
|
+
exit_now! "Did you mean `doing view #{alt}`?" if alt
|
442
442
|
|
443
443
|
res = yn("Section #{frag} not found, create it", default_response: false)
|
444
444
|
|
@@ -448,7 +448,7 @@ class WWID
|
|
448
448
|
return frag.cap_first
|
449
449
|
end
|
450
450
|
|
451
|
-
|
451
|
+
exit_now! "Unknown section: #{frag}"
|
452
452
|
end
|
453
453
|
section ? section.cap_first : guessed
|
454
454
|
end
|
@@ -518,9 +518,9 @@ class WWID
|
|
518
518
|
unless view || guessed
|
519
519
|
alt = guess_section(frag, guessed: true)
|
520
520
|
if alt
|
521
|
-
|
521
|
+
exit_now! "Did you mean `doing show #{alt}`?"
|
522
522
|
else
|
523
|
-
|
523
|
+
exit_now! "Unknown view: #{frag}"
|
524
524
|
end
|
525
525
|
end
|
526
526
|
view
|
@@ -631,7 +631,7 @@ class WWID
|
|
631
631
|
|
632
632
|
add_tags = opt[:tag] ? opt[:tag].split(/[ ,]+/).map { |t| t.sub(/^@?/, '@') }.join(' ') : ''
|
633
633
|
prefix = opt[:prefix] ? opt[:prefix] : '[Timing.app]'
|
634
|
-
|
634
|
+
exit_now! "File not found" unless File.exist?(File.expand_path(path))
|
635
635
|
|
636
636
|
data = JSON.parse(IO.read(File.expand_path(path)))
|
637
637
|
new_items = []
|
@@ -688,7 +688,7 @@ class WWID
|
|
688
688
|
section = combined['items'].dup.sort_by { |item| item['date'] }.reverse[0]['section']
|
689
689
|
end
|
690
690
|
|
691
|
-
|
691
|
+
exit_now! "Section #{section} not found" unless @content.key?(section)
|
692
692
|
|
693
693
|
last_item = @content[section]['items'].dup.sort_by { |item| item['date'] }.reverse[0]
|
694
694
|
warn "Editing note for #{last_item['title']}"
|
@@ -773,7 +773,7 @@ class WWID
|
|
773
773
|
## @param opt (Hash) Additional options
|
774
774
|
##
|
775
775
|
def interactive(opt = {})
|
776
|
-
|
776
|
+
exit_now! "Select command requires that fzf be installed" unless exec_available('fzf')
|
777
777
|
|
778
778
|
section = opt[:section] ? guess_section(opt[:section]) : 'All'
|
779
779
|
|
@@ -895,7 +895,7 @@ class WWID
|
|
895
895
|
## @param opt (Hash) Additional Options
|
896
896
|
##
|
897
897
|
def tag_last(opt = {})
|
898
|
-
opt[:section] ||=
|
898
|
+
opt[:section] ||= nil
|
899
899
|
opt[:count] ||= 1
|
900
900
|
opt[:archive] ||= false
|
901
901
|
opt[:tags] ||= ['done']
|
@@ -910,7 +910,11 @@ class WWID
|
|
910
910
|
sec_arr = []
|
911
911
|
|
912
912
|
if opt[:section].nil?
|
913
|
-
|
913
|
+
if opt[:search] || opt[:tag]
|
914
|
+
sec_arr = sections
|
915
|
+
else
|
916
|
+
sec_arr = [@current_section]
|
917
|
+
end
|
914
918
|
elsif opt[:section].instance_of?(String)
|
915
919
|
if opt[:section] =~ /^all$/i
|
916
920
|
if opt[:count] == 1
|
@@ -921,7 +925,11 @@ class WWID
|
|
921
925
|
items = combined['items'].dup.sort_by { |item| item['date'] }.reverse
|
922
926
|
sec_arr.push(items[0]['section'])
|
923
927
|
elsif opt[:count] > 1
|
924
|
-
|
928
|
+
if opt[:search] || opt[:tag]
|
929
|
+
sec_arr = sections
|
930
|
+
else
|
931
|
+
exit_now! 'A count greater than one requires a section to be specified'
|
932
|
+
end
|
925
933
|
else
|
926
934
|
sec_arr = sections
|
927
935
|
end
|
@@ -1021,7 +1029,7 @@ class WWID
|
|
1021
1029
|
@results.push('Archiving is skipped when operating on all entries') if (opt[:count]).zero?
|
1022
1030
|
end
|
1023
1031
|
else
|
1024
|
-
|
1032
|
+
exit_now! "Section not found: #{section}"
|
1025
1033
|
end
|
1026
1034
|
end
|
1027
1035
|
|
@@ -1191,7 +1199,7 @@ class WWID
|
|
1191
1199
|
section = combined['items'].dup.sort_by { |item| item['date'] }.reverse[0]['section']
|
1192
1200
|
end
|
1193
1201
|
|
1194
|
-
|
1202
|
+
exit_now! "Section #{section} not found" unless @content.key?(section)
|
1195
1203
|
|
1196
1204
|
# sort_section(opt[:section])
|
1197
1205
|
items = @content[section]['items'].dup.sort_by { |item| item['date'] }.reverse
|
@@ -1427,7 +1435,7 @@ class WWID
|
|
1427
1435
|
end
|
1428
1436
|
end
|
1429
1437
|
|
1430
|
-
|
1438
|
+
exit_now! 'Invalid section object' unless opt[:section].instance_of? Hash
|
1431
1439
|
|
1432
1440
|
items = opt[:section]['items'].sort_by { |item| item['date'] }
|
1433
1441
|
|
@@ -1477,7 +1485,7 @@ class WWID
|
|
1477
1485
|
|
1478
1486
|
out = ''
|
1479
1487
|
|
1480
|
-
|
1488
|
+
exit_now! 'Unknown output format' if opt[:output] && (opt[:output] !~ /^(template|html|csv|json|timeline)$/i)
|
1481
1489
|
|
1482
1490
|
case opt[:output]
|
1483
1491
|
when /^csv$/i
|
@@ -1750,7 +1758,7 @@ class WWID
|
|
1750
1758
|
do_archive(section, destination, { count: count, tags: tags, bool: bool, search: options[:search], label: options[:label] })
|
1751
1759
|
write(doing_file)
|
1752
1760
|
else
|
1753
|
-
|
1761
|
+
exit_now! 'Either source or destination does not exist'
|
1754
1762
|
end
|
1755
1763
|
end
|
1756
1764
|
|
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: 1.0.
|
4
|
+
version: 1.0.74
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brett Terpstra
|
@@ -36,14 +36,14 @@ dependencies:
|
|
36
36
|
requirements:
|
37
37
|
- - "~>"
|
38
38
|
- !ruby/object:Gem::Version
|
39
|
-
version: 6.
|
39
|
+
version: 6.3.1
|
40
40
|
type: :development
|
41
41
|
prerelease: false
|
42
42
|
version_requirements: !ruby/object:Gem::Requirement
|
43
43
|
requirements:
|
44
44
|
- - "~>"
|
45
45
|
- !ruby/object:Gem::Version
|
46
|
-
version: 6.
|
46
|
+
version: 6.3.1
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: aruba
|
49
49
|
requirement: !ruby/object:Gem::Requirement
|
@@ -62,16 +62,16 @@ dependencies:
|
|
62
62
|
name: test-unit
|
63
63
|
requirement: !ruby/object:Gem::Requirement
|
64
64
|
requirements:
|
65
|
-
- - "
|
65
|
+
- - "~>"
|
66
66
|
- !ruby/object:Gem::Version
|
67
|
-
version:
|
67
|
+
version: 3.4.4
|
68
68
|
type: :development
|
69
69
|
prerelease: false
|
70
70
|
version_requirements: !ruby/object:Gem::Requirement
|
71
71
|
requirements:
|
72
|
-
- - "
|
72
|
+
- - "~>"
|
73
73
|
- !ruby/object:Gem::Version
|
74
|
-
version:
|
74
|
+
version: 3.4.4
|
75
75
|
- !ruby/object:Gem::Dependency
|
76
76
|
name: gli
|
77
77
|
requirement: !ruby/object:Gem::Requirement
|