doing 2.1.26 → 2.1.27

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 (151) hide show
  1. checksums.yaml +4 -4
  2. data/.yardoc/checksums +14 -19
  3. data/.yardoc/object_types +0 -0
  4. data/.yardoc/objects/root.dat +0 -0
  5. data/CHANGELOG.md +23 -0
  6. data/Dockerfile +5 -5
  7. data/Dockerfile-2.6 +5 -5
  8. data/Dockerfile-2.7 +5 -4
  9. data/Dockerfile-3.0 +5 -4
  10. data/Gemfile.lock +1 -1
  11. data/README.md +1 -1
  12. data/Rakefile +2 -3
  13. data/bin/commands/add_section.rb +2 -0
  14. data/bin/commands/again.rb +23 -65
  15. data/bin/commands/archive.rb +20 -61
  16. data/bin/commands/cancel.rb +27 -69
  17. data/bin/commands/changes.rb +32 -5
  18. data/bin/commands/colors.rb +4 -2
  19. data/bin/commands/commands.rb +4 -2
  20. data/bin/commands/commands_accepting.rb +62 -11
  21. data/bin/commands/completion.rb +10 -7
  22. data/bin/commands/config.rb +1 -1
  23. data/bin/commands/done.rb +3 -17
  24. data/bin/commands/finish.rb +7 -30
  25. data/bin/commands/flag.rb +15 -51
  26. data/bin/commands/grep.rb +12 -28
  27. data/bin/commands/import.rb +3 -33
  28. data/bin/commands/last.rb +3 -36
  29. data/bin/commands/meanwhile.rb +3 -13
  30. data/bin/commands/note.rb +13 -52
  31. data/bin/commands/now.rb +15 -21
  32. data/bin/commands/on.rb +3 -4
  33. data/bin/commands/recent.rb +3 -4
  34. data/bin/commands/redo.rb +6 -2
  35. data/bin/commands/reset.rb +19 -52
  36. data/bin/commands/rotate.rb +5 -36
  37. data/bin/commands/select.rb +23 -41
  38. data/bin/commands/show.rb +28 -74
  39. data/bin/commands/since.rb +3 -4
  40. data/bin/commands/tag.rb +4 -34
  41. data/bin/commands/tags.rb +5 -32
  42. data/bin/commands/today.rb +3 -4
  43. data/bin/commands/view.rb +36 -73
  44. data/bin/commands/yesterday.rb +4 -5
  45. data/bin/doing +150 -13
  46. data/docs/doc/Array.html +3 -502
  47. data/docs/doc/BooleanTermParser/Clause.html +1 -1
  48. data/docs/doc/BooleanTermParser/Operator.html +1 -1
  49. data/docs/doc/BooleanTermParser/Query.html +1 -1
  50. data/docs/doc/BooleanTermParser/QueryParser.html +1 -1
  51. data/docs/doc/BooleanTermParser/QueryTransformer.html +1 -1
  52. data/docs/doc/BooleanTermParser.html +1 -1
  53. data/docs/doc/Doing/Color.html +62 -56
  54. data/docs/doc/Doing/Completion.html +1 -1
  55. data/docs/doc/Doing/Configuration.html +35 -1
  56. data/docs/doc/Doing/Errors/DoingNoTraceError.html +1 -1
  57. data/docs/doc/Doing/Errors/DoingRuntimeError.html +1 -1
  58. data/docs/doc/Doing/Errors/DoingStandardError.html +1 -1
  59. data/docs/doc/Doing/Errors/EmptyInput.html +1 -1
  60. data/docs/doc/Doing/Errors/NoResults.html +1 -1
  61. data/docs/doc/Doing/Errors/PluginException.html +1 -1
  62. data/docs/doc/Doing/Errors/UserCancelled.html +1 -1
  63. data/docs/doc/Doing/Errors/WrongCommand.html +1 -1
  64. data/docs/doc/Doing/Errors.html +1 -1
  65. data/docs/doc/Doing/Hooks.html +1 -1
  66. data/docs/doc/Doing/Item.html +1 -1
  67. data/docs/doc/Doing/Items.html +2 -2
  68. data/docs/doc/Doing/LogAdapter.html +1 -1
  69. data/docs/doc/Doing/Note.html +2 -2
  70. data/docs/doc/Doing/Pager.html +1 -1
  71. data/docs/doc/Doing/Plugins.html +1 -1
  72. data/docs/doc/Doing/Prompt.html +1 -1
  73. data/docs/doc/Doing/Section.html +1 -1
  74. data/docs/doc/Doing/TemplateString.html +2 -2
  75. data/docs/doc/Doing/Types.html +41 -1
  76. data/docs/doc/Doing/Util/Backup.html +1 -1
  77. data/docs/doc/Doing/Util.html +1 -1
  78. data/docs/doc/Doing/WWID.html +10 -10
  79. data/docs/doc/Doing.html +3 -3
  80. data/docs/doc/FalseClass.html +35 -1
  81. data/docs/doc/GLI/Commands/Help.html +1 -1
  82. data/docs/doc/GLI/Commands/MarkdownDocumentListener.html +1 -1
  83. data/docs/doc/GLI/Commands.html +1 -1
  84. data/docs/doc/GLI.html +1 -1
  85. data/docs/doc/Hash.html +1 -1
  86. data/docs/doc/Object.html +1 -1
  87. data/docs/doc/PhraseParser/Operator.html +1 -1
  88. data/docs/doc/PhraseParser/PhraseClause.html +1 -1
  89. data/docs/doc/PhraseParser/Query.html +1 -1
  90. data/docs/doc/PhraseParser/QueryParser.html +1 -1
  91. data/docs/doc/PhraseParser/QueryTransformer.html +1 -1
  92. data/docs/doc/PhraseParser/TermClause.html +1 -1
  93. data/docs/doc/PhraseParser.html +1 -1
  94. data/docs/doc/Status.html +1 -1
  95. data/docs/doc/String.html +287 -3155
  96. data/docs/doc/Symbol.html +40 -6
  97. data/docs/doc/Time.html +1 -1
  98. data/docs/doc/TrueClass.html +35 -1
  99. data/docs/doc/_index.html +5 -10
  100. data/docs/doc/class_list.html +1 -1
  101. data/docs/doc/file.README.html +2 -2
  102. data/docs/doc/index.html +2 -2
  103. data/docs/doc/method_list.html +278 -678
  104. data/docs/doc/top-level-namespace.html +2 -2
  105. data/doing.rdoc +277 -175
  106. data/lib/completion/_doing.zsh +33 -29
  107. data/lib/completion/doing.bash +30 -19
  108. data/lib/completion/doing.fish +84 -72
  109. data/lib/doing/array/array.rb +4 -0
  110. data/lib/doing/array/nested_hash.rb +17 -0
  111. data/lib/doing/{array.rb → array/tags.rb} +7 -25
  112. data/lib/doing/changelog/change.rb +26 -11
  113. data/lib/doing/changelog/changes.rb +13 -3
  114. data/lib/doing/{array_chronify.rb → chronify/array.rb} +0 -0
  115. data/lib/doing/chronify/chronify.rb +5 -0
  116. data/lib/doing/{numeric_chronify.rb → chronify/numeric.rb} +0 -0
  117. data/lib/doing/{string_chronify.rb → chronify/string.rb} +0 -0
  118. data/lib/doing/colors.rb +115 -54
  119. data/lib/doing/configuration.rb +4 -0
  120. data/lib/doing/good.rb +8 -0
  121. data/lib/doing/help_monkey_patch.rb +6 -5
  122. data/lib/doing/item.rb +5 -5
  123. data/lib/doing/items.rb +2 -2
  124. data/lib/doing/log_adapter.rb +35 -2
  125. data/lib/doing/normalize.rb +188 -0
  126. data/lib/doing/plugins/export/dayone_export.rb +1 -1
  127. data/lib/doing/plugins/export/html_export.rb +1 -1
  128. data/lib/doing/plugins/export/json_export.rb +1 -1
  129. data/lib/doing/plugins/export/markdown_export.rb +1 -1
  130. data/lib/doing/plugins/export/template_export.rb +3 -1
  131. data/lib/doing/prompt.rb +1 -3
  132. data/lib/doing/string/highlight.rb +95 -0
  133. data/lib/doing/string/query.rb +129 -0
  134. data/lib/doing/string/string.rb +12 -0
  135. data/lib/doing/string/tags.rb +164 -0
  136. data/lib/doing/string/transform.rb +168 -0
  137. data/lib/doing/string/truncate.rb +75 -0
  138. data/lib/doing/string/url.rb +82 -0
  139. data/lib/doing/template_string.rb +0 -22
  140. data/lib/doing/types.rb +8 -0
  141. data/lib/doing/util.rb +13 -9
  142. data/lib/doing/version.rb +1 -1
  143. data/lib/doing/wwid.rb +53 -35
  144. data/lib/doing.rb +4 -6
  145. data/lib/examples/plugins/wiki_export/wiki_export.rb +1 -1
  146. data/lib/helpers/threaded_tests.rb +15 -2
  147. data/scripts/deploy.rb +107 -0
  148. data/scripts/runtests.sh +4 -0
  149. metadata +19 -8
  150. data/lib/doing/string.rb +0 -765
  151. data/lib/doing/symbol.rb +0 -28
@@ -1,25 +1,76 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # @@commands_accepting
2
4
  arg_name 'OPTION'
3
5
  command :commands_accepting do |c|
4
6
  c.desc 'Output in single column for completion'
5
7
  c.switch %i[c column]
6
8
 
9
+ c.desc 'Join multiple arguments using boolean (AND|OR|NOT)'
10
+ c.flag [:bool], must_match: REGEX_BOOL,
11
+ default_value: :and,
12
+ type: BooleanSymbol
13
+
7
14
  c.action do |g, o, a|
8
- a.each do |option|
9
- cmds = []
10
- commands.each do |cmd, v|
11
- v.flags.merge(v.switches).each do |n, flag|
12
- if flag.name == option.to_sym || flag.aliases&.include?(option.to_sym)
13
- cmds.push(cmd)
14
- end
15
+ cmds = []
16
+ commands.each { |cmd, v| cmds.push(cmd) if has_flags?(v, a, o[:bool]) }
17
+
18
+ if o[:column]
19
+ puts cmds.sort
20
+ else
21
+ description = "Commands "
22
+ description += "not " if o[:bool] == :not
23
+ description += "accepting "
24
+ description += a.map { |arg| "--#{arg}" }.join(o[:bool] == :and ? ' and ' : ' or ')
25
+ puts "#{description}: #{cmds.sort.join(', ')}"
26
+ end
27
+ end
28
+
29
+ def has_flags?(options, args, bool)
30
+ case bool
31
+ when :and
32
+ all_flags?(options, args)
33
+ when :not
34
+ no_flags?(options, args)
35
+ else
36
+ any_flags?(options, args)
37
+ end
38
+ end
39
+
40
+ def all_flags?(options, args)
41
+ args.each do |arg|
42
+ has_flag = false
43
+ options.flags.merge(options.switches).each do |_, flag|
44
+ if flag.name == arg.to_sym || flag.aliases&.include?(arg.to_sym)
45
+ has_flag = true
46
+ break
15
47
  end
16
48
  end
49
+ return false unless has_flag
50
+ end
51
+
52
+ true
53
+ end
54
+
55
+ def any_flags?(options, args)
56
+ args.each do |option|
57
+ options.flags.merge(options.switches).each do |_, flag|
58
+ return true if flag.name == option.to_sym || flag.aliases&.include?(option.to_sym)
59
+ end
60
+ end
17
61
 
18
- if o[:column]
19
- puts cmds.sort
20
- else
21
- puts "Commands accepting --#{option}: #{cmds.sort.join(', ')}"
62
+ false
63
+ end
64
+
65
+ def no_flags?(options, args)
66
+ args.each do |option|
67
+ options.flags.merge(options.switches).each do |_, flag|
68
+ return false if flag.name == option.to_sym || flag.aliases&.include?(option.to_sym)
22
69
  end
23
70
  end
71
+
72
+ true
24
73
  end
25
74
  end
75
+
76
+
@@ -1,12 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # @@completion
2
4
  desc 'Generate shell completion scripts'
3
- long_desc 'Generates the necessary scripts to add command line completion to various shells, so typing \'doing\' and hitting
4
- tab will offer completions of subcommands and their options.'
5
+ long_desc 'Generates the necessary scripts to add command line completion to various shells,
6
+ so typing \'doing\' and hitting tab will offer completions of subcommands and their options.'
5
7
  command :completion do |c|
6
8
  c.example 'doing completion', desc: 'Output zsh (default) to STDOUT'
7
- c.example 'doing completion --type zsh --file ~/.zsh-completions/_doing.zsh', desc: 'Output zsh completions to file'
8
- c.example 'doing completion --type fish --file ~/.config/fish/completions/doing.fish', desc: 'Output fish completions to file'
9
- c.example 'doing completion --type bash --file ~/.bash_it/completion/enabled/doing.bash', desc: 'Output bash completions to file'
9
+ c.example 'doing completion --type zsh --file ~/.zsh-completions/_doing.zsh',
10
+ desc: 'Output zsh completions to file'
11
+ c.example 'doing completion --type fish --file ~/.config/fish/completions/doing.fish',
12
+ desc: 'Output fish completions to file'
13
+ c.example 'doing completion --type bash --file ~/.bash_it/completion/enabled/doing.bash',
14
+ desc: 'Output bash completions to file'
10
15
 
11
16
  c.desc 'Shell to generate for (bash, zsh, fish)'
12
17
  c.arg_name 'SHELL'
@@ -17,8 +22,6 @@ command :completion do |c|
17
22
  c.flag %i[f file], default_value: 'STDOUT'
18
23
 
19
24
  c.action do |_global_options, options, _args|
20
- script_dir = File.join(File.dirname(__FILE__), '..', 'scripts')
21
-
22
25
  Doing::Completion.generate_completion(type: options[:type], file: options[:file])
23
26
  end
24
27
  end
@@ -218,7 +218,7 @@ command :config do |c|
218
218
 
219
219
  config_file = @config.choose_config(create: true)
220
220
 
221
- cfg = YAML.safe_load_file(config_file) || {}
221
+ cfg = Doing::Util.safe_load_file(config_file) || {}
222
222
 
223
223
  $stderr.puts "Updating #{config_file}".yellow
224
224
 
data/bin/commands/done.rb CHANGED
@@ -24,10 +24,6 @@ command %i[done did] do |c|
24
24
  c.arg_name 'DATE_STRING'
25
25
  c.flag %i[at finished], type: DateEndString
26
26
 
27
- c.desc 'Backdate start date by interval or set to time [4pm|20m|2h|"yesterday noon"]'
28
- c.arg_name 'DATE_STRING'
29
- c.flag %i[b back started], type: DateBeginString
30
-
31
27
  c.desc %(
32
28
  Start and end times as a date/time range `doing done --from "1am to 8am"`.
33
29
  Overrides other date flags.
@@ -45,24 +41,14 @@ command %i[done did] do |c|
45
41
  c.arg_name 'NAME'
46
42
  c.flag %i[s section]
47
43
 
48
- c.desc "Edit entry with #{Doing::Util.default_editor} (with no arguments, edits the last entry)"
49
- c.switch %i[e editor], negatable: false, default_value: false
50
-
51
- c.desc 'Include a note'
52
- c.arg_name 'TEXT'
53
- c.flag %i[n note]
54
-
55
- c.desc 'Prompt for note via multi-line input'
56
- c.switch %i[ask], negatable: false, default_value: false
57
-
58
44
  c.desc 'Finish last entry not already marked @done'
59
45
  c.switch %i[u unfinished], negatable: false, default_value: false
60
46
 
61
- # c.desc "Edit entry with specified app"
62
- # c.arg_name 'editor_app'
63
- # # c.flag [:a, :app]
47
+ add_options(:add_entry, c)
64
48
 
65
49
  c.action do |_global_options, options, args|
50
+ @wwid.auto_tag = !options[:noauto]
51
+
66
52
  took = 0
67
53
  donedate = nil
68
54
 
@@ -22,37 +22,10 @@ command :finish do |c|
22
22
  c.arg_name 'DATE_STRING'
23
23
  c.flag %i[at finished], type: DateEndString
24
24
 
25
- c.desc 'Finish the last X entries containing TAG.
26
- Separate multiple tags with comma (--tag=tag1,tag2), combine with --bool. Wildcards allowed (*, ?).'
27
- c.arg_name 'TAG'
28
- c.flag [:tag], type: TagArray
25
+ c.desc 'Overwrite existing @done tag with new date'
26
+ c.switch %i[update], negatable: false, default_value: false
29
27
 
30
- c.desc 'Finish the last X entries matching search filter, surround with slashes for regex (e.g. "/query.*/"), start with single quote for exact match ("\'query")'
31
- c.arg_name 'QUERY'
32
- c.flag [:search]
33
-
34
- c.desc 'Perform a tag value query ("@done > two hours ago" or "@progress < 50"). May be used multiple times, combined with --bool'
35
- c.arg_name 'QUERY'
36
- c.flag [:val], multiple: true, must_match: REGEX_VALUE_QUERY
37
-
38
- # c.desc '[DEPRECATED] Use alternative fuzzy matching for search string'
39
- # c.switch [:fuzzy], default_value: false, negatable: false
40
-
41
- c.desc 'Force exact search string matching (case sensitive)'
42
- c.switch %i[x exact], default_value: @config.exact_match?, negatable: @config.exact_match?
43
-
44
- c.desc 'Finish items that *don\'t* match search/tag filters'
45
- c.switch [:not], default_value: false, negatable: false
46
-
47
- c.desc 'Case sensitivity for search string matching [(c)ase-sensitive, (i)gnore, (s)mart]'
48
- c.arg_name 'TYPE'
49
- c.flag [:case], must_match: /^[csi]/, default_value: @settings.dig('search', 'case')
50
-
51
- c.desc 'Boolean (AND|OR|NOT) with which to combine multiple tag filters. Use PATTERN to parse + and - as booleans'
52
- c.arg_name 'BOOLEAN'
53
- c.flag [:bool], must_match: REGEX_BOOL, default_value: 'PATTERN'
54
-
55
- c.desc 'Remove done tag'
28
+ c.desc 'Remove @done tag'
56
29
  c.switch %i[r remove], negatable: false, default_value: false
57
30
 
58
31
  c.desc 'Finish last entry (or entries) not already marked @done'
@@ -73,6 +46,9 @@ command :finish do |c|
73
46
  c.desc 'Select item(s) to finish from a menu of matching entries'
74
47
  c.switch %i[i interactive], negatable: false, default_value: false
75
48
 
49
+ add_options(:search, c)
50
+ add_options(:tag_filter, c)
51
+
76
52
  c.action do |_global_options, options, args|
77
53
  options[:fuzzy] = false
78
54
  unless options[:auto]
@@ -141,6 +117,7 @@ command :finish do |c|
141
117
  tags: ['done'],
142
118
  took: options[:took],
143
119
  unfinished: options[:unfinished],
120
+ update: options[:update],
144
121
  val: options[:val]
145
122
  }
146
123
 
data/bin/commands/flag.rb CHANGED
@@ -26,39 +26,12 @@ command %i[mark flag] do |c|
26
26
  c.desc 'Flag last entry (or entries) not marked @done'
27
27
  c.switch %i[u unfinished], negatable: false, default_value: false
28
28
 
29
- c.desc 'Flag the last entry containing TAG.
30
- Separate multiple tags with comma (--tag=tag1,tag2), combine with --bool. Wildcards allowed (*, ?).'
31
- c.arg_name 'TAG'
32
- c.flag [:tag], type: TagArray
33
-
34
- c.desc 'Flag the last entry matching search filter, surround with slashes for regex (e.g. "/query.*/"), start with single quote for exact match ("\'query")'
35
- c.arg_name 'QUERY'
36
- c.flag [:search]
37
-
38
- c.desc 'Perform a tag value query ("@done > two hours ago" or "@progress < 50"). May be used multiple times, combined with --bool'
39
- c.arg_name 'QUERY'
40
- c.flag [:val], multiple: true, must_match: REGEX_VALUE_QUERY
41
-
42
- # c.desc '[DEPRECATED] Use alternative fuzzy matching for search string'
43
- # c.switch [:fuzzy], default_value: false, negatable: false
44
-
45
- c.desc 'Force exact search string matching (case sensitive)'
46
- c.switch %i[x exact], default_value: @config.exact_match?, negatable: @config.exact_match?
47
-
48
- c.desc 'Flag items that *don\'t* match search/tag/date filters'
49
- c.switch [:not], default_value: false, negatable: false
50
-
51
- c.desc 'Case sensitivity for search string matching [(c)ase-sensitive, (i)gnore, (s)mart]'
52
- c.arg_name 'TYPE'
53
- c.flag [:case], must_match: /^[csi]/, default_value: @settings.dig('search', 'case')
54
-
55
- c.desc 'Boolean (AND|OR|NOT) with which to combine multiple tag filters. Use PATTERN to parse + and - as booleans'
56
- c.arg_name 'BOOLEAN'
57
- c.flag [:bool], must_match: REGEX_BOOL, default_value: 'PATTERN'
58
-
59
29
  c.desc 'Select item(s) to flag from a menu of matching entries'
60
30
  c.switch %i[i interactive], negatable: false, default_value: false
61
31
 
32
+ add_options(:search, c)
33
+ add_options(:tag_filter, c)
34
+
62
35
  c.action do |_global_options, options, _args|
63
36
  options[:fuzzy] = false
64
37
  mark = @settings['marker_tag'] || 'flagged'
@@ -67,15 +40,9 @@ command %i[mark flag] do |c|
67
40
 
68
41
  section = 'All'
69
42
 
70
- if options[:section]
71
- section = @wwid.guess_section(options[:section]) || options[:section].cap_first
72
- end
43
+ section = @wwid.guess_section(options[:section]) || options[:section].cap_first if options[:section]
73
44
 
74
- if options[:tag].nil?
75
- search_tags = []
76
- else
77
- search_tags = options[:tag]
78
- end
45
+ search_tags = options[:tag].nil? ? [] : options[:tag]
79
46
 
80
47
  if options[:interactive]
81
48
  count = 0
@@ -84,8 +51,6 @@ command %i[mark flag] do |c|
84
51
  count = options[:count].to_i
85
52
  end
86
53
 
87
- options[:case] = options[:case].normalize_case
88
-
89
54
  if options[:search]
90
55
  search = options[:search]
91
56
  search.sub!(/^'?/, "'") if options[:exact]
@@ -93,16 +58,15 @@ command %i[mark flag] do |c|
93
58
  end
94
59
 
95
60
  if count.zero? && !options[:force]
96
- if options[:search]
97
- section_q = ' matching your search terms'
98
- elsif options[:tag]
99
- section_q = ' matching your tag search'
100
- elsif section == 'All'
101
- section_q = ''
102
- else
103
- section_q = " in section #{section}"
104
- end
105
-
61
+ section_q = if options[:search]
62
+ ' matching your search terms'
63
+ elsif options[:tag]
64
+ ' matching your tag search'
65
+ elsif section == 'All'
66
+ ''
67
+ else
68
+ " in section #{section}"
69
+ end
106
70
 
107
71
  question = if options[:remove]
108
72
  "Are you sure you want to unflag all entries#{section_q}"
@@ -119,7 +83,7 @@ command %i[mark flag] do |c|
119
83
  options[:section] = section
120
84
  options[:tag] = search_tags
121
85
  options[:tags] = [mark]
122
- options[:tag_bool] = options[:bool].normalize_bool
86
+ options[:tag_bool] = options[:bool]
123
87
 
124
88
  @wwid.tag_last(options)
125
89
  end
data/bin/commands/grep.rb CHANGED
@@ -16,25 +16,6 @@ command %i[grep search] do |c|
16
16
  c.arg_name 'NAME'
17
17
  c.flag %i[s section], default_value: 'All'
18
18
 
19
- c.desc 'Search entries older than date. If this is only a time (8am, 1:30pm, 15:00), all dates will be included, but entries will be filtered by time of day'
20
- c.arg_name 'DATE_STRING'
21
- c.flag [:before], type: DateBeginString
22
-
23
- c.desc 'Search entries newer than date. If this is only a time (8am, 1:30pm, 15:00), all dates will be included, but entries will be filtered by time of day'
24
- c.arg_name 'DATE_STRING'
25
- c.flag [:after], type: DateEndString
26
-
27
- c.desc %(
28
- Date range to show, or a single day to filter date on.
29
- Date range argument should be quoted. Date specifications can be natural language.
30
- To specify a range, use "to" or "through": `doing search --from "monday 8am to friday 5pm"`.
31
-
32
- If values are only time(s) (6am to noon) all dates will be included, but entries will be filtered
33
- by time of day.
34
- )
35
- c.arg_name 'DATE_OR_RANGE'
36
- c.flag [:from], type: DateRangeString
37
-
38
19
  c.desc "Output to export format (#{Doing::Plugins.plugin_names(type: :export)})"
39
20
  c.arg_name 'FORMAT'
40
21
  c.flag %i[o output]
@@ -57,10 +38,9 @@ command %i[grep search] do |c|
57
38
  c.switch [:totals], default_value: false, negatable: false
58
39
 
59
40
  c.desc 'Sort tags by (name|time)'
60
- default = 'time'
61
- default = @settings['tag_sort'] || 'name'
41
+ default = @settings['tag_sort'].normalize_tag_sort || :name
62
42
  c.arg_name 'KEY'
63
- c.flag [:tag_sort], must_match: /^(?:name|time)$/i, default_value: default
43
+ c.flag [:tag_sort], must_match: REGEX_TAG_SORT, default_value: default, type: TagSortSymbol
64
44
 
65
45
  c.desc 'Only show items with recorded time intervals'
66
46
  c.switch [:only_timed], default_value: false, negatable: false
@@ -76,7 +56,9 @@ command %i[grep search] do |c|
76
56
 
77
57
  c.desc 'Case sensitivity for search string matching [(c)ase-sensitive, (i)gnore, (s)mart]'
78
58
  c.arg_name 'TYPE'
79
- c.flag [:case], must_match: /^[csi]/, default_value: @settings.dig('search', 'case')
59
+ c.flag [:case], must_match: REGEX_CASE,
60
+ default_value: @settings.dig('search', 'case').normalize_case,
61
+ type: CaseSymbol
80
62
 
81
63
  c.desc "Highlight search matches in output. Only affects command line output"
82
64
  c.switch %i[h hilite], default_value: @settings.dig('search', 'highlight')
@@ -95,7 +77,12 @@ command %i[grep search] do |c|
95
77
  c.flag [:val], multiple: true, must_match: REGEX_VALUE_QUERY
96
78
 
97
79
  c.desc 'Combine multiple tags or value queries using AND, OR, or NOT'
98
- c.flag [:bool], must_match: REGEX_BOOL, default_value: 'AND'
80
+ c.arg_name 'BOOLEAN'
81
+ c.flag [:bool], must_match: REGEX_BOOL,
82
+ default_value: :pattern,
83
+ type: BooleanSymbol
84
+
85
+ add_options(:date_filter, c)
99
86
 
100
87
  c.action do |_global_options, options, args|
101
88
  options[:fuzzy] = false
@@ -106,14 +93,11 @@ command %i[grep search] do |c|
106
93
 
107
94
  section = @wwid.guess_section(options[:section]) if options[:section]
108
95
 
109
- options[:case] = options[:case].normalize_case
110
- options[:bool] = options[:bool].normalize_bool
111
-
112
96
  search = args.join(' ')
113
97
  search.sub!(/^'?/, "'") if options[:exact]
114
98
 
115
99
  options[:times] = true if options[:totals]
116
- options[:sort_tags] = options[:tag_sort] =~ /^n/i
100
+ options[:sort_tags] = options[:tag_sort]
117
101
  options[:highlight] = true
118
102
  options[:search] = search
119
103
  options[:section] = section
@@ -11,23 +11,9 @@ command :import do |c|
11
11
  c.arg_name 'TYPE'
12
12
  c.flag :type, default_value: 'doing'
13
13
 
14
- c.desc 'Only import items matching search. Surround with slashes for regex (/query/), start with single quote for exact match ("\'query")'
15
- c.arg_name 'QUERY'
16
- c.flag [:search]
17
-
18
- # c.desc '[DEPRECATED] Use alternative fuzzy matching for search string'
19
- # c.switch [:fuzzy], default_value: false, negatable: false
20
-
21
- c.desc 'Force exact search string matching (case sensitive)'
22
- c.switch %i[x exact], default_value: @config.exact_match?, negatable: @config.exact_match?
23
-
24
14
  c.desc 'Import items that *don\'t* match search/tag/date filters'
25
15
  c.switch [:not], default_value: false, negatable: false
26
16
 
27
- c.desc 'Case sensitivity for search string matching [(c)ase-sensitive, (i)gnore, (s)mart]'
28
- c.arg_name 'TYPE'
29
- c.flag [:case], must_match: /^[csi]/, default_value: @settings.dig('search', 'case')
30
-
31
17
  c.desc 'Only import items with recorded time intervals'
32
18
  c.switch [:only_timed], default_value: false, negatable: false
33
19
 
@@ -46,26 +32,12 @@ command :import do |c|
46
32
  c.arg_name 'PREFIX'
47
33
  c.flag :prefix
48
34
 
49
- # TODO: Allow time range filtering
50
- c.desc 'Import entries older than date'
51
- c.arg_name 'DATE_STRING'
52
- c.flag [:before], type: DateBeginString
53
-
54
- c.desc 'Import entries newer than date'
55
- c.arg_name 'DATE_STRING'
56
- c.flag [:after], type: DateEndString
57
-
58
- c.desc %(
59
- Date range to import. Date range argument should be quoted. Date specifications can be natural language.
60
- To specify a range, use "to" or "through": `--from "monday to friday"` or `--from 10/1 to 10/31`.
61
- Has no effect unless the import plugin has implemented date range filtering.
62
- )
63
- c.arg_name 'DATE_OR_RANGE'
64
- c.flag %i[f from], type: DateRangeString
65
-
66
35
  c.desc 'Allow entries that overlap existing times'
67
36
  c.switch [:overlap], negatable: true
68
37
 
38
+ add_options(:search, c)
39
+ add_options(:date_filter, c)
40
+
69
41
  c.action do |_global_options, options, args|
70
42
  options[:fuzzy] = false
71
43
  if options[:section]
@@ -88,8 +60,6 @@ command :import do |c|
88
60
  options[:date_filter][0] = options[:after] || Time.now - (1 << 64)
89
61
  end
90
62
 
91
- options[:case] = options[:case].normalize_case
92
-
93
63
  if options[:type] =~ Doing::Plugins.plugin_regex(type: :import)
94
64
  options[:no_overlap] = !options[:overlap]
95
65
  @wwid.import(args, options)
data/bin/commands/last.rb CHANGED
@@ -21,18 +21,6 @@ command :last do |c|
21
21
  c.desc "Delete the last entry"
22
22
  c.switch %i[d delete], negatable: false, default_value: false
23
23
 
24
- c.desc 'Tag filter, combine multiple tags with a comma. Wildcards allowed (*, ?)'
25
- c.arg_name 'TAG'
26
- c.flag [:tag], type: TagArray
27
-
28
- c.desc 'Tag boolean (AND|OR|NOT). Use PATTERN to parse + and - as booleans'
29
- c.arg_name 'BOOLEAN'
30
- c.flag [:bool], must_match: REGEX_BOOL, default_value: 'PATTERN'
31
-
32
- c.desc 'Search filter, surround with slashes for regex (/query/), start with single quote for exact match ("\'query")'
33
- c.arg_name 'QUERY'
34
- c.flag [:search]
35
-
36
24
  c.desc "Output using a template from configuration"
37
25
  c.arg_name 'TEMPLATE_KEY'
38
26
  c.flag [:config_template], type: TemplateName, default_value: 'last'
@@ -44,38 +32,17 @@ command :last do |c|
44
32
  c.desc "Highlight search matches in output. Only affects command line output"
45
33
  c.switch %i[h hilite], default_value: @settings.dig('search', 'highlight')
46
34
 
47
- c.desc 'Perform a tag value query ("@done > two hours ago" or "@progress < 50"). May be used multiple times, combined with --bool'
48
- c.arg_name 'QUERY'
49
- c.flag [:val], multiple: true, must_match: REGEX_VALUE_QUERY
50
-
51
35
  c.desc 'Show elapsed time if entry is not tagged @done'
52
36
  c.switch [:duration]
53
37
 
54
- # c.desc '[DEPRECATED] Use alternative fuzzy matching for search string'
55
- # c.switch [:fuzzy], default_value: false, negatable: false
56
-
57
- c.desc 'Force exact search string matching (case sensitive)'
58
- c.switch %i[x exact], default_value: @config.exact_match?, negatable: @config.exact_match?
59
-
60
- c.desc 'Show items that *don\'t* match search string or tag filter'
61
- c.switch [:not], default_value: false, negatable: false
62
-
63
- c.desc 'Case sensitivity for search string matching [(c)ase-sensitive, (i)gnore, (s)mart]'
64
- c.arg_name 'TYPE'
65
- c.flag [:case], must_match: /^[csi]/, default_value: @settings.dig('search', 'case')
38
+ add_options(:search, c)
39
+ add_options(:tag_filter, c)
66
40
 
67
41
  c.action do |global_options, options, _args|
68
42
  options[:fuzzy] = false
69
43
  raise InvalidArgument, '--tag and --search can not be used together' if options[:tag] && options[:search]
70
44
 
71
- if options[:tag].nil?
72
- options[:tag] = []
73
- else
74
- options[:tag] = options[:tag]
75
- options[:bool] = options[:bool].normalize_bool
76
- end
77
-
78
- options[:case] = options[:case].normalize_case
45
+ options[:tag] ||= []
79
46
 
80
47
  options[:search] = options[:search].sub(/^'?/, "'") if options[:search] && options[:exact]
81
48
 
@@ -15,24 +15,14 @@ command :meanwhile do |c|
15
15
  c.arg_name 'NAME'
16
16
  c.flag %i[s section]
17
17
 
18
- c.desc "Edit entry with #{Doing::Util.default_editor}"
19
- c.switch %i[e editor], negatable: false, default_value: false
20
-
21
18
  c.desc 'Archive previous @meanwhile entry'
22
19
  c.switch %i[a archive], negatable: false, default_value: false
23
20
 
24
- c.desc 'Backdate start date for new entry to date string [4pm|20m|2h|yesterday noon]'
25
- c.arg_name 'DATE_STRING'
26
- c.flag %i[b back started], type: DateBeginString
27
-
28
- c.desc 'Note'
29
- c.arg_name 'TEXT'
30
- c.flag %i[n note]
31
-
32
- c.desc 'Prompt for note via multi-line input'
33
- c.switch %i[ask], negatable: false, default_value: false
21
+ add_options(:add_entry, c)
34
22
 
35
23
  c.action do |_global_options, options, args|
24
+ @wwid.auto_tag = !options[:noauto]
25
+
36
26
  if options[:back]
37
27
  date = options[:back]
38
28
 
data/bin/commands/note.rb CHANGED
@@ -24,50 +24,19 @@ command :note do |c|
24
24
  c.desc "Replace/Remove last entry's note (default append)"
25
25
  c.switch %i[r remove], negatable: false, default_value: false
26
26
 
27
- c.desc 'Add/remove note from last entry matching tag. Wildcards allowed (*, ?)'
28
- c.arg_name 'TAG'
29
- c.flag [:tag], type: TagArray
30
-
31
- c.desc 'Add/remove note from last entry matching search filter, surround with slashes for regex (e.g. "/query.*/"), start with single quote for exact match ("\'query")'
32
- c.arg_name 'QUERY'
33
- c.flag [:search]
34
-
35
- c.desc 'Perform a tag value query ("@done > two hours ago" or "@progress < 50"). May be used multiple times, combined with --bool'
36
- c.arg_name 'QUERY'
37
- c.flag [:val], multiple: true, must_match: REGEX_VALUE_QUERY
38
-
39
- # c.desc '[DEPRECATED] Use alternative fuzzy matching for search string'
40
- # c.switch [:fuzzy], default_value: false, negatable: false
41
-
42
- c.desc 'Force exact search string matching (case sensitive)'
43
- c.switch %i[x exact], default_value: @config.exact_match?, negatable: @config.exact_match?
44
-
45
- c.desc 'Add note to item that *doesn\'t* match search/tag filters'
46
- c.switch [:not], default_value: false, negatable: false
47
-
48
- c.desc 'Case sensitivity for search string matching [(c)ase-sensitive, (i)gnore, (s)mart]'
49
- c.arg_name 'TYPE'
50
- c.flag [:case], must_match: /^[csi]/, default_value: @settings.dig('search', 'case')
51
-
52
- c.desc 'Boolean (AND|OR|NOT) with which to combine multiple tag filters. Use PATTERN to parse + and - as booleans'
53
- c.arg_name 'BOOLEAN'
54
- c.flag [:bool], must_match: REGEX_BOOL, default_value: 'PATTERN'
55
-
56
27
  c.desc 'Select item for new note from a menu of matching entries'
57
28
  c.switch %i[i interactive], negatable: false, default_value: false
58
29
 
59
30
  c.desc 'Prompt for note via multi-line input'
60
31
  c.switch %i[ask], negatable: false, default_value: false
61
32
 
33
+ add_options(:search, c)
34
+ add_options(:tag_filter, c)
35
+
62
36
  c.action do |_global_options, options, args|
63
37
  options[:fuzzy] = false
64
- if options[:section]
65
- options[:section] = @wwid.guess_section(options[:section]) || options[:section].cap_first
66
- end
67
-
68
- options[:tag_bool] = options[:bool].normalize_bool
69
-
70
- options[:case] = options[:case].normalize_case
38
+ options[:section] = @wwid.guess_section(options[:section]) || options[:section].cap_first if options[:section]
39
+ options[:tag_bool] = options[:bool]
71
40
 
72
41
  if options[:search]
73
42
  search = options[:search]
@@ -78,30 +47,22 @@ command :note do |c|
78
47
  last_entry = @wwid.last_entry(options)
79
48
  old_entry = last_entry.clone
80
49
 
81
- unless last_entry
82
- Doing.logger.warn('Not found:', 'No entry matching parameters was found.')
83
- return
84
- end
50
+ raise NoResults, 'No entry matching parameters was found.' unless last_entry
85
51
 
86
52
  last_note = last_entry.note || Doing::Note.new
87
53
  new_note = Doing::Note.new
88
54
 
89
- if $stdin.stat.size.positive?
90
- new_note.add($stdin.read.strip)
91
- end
92
-
93
- unless args.empty?
94
- new_note.add(args.join(' '))
95
- end
55
+ new_note.add($stdin.read.strip) if $stdin.stat.size.positive?
56
+ new_note.add(args.join(' ')) unless args.empty?
96
57
 
97
58
  if options[:editor]
98
59
  raise MissingEditor, 'No EDITOR variable defined in environment' if Doing::Util.default_editor.nil?
99
60
 
100
- if options[:remove]
101
- input = Doing::Note.new
102
- else
103
- input = last_entry.note || Doing::Note.new
104
- end
61
+ input = if options[:remove]
62
+ Doing::Note.new
63
+ else
64
+ last_entry.note || Doing::Note.new
65
+ end
105
66
 
106
67
  input.add(new_note)
107
68