doing 2.1.26 → 2.1.30

Sign up to get free protection for your applications and to get access to all the features.
Files changed (156) hide show
  1. checksums.yaml +4 -4
  2. data/.yardoc/checksums +15 -20
  3. data/.yardoc/object_types +0 -0
  4. data/.yardoc/objects/root.dat +0 -0
  5. data/CHANGELOG.md +52 -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 +2 -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 +53 -12
  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 +8 -8
  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/open.rb +3 -3
  34. data/bin/commands/recent.rb +3 -4
  35. data/bin/commands/redo.rb +6 -2
  36. data/bin/commands/reset.rb +19 -52
  37. data/bin/commands/rotate.rb +5 -36
  38. data/bin/commands/select.rb +23 -41
  39. data/bin/commands/show.rb +28 -74
  40. data/bin/commands/since.rb +3 -4
  41. data/bin/commands/tag.rb +4 -34
  42. data/bin/commands/tags.rb +5 -32
  43. data/bin/commands/today.rb +3 -4
  44. data/bin/commands/view.rb +36 -73
  45. data/bin/commands/yesterday.rb +4 -5
  46. data/bin/doing +150 -13
  47. data/docs/doc/Array.html +3 -502
  48. data/docs/doc/BooleanTermParser/Clause.html +1 -1
  49. data/docs/doc/BooleanTermParser/Operator.html +1 -1
  50. data/docs/doc/BooleanTermParser/Query.html +1 -1
  51. data/docs/doc/BooleanTermParser/QueryParser.html +1 -1
  52. data/docs/doc/BooleanTermParser/QueryTransformer.html +1 -1
  53. data/docs/doc/BooleanTermParser.html +1 -1
  54. data/docs/doc/Doing/Color.html +62 -56
  55. data/docs/doc/Doing/Completion.html +1 -1
  56. data/docs/doc/Doing/Configuration.html +35 -1
  57. data/docs/doc/Doing/Errors/DoingNoTraceError.html +1 -1
  58. data/docs/doc/Doing/Errors/DoingRuntimeError.html +1 -1
  59. data/docs/doc/Doing/Errors/DoingStandardError.html +1 -1
  60. data/docs/doc/Doing/Errors/EmptyInput.html +1 -1
  61. data/docs/doc/Doing/Errors/NoResults.html +1 -1
  62. data/docs/doc/Doing/Errors/PluginException.html +1 -1
  63. data/docs/doc/Doing/Errors/UserCancelled.html +1 -1
  64. data/docs/doc/Doing/Errors/WrongCommand.html +1 -1
  65. data/docs/doc/Doing/Errors.html +1 -1
  66. data/docs/doc/Doing/Hooks.html +1 -1
  67. data/docs/doc/Doing/Item.html +1 -1
  68. data/docs/doc/Doing/Items.html +2 -2
  69. data/docs/doc/Doing/LogAdapter.html +1 -1
  70. data/docs/doc/Doing/Note.html +2 -2
  71. data/docs/doc/Doing/Pager.html +1 -1
  72. data/docs/doc/Doing/Plugins.html +1 -1
  73. data/docs/doc/Doing/Prompt.html +1 -1
  74. data/docs/doc/Doing/Section.html +1 -1
  75. data/docs/doc/Doing/TemplateString.html +2 -2
  76. data/docs/doc/Doing/Types.html +41 -1
  77. data/docs/doc/Doing/Util/Backup.html +1 -1
  78. data/docs/doc/Doing/Util.html +1 -1
  79. data/docs/doc/Doing/WWID.html +10 -10
  80. data/docs/doc/Doing.html +3 -3
  81. data/docs/doc/FalseClass.html +35 -1
  82. data/docs/doc/GLI/Commands/Help.html +1 -1
  83. data/docs/doc/GLI/Commands/MarkdownDocumentListener.html +1 -1
  84. data/docs/doc/GLI/Commands.html +1 -1
  85. data/docs/doc/GLI.html +1 -1
  86. data/docs/doc/Hash.html +1 -1
  87. data/docs/doc/Object.html +1 -1
  88. data/docs/doc/PhraseParser/Operator.html +1 -1
  89. data/docs/doc/PhraseParser/PhraseClause.html +1 -1
  90. data/docs/doc/PhraseParser/Query.html +1 -1
  91. data/docs/doc/PhraseParser/QueryParser.html +1 -1
  92. data/docs/doc/PhraseParser/QueryTransformer.html +1 -1
  93. data/docs/doc/PhraseParser/TermClause.html +1 -1
  94. data/docs/doc/PhraseParser.html +1 -1
  95. data/docs/doc/Status.html +1 -1
  96. data/docs/doc/String.html +287 -3155
  97. data/docs/doc/Symbol.html +40 -6
  98. data/docs/doc/Time.html +1 -1
  99. data/docs/doc/TrueClass.html +35 -1
  100. data/docs/doc/_index.html +5 -10
  101. data/docs/doc/class_list.html +1 -1
  102. data/docs/doc/file.README.html +2 -2
  103. data/docs/doc/index.html +2 -2
  104. data/docs/doc/method_list.html +278 -678
  105. data/docs/doc/top-level-namespace.html +2 -2
  106. data/doing.gemspec +1 -0
  107. data/doing.rdoc +297 -206
  108. data/lib/completion/_doing.zsh +32 -32
  109. data/lib/completion/doing.bash +30 -30
  110. data/lib/completion/doing.fish +87 -77
  111. data/lib/doing/array/array.rb +4 -0
  112. data/lib/doing/array/nested_hash.rb +17 -0
  113. data/lib/doing/{array.rb → array/tags.rb} +7 -25
  114. data/lib/doing/changelog/change.rb +26 -11
  115. data/lib/doing/changelog/changes.rb +37 -8
  116. data/lib/doing/changelog/version.rb +11 -3
  117. data/lib/doing/{array_chronify.rb → chronify/array.rb} +0 -0
  118. data/lib/doing/chronify/chronify.rb +5 -0
  119. data/lib/doing/{numeric_chronify.rb → chronify/numeric.rb} +0 -0
  120. data/lib/doing/{string_chronify.rb → chronify/string.rb} +0 -0
  121. data/lib/doing/colors.rb +115 -54
  122. data/lib/doing/completion/zsh_completion.rb +5 -0
  123. data/lib/doing/configuration.rb +9 -5
  124. data/lib/doing/good.rb +8 -0
  125. data/lib/doing/help_monkey_patch.rb +6 -5
  126. data/lib/doing/item.rb +5 -5
  127. data/lib/doing/items.rb +2 -2
  128. data/lib/doing/log_adapter.rb +35 -2
  129. data/lib/doing/normalize.rb +188 -0
  130. data/lib/doing/plugins/export/dayone_export.rb +1 -1
  131. data/lib/doing/plugins/export/html_export.rb +1 -1
  132. data/lib/doing/plugins/export/json_export.rb +1 -1
  133. data/lib/doing/plugins/export/markdown_export.rb +1 -1
  134. data/lib/doing/plugins/export/template_export.rb +3 -1
  135. data/lib/doing/prompt.rb +1 -3
  136. data/lib/doing/section.rb +1 -1
  137. data/lib/doing/string/highlight.rb +95 -0
  138. data/lib/doing/string/query.rb +129 -0
  139. data/lib/doing/string/string.rb +12 -0
  140. data/lib/doing/string/tags.rb +164 -0
  141. data/lib/doing/string/transform.rb +168 -0
  142. data/lib/doing/string/truncate.rb +75 -0
  143. data/lib/doing/string/url.rb +82 -0
  144. data/lib/doing/template_string.rb +0 -22
  145. data/lib/doing/types.rb +8 -0
  146. data/lib/doing/util.rb +13 -9
  147. data/lib/doing/version.rb +1 -1
  148. data/lib/doing/wwid.rb +54 -36
  149. data/lib/doing.rb +5 -6
  150. data/lib/examples/plugins/wiki_export/wiki_export.rb +1 -1
  151. data/lib/helpers/threaded_tests.rb +15 -2
  152. data/scripts/deploy.rb +107 -0
  153. data/scripts/runtests.sh +4 -0
  154. metadata +39 -8
  155. data/lib/doing/string.rb +0 -765
  156. data/lib/doing/symbol.rb +0 -28
@@ -1,4 +1,21 @@
1
1
  # @@changelog @@changes
2
+
3
+ MARKDOWN_THEME = {
4
+ em: %i[white dark],
5
+ header: %i[cyan bold],
6
+ hr: :yellow,
7
+ link: %i[bright_cyan underline],
8
+ list: :yellow,
9
+ strong: %i[yellow bold],
10
+ table: :yellow,
11
+ quote: :yellow,
12
+ image: :bright_black,
13
+ note: :yellow,
14
+ comment: :bright_black
15
+ }.deep_freeze
16
+
17
+ CHANGE_RX = /^(?:(?:(?:[<>=]+|p(?:rior)|b(?:efore)|o(?:lder)|s(?:ince)|a(?:fter)|n(?:ewer))? *[0-9.*?]+ *)+|(?:[\d.]+ *(?:-|to)+ *[0-9.]+))$/
18
+
2
19
  desc 'List recent changes in Doing'
3
20
  long_desc %(Display a formatted list of changes in recent versions.
4
21
 
@@ -10,14 +27,30 @@ command %i[changes changelog] do |c|
10
27
 
11
28
  c.desc %(Look up a specific version. Specify versions as "MAJ.MIN.PATCH", MIN
12
29
  and PATCH are optional. Use > or < to see all changes since or prior
13
- to a version.)
30
+ to a version. Wildcards (*?) accepted unless using < or >.)
14
31
  c.arg_name 'VERSION'
15
- c.flag %i[l lookup], must_match: /^(?:(?:(?:[<>=]|p(?:rior)|b(?:efore)|o(?:lder)|s(?:ince)|a(?:fter)|n(?:ewer))? *[\d.*?]+ *)+|(?:[\d.]+ *-+ *[\d.]+))$/
32
+ c.flag %i[l lookup], must_match: CHANGE_RX
16
33
 
17
34
  c.desc %(Show changelogs matching search terms (uses pattern-based searching).
18
35
  Add slashes to search with regular expressions, e.g. `--search "/output.*flag/"`)
19
36
  c.flag %i[s search]
20
37
 
38
+ c.desc 'Sort order (asc/desc)'
39
+ c.arg_name 'ORDER'
40
+ c.flag %i[sort], must_match: REGEX_SORT_ORDER, default_value: :desc, type: OrderSymbol
41
+
42
+ c.desc 'Only output changes, no version numbers, headers, or dates'
43
+ c.switch %i[C changes], default_value: false, negatable: false
44
+
45
+ c.desc 'Output raw Markdown'
46
+ c.switch %i[m md markdown], default_value: false, negatable: false
47
+
48
+ c.desc 'Force rendered output'
49
+ c.switch %i[render], default_value: false, negatable: false
50
+
51
+ c.desc 'Open changelog in interactive viewer'
52
+ c.switch %i[i interactive], default_value: false, negatable: false
53
+
21
54
  c.example 'doing changes', desc: 'View changes in the current version'
22
55
  c.example 'doing changes --all', desc: 'See the entire changelog'
23
56
  c.example 'doing changes --lookup 2.0.21', desc: 'See changes from version 2.0.21'
@@ -25,18 +58,26 @@ command %i[changes changelog] do |c|
25
58
  c.example 'doing changes --search "tags +bool"', desc: 'See all changes containing "tags" and "bool"'
26
59
  c.example 'doing changes -l "> 2.1" -s "pattern"', desc: 'Lookup and search can be combined'
27
60
 
61
+ c.action do |_global_options, options, _args|
62
+ cl = Doing::Changes.new(lookup: options[:lookup], search: options[:search], changes: options[:changes], sort: options[:sort])
28
63
 
29
- c.action do |_global_options, options, args|
30
- cl = Doing::Changes.new(lookup: options[:lookup], search: options[:search])
64
+ if options[:interactive]
65
+ cl.interactive
66
+ else
67
+ content = if options[:all] || options[:search] || options[:lookup]
68
+ cl.to_s
69
+ else
70
+ cl.latest
71
+ end
31
72
 
32
- content = if options[:all] || options[:search] || options[:lookup]
33
- cl.to_s
34
- else
35
- cl.latest
36
- end
73
+ parsed = if (options[:markdown] || !$stdout.isatty) && !options[:render]
74
+ content
75
+ else
76
+ TTY::Markdown.parse(content, width: 80, theme: MARKDOWN_THEME, symbols: { override: { bullet: '•' } })
77
+ end
37
78
 
38
- parsed = TTY::Markdown.parse(content, width: 80, symbols: {override: {bullet: "•"}})
39
- Doing::Pager.paginate = true
40
- Doing::Pager.page parsed
79
+ Doing::Pager.paginate = true
80
+ Doing::Pager.page parsed
81
+ end
41
82
  end
42
83
  end
@@ -5,10 +5,12 @@ command :colors do |c|
5
5
  bgs = []
6
6
  fgs = []
7
7
  @colors::attributes.each do |color|
8
+ colname = color.to_s
9
+ colname << " (#{color.to_s.sub(/bold/, 'bright')})" if colname =~ /bold/
8
10
  if color.to_s =~ /bg/
9
- bgs.push("#{@colors.send(color, " ")}#{@colors.default} <-- #{color.to_s}")
11
+ bgs.push("#{@colors.send(color, " ")}#{@colors.default} <-- #{colname}")
10
12
  else
11
- fgs.push("#{@colors.send(color, "XXXX")}#{@colors.default} <-- #{color.to_s}")
13
+ fgs.push("#{@colors.send(color, "XXXX")}#{@colors.default} <-- #{colname}")
12
14
  end
13
15
  end
14
16
  out = []
@@ -27,9 +27,11 @@ command :commands do |c|
27
27
  Doing::Prompt.choose_from(available,
28
28
  prompt: 'Select commands to enable',
29
29
  multiple: true,
30
- sorted: true).strip.split("\n")
30
+ sorted: true)
31
31
  end
32
- to_enable.each do |cmd|
32
+ raise UserCancelled unless to_enable
33
+
34
+ to_enable.strip.split("\n").each do |cmd|
33
35
  default_command = File.join(File.dirname(__FILE__), "#{cmd}.rb")
34
36
  custom_command = File.join(File.expand_path(custom_dir), "#{cmd}.rb")
35
37
  unless File.exist?(default_command) || File.exist?(custom_command)
@@ -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
@@ -45,7 +45,7 @@ command :config do |c|
45
45
  edit.arg_name 'EDITOR'
46
46
  edit.flag %i[e editor], default_value: nil
47
47
 
48
- if `uname` =~ /Darwin/
48
+ if Sys::Platform.mac?
49
49
  edit.desc 'Application to use'
50
50
  edit.arg_name 'APP_NAME'
51
51
  edit.flag %i[a app]
@@ -76,7 +76,7 @@ command :config do |c|
76
76
 
77
77
  config_file = @config.choose_config
78
78
 
79
- if `uname` =~ /Darwin/
79
+ if Sys::Platform.mac?
80
80
  if options[:default]
81
81
  editor = Doing::Util.find_default_editor('config')
82
82
  if editor
@@ -139,7 +139,7 @@ command :config do |c|
139
139
  dump.example 'doing config get', desc: 'Output the entire configuration'
140
140
  dump.example 'doing config get timer_format --output raw', desc: 'Output the value of timer_format as a plain string'
141
141
  dump.example 'doing config get doing_file', desc: 'Output the value of the doing_file setting, respecting local configurations'
142
- dump.example 'doing config get -o json plug.plugpath', desc: 'Key path is fuzzy matched: output the value of plugins->plugin_path as JSON'
142
+ dump.example 'doing config get -o json plug.plugpath', desc: 'Key path is fuzzy matched: output the value of plugins.plugin_path as JSON'
143
143
 
144
144
  dump.desc 'Format for output (json|yaml|raw)'
145
145
  dump.arg_name 'FORMAT'
@@ -188,7 +188,7 @@ command :config do |c|
188
188
  c.arg 'KEY VALUE'
189
189
  c.command :set do |set|
190
190
  set.example 'doing config set timer_format human', desc: 'Set the value of timer_format to "human"'
191
- set.example 'doing config set plug.plugpath ~/my_plugins', desc: 'Key path is fuzzy matched: set the value of plugins->plugin_path'
191
+ set.example 'doing config set plug.plugpath ~/my_plugins', desc: 'Key path is fuzzy matched: set the value of plugins.plugin_path'
192
192
 
193
193
  set.desc 'Delete specified key'
194
194
  set.switch %i[r remove], default_value: false, negatable: false
@@ -206,7 +206,7 @@ command :config do |c|
206
206
  old_type = old_value&.class.to_s || nil
207
207
 
208
208
  if old_value.is_a?(Hash) && !options[:remove]
209
- Doing.logger.log_now(:warn, 'Config:', "Config key must point to a single value, #{real_path.join('->').boldwhite} is a mapping")
209
+ Doing.logger.log_now(:warn, 'Config:', "Config key must point to a single value, #{real_path.join('.').boldwhite} is a mapping")
210
210
  didyou = 'Did you mean:'
211
211
  old_value.keys.each do |k|
212
212
  Doing.logger.log_now(:warn, "#{didyou}", "#{keypath}.#{k}?")
@@ -218,17 +218,17 @@ 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
 
225
225
  if options[:remove]
226
226
  cfg.deep_set(real_path, nil)
227
- $stderr.puts "#{'Deleting key:'.yellow} #{real_path.join('->').boldwhite}"
227
+ $stderr.puts "#{'Deleting key:'.yellow} #{real_path.join('.').boldwhite}"
228
228
  else
229
229
  current_value = cfg.dig(*real_path)
230
230
  cfg.deep_set(real_path, value.set_type(old_type))
231
- $stderr.puts "#{' Key path:'.yellow} #{real_path.join('->').boldwhite}"
231
+ $stderr.puts "#{' Key path:'.yellow} #{real_path.join('.').boldwhite}"
232
232
  $stderr.puts "#{'Inherited:'.yellow} #{(old_value ? old_value.to_s : 'empty').boldwhite}"
233
233
  $stderr.puts "#{' Current:'.yellow} #{ (current_value ? current_value.to_s : 'empty').boldwhite }"
234
234
  $stderr.puts "#{' New:'.yellow} #{value.set_type(old_type).to_s.boldwhite}"
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)