doing 2.1.39 → 2.1.42

Sign up to get free protection for your applications and to get access to all the features.
Files changed (229) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -1
  3. data/CHANGELOG.md +67 -0
  4. data/Gemfile.lock +1 -1
  5. data/README.md +1 -1
  6. data/Rakefile +4 -4
  7. data/bin/commands/again.rb +1 -3
  8. data/bin/commands/changes.rb +50 -34
  9. data/bin/commands/commands.rb +77 -52
  10. data/bin/commands/commands_accepting.rb +57 -53
  11. data/bin/commands/config.rb +45 -36
  12. data/bin/commands/done.rb +1 -18
  13. data/bin/commands/finish.rb +90 -59
  14. data/bin/commands/flag.rb +5 -1
  15. data/bin/commands/grep.rb +3 -14
  16. data/bin/commands/last.rb +2 -8
  17. data/bin/commands/meanwhile.rb +13 -6
  18. data/bin/commands/now.rb +151 -107
  19. data/bin/commands/on.rb +8 -18
  20. data/bin/commands/recent.rb +2 -8
  21. data/bin/commands/reset.rb +24 -1
  22. data/bin/commands/select.rb +1 -1
  23. data/bin/commands/show.rb +6 -17
  24. data/bin/commands/since.rb +1 -12
  25. data/bin/commands/tag_dir.rb +49 -15
  26. data/bin/commands/today.rb +2 -13
  27. data/bin/commands/undo.rb +4 -6
  28. data/bin/commands/view.rb +1 -1
  29. data/bin/commands/yesterday.rb +2 -13
  30. data/bin/doing +15 -8
  31. data/{Dockerfile → docker/Dockerfile} +3 -1
  32. data/{Dockerfile-2.6 → docker/Dockerfile-2.6} +2 -2
  33. data/{Dockerfile-2.7 → docker/Dockerfile-2.7} +2 -2
  34. data/{Dockerfile-3.0 → docker/Dockerfile-3.0} +2 -2
  35. data/{bash_profile → docker/bash_profile} +0 -0
  36. data/{inputrc → docker/inputrc} +0 -0
  37. data/docs/doc/Array.html +85 -2
  38. data/docs/doc/BooleanTermParser/Clause.html +1 -1
  39. data/docs/doc/BooleanTermParser/Operator.html +1 -1
  40. data/docs/doc/BooleanTermParser/Query.html +1 -1
  41. data/docs/doc/BooleanTermParser/QueryParser.html +1 -1
  42. data/docs/doc/BooleanTermParser/QueryTransformer.html +1 -1
  43. data/docs/doc/BooleanTermParser.html +1 -1
  44. data/docs/doc/Doing/ArrayNestedHash.html +198 -0
  45. data/docs/doc/Doing/ArrayTags.html +424 -0
  46. data/docs/doc/Doing/CSVExport.html +266 -0
  47. data/docs/doc/Doing/CalendarImport.html +232 -0
  48. data/docs/doc/Doing/Change.html +617 -0
  49. data/docs/doc/Doing/Changes.html +468 -0
  50. data/docs/doc/Doing/ChronifyArray.html +347 -0
  51. data/docs/doc/Doing/ChronifyNumeric.html +271 -0
  52. data/docs/doc/Doing/ChronifyString.html +682 -0
  53. data/docs/doc/Doing/Color.html +167 -21
  54. data/docs/doc/Doing/Completion/BashCompletions.html +445 -0
  55. data/docs/doc/Doing/Completion/FishCompletions.html +445 -0
  56. data/docs/doc/Doing/Completion/StringUtils.html +229 -0
  57. data/docs/doc/Doing/Completion/ZshCompletions.html +445 -0
  58. data/docs/doc/Doing/Completion.html +17 -3
  59. data/docs/doc/Doing/Configuration.html +3 -2
  60. data/docs/doc/Doing/DayOneRenderer.html +383 -0
  61. data/docs/doc/Doing/DayoneExport.html +290 -0
  62. data/docs/doc/Doing/DoingImport.html +391 -0
  63. data/docs/doc/Doing/Entry.html +381 -0
  64. data/docs/doc/Doing/Errors/DoingNoTraceError.html +7 -3
  65. data/docs/doc/Doing/Errors/DoingRuntimeError.html +7 -3
  66. data/docs/doc/Doing/Errors/DoingStandardError.html +1 -1
  67. data/docs/doc/Doing/Errors/EmptyInput.html +10 -2
  68. data/docs/doc/Doing/Errors/HistoryLimitError.html +194 -0
  69. data/docs/doc/Doing/Errors/InvalidPlugin.html +194 -0
  70. data/docs/doc/Doing/Errors/MissingBackupFile.html +194 -0
  71. data/docs/doc/Doing/Errors/NoResults.html +10 -2
  72. data/docs/doc/Doing/Errors/PluginException.html +1 -1
  73. data/docs/doc/Doing/Errors/UserCancelled.html +10 -2
  74. data/docs/doc/Doing/Errors/WrongCommand.html +10 -2
  75. data/docs/doc/Doing/Errors.html +9 -9
  76. data/docs/doc/Doing/HTMLExport.html +256 -0
  77. data/docs/doc/Doing/Hooks.html +1 -1
  78. data/docs/doc/Doing/Item.html +179 -1660
  79. data/docs/doc/Doing/ItemDates.html +564 -0
  80. data/docs/doc/Doing/ItemQuery.html +614 -0
  81. data/docs/doc/Doing/ItemState.html +387 -0
  82. data/docs/doc/Doing/ItemTags.html +498 -0
  83. data/docs/doc/Doing/Items.html +581 -15
  84. data/docs/doc/Doing/JSONExport.html +222 -0
  85. data/docs/doc/Doing/Logger.html +1 -1
  86. data/docs/doc/Doing/MarkdownExport.html +266 -0
  87. data/docs/doc/Doing/MarkdownRenderer.html +383 -0
  88. data/docs/doc/Doing/Note.html +18 -4
  89. data/docs/doc/Doing/Pager.html +1 -1
  90. data/docs/doc/Doing/Plugins.html +181 -76
  91. data/docs/doc/Doing/Prompt.html +32 -683
  92. data/docs/doc/Doing/PromptChoose.html +484 -0
  93. data/docs/doc/Doing/PromptFZF.html +391 -0
  94. data/docs/doc/Doing/PromptInput.html +572 -0
  95. data/docs/doc/Doing/PromptSTD.html +293 -0
  96. data/docs/doc/Doing/PromptYN.html +237 -0
  97. data/docs/doc/Doing/Section.html +58 -2
  98. data/docs/doc/Doing/StringHighlight.html +533 -0
  99. data/docs/doc/Doing/StringNormalize.html +929 -0
  100. data/docs/doc/Doing/StringQuery.html +725 -0
  101. data/docs/doc/Doing/StringTags.html +884 -0
  102. data/docs/doc/Doing/StringTransform.html +599 -0
  103. data/docs/doc/Doing/StringTruncate.html +448 -0
  104. data/docs/doc/Doing/StringURL.html +409 -0
  105. data/docs/doc/Doing/SymbolNormalize.html +341 -0
  106. data/docs/doc/Doing/TaskPaperExport.html +222 -0
  107. data/docs/doc/Doing/TemplateExport.html +249 -0
  108. data/docs/doc/Doing/TemplateString.html +102 -3
  109. data/docs/doc/Doing/TimingImport.html +285 -0
  110. data/docs/doc/Doing/Types.html +1 -1
  111. data/docs/doc/Doing/Util/Backup.html +11 -163
  112. data/docs/doc/Doing/Util.html +67 -10
  113. data/docs/doc/Doing/Version.html +523 -0
  114. data/docs/doc/Doing/WWID/WWIDUtil.html +510 -0
  115. data/docs/doc/Doing/WWID.html +476 -139
  116. data/docs/doc/Doing/WWIDDisplay.html +865 -0
  117. data/docs/doc/Doing/WWIDEditor.html +466 -0
  118. data/docs/doc/Doing/WWIDFileTools.html +359 -0
  119. data/docs/doc/Doing/WWIDFilter.html +466 -0
  120. data/docs/doc/Doing/WWIDGuess.html +299 -0
  121. data/docs/doc/Doing/WWIDInteractive.html +752 -0
  122. data/docs/doc/Doing/WWIDModify.html +1078 -0
  123. data/docs/doc/Doing/WWIDTags.html +302 -0
  124. data/docs/doc/Doing/WWIDTimers.html +359 -0
  125. data/docs/doc/Doing/WWIDUtil.html +510 -0
  126. data/docs/doc/Doing.html +9 -6
  127. data/docs/doc/FalseClass.html +1 -1
  128. data/docs/doc/GLI/Commands/Help.html +1 -1
  129. data/docs/doc/GLI/Commands/MarkdownDocumentListener.html +1 -1
  130. data/docs/doc/GLI/Commands.html +1 -1
  131. data/docs/doc/GLI.html +1 -1
  132. data/docs/doc/Hash.html +1 -1
  133. data/docs/doc/Numeric.html +23 -78
  134. data/docs/doc/Object.html +1 -1
  135. data/docs/doc/PhraseParser/Operator.html +1 -1
  136. data/docs/doc/PhraseParser/PhraseClause.html +1 -1
  137. data/docs/doc/PhraseParser/Query.html +1 -1
  138. data/docs/doc/PhraseParser/QueryParser.html +1 -1
  139. data/docs/doc/PhraseParser/QueryTransformer.html +1 -1
  140. data/docs/doc/PhraseParser/TermClause.html +1 -1
  141. data/docs/doc/PhraseParser.html +1 -1
  142. data/docs/doc/Status.html +1 -1
  143. data/docs/doc/String.html +58 -633
  144. data/docs/doc/Symbol.html +9 -224
  145. data/docs/doc/Time.html +119 -13
  146. data/docs/doc/TrueClass.html +1 -1
  147. data/docs/doc/_index.html +348 -4
  148. data/docs/doc/class_list.html +1 -1
  149. data/docs/doc/file.README.html +2 -2
  150. data/docs/doc/index.html +2 -2
  151. data/docs/doc/method_list.html +1904 -592
  152. data/docs/doc/top-level-namespace.html +12 -4
  153. data/docs/index.md +1 -1
  154. data/doing.rdoc +67 -15
  155. data/lib/completion/_doing.zsh +6 -6
  156. data/lib/completion/doing.bash +10 -10
  157. data/lib/completion/doing.fish +10 -3
  158. data/lib/doing/add_options.rb +39 -1
  159. data/lib/doing/array/array.rb +18 -12
  160. data/lib/doing/array/cleanup.rb +31 -0
  161. data/lib/doing/array/nested_hash.rb +1 -1
  162. data/lib/doing/array/tags.rb +6 -5
  163. data/lib/doing/changelog/changelog.rb +6 -0
  164. data/lib/doing/chronify/array.rb +65 -25
  165. data/lib/doing/chronify/chronify.rb +12 -0
  166. data/lib/doing/chronify/numeric.rb +3 -2
  167. data/lib/doing/chronify/string.rb +1 -1
  168. data/lib/doing/colors.rb +77 -30
  169. data/lib/doing/completion/completion_string.rb +25 -0
  170. data/lib/doing/completion.rb +4 -5
  171. data/lib/doing/configuration.rb +7 -3
  172. data/lib/doing/errors.rb +51 -35
  173. data/lib/doing/good.rb +8 -0
  174. data/lib/doing/hooks.rb +3 -3
  175. data/lib/doing/item/dates.rb +112 -0
  176. data/lib/doing/item/item.rb +128 -0
  177. data/lib/doing/{item.rb → item/query.rb} +2 -353
  178. data/lib/doing/item/state.rb +59 -0
  179. data/lib/doing/item/tags.rb +87 -0
  180. data/lib/doing/items/filter.rb +67 -0
  181. data/lib/doing/items/items.rb +57 -0
  182. data/lib/doing/items/modify.rb +36 -0
  183. data/lib/doing/items/sections.rb +83 -0
  184. data/lib/doing/items/util.rb +74 -0
  185. data/lib/doing/normalize.rb +10 -2
  186. data/lib/doing/note.rb +1 -1
  187. data/lib/doing/pager.rb +9 -3
  188. data/lib/doing/plugin_manager.rb +33 -8
  189. data/lib/doing/plugins/export/markdown_export.rb +4 -2
  190. data/lib/doing/plugins/export/template_export.rb +4 -4
  191. data/lib/doing/plugins/import/cal_to_json.scpt +0 -0
  192. data/lib/doing/plugins/import/doing_import.rb +1 -1
  193. data/lib/doing/prompt/choose.rb +118 -0
  194. data/lib/doing/prompt/fzf.rb +84 -0
  195. data/lib/doing/prompt/input.rb +129 -0
  196. data/lib/doing/prompt/prompt.rb +41 -0
  197. data/lib/doing/prompt/std.rb +32 -0
  198. data/lib/doing/prompt/yn.rb +64 -0
  199. data/lib/doing/section.rb +4 -0
  200. data/lib/doing/string/highlight.rb +1 -1
  201. data/lib/doing/string/query.rb +1 -1
  202. data/lib/doing/string/string.rb +18 -7
  203. data/lib/doing/string/tags.rb +14 -3
  204. data/lib/doing/string/transform.rb +7 -1
  205. data/lib/doing/string/truncate.rb +1 -1
  206. data/lib/doing/string/url.rb +1 -1
  207. data/lib/doing/time.rb +19 -1
  208. data/lib/doing/util.rb +12 -6
  209. data/lib/doing/util_backup.rb +62 -57
  210. data/lib/doing/version.rb +1 -1
  211. data/lib/doing/wwid/display.rb +396 -0
  212. data/lib/doing/wwid/editor.rb +214 -0
  213. data/lib/doing/wwid/filetools.rb +183 -0
  214. data/lib/doing/wwid/filter.rb +226 -0
  215. data/lib/doing/wwid/guess.rb +85 -0
  216. data/lib/doing/wwid/interactive.rb +377 -0
  217. data/lib/doing/wwid/modify.rb +617 -0
  218. data/lib/doing/wwid/tags.rb +51 -0
  219. data/lib/doing/wwid/timers.rb +342 -0
  220. data/lib/doing/wwid/wwid.rb +121 -0
  221. data/lib/doing/wwid/wwidutil.rb +101 -0
  222. data/lib/doing.rb +7 -7
  223. data/lib/helpers/threaded_tests.rb +1 -0
  224. metadata +94 -14
  225. data/lib/doing/changelog.rb +0 -6
  226. data/lib/doing/completion/string.rb +0 -17
  227. data/lib/doing/items.rb +0 -196
  228. data/lib/doing/prompt.rb +0 -330
  229. data/lib/doing/wwid.rb +0 -2398
@@ -29,7 +29,7 @@ command :config do |c|
29
29
  c.long_desc 'Config files are listed in order of precedence (if there are multiple configs detected).
30
30
  Values defined in the top item in the list will override values in configutations below it.'
31
31
  c.command :list do |list|
32
- list.action do |global, options, args|
32
+ list.action do
33
33
  puts Doing.config.additional_configs.join("\n")
34
34
  puts Doing.config.config_file
35
35
  end
@@ -54,7 +54,8 @@ command :config do |c|
54
54
  edit.arg_name 'BUNDLE_ID'
55
55
  edit.flag %i[b bundle_id]
56
56
 
57
- edit.desc "Use the config editor defined in ~/.config/doing/config.yml (#{Doing.setting('editors.config', 'editors.config not set')})"
57
+ default_editor = Doing.setting('editors.config', 'editors.config not set')
58
+ edit.desc "Use the config editor defined in ~/.config/doing/config.yml (#{default_editor})"
58
59
  edit.switch %i[x default], negatable: false
59
60
  end
60
61
 
@@ -68,10 +69,11 @@ command :config do |c|
68
69
  end
69
70
  action = cmd.send(:get_action, nil)
70
71
  action.call(global, options, args)
71
- Doing.logger.warn('Deprecated:', '--dump and --update are deprecated,
72
- use `doing config get` and `doing config update`')
73
- Doing.logger.output_results
74
- return
72
+
73
+ raise DoingNoTraceError.new('--dump and --update are deprecated,
74
+ use `doing config get` and `doing config update`',
75
+ level: :warn,
76
+ topic: 'Deprecated:')
75
77
  end
76
78
 
77
79
  config_file = Doing.config.choose_config
@@ -79,14 +81,12 @@ command :config do |c|
79
81
  if `uname` =~ /Darwin/
80
82
  if options[:default]
81
83
  editor = Doing::Util.find_default_editor('config')
82
- if editor
83
- if Doing::Util.exec_available(editor.split(/ /).first)
84
- system %(#{editor} "#{config_file}")
85
- else
86
- `open -a "#{editor}" "#{config_file}"`
87
- end
84
+ raise InvalidArgument, 'No viable editor found in config or environment.' unless editor
85
+
86
+ if Doing::Util.exec_available(editor.split(/ /).first)
87
+ system %(#{editor} "#{config_file}")
88
88
  else
89
- raise InvalidArgument, 'No viable editor found in config or environment.'
89
+ `open -a "#{editor}" "#{config_file}"`
90
90
  end
91
91
  elsif options[:app] || options[:bundle_id]
92
92
  if options[:app]
@@ -107,7 +107,10 @@ command :config do |c|
107
107
  end
108
108
  else
109
109
  editor = options[:editor] || Doing::Util.default_editor
110
- raise MissingEditor, 'No EDITOR variable defined in environment' unless editor && Doing::Util.exec_available(editor.split(/ /).first)
110
+ unless editor && Doing::Util.exec_available(editor.split(/ /).first)
111
+ raise MissingEditor, 'No EDITOR variable defined in environment'
112
+
113
+ end
111
114
 
112
115
  system %(#{editor} "#{config_file}")
113
116
  end
@@ -117,8 +120,8 @@ command :config do |c|
117
120
  # @@config.update @@config.refresh
118
121
  c.desc 'Update default config file, adding any missing keys'
119
122
  c.command %i[update refresh] do |update|
120
- update.action do |_global, options, args|
121
- Doing.config.configure({rewrite: true, ignore_local: true})
123
+ update.action do
124
+ Doing.config.configure({ rewrite: true, ignore_local: true })
122
125
  Doing.logger.warn('Config:', 'config refreshed')
123
126
  end
124
127
  end
@@ -126,7 +129,7 @@ command :config do |c|
126
129
  # @@config.undo
127
130
  c.desc 'Undo the last change to a config file'
128
131
  c.command :undo do |undo|
129
- undo.action do |_global, options, args|
132
+ undo.action do
130
133
  config_file = Doing.config.choose_config
131
134
  Doing::Util::Backup.restore_last_backup(config_file, count: 1)
132
135
  end
@@ -137,22 +140,24 @@ command :config do |c|
137
140
  c.arg 'KEY_PATH'
138
141
  c.command %i[get dump] do |dump|
139
142
  dump.example 'doing config get', desc: 'Output the entire configuration'
140
- dump.example 'doing config get timer_format --output raw', desc: 'Output the value of timer_format as a plain string'
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'
143
+ dump.example 'doing config get timer_format --output raw',
144
+ desc: 'Output the value of timer_format as a plain string'
145
+ dump.example 'doing config get doing_file',
146
+ desc: 'Output the value of the doing_file setting, respecting local configurations'
147
+ dump.example 'doing config get -o json plug.plugpath',
148
+ desc: 'Key path is fuzzy matched: output the value of plugins.plugin_path as JSON'
143
149
 
144
150
  dump.desc 'Format for output (json|yaml|raw)'
145
151
  dump.arg_name 'FORMAT'
146
152
  dump.flag %i[o output], default_value: 'yaml', must_match: /^(?:y(?:aml)?|j(?:son)?|r(?:aw)?)$/
147
153
 
148
154
  dump.action do |_global, options, args|
149
-
150
155
  keypath = args.join('.')
151
156
  cfg = Doing.config.value_for_key(keypath)
152
157
  real_path = Doing.config.resolve_key_path(keypath)
153
158
 
154
159
  if cfg
155
- val = cfg.map {|k, v| v }[0]
160
+ val = cfg.map { |_, v| v }[0]
156
161
  if real_path.count.positive?
157
162
  nested_cfg = {}
158
163
  nested_cfg.deep_set(real_path, val)
@@ -161,13 +166,15 @@ command :config do |c|
161
166
  end
162
167
 
163
168
  if options[:output] =~ /^r/
164
- if val.is_a?(Hash)
165
- $stdout.puts YAML.dump(val)
166
- elsif val.is_a?(Array)
167
- $stdout.puts val.join(', ')
168
- else
169
- $stdout.puts val.to_s
170
- end
169
+
170
+ $stdout.puts case val
171
+ when Hash
172
+ YAML.dump(val)
173
+ when Array
174
+ val.join(', ')
175
+ else
176
+ val.to_s
177
+ end
171
178
  else
172
179
  $stdout.puts case options[:output]
173
180
  when /^j/
@@ -188,13 +195,14 @@ command :config do |c|
188
195
  c.arg 'KEY VALUE'
189
196
  c.command :set do |set|
190
197
  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'
198
+ set.example 'doing config set plug.plugpath ~/my_plugins',
199
+ desc: 'Key path is fuzzy matched: set the value of plugins.plugin_path'
192
200
 
193
201
  set.desc 'Delete specified key'
194
202
  set.switch %i[r remove], negatable: false
195
203
 
196
204
  set.desc 'Force update to .doingrc in the current directory'
197
- set.switch %[local], negatable: false
205
+ set.switch %i[local], negatable: false
198
206
 
199
207
  set.action do |_global, options, args|
200
208
  if args.count < 2 && !options[:remove]
@@ -206,13 +214,14 @@ command :config do |c|
206
214
  keypath = args.join('.')
207
215
  real_path = Doing.config.resolve_key_path(keypath, create: true)
208
216
  old_value = Doing.config.settings.dig(*real_path)
209
- old_type = old_value&.class.to_s || nil
217
+ old_type = old_value.good? ? old_value&.class.to_s : nil
210
218
 
211
219
  if old_value.is_a?(Hash) && !options[:remove]
212
- Doing.logger.log_now(:warn, 'Config:', "Config key must point to a single value, #{real_path.join('.').boldwhite} is a mapping")
220
+ Doing.logger.log_now(:warn, 'Config:', ['Config key must point to a single value, ',
221
+ "#{real_path.join('.').boldwhite} is a mapping"].join(' '))
213
222
  didyou = 'Did you mean:'
214
- old_value.keys.each do |k|
215
- Doing.logger.log_now(:warn, "#{didyou}", "#{keypath}.#{k}?")
223
+ old_value.each_key do |k|
224
+ Doing.logger.log_now(:warn, didyou, "#{keypath}.#{k}?")
216
225
  didyou = '..........or:'
217
226
  end
218
227
  raise InvalidArgument, 'Config value is a mapping, can not be set to a single value'
@@ -226,8 +235,8 @@ command :config do |c|
226
235
  $stderr.puts "> Config: Updating #{config_file}".yellow
227
236
 
228
237
  if options[:remove]
229
- cfg.deep_set(real_path, nil)
230
238
  $stderr.puts "#{'Deleting key:'.yellow} #{real_path.join('.').boldwhite}"
239
+ cfg.deep_set(real_path, nil)
231
240
  else
232
241
  current_value = cfg.dig(*real_path)
233
242
  cfg.deep_set(real_path, value.set_type(old_type))
data/bin/commands/done.rb CHANGED
@@ -19,24 +19,6 @@ command %i[done did] do |c|
19
19
  c.desc 'Immediately archive the entry'
20
20
  c.switch %i[a archive], negatable: false, default_value: false
21
21
 
22
- c.desc %(Set finish date to specific date/time (natural langauge parsed, e.g. --at=1:30pm).
23
- Used with --took, backdates start date)
24
- c.arg_name 'DATE_STRING'
25
- c.flag %i[at finished], type: DateEndString
26
-
27
- c.desc %(
28
- Start and end times as a date/time range `doing done --from "1am to 8am"`.
29
- Overrides other date flags.
30
- )
31
- c.arg_name 'TIME_RANGE'
32
- c.flag [:from], must_match: REGEX_RANGE
33
-
34
- c.desc %(Set completion date to start date plus interval (XX[mhd] or HH:MM).
35
- If used without the --back option, the start date will be moved back to allow
36
- the completion date to be the current time.)
37
- c.arg_name 'INTERVAL'
38
- c.flag %i[t took for], type: DateIntervalString
39
-
40
22
  c.desc 'Section'
41
23
  c.arg_name 'NAME'
42
24
  c.flag %i[s section]
@@ -45,6 +27,7 @@ command %i[done did] do |c|
45
27
  c.switch %i[u unfinished], negatable: false, default_value: false
46
28
 
47
29
  add_options(:add_entry, c)
30
+ add_options(:finish_entry, c)
48
31
 
49
32
  c.action do |global_options, options, args|
50
33
  Doing.auto_tag = !options[:noauto]
@@ -1,97 +1,126 @@
1
- # @@finish
2
- desc 'Mark last X entries as @done'
3
- long_desc 'Marks the last X entries with a @done tag and current date. Does not alter already completed entries.'
4
- arg_name 'COUNT', optional: true
5
- command :finish do |c|
6
- c.example 'doing finish', desc: 'Mark the last entry @done'
7
- c.example 'doing finish --auto --section Later 10', desc: 'Add @done to any unfinished entries in the last 10 in Later, setting the finish time based on the start time of the task after it'
8
- c.example 'doing finish --search "a specific entry" --at "yesterday 3pm"', desc: 'Search for an entry containing string and set its @done time to yesterday at 3pm'
1
+ # frozen_string_literal: true
9
2
 
10
- c.desc 'Include date'
11
- c.switch [:date], negatable: true, default_value: true
3
+ # @@finish
4
+ module Doing
5
+ # finish command methods
6
+ class FinishCommand
7
+ def initialize(wwid)
8
+ @wwid = wwid
9
+ end
12
10
 
13
- c.desc 'Backdate completed date to date string [4pm|20m|2h|yesterday noon]'
14
- c.arg_name 'DATE_STRING'
15
- c.flag %i[b back started], type: DateBeginString
11
+ def add_examples(cmd)
12
+ cmd.example 'doing finish', desc: 'Mark the last entry @done'
13
+ cmd.example 'doing finish --auto --section Later 10', desc: 'Add @done to any unfinished entries in the last 10 in Later, setting the finish time based on the start time of the task after it'
14
+ cmd.example 'doing finish --search "a specific entry" --at "yesterday 3pm"', desc: 'Search for an entry containing string and set its @done time to yesterday at 3pm'
15
+ end
16
16
 
17
- c.desc 'Set the completed date to the start date plus XX[hmd]'
18
- c.arg_name 'INTERVAL'
19
- c.flag %i[t took for], type: DateIntervalString
17
+ def add_options(cmd)
18
+ cmd.desc 'Include date'
19
+ cmd.switch [:date], negatable: true, default_value: true
20
20
 
21
- c.desc %(Set finish date to specific date/time (natural langauge parsed, e.g. --at=1:30pm). If used, ignores --back.)
22
- c.arg_name 'DATE_STRING'
23
- c.flag %i[at finished], type: DateEndString
21
+ cmd.desc 'Backdate completed date to date string [4pm|20m|2h|yesterday noon]'
22
+ cmd.arg_name 'DATE_STRING'
23
+ cmd.flag %i[b back started], type: DateBeginString
24
24
 
25
- c.desc 'Overwrite existing @done tag with new date'
26
- c.switch %i[update], negatable: false, default_value: false
25
+ cmd.desc 'Overwrite existing @done tag with new date'
26
+ cmd.switch %i[update], negatable: false, default_value: false
27
27
 
28
- c.desc 'Remove @done tag'
29
- c.switch %i[r remove], negatable: false, default_value: false
28
+ cmd.desc 'Remove @done tag'
29
+ cmd.switch %i[r remove], negatable: false, default_value: false
30
30
 
31
- c.desc 'Finish last entry (or entries) not already marked @done'
32
- c.switch %i[u unfinished], negatable: false, default_value: false
31
+ cmd.desc 'Finish last entry (or entries) not already marked @done'
32
+ cmd.switch %i[u unfinished], negatable: false, default_value: false
33
33
 
34
- c.desc %(Auto-generate finish dates from next entry's start time.
35
- Automatically generate completion dates 1 minute before next item (in any section) began.
36
- --auto overrides the --date and --back parameters.)
37
- c.switch [:auto], negatable: false, default_value: false
34
+ cmd.desc %(Auto-generate finish dates from next entry's start time.
35
+ Automatically generate completion dates 1 minute before next item (in any section) began.
36
+ --auto overrides the --date and --back parameters.)
37
+ cmd.switch [:auto], negatable: false, default_value: false
38
38
 
39
- c.desc 'Archive entries'
40
- c.switch %i[a archive], negatable: false, default_value: false
39
+ cmd.desc 'Archive entries'
40
+ cmd.switch %i[a archive], negatable: false, default_value: false
41
41
 
42
- c.desc 'Section'
43
- c.arg_name 'NAME'
44
- c.flag %i[s section]
42
+ cmd.desc 'Section'
43
+ cmd.arg_name 'NAME'
44
+ cmd.flag %i[s section]
45
45
 
46
- c.desc 'Select item(s) to finish from a menu of matching entries'
47
- c.switch %i[i interactive], negatable: false, default_value: false
46
+ cmd.desc 'Select item(s) to finish from a menu of matching entries'
47
+ cmd.switch %i[i interactive], negatable: false, default_value: false
48
+ end
48
49
 
49
- add_options(:search, c)
50
- add_options(:tag_filter, c)
50
+ def handle_from(options)
51
+ options[:from] = options[:from].split(/#{REGEX_RANGE_INDICATOR}/).map do |time|
52
+ time =~ REGEX_TIME ? "today #{time.sub(/(?mi)(^.*?(?=\d+)|(?<=[ap]m).*?$)/, '')}" : time
53
+ end.join(' to ').split_date_range
54
+ start_date, finish_date = options[:from]
55
+ finish_date ||= Time.now
56
+ [start_date, finish_date]
57
+ end
51
58
 
52
- c.action do |_global_options, options, args|
53
- options[:fuzzy] = false
54
- unless options[:auto]
59
+ def handle_date_options(options)
55
60
  if options[:took]
56
61
  took = options[:took]
57
62
  raise InvalidTimeExpression, 'Unable to parse date string for --took' if took.nil?
58
- end
59
63
 
60
- raise InvalidArgument, '--back and --took can not be used together' if options[:back] && options[:took]
61
-
62
- raise InvalidArgument, '--search and --tag can not be used together' if options[:search] && options[:tag]
64
+ end
63
65
 
64
66
  if options[:at]
65
67
  finish_date = options[:at]
66
68
  finish_date = finish_date.chronify(guess: :begin) if finish_date.is_a? String
67
69
  raise InvalidTimeExpression, 'Unable to parse date string for --at' if finish_date.nil?
68
70
 
69
- date = options[:took] ? finish_date - took : finish_date
71
+ start_date = options[:took] ? finish_date - took : nil
70
72
  elsif options[:back]
71
- date = options[:back]
73
+ start_date = options[:back]
74
+ finish_date = options[:took] ? start_date + took : Time.now
75
+
76
+ raise InvalidTimeExpression, 'Unable to parse date string' if start_date.nil?
72
77
 
73
- raise InvalidTimeExpression, 'Unable to parse date string' if date.nil?
74
78
  else
75
- date = Time.now
79
+ start_date = options[:took] ? Time.now - took : nil
80
+ finish_date = Time.now
76
81
  end
82
+
83
+ [start_date, finish_date]
77
84
  end
85
+ end
86
+ end
87
+
88
+ desc 'Mark last X entries as @done'
89
+ long_desc 'Marks the last X entries with a @done tag and current date. Does not alter already completed entries.'
90
+ arg_name 'COUNT', optional: true
91
+ command :finish do |c|
92
+ cmd = Doing::FinishCommand.new(@wwid)
93
+ cmd.add_examples(c)
94
+ cmd.add_options(c)
95
+ add_options(:search, c)
96
+ add_options(:tag_filter, c)
97
+ add_options(:finish_entry, c)
78
98
 
79
- if options[:tag].nil?
80
- tags = []
81
- else
82
- tags = options[:tag]
99
+ c.action do |_global_options, options, args|
100
+ options[:fuzzy] = false
101
+ unless options[:auto]
102
+ if options[:from]
103
+ start_date, finish_date = cmd.handle_from(options)
104
+ else
105
+ start_date, finish_date = cmd.handle_date_options(options)
106
+ end
83
107
  end
84
108
 
109
+ tags = options[:tag] || []
110
+
85
111
  raise InvalidArgument, 'Only one argument allowed' if args.length > 1
86
112
 
87
- raise InvalidArgument, 'Invalid argument (specify number of recent items to mark @done)' unless args.length == 0 || args[0] =~ /\d+/
113
+ unless args.empty? || args[0] =~ /\d+/
114
+ raise InvalidArgument, 'Invalid argument (specify number of recent items to mark @done)'
88
115
 
89
- if options[:interactive]
90
- count = 0
91
- else
92
- count = args[0] ? args[0].to_i : 1
93
116
  end
94
117
 
118
+ count = if options[:interactive]
119
+ 0
120
+ else
121
+ args[0] ? args[0].to_i : 1
122
+ end
123
+
95
124
  search = nil
96
125
 
97
126
  if options[:search]
@@ -101,10 +130,11 @@ command :finish do |c|
101
130
 
102
131
  opts = {
103
132
  archive: options[:archive],
104
- back: date,
133
+ back: start_date,
105
134
  case: options[:case].normalize_case,
106
135
  count: count,
107
136
  date: options[:date],
137
+ done_date: finish_date,
108
138
  fuzzy: options[:fuzzy],
109
139
  interactive: options[:interactive],
110
140
  not: options[:not],
@@ -112,6 +142,7 @@ command :finish do |c|
112
142
  search: search,
113
143
  section: options[:section],
114
144
  sequential: options[:auto],
145
+ start_date: start_date,
115
146
  tag: tags,
116
147
  tag_bool: options[:bool].normalize_bool,
117
148
  tags: ['done'],
data/bin/commands/flag.rb CHANGED
@@ -1,10 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # @@mark @@flag
2
4
  desc 'Mark last entry as flagged'
3
5
  command %i[mark flag] do |c|
4
6
  c.example 'doing flag', desc: 'Add @flagged to the last entry created'
5
7
  c.example 'doing mark', desc: 'mark is an alias for flag'
6
8
  c.example 'doing flag --tag project1 --count 2', desc: 'Add @flagged to the last 2 entries tagged @project1'
7
- c.example 'doing flag --interactive --search "/(develop|cod)ing/"', desc: 'Find entries matching regular expression and create a menu allowing multiple selections, selected items will be @flagged'
9
+ c.example 'doing flag --interactive --search "/(develop|cod)ing/"',
10
+ desc: 'Find entries matching regular expression and create a menu allowing multiple selections,
11
+ selected items will be @flagged'
8
12
 
9
13
  c.desc 'Section'
10
14
  c.arg_name 'SECTION_NAME'
data/bin/commands/grep.rb CHANGED
@@ -16,25 +16,12 @@ 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 "Output to export format (#{Doing::Plugins.plugin_names(type: :export)})"
20
- c.arg_name 'FORMAT'
21
- c.flag %i[o output]
22
-
23
- c.desc "Output using a template from configuration"
24
- c.arg_name 'TEMPLATE_KEY'
25
- c.flag [:config_template], type: TemplateName, default_value: 'default'
26
-
27
- c.desc 'Override output format with a template string containing %placeholders'
28
- c.arg_name 'TEMPLATE_STRING'
29
- c.flag [:template]
30
-
31
19
  # c.desc '[DEPRECATED] Use alternative fuzzy matching for search string'
32
20
  # c.switch [:fuzzy], default_value: false, negatable: false
33
21
 
34
22
  c.desc 'Force exact string matching (case sensitive)'
35
23
  c.switch %i[x exact], default_value: Doing.config.exact_match?, negatable: Doing.config.exact_match?
36
24
 
37
-
38
25
  c.desc 'Case sensitivity for search string matching [(c)ase-sensitive, (i)gnore, (s)mart]'
39
26
  c.arg_name 'TYPE'
40
27
  c.flag [:case], must_match: REGEX_CASE,
@@ -53,13 +40,15 @@ command %i[grep search] do |c|
53
40
  c.desc 'Display an interactive menu of results to perform further operations'
54
41
  c.switch %i[i interactive], default_value: false, negatable: false
55
42
 
43
+ add_options(:output_template, c)
56
44
  add_options(:tag_filter, c)
57
45
  add_options(:date_filter, c)
58
46
  add_options(:time_display, c)
59
47
 
60
48
  c.action do |_global_options, options, args|
61
49
  options[:fuzzy] = false
62
- raise DoingRuntimeError, %(Invalid output type "#{options[:output]}") if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
50
+
51
+ raise InvalidPlugin.new('output', options[:output]) if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
63
52
 
64
53
  template = Doing.setting(['templates', options[:config_template]]).deep_merge(Doing.settings)
65
54
  tags_color = template.key?('tags_color') ? template['tags_color'] : nil
data/bin/commands/last.rb CHANGED
@@ -21,20 +21,13 @@ 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 "Output using a template from configuration"
25
- c.arg_name 'TEMPLATE_KEY'
26
- c.flag [:config_template], type: TemplateName, default_value: 'last'
27
-
28
- c.desc 'Override output format with a template string containing %placeholders'
29
- c.arg_name 'TEMPLATE_STRING'
30
- c.flag [:template]
31
-
32
24
  c.desc "Highlight search matches in output. Only affects command line output"
33
25
  c.switch %i[h hilite], default_value: Doing.settings.dig('search', 'highlight')
34
26
 
35
27
  c.desc 'Show elapsed time if entry is not tagged @done'
36
28
  c.switch [:duration]
37
29
 
30
+ add_options(:output_template, c, default_template: 'last')
38
31
  add_options(:search, c)
39
32
  add_options(:tag_filter, c)
40
33
 
@@ -69,6 +62,7 @@ command :last do |c|
69
62
  case: options[:case],
70
63
  hilite: options[:hilite],
71
64
  negate: options[:not],
65
+ output: options[:output],
72
66
  tag: options[:tag],
73
67
  tag_bool: options[:bool],
74
68
  delete: options[:delete],
@@ -19,6 +19,8 @@ command :meanwhile do |c|
19
19
  c.switch %i[a archive], negatable: false, default_value: false
20
20
 
21
21
  add_options(:add_entry, c)
22
+ # TODO: Add took and from handling for post-dating meanwhile entries
23
+ # add_options(:add_dated, c)
22
24
 
23
25
  c.action do |global_options, options, args|
24
26
  Doing.auto_tag = !options[:noauto]
@@ -31,17 +33,18 @@ command :meanwhile do |c|
31
33
  date = Time.now
32
34
  end
33
35
 
34
- if options[:section]
35
- section = @wwid.guess_section(options[:section]) || options[:section].cap_first
36
- else
37
- section = Doing.setting('current_section')
38
- end
36
+ section = if options[:section]
37
+ @wwid.guess_section(options[:section]) || options[:section].cap_first
38
+ else
39
+ Doing.setting('current_section')
40
+ end
39
41
  input = ''
40
42
 
41
43
  ask_note = options[:ask] ? Doing::Prompt.read_lines(prompt: 'Add a note') : []
42
44
 
43
45
  if options[:editor]
44
46
  raise MissingEditor, 'No EDITOR variable defined in environment' if Doing::Util.default_editor.nil?
47
+
45
48
  input += date.strftime('%F %R | ')
46
49
  input += args.join(' ') unless args.empty?
47
50
  input += "\n#{options[:note]}" if options[:note]
@@ -70,7 +73,11 @@ command :meanwhile do |c|
70
73
  note.add(ask_note) if ask_note.good?
71
74
  end
72
75
 
73
- @wwid.stop_start('meanwhile', { new_item: input, back: date, section: section, archive: options[:archive], note: note })
76
+ @wwid.stop_start('meanwhile', { new_item: input,
77
+ back: date,
78
+ section: section,
79
+ archive: options[:archive],
80
+ note: note })
74
81
  @wwid.write(@wwid.doing_file)
75
82
  end
76
83
  end