doing 2.0.7.pre → 2.0.8.pre

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