doing 2.1.40 → 2.1.41

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 (192) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -1
  3. data/CHANGELOG.md +22 -0
  4. data/Gemfile.lock +1 -1
  5. data/Rakefile +4 -4
  6. data/bin/commands/changes.rb +1 -1
  7. data/bin/commands/tag_dir.rb +49 -15
  8. data/{Dockerfile → docker/Dockerfile} +3 -1
  9. data/{Dockerfile-2.6 → docker/Dockerfile-2.6} +2 -2
  10. data/{Dockerfile-2.7 → docker/Dockerfile-2.7} +2 -2
  11. data/{Dockerfile-3.0 → docker/Dockerfile-3.0} +2 -2
  12. data/{bash_profile → docker/bash_profile} +0 -0
  13. data/{inputrc → docker/inputrc} +0 -0
  14. data/docs/doc/Array.html +84 -2
  15. data/docs/doc/BooleanTermParser/Clause.html +1 -1
  16. data/docs/doc/BooleanTermParser/Operator.html +1 -1
  17. data/docs/doc/BooleanTermParser/Query.html +1 -1
  18. data/docs/doc/BooleanTermParser/QueryParser.html +1 -1
  19. data/docs/doc/BooleanTermParser/QueryTransformer.html +1 -1
  20. data/docs/doc/BooleanTermParser.html +1 -1
  21. data/docs/doc/Doing/ArrayNestedHash.html +198 -0
  22. data/docs/doc/Doing/ArrayTags.html +424 -0
  23. data/docs/doc/Doing/CSVExport.html +266 -0
  24. data/docs/doc/Doing/CalendarImport.html +232 -0
  25. data/docs/doc/Doing/Change.html +617 -0
  26. data/docs/doc/Doing/Changes.html +468 -0
  27. data/docs/doc/Doing/ChronifyArray.html +347 -0
  28. data/docs/doc/Doing/ChronifyNumeric.html +271 -0
  29. data/docs/doc/Doing/ChronifyString.html +682 -0
  30. data/docs/doc/Doing/Color.html +2 -2
  31. data/docs/doc/Doing/Completion/BashCompletions.html +445 -0
  32. data/docs/doc/Doing/Completion/FishCompletions.html +445 -0
  33. data/docs/doc/Doing/Completion/StringUtils.html +229 -0
  34. data/docs/doc/Doing/Completion/ZshCompletions.html +445 -0
  35. data/docs/doc/Doing/Completion.html +17 -3
  36. data/docs/doc/Doing/Configuration.html +1 -1
  37. data/docs/doc/Doing/DayOneRenderer.html +383 -0
  38. data/docs/doc/Doing/DayoneExport.html +290 -0
  39. data/docs/doc/Doing/DoingImport.html +391 -0
  40. data/docs/doc/Doing/Entry.html +381 -0
  41. data/docs/doc/Doing/Errors/DoingNoTraceError.html +1 -1
  42. data/docs/doc/Doing/Errors/DoingRuntimeError.html +1 -1
  43. data/docs/doc/Doing/Errors/DoingStandardError.html +1 -1
  44. data/docs/doc/Doing/Errors/EmptyInput.html +1 -1
  45. data/docs/doc/Doing/Errors/HistoryLimitError.html +1 -1
  46. data/docs/doc/Doing/Errors/InvalidPlugin.html +1 -1
  47. data/docs/doc/Doing/Errors/MissingBackupFile.html +1 -1
  48. data/docs/doc/Doing/Errors/NoResults.html +1 -1
  49. data/docs/doc/Doing/Errors/PluginException.html +1 -1
  50. data/docs/doc/Doing/Errors/UserCancelled.html +1 -1
  51. data/docs/doc/Doing/Errors/WrongCommand.html +1 -1
  52. data/docs/doc/Doing/Errors.html +1 -1
  53. data/docs/doc/Doing/HTMLExport.html +256 -0
  54. data/docs/doc/Doing/Hooks.html +1 -1
  55. data/docs/doc/Doing/Item.html +47 -3
  56. data/docs/doc/Doing/ItemDates.html +564 -0
  57. data/docs/doc/Doing/ItemQuery.html +614 -0
  58. data/docs/doc/Doing/ItemState.html +387 -0
  59. data/docs/doc/Doing/ItemTags.html +498 -0
  60. data/docs/doc/Doing/Items.html +460 -11
  61. data/docs/doc/Doing/JSONExport.html +222 -0
  62. data/docs/doc/Doing/Logger.html +1 -1
  63. data/docs/doc/Doing/MarkdownExport.html +266 -0
  64. data/docs/doc/Doing/MarkdownRenderer.html +383 -0
  65. data/docs/doc/Doing/Note.html +16 -3
  66. data/docs/doc/Doing/Pager.html +1 -1
  67. data/docs/doc/Doing/Plugins.html +1 -1
  68. data/docs/doc/Doing/Prompt.html +31 -682
  69. data/docs/doc/Doing/PromptChoose.html +484 -0
  70. data/docs/doc/Doing/PromptFZF.html +391 -0
  71. data/docs/doc/Doing/PromptInput.html +572 -0
  72. data/docs/doc/Doing/PromptSTD.html +293 -0
  73. data/docs/doc/Doing/PromptYN.html +237 -0
  74. data/docs/doc/Doing/Section.html +58 -2
  75. data/docs/doc/Doing/StringHighlight.html +533 -0
  76. data/docs/doc/Doing/StringNormalize.html +929 -0
  77. data/docs/doc/Doing/StringQuery.html +725 -0
  78. data/docs/doc/Doing/StringTags.html +884 -0
  79. data/docs/doc/Doing/StringTransform.html +565 -0
  80. data/docs/doc/Doing/StringTruncate.html +448 -0
  81. data/docs/doc/Doing/StringURL.html +409 -0
  82. data/docs/doc/Doing/SymbolNormalize.html +341 -0
  83. data/docs/doc/Doing/TaskPaperExport.html +222 -0
  84. data/docs/doc/Doing/TemplateExport.html +249 -0
  85. data/docs/doc/Doing/TemplateString.html +101 -2
  86. data/docs/doc/Doing/TimingImport.html +285 -0
  87. data/docs/doc/Doing/Types.html +1 -1
  88. data/docs/doc/Doing/Util/Backup.html +9 -7
  89. data/docs/doc/Doing/Util.html +2 -2
  90. data/docs/doc/Doing/Version.html +523 -0
  91. data/docs/doc/Doing/WWID/WWIDUtil.html +510 -0
  92. data/docs/doc/Doing/WWID.html +4377 -217
  93. data/docs/doc/Doing/WWIDDisplay.html +865 -0
  94. data/docs/doc/Doing/WWIDEditor.html +466 -0
  95. data/docs/doc/Doing/WWIDFileTools.html +359 -0
  96. data/docs/doc/Doing/WWIDFilter.html +466 -0
  97. data/docs/doc/Doing/WWIDGuess.html +299 -0
  98. data/docs/doc/Doing/WWIDInteractive.html +752 -0
  99. data/docs/doc/Doing/WWIDModify.html +1078 -0
  100. data/docs/doc/Doing/WWIDTags.html +302 -0
  101. data/docs/doc/Doing/WWIDTimers.html +359 -0
  102. data/docs/doc/Doing/WWIDUtil.html +510 -0
  103. data/docs/doc/Doing.html +9 -6
  104. data/docs/doc/FalseClass.html +1 -1
  105. data/docs/doc/GLI/Commands/Help.html +1 -1
  106. data/docs/doc/GLI/Commands/MarkdownDocumentListener.html +1 -1
  107. data/docs/doc/GLI/Commands.html +1 -1
  108. data/docs/doc/GLI.html +1 -1
  109. data/docs/doc/Hash.html +1 -1
  110. data/docs/doc/Numeric.html +23 -78
  111. data/docs/doc/Object.html +1 -1
  112. data/docs/doc/PhraseParser/Operator.html +1 -1
  113. data/docs/doc/PhraseParser/PhraseClause.html +1 -1
  114. data/docs/doc/PhraseParser/Query.html +1 -1
  115. data/docs/doc/PhraseParser/QueryParser.html +1 -1
  116. data/docs/doc/PhraseParser/QueryTransformer.html +1 -1
  117. data/docs/doc/PhraseParser/TermClause.html +1 -1
  118. data/docs/doc/PhraseParser.html +1 -1
  119. data/docs/doc/Status.html +1 -1
  120. data/docs/doc/String.html +58 -633
  121. data/docs/doc/Symbol.html +9 -224
  122. data/docs/doc/Time.html +119 -13
  123. data/docs/doc/TrueClass.html +1 -1
  124. data/docs/doc/_index.html +324 -8
  125. data/docs/doc/class_list.html +1 -1
  126. data/docs/doc/file.README.html +1 -1
  127. data/docs/doc/index.html +1 -1
  128. data/docs/doc/method_list.html +2326 -542
  129. data/docs/doc/top-level-namespace.html +2 -2
  130. data/doing.rdoc +13 -3
  131. data/lib/completion/_doing.zsh +1 -1
  132. data/lib/completion/doing.bash +2 -2
  133. data/lib/completion/doing.fish +3 -1
  134. data/lib/doing/array/array.rb +16 -12
  135. data/lib/doing/array/nested_hash.rb +1 -1
  136. data/lib/doing/array/tags.rb +6 -5
  137. data/lib/doing/changelog/changelog.rb +6 -0
  138. data/lib/doing/chronify/array.rb +1 -3
  139. data/lib/doing/chronify/chronify.rb +12 -0
  140. data/lib/doing/chronify/numeric.rb +3 -2
  141. data/lib/doing/chronify/string.rb +1 -1
  142. data/lib/doing/completion/completion_string.rb +25 -0
  143. data/lib/doing/completion.rb +1 -1
  144. data/lib/doing/good.rb +8 -0
  145. data/lib/doing/item/dates.rb +1 -1
  146. data/lib/doing/{item.rb → item/item.rb} +10 -5
  147. data/lib/doing/item/query.rb +1 -1
  148. data/lib/doing/item/state.rb +1 -1
  149. data/lib/doing/item/tags.rb +1 -1
  150. data/lib/doing/items/filter.rb +67 -0
  151. data/lib/doing/items/items.rb +57 -0
  152. data/lib/doing/items/modify.rb +36 -0
  153. data/lib/doing/items/sections.rb +83 -0
  154. data/lib/doing/items/util.rb +74 -0
  155. data/lib/doing/normalize.rb +10 -2
  156. data/lib/doing/plugins/export/markdown_export.rb +4 -2
  157. data/lib/doing/plugins/import/doing_import.rb +1 -1
  158. data/lib/doing/prompt/choose.rb +118 -0
  159. data/lib/doing/prompt/fzf.rb +84 -0
  160. data/lib/doing/prompt/input.rb +129 -0
  161. data/lib/doing/prompt/prompt.rb +41 -0
  162. data/lib/doing/prompt/std.rb +32 -0
  163. data/lib/doing/prompt/yn.rb +64 -0
  164. data/lib/doing/section.rb +4 -0
  165. data/lib/doing/string/highlight.rb +1 -1
  166. data/lib/doing/string/query.rb +1 -1
  167. data/lib/doing/string/string.rb +18 -7
  168. data/lib/doing/string/tags.rb +14 -3
  169. data/lib/doing/string/transform.rb +1 -1
  170. data/lib/doing/string/truncate.rb +1 -1
  171. data/lib/doing/string/url.rb +1 -1
  172. data/lib/doing/time.rb +19 -1
  173. data/lib/doing/util_backup.rb +2 -2
  174. data/lib/doing/version.rb +1 -1
  175. data/lib/doing/wwid/display.rb +357 -360
  176. data/lib/doing/wwid/editor.rb +173 -176
  177. data/lib/doing/wwid/filetools.rb +156 -159
  178. data/lib/doing/wwid/filter.rb +191 -183
  179. data/lib/doing/wwid/guess.rb +58 -60
  180. data/lib/doing/wwid/interactive.rb +332 -330
  181. data/lib/doing/wwid/modify.rb +509 -512
  182. data/lib/doing/wwid/tags.rb +38 -41
  183. data/lib/doing/wwid/timers.rb +293 -296
  184. data/lib/doing/{wwid.rb → wwid/wwid.rb} +32 -23
  185. data/lib/doing/wwid/wwidutil.rb +79 -82
  186. data/lib/doing.rb +5 -5
  187. data/lib/helpers/threaded_tests.rb +1 -0
  188. metadata +76 -14
  189. data/lib/doing/changelog.rb +0 -6
  190. data/lib/doing/completion/string.rb +0 -17
  191. data/lib/doing/items.rb +0 -221
  192. data/lib/doing/prompt.rb +0 -330
@@ -86,7 +86,7 @@
86
86
 
87
87
 
88
88
 
89
- <strong class="classes">Classes:</strong> <span class='object_link'><a href="Array.html" title="Array (class)">Array</a></span>, <span class='object_link'><a href="FalseClass.html" title="FalseClass (class)">FalseClass</a></span>, <span class='object_link'><a href="Hash.html" title="Hash (class)">Hash</a></span>, <span class='object_link'><a href="Object.html" title="Object (class)">Object</a></span>, <span class='object_link'><a href="String.html" title="String (class)">String</a></span>, <span class='object_link'><a href="Symbol.html" title="Symbol (class)">Symbol</a></span>, <span class='object_link'><a href="Time.html" title="Time (class)">Time</a></span>, <span class='object_link'><a href="TrueClass.html" title="TrueClass (class)">TrueClass</a></span>
89
+ <strong class="classes">Classes:</strong> <span class='object_link'><a href="Array.html" title="Array (class)">Array</a></span>, <span class='object_link'><a href="FalseClass.html" title="FalseClass (class)">FalseClass</a></span>, <span class='object_link'><a href="Hash.html" title="Hash (class)">Hash</a></span>, <span class='object_link'><a href="Numeric.html" title="Numeric (class)">Numeric</a></span>, <span class='object_link'><a href="Object.html" title="Object (class)">Object</a></span>, <span class='object_link'><a href="String.html" title="String (class)">String</a></span>, <span class='object_link'><a href="Symbol.html" title="Symbol (class)">Symbol</a></span>, <span class='object_link'><a href="Time.html" title="Time (class)">Time</a></span>, <span class='object_link'><a href="TrueClass.html" title="TrueClass (class)">TrueClass</a></span>
90
90
 
91
91
 
92
92
  </p>
@@ -206,7 +206,7 @@
206
206
  </div>
207
207
 
208
208
  <div id="footer">
209
- Generated on Tue Mar 15 11:14:28 2022 by
209
+ Generated on Wed Mar 16 09:42:15 2022 by
210
210
  <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
211
211
  0.9.27 (ruby-3.0.1).
212
212
  </div>
data/doing.rdoc CHANGED
@@ -5,7 +5,7 @@ record of what you've been doing, complete with tag-based time tracking. The
5
5
  command line tool allows you to add entries, annotate with tags and notes, and
6
6
  view your entries with myriad options, with a focus on a "natural" language syntax.
7
7
 
8
- v2.1.40
8
+ v2.1.41
9
9
 
10
10
  === Global Options
11
11
  === --config_file arg
@@ -439,7 +439,7 @@ Look up a specific version. Specify versions as "MAJ.MIN.PATCH", MIN
439
439
  to a version. Wildcards (*?) accepted unless using < or >.
440
440
 
441
441
  [Default Value] None
442
- [Must Match] (?-mix:^(?:(?:(?:[<>=]+|p(?:rior)|b(?:efore)|o(?:lder)|s(?:ince)|a(?:fter)|n(?:ewer))? *[0-9.*?]+ *)+|(?:[\d.]+ *(?:-|to)+ *[0-9.]+))$)
442
+ [Must Match] (?-mix:^(?:(?:(?:[<>=]+|p(?:rior)|b(?:efore)|o(?:lder)|s(?:ince)|a(?:fter)|n(?:ewer))? *[0-9.*?]{1,10} *)+|(?:[\d.]+ *(?:-|to)+ *[0-9.]{1,10}))$)
443
443
 
444
444
 
445
445
  ===== -s|--search arg
@@ -2628,11 +2628,21 @@ Adds default_tags to a .doingrc file in the current directory. Any entry created
2628
2628
  subdirectories will be tagged with the default tags. You can modify these any time using the `config set` commnand or
2629
2629
  manually editing the .doingrc file.
2630
2630
  ===== Options
2631
- ===== -r|--remove
2631
+ ===== --clear
2632
2632
  Remove all default_tags from the local .doingrc
2633
2633
 
2634
2634
 
2635
2635
 
2636
+ ===== -e|--editor
2637
+ Use default editor to edit tag list
2638
+
2639
+
2640
+
2641
+ ===== -r|--remove
2642
+ Delete tag(s) from the current list
2643
+
2644
+
2645
+
2636
2646
  ==== Command: <tt>tags [MAX_COUNT]</tt>
2637
2647
  List all tags in the current Doing file
2638
2648
 
@@ -183,7 +183,7 @@ function _doing() {
183
183
  args=( {'(--autotag)-a','(-a)--autotag'}"[Autotag entries based on autotag configuration in ~/]" "--bool[Boolean used to combine multiple tags]:BOOLEAN:" {'(--count)-c','(-c)--count'}"[How many recent entries to tag]:COUNT:" "--case[Case sensitivity for search string matching ((c)ase-sensitive]:TYPE:" {'(--date)-d','(-d)--date'}"[Include current date/time with tag]" "--force[Dont ask permission to tag all entries when count is 0t ask permission to tag all entries when count is 0]" {'(--interactive)-i','(-i)--interactive'}"[Select item(s) to tag from a menu of matching entries]" "--not[Tag items that *dont* match search/tag filterst* match search/tag filters]" {'(--remove)-r','(-r)--remove'}"[Remove given tag(s)]" "--regex[Interpret tag string as regular expression]" "--rename[Replace existing tag with tag argument]:ORIG_TAG:" {'(--section)-s','(-s)--section'}"[Section]:SECTION_NAME:" "--search[Filter entries using a search query]:QUERY:" "--tag[Filter entries by tag]:TAG:" {'(--unfinished)-u','(-u)--unfinished'}"[Tag last entry]" {'(--value)-v','(-v)--value'}"[Include a value]:VALUE:" "--val[Perform a tag value query]:QUERY:" {'(--exact)-x','(-x)--exact'}"[Force exact search string matching]" )
184
184
  ;;
185
185
  tag_dir)
186
- args=( {'(--remove)-r','(-r)--remove'}"[Remove all default_tags from the local]" )
186
+ args=( "--clear[Remove all default_tags from the local]" {'(--editor)-e','(-e)--editor'}"[Use default editor to edit tag list]" {'(--remove)-r','(-r)--remove'}"[Delete tag(s) from the current list]" )
187
187
  ;;
188
188
  tags)
189
189
  args=( "--bool[Boolean used to combine multiple tags]:BOOLEAN:" {'(--counts)-c','(-c)--counts'}"[Show count of occurrences]" "--case[Case sensitivity for search string matching ((c)ase-sensitive]:TYPE:" {'(--interactive)-i','(-i)--interactive'}"[Select items to scan from a menu of matching entries]" {'(--line)-l','(-l)--line'}"[Output in a single line with @ symbols]" "--not[Show items that *dont* match search/tag filterst* match search/tag filters]" {'(--order)-o','(-o)--order'}"[Sort order]:ORDER:" {'(--section)-s','(-s)--section'}"[Section]:SECTION_NAME:" "--search[Filter entries using a search query]:QUERY:" "--sort[Sort by name or count]:SORT_ORDER:" "--tag[Filter entries by tag]:TAG:" "--val[Perform a tag value query]:QUERY:" {'(--exact)-x','(-x)--exact'}"[Force exact search string matching]" )
@@ -321,9 +321,9 @@ _doing_tag() {
321
321
  _doing_tag_dir() {
322
322
 
323
323
  if [[ "$token" == --* ]]; then
324
- COMPREPLY=( $( compgen -W '--remove' -- $token ) )
324
+ COMPREPLY=( $( compgen -W '--clear --editor --remove' -- $token ) )
325
325
  elif [[ "$token" == -* ]]; then
326
- COMPREPLY=( $( compgen -W '-r --remove' -- $token ) )
326
+ COMPREPLY=( $( compgen -W '-e -r --clear --editor --remove' -- $token ) )
327
327
 
328
328
  fi
329
329
  }
@@ -505,7 +505,9 @@ complete -c doing -l unfinished -s u -f -n '__fish_doing_using_command tag' -d
505
505
  complete -c doing -l value -s v -f -r -n '__fish_doing_using_command tag' -d Include\ a\ value
506
506
  complete -c doing -l val -f -r -n '__fish_doing_using_command tag' -d Perform\ a\ tag\ value\ query
507
507
  complete -c doing -l exact -s x -f -n '__fish_doing_using_command tag' -d Force\ exact\ search\ string\ matching
508
- complete -c doing -l remove -s r -f -n '__fish_doing_using_command tag_dir' -d Remove\ all\ default_tags\ from\ the\ local
508
+ complete -c doing -l clear -f -n '__fish_doing_using_command tag_dir' -d Remove\ all\ default_tags\ from\ the\ local
509
+ complete -c doing -l editor -s e -f -n '__fish_doing_using_command tag_dir' -d Use\ default\ editor\ to\ edit\ tag\ list
510
+ complete -c doing -l remove -s r -f -n '__fish_doing_using_command tag_dir' -d Delete\ tag\(s\)\ from\ the\ current\ list
509
511
  complete -c doing -l bool -f -r -n '__fish_doing_using_command tags' -d Boolean\ used\ to\ combine\ multiple\ tags
510
512
  complete -c doing -l counts -s c -f -n '__fish_doing_using_command tags' -d Show\ count\ of\ occurrences
511
513
  complete -c doing -l case -f -r -n '__fish_doing_using_command tags' -d Case\ sensitivity\ for\ search\ string\ matching\ \[\(c\)ase-sensitive
@@ -3,18 +3,22 @@
3
3
  require_relative 'tags'
4
4
  require_relative 'nested_hash'
5
5
 
6
- class ::Array
7
- ##
8
- ## Force UTF-8 encoding of strings in array
9
- ##
10
- ## @return [Array] Encoded lines
11
- ##
12
- def utf8
13
- c = self.class
14
- if String.method_defined? :force_encoding
15
- replace c.new(map(&:utf8))
16
- else
17
- self
6
+ module Doing
7
+ class ::Array
8
+ include ArrayTags
9
+ include ArrayNestedHash
10
+ ##
11
+ ## Force UTF-8 encoding of strings in array
12
+ ##
13
+ ## @return [Array] Encoded lines
14
+ ##
15
+ def utf8
16
+ c = self.class
17
+ if String.method_defined? :force_encoding
18
+ replace c.new(map(&:utf8))
19
+ else
20
+ self
21
+ end
18
22
  end
19
23
  end
20
24
  end
@@ -2,7 +2,7 @@ module Doing
2
2
  ##
3
3
  ## Array helpers
4
4
  ##
5
- class ::Array
5
+ module ArrayNestedHash
6
6
  ##
7
7
  ## Convert array to nested hash, setting last key to value
8
8
  ##
@@ -4,12 +4,14 @@ module Doing
4
4
  ##
5
5
  ## Array helpers
6
6
  ##
7
- class ::Array
7
+ module ArrayTags
8
8
  ##
9
9
  ## Convert an array of @tags to plain strings
10
10
  ##
11
- ## @return [Array] array of strings
11
+ ## @return [Array] array of strings without @ symbols
12
12
  ##
13
+ ## @example Convert an array of tags to strings
14
+ ## ['@one', '@two', 'three'].to_tags => ['one', 'two', 'three']
13
15
  def tags_to_array
14
16
  map(&:remove_at).map(&:strip)
15
17
  end
@@ -18,9 +20,8 @@ module Doing
18
20
  #
19
21
  # @return [Array] Array of @tags
20
22
  #
21
- # @example
22
- # ['one', '@two', 'three'].to_tags
23
- # # => ['@one', '@two', '@three']
23
+ # @example Convert an array of strings with or without @ symbols
24
+ # ['one', '@two', 'three'].to_tags => ['@one', '@two', '@three']
24
25
  def to_tags
25
26
  map(&:add_at)
26
27
  end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'version'
4
+ require_relative 'entry'
5
+ require_relative 'change'
6
+ require_relative 'changes'
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Doing
4
4
  # Chronify array helpers
5
- class ::Array
5
+ module ChronifyArray
6
6
  # Convert [d, h, m] to [y, d, h, m]
7
7
  def to_years
8
8
  d, h, m = self
@@ -56,8 +56,6 @@ module Doing
56
56
  ##
57
57
  ## Format [d, h, m] as string
58
58
  ##
59
- ## @accept [Array] Array of [days, hours, minutes]
60
- ##
61
59
  ## @param format [Symbol] The format, :dhm, :hm,
62
60
  ## :m, :clock, :natural
63
61
  ## @return [String] formatted string
@@ -3,3 +3,15 @@
3
3
  require_relative 'array'
4
4
  require_relative 'numeric'
5
5
  require_relative 'string'
6
+
7
+ class ::String
8
+ include Doing::ChronifyString
9
+ end
10
+
11
+ class ::Array
12
+ include Doing::ChronifyArray
13
+ end
14
+
15
+ class ::Numeric
16
+ include Doing::ChronifyNumeric
17
+ end
@@ -4,11 +4,12 @@ module Doing
4
4
  ##
5
5
  ## Number helpers
6
6
  ##
7
- class ::Numeric
7
+ module ChronifyNumeric
8
8
  ##
9
9
  ## Format human readable time from seconds
10
10
  ##
11
- ## @param seconds [Integer] Seconds
11
+ ## @param human [Boolean] if True, don't convert
12
+ ## hours into days
12
13
  ##
13
14
  def format_time(human: false)
14
15
  return [0, 0, 0] if nil?
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Doing
4
4
  # Chronify methods for strings
5
- class ::String
5
+ module ChronifyString
6
6
  ##
7
7
  ## Converts input string into a Time object when input
8
8
  ## takes on the following formats:
@@ -0,0 +1,25 @@
1
+ module Doing
2
+ module Completion
3
+ module StringUtils
4
+ def short_desc
5
+ split(/[,.]/)[0].sub(/ \(.*?\)?$/, '').strip
6
+ end
7
+
8
+ def ltrunc(max)
9
+ if length > max
10
+ sub(/^.*?(.{#{max - 3}})$/, '...\1')
11
+ else
12
+ self
13
+ end
14
+ end
15
+
16
+ def ltrunc!(max)
17
+ replace ltrunc(max)
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ class ::String
24
+ include Doing::Completion::StringUtils
25
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'tty-progressbar'
4
4
 
5
- require_relative 'completion/string'
5
+ require_relative 'completion/completion_string'
6
6
  require_relative 'completion/fish_completion'
7
7
  require_relative 'completion/zsh_completion'
8
8
  require_relative 'completion/bash_completion'
data/lib/doing/good.rb CHANGED
@@ -1,6 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Doing
4
+ # Numeric helpers
5
+ class ::Numeric
6
+ # Test of number is positive
7
+ def good?
8
+ positive?
9
+ end
10
+ end
11
+
4
12
  # Object helpers
5
13
  class ::Object
6
14
  ##
@@ -1,5 +1,5 @@
1
1
  module Doing
2
- class Item
2
+ module ItemDates
3
3
  # def date=(new_date)
4
4
  # @date = new_date.is_a?(Time) ? new_date : Time.parse(new_date)
5
5
  # end
@@ -1,15 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'item/dates'
4
- require_relative 'item/tags'
5
- require_relative 'item/state'
6
- require_relative 'item/query'
3
+ require_relative 'dates'
4
+ require_relative 'tags'
5
+ require_relative 'state'
6
+ require_relative 'query'
7
7
 
8
8
  module Doing
9
9
  ##
10
10
  ## This class describes a single WWID item
11
11
  ##
12
12
  class Item
13
+ include ItemDates
14
+ include ItemQuery
15
+ include ItemState
16
+ include ItemTags
17
+
13
18
  attr_accessor :date, :title, :section, :note
14
19
 
15
20
  # attr_reader :id
@@ -56,7 +61,7 @@ module Doing
56
61
 
57
62
  return false unless @note.equal?(other.note)
58
63
 
59
- return false if match_section && @section != other.section
64
+ return false if match_section && !@section.equal?(other.section)
60
65
 
61
66
  true
62
67
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Doing
4
4
  # Tag and search filtering for a Doing entry
5
- class Item
5
+ module ItemQuery
6
6
  ##
7
7
  ## Test if item contains tag(s)
8
8
  ##
@@ -1,6 +1,6 @@
1
1
  module Doing
2
2
  # State queries for a Doing entry
3
- class Item
3
+ module ItemState
4
4
  ##
5
5
  ## Test if item has a @done tag
6
6
  ##
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Doing
4
4
  # A Doing entry
5
- class Item
5
+ module ItemTags
6
6
  ##
7
7
  ## Add (or remove) tags from the title of the item
8
8
  ##
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doing
4
+ class Items < Array
5
+ # Get a new Items object containing only items in a
6
+ # specified section
7
+ #
8
+ # @param section [String] section title
9
+ #
10
+ # @return [Items] Array of items
11
+ #
12
+ def in_section(section)
13
+ if section =~ /^all$/i
14
+ dup
15
+ else
16
+ items = Items.new.concat(select { |item| !item.nil? && item.section == section })
17
+ items.add_section(section, log: false)
18
+ items
19
+ end
20
+ end
21
+
22
+ ##
23
+ ## Search Items for a string (title and note)
24
+ ##
25
+ ## @param query [String] The query
26
+ ## @param case_type [Symbol] The case type
27
+ ## (:smart, :sensitive, :ignore)
28
+ ##
29
+ ## @return [Items] array of items matching search
30
+ ##
31
+ def search(query, case_type: :smart)
32
+ WWID.new.fuzzy_filter_items(self, query, case_type: case_type)
33
+ end
34
+
35
+ ##
36
+ ## Search items by tags
37
+ ##
38
+ ## @param tags [Array,String] The tags by which to
39
+ ## filter
40
+ ## @param bool [Symbol] The bool with which to
41
+ ## combine multiple tags
42
+ ##
43
+ ## @return [Items] array of items matching tag filter
44
+ ##
45
+ def tagged(tags, bool: :and)
46
+ WWID.new.filter_items(self, opt: { tag: tags, bool: bool })
47
+ end
48
+
49
+ ##
50
+ ## Filter Items by date. String arguments will be
51
+ ## chronified
52
+ ##
53
+ ## @param start [Time,String] Filter items after
54
+ ## this date
55
+ ## @param finish [Time,String] Filter items before
56
+ ## this date
57
+ ##
58
+ ## @return [Items] array of items with dates between
59
+ ## targets
60
+ ##
61
+ def between_dates(start, finish)
62
+ start = start.chronify(guess: :begin, future: false) if start.is_a?(String)
63
+ finish = finish.chronify(guess: :end) if finish.is_a?(String)
64
+ WWID.new.filter_items(self, opt: { date_filter: [start, finish] })
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'filter'
4
+ require_relative 'modify'
5
+ require_relative 'sections'
6
+ require_relative 'util'
7
+
8
+ module Doing
9
+ # A collection of Item objects
10
+ class Items < Array
11
+ attr_accessor :sections
12
+
13
+ def initialize
14
+ super
15
+ @sections = []
16
+ end
17
+
18
+ ##
19
+ ## Test if self includes Item
20
+ ##
21
+ ## @param item [Item] The item to search for
22
+ ## @param match_section [Boolean] Section must match
23
+ ##
24
+ ## @return [Boolean] True if Item exists
25
+ ##
26
+ def include?(item, match_section: true)
27
+ includes = false
28
+ each do |other_item|
29
+ if other_item.equal?(item, match_section: match_section)
30
+ includes = true
31
+ break
32
+ end
33
+ end
34
+
35
+ includes
36
+ end
37
+
38
+ # Output sections and items in Doing file format
39
+ def to_s
40
+ out = []
41
+ @sections.each do |section|
42
+ out.push(section.original)
43
+ items = in_section(section.title).sort_by { |i| [i.date, i.title] }
44
+ items.reverse! if Doing.setting('doing_file_sort').normalize_order == :desc
45
+ items.each { |item| out.push(item.to_s) }
46
+ end
47
+
48
+ out.join("\n")
49
+ end
50
+
51
+ # @private
52
+ def inspect
53
+ sections = @sections.map { |s| "<Section:#{s.title} #{in_section(s.title).count} items>" }.join(', ')
54
+ "#<Doing::Items #{count} items, #{@sections.count} sections: #{sections}>"
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doing
4
+ class Items < Array
5
+ ##
6
+ ## Delete an item from the index
7
+ ##
8
+ ## @param item The item
9
+ ##
10
+ def delete_item(item, single: false)
11
+ deleted = delete(item)
12
+ Doing.logger.count(:deleted)
13
+ Doing.logger.info('Entry deleted:', deleted.title) if single
14
+ deleted
15
+ end
16
+
17
+ ##
18
+ ## Update an item in the index with a modified item
19
+ ##
20
+ ## @param old_item The old item
21
+ ## @param new_item The new item
22
+ ##
23
+ def update_item(old_item, new_item)
24
+ s_idx = index { |item| item.equal?(old_item) }
25
+
26
+ raise ItemNotFound, 'Unable to find item in index, did it mutate?' unless s_idx
27
+
28
+ return if fetch(s_idx).equal?(new_item)
29
+
30
+ self[s_idx] = new_item
31
+ Doing.logger.count(:updated)
32
+ Doing.logger.info('Entry updated:', self[s_idx].title.trunc(60))
33
+ new_item
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doing
4
+ class Items < Array
5
+ # List sections, title only
6
+ #
7
+ # @return [Array] section titles
8
+ #
9
+ def section_titles
10
+ @sections.map(&:title)
11
+ end
12
+
13
+ # Test if section already exists
14
+ #
15
+ # @param section [String] section title
16
+ #
17
+ # @return [Boolean] true if section exists
18
+ #
19
+ def section?(section)
20
+ section = section.is_a?(Section) ? section.title.downcase : section.downcase
21
+ @sections.map { |i| i.title.downcase }.include?(section)
22
+ end
23
+
24
+ ##
25
+ ## Return the best section match for a search query
26
+ ##
27
+ ## @param frag The search query
28
+ ## @param distance The distance apart characters can be (fuzziness)
29
+ ##
30
+ ## @return [Section] (first) matching section object
31
+ ##
32
+ def guess_section(frag, distance: 2)
33
+ section = nil
34
+ re = frag.to_rx(distance: distance, case_type: :ignore)
35
+ @sections.each do |sect|
36
+ next unless sect.title =~ /#{re}/i
37
+
38
+ Doing.logger.debug('Match:', %(Assuming "#{sect.title}" from "#{frag}"))
39
+ section = sect
40
+ break
41
+ end
42
+
43
+ section
44
+ end
45
+
46
+ # Add a new section to the sections array. Accepts
47
+ # either a Section object, or a title string that will
48
+ # be converted into a Section.
49
+ #
50
+ # @param section [Section] The section to add. A
51
+ # String value will be converted to
52
+ # Section automatically.
53
+ # @param log [Boolean] Add a log message
54
+ # notifying the user about the
55
+ # creation of the section.
56
+ #
57
+ # @return nothing
58
+ #
59
+ def add_section(section, log: false)
60
+ section = section.is_a?(Section) ? section : Section.new(section.cap_first)
61
+
62
+ return if section?(section)
63
+
64
+ @sections.push(section)
65
+ Doing.logger.info('New section:', %("#{section}" added)) if log
66
+ end
67
+
68
+ def delete_section(section, log: false)
69
+ return unless section?(section)
70
+
71
+ raise DoingRuntimeError, 'Section not empty' if in_section(section).count.positive?
72
+
73
+ @sections.each do |sect|
74
+ next unless sect.title == section && in_section(sect).count.zero?
75
+
76
+ @sections.delete(sect)
77
+ Doing.logger.info('Removed section:', %("#{section}" removed)) if log
78
+ end
79
+
80
+ Doing.logger.error('Not found:', %("#{section}" not found))
81
+ end
82
+ end
83
+ end