doing 2.1.40 → 2.1.41

Sign up to get free protection for your applications and to get access to all the features.
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