doing 1.0.54 → 1.0.55

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