doing 1.0.66 → 1.0.70

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6e02c8453f718dfa428e578ab1b125528aa07ffee1390bbe8222fb425abd63fa
4
- data.tar.gz: 333707321b7e92312ee8a2874292b45df782c83eff2ddcdb5279b648b14730ff
3
+ metadata.gz: 10dc5d92f3e7079a9525d39c47fe0f69ef22f64ae5400cd9da1925f9b0fc1964
4
+ data.tar.gz: ec08df722ce07bbfcee808bfd4bd9eabe5470ef173f5d11e47fc52356b2c2c95
5
5
  SHA512:
6
- metadata.gz: 187ce7735035b86ae1233d08dd154f502c7b2def0fc1fd11532de5256647c8cce57bd4ea73ca3fa704c2dfb4a307b94f339222a476e3fd30ba991fea77daa190
7
- data.tar.gz: 29f7123aa51cb8a5754bca68854dbbb606f69533d8cdbd90a7a2187ca1cc29dd09131aed0787f72fd680e2d04fe9e47a92555cf90c1327b74cba23cc8a2fab2b
6
+ metadata.gz: c56a9a328d2ed31e70633333be5f7666d9586154fba7cc59f34116f1a972156fb4dd7d087e59452bf860167e618dad815da8a113fe0e4cf9f12faafa98d68d72
7
+ data.tar.gz: 3152021d8017af18e4046736f1f994bd1821f95bece924ca9ad78de997ad3473cbfd411389ede1dfe6313a1c4fde3debdc9907f3f2da899b1bab823a02a932e7
data/README.md CHANGED
@@ -25,11 +25,9 @@ While I'm working, I have hourly reminders to record what I'm working on, and I
25
25
 
26
26
  If there's something I want to look at later but doesn't need to be added to a task list or tracker, I can type `doing later check out the pinboard bookmarks from macdrifter`. When I get back to my computer --- or just need a refresher after a distraction --- I can type `doing last` to see what the last thing on my plate was. I can also type `doing recent` (or just `doing`) to get a list of the last few entries. `doing today` gives me everything since midnight for the current day, making it easy to see what I've accomplished over a sleepless night.
27
27
 
28
- _Side note:_ I actually use the library behind this utility as part of another script that mirrors entries in [Day One](http://dayoneapp.com) that have the tag `wwid`. I can use the hourly writing reminders and enter my stuff in the quick entry popup. Someday I'll get around to cleaning that up and putting it out there.
29
-
30
28
  ## Installation
31
29
 
32
- The current version of `doing` is <!--VER-->1.0.65<!--END VER-->.
30
+ The current version of `doing` is <!--VER-->1.0.69<!--END VER-->.
33
31
 
34
32
  $ [sudo] gem install doing
35
33
 
@@ -464,6 +462,7 @@ You can also include a `--no-date` switch to add `@done` without a finish date,
464
462
 
465
463
  By default `doing finish` works on a single entry, the last entry or the most recent entry matching a `--tag` or `--search` query. Specifying `doing finish 10` would finish any unfinished entries within the last 10 entries. In the case of `--tag` or `--search` queries, the count serves as the maximum number of matches doing will act on, sorted in reverse date order (most recent first). A count of 0 will disable the limit entirely, acting on all matching entries.
466
464
 
465
+ Both `finish` and `cancel` accept `--unfinished` as an argument. This causes them to act on the last entry not already marked @done, no matter how far back it's dated or how many @done entries come after it. You can use `doing finish --unfinished X -s SECTION` to finish the last X unfinished entries in SECTION.
467
466
 
468
467
  ##### Tagging and Autotagging
469
468
 
data/bin/doing CHANGED
@@ -368,7 +368,7 @@ command %i[done did] do |c|
368
368
 
369
369
  date = options[:took] ? finish_date - took : finish_date
370
370
  elsif options[:took]
371
- finish_date = options[:back] ? date + took : nil
371
+ finish_date = date + took
372
372
  elsif options[:back]
373
373
  finish_date = date
374
374
  else
@@ -446,6 +446,9 @@ command :cancel do |c|
446
446
  c.arg_name 'BOOLEAN'
447
447
  c.flag [:bool], must_match: /(?:and|all|any|or|not|none)/i, default_value: 'AND'
448
448
 
449
+ c.desc 'Cancel last entry (or entries) not already marked @done'
450
+ c.switch %i[u unfinished], negatable: false, default_value: false
451
+
449
452
  c.action do |_global_options, options, args|
450
453
  section = wwid.guess_section(options[:s]) || options[:s].cap_first
451
454
 
@@ -478,7 +481,8 @@ command :cancel do |c|
478
481
  sequential: false,
479
482
  tag: tags,
480
483
  tag_bool: options[:bool],
481
- tags: ['done']
484
+ tags: ['done'],
485
+ unfinished: options[:unfinished]
482
486
  }
483
487
  wwid.tag_last(opts)
484
488
  end
@@ -512,6 +516,9 @@ command :finish do |c|
512
516
  c.arg_name 'BOOLEAN'
513
517
  c.flag [:bool], must_match: /(?:and|all|any|or|not|none)/i, default_value: 'AND'
514
518
 
519
+ c.desc 'Finish last entry (or entries) not already marked @done'
520
+ c.switch %i[u unfinished], negatable: false, default_value: false
521
+
515
522
  c.desc %(Auto-generate finish dates from next entry's start time.
516
523
  Automatically generate completion dates 1 minute before next start date.
517
524
  --auto overrides the --date and --back parameters.)
@@ -574,7 +581,8 @@ command :finish do |c|
574
581
  sequential: options[:auto],
575
582
  tag: tags,
576
583
  tag_bool: options[:bool],
577
- tags: ['done']
584
+ tags: ['done'],
585
+ unfinished: options[:unfinished]
578
586
  }
579
587
  wwid.tag_last(opts)
580
588
  end
@@ -648,6 +656,9 @@ command :tag do |c|
648
656
  c.desc 'Remove given tag(s)'
649
657
  c.switch %i[r remove], negatable: false, default_value: false
650
658
 
659
+ c.desc 'Tag last entry (or entries) not marked @done'
660
+ c.switch %i[u unfinished], negatable: false, default_value: false
661
+
651
662
  c.desc 'Autotag entries based on autotag configuration in ~/.doingrc'
652
663
  c.switch %i[a autotag], negatable: false, default_value: false
653
664
 
@@ -691,7 +702,8 @@ command :tag do |c|
691
702
  date: options[:date],
692
703
  remove: options[:r],
693
704
  section: section,
694
- tags: tags
705
+ tags: tags,
706
+ unfinished: options[:unfinished]
695
707
  }
696
708
  wwid.tag_last(opts)
697
709
  end
@@ -706,9 +718,17 @@ command [:mark, :flag] do |c|
706
718
  c.desc 'Remove mark'
707
719
  c.switch %i[r remove], negatable: false, default_value: false
708
720
 
721
+ c.desc 'Mark last entry not marked @done'
722
+ c.switch %i[u unfinished], negatable: false, default_value: false
723
+
709
724
  c.action do |_global_options, options, _args|
710
725
  mark = wwid.config['marker_tag'] || 'flagged'
711
- wwid.tag_last({ tags: [mark], section: options[:s], remove: options[:r] })
726
+ wwid.tag_last({
727
+ remove: options[:r],
728
+ section: options[:s],
729
+ tags: [mark],
730
+ unfinished: options[:unfinished]
731
+ })
712
732
  end
713
733
  end
714
734
 
@@ -737,7 +757,7 @@ command :show do |c|
737
757
 
738
758
  c.desc 'Sort order (asc/desc)'
739
759
  c.arg_name 'ORDER'
740
- c.flag %i[s sort], must_match: /^(?:a|d)/i, default_value: 'ASC'
760
+ c.flag %i[s sort], must_match: /^[ad].*/i, default_value: 'ASC'
741
761
 
742
762
  c.desc %(
743
763
  Date range to show, or a single day to filter date on.
@@ -0,0 +1,121 @@
1
+ ##
2
+ ## @brief Hash helpers
3
+ ##
4
+ class ::Hash
5
+ def has_tags?(tags, bool = :and)
6
+ tags = tags.split(/ *, */) if tags.is_a? String
7
+ bool = bool.normalize_bool if bool.is_a? String
8
+ item = self
9
+ tags.map! {|t| t.strip.sub(/^@/, '')}
10
+ case bool
11
+ when :and
12
+ result = true
13
+ tags.each do |tag|
14
+ unless item['title'] =~ /@#{tag}/
15
+ result = false
16
+ break
17
+ end
18
+ end
19
+ result
20
+ when :not
21
+ result = true
22
+ tags.each do |tag|
23
+ if item['title'] =~ /@#{tag}/
24
+ result = false
25
+ break
26
+ end
27
+ end
28
+ result
29
+ else
30
+ result = false
31
+ tags.each do |tag|
32
+ if item['title'] =~ /@#{tag}/
33
+ result = true
34
+ break
35
+ end
36
+ end
37
+ result
38
+ end
39
+ end
40
+
41
+ def matches_search?(search)
42
+ item = self
43
+ text = item['note'] ? item['title'] + item['note'].join(' ') : item['title']
44
+ pattern = if search.strip =~ %r{^/.*?/$}
45
+ search.sub(%r{/(.*?)/}, '\1')
46
+ else
47
+ search.split('').join('.{0,3}')
48
+ end
49
+ text =~ /#{pattern}/i ? true : false
50
+ end
51
+ end
52
+
53
+ ##
54
+ ## @brief String helpers
55
+ ##
56
+ class ::String
57
+ def cap_first
58
+ sub(/^\w/) do |m|
59
+ m.upcase
60
+ end
61
+ end
62
+
63
+
64
+ ##
65
+ ## @brief Convert a boolean string to a symbol
66
+ ##
67
+ ## @return Symbol :and, :or, or :not
68
+ ##
69
+ def normalize_bool!
70
+ replace normalize_bool
71
+ end
72
+
73
+ def normalize_bool(default = :and)
74
+ case self
75
+ when /(and|all)/i
76
+ :and
77
+ when /(any|or)/i
78
+ :or
79
+ when /(not|none)/i
80
+ :not
81
+ else
82
+ default.is_a?(Symbol) ? default : default.normalize_bool
83
+ end
84
+ end
85
+
86
+
87
+ ##
88
+ ## @brief Turn raw urls into HTML links
89
+ ##
90
+ ## @param opt (Hash) Additional Options
91
+ ##
92
+ def link_urls(opt = {})
93
+ opt[:format] ||= :html
94
+ if opt[:format] == :html
95
+ gsub(%r{(?mi)((http|https)://)?([\w\-_]+(\.[\w\-_]+)+)([\w\-.,@?^=%&amp;:/~+#]*[\w\-@^=%&amp;/~+#])?}) do |_match|
96
+ m = Regexp.last_match
97
+ proto = m[1].nil? ? 'http://' : ''
98
+ %(<a href="#{proto}#{m[0]}" title="Link to #{m[0]}">[#{m[3]}]</a>)
99
+ end.gsub(/<(\w+:.*?)>/) do |match|
100
+ m = Regexp.last_match
101
+ if m[1] =~ /<a href/
102
+ match
103
+ else
104
+ %(<a href="#{m[1]}" title="Link to #{m[1]}">[link]</a>)
105
+ end
106
+ end
107
+ else
108
+ self
109
+ end
110
+ end
111
+ end
112
+
113
+ class ::Symbol
114
+ def normalize_bool!
115
+ replace normalize_bool
116
+ end
117
+
118
+ def normalize_bool
119
+ to_s.normalize_bool
120
+ end
121
+ end
data/lib/doing/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Doing
2
- VERSION = '1.0.66'
2
+ VERSION = '1.0.70'
3
3
  end
data/lib/doing/wwid.rb CHANGED
@@ -781,6 +781,7 @@ class WWID
781
781
  opt[:autotag] ||= false
782
782
  opt[:back] ||= false
783
783
  opt[:took] ||= nil
784
+ opt[:unfinished] ||= false
784
785
 
785
786
  sec_arr = []
786
787
 
@@ -816,10 +817,11 @@ class WWID
816
817
  items.map! do |item|
817
818
  break if idx == count
818
819
 
820
+ finished = opt[:unfinished] && item.has_tags?('done', :and)
819
821
  tag_match = opt[:tag].nil? || opt[:tag].empty? ? true : item.has_tags?(opt[:tag], opt[:tag_bool])
820
822
  search_match = opt[:search].nil? || opt[:search].empty? ? true : item.matches_search?(opt[:search])
821
823
 
822
- if tag_match && search_match
824
+ if tag_match && search_match && !finished
823
825
  if opt[:autotag]
824
826
  new_title = autotag(item['title']) if @auto_tag
825
827
  if new_title == item['title']
@@ -832,12 +834,6 @@ class WWID
832
834
  if opt[:sequential]
833
835
  done_date = next_start - 1
834
836
  next_start = item['date']
835
- elsif opt[:back]
836
- if opt[:back].is_a? Integer
837
- done_date = item['date'] + opt[:back]
838
- else
839
- done_date = item['date'] + (opt[:back] - item['date'])
840
- end
841
837
  elsif opt[:took]
842
838
  if item['date'] + opt[:took] > Time.now
843
839
  item['date'] = Time.now - opt[:took]
@@ -845,6 +841,12 @@ class WWID
845
841
  else
846
842
  done_date = item['date'] + opt[:took]
847
843
  end
844
+ elsif opt[:back]
845
+ if opt[:back].is_a? Integer
846
+ done_date = item['date'] + opt[:back]
847
+ else
848
+ done_date = item['date'] + (opt[:back] - item['date'])
849
+ end
848
850
  else
849
851
  done_date = Time.now
850
852
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: doing
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.66
4
+ version: 1.0.70
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brett Terpstra
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-07-26 00:00:00.000000000 Z
11
+ date: 2021-09-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -165,6 +165,7 @@ files:
165
165
  - README.md
166
166
  - bin/doing
167
167
  - lib/doing.rb
168
+ - lib/doing/helpers.rb
168
169
  - lib/doing/version.rb
169
170
  - lib/doing/wwid.rb
170
171
  - lib/templates/doing.css