doing 2.0.7.pre → 2.0.8.pre

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: fc54a9b143b7a97dcd2ad9867a5166b06cfe52e4631f7658c65aa76ea9e07c19
4
- data.tar.gz: 5c60bc065b3b25cd450403dbf42089f30f5367416fee6b88f9baaf43b4ede04e
3
+ metadata.gz: 395218e7fd0178eb8d626e58c6b4ec078ff2631506829f98e7a41daacc7dd9bc
4
+ data.tar.gz: c7bda7d2e832b3f657f409c512f16bf71b8d0b247b776c4f3ce467c399482241
5
5
  SHA512:
6
- metadata.gz: 7ef6e317026dae9f46a8abdaa3c0a4c8a7ef29598e192c223e4158510c65e8deac0a87719d0cbcc634c8d7c654cab7239e95d88369819fb1f7f9c6b1da027485
7
- data.tar.gz: eb8c1115272cd4c23d158719f15a1f0af81a1e091af25a0e436c0832b4e21046bc8dd393d47e62214de59628a1e1e582a8c68533d8ce39b33d1ce2ee856a1971
6
+ metadata.gz: 73d7cfcafe737b446a6fec46a8bb8ae41503137240546cd361b1f6f292ecbf6a37c361a4708aeb9a4efcfddf76e42515e580a6c03b86c5512082e90e2bd8b325
7
+ data.tar.gz: 4f5ccc666a48629fe674923dfece11a872b59b5af7329f7484c777b4cc6166bebb287df5f19ea2d802212e553d03a2ac4b853f31c1f41f4204b6293838cd2695
data/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ### 2.0.8.pre
2
+
3
+ #### NEW
4
+
5
+ - Add `--exact` flag to all commands with `--search` flag to force exact matching without requiring single quote prefix
6
+ - Add `--not` flag to all commands with filters (--tag, --search, --before, etc.) to negate the filter and return entries NOT matched
7
+
1
8
  ### 2.0.7.pre
2
9
 
3
10
  #### FIXED
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- doing (2.0.7.pre)
4
+ doing (2.0.8.pre)
5
5
  chronic (~> 0.10, >= 0.10.2)
6
6
  deep_merge (~> 1.2, >= 1.2.1)
7
7
  gli (~> 2.19, >= 2.19.2)
data/README.md CHANGED
@@ -6,7 +6,7 @@ _If you're one of the rare people like me who find this useful, feel free to [bu
6
6
 
7
7
  <!--README-->
8
8
 
9
- The current version of `doing` is <!--VER-->2.0.6<!--END VER-->.
9
+ The current version of `doing` is <!--VER-->2.0.7<!--END VER-->.
10
10
 
11
11
  Find all of the documentation in the [doing wiki](https://github.com/ttscoff/doing/wiki).
12
12
 
data/bin/doing CHANGED
@@ -180,7 +180,7 @@ end
180
180
 
181
181
  desc 'Reset the start time of an entry'
182
182
  command %i[reset begin] do |c|
183
- c.desc 'Set the start date of an item to now'
183
+ c.desc 'Limit search to section'
184
184
  c.arg_name 'NAME'
185
185
  c.flag %i[s section], default_value: 'All'
186
186
 
@@ -195,6 +195,12 @@ command %i[reset begin] do |c|
195
195
  c.arg_name 'QUERY'
196
196
  c.flag [:search]
197
197
 
198
+ c.desc 'Force exact search string matching (case sensitive)'
199
+ c.switch %i[x exact], default_value: false, negatable: false
200
+
201
+ c.desc 'Reset items that *don\'t* match search/tag filters'
202
+ c.switch [:not], default_value: false, negatable: false
203
+
198
204
  c.desc 'Boolean (AND|OR|NOT) with which to combine multiple tag filters'
199
205
  c.arg_name 'BOOLEAN'
200
206
  c.flag [:bool], must_match: REGEX_BOOL, default_value: 'AND'
@@ -209,6 +215,13 @@ command %i[reset begin] do |c|
209
215
 
210
216
  options[:tag_bool] = options[:bool].normalize_bool
211
217
 
218
+ if options[:search]
219
+ search = options[:search]
220
+ search.sub!(/^'?/, "'") if options[:exact]
221
+ options[:search] = search
222
+ end
223
+
224
+
212
225
  items = wwid.filter_items([], opt: options)
213
226
 
214
227
  if options[:interactive]
@@ -266,6 +279,12 @@ command :note do |c|
266
279
  c.arg_name 'QUERY'
267
280
  c.flag [:search]
268
281
 
282
+ c.desc 'Force exact search string matching (case sensitive)'
283
+ c.switch %i[x exact], default_value: false, negatable: false
284
+
285
+ c.desc 'Add note to item that *doesn\'t* match search/tag filters'
286
+ c.switch [:not], default_value: false, negatable: false
287
+
269
288
  c.desc 'Boolean (AND|OR|NOT) with which to combine multiple tag filters'
270
289
  c.arg_name 'BOOLEAN'
271
290
  c.flag [:bool], must_match: REGEX_BOOL, default_value: 'AND'
@@ -280,6 +299,13 @@ command :note do |c|
280
299
 
281
300
  options[:tag_bool] = options[:bool].normalize_bool
282
301
 
302
+ if options[:search]
303
+ search = options[:search]
304
+ search.sub!(/^'?/, "'") if options[:exact]
305
+ options[:search] = search
306
+ end
307
+
308
+
283
309
  last_entry = wwid.last_entry(options)
284
310
 
285
311
  unless last_entry
@@ -473,6 +499,12 @@ command :select do |c|
473
499
  c.arg_name 'QUERY'
474
500
  c.flag %i[q query search]
475
501
 
502
+ c.desc 'Force exact search string matching (case sensitive)'
503
+ c.switch %i[x exact], default_value: false, negatable: false
504
+
505
+ c.desc 'Select items that *don\'t* match search/tag filters'
506
+ c.switch [:not], default_value: false, negatable: false
507
+
476
508
  c.desc 'Use --no-menu to skip the interactive menu. Use with --query to filter items and act on results automatically. Test with `--output doing` to preview matches.'
477
509
  c.switch %i[menu], negatable: true, default_value: true
478
510
 
@@ -844,6 +876,12 @@ command :finish do |c|
844
876
  c.arg_name 'QUERY'
845
877
  c.flag [:search]
846
878
 
879
+ c.desc 'Force exact search string matching (case sensitive)'
880
+ c.switch %i[x exact], default_value: false, negatable: false
881
+
882
+ c.desc 'Finish items that *don\'t* match search/tag filters'
883
+ c.switch [:not], default_value: false, negatable: false
884
+
847
885
  c.desc 'Boolean (AND|OR|NOT) with which to combine multiple tag filters'
848
886
  c.arg_name 'BOOLEAN'
849
887
  c.flag [:bool], must_match: REGEX_BOOL, default_value: 'AND'
@@ -912,12 +950,20 @@ command :finish do |c|
912
950
  count = args[0] ? args[0].to_i : 1
913
951
  end
914
952
 
953
+ search = nil
954
+
955
+ if options[:search]
956
+ search = options[:search]
957
+ search.sub!(/^'?/, "'") if options[:exact]
958
+ end
959
+
915
960
  opts = {
916
961
  archive: options[:archive],
917
962
  back: date,
918
963
  count: count,
919
964
  date: options[:date],
920
- search: options[:search],
965
+ search: search,
966
+ not: options[:not],
921
967
  section: options[:section],
922
968
  sequential: options[:auto],
923
969
  tag: tags,
@@ -951,6 +997,12 @@ command %i[again resume] do |c|
951
997
  c.arg_name 'QUERY'
952
998
  c.flag [:search]
953
999
 
1000
+ c.desc 'Force exact search string matching (case sensitive)'
1001
+ c.switch %i[x exact], default_value: false, negatable: false
1002
+
1003
+ c.desc 'Resume items that *don\'t* match search/tag filters'
1004
+ c.switch [:not], default_value: false, negatable: false
1005
+
954
1006
  c.desc 'Boolean used to combine multiple tags'
955
1007
  c.arg_name 'BOOLEAN'
956
1008
  c.flag [:bool], must_match: REGEX_BOOL, default_value: 'AND'
@@ -967,7 +1019,15 @@ command %i[again resume] do |c|
967
1019
 
968
1020
  c.action do |_global_options, options, _args|
969
1021
  tags = options[:tag].nil? ? [] : options[:tag].to_tags
1022
+
1023
+ if options[:search]
1024
+ search = options[:search]
1025
+ search.sub!(/^'?/, "'") if options[:exact]
1026
+ options[:search] = search
1027
+ end
1028
+
970
1029
  opts = options
1030
+
971
1031
  opts[:tag] = tags
972
1032
  opts[:tag_bool] = options[:bool].normalize_bool
973
1033
  opts[:interactive] = options[:interactive]
@@ -1037,6 +1097,12 @@ command :tag do |c|
1037
1097
  c.arg_name 'QUERY'
1038
1098
  c.flag [:search]
1039
1099
 
1100
+ c.desc 'Force exact search string matching (case sensitive)'
1101
+ c.switch %i[x exact], default_value: false, negatable: false
1102
+
1103
+ c.desc 'Tag items that *don\'t* match search/tag filters'
1104
+ c.switch [:not], default_value: false, negatable: false
1105
+
1040
1106
  c.desc 'Boolean (AND|OR|NOT) with which to combine multiple tag filters'
1041
1107
  c.arg_name 'BOOLEAN'
1042
1108
  c.flag [:bool], must_match: REGEX_BOOL, default_value: 'AND'
@@ -1081,6 +1147,11 @@ command :tag do |c|
1081
1147
  count = options[:count].to_i
1082
1148
  end
1083
1149
 
1150
+ if options[:search]
1151
+ search = options[:search]
1152
+ search.sub!(/^'?/, "'") if options[:exact]
1153
+ options[:search] = search
1154
+ end
1084
1155
 
1085
1156
  if count.zero? && !options[:force]
1086
1157
  if options[:search]
@@ -1104,7 +1175,7 @@ command :tag do |c|
1104
1175
 
1105
1176
  res = wwid.yn(question, default_response: false)
1106
1177
 
1107
- exit_now! 'Cancelled' unless res
1178
+ raise UserCancelled unless res
1108
1179
  end
1109
1180
 
1110
1181
  options[:count] = count
@@ -1152,6 +1223,12 @@ command [:mark, :flag] do |c|
1152
1223
  c.arg_name 'QUERY'
1153
1224
  c.flag [:search]
1154
1225
 
1226
+ c.desc 'Force exact search string matching (case sensitive)'
1227
+ c.switch %i[x exact], default_value: false, negatable: false
1228
+
1229
+ c.desc 'Flag items that *don\'t* match search/tag/date filters'
1230
+ c.switch [:not], default_value: false, negatable: false
1231
+
1155
1232
  c.desc 'Boolean (AND|OR|NOT) with which to combine multiple tag filters'
1156
1233
  c.arg_name 'BOOLEAN'
1157
1234
  c.flag [:bool], must_match: REGEX_BOOL, default_value: 'AND'
@@ -1183,6 +1260,12 @@ command [:mark, :flag] do |c|
1183
1260
  count = options[:count].to_i
1184
1261
  end
1185
1262
 
1263
+ if options[:search]
1264
+ search = options[:search]
1265
+ search.sub!(/^'?/, "'") if options[:exact]
1266
+ options[:search] = search
1267
+ end
1268
+
1186
1269
  if count.zero? && !options[:force]
1187
1270
  if options[:search]
1188
1271
  section_q = ' matching your search terms'
@@ -1257,6 +1340,12 @@ command :show do |c|
1257
1340
  c.arg_name 'QUERY'
1258
1341
  c.flag [:search]
1259
1342
 
1343
+ c.desc 'Force exact search string matching (case sensitive)'
1344
+ c.switch %i[x exact], default_value: false, negatable: false
1345
+
1346
+ c.desc 'Show items that *don\'t* match search/tag/date filters'
1347
+ c.switch [:not], default_value: false, negatable: false
1348
+
1260
1349
  c.desc 'Sort order (asc/desc)'
1261
1350
  c.arg_name 'ORDER'
1262
1351
  c.flag %i[s sort], must_match: REGEX_SORT_ORDER, default_value: 'asc'
@@ -1361,6 +1450,12 @@ command :show do |c|
1361
1450
 
1362
1451
  tags_color = settings.key?('tags_color') ? settings['tags_color'] : nil
1363
1452
 
1453
+ if options[:search]
1454
+ search = options[:search]
1455
+ search.sub!(/^'?/, "'") if options[:exact]
1456
+ options[:search] = search
1457
+ end
1458
+
1364
1459
  opt = options.dup
1365
1460
 
1366
1461
  opt[:sort_tags] = options[:tag_sort] =~ /^n/i
@@ -1423,6 +1518,15 @@ command %i[grep search] do |c|
1423
1518
  c.desc 'Only show items with recorded time intervals'
1424
1519
  c.switch [:only_timed], default_value: false, negatable: false
1425
1520
 
1521
+ c.desc 'Force exact string matching (case sensitive)'
1522
+ c.switch %i[x exact], default_value: false, negatable: false
1523
+
1524
+ c.desc 'Force case sensitive matching'
1525
+ c.switch %i[case]
1526
+
1527
+ c.desc 'Show items that *don\'t* match search string'
1528
+ c.switch [:not], default_value: false, negatable: false
1529
+
1426
1530
  c.desc 'Display an interactive menu of results to perform further operations'
1427
1531
  c.switch %i[i interactive], default_value: false, negatable: false
1428
1532
 
@@ -1432,11 +1536,13 @@ command %i[grep search] do |c|
1432
1536
  tags_color = settings.key?('tags_color') ? settings['tags_color'] : nil
1433
1537
 
1434
1538
  section = wwid.guess_section(options[:section]) if options[:section]
1539
+ search = args.join(' ')
1540
+ search.sub!(/^'?/, "'") if options[:exact]
1435
1541
 
1436
1542
  options[:times] = true if options[:totals]
1437
1543
  options[:sort_tags] = options[:tag_sort] =~ /^n/i
1438
1544
  options[:highlight] = true
1439
- options[:search] = args.join(' ')
1545
+ options[:search] = search
1440
1546
  options[:section] = section
1441
1547
  options[:tags_color] = tags_color
1442
1548
 
@@ -1754,6 +1860,12 @@ command :last do |c|
1754
1860
  c.arg_name 'QUERY'
1755
1861
  c.flag [:search]
1756
1862
 
1863
+ c.desc 'Force exact search string matching (case sensitive)'
1864
+ c.switch %i[x exact], default_value: false, negatable: false
1865
+
1866
+ c.desc 'Show items that *don\'t* match search string or tag filter'
1867
+ c.switch [:not], default_value: false, negatable: false
1868
+
1757
1869
  c.action do |global_options, options, _args|
1758
1870
  raise InvalidArgument, '--tag and --search can not be used together' if options[:tag] && options[:search]
1759
1871
 
@@ -1772,11 +1884,18 @@ command :last do |c|
1772
1884
 
1773
1885
  end
1774
1886
 
1887
+ search = nil
1888
+
1889
+ if options[:search]
1890
+ search = options[:search]
1891
+ search.sub!(/^'?/, "'") if options[:exact]
1892
+ end
1893
+
1775
1894
  if options[:editor]
1776
- wwid.edit_last(section: options[:s], options: { search: options[:search], tag: tags, tag_bool: options[:bool] })
1895
+ wwid.edit_last(section: options[:s], options: { search: search, tag: tags, tag_bool: options[:bool], not: options[:not] })
1777
1896
  else
1778
1897
  Doing::Pager::page wwid.last(times: true, section: options[:s],
1779
- options: { search: options[:search], tag: tags, tag_bool: options[:bool] }).strip
1898
+ options: { search: search, negate: options[:not], tag: tags, tag_bool: options[:bool] }).strip
1780
1899
  end
1781
1900
  end
1782
1901
  end
@@ -1934,6 +2053,12 @@ command :view do |c|
1934
2053
  c.arg_name 'QUERY'
1935
2054
  c.flag [:search]
1936
2055
 
2056
+ c.desc 'Force exact search string matching (case sensitive)'
2057
+ c.switch %i[x exact], default_value: false, negatable: false
2058
+
2059
+ c.desc 'Show items that *don\'t* match search string'
2060
+ c.switch [:not], default_value: false, negatable: false
2061
+
1937
2062
  c.desc 'Sort tags by (name|time)'
1938
2063
  c.arg_name 'KEY'
1939
2064
  c.flag [:tag_sort], must_match: /^(?:name|time)$/i
@@ -2065,7 +2190,15 @@ command :view do |c|
2065
2190
  dates = [start, finish]
2066
2191
  end
2067
2192
 
2193
+ search = nil
2194
+
2195
+ if options[:search]
2196
+ search = options[:search]
2197
+ search.sub!(/^'?/, "'") if options[:exact]
2198
+ end
2199
+
2068
2200
  opts = options
2201
+ opts[:search] = search
2069
2202
  opts[:output] = output_format
2070
2203
  opts[:count] = count
2071
2204
  opts[:format] = date_format
@@ -2138,6 +2271,12 @@ command %i[archive move] do |c|
2138
2271
  c.arg_name 'QUERY'
2139
2272
  c.flag [:search]
2140
2273
 
2274
+ c.desc 'Force exact search string matching (case sensitive)'
2275
+ c.switch %i[x exact], default_value: false, negatable: false
2276
+
2277
+ c.desc 'Show items that *don\'t* match search string'
2278
+ c.switch [:not], default_value: false, negatable: false
2279
+
2141
2280
  c.desc 'Archive entries older than date
2142
2281
  (Flexible date format, e.g. 1/27/2021, 2020-07-19, or Monday 3pm)'
2143
2282
  c.arg_name 'DATE_STRING'
@@ -2161,7 +2300,15 @@ command %i[archive move] do |c|
2161
2300
 
2162
2301
  tags.concat(options[:tag].to_tags) if options[:tag]
2163
2302
 
2303
+ search = nil
2304
+
2305
+ if options[:search]
2306
+ search = options[:search]
2307
+ search.sub!(/^'?/, "'") if options[:exact]
2308
+ end
2309
+
2164
2310
  opts = options
2311
+ opts[:search] = search
2165
2312
  opts[:bool] = options[:bool].normalize_bool
2166
2313
  opts[:destination] = options[:to]
2167
2314
  opts[:tags] = tags
@@ -2196,6 +2343,12 @@ command :rotate do |c|
2196
2343
  c.arg_name 'QUERY'
2197
2344
  c.flag [:search]
2198
2345
 
2346
+ c.desc 'Force exact search string matching (case sensitive)'
2347
+ c.switch %i[x exact], default_value: false, negatable: false
2348
+
2349
+ c.desc 'Rotate items that *don\'t* match search string or tag filter'
2350
+ c.switch [:not], default_value: false, negatable: false
2351
+
2199
2352
  c.desc 'Rotate entries older than date
2200
2353
  (Flexible date format, e.g. 1/27/2021, 2020-07-19, or Monday 3pm)'
2201
2354
  c.arg_name 'DATE_STRING'
@@ -2208,6 +2361,14 @@ command :rotate do |c|
2208
2361
 
2209
2362
  options[:bool] = options[:bool].normalize_bool
2210
2363
 
2364
+ search = nil
2365
+
2366
+ if options[:search]
2367
+ search = options[:search]
2368
+ search.sub!(/^'?/, "'") if options[:exact]
2369
+ options[:search] = search
2370
+ end
2371
+
2211
2372
  wwid.rotate(options)
2212
2373
  end
2213
2374
  end
@@ -2396,6 +2557,12 @@ command :import do |c|
2396
2557
  c.arg_name 'QUERY'
2397
2558
  c.flag [:search]
2398
2559
 
2560
+ c.desc 'Force exact search string matching (case sensitive)'
2561
+ c.switch %i[x exact], default_value: false, negatable: false
2562
+
2563
+ c.desc 'Import items that *don\'t* match search/tag/date filters'
2564
+ c.switch [:not], default_value: false, negatable: false
2565
+
2399
2566
  c.desc 'Only import items with recorded time intervals'
2400
2567
  c.switch [:only_timed], default_value: false, negatable: false
2401
2568
 
data/doing.rdoc CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Doing uses a TaskPaper-like formatting to keep a plain text record of what you've been doing, complete with tag-based time tracking. The command line tool allows you to add entries, annotate with tags and notes, and view your entries with myriad options, with a focus on a "natural" language syntax.
4
4
 
5
- v2.0.7.pre
5
+ v2.0.8.pre
6
6
 
7
7
  === Global Options
8
8
  === --config_file arg
@@ -133,6 +133,16 @@ Select item to resume from a menu of matching entries
133
133
 
134
134
 
135
135
 
136
+ ===== --not
137
+ Resume items that *don't* match search/tag filters
138
+
139
+
140
+
141
+ ===== -x|--exact
142
+ Force exact search string matching (case sensitive)
143
+
144
+
145
+
136
146
  ==== Command: <tt>archive|move SECTION_OR_TAG</tt>
137
147
  Move entries between sections
138
148
 
@@ -191,6 +201,16 @@ Label moved items with @from(SECTION_NAME)
191
201
 
192
202
 
193
203
 
204
+ ===== --not
205
+ Show items that *don't* match search string
206
+
207
+
208
+
209
+ ===== -x|--exact
210
+ Force exact search string matching (case sensitive)
211
+
212
+
213
+
194
214
  ==== Command: <tt>autotag </tt>
195
215
  Autotag last entry or filtered entries
196
216
 
@@ -524,6 +544,11 @@ Select item(s) to finish from a menu of matching entries
524
544
 
525
545
 
526
546
 
547
+ ===== --not
548
+ Finish items that *don't* match search/tag filters
549
+
550
+
551
+
527
552
  ===== -r|--remove
528
553
  Remove done tag
529
554
 
@@ -534,6 +559,11 @@ Finish last entry (or entries) not already marked @done
534
559
 
535
560
 
536
561
 
562
+ ===== -x|--exact
563
+ Force exact search string matching (case sensitive)
564
+
565
+
566
+
537
567
  ==== Command: <tt>grep|search SEARCH_PATTERN</tt>
538
568
  Search for entries
539
569
 
@@ -577,11 +607,21 @@ Sort tags by (name|time)
577
607
  [Must Match] (?i-mx:^(?:name|time)$)
578
608
 
579
609
 
610
+ ===== --[no-]case
611
+ Force case sensitive matching
612
+
613
+
614
+
580
615
  ===== -i|--interactive
581
616
  Display an interactive menu of results to perform further operations
582
617
 
583
618
 
584
619
 
620
+ ===== --not
621
+ Show items that *don't* match search string
622
+
623
+
624
+
585
625
  ===== --only_timed
586
626
  Only show items with recorded time intervals
587
627
 
@@ -597,6 +637,11 @@ Show intervals with totals at the end of output
597
637
 
598
638
 
599
639
 
640
+ ===== -x|--exact
641
+ Force exact string matching (case sensitive)
642
+
643
+
644
+
600
645
  ==== Command: <tt>help command</tt>
601
646
  Shows a list of commands or help for one command
602
647
 
@@ -675,6 +720,11 @@ Autotag entries
675
720
 
676
721
 
677
722
 
723
+ ===== --not
724
+ Import items that *don't* match search/tag/date filters
725
+
726
+
727
+
678
728
  ===== --only_timed
679
729
  Only import items with recorded time intervals
680
730
 
@@ -685,6 +735,11 @@ Allow entries that overlap existing times
685
735
 
686
736
 
687
737
 
738
+ ===== -x|--exact
739
+ Force exact search string matching (case sensitive)
740
+
741
+
742
+
688
743
  ==== Command: <tt>last </tt>
689
744
  Show the last entry, optionally edit
690
745
 
@@ -724,6 +779,16 @@ Edit entry with vim
724
779
 
725
780
 
726
781
 
782
+ ===== --not
783
+ Show items that *don't* match search string or tag filter
784
+
785
+
786
+
787
+ ===== -x|--exact
788
+ Force exact search string matching (case sensitive)
789
+
790
+
791
+
727
792
  ==== Command: <tt>later ENTRY</tt>
728
793
  Add an item to the Later section
729
794
 
@@ -806,6 +871,11 @@ Select item(s) to flag from a menu of matching entries
806
871
 
807
872
 
808
873
 
874
+ ===== --not
875
+ Flag items that *don't* match search/tag/date filters
876
+
877
+
878
+
809
879
  ===== -r|--remove
810
880
  Remove flag
811
881
 
@@ -816,6 +886,11 @@ Flag last entry (or entries) not marked @done
816
886
 
817
887
 
818
888
 
889
+ ===== -x|--exact
890
+ Force exact search string matching (case sensitive)
891
+
892
+
893
+
819
894
  ==== Command: <tt>meanwhile ENTRY</tt>
820
895
  Finish any running @meanwhile tasks and optionally create a new one
821
896
 
@@ -900,11 +975,21 @@ Select item for new note from a menu of matching entries
900
975
 
901
976
 
902
977
 
978
+ ===== --not
979
+ Add note to item that *doesn't* match search/tag filters
980
+
981
+
982
+
903
983
  ===== -r|--remove
904
984
  Replace/Remove last entry's note (default append)
905
985
 
906
986
 
907
987
 
988
+ ===== -x|--exact
989
+ Force exact search string matching (case sensitive)
990
+
991
+
992
+
908
993
  ==== Command: <tt>now|next ENTRY</tt>
909
994
  Add an entry
910
995
 
@@ -1075,7 +1160,7 @@ Boolean (AND|OR|NOT) with which to combine multiple tag filters
1075
1160
 
1076
1161
  ===== -s|--section NAME
1077
1162
 
1078
- Set the start date of an item to now
1163
+ Limit search to section
1079
1164
 
1080
1165
  [Default Value] All
1081
1166
 
@@ -1099,11 +1184,21 @@ Select from a menu of matching entries
1099
1184
 
1100
1185
 
1101
1186
 
1187
+ ===== --not
1188
+ Reset items that *don't* match search/tag filters
1189
+
1190
+
1191
+
1102
1192
  ===== -r|--[no-]resume
1103
1193
  Resume entry (remove @done)
1104
1194
 
1105
1195
 
1106
1196
 
1197
+ ===== -x|--exact
1198
+ Force exact search string matching (case sensitive)
1199
+
1200
+
1201
+
1107
1202
  ==== Command: <tt>rotate </tt>
1108
1203
  Move entries to archive file
1109
1204
 
@@ -1154,6 +1249,16 @@ Tag filter, combine multiple tags with a comma. Added for compatibility with oth
1154
1249
  [Default Value] None
1155
1250
 
1156
1251
 
1252
+ ===== --not
1253
+ Rotate items that *don't* match search string or tag filter
1254
+
1255
+
1256
+
1257
+ ===== -x|--exact
1258
+ Force exact search string matching (case sensitive)
1259
+
1260
+
1261
+
1157
1262
  ==== Command: <tt>sections </tt>
1158
1263
  List sections
1159
1264
 
@@ -1260,11 +1365,21 @@ Use --no-menu to skip the interactive menu. Use with --query to filter items and
1260
1365
 
1261
1366
 
1262
1367
 
1368
+ ===== --not
1369
+ Select items that *don't* match search/tag filters
1370
+
1371
+
1372
+
1263
1373
  ===== -r|--remove
1264
1374
  Reverse -c, -f, --flag, and -t (remove instead of adding)
1265
1375
 
1266
1376
 
1267
1377
 
1378
+ ===== -x|--exact
1379
+ Force exact search string matching (case sensitive)
1380
+
1381
+
1382
+
1268
1383
  ==== Command: <tt>show [SECTION|@TAGS]</tt>
1269
1384
  List all entries
1270
1385
 
@@ -1366,6 +1481,11 @@ Select from a menu of matching entries to perform additional operations
1366
1481
 
1367
1482
 
1368
1483
 
1484
+ ===== --not
1485
+ Show items that *don't* match search/tag/date filters
1486
+
1487
+
1488
+
1369
1489
  ===== --only_timed
1370
1490
  Only show items with recorded time intervals
1371
1491
 
@@ -1381,6 +1501,11 @@ Show intervals with totals at the end of output
1381
1501
 
1382
1502
 
1383
1503
 
1504
+ ===== -x|--exact
1505
+ Force exact search string matching (case sensitive)
1506
+
1507
+
1508
+
1384
1509
  ==== Command: <tt>since DATE_STRING</tt>
1385
1510
  List entries since a date
1386
1511
 
@@ -1501,6 +1626,11 @@ Select item(s) to tag from a menu of matching entries
1501
1626
 
1502
1627
 
1503
1628
 
1629
+ ===== --not
1630
+ Tag items that *don't* match search/tag filters
1631
+
1632
+
1633
+
1504
1634
  ===== -r|--remove
1505
1635
  Remove given tag(s)
1506
1636
 
@@ -1516,6 +1646,11 @@ Tag last entry (or entries) not marked @done
1516
1646
 
1517
1647
 
1518
1648
 
1649
+ ===== -x|--exact
1650
+ Force exact search string matching (case sensitive)
1651
+
1652
+
1653
+
1519
1654
  ==== Command: <tt>template TYPE</tt>
1520
1655
  Output HTML, CSS, and Markdown (ERB) templates for customization
1521
1656
 
@@ -1690,6 +1825,11 @@ Select from a menu of matching entries to perform additional operations
1690
1825
 
1691
1826
 
1692
1827
 
1828
+ ===== --not
1829
+ Show items that *don't* match search string
1830
+
1831
+
1832
+
1693
1833
  ===== --only_timed
1694
1834
  Only show items with recorded time intervals (override view settings)
1695
1835
 
@@ -1705,6 +1845,11 @@ Show intervals with totals at the end of output
1705
1845
 
1706
1846
 
1707
1847
 
1848
+ ===== -x|--exact
1849
+ Force exact search string matching (case sensitive)
1850
+
1851
+
1852
+
1708
1853
  ==== Command: <tt>views </tt>
1709
1854
  List available custom views
1710
1855
 
data/lib/doing/errors.rb CHANGED
@@ -27,7 +27,7 @@ module Doing
27
27
  end
28
28
 
29
29
  class WrongCommand < ::StandardError
30
- def initialize(msg = 'wrong command', topic = 'Error:')
30
+ def initialize(msg = 'wrong command', topic: 'Error:')
31
31
  Doing.logger.warn(topic, msg)
32
32
 
33
33
  super(msg)
data/lib/doing/item.rb CHANGED
@@ -60,21 +60,22 @@ module Doing
60
60
  @title.scan(/(?<= |\A)@([^\s(]+)/).map {|tag| tag[0]}.sort.uniq
61
61
  end
62
62
 
63
- def tags?(tags, bool = :and)
63
+ def tags?(tags, bool = :and, negate: false)
64
64
  tags = split_tags(tags)
65
65
  bool = bool.normalize_bool
66
66
 
67
- case bool
68
- when :and
69
- all_tags?(tags)
70
- when :not
71
- no_tags?(tags)
72
- else
73
- any_tags?(tags)
74
- end
67
+ matches = case bool
68
+ when :and
69
+ all_tags?(tags)
70
+ when :not
71
+ no_tags?(tags)
72
+ else
73
+ any_tags?(tags)
74
+ end
75
+ negate ? !matches : matches
75
76
  end
76
77
 
77
- def search(search)
78
+ def search(search, negate: false)
78
79
  text = @title + @note.to_s
79
80
  pattern = case search.strip
80
81
  when %r{^/.*?/$}
@@ -88,7 +89,7 @@ module Doing
88
89
  end
89
90
  rx = Regexp.new(pattern, !case_sensitive)
90
91
 
91
- text =~ rx
92
+ negate ? text !~ rx : text =~ rx
92
93
  end
93
94
 
94
95
  def should_finish?
@@ -64,9 +64,15 @@ module Doing
64
64
  new_items.push(new_entry)
65
65
  end
66
66
  total = new_items.count
67
+
68
+ new_items = wwid.filter_items(new_items, opt: options)
69
+ filtered = total - new_items.count
70
+ Doing.logger.debug('Skipped:' , %(#{filtered} items that didn't match filter criteria)) if filtered.positive?
71
+
67
72
  new_items = wwid.dedup(new_items, options[:no_overlap])
68
- dups = total - new_items.count
73
+ dups = filtered - new_items.count
69
74
  Doing.logger.info(%(Skipped #{dups} items with overlapping times)) if dups.positive?
75
+
70
76
  wwid.content[section][:items].concat(new_items)
71
77
  Doing.logger.info(%(Imported #{new_items.count} items to #{section}))
72
78
  end
@@ -40,13 +40,13 @@ module Doing
40
40
 
41
41
  new_items = read_doing_file(path)
42
42
 
43
- if options[:date_filter]
44
- new_items = wwid.filter_items(new_items, opt: { count: 0, date_filter: options[:date_filter] })
45
- end
43
+ total = new_items.count
46
44
 
47
- if options[:before] || options[:after]
48
- new_items = wwid.filter_items(new_items, opt: { count: 0, before: options[:before], after: options[:after] })
49
- end
45
+ options[:count] = 0
46
+ new_items = wwid.filter_items(new_items, opt: options)
47
+
48
+ skipped = total - new_items.count
49
+ Doing.logger.debug('Skipped:' , %(#{skipped} items that didn't match filter criteria)) if skipped.positive?
50
50
 
51
51
  imported = []
52
52
 
@@ -66,9 +66,15 @@ module Doing
66
66
  total = new_items.count
67
67
  skipped = data.count - total
68
68
  Doing.logger.debug('Skipped:' , %(#{skipped} items, invalid type or no time interval)) if skipped.positive?
69
+
70
+ new_items = wwid.filter_items(new_items, opt: options)
71
+ filtered = skipped - new_items.count
72
+ Doing.logger.debug('Skipped:' , %(#{filtered} items that didn't match filter criteria)) if filtered.positive?
73
+
69
74
  new_items = wwid.dedup(new_items, options[:no_overlap])
70
- dups = total - new_items.count
75
+ dups = filtered - new_items.count
71
76
  Doing.logger.debug('Skipped:' , %(#{dups} items with overlapping times)) if dups.positive?
77
+
72
78
  wwid.content[section][:items].concat(new_items)
73
79
  Doing.logger.info('Imported:', %(#{new_items.count} items to #{section}))
74
80
  end
data/lib/doing/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Doing
2
- VERSION = '2.0.7.pre'
2
+ VERSION = '2.0.8.pre'
3
3
  end
data/lib/doing/wwid.rb CHANGED
@@ -473,7 +473,7 @@ module Doing
473
473
  duped = no_overlap ? item.overlapping_time?(comp) : item.same_time?(comp)
474
474
  break if duped
475
475
  end
476
- logger.count(:skipped, level: :debug, message: 'overlapping %item') if duped
476
+ logger.count(:skipped, level: :debug, message: '%count overlapping %items') if duped
477
477
  # logger.log_now(:debug, 'Skipped:', "overlapping entry: #{item.title}") if duped
478
478
  duped
479
479
  end
@@ -686,20 +686,26 @@ module Doing
686
686
  items.sort_by! { |item| [item.date, item.title.downcase] }.reverse
687
687
  filtered_items = items.select do |item|
688
688
  keep = true
689
- finished = opt[:unfinished] && item.tags?('done', :and)
690
- keep = false if finished
689
+ if opt[:unfinished]
690
+ finished = item.tags?('done', :and)
691
+ finished = opt[:not] ? !finished : finished
692
+ keep = false if finished
693
+ end
691
694
 
692
695
  if keep && opt[:tag]
693
696
  opt[:tag_bool] ||= :and
694
697
  tag_match = opt[:tag].nil? || opt[:tag].empty? ? true : item.tags?(opt[:tag], opt[:tag_bool])
695
698
  keep = false unless tag_match
699
+ keep = opt[:not] ? !keep : keep
696
700
  end
697
701
 
698
702
  if keep && opt[:search]
699
703
  search_match = opt[:search].nil? || opt[:search].empty? ? true : item.search(opt[:search])
700
704
  keep = false unless search_match
705
+ keep = opt[:not] ? !keep : keep
701
706
  end
702
707
 
708
+
703
709
  if keep && opt[:date_filter]&.length == 2
704
710
  start_date = opt[:date_filter][0]
705
711
  end_date = opt[:date_filter][1]
@@ -710,30 +716,36 @@ module Doing
710
716
  item.date.strftime('%F') == start_date.strftime('%F')
711
717
  end
712
718
  keep = false unless in_date_range
719
+ keep = opt[:not] ? !keep : keep
713
720
  end
714
721
 
715
722
  keep = false if keep && opt[:only_timed] && !item.interval
716
723
 
717
724
  if keep && opt[:tag_filter] && !opt[:tag_filter]['tags'].empty?
718
725
  keep = item.tags?(opt[:tag_filter]['tags'], opt[:tag_filter]['bool'])
726
+ keep = opt[:not] ? !keep : keep
719
727
  end
720
728
 
721
729
  if keep && opt[:before]
722
730
  time_string = opt[:before]
723
731
  cutoff = chronify(time_string, guess: :begin)
724
732
  keep = cutoff && item.date <= cutoff
733
+ keep = opt[:not] ? !keep : keep
725
734
  end
726
735
 
727
736
  if keep && opt[:after]
728
737
  time_string = opt[:after]
729
738
  cutoff = chronify(time_string, guess: :end)
730
739
  keep = cutoff && item.date >= cutoff
740
+ keep = opt[:not] ? !keep : keep
731
741
  end
732
742
 
733
743
  if keep && opt[:today]
734
744
  keep = item.date >= Date.today.to_time && item.date < Date.today.next_day.to_time
745
+ keep = opt[:not] ? !keep : keep
735
746
  elsif keep && opt[:yesterday]
736
747
  keep = item.date >= Date.today.prev_day.to_time && item.date < Date.today.to_time
748
+ keep = opt[:not] ? !keep : keep
737
749
  end
738
750
 
739
751
  keep
@@ -755,7 +767,18 @@ module Doing
755
767
  ##
756
768
  def interactive(opt = {})
757
769
  section = opt[:section] ? guess_section(opt[:section]) : 'All'
770
+
771
+ search = nil
772
+
773
+ if opt[:search]
774
+ search = opt[:search]
775
+ search.sub!(/^'?/, "'") if opt[:exact]
776
+ search.downcase! if opt[:case] == false
777
+ opt[:search] = search
778
+ end
779
+
758
780
  opt[:query] = opt[:search] if opt[:search] && !opt[:query]
781
+ opt[:query] = "!#{opt[:query]}" if opt[:not]
759
782
  opt[:multiple] = true
760
783
  items = filter_items([], opt: { section: section, search: opt[:search] })
761
784
 
@@ -1564,7 +1587,6 @@ module Doing
1564
1587
 
1565
1588
  items.reverse! if opt[:order] =~ /^d/i
1566
1589
 
1567
-
1568
1590
  if opt[:interactive]
1569
1591
  opt[:menu] = !opt[:force]
1570
1592
  opt[:query] = '' # opt[:search]
@@ -1875,7 +1897,7 @@ module Doing
1875
1897
  end
1876
1898
 
1877
1899
  opts[:search] = options[:search] if options[:search]
1878
-
1900
+ opts[:not] = options[:negate]
1879
1901
  list_section(opts)
1880
1902
  end
1881
1903
 
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.0.7.pre
4
+ version: 2.0.8.pre
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brett Terpstra
@@ -231,10 +231,10 @@ files:
231
231
  - lib/examples/commands/wiki.rb
232
232
  - lib/examples/plugins/hooks.rb
233
233
  - lib/examples/plugins/say_export.rb
234
- - lib/examples/plugins/templates/wiki.css
235
- - lib/examples/plugins/templates/wiki.haml
236
- - lib/examples/plugins/templates/wiki_index.haml
237
- - lib/examples/plugins/wiki_export.rb
234
+ - lib/examples/plugins/wiki_export/templates/wiki.css
235
+ - lib/examples/plugins/wiki_export/templates/wiki.haml
236
+ - lib/examples/plugins/wiki_export/templates/wiki_index.haml
237
+ - lib/examples/plugins/wiki_export/wiki_export.rb
238
238
  - lib/helpers/fuzzyfilefinder
239
239
  - lib/templates/doing-markdown.erb
240
240
  - lib/templates/doing.css