doing 1.0.54 → 1.0.55

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 (6) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +25 -6
  3. data/bin/doing +699 -557
  4. data/lib/doing/version.rb +1 -1
  5. data/lib/doing/wwid.rb +175 -54
  6. metadata +2 -2
data/lib/doing/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Doing
2
- VERSION = '1.0.54'
2
+ VERSION = '1.0.55'
3
3
  end
data/lib/doing/wwid.rb CHANGED
@@ -40,6 +40,17 @@ class Hash
40
40
  result
41
41
  end
42
42
  end
43
+
44
+ def matches_search?(search)
45
+ item = self
46
+ text = item['note'] ? item['title'] + item['note'].join(' ') : item['title']
47
+ pattern = if search.strip =~ %r{^/.*?/$}
48
+ search.sub(%r{/(.*?)/}, '\1')
49
+ else
50
+ search.split('').join('.{0,3}')
51
+ end
52
+ text =~ /#{pattern}/i ? true : false
53
+ end
43
54
  end
44
55
 
45
56
  ##
@@ -170,6 +181,7 @@ class WWID
170
181
  @config['autotag']['synonyms'] ||= {}
171
182
  @config['doing_file'] ||= '~/what_was_i_doing.md'
172
183
  @config['current_section'] ||= 'Currently'
184
+ @config['config_editor_app'] ||= nil
173
185
  @config['editor_app'] ||= nil
174
186
 
175
187
  @config['html_template'] ||= {}
@@ -372,7 +384,7 @@ class WWID
372
384
  tmpfile.unlink
373
385
  end
374
386
 
375
- input
387
+ input.split(/\n/).delete_if {|line| line =~ /^#/ }.join("\n").strip
376
388
  end
377
389
 
378
390
  #
@@ -385,9 +397,20 @@ class WWID
385
397
  def format_input(input)
386
398
  raise 'No content in entry' if input.nil? || input.strip.empty?
387
399
 
388
- input_lines = input.split(/[\n\r]+/)
389
- title = input_lines[0].strip
400
+ input_lines = input.split(/[\n\r]+/).delete_if {|line| line =~ /^#/ || line =~ /^\s*$/ }
401
+ title = input_lines[0]&.strip
402
+ raise 'No content in first line' if title.nil? || title.strip.empty?
403
+
390
404
  note = input_lines.length > 1 ? input_lines[1..-1] : []
405
+ # If title line ends in a parenthetical, use that as the note
406
+ if note.empty? && title =~ /\(.*?\)$/
407
+ title.sub!(/\((.*?)\)$/) do
408
+ m = Regexp.last_match
409
+ note.push(m[1])
410
+ ''
411
+ end
412
+ end
413
+
391
414
  note.map!(&:strip)
392
415
  note.delete_if { |line| line =~ /^\s*$/ || line =~ /^#/ }
393
416
 
@@ -698,7 +721,6 @@ class WWID
698
721
  ## @param opt (Hash) Additional Options
699
722
  ##
700
723
  def last_entry(opt = {})
701
- opt[:tag] ||= []
702
724
  opt[:tag_bool] ||= 'AND'
703
725
  opt[:section] ||= @current_section
704
726
 
@@ -726,7 +748,11 @@ class WWID
726
748
  all_items.concat(@content[section]['items'].dup) if @content.key?(section)
727
749
  end
728
750
 
729
- all_items.select! { |item| item.has_tags?(opt[:tag], opt[:tag_bool]) } if !opt[:tag].nil? && opt[:tag].length.positive?
751
+ if opt[:tag] && opt[:tag].length.positive?
752
+ all_items.select! { |item| item.has_tags?(opt[:tag], opt[:tag_bool]) }
753
+ elsif opt[:search]&.length
754
+ all_items.select! { |item| item.matches_search?(opt[:search]) }
755
+ end
730
756
 
731
757
  all_items.max_by { |item| item['date'] }
732
758
  end
@@ -781,9 +807,10 @@ class WWID
781
807
  items.map! do |item|
782
808
  break if index == count
783
809
 
784
- tag_match = !opt[:tag].nil? && opt[:tag].length.positive? ? item.has_tags?(opt[:tag], opt[:tag_bool]) : true
810
+ tag_match = opt[:tag].nil? || opt[:tag].empty? ? true : item.has_tags?(opt[:tag], opt[:tag_bool])
811
+ search_match = opt[:search].nil? || opt[:search].empty? ? true : item.matches_search?(opt[:search])
785
812
 
786
- if tag_match
813
+ if tag_match && search_match
787
814
  if opt[:autotag]
788
815
  new_title = autotag(item['title']) if @auto_tag
789
816
  if new_title == item['title']
@@ -797,17 +824,23 @@ class WWID
797
824
  done_date = next_start - 1
798
825
  next_start = item['date']
799
826
  elsif opt[:back]
800
- done_date = item['date'] + (opt[:back] - item['date'])
827
+ if opt[:back].is_a? Integer
828
+ done_date = item['date'] + opt[:back]
829
+ else
830
+ done_date = item['date'] + (opt[:back] - item['date'])
831
+ end
801
832
  else
802
833
  done_date = Time.now
803
834
  end
804
835
 
805
836
  title = item['title']
806
837
  opt[:tags].each do |tag|
807
- tag.strip!
808
- if opt[:remove] && title =~ /@#{tag}\b/
809
- title.gsub!(/(^| )@#{tag}(\([^)]*\))?/, '')
810
- @results.push(%(Removed @#{tag}: "#{title}" in #{section}))
838
+ tag = tag.strip
839
+ if opt[:remove]
840
+ if title =~ /@#{tag}\b/
841
+ title.gsub!(/(^| )@#{tag}(\([^)]*\))?/, '')
842
+ @results.push(%(Removed @#{tag}: "#{title}" in #{section}))
843
+ end
811
844
  elsif title !~ /@#{tag}/
812
845
  title.chomp!
813
846
  title += if opt[:date]
@@ -853,6 +886,75 @@ class WWID
853
886
  write(@doing_file)
854
887
  end
855
888
 
889
+ ##
890
+ ## @brief Edit the last entry
891
+ ##
892
+ ## @param section (String) The section, default "All"
893
+ ##
894
+ def edit_last(section: 'All', options: {})
895
+ section = guess_section(section)
896
+
897
+ if section =~ /^all$/i
898
+ items = []
899
+ @content.each do |_k, v|
900
+ items.concat(v['items'])
901
+ end
902
+ # section = combined['items'].dup.sort_by { |item| item['date'] }.reverse[0]['section']
903
+ else
904
+ items = @content[section]['items']
905
+ end
906
+
907
+ items = items.sort_by { |item| item['date'] }.reverse
908
+
909
+ idx = nil
910
+
911
+ if options[:tag] && !options[:tag].empty?
912
+ items.each_with_index do |item, i|
913
+ if item.has_tags?(options[:tag], options[:tag_bool])
914
+ idx = i
915
+ break
916
+ end
917
+ end
918
+ elsif options[:search]
919
+ items.each_with_index do |item, i|
920
+ if item.matches_search?(options[:search])
921
+ idx = i
922
+ break
923
+ end
924
+ end
925
+ else
926
+ idx = 0
927
+ end
928
+
929
+ if idx.nil?
930
+ @results.push('No entries found')
931
+ return
932
+ end
933
+
934
+ section = items[idx]['section']
935
+
936
+ section_items = @content[section]['items']
937
+ s_idx = section_items.index(items[idx])
938
+
939
+ current_item = section_items[s_idx]['title']
940
+ old_note = section_items[s_idx]['note'] ? section_items[s_idx]['note'].map(&:strip).join("\n") : nil
941
+ current_item += "\n#{old_note}" unless old_note.nil?
942
+ new_item = fork_editor(current_item)
943
+ title, note = format_input(new_item)
944
+
945
+ if title.nil? || title.empty?
946
+ @results.push('No content provided')
947
+ elsif title == section_items[s_idx]['title'] && note == old_note
948
+ @results.push('No change in content')
949
+ else
950
+ section_items[s_idx]['title'] = title
951
+ section_items[s_idx]['note'] = note
952
+ @results.push("Entry edited: #{section_items[s_idx]['title']}")
953
+ @content[section]['items'] = section_items
954
+ write(@doing_file)
955
+ end
956
+ end
957
+
856
958
  ##
857
959
  ## @brief Add a note to the last entry in a section
858
960
  ##
@@ -948,7 +1050,7 @@ class WWID
948
1050
 
949
1051
  if opt[:new_item]
950
1052
  title, note = format_input(opt[:new_item])
951
- note.push(opt[:note].gsub(/ *$/, '')) if opt[:note]
1053
+ note.push(opt[:note].map(&:chomp)) if opt[:note]
952
1054
  title += " @#{tag}"
953
1055
  add_item(title.cap_first, opt[:section], { note: note.join(' ').rstrip, back: opt[:back] })
954
1056
  end
@@ -1106,10 +1208,7 @@ class WWID
1106
1208
  end
1107
1209
  end
1108
1210
 
1109
- if opt[:section].class != Hash
1110
- warn 'Invalid section object'
1111
- return
1112
- end
1211
+ raise 'Invalid section object' unless opt[:section].instance_of? Hash
1113
1212
 
1114
1213
  items = opt[:section]['items'].sort_by { |item| item['date'] }
1115
1214
 
@@ -1127,19 +1226,23 @@ class WWID
1127
1226
 
1128
1227
  if opt[:tag_filter] && !opt[:tag_filter]['tags'].empty?
1129
1228
  items.delete_if do |item|
1130
- if opt[:tag_filter]['bool'] =~ /(AND|ALL)/
1131
- score = 0
1229
+ case opt[:tag_filter]['bool']
1230
+ when /(AND|ALL)/
1231
+ del = false
1132
1232
  opt[:tag_filter]['tags'].each do |tag|
1133
- score += 1 if item['title'] =~ /@#{tag}/
1233
+ unless item['title'] =~ /@#{tag}/
1234
+ del = true
1235
+ break
1236
+ end
1134
1237
  end
1135
- score < opt[:tag_filter]['tags'].length
1136
- elsif opt[:tag_filter]['bool'] =~ /NONE/
1238
+ del
1239
+ when /NONE/
1137
1240
  del = false
1138
1241
  opt[:tag_filter]['tags'].each do |tag|
1139
1242
  del = true if item['title'] =~ /@#{tag}/
1140
1243
  end
1141
1244
  del
1142
- elsif opt[:tag_filter]['bool'] =~ /(OR|ANY)/
1245
+ when /(OR|ANY)/
1143
1246
  del = true
1144
1247
  opt[:tag_filter]['tags'].each do |tag|
1145
1248
  del = false if item['title'] =~ /@#{tag}/
@@ -1150,15 +1253,7 @@ class WWID
1150
1253
  end
1151
1254
 
1152
1255
  if opt[:search]
1153
- items.keep_if do |item|
1154
- text = item['note'] ? item['title'] + item['note'].join(' ') : item['title']
1155
- pattern = if opt[:search].strip =~ %r{^/.*?/$}
1156
- opt[:search].sub(%r{/(.*?)/}, '\1')
1157
- else
1158
- opt[:search].split('').join('.{0,3}')
1159
- end
1160
- text =~ /#{pattern}/i
1161
- end
1256
+ items.keep_if {|item| item.matches_search?(opt[:search]) }
1162
1257
  end
1163
1258
 
1164
1259
  if opt[:only_timed]
@@ -1187,9 +1282,10 @@ class WWID
1187
1282
 
1188
1283
  out = ''
1189
1284
 
1190
- raise 'Unknown output format' if opt[:output] && !(opt[:output] =~ /(template|html|csv|json|timeline)/)
1285
+ raise 'Unknown output format' if opt[:output] && (opt[:output] !~ /^(template|html|csv|json|timeline)$/i)
1191
1286
 
1192
- if opt[:output] == 'csv'
1287
+ case opt[:output]
1288
+ when /^csv$/i
1193
1289
  output = [CSV.generate_line(%w[date title note timer section])]
1194
1290
  items.each do |i|
1195
1291
  note = ''
@@ -1202,7 +1298,7 @@ class WWID
1202
1298
  output.push(CSV.generate_line([i['date'], i['title'], note, interval, i['section']]))
1203
1299
  end
1204
1300
  out = output.join('')
1205
- elsif opt[:output] == 'json' || opt[:output] == 'timeline'
1301
+ when /^(json|timeline)/i
1206
1302
  items_out = []
1207
1303
  max = items[-1]['date'].strftime('%F')
1208
1304
  min = items[0]['date'].strftime('%F')
@@ -1298,7 +1394,7 @@ class WWID
1298
1394
  EOTEMPLATE
1299
1395
  return template
1300
1396
  end
1301
- elsif opt[:output] == 'html'
1397
+ when /^html$/i
1302
1398
  page_title = section
1303
1399
  items_out = []
1304
1400
  items.each do |i|
@@ -1412,7 +1508,7 @@ class WWID
1412
1508
  else
1413
1509
  colors['default']
1414
1510
  end
1415
- output.gsub!(/\s(@[^ (]+)/, " #{colors[opt[:tags_color]]}\\1#{last_color}")
1511
+ output.gsub!(/(\s|m)(@[^ (]+)/, "\\1#{colors[opt[:tags_color]]}\\2#{last_color}")
1416
1512
  end
1417
1513
  output.sub!(/%note/, note)
1418
1514
  output.sub!(/%odnote/, note.gsub(/^\t*/, ''))
@@ -1427,7 +1523,7 @@ class WWID
1427
1523
  output.gsub!(/%n/, "\n")
1428
1524
  output.gsub!(/%t/, "\t")
1429
1525
 
1430
- out += output + "\n"
1526
+ out += "#{output}\n"
1431
1527
  end
1432
1528
  out += tag_times('text', opt[:sort_tags]) if opt[:totals]
1433
1529
  end
@@ -1488,7 +1584,8 @@ class WWID
1488
1584
 
1489
1585
  if tags && !tags.empty?
1490
1586
  items.delete_if do |item|
1491
- if bool =~ /(AND|ALL)/
1587
+ case bool
1588
+ when /(AND|ALL)/i
1492
1589
  score = 0
1493
1590
  tags.each do |tag|
1494
1591
  score += 1 if item['title'] =~ /@#{tag}/i
@@ -1496,14 +1593,14 @@ class WWID
1496
1593
  res = score < tags.length
1497
1594
  moved_items.push(item) if res
1498
1595
  res
1499
- elsif bool =~ /NONE/
1596
+ when /NONE/i
1500
1597
  del = false
1501
1598
  tags.each do |tag|
1502
1599
  del = true if item['title'] =~ /@#{tag}/i
1503
1600
  end
1504
1601
  moved_items.push(item) if del
1505
1602
  del
1506
- elsif bool =~ /(OR|ANY)/
1603
+ when /(OR|ANY)/i
1507
1604
  del = true
1508
1605
  tags.each do |tag|
1509
1606
  del = false if item['title'] =~ /@#{tag}/i
@@ -1513,7 +1610,7 @@ class WWID
1513
1610
  end
1514
1611
  end
1515
1612
  moved_items.each do |item|
1516
- if label && !(section == 'Currently')
1613
+ if label && section != 'Currently'
1517
1614
  item['title'] =
1518
1615
  item['title'].sub(/(?:@from\(.*?\))?(.*)$/, "\\1 @from(#{section})")
1519
1616
  end
@@ -1522,23 +1619,27 @@ class WWID
1522
1619
  @content[destination]['items'] += items
1523
1620
  @results.push("Archived #{items.length} items from #{section} to #{destination}")
1524
1621
  else
1622
+ count = items.length if items.length < count
1525
1623
 
1526
- return if items.length < count
1527
-
1528
- @content[section]['items'] = if count == 0
1624
+ @content[section]['items'] = if count.zero?
1529
1625
  []
1530
1626
  else
1531
1627
  items[0..count - 1]
1532
1628
  end
1533
1629
 
1534
- items.each do |item|
1535
- if label && !(section == 'Currently')
1630
+ items.map! do |item|
1631
+ if label && section != 'Currently'
1536
1632
  item['title'] =
1537
1633
  item['title'].sub(/(?:@from\(.*?\))?(.*)$/, "\\1 @from(#{section})")
1538
1634
  end
1635
+ item
1636
+ end
1637
+ if items.count > count
1638
+ @content[destination]['items'].concat(items[count..-1])
1639
+ else
1640
+ @content[destination]['items'].concat(items)
1539
1641
  end
1540
1642
 
1541
- @content[destination]['items'] += items[count..-1]
1542
1643
  @results.push("Archived #{items.length - count} items from #{section} to #{destination}")
1543
1644
  end
1544
1645
  end
@@ -1659,8 +1760,11 @@ class WWID
1659
1760
  cfg = @config['templates']['recent']
1660
1761
  section ||= @current_section
1661
1762
  section = guess_section(section)
1763
+
1662
1764
  list_section({ section: section, wrap_width: cfg['wrap_width'], count: count,
1663
- format: cfg['date_format'], template: cfg['template'], order: 'asc', times: times, totals: opt[:totals], sort_tags: opt[:sort_tags] })
1765
+ format: cfg['date_format'], template: cfg['template'],
1766
+ order: 'asc', times: times, totals: opt[:totals],
1767
+ sort_tags: opt[:sort_tags], tags_color: opt[:tags_color] })
1664
1768
  end
1665
1769
 
1666
1770
  ##
@@ -1669,12 +1773,29 @@ class WWID
1669
1773
  ## @param times (Bool) Show times
1670
1774
  ## @param section (String) Section to pull from, default Currently
1671
1775
  ##
1672
- def last(times = true, section = nil)
1673
- section ||= @current_section
1674
- section = guess_section(section)
1776
+ def last(times: true, section: nil, options: {})
1777
+ section = section.nil? || section =~ /all/i ? 'All' : guess_section(section)
1675
1778
  cfg = @config['templates']['last']
1676
- list_section({ section: section, wrap_width: cfg['wrap_width'], count: 1, format: cfg['date_format'],
1677
- template: cfg['template'], times: times })
1779
+
1780
+ opts = {
1781
+ section: section,
1782
+ wrap_width: cfg['wrap_width'],
1783
+ count: 1,
1784
+ format: cfg['date_format'],
1785
+ template: cfg['template'],
1786
+ times: times
1787
+ }
1788
+
1789
+ if options[:tag]
1790
+ opts[:tag_filter] = {
1791
+ 'tags' => options[:tag],
1792
+ 'bool' => options[:tag_bool]
1793
+ }
1794
+ end
1795
+
1796
+ opts[:search] = options[:search] if options[:search]
1797
+
1798
+ list_section(opts)
1678
1799
  end
1679
1800
 
1680
1801
  ##
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.54
4
+ version: 1.0.55
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-03 00:00:00.000000000 Z
11
+ date: 2021-07-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake