doing 2.1.89 → 2.1.90

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 (242) hide show
  1. checksums.yaml +4 -4
  2. data/.irbrc +2 -0
  3. data/.rubocop.yml +1 -0
  4. data/.rubocop_todo.yml +251 -0
  5. data/CHANGELOG.md +22 -0
  6. data/Gemfile +2 -0
  7. data/Gemfile.lock +1 -1
  8. data/Rakefile +5 -3
  9. data/bin/commands/changes.rb +3 -1
  10. data/bin/commands/choose.rb +2 -0
  11. data/bin/commands/colors.rb +5 -3
  12. data/bin/commands/commands_accepting.rb +3 -3
  13. data/bin/commands/completion.rb +2 -1
  14. data/bin/commands/config.rb +8 -6
  15. data/bin/commands/done.rb +19 -12
  16. data/bin/commands/finish.rb +4 -2
  17. data/bin/commands/grep.rb +9 -5
  18. data/bin/commands/import.rb +14 -11
  19. data/bin/commands/install_fzf.rb +4 -2
  20. data/bin/commands/last.rb +31 -27
  21. data/bin/commands/meanwhile.rb +6 -2
  22. data/bin/commands/note.rb +6 -3
  23. data/bin/commands/now.rb +2 -0
  24. data/bin/commands/open.rb +6 -1
  25. data/bin/commands/plugins.rb +2 -0
  26. data/bin/commands/recent.rb +47 -33
  27. data/bin/commands/reset.rb +7 -3
  28. data/bin/commands/rotate.rb +6 -3
  29. data/bin/commands/sections.rb +3 -3
  30. data/bin/commands/select.rb +2 -0
  31. data/bin/commands/show.rb +24 -20
  32. data/bin/commands/since.rb +10 -3
  33. data/bin/commands/tag.rb +18 -16
  34. data/bin/commands/tag_dir.rb +5 -2
  35. data/bin/commands/tags.rb +17 -17
  36. data/bin/commands/template.rb +8 -2
  37. data/bin/commands/today.rb +10 -2
  38. data/bin/commands/undo.rb +2 -0
  39. data/bin/commands/update.rb +9 -7
  40. data/bin/commands/view.rb +11 -8
  41. data/bin/commands/views.rb +2 -0
  42. data/bin/commands/yesterday.rb +6 -1
  43. data/bin/doing +54 -57
  44. data/docs/doc/Array.html +3 -3
  45. data/docs/doc/BooleanTermParser/Clause.html +3 -3
  46. data/docs/doc/BooleanTermParser/Operator.html +3 -3
  47. data/docs/doc/BooleanTermParser/Query.html +3 -3
  48. data/docs/doc/BooleanTermParser/QueryParser.html +3 -3
  49. data/docs/doc/BooleanTermParser/QueryTransformer.html +3 -3
  50. data/docs/doc/BooleanTermParser.html +3 -3
  51. data/docs/doc/Doing/ArrayCleanup.html +3 -3
  52. data/docs/doc/Doing/ArrayNestedHash.html +3 -3
  53. data/docs/doc/Doing/ArrayTags.html +9 -9
  54. data/docs/doc/Doing/ByDayExport.html +3 -3
  55. data/docs/doc/Doing/CSVExport.html +4 -4
  56. data/docs/doc/Doing/CalendarImport.html +4 -4
  57. data/docs/doc/Doing/Change.html +3 -3
  58. data/docs/doc/Doing/Changes.html +3 -3
  59. data/docs/doc/Doing/ChronifyArray.html +3 -3
  60. data/docs/doc/Doing/ChronifyNumeric.html +3 -3
  61. data/docs/doc/Doing/ChronifyString.html +6 -6
  62. data/docs/doc/Doing/Color.html +88 -47
  63. data/docs/doc/Doing/Completion/BashCompletions.html +3 -3
  64. data/docs/doc/Doing/Completion/FigCompletions.html +3 -3
  65. data/docs/doc/Doing/Completion/FishCompletions.html +3 -3
  66. data/docs/doc/Doing/Completion/StringUtils.html +3 -3
  67. data/docs/doc/Doing/Completion/ZshCompletions.html +3 -3
  68. data/docs/doc/Doing/Completion.html +5 -5
  69. data/docs/doc/Doing/Configuration.html +6 -6
  70. data/docs/doc/Doing/DayOneRenderer.html +3 -3
  71. data/docs/doc/Doing/DayoneExport.html +4 -4
  72. data/docs/doc/Doing/DoingExport.html +5 -5
  73. data/docs/doc/Doing/DoingImport.html +4 -4
  74. data/docs/doc/Doing/Entry.html +3 -3
  75. data/docs/doc/Doing/Errors/DoingNoTraceError.html +3 -3
  76. data/docs/doc/Doing/Errors/DoingRuntimeError.html +3 -3
  77. data/docs/doc/Doing/Errors/DoingStandardError.html +3 -3
  78. data/docs/doc/Doing/Errors/EmptyInput.html +3 -3
  79. data/docs/doc/Doing/Errors/HistoryLimitError.html +3 -3
  80. data/docs/doc/Doing/Errors/InvalidPlugin.html +3 -3
  81. data/docs/doc/Doing/Errors/MissingBackupFile.html +3 -3
  82. data/docs/doc/Doing/Errors/NoResults.html +3 -3
  83. data/docs/doc/Doing/Errors/PluginException.html +3 -3
  84. data/docs/doc/Doing/Errors/UserCancelled.html +3 -3
  85. data/docs/doc/Doing/Errors/WrongCommand.html +3 -3
  86. data/docs/doc/Doing/Errors.html +3 -3
  87. data/docs/doc/Doing/HTMLExport.html +4 -4
  88. data/docs/doc/Doing/Hooks.html +3 -16
  89. data/docs/doc/Doing/Item.html +4 -4
  90. data/docs/doc/Doing/ItemDates.html +3 -3
  91. data/docs/doc/Doing/ItemQuery.html +3 -3
  92. data/docs/doc/Doing/ItemState.html +3 -3
  93. data/docs/doc/Doing/ItemTags.html +3 -3
  94. data/docs/doc/Doing/Items.html +3 -3
  95. data/docs/doc/Doing/JSONExport.html +4 -4
  96. data/docs/doc/Doing/JSONImport.html +4 -4
  97. data/docs/doc/Doing/Logger.html +183 -3
  98. data/docs/doc/Doing/MarkdownExport.html +4 -4
  99. data/docs/doc/Doing/Note.html +3 -3
  100. data/docs/doc/Doing/Pager.html +54 -58
  101. data/docs/doc/Doing/Plugins.html +3 -3
  102. data/docs/doc/Doing/Prompt.html +4 -4
  103. data/docs/doc/Doing/PromptChoose.html +3 -3
  104. data/docs/doc/Doing/PromptFZF.html +3 -3
  105. data/docs/doc/Doing/PromptInput.html +3 -3
  106. data/docs/doc/Doing/PromptSTD.html +3 -3
  107. data/docs/doc/Doing/PromptYN.html +3 -3
  108. data/docs/doc/Doing/Section.html +3 -3
  109. data/docs/doc/Doing/StringHighlight.html +3 -3
  110. data/docs/doc/Doing/StringNormalize.html +3 -3
  111. data/docs/doc/Doing/StringQuery.html +4 -4
  112. data/docs/doc/Doing/StringTags.html +3 -3
  113. data/docs/doc/Doing/StringTransform.html +3 -3
  114. data/docs/doc/Doing/StringTruncate.html +3 -3
  115. data/docs/doc/Doing/StringURL.html +3 -3
  116. data/docs/doc/Doing/SymbolNormalize.html +5 -5
  117. data/docs/doc/Doing/TaskPaperExport.html +4 -4
  118. data/docs/doc/Doing/TemplateExport.html +5 -5
  119. data/docs/doc/Doing/TemplateString.html +7 -7
  120. data/docs/doc/Doing/TimingImport.html +4 -4
  121. data/docs/doc/Doing/Types.html +3 -3
  122. data/docs/doc/Doing/Util/Backup.html +4 -17
  123. data/docs/doc/Doing/Util.html +56 -61
  124. data/docs/doc/Doing/Version.html +3 -3
  125. data/docs/doc/Doing/WWID.html +6 -4
  126. data/docs/doc/Doing.html +4 -4
  127. data/docs/doc/FalseClass.html +3 -3
  128. data/docs/doc/GLI/Commands/Help.html +5 -5
  129. data/docs/doc/GLI/Commands/MarkdownDocumentListener.html +3 -3
  130. data/docs/doc/GLI/Commands.html +3 -3
  131. data/docs/doc/GLI.html +3 -3
  132. data/docs/doc/Hash.html +4 -4
  133. data/docs/doc/Numeric.html +3 -3
  134. data/docs/doc/Object.html +3 -3
  135. data/docs/doc/PhraseParser/Operator.html +3 -3
  136. data/docs/doc/PhraseParser/PhraseClause.html +3 -3
  137. data/docs/doc/PhraseParser/Query.html +3 -3
  138. data/docs/doc/PhraseParser/QueryParser.html +3 -3
  139. data/docs/doc/PhraseParser/QueryTransformer.html +3 -3
  140. data/docs/doc/PhraseParser/TermClause.html +3 -3
  141. data/docs/doc/PhraseParser.html +3 -3
  142. data/docs/doc/Status.html +3 -3
  143. data/docs/doc/String.html +51 -5
  144. data/docs/doc/Symbol.html +3 -3
  145. data/docs/doc/Time.html +3 -3
  146. data/docs/doc/TrueClass.html +3 -3
  147. data/docs/doc/_index.html +4 -4
  148. data/docs/doc/class_list.html +6 -3
  149. data/docs/doc/css/full_list.css +3 -3
  150. data/docs/doc/css/style.css +6 -0
  151. data/docs/doc/file.README.html +3 -3
  152. data/docs/doc/file_list.html +5 -2
  153. data/docs/doc/frames.html +1 -1
  154. data/docs/doc/index.html +3 -3
  155. data/docs/doc/js/app.js +294 -264
  156. data/docs/doc/js/full_list.js +30 -4
  157. data/docs/doc/method_list.html +443 -392
  158. data/docs/doc/top-level-namespace.html +3 -3
  159. data/doing.gemspec +2 -0
  160. data/doing.rdoc +1 -1
  161. data/example_plugin.rb +1 -4
  162. data/lib/doing/add_options.rb +2 -2
  163. data/lib/doing/array/cleanup.rb +2 -0
  164. data/lib/doing/array/nested_hash.rb +2 -0
  165. data/lib/doing/boolean_term_parser.rb +3 -3
  166. data/lib/doing/changelog/changes.rb +4 -5
  167. data/lib/doing/changelog/version.rb +8 -11
  168. data/lib/doing/chronify/array.rb +4 -4
  169. data/lib/doing/chronify/string.rb +5 -4
  170. data/lib/doing/cli_status.rb +7 -2
  171. data/lib/doing/colors.rb +93 -51
  172. data/lib/doing/completion/bash_completion.rb +36 -39
  173. data/lib/doing/completion/completion_string.rb +2 -1
  174. data/lib/doing/completion/fig_completion.rb +33 -33
  175. data/lib/doing/completion/fish_completion.rb +22 -23
  176. data/lib/doing/completion/zsh_completion.rb +5 -5
  177. data/lib/doing/completion.rb +7 -4
  178. data/lib/doing/configuration.rb +21 -13
  179. data/lib/doing/errors.rb +1 -4
  180. data/lib/doing/good.rb +1 -1
  181. data/lib/doing/hash.rb +4 -4
  182. data/lib/doing/help_monkey_patch.rb +1 -1
  183. data/lib/doing/hooks.rb +6 -2
  184. data/lib/doing/item/dates.rb +3 -1
  185. data/lib/doing/item/query.rb +10 -10
  186. data/lib/doing/item/state.rb +2 -0
  187. data/lib/doing/logger.rb +113 -45
  188. data/lib/doing/markdown_document_listener.rb +25 -25
  189. data/lib/doing/normalize.rb +1 -1
  190. data/lib/doing/pager.rb +73 -29
  191. data/lib/doing/plugin_manager.rb +4 -5
  192. data/lib/doing/plugins/export/byday.rb +1 -1
  193. data/lib/doing/plugins/export/dayone_export.rb +28 -27
  194. data/lib/doing/plugins/export/doing_export.rb +1 -1
  195. data/lib/doing/plugins/export/html_export.rb +3 -3
  196. data/lib/doing/plugins/export/json_export.rb +4 -5
  197. data/lib/doing/plugins/export/markdown_export.rb +10 -2
  198. data/lib/doing/plugins/export/taskpaper_export.rb +1 -0
  199. data/lib/doing/plugins/export/template_export.rb +110 -107
  200. data/lib/doing/plugins/import/calendar_import.rb +1 -1
  201. data/lib/doing/plugins/import/doing_import.rb +2 -2
  202. data/lib/doing/plugins/import/timing_import.rb +3 -3
  203. data/lib/doing/prompt/choose.rb +5 -6
  204. data/lib/doing/prompt/fzf.rb +3 -2
  205. data/lib/doing/prompt/input.rb +7 -6
  206. data/lib/doing/prompt/yn.rb +9 -11
  207. data/lib/doing/string/tags.rb +7 -4
  208. data/lib/doing/string/transform.rb +15 -10
  209. data/lib/doing/string/truncate.rb +1 -0
  210. data/lib/doing/string/url.rb +1 -1
  211. data/lib/doing/template_string.rb +13 -9
  212. data/lib/doing/time.rb +4 -2
  213. data/lib/doing/util.rb +12 -11
  214. data/lib/doing/util_backup.rb +29 -26
  215. data/lib/doing/version.rb +3 -1
  216. data/lib/doing/wwid/display.rb +182 -151
  217. data/lib/doing/wwid/editor.rb +13 -12
  218. data/lib/doing/wwid/filetools.rb +13 -11
  219. data/lib/doing/wwid/filter.rb +41 -40
  220. data/lib/doing/wwid/guess.rb +6 -8
  221. data/lib/doing/wwid/interactive.rb +9 -9
  222. data/lib/doing/wwid/modify.rb +53 -53
  223. data/lib/doing/wwid/timers.rb +4 -5
  224. data/lib/doing/wwid/wwid.rb +0 -0
  225. data/lib/doing/wwid/wwidutil.rb +8 -10
  226. data/lib/examples/commands/wiki.rb +2 -0
  227. data/lib/examples/plugins/capture_thing_import.rb +1 -1
  228. data/lib/examples/plugins/say_export.rb +1 -2
  229. data/lib/examples/plugins/wiki_export/wiki_export.rb +3 -4
  230. data/lib/helpers/fzf/test/test_go.rb +5 -5
  231. data/lib/helpers/threaded_tests.rb +20 -20
  232. data/lib/helpers/threaded_tests_string.rb +2 -0
  233. data/rdoc_to_mmd.rb +5 -4
  234. data/rdocfixer.rb +1 -2
  235. data/scripts/deploy.rb +3 -4
  236. data/scripts/generate_bash_completions.rb +40 -43
  237. data/scripts/generate_fish_completions.rb +17 -15
  238. data/scripts/generate_zsh_completions.rb +9 -7
  239. data/scripts/setting_replace.rb +1 -0
  240. data/scripts/sort_commands.rb +4 -2
  241. data/yard_templates/default/method_details/setup.rb +3 -1
  242. metadata +3 -1
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'zlib'
3
4
 
4
5
  module Doing
@@ -17,7 +18,7 @@ module Doing
17
18
  backups = get_backups(filename)
18
19
  return unless backups.count > limit
19
20
 
20
- backups[limit..-1].each do |file|
21
+ backups[limit..].each do |file|
21
22
  FileUtils.rm(File.join(backup_dir, file))
22
23
  end
23
24
 
@@ -45,18 +46,18 @@ module Doing
45
46
  ## different from default
46
47
  ##
47
48
  def restore_last_backup(filename = nil, count: 1)
48
- Doing.logger.benchmark(:restore_backup, :start)
49
- filename ||= Doing.setting('doing_file')
49
+ Doing.logger.measure(:restore_backup) do
50
+ filename ||= Doing.setting('doing_file')
50
51
 
51
- backup_file = last_backup(filename, count: count)
52
- raise HistoryLimitError, 'End of undo history' if backup_file.nil?
52
+ backup_file = last_backup(filename, count: count)
53
+ raise HistoryLimitError, 'End of undo history' if backup_file.nil?
53
54
 
54
- save_undone(filename)
55
- move_backup(backup_file, filename)
55
+ save_undone(filename)
56
+ move_backup(backup_file, filename)
56
57
 
57
- prune_backups_after(File.basename(backup_file))
58
- Doing.logger.warn('File update:', "restored from #{backup_file}")
59
- Doing.logger.benchmark(:restore_backup, :finish)
58
+ prune_backups_after(File.basename(backup_file))
59
+ Doing.logger.warn('File update:', "restored from #{backup_file}")
60
+ end
60
61
  end
61
62
 
62
63
  ##
@@ -138,13 +139,15 @@ module Doing
138
139
  options = get_backups(filename).each_with_object([]) do |file, arr|
139
140
  d, _base = date_of_backup(file)
140
141
  next if d.nil?
142
+
141
143
  arr.push("#{d.time_ago}\t#{File.join(backup_dir, file)}")
142
144
  end
143
145
 
144
146
  raise MissingBackupFile, 'No backup files to load' if options.empty?
145
147
 
146
148
  backup_file = show_menu(options, filename)
147
- Util.write_to_file(File.join(backup_dir, "undone___#{File.basename(filename)}"), IO.read(filename), backup: false)
149
+ Util.write_to_file(File.join(backup_dir, "undone___#{File.basename(filename)}"), IO.read(filename),
150
+ backup: false)
148
151
  move_backup(backup_file, filename)
149
152
  prune_backups_after(File.basename(backup_file))
150
153
  Doing.logger.warn('File update:', "restored from #{backup_file}")
@@ -157,25 +160,25 @@ module Doing
157
160
  ## @param filename [String] The filename
158
161
  ##
159
162
  def write_backup(filename = nil)
160
- Doing.logger.benchmark(:_write_backup, :start)
161
- filename ||= Doing.setting('doing_file')
163
+ Doing.logger.measure(:_write_backup) do
164
+ filename ||= Doing.setting('doing_file')
162
165
 
163
- unless File.exist?(filename)
164
- Doing.logger.debug('Backup:', "original file doesn't exist (#{filename})")
165
- return
166
- end
166
+ unless File.exist?(filename)
167
+ Doing.logger.debug('Backup:', "original file doesn't exist (#{filename})")
168
+ return
169
+ end
167
170
 
168
- backup_file = File.join(backup_dir, "#{timestamp_filename}___#{File.basename(filename)}")
169
- # compressed = Zlib::Deflate.deflate(content)
170
- # Zlib::GzipWriter.open(backup_file + '.gz') do |gz|
171
- # gz.write(IO.read(filename))
172
- # end
171
+ backup_file = File.join(backup_dir, "#{timestamp_filename}___#{File.basename(filename)}")
172
+ # compressed = Zlib::Deflate.deflate(content)
173
+ # Zlib::GzipWriter.open(backup_file + '.gz') do |gz|
174
+ # gz.write(IO.read(filename))
175
+ # end
173
176
 
174
- FileUtils.cp(filename, backup_file)
177
+ FileUtils.cp(filename, backup_file)
175
178
 
176
- prune_backups(filename, Doing.setting('history_size').to_i)
177
- clear_undone(filename)
178
- Doing.logger.benchmark(:_write_backup, :finish)
179
+ prune_backups(filename, Doing.setting('history_size').to_i)
180
+ clear_undone(filename)
181
+ end
179
182
  end
180
183
 
181
184
  private
data/lib/doing/version.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doing
2
- VERSION = '2.1.89'
4
+ VERSION = '2.1.90'
3
5
  end
@@ -8,91 +8,108 @@ module Doing
8
8
  ## @param opt [Hash] Additional Options
9
9
  ##
10
10
  def list_section(opt, items: Items.new)
11
- logger.benchmark(:list_section, :start)
12
- opt[:config_template] ||= 'default'
13
-
14
- tpl_cfg = Doing.setting(['templates', opt[:config_template]])
15
-
16
- cfg = if opt[:view_template]
17
- Doing.setting(['views', opt[:view_template]]).deep_merge(tpl_cfg, { extend_existing_arrays: true, sort_merged_arrays: true })
18
- else
19
- tpl_cfg
20
- end
21
-
22
- cfg.deep_merge({
23
- 'wrap_width' => Doing.setting('wrap_width') || 0,
24
- 'date_format' => Doing.setting('default_date_format'),
25
- 'order' => Doing.setting('order') || :asc,
26
- 'tags_color' => Doing.setting('tags_color'),
27
- 'duration' => Doing.setting('duration'),
28
- 'interval_format' => Doing.setting('interval_format')
29
- }, { extend_existing_arrays: true, sort_merged_arrays: true })
11
+ logger.measure(:list_section) do
12
+ cfg = nil
13
+ logger.measure(:list_section_config) do
14
+ opt[:config_template] ||= 'default'
30
15
 
31
- opt[:duration] ||= cfg['duration'] || false
32
- opt[:interval_format] ||= cfg['interval_format'] || 'text'
33
- opt[:count] ||= 0
34
- opt[:age] ||= :newest
35
- opt[:age] = opt[:age].normalize_age
36
- opt[:format] ||= cfg['date_format']
37
- opt[:order] ||= cfg['order'] || :asc
38
- opt[:tag_order] ||= :asc
39
- opt[:tags_color] = cfg['tags_color'] || false if opt[:tags_color].nil?
40
- opt[:template] ||= cfg['template']
41
- opt[:sort_tags] ||= opt[:tag_sort]
42
-
43
- # opt[:highlight] ||= true
44
- title = ''
45
- is_single = true
46
- if opt[:section].nil?
47
- opt[:section] = choose_section
48
- title = opt[:section]
49
- elsif opt[:section].is_a?(Array)
50
- title = opt[:section].join(', ')
51
- elsif opt[:section].is_a?(String)
52
- title = if opt[:section] =~ /^all$/i
53
- if opt[:page_title]
54
- opt[:page_title]
55
- elsif opt[:tag_filter] && opt[:tag_filter]['bool'].normalize_bool != :not
56
- opt[:tag_filter]['tags'].map { |tag| "@#{tag}" }.join(' + ')
57
- else
58
- 'doing'
59
- end
16
+ tpl_cfg = Doing.setting(['templates', opt[:config_template]])
17
+
18
+ cfg = if opt[:view_template]
19
+ Doing.setting(['views', opt[:view_template]]).deep_merge(tpl_cfg,
20
+ { extend_existing_arrays: true,
21
+ sort_merged_arrays: true })
60
22
  else
61
- guess_section(opt[:section])
23
+ tpl_cfg
62
24
  end
63
- end
64
-
65
- items = filter_items(items, opt: opt)
66
-
67
- items.reverse! unless opt[:order].normalize_order == :desc
68
-
69
- if opt[:delete]
70
- delete_items(items, force: opt[:force])
71
-
72
- write(@doing_file)
73
- return
74
- elsif opt[:editor]
75
- edit_items(items)
76
-
77
- write(@doing_file)
78
- return
79
- elsif opt[:interactive]
80
- opt[:menu] = !opt[:force]
81
- opt[:query] = '' # opt[:search]
82
- opt[:multiple] = true
83
- selected = Prompt.choose_from_items(items.reverse, include_section: opt[:section] =~ /^all$/i, **opt)
84
-
85
- raise NoResults, 'no items selected' if selected.nil? || selected.empty?
86
25
 
87
- act_on(selected, opt)
88
- return
26
+ cfg.deep_merge({
27
+ 'wrap_width' => Doing.setting('wrap_width') || 0,
28
+ 'date_format' => Doing.setting('default_date_format'),
29
+ 'order' => Doing.setting('order') || :asc,
30
+ 'tags_color' => Doing.setting('tags_color'),
31
+ 'duration' => Doing.setting('duration'),
32
+ 'interval_format' => Doing.setting('interval_format')
33
+ }, { extend_existing_arrays: true, sort_merged_arrays: true })
34
+ end
35
+
36
+ logger.measure(:list_section_options) do
37
+ opt[:duration] ||= cfg['duration'] || false
38
+ opt[:interval_format] ||= cfg['interval_format'] || 'text'
39
+ opt[:count] ||= 0
40
+ opt[:age] ||= :newest
41
+ opt[:age] = opt[:age].normalize_age
42
+ opt[:format] ||= cfg['date_format']
43
+ opt[:order] ||= cfg['order'] || :asc
44
+ opt[:tag_order] ||= :asc
45
+ opt[:tags_color] = cfg['tags_color'] || false if opt[:tags_color].nil?
46
+ opt[:template] ||= cfg['template']
47
+ opt[:sort_tags] ||= opt[:tag_sort]
48
+ end
49
+
50
+ title = ''
51
+ is_single = true
52
+ logger.measure(:list_section_title) do
53
+ # opt[:highlight] ||= true
54
+ if opt[:section].nil?
55
+ opt[:section] = choose_section
56
+ title = opt[:section]
57
+ elsif opt[:section].is_a?(Array)
58
+ title = opt[:section].join(', ')
59
+ elsif opt[:section].is_a?(String)
60
+ title = if opt[:section] =~ /^all$/i
61
+ if opt[:page_title]
62
+ opt[:page_title]
63
+ elsif opt[:tag_filter] && opt[:tag_filter]['bool'].normalize_bool != :not
64
+ opt[:tag_filter]['tags'].map { |tag| "@#{tag}" }.join(' + ')
65
+ else
66
+ 'doing'
67
+ end
68
+ else
69
+ guess_section(opt[:section])
70
+ end
71
+ end
72
+ end
73
+
74
+ logger.measure(:list_section_filter) do
75
+ items = filter_items(items, opt: opt)
76
+ end
77
+
78
+ logger.measure(:list_section_sort) do
79
+ items.reverse! unless opt[:order].normalize_order == :desc
80
+ end
81
+
82
+ logger.measure(:list_section_actions) do
83
+ if opt[:delete]
84
+ delete_items(items, force: opt[:force])
85
+
86
+ write(@doing_file)
87
+ return
88
+ elsif opt[:editor]
89
+ edit_items(items)
90
+
91
+ write(@doing_file)
92
+ return
93
+ elsif opt[:interactive]
94
+ opt[:menu] = !opt[:force]
95
+ opt[:query] = '' # opt[:search]
96
+ opt[:multiple] = true
97
+ selected = Prompt.choose_from_items(items.reverse, include_section: opt[:section] =~ /^all$/i, **opt)
98
+
99
+ raise NoResults, 'no items selected' if selected.nil? || selected.empty?
100
+
101
+ act_on(selected, opt)
102
+ return
103
+ end
104
+ end
105
+
106
+ logger.measure(:list_section_output) do
107
+ opt[:output] ||= 'template'
108
+ opt[:wrap_width] ||= Doing.setting('templates.default.wrap_width', 0)
109
+
110
+ output(items, title, is_single, opt)
111
+ end
89
112
  end
90
-
91
- opt[:output] ||= 'template'
92
- opt[:wrap_width] ||= Doing.setting('templates.default.wrap_width', 0)
93
-
94
- logger.benchmark(:list_section, :finish)
95
- output(items, title, is_single, opt)
96
113
  end
97
114
 
98
115
  ##
@@ -120,9 +137,7 @@ module Doing
120
137
  opt[:output] = output
121
138
 
122
139
  time_rx = /^(\d{1,2}+(:\d{1,2}+)?( *(am|pm))?|midnight|noon)$/
123
- if opt[:from] && opt[:from][0].is_a?(String) && opt[:from][0] =~ time_rx
124
- opt[:time_filter] = opt[:from]
125
- end
140
+ opt[:time_filter] = opt[:from] if opt[:from] && opt[:from][0].is_a?(String) && opt[:from][0] =~ time_rx
126
141
 
127
142
  list_section(opt)
128
143
  end
@@ -220,33 +235,40 @@ module Doing
220
235
  ## @param opt [Hash] Additional Options
221
236
  ##
222
237
  def recent(count = 10, section = nil, opt)
223
- opt ||= {}
224
- opt[:times] ||= false
225
- opt[:totals] ||= false
226
- opt[:sort_tags] ||= false
227
-
228
- cfg = Doing.setting('templates.recent').deep_merge(Doing.setting('templates.default'), { extend_existing_arrays: true, sort_merged_arrays: true }).deep_merge({
229
- 'wrap_width' => Doing.setting('wrap_width') || 0,
230
- 'date_format' => Doing.setting('default_date_format'),
231
- 'order' => Doing.setting('order') || :asc,
232
- 'tags_color' => Doing.setting('tags_color'),
233
- 'duration' => Doing.setting('duration'),
234
- 'interval_format' => Doing.setting('interval_format')
235
- }, { extend_existing_arrays: true, sort_merged_arrays: true })
236
- opt[:duration] ||= cfg['duration'] || false
237
- opt[:interval_format] ||= cfg['interval_format'] || 'text'
238
-
239
- section ||= Doing.setting('current_section')
240
- section = guess_section(section)
241
-
242
- opt[:section] = section
243
- opt[:wrap_width] = cfg['wrap_width']
244
- opt[:count] = count
245
- opt[:format] = cfg['date_format']
246
- opt[:template] = opt[:template] || cfg['template']
247
- opt[:order] = :asc
248
-
249
- list_section(opt)
238
+ logger.measure(:recent_method) do
239
+ opt ||= {}
240
+ opt[:times] ||= false
241
+ opt[:totals] ||= false
242
+ opt[:sort_tags] ||= false
243
+
244
+ cfg = nil
245
+ logger.measure(:recent_config_merge) do
246
+ cfg = Doing.setting('templates.recent').deep_merge(Doing.setting('templates.default'), { extend_existing_arrays: true, sort_merged_arrays: true }).deep_merge({
247
+ 'wrap_width' => Doing.setting('wrap_width') || 0,
248
+ 'date_format' => Doing.setting('default_date_format'),
249
+ 'order' => Doing.setting('order') || :asc,
250
+ 'tags_color' => Doing.setting('tags_color'),
251
+ 'duration' => Doing.setting('duration'),
252
+ 'interval_format' => Doing.setting('interval_format')
253
+ }, { extend_existing_arrays: true, sort_merged_arrays: true })
254
+ opt[:duration] ||= cfg['duration'] || false
255
+ opt[:interval_format] ||= cfg['interval_format'] || 'text'
256
+ end
257
+
258
+ logger.measure(:recent_section_setup) do
259
+ section ||= Doing.setting('current_section')
260
+ section = guess_section(section)
261
+
262
+ opt[:section] = section
263
+ opt[:wrap_width] = cfg['wrap_width']
264
+ opt[:count] = count
265
+ opt[:format] = cfg['date_format']
266
+ opt[:template] = opt[:template] || cfg['template']
267
+ opt[:order] = :asc
268
+ end
269
+
270
+ list_section(opt)
271
+ end
250
272
  end
251
273
 
252
274
  ##
@@ -259,13 +281,17 @@ module Doing
259
281
  section = section[0] if section.is_a?(Array) && section.count == 1
260
282
  section = section.nil? ? 'All' : guess_section(section)
261
283
  cfg = Doing.setting(['templates', options[:config_template]]).deep_merge(Doing.setting('templates.default'), { extend_existing_arrays: true, sort_merged_arrays: true }).deep_merge({
262
- 'wrap_width' => Doing.setting('wrap_width', 0),
263
- 'date_format' => Doing.setting('default_date_format'),
264
- 'order' => Doing.setting('order', :asc),
265
- 'tags_color' => Doing.setting('tags_color'),
266
- 'duration' => Doing.setting('duration'),
267
- 'interval_format' => Doing.setting('interval_format')
268
- }, { extend_existing_arrays: true, sort_merged_arrays: true })
284
+ 'wrap_width' => Doing.setting(
285
+ 'wrap_width', 0
286
+ ),
287
+ 'date_format' => Doing.setting('default_date_format'),
288
+ 'order' => Doing.setting(
289
+ 'order', :asc
290
+ ),
291
+ 'tags_color' => Doing.setting('tags_color'),
292
+ 'duration' => Doing.setting('duration'),
293
+ 'interval_format' => Doing.setting('interval_format')
294
+ }, { extend_existing_arrays: true, sort_merged_arrays: true })
269
295
  options[:duration] ||= cfg['duration'] || false
270
296
  options[:interval_format] ||= cfg['interval_format'] || 'text'
271
297
 
@@ -313,7 +339,7 @@ module Doing
313
339
 
314
340
  logger.log_now(:info, 'Edit note:', last_item.title)
315
341
 
316
- note = last_item.note&.to_s || ''
342
+ note = last_item.note.to_s
317
343
  "#{last_item.title}\n# EDIT BELOW THIS LINE ------------\n#{note}"
318
344
  end
319
345
 
@@ -332,19 +358,16 @@ module Doing
332
358
  logger.debug('Filtered:', "Parameters matched #{items.count} entries")
333
359
 
334
360
  if opt[:interactive]
335
- last_entry = Prompt.choose_from_items(items, include_section: opt[:section] =~ /^all$/i,
336
- menu: true,
337
- header: '',
338
- prompt: 'Select an entry > ',
339
- multiple: false,
340
- sort: false,
341
- show_if_single: true
342
- )
361
+ Prompt.choose_from_items(items, include_section: opt[:section] =~ /^all$/i,
362
+ menu: true,
363
+ header: '',
364
+ prompt: 'Select an entry > ',
365
+ multiple: false,
366
+ sort: false,
367
+ show_if_single: true)
343
368
  else
344
- last_entry = items.max_by { |item| item.date }
369
+ items.max_by(&:date)
345
370
  end
346
-
347
- last_entry
348
371
  end
349
372
 
350
373
  private
@@ -362,29 +385,37 @@ module Doing
362
385
  ## template trigger
363
386
  ## @api private
364
387
  def output(items, title, is_single, opt)
365
- logger.benchmark(:output, :start)
366
- opt ||= {}
367
- out = nil
368
-
369
- unless opt[:output] =~ Plugins.plugin_regex(type: :export)
370
- raise InvalidPlugin.new('Unknown output format', opt[:output])
371
-
388
+ logger.measure(:output) do
389
+ opt ||= {}
390
+ out = nil
391
+
392
+ logger.measure(:output_validation) do
393
+ unless opt[:output] =~ Plugins.plugin_regex(type: :export)
394
+ raise InvalidPlugin.new('Unknown output format', opt[:output])
395
+ end
396
+ end
397
+
398
+ export_options = nil
399
+ logger.measure(:output_setup) do
400
+ export_options = { page_title: title, is_single: is_single, options: opt }
401
+ end
402
+
403
+ logger.measure(:output_hooks) do
404
+ Hooks.trigger :pre_export, self, opt[:output], items
405
+ end
406
+
407
+ logger.measure(:output_render) do
408
+ Plugins.plugins[:export].each_value do |options|
409
+ next unless opt[:output] =~ /^(#{options[:trigger].normalize_trigger})$/i
410
+
411
+ out = options[:class].render(self, items, variables: export_options)
412
+ break
413
+ end
414
+ end
415
+
416
+ logger.debug('Output:', "#{items.count} #{items.count == 1 ? 'item' : 'items'} shown")
417
+ out
372
418
  end
373
-
374
- export_options = { page_title: title, is_single: is_single, options: opt }
375
-
376
- Hooks.trigger :pre_export, self, opt[:output], items
377
-
378
- Plugins.plugins[:export].each do |_, options|
379
- next unless opt[:output] =~ /^(#{options[:trigger].normalize_trigger})$/i
380
-
381
- out = options[:class].render(self, items, variables: export_options)
382
- break
383
- end
384
-
385
- logger.debug('Output:', "#{items.count} #{items.count == 1 ? 'item' : 'items'} shown")
386
- logger.benchmark(:output, :finish)
387
- out
388
419
  end
389
420
 
390
421
  ##
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'English'
3
4
  module Doing
4
5
  class WWID
5
6
  ##
@@ -37,7 +38,7 @@ module Doing
37
38
  Process.wait(pid)
38
39
 
39
40
  begin
40
- if $?.exitstatus == 0
41
+ if $CHILD_STATUS.exitstatus.zero?
41
42
  input = IO.read(tmpfile.path)
42
43
  else
43
44
  exit_now! 'Cancelled'
@@ -84,7 +85,7 @@ module Doing
84
85
  end
85
86
 
86
87
  note = Note.new
87
- note.add(input_lines[1..-1]) if input_lines.length > 1
88
+ note.add(input_lines[1..]) if input_lines.length > 1
88
89
  # If title line ends in a parenthetical, use that as the note
89
90
  if note.empty? && title =~ /\s+\(.*?\)$/
90
91
  title.sub!(/\s+\((?<note>.*?)\)$/) do
@@ -123,24 +124,24 @@ module Doing
123
124
  end
124
125
 
125
126
  def edit_items(items)
126
- items.sort_by! { |i| i.date }
127
+ items.sort_by!(&:date)
127
128
  editable_items = []
128
129
 
129
130
  items.each do |i|
130
131
  editable = "#{i.date.strftime('%F %R')} | #{i.title}"
131
- old_note = i.note ? i.note.strip_lines.join("\n") : nil
132
+ old_note = i.note&.strip_lines&.join("\n")
132
133
  editable += "\n#{old_note}" unless old_note.nil?
133
134
  editable_items << editable
134
135
  end
135
- divider = "-----------"
136
- notice =<<~EONOTICE
136
+ divider = '-----------'
137
+ notice = <<~EONOTICE
137
138
 
138
- # - You may delete entries, but leave all divider lines (---) in place.
139
- # - Start and @done dates replaced with a time string (yesterday 3pm) will
140
- # be parsed automatically. Do not delete the pipe (|) between start date
141
- # and entry title.
139
+ # - You may delete entries, but leave all divider lines (---) in place.
140
+ # - Start and @done dates replaced with a time string (yesterday 3pm) will
141
+ # be parsed automatically. Do not delete the pipe (|) between start date
142
+ # and entry title.
142
143
  EONOTICE
143
- input = "#{editable_items.map(&:strip).join("\n#{divider}\n")}\n"
144
+ input = "#{editable_items.map(&:strip).join("\n#{divider}\n")}\n"
144
145
 
145
146
  new_items = fork_editor(input, message: notice).split(/^#{divider}/).map(&:strip)
146
147
 
@@ -162,7 +163,7 @@ module Doing
162
163
  item.date = date || items[i].date
163
164
  item.title = title
164
165
  item.note = note
165
- if (item.equal?(old_item))
166
+ if item.equal?(old_item)
166
167
  Doing.logger.count(:skipped, level: :debug)
167
168
  else
168
169
  Doing.logger.count(:updated)
@@ -8,7 +8,7 @@ module Doing
8
8
  ## @param path [String] Override path to a doing file, optional
9
9
  ##
10
10
  def init_doing_file(path = nil)
11
- @doing_file = File.expand_path(Doing.setting('doing_file'))
11
+ @doing_file = File.expand_path(Doing.setting('doing_file'))
12
12
 
13
13
  if path.nil?
14
14
  create(@doing_file) unless File.exist?(@doing_file)
@@ -107,7 +107,7 @@ module Doing
107
107
  keep = opt[:keep] || 0
108
108
  tags = []
109
109
  tags.concat(opt[:tag].split(/ *, */).map { |t| t.sub(/^@/, '').strip }) if opt[:tag]
110
- bool = opt[:bool] || :and
110
+ bool = opt[:bool] || :and
111
111
 
112
112
  sect = opt[:section] !~ /^all$/i ? guess_section(opt[:section]) : 'all'
113
113
 
@@ -121,27 +121,29 @@ module Doing
121
121
 
122
122
  section_items.each do |item|
123
123
  break if counter >= max
124
+
124
125
  if opt[:before]
125
126
  time_string = opt[:before]
126
127
  cutoff = time_string.chronify(guess: :begin)
127
128
  end
128
129
 
129
- unless ((!tags.empty? && !item.tags?(tags, bool)) || (opt[:search] && !item.search(opt[:search].to_s)) || (opt[:before] && item.date >= cutoff))
130
- new_item = @content.delete(item)
131
- Hooks.trigger :post_entry_removed, self, item.clone
132
- raise DoingRuntimeError, "Error deleting item: #{item}" if new_item.nil?
130
+ next if (!tags.empty? && !item.tags?(tags,
131
+ bool)) || (opt[:search] && !item.search(opt[:search].to_s)) || (opt[:before] && item.date >= cutoff)
133
132
 
134
- new_content.add_section(new_item.section, log: false)
135
- new_content.push(new_item)
136
- counter += 1
137
- end
133
+ new_item = @content.delete(item)
134
+ Hooks.trigger :post_entry_removed, self, item.clone
135
+ raise DoingRuntimeError, "Error deleting item: #{item}" if new_item.nil?
136
+
137
+ new_content.add_section(new_item.section, log: false)
138
+ new_content.push(new_item)
139
+ counter += 1
138
140
  end
139
141
 
140
142
  if counter.positive?
141
143
  logger.count(:rotated,
142
144
  level: :info,
143
145
  count: counter,
144
- message: "Rotated %count %items")
146
+ message: 'Rotated %count %items')
145
147
  else
146
148
  logger.info('Skipped:', 'No items were rotated')
147
149
  end