doing 2.1.89 → 2.1.91

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 (248) 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 +26 -0
  6. data/Gemfile +2 -0
  7. data/Gemfile.lock +1 -1
  8. data/README.md +1 -1
  9. data/Rakefile +5 -3
  10. data/bin/commands/changes.rb +3 -1
  11. data/bin/commands/choose.rb +2 -0
  12. data/bin/commands/colors.rb +5 -3
  13. data/bin/commands/commands_accepting.rb +3 -3
  14. data/bin/commands/completion.rb +2 -1
  15. data/bin/commands/config.rb +8 -6
  16. data/bin/commands/done.rb +19 -12
  17. data/bin/commands/finish.rb +4 -2
  18. data/bin/commands/grep.rb +9 -5
  19. data/bin/commands/import.rb +14 -11
  20. data/bin/commands/install_fzf.rb +4 -2
  21. data/bin/commands/last.rb +31 -27
  22. data/bin/commands/meanwhile.rb +6 -2
  23. data/bin/commands/note.rb +6 -3
  24. data/bin/commands/now.rb +2 -0
  25. data/bin/commands/open.rb +6 -1
  26. data/bin/commands/plugins.rb +2 -0
  27. data/bin/commands/recent.rb +47 -33
  28. data/bin/commands/reset.rb +7 -3
  29. data/bin/commands/rotate.rb +6 -3
  30. data/bin/commands/sections.rb +3 -3
  31. data/bin/commands/select.rb +2 -0
  32. data/bin/commands/show.rb +24 -20
  33. data/bin/commands/since.rb +10 -3
  34. data/bin/commands/tag.rb +18 -16
  35. data/bin/commands/tag_dir.rb +5 -2
  36. data/bin/commands/tags.rb +17 -17
  37. data/bin/commands/template.rb +8 -2
  38. data/bin/commands/today.rb +10 -2
  39. data/bin/commands/undo.rb +2 -0
  40. data/bin/commands/update.rb +9 -7
  41. data/bin/commands/view.rb +11 -8
  42. data/bin/commands/views.rb +2 -0
  43. data/bin/commands/yesterday.rb +6 -1
  44. data/bin/doing +54 -57
  45. data/docs/doc/Array.html +3 -3
  46. data/docs/doc/BooleanTermParser/Clause.html +3 -3
  47. data/docs/doc/BooleanTermParser/Operator.html +3 -3
  48. data/docs/doc/BooleanTermParser/Query.html +3 -3
  49. data/docs/doc/BooleanTermParser/QueryParser.html +3 -3
  50. data/docs/doc/BooleanTermParser/QueryTransformer.html +3 -3
  51. data/docs/doc/BooleanTermParser.html +3 -3
  52. data/docs/doc/Doing/ArrayCleanup.html +3 -3
  53. data/docs/doc/Doing/ArrayNestedHash.html +3 -3
  54. data/docs/doc/Doing/ArrayTags.html +9 -9
  55. data/docs/doc/Doing/ByDayExport.html +3 -3
  56. data/docs/doc/Doing/CSVExport.html +4 -4
  57. data/docs/doc/Doing/CalendarImport.html +4 -4
  58. data/docs/doc/Doing/Change.html +3 -3
  59. data/docs/doc/Doing/Changes.html +3 -3
  60. data/docs/doc/Doing/ChronifyArray.html +3 -3
  61. data/docs/doc/Doing/ChronifyNumeric.html +3 -3
  62. data/docs/doc/Doing/ChronifyString.html +6 -6
  63. data/docs/doc/Doing/Color.html +88 -47
  64. data/docs/doc/Doing/Completion/BashCompletions.html +3 -3
  65. data/docs/doc/Doing/Completion/FigCompletions.html +3 -3
  66. data/docs/doc/Doing/Completion/FishCompletions.html +3 -3
  67. data/docs/doc/Doing/Completion/StringUtils.html +3 -3
  68. data/docs/doc/Doing/Completion/ZshCompletions.html +3 -3
  69. data/docs/doc/Doing/Completion.html +5 -5
  70. data/docs/doc/Doing/Configuration.html +6 -6
  71. data/docs/doc/Doing/DayOneRenderer.html +3 -3
  72. data/docs/doc/Doing/DayoneExport.html +4 -4
  73. data/docs/doc/Doing/DoingExport.html +5 -5
  74. data/docs/doc/Doing/DoingImport.html +4 -4
  75. data/docs/doc/Doing/Entry.html +3 -3
  76. data/docs/doc/Doing/Errors/DoingNoTraceError.html +3 -3
  77. data/docs/doc/Doing/Errors/DoingRuntimeError.html +3 -3
  78. data/docs/doc/Doing/Errors/DoingStandardError.html +3 -3
  79. data/docs/doc/Doing/Errors/EmptyInput.html +3 -3
  80. data/docs/doc/Doing/Errors/HistoryLimitError.html +3 -3
  81. data/docs/doc/Doing/Errors/InvalidPlugin.html +3 -3
  82. data/docs/doc/Doing/Errors/MissingBackupFile.html +3 -3
  83. data/docs/doc/Doing/Errors/NoResults.html +3 -3
  84. data/docs/doc/Doing/Errors/PluginException.html +3 -3
  85. data/docs/doc/Doing/Errors/UserCancelled.html +3 -3
  86. data/docs/doc/Doing/Errors/WrongCommand.html +3 -3
  87. data/docs/doc/Doing/Errors.html +3 -3
  88. data/docs/doc/Doing/HTMLExport.html +4 -4
  89. data/docs/doc/Doing/Hooks.html +3 -16
  90. data/docs/doc/Doing/Item.html +4 -4
  91. data/docs/doc/Doing/ItemDates.html +3 -3
  92. data/docs/doc/Doing/ItemQuery.html +3 -3
  93. data/docs/doc/Doing/ItemState.html +3 -3
  94. data/docs/doc/Doing/ItemTags.html +3 -3
  95. data/docs/doc/Doing/Items.html +3 -3
  96. data/docs/doc/Doing/JSONExport.html +4 -4
  97. data/docs/doc/Doing/JSONImport.html +4 -4
  98. data/docs/doc/Doing/Logger.html +183 -3
  99. data/docs/doc/Doing/MarkdownExport.html +4 -4
  100. data/docs/doc/Doing/Note.html +3 -3
  101. data/docs/doc/Doing/Pager.html +54 -58
  102. data/docs/doc/Doing/Plugins.html +3 -3
  103. data/docs/doc/Doing/Prompt.html +4 -4
  104. data/docs/doc/Doing/PromptChoose.html +3 -3
  105. data/docs/doc/Doing/PromptFZF.html +3 -3
  106. data/docs/doc/Doing/PromptInput.html +3 -3
  107. data/docs/doc/Doing/PromptSTD.html +3 -3
  108. data/docs/doc/Doing/PromptYN.html +3 -3
  109. data/docs/doc/Doing/Section.html +3 -3
  110. data/docs/doc/Doing/StringHighlight.html +3 -3
  111. data/docs/doc/Doing/StringNormalize.html +3 -3
  112. data/docs/doc/Doing/StringQuery.html +4 -4
  113. data/docs/doc/Doing/StringTags.html +3 -3
  114. data/docs/doc/Doing/StringTransform.html +3 -3
  115. data/docs/doc/Doing/StringTruncate.html +3 -3
  116. data/docs/doc/Doing/StringURL.html +3 -3
  117. data/docs/doc/Doing/SymbolNormalize.html +5 -5
  118. data/docs/doc/Doing/TaskPaperExport.html +4 -4
  119. data/docs/doc/Doing/TemplateExport.html +5 -5
  120. data/docs/doc/Doing/TemplateString.html +7 -7
  121. data/docs/doc/Doing/TimingImport.html +4 -4
  122. data/docs/doc/Doing/Types.html +3 -3
  123. data/docs/doc/Doing/Util/Backup.html +4 -17
  124. data/docs/doc/Doing/Util.html +56 -61
  125. data/docs/doc/Doing/Version.html +3 -3
  126. data/docs/doc/Doing/WWID.html +6 -4
  127. data/docs/doc/Doing.html +4 -4
  128. data/docs/doc/FalseClass.html +3 -3
  129. data/docs/doc/GLI/Commands/Help.html +5 -5
  130. data/docs/doc/GLI/Commands/MarkdownDocumentListener.html +3 -3
  131. data/docs/doc/GLI/Commands.html +3 -3
  132. data/docs/doc/GLI.html +3 -3
  133. data/docs/doc/Hash.html +4 -4
  134. data/docs/doc/Numeric.html +3 -3
  135. data/docs/doc/Object.html +3 -3
  136. data/docs/doc/PhraseParser/Operator.html +3 -3
  137. data/docs/doc/PhraseParser/PhraseClause.html +3 -3
  138. data/docs/doc/PhraseParser/Query.html +3 -3
  139. data/docs/doc/PhraseParser/QueryParser.html +3 -3
  140. data/docs/doc/PhraseParser/QueryTransformer.html +3 -3
  141. data/docs/doc/PhraseParser/TermClause.html +3 -3
  142. data/docs/doc/PhraseParser.html +3 -3
  143. data/docs/doc/Status.html +3 -3
  144. data/docs/doc/String.html +51 -5
  145. data/docs/doc/Symbol.html +3 -3
  146. data/docs/doc/Time.html +3 -3
  147. data/docs/doc/TrueClass.html +3 -3
  148. data/docs/doc/_index.html +4 -4
  149. data/docs/doc/class_list.html +6 -3
  150. data/docs/doc/css/full_list.css +3 -3
  151. data/docs/doc/css/style.css +6 -0
  152. data/docs/doc/file.README.html +4 -4
  153. data/docs/doc/file_list.html +5 -2
  154. data/docs/doc/frames.html +1 -1
  155. data/docs/doc/index.html +4 -4
  156. data/docs/doc/js/app.js +294 -264
  157. data/docs/doc/js/full_list.js +30 -4
  158. data/docs/doc/method_list.html +443 -392
  159. data/docs/doc/top-level-namespace.html +3 -3
  160. data/doing.gemspec +2 -0
  161. data/doing.rdoc +1 -30
  162. data/example_plugin.rb +1 -4
  163. data/lib/completion/_doing.zsh +0 -4
  164. data/lib/completion/doing.bash +0 -11
  165. data/lib/completion/doing.fish +0 -5
  166. data/lib/completion/doing.ts +0 -40
  167. data/lib/doing/add_options.rb +2 -2
  168. data/lib/doing/array/cleanup.rb +2 -0
  169. data/lib/doing/array/nested_hash.rb +2 -0
  170. data/lib/doing/boolean_term_parser.rb +3 -3
  171. data/lib/doing/changelog/changes.rb +4 -5
  172. data/lib/doing/changelog/version.rb +8 -11
  173. data/lib/doing/chronify/array.rb +4 -4
  174. data/lib/doing/chronify/string.rb +5 -4
  175. data/lib/doing/cli_status.rb +7 -2
  176. data/lib/doing/colors.rb +93 -51
  177. data/lib/doing/completion/bash_completion.rb +36 -39
  178. data/lib/doing/completion/completion_string.rb +2 -1
  179. data/lib/doing/completion/fig_completion.rb +33 -33
  180. data/lib/doing/completion/fish_completion.rb +22 -23
  181. data/lib/doing/completion/zsh_completion.rb +5 -5
  182. data/lib/doing/completion.rb +7 -4
  183. data/lib/doing/configuration.rb +21 -13
  184. data/lib/doing/errors.rb +1 -4
  185. data/lib/doing/good.rb +1 -1
  186. data/lib/doing/hash.rb +4 -4
  187. data/lib/doing/help_monkey_patch.rb +1 -1
  188. data/lib/doing/hooks.rb +6 -2
  189. data/lib/doing/item/dates.rb +3 -1
  190. data/lib/doing/item/item.rb +1 -1
  191. data/lib/doing/item/query.rb +14 -14
  192. data/lib/doing/item/state.rb +2 -0
  193. data/lib/doing/logger.rb +113 -45
  194. data/lib/doing/markdown_document_listener.rb +25 -25
  195. data/lib/doing/normalize.rb +1 -1
  196. data/lib/doing/pager.rb +73 -29
  197. data/lib/doing/plugin_manager.rb +4 -5
  198. data/lib/doing/plugins/export/byday.rb +1 -1
  199. data/lib/doing/plugins/export/dayone_export.rb +28 -27
  200. data/lib/doing/plugins/export/doing_export.rb +1 -1
  201. data/lib/doing/plugins/export/html_export.rb +3 -3
  202. data/lib/doing/plugins/export/json_export.rb +4 -5
  203. data/lib/doing/plugins/export/markdown_export.rb +10 -2
  204. data/lib/doing/plugins/export/taskpaper_export.rb +1 -0
  205. data/lib/doing/plugins/export/template_export.rb +110 -107
  206. data/lib/doing/plugins/import/calendar_import.rb +1 -1
  207. data/lib/doing/plugins/import/doing_import.rb +2 -2
  208. data/lib/doing/plugins/import/timing_import.rb +3 -3
  209. data/lib/doing/prompt/choose.rb +5 -6
  210. data/lib/doing/prompt/fzf.rb +3 -2
  211. data/lib/doing/prompt/input.rb +10 -9
  212. data/lib/doing/prompt/yn.rb +9 -11
  213. data/lib/doing/string/tags.rb +7 -4
  214. data/lib/doing/string/transform.rb +15 -10
  215. data/lib/doing/string/truncate.rb +1 -0
  216. data/lib/doing/string/url.rb +1 -1
  217. data/lib/doing/template_string.rb +13 -9
  218. data/lib/doing/time.rb +4 -2
  219. data/lib/doing/util.rb +12 -11
  220. data/lib/doing/util_backup.rb +29 -26
  221. data/lib/doing/version.rb +3 -1
  222. data/lib/doing/wwid/display.rb +182 -151
  223. data/lib/doing/wwid/editor.rb +13 -12
  224. data/lib/doing/wwid/filetools.rb +13 -11
  225. data/lib/doing/wwid/filter.rb +41 -40
  226. data/lib/doing/wwid/guess.rb +6 -8
  227. data/lib/doing/wwid/interactive.rb +10 -10
  228. data/lib/doing/wwid/modify.rb +55 -55
  229. data/lib/doing/wwid/timers.rb +4 -5
  230. data/lib/doing/wwid/wwid.rb +0 -0
  231. data/lib/doing/wwid/wwidutil.rb +8 -10
  232. data/lib/examples/commands/wiki.rb +2 -0
  233. data/lib/examples/plugins/capture_thing_import.rb +1 -1
  234. data/lib/examples/plugins/say_export.rb +1 -2
  235. data/lib/examples/plugins/wiki_export/wiki_export.rb +3 -4
  236. data/lib/helpers/fzf/test/test_go.rb +5 -5
  237. data/lib/helpers/threaded_tests.rb +20 -20
  238. data/lib/helpers/threaded_tests_string.rb +2 -0
  239. data/rdoc_to_mmd.rb +5 -4
  240. data/rdocfixer.rb +1 -2
  241. data/scripts/deploy.rb +3 -4
  242. data/scripts/generate_bash_completions.rb +40 -43
  243. data/scripts/generate_fish_completions.rb +17 -15
  244. data/scripts/generate_zsh_completions.rb +9 -7
  245. data/scripts/setting_replace.rb +1 -0
  246. data/scripts/sort_commands.rb +4 -2
  247. data/yard_templates/default/method_details/setup.rb +3 -1
  248. metadata +3 -1
@@ -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
@@ -13,7 +13,9 @@ module Doing
13
13
  # @return [Items] Filtered Items array
14
14
  #
15
15
  def fuzzy_filter_items(items, query, case_type: :smart)
16
- scannable = items.map.with_index { |item, idx| "#{item.title} #{item.note.join(' ')}".gsub(/[|*?!]/, '') + "|#{idx}" }.join("\n")
16
+ scannable = items.map.with_index do |item, idx|
17
+ "#{item.title} #{item.note.join(' ')}".gsub(/[|*?!]/, '') + "|#{idx}"
18
+ end.join("\n")
17
19
 
18
20
  res = `echo #{Shellwords.escape(scannable)}|#{Prompt.fzf} #{fuzzy_filter_args(query, case_type).join(' ')}`
19
21
  selected = Items.new
@@ -59,59 +61,58 @@ module Doing
59
61
  ## @option opt [Array] :val (nil) Array of tag value queries
60
62
  ##
61
63
  def filter_items(items = Items.new, opt: {})
62
- logger.benchmark(:filter_items, :start)
63
- time_rx = /^(\d{1,2}+(:\d{1,2}+)?( *(am|pm))?|midnight|noon)$/i
64
+ logger.measure(:filter_items) do
65
+ time_rx = /^(\d{1,2}+(:\d{1,2}+)?( *(am|pm))?|midnight|noon)$/i
64
66
 
65
- if items.nil? || items.empty?
66
- section = !opt[:section] || opt[:section].empty? ? 'All' : guess_section(opt[:section])
67
- if section.is_a?(Array)
68
- section.each do |s|
69
- s = s[0] if s.is_a?(Array)
70
- items.concat(s =~ /^all$/i ? @content.clone : @content.in_section(s))
67
+ if items.nil? || items.empty?
68
+ section = !opt[:section] || opt[:section].empty? ? 'All' : guess_section(opt[:section])
69
+ if section.is_a?(Array)
70
+ section.each do |s|
71
+ s = s[0] if s.is_a?(Array)
72
+ items.concat(s =~ /^all$/i ? @content.clone : @content.in_section(s))
73
+ end
74
+ else
75
+ items = section =~ /^all$/i ? @content.clone : @content.in_section(section)
71
76
  end
72
- else
73
- items = section =~ /^all$/i ? @content.clone : @content.in_section(section)
74
77
  end
75
- end
76
78
 
77
- unless opt[:time_filter]
78
- opt[:time_filter] = [nil, nil]
79
- if opt[:from] && !opt[:date_filter]
80
- if opt[:from][0].is_a?(String) && opt[:from][0] =~ time_rx
81
- opt[:time_filter] = opt[:from]
82
- elsif opt[:from][0].is_a?(Time)
83
- opt[:date_filter] = opt[:from]
79
+ unless opt[:time_filter]
80
+ opt[:time_filter] = [nil, nil]
81
+ if opt[:from] && !opt[:date_filter]
82
+ if opt[:from][0].is_a?(String) && opt[:from][0] =~ time_rx
83
+ opt[:time_filter] = opt[:from]
84
+ elsif opt[:from][0].is_a?(Time)
85
+ opt[:date_filter] = opt[:from]
86
+ end
84
87
  end
85
88
  end
86
- end
87
89
 
88
- if opt[:before].is_a?(String) && opt[:before] =~ time_rx
89
- opt[:time_filter][1] = opt[:before]
90
- opt[:before] = nil
91
- end
92
-
93
- if opt[:after].is_a?(String) && opt[:after] =~ time_rx
94
- opt[:time_filter][0] = opt[:after]
95
- opt[:after] = nil
96
- end
90
+ if opt[:before].is_a?(String) && opt[:before] =~ time_rx
91
+ opt[:time_filter][1] = opt[:before]
92
+ opt[:before] = nil
93
+ end
97
94
 
98
- items.sort_by! { |item| [item.date, item.title.downcase] }.reverse
95
+ if opt[:after].is_a?(String) && opt[:after] =~ time_rx
96
+ opt[:time_filter][0] = opt[:after]
97
+ opt[:after] = nil
98
+ end
99
99
 
100
- filtered_items = items.select { |item| item.keep_item?(opt) }
100
+ items.sort_by! { |item| [item.date, item.title.downcase] }.reverse
101
101
 
102
- count = opt[:count].to_i&.positive? ? opt[:count].to_i : filtered_items.count
102
+ filtered_items = items.select { |item| item.keep_item?(opt) }
103
103
 
104
- output = Items.new
104
+ count = opt[:count].to_i&.positive? ? opt[:count].to_i : filtered_items.count
105
105
 
106
- if opt[:age] && opt[:age].normalize_age == :oldest
107
- output.concat(filtered_items.slice(0, count).reverse)
108
- else
109
- output.concat(filtered_items.reverse.slice(0, count))
110
- end
106
+ output = Items.new
111
107
 
112
- logger.benchmark(:filter_items, :finish)
108
+ if opt[:age] && opt[:age].normalize_age == :oldest
109
+ output.concat(filtered_items.slice(0, count).reverse)
110
+ else
111
+ output.concat(filtered_items.reverse.slice(0, count))
112
+ end
113
113
 
114
- output
114
+ output
115
+ end
115
116
  end
116
117
  end
117
118
  end
@@ -9,9 +9,7 @@ module Doing
9
9
  ## @param guessed [Boolean] already guessed and failed
10
10
  ##
11
11
  def guess_section(frag, guessed: false, suggest: false)
12
- if frag.is_a?(Array) && frag.count == 1
13
- frag = frag[0]
14
- end
12
+ frag = frag[0] if frag.is_a?(Array) && frag.count == 1
15
13
 
16
14
  frag = frag.split(/ *, */).map(&:strip) if frag.is_a?(String) && frag =~ /,/
17
15
 
@@ -25,7 +23,7 @@ module Doing
25
23
 
26
24
  found = @content.guess_section(frag, distance: 2)
27
25
 
28
- section = found ? found.title : nil
26
+ section = found&.title
29
27
 
30
28
  if section && suggest
31
29
  Doing.logger.debug('Match:', %(Assuming "#{section}" from "#{frag}"))
@@ -38,12 +36,12 @@ module Doing
38
36
  prompt = Color.template("{bw}Did you mean `{xy}doing {by}view {xy}#{alt}`{bw}?{x}")
39
37
  meant_view = Prompt.yn(prompt, default_response: 'n')
40
38
 
41
- msg = format('%<y>srun with `%<w>sdoing view %<alt>s%<y>s`', w: boldwhite, y: yellow, alt: alt)
39
+ msg = format('%<y>srun with `%<w>sdoing view %<alt>s%<y>s`', w: Color.boldwhite, y: Color.yellow, alt: alt)
42
40
  raise Errors::WrongCommand.new(msg, topic: 'Try again:') if meant_view
43
41
 
44
42
  end
45
43
 
46
- res = Prompt.yn("#{boldwhite}Section #{frag.yellow}#{boldwhite} not found, create it", default_response: 'n')
44
+ res = Prompt.yn("#{Color.boldwhite}Section #{Color.yellow(frag)}#{Color.boldwhite} not found, create it", default_response: 'n')
47
45
 
48
46
  if res
49
47
  @content.add_section(frag.cap_first, log: true)
@@ -53,7 +51,7 @@ module Doing
53
51
 
54
52
  raise Errors::InvalidSection.new("unknown section #{frag.bold.white}", topic: 'Missing:')
55
53
  end
56
- section ? section.cap_first : nil
54
+ section&.cap_first
57
55
  end
58
56
 
59
57
  ##
@@ -82,7 +80,7 @@ module Doing
82
80
  meant_view = Prompt.yn(prompt, default_response: 'n')
83
81
 
84
82
  if meant_view
85
- msg = format('%<y>srun with `%<w>sdoing show %<alt>s%<y>s`', w: boldwhite, y: yellow, alt: alt)
83
+ msg = format('%<y>srun with `%<w>sdoing show %<alt>s%<y>s`', w: Color.boldwhite, y: Color.yellow, alt: alt)
86
84
  raise Errors::WrongCommand.new(msg, topic: 'Try again:')
87
85
 
88
86
  end
@@ -25,14 +25,14 @@ module Doing
25
25
  opt[:query] = "!#{opt[:query]}" if opt[:query] && opt[:not]
26
26
  opt[:multiple] = true
27
27
  opt[:show_if_single] = true
28
- filter_options = %i[after before case date_filter from fuzzy not search section val].each_with_object({}) {
28
+ filter_options = %i[after before case date_filter from fuzzy not search section val].each_with_object({}) do
29
29
  |k, hsh| hsh[k] = opt[k]
30
- }
30
+ end
31
31
  items = filter_items(Items.new, opt: filter_options)
32
32
 
33
- menu_options = %i[search query exact multiple show_if_single menu sort case].each_with_object({}) {
34
- |k, hsh| hsh[k] = opt[k]
35
- }
33
+ menu_options = %i[search query exact multiple show_if_single menu sort case].each_with_object({}) do |k, hsh|
34
+ hsh[k] = opt[k]
35
+ end
36
36
  include_section = (opt[:section].is_a?(Array) && opt[:section][0] =~ /^all$/i) || (opt[:section].is_a?(String) && opt[:section] =~ /^all$/i)
37
37
 
38
38
  selection = Prompt.choose_from_items(items, include_section: include_section, **menu_options)
@@ -118,7 +118,7 @@ module Doing
118
118
  tags = type == 'add' ? all_tags(@content) : all_tags(items)
119
119
 
120
120
  add_msg = type == 'add' ? ', include values with tag(value)' : ''
121
- puts "#{yellow}Separate multiple tags with spaces, hit tab to complete known tags#{add_msg}"
121
+ puts "#{Color.yellow}Separate multiple tags with spaces, hit tab to complete known tags#{add_msg}"
122
122
  puts "#{boldgreen}Available tags: #{boldwhite}#{tags.sort.map(&:add_at).join(', ')}" if type == 'remove'
123
123
  tag = Prompt.read_line(prompt: "Tags to #{type}", completions: tags)
124
124
 
@@ -255,8 +255,8 @@ module Doing
255
255
 
256
256
  if opt[:editor]
257
257
  sleep 2 # This seems to be necessary between running fzf
258
- # and forking the editor, otherwise vim gets all
259
- # screwy and I can't figure out why
258
+ # and forking the editor, otherwise vim gets all
259
+ # screwy and I can't figure out why
260
260
  edit_items(items) # hooked
261
261
 
262
262
  write(@doing_file)
@@ -365,9 +365,9 @@ module Doing
365
365
  elapsed = finish_date - date
366
366
 
367
367
  if max_elapsed.positive? && (elapsed > max_elapsed)
368
- puts boldwhite(title) if title
368
+ puts Color.boldwhite(title) if title
369
369
  human = elapsed.time_string(format: :natural)
370
- res = Prompt.yn(yellow("Did this entry actually take #{human}"), default_response: true)
370
+ res = Prompt.yn(Color.yellow("Did this entry actually take #{human}"), default_response: true)
371
371
  unless res
372
372
  new_elapsed = Prompt.enter_text('How long did it take?').chronify_qty
373
373
  raise InvalidTimeExpression, 'Unrecognized time span entry' unless new_elapsed.positive?
@@ -19,7 +19,7 @@ module Doing
19
19
  opt ||= {}
20
20
  section ||= Doing.setting('current_section')
21
21
  @content.add_section(section, log: false)
22
- opt[:back] ||= opt[:date] ? opt[:date] : Time.now
22
+ opt[:back] ||= opt[:date] || Time.now
23
23
  opt[:date] ||= Time.now
24
24
  note = Note.new
25
25
  opt[:timed] ||= false
@@ -78,8 +78,8 @@ module Doing
78
78
  if finish_date
79
79
  item.tag('done', remove: true)
80
80
  item.tag('done', value: finish_date.strftime('%F %R'))
81
- else
82
- item.tag('done', remove: true) if resume
81
+ elsif resume
82
+ item.tag('done', remove: true)
83
83
  end
84
84
  logger.info('Reset:', %(Reset #{resume ? 'and resumed ' : ''} "#{item.title}" in #{item.section}))
85
85
  item
@@ -118,7 +118,7 @@ module Doing
118
118
  note = opt[:note] || Note.new
119
119
 
120
120
  if opt[:editor]
121
- start = opt[:date] ? opt[:date] : Time.now
121
+ start = opt[:date] || Time.now
122
122
  to_edit = "#{start.strftime('%F %R')} | #{title}"
123
123
  to_edit += "\n#{note.strip_lines.join("\n")}" unless note.empty?
124
124
  new_item = fork_editor(to_edit)
@@ -168,7 +168,8 @@ module Doing
168
168
  ##
169
169
  ## @see #filter_items
170
170
  ##
171
- def tag_last(opt) # hooked
171
+ # hooked
172
+ def tag_last(opt)
172
173
  opt ||= {}
173
174
  opt[:count] ||= 1
174
175
  opt[:archive] ||= false
@@ -186,11 +187,11 @@ module Doing
186
187
 
187
188
  if opt[:interactive]
188
189
  items = Prompt.choose_from_items(items, include_section: opt[:section] =~ /^all$/i, menu: true,
189
- header: '',
190
- prompt: 'Select entries to tag > ',
191
- multiple: true,
192
- sort: true,
193
- show_if_single: true)
190
+ header: '',
191
+ prompt: 'Select entries to tag > ',
192
+ multiple: true,
193
+ sort: true,
194
+ show_if_single: true)
194
195
 
195
196
  raise NoResults, 'no items selected' if items.empty?
196
197
 
@@ -201,9 +202,9 @@ module Doing
201
202
  if opt[:tags].empty? && !opt[:autotag]
202
203
  completions = opt[:remove] ? all_tags(items) : all_tags(@content)
203
204
  if opt[:remove]
204
- puts "#{yellow}Available tags: #{boldwhite}#{completions.map(&:add_at).join(', ')}"
205
+ puts "#{Color.yellow}Available tags: #{Color.boldwhite}#{completions.map(&:add_at).join(', ')}"
205
206
  else
206
- puts "#{yellow}Use tab to complete known tags"
207
+ puts "#{Color.yellow}Use tab to complete known tags"
207
208
  end
208
209
  opt[:tags] = Doing::Prompt.read_line(prompt: "Enter tag(s) to #{opt[:remove] ? 'remove' : 'add'}",
209
210
  completions: completions,
@@ -265,12 +266,12 @@ module Doing
265
266
  elapsed = done_date - item.date
266
267
 
267
268
  if max_elapsed.positive? && (elapsed > max_elapsed) && !opt[:took]
268
- puts boldwhite(item.title)
269
+ puts Color.boldwhite(item.title)
269
270
  human = elapsed.time_string(format: :natural)
270
- res = Prompt.yn(yellow("Did this actually take #{human}"), default_response: true)
271
+ res = Prompt.yn(Color.yellow("Did this actually take #{human}"), default_response: true)
271
272
  unless res
272
273
  new_elapsed = Prompt.enter_text('How long did it take?').chronify_qty
273
- raise InvalidTimeExpression, 'Unrecognized time span entry' unless new_elapsed > 0
274
+ raise InvalidTimeExpression, 'Unrecognized time span entry' unless new_elapsed.positive?
274
275
 
275
276
  opt[:took] = new_elapsed
276
277
  done_date = item.calculate_end_date(opt) if opt[:took]
@@ -289,7 +290,8 @@ module Doing
289
290
  end
290
291
  old_title = item.title.dup
291
292
  force = opt[:value].nil? ? false : true
292
- item.title.tag!(tag, remove: opt[:remove], rename_to: rename_to, regex: opt[:regex], value: opt[:value], force: force)
293
+ item.title.tag!(tag, remove: opt[:remove], rename_to: rename_to, regex: opt[:regex], value: opt[:value],
294
+ force: force)
293
295
  if old_title != item.title
294
296
  removed << tag
295
297
  added << rename_to if rename_to
@@ -310,7 +312,7 @@ module Doing
310
312
 
311
313
  item.note.add(opt[:note]) if opt[:note]
312
314
 
313
- if opt[:archive] && opt[:section] != 'Archive' && (opt[:count]).positive?
315
+ if opt[:archive] && opt[:section] != 'Archive' && opt[:count].positive?
314
316
  item.move_to('Archive', label: true)
315
317
  elsif opt[:archive] && opt[:count].zero?
316
318
  logger.warn('Skipped:', 'Archiving is skipped when operating on all entries')
@@ -376,7 +378,6 @@ module Doing
376
378
  Hooks.trigger :post_entry_updated, self, item, old_item
377
379
  end
378
380
 
379
-
380
381
  logger.debug('Skipped:', "No active @#{tag} tasks found.") if found_items.zero?
381
382
 
382
383
  if opt[:new_item]
@@ -398,7 +399,7 @@ module Doing
398
399
  ##
399
400
  def delete_items(items, force: false)
400
401
  items.slice(0, 5).each { |i| puts i.to_pretty } unless force
401
- puts softpurple("+ #{items.size - 5} additional #{'item'.to_p(items.size - 5)}") if items.size > 5 && !force
402
+ puts Color.softpurple("+ #{items.size - 5} additional #{'item'.to_p(items.size - 5)}") if items.size > 5 && !force
402
403
 
403
404
  res = force ? true : Prompt.yn("Delete #{items.size} #{'item'.to_p(items.size)}?", default_response: 'y')
404
405
  return unless res
@@ -431,12 +432,13 @@ module Doing
431
432
 
432
433
  destination = guess_section(destination)
433
434
 
434
- if @content.section?(destination) && (@content.section?(section) || archive_all)
435
- do_archive(section, destination, { count: count, tags: tags, bool: bool, search: options[:search], label: options[:label], before: options[:before], after: options[:after], from: options[:from] })
436
- write(doing_file)
437
- else
435
+ unless @content.section?(destination) && (@content.section?(section) || archive_all)
438
436
  raise InvalidArgument, 'Either source or destination does not exist'
439
437
  end
438
+
439
+ do_archive(section, destination,
440
+ { count: count, tags: tags, bool: bool, search: options[:search], label: options[:label], before: options[:before], after: options[:after], from: options[:from] })
441
+ write(doing_file)
440
442
  end
441
443
 
442
444
  ##
@@ -483,43 +485,41 @@ module Doing
483
485
  end
484
486
  end
485
487
 
486
- if Doing.setting('autotag.transform')
487
- Doing.setting('autotag.transform').each do |tag|
488
- next unless tag =~ /\S+:\S+/
488
+ Doing.setting('autotag.transform')&.each do |tag|
489
+ next unless tag =~ /\S+:\S+/
489
490
 
490
- if tag =~ /::/
491
- rx, r = tag.split(/::/)
492
- else
493
- rx, r = tag.split(/:/)
494
- end
491
+ if tag =~ /::/
492
+ rx, r = tag.split(/::/)
493
+ else
494
+ rx, r = tag.split(/:/)
495
+ end
495
496
 
496
- flag_rx = %r{/([r]+)$}
497
- if r =~ flag_rx
498
- flags = r.match(flag_rx)[1].split(//)
499
- r.sub!(flag_rx, '')
500
- end
501
- r.gsub!(/\$/, '\\')
502
- rx.sub!(/^@?/, '@')
503
- regex = Regexp.new("(?<= |\\A)#{rx}(?= |\\Z)")
497
+ flag_rx = %r{/(r+)$}
498
+ if r =~ flag_rx
499
+ flags = r.match(flag_rx)[1].split(//)
500
+ r.sub!(flag_rx, '')
501
+ end
502
+ r.gsub!(/\$/, '\\')
503
+ rx.sub!(/^@?/, '@')
504
+ regex = Regexp.new("(?<= |\\A)#{rx}(?= |\\Z)")
504
505
 
505
- text.sub!(regex) do
506
- m = Regexp.last_match
507
- new_tag = r
506
+ text.sub!(regex) do
507
+ m = Regexp.last_match
508
+ new_tag = r
508
509
 
509
- m.to_a.slice(1, m.length - 1).each_with_index do |v, idx|
510
- next if v.nil?
510
+ m.to_a.slice(1, m.length - 1).each_with_index do |v, idx|
511
+ next if v.nil?
511
512
 
512
- new_tag.gsub!("\\#{idx + 1}", v)
513
- end
514
- # Replace original tag if /r
515
- if flags&.include?('r')
516
- tagged[:replaced].concat(new_tag.split(/ /).map { |t| t.sub(/^@/, '') })
517
- new_tag.split(/ /).map { |t| t.sub(/^@?/, '@') }.join(' ')
518
- else
519
- tagged[:transformed].concat(new_tag.split(/ /).map { |t| t.sub(/^@/, '') })
520
- tagged[:transformed] = tagged[:transformed].uniq
521
- m[0]
522
- end
513
+ new_tag.gsub!("\\#{idx + 1}", v)
514
+ end
515
+ # Replace original tag if /r
516
+ if flags&.include?('r')
517
+ tagged[:replaced].concat(new_tag.split(/ /).map { |t| t.sub(/^@/, '') })
518
+ new_tag.split(/ /).map { |t| t.sub(/^@?/, '@') }.join(' ')
519
+ else
520
+ tagged[:transformed].concat(new_tag.split(/ /).map { |t| t.sub(/^@/, '') })
521
+ tagged[:transformed] = tagged[:transformed].uniq
522
+ m[0]
523
523
  end
524
524
  end
525
525
  end
@@ -69,13 +69,11 @@ EOTAIL
69
69
  pad = sorted_tags_data.map { |k, _| k }.group_by(&:size).max.last[0].length
70
70
  pad = 7 if pad < 7
71
71
  output = <<~EOHEADER
72
- | #{' ' * (pad - 7)}project | time |
73
- | #{'-' * (pad - 1)}: | :------- |
72
+ | #{' ' * (pad - 7)}project | time |
73
+ | #{'-' * (pad - 1)}: | :------- |
74
74
  EOHEADER
75
75
  sorted_tags_data.reverse.each do |k, v|
76
- if v.positive?
77
- output += "| #{' ' * (pad - k.length)}#{k} | #{v.time_string(format: :clock)} |\n"
78
- end
76
+ output += "| #{' ' * (pad - k.length)}#{k} | #{v.time_string(format: :clock)} |\n" if v.positive?
79
77
  end
80
78
  tail = '[Tag Totals]'
81
79
  output + tail
@@ -173,6 +171,7 @@ EOTAIL
173
171
  def record_tag_times(item, seconds)
174
172
  item_hash = "#{item.date.strftime('%s')}#{item.title}#{item.section}"
175
173
  return if @recorded_items.include?(item_hash)
174
+
176
175
  item.title.scan(/(?mi)@(\S+?)(\(.*\))?(?=\s|$)/).each do |m|
177
176
  k = m[0] == 'done' ? 'All' : m[0].downcase
178
177
  if @timers.key?(k)
File without changes
@@ -32,7 +32,7 @@ module Doing
32
32
  ##
33
33
  def import(paths, opt)
34
34
  opt ||= {}
35
- Plugins.plugins[:import].each do |_, options|
35
+ Plugins.plugins[:import].each_value do |options|
36
36
  next unless opt[:type] =~ /^(#{options[:trigger].normalize_trigger})$/i
37
37
 
38
38
  if paths.count.positive?
@@ -54,17 +54,15 @@ module Doing
54
54
  ## alternative config file
55
55
  ##
56
56
  def configure(filename = nil)
57
- logger.benchmark(:configure, :start)
57
+ logger.measure(:configure) do
58
+ if filename
59
+ Doing.config_with(filename, { ignore_local: true })
60
+ elsif ENV['DOING_CONFIG']
61
+ Doing.config_with(ENV['DOING_CONFIG'], { ignore_local: true })
62
+ end
58
63
 
59
- if filename
60
- Doing.config_with(filename, { ignore_local: true })
61
- elsif ENV['DOING_CONFIG']
62
- Doing.config_with(ENV['DOING_CONFIG'], { ignore_local: true })
64
+ Doing.set('backup_dir', ENV['DOING_BACKUP_DIR']) if ENV['DOING_BACKUP_DIR']
63
65
  end
64
-
65
- logger.benchmark(:configure, :finish)
66
-
67
- Doing.set('backup_dir', ENV['DOING_BACKUP_DIR']) if ENV['DOING_BACKUP_DIR']
68
66
  end
69
67
 
70
68
  ##
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  desc 'Output a tag wiki'
2
4
  command :wiki do |c|
3
5
  c.desc 'Section to rotate'
@@ -50,7 +50,7 @@ module Doing
50
50
  new_items = wwid.filter_items(new_items, opt: options)
51
51
 
52
52
  skipped = total - new_items.count
53
- Doing.logger.debug('Skipped:' , %(#{skipped} items that didn't match filter criteria)) if skipped.positive?
53
+ Doing.logger.debug('Skipped:', %(#{skipped} items that didn't match filter criteria)) if skipped.positive?
54
54
 
55
55
  imported = []
56
56
 
@@ -81,7 +81,6 @@ module Doing
81
81
  }
82
82
  end
83
83
 
84
-
85
84
  #-------------------------------------------------------
86
85
  ## Output a template. Only required if template(s) are
87
86
  ## included in settings. The method should return a
@@ -100,10 +99,10 @@ module Doing
100
99
  ##
101
100
  def self.template(trigger)
102
101
  return unless trigger =~ /^say(it)?$/
102
+
103
103
  'On %date, you were %title, recorded in section %section%took'
104
104
  end
105
105
 
106
-
107
106
  ##
108
107
  ## Render data received from an output
109
108
  ## command