doing 2.1.39 → 2.1.42

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 (229) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -1
  3. data/CHANGELOG.md +67 -0
  4. data/Gemfile.lock +1 -1
  5. data/README.md +1 -1
  6. data/Rakefile +4 -4
  7. data/bin/commands/again.rb +1 -3
  8. data/bin/commands/changes.rb +50 -34
  9. data/bin/commands/commands.rb +77 -52
  10. data/bin/commands/commands_accepting.rb +57 -53
  11. data/bin/commands/config.rb +45 -36
  12. data/bin/commands/done.rb +1 -18
  13. data/bin/commands/finish.rb +90 -59
  14. data/bin/commands/flag.rb +5 -1
  15. data/bin/commands/grep.rb +3 -14
  16. data/bin/commands/last.rb +2 -8
  17. data/bin/commands/meanwhile.rb +13 -6
  18. data/bin/commands/now.rb +151 -107
  19. data/bin/commands/on.rb +8 -18
  20. data/bin/commands/recent.rb +2 -8
  21. data/bin/commands/reset.rb +24 -1
  22. data/bin/commands/select.rb +1 -1
  23. data/bin/commands/show.rb +6 -17
  24. data/bin/commands/since.rb +1 -12
  25. data/bin/commands/tag_dir.rb +49 -15
  26. data/bin/commands/today.rb +2 -13
  27. data/bin/commands/undo.rb +4 -6
  28. data/bin/commands/view.rb +1 -1
  29. data/bin/commands/yesterday.rb +2 -13
  30. data/bin/doing +15 -8
  31. data/{Dockerfile → docker/Dockerfile} +3 -1
  32. data/{Dockerfile-2.6 → docker/Dockerfile-2.6} +2 -2
  33. data/{Dockerfile-2.7 → docker/Dockerfile-2.7} +2 -2
  34. data/{Dockerfile-3.0 → docker/Dockerfile-3.0} +2 -2
  35. data/{bash_profile → docker/bash_profile} +0 -0
  36. data/{inputrc → docker/inputrc} +0 -0
  37. data/docs/doc/Array.html +85 -2
  38. data/docs/doc/BooleanTermParser/Clause.html +1 -1
  39. data/docs/doc/BooleanTermParser/Operator.html +1 -1
  40. data/docs/doc/BooleanTermParser/Query.html +1 -1
  41. data/docs/doc/BooleanTermParser/QueryParser.html +1 -1
  42. data/docs/doc/BooleanTermParser/QueryTransformer.html +1 -1
  43. data/docs/doc/BooleanTermParser.html +1 -1
  44. data/docs/doc/Doing/ArrayNestedHash.html +198 -0
  45. data/docs/doc/Doing/ArrayTags.html +424 -0
  46. data/docs/doc/Doing/CSVExport.html +266 -0
  47. data/docs/doc/Doing/CalendarImport.html +232 -0
  48. data/docs/doc/Doing/Change.html +617 -0
  49. data/docs/doc/Doing/Changes.html +468 -0
  50. data/docs/doc/Doing/ChronifyArray.html +347 -0
  51. data/docs/doc/Doing/ChronifyNumeric.html +271 -0
  52. data/docs/doc/Doing/ChronifyString.html +682 -0
  53. data/docs/doc/Doing/Color.html +167 -21
  54. data/docs/doc/Doing/Completion/BashCompletions.html +445 -0
  55. data/docs/doc/Doing/Completion/FishCompletions.html +445 -0
  56. data/docs/doc/Doing/Completion/StringUtils.html +229 -0
  57. data/docs/doc/Doing/Completion/ZshCompletions.html +445 -0
  58. data/docs/doc/Doing/Completion.html +17 -3
  59. data/docs/doc/Doing/Configuration.html +3 -2
  60. data/docs/doc/Doing/DayOneRenderer.html +383 -0
  61. data/docs/doc/Doing/DayoneExport.html +290 -0
  62. data/docs/doc/Doing/DoingImport.html +391 -0
  63. data/docs/doc/Doing/Entry.html +381 -0
  64. data/docs/doc/Doing/Errors/DoingNoTraceError.html +7 -3
  65. data/docs/doc/Doing/Errors/DoingRuntimeError.html +7 -3
  66. data/docs/doc/Doing/Errors/DoingStandardError.html +1 -1
  67. data/docs/doc/Doing/Errors/EmptyInput.html +10 -2
  68. data/docs/doc/Doing/Errors/HistoryLimitError.html +194 -0
  69. data/docs/doc/Doing/Errors/InvalidPlugin.html +194 -0
  70. data/docs/doc/Doing/Errors/MissingBackupFile.html +194 -0
  71. data/docs/doc/Doing/Errors/NoResults.html +10 -2
  72. data/docs/doc/Doing/Errors/PluginException.html +1 -1
  73. data/docs/doc/Doing/Errors/UserCancelled.html +10 -2
  74. data/docs/doc/Doing/Errors/WrongCommand.html +10 -2
  75. data/docs/doc/Doing/Errors.html +9 -9
  76. data/docs/doc/Doing/HTMLExport.html +256 -0
  77. data/docs/doc/Doing/Hooks.html +1 -1
  78. data/docs/doc/Doing/Item.html +179 -1660
  79. data/docs/doc/Doing/ItemDates.html +564 -0
  80. data/docs/doc/Doing/ItemQuery.html +614 -0
  81. data/docs/doc/Doing/ItemState.html +387 -0
  82. data/docs/doc/Doing/ItemTags.html +498 -0
  83. data/docs/doc/Doing/Items.html +581 -15
  84. data/docs/doc/Doing/JSONExport.html +222 -0
  85. data/docs/doc/Doing/Logger.html +1 -1
  86. data/docs/doc/Doing/MarkdownExport.html +266 -0
  87. data/docs/doc/Doing/MarkdownRenderer.html +383 -0
  88. data/docs/doc/Doing/Note.html +18 -4
  89. data/docs/doc/Doing/Pager.html +1 -1
  90. data/docs/doc/Doing/Plugins.html +181 -76
  91. data/docs/doc/Doing/Prompt.html +32 -683
  92. data/docs/doc/Doing/PromptChoose.html +484 -0
  93. data/docs/doc/Doing/PromptFZF.html +391 -0
  94. data/docs/doc/Doing/PromptInput.html +572 -0
  95. data/docs/doc/Doing/PromptSTD.html +293 -0
  96. data/docs/doc/Doing/PromptYN.html +237 -0
  97. data/docs/doc/Doing/Section.html +58 -2
  98. data/docs/doc/Doing/StringHighlight.html +533 -0
  99. data/docs/doc/Doing/StringNormalize.html +929 -0
  100. data/docs/doc/Doing/StringQuery.html +725 -0
  101. data/docs/doc/Doing/StringTags.html +884 -0
  102. data/docs/doc/Doing/StringTransform.html +599 -0
  103. data/docs/doc/Doing/StringTruncate.html +448 -0
  104. data/docs/doc/Doing/StringURL.html +409 -0
  105. data/docs/doc/Doing/SymbolNormalize.html +341 -0
  106. data/docs/doc/Doing/TaskPaperExport.html +222 -0
  107. data/docs/doc/Doing/TemplateExport.html +249 -0
  108. data/docs/doc/Doing/TemplateString.html +102 -3
  109. data/docs/doc/Doing/TimingImport.html +285 -0
  110. data/docs/doc/Doing/Types.html +1 -1
  111. data/docs/doc/Doing/Util/Backup.html +11 -163
  112. data/docs/doc/Doing/Util.html +67 -10
  113. data/docs/doc/Doing/Version.html +523 -0
  114. data/docs/doc/Doing/WWID/WWIDUtil.html +510 -0
  115. data/docs/doc/Doing/WWID.html +476 -139
  116. data/docs/doc/Doing/WWIDDisplay.html +865 -0
  117. data/docs/doc/Doing/WWIDEditor.html +466 -0
  118. data/docs/doc/Doing/WWIDFileTools.html +359 -0
  119. data/docs/doc/Doing/WWIDFilter.html +466 -0
  120. data/docs/doc/Doing/WWIDGuess.html +299 -0
  121. data/docs/doc/Doing/WWIDInteractive.html +752 -0
  122. data/docs/doc/Doing/WWIDModify.html +1078 -0
  123. data/docs/doc/Doing/WWIDTags.html +302 -0
  124. data/docs/doc/Doing/WWIDTimers.html +359 -0
  125. data/docs/doc/Doing/WWIDUtil.html +510 -0
  126. data/docs/doc/Doing.html +9 -6
  127. data/docs/doc/FalseClass.html +1 -1
  128. data/docs/doc/GLI/Commands/Help.html +1 -1
  129. data/docs/doc/GLI/Commands/MarkdownDocumentListener.html +1 -1
  130. data/docs/doc/GLI/Commands.html +1 -1
  131. data/docs/doc/GLI.html +1 -1
  132. data/docs/doc/Hash.html +1 -1
  133. data/docs/doc/Numeric.html +23 -78
  134. data/docs/doc/Object.html +1 -1
  135. data/docs/doc/PhraseParser/Operator.html +1 -1
  136. data/docs/doc/PhraseParser/PhraseClause.html +1 -1
  137. data/docs/doc/PhraseParser/Query.html +1 -1
  138. data/docs/doc/PhraseParser/QueryParser.html +1 -1
  139. data/docs/doc/PhraseParser/QueryTransformer.html +1 -1
  140. data/docs/doc/PhraseParser/TermClause.html +1 -1
  141. data/docs/doc/PhraseParser.html +1 -1
  142. data/docs/doc/Status.html +1 -1
  143. data/docs/doc/String.html +58 -633
  144. data/docs/doc/Symbol.html +9 -224
  145. data/docs/doc/Time.html +119 -13
  146. data/docs/doc/TrueClass.html +1 -1
  147. data/docs/doc/_index.html +348 -4
  148. data/docs/doc/class_list.html +1 -1
  149. data/docs/doc/file.README.html +2 -2
  150. data/docs/doc/index.html +2 -2
  151. data/docs/doc/method_list.html +1904 -592
  152. data/docs/doc/top-level-namespace.html +12 -4
  153. data/docs/index.md +1 -1
  154. data/doing.rdoc +67 -15
  155. data/lib/completion/_doing.zsh +6 -6
  156. data/lib/completion/doing.bash +10 -10
  157. data/lib/completion/doing.fish +10 -3
  158. data/lib/doing/add_options.rb +39 -1
  159. data/lib/doing/array/array.rb +18 -12
  160. data/lib/doing/array/cleanup.rb +31 -0
  161. data/lib/doing/array/nested_hash.rb +1 -1
  162. data/lib/doing/array/tags.rb +6 -5
  163. data/lib/doing/changelog/changelog.rb +6 -0
  164. data/lib/doing/chronify/array.rb +65 -25
  165. data/lib/doing/chronify/chronify.rb +12 -0
  166. data/lib/doing/chronify/numeric.rb +3 -2
  167. data/lib/doing/chronify/string.rb +1 -1
  168. data/lib/doing/colors.rb +77 -30
  169. data/lib/doing/completion/completion_string.rb +25 -0
  170. data/lib/doing/completion.rb +4 -5
  171. data/lib/doing/configuration.rb +7 -3
  172. data/lib/doing/errors.rb +51 -35
  173. data/lib/doing/good.rb +8 -0
  174. data/lib/doing/hooks.rb +3 -3
  175. data/lib/doing/item/dates.rb +112 -0
  176. data/lib/doing/item/item.rb +128 -0
  177. data/lib/doing/{item.rb → item/query.rb} +2 -353
  178. data/lib/doing/item/state.rb +59 -0
  179. data/lib/doing/item/tags.rb +87 -0
  180. data/lib/doing/items/filter.rb +67 -0
  181. data/lib/doing/items/items.rb +57 -0
  182. data/lib/doing/items/modify.rb +36 -0
  183. data/lib/doing/items/sections.rb +83 -0
  184. data/lib/doing/items/util.rb +74 -0
  185. data/lib/doing/normalize.rb +10 -2
  186. data/lib/doing/note.rb +1 -1
  187. data/lib/doing/pager.rb +9 -3
  188. data/lib/doing/plugin_manager.rb +33 -8
  189. data/lib/doing/plugins/export/markdown_export.rb +4 -2
  190. data/lib/doing/plugins/export/template_export.rb +4 -4
  191. data/lib/doing/plugins/import/cal_to_json.scpt +0 -0
  192. data/lib/doing/plugins/import/doing_import.rb +1 -1
  193. data/lib/doing/prompt/choose.rb +118 -0
  194. data/lib/doing/prompt/fzf.rb +84 -0
  195. data/lib/doing/prompt/input.rb +129 -0
  196. data/lib/doing/prompt/prompt.rb +41 -0
  197. data/lib/doing/prompt/std.rb +32 -0
  198. data/lib/doing/prompt/yn.rb +64 -0
  199. data/lib/doing/section.rb +4 -0
  200. data/lib/doing/string/highlight.rb +1 -1
  201. data/lib/doing/string/query.rb +1 -1
  202. data/lib/doing/string/string.rb +18 -7
  203. data/lib/doing/string/tags.rb +14 -3
  204. data/lib/doing/string/transform.rb +7 -1
  205. data/lib/doing/string/truncate.rb +1 -1
  206. data/lib/doing/string/url.rb +1 -1
  207. data/lib/doing/time.rb +19 -1
  208. data/lib/doing/util.rb +12 -6
  209. data/lib/doing/util_backup.rb +62 -57
  210. data/lib/doing/version.rb +1 -1
  211. data/lib/doing/wwid/display.rb +396 -0
  212. data/lib/doing/wwid/editor.rb +214 -0
  213. data/lib/doing/wwid/filetools.rb +183 -0
  214. data/lib/doing/wwid/filter.rb +226 -0
  215. data/lib/doing/wwid/guess.rb +85 -0
  216. data/lib/doing/wwid/interactive.rb +377 -0
  217. data/lib/doing/wwid/modify.rb +617 -0
  218. data/lib/doing/wwid/tags.rb +51 -0
  219. data/lib/doing/wwid/timers.rb +342 -0
  220. data/lib/doing/wwid/wwid.rb +121 -0
  221. data/lib/doing/wwid/wwidutil.rb +101 -0
  222. data/lib/doing.rb +7 -7
  223. data/lib/helpers/threaded_tests.rb +1 -0
  224. metadata +94 -14
  225. data/lib/doing/changelog.rb +0 -6
  226. data/lib/doing/completion/string.rb +0 -17
  227. data/lib/doing/items.rb +0 -196
  228. data/lib/doing/prompt.rb +0 -330
  229. data/lib/doing/wwid.rb +0 -2398
@@ -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,12 +2,60 @@
2
2
 
3
3
  module Doing
4
4
  # Chronify array helpers
5
- class ::Array
5
+ module ChronifyArray
6
+ # Convert [d, h, m] to [y, d, h, m]
7
+ def to_years
8
+ d, h, m = self
9
+
10
+ if d.zero? && h > 24
11
+ d = (h / 24).floor
12
+ h = h % 24
13
+ end
14
+
15
+ if d > 365
16
+ y = (d / 365).floor
17
+ d = d % 365
18
+ else
19
+ y = 0
20
+ end
21
+
22
+ [y, d, h, m]
23
+ end
24
+
25
+ def to_natural
26
+ y, d, h, m = to_years
27
+ human = []
28
+ human.push(format('%<y>d %<s>s', y: y, s: 'year'.to_p(y))) if y.positive?
29
+ human.push(format('%<d>d %<s>s', d: d, s: 'day'.to_p(d))) if d.positive?
30
+ human.push(format('%<h>d %<s>s', h: h, s: 'hour'.to_p(h))) if h.positive?
31
+ human.push(format('%<m>d %<s>s', m: m, s: 'minute'.to_p(m))) if m.positive?
32
+ human
33
+ end
34
+
35
+ def to_abbr(years: false, separator: '')
36
+ if years
37
+ y, d, h, m = to_years
38
+ else
39
+ y = 0
40
+ d, h, m = self
41
+
42
+ if d.zero? && h > 24
43
+ d = (h / 24).floor
44
+ h = h % 24
45
+ end
46
+ end
47
+
48
+ output = []
49
+ output.push(format('%<y>dy', y: y)) if y.positive?
50
+ output.push(format('%<d>dd', d: d)) if d.positive?
51
+ output.push(format('%<h>dh', h: h)) if h.positive?
52
+ output.push(format('%<m>dm', m: m)) if m.positive?
53
+ output.join(separator)
54
+ end
55
+
6
56
  ##
7
57
  ## Format [d, h, m] as string
8
58
  ##
9
- ## @accept [Array] Array of [days, hours, minutes]
10
- ##
11
59
  ## @param format [Symbol] The format, :dhm, :hm,
12
60
  ## :m, :clock, :natural
13
61
  ## @return [String] formatted string
@@ -23,16 +71,13 @@ module Doing
23
71
  h = h % 24
24
72
  end
25
73
  format('%<d>02d:%<h>02d:%<m>02d', d: d, h: h, m: m)
74
+ when :hmclock
75
+ h += d * 24 if d.positive?
76
+ format('%<h>02d:%<m>02d', h: h, m: m)
77
+ when :ydhm
78
+ to_abbr(years: true, separator: ' ')
26
79
  when :dhm
27
- if d.zero? && h > 24
28
- d = (h / 24).floor
29
- h = h % 24
30
- end
31
- output = []
32
- output.push(format('%<d>dd', d: d)) if d.positive?
33
- output.push(format('%<h>dh', h: h)) if h.positive?
34
- output.push(format('%<m>dm', m: m)) if m.positive?
35
- output.join(' ')
80
+ to_abbr(years: false, separator: ' ')
36
81
  when :hm
37
82
  h += d * 24 if d.positive?
38
83
  format('%<h> 4dh %<m>02dm', h: h, m: m)
@@ -40,25 +85,20 @@ module Doing
40
85
  h += d * 24 if d.positive?
41
86
  m += h * 60 if h.positive?
42
87
  format('%<m> 4dm', m: m)
88
+ when :tight
89
+ to_abbr(years: true, separator: '')
43
90
  when :natural
44
- human = []
45
- human.push(format('%<d>d %<s>s', d: d, s: 'day'.to_p(d))) if d.positive?
46
- human.push(format('%<h>d %<s>s', h: h, s: 'hour'.to_p(h))) if h.positive?
47
- human.push(format('%<m>d %<s>s', m: m, s: 'minute'.to_p(m))) if m.positive?
48
- human.join(', ')
91
+ to_natural.join(', ')
49
92
  when :speech
50
- human = []
51
- human.push(format('%<d>d %<s>s', d: d, s: 'day'.to_p(d))) if d.positive?
52
- human.push(format('%<h>d %<s>s', h: h, s: 'hour'.to_p(h))) if h.positive?
53
- human.push(format('%<m>d %<s>s', m: m, s: 'minute'.to_p(m))) if m.positive?
93
+ human = to_natural
54
94
  last = human.pop
55
95
  case human.count
56
- when 2
57
- human.join(', ') + ", and #{last}"
58
- when 1
59
- "#{human[0]} and #{last}"
60
96
  when 0
61
97
  last
98
+ when 1
99
+ "#{human[0]} and #{last}"
100
+ else
101
+ human.join(', ') + ", and #{last}"
62
102
  end
63
103
  end
64
104
  end
@@ -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:
data/lib/doing/colors.rb CHANGED
@@ -2,9 +2,18 @@
2
2
 
3
3
  # Cribbed from <https://github.com/flori/term-ansicolor>
4
4
  module Doing
5
- # Terminal color functions
5
+ # Terminal output color functions.
6
6
  module Color
7
- # :stopdoc:
7
+ # All available color names. Available as methods and string extensions.
8
+ #
9
+ # @example Use a color as a method. Color reset will be added to end of string.
10
+ # Color.yellow('This text is yellow') => "\e[33mThis text is yellow\e[0m"
11
+ #
12
+ # @example Use a color as a string extension. Color reset added automatically.
13
+ # 'This text is green'.green => "\e[1;32mThis text is green\e[0m"
14
+ #
15
+ # @example Send a text string as a color
16
+ # Color.send('red') => "\e[31m"
8
17
  ATTRIBUTES = [
9
18
  [:clear, 0], # String#clear is already used to empty string in Ruby 1.9
10
19
  [:reset, 0], # synonym for :clear
@@ -66,7 +75,7 @@ module Doing
66
75
  [:redacted, '0;30;40'],
67
76
  [:alert, '1;31;43'],
68
77
  [:error, '1;37;41'],
69
- [:default, '0;39']
78
+ [:default, '0;39']
70
79
  ].map(&:freeze).freeze
71
80
 
72
81
  ATTRIBUTE_NAMES = ATTRIBUTES.transpose.first
@@ -119,24 +128,60 @@ module Doing
119
128
  end
120
129
 
121
130
  class << self
122
- # Returns true, if the coloring function of this module
131
+ # Returns true if the coloring function of this module
123
132
  # is switched on, false otherwise.
124
133
  def coloring?
125
134
  @coloring
126
135
  end
127
136
 
128
- # Turns the coloring on or off globally, so you can easily do
129
- # this for example:
130
- # Doing::Color::coloring = STDOUT.isatty
131
137
  attr_writer :coloring
132
138
 
139
+ ##
140
+ ## Enables colored output
141
+ ##
142
+ ## @example Turn color on or off based on TTY
143
+ ## Doing::Color.coloring = STDOUT.isatty
133
144
  def coloring
134
145
  @coloring ||= true
135
146
  end
147
+
148
+ ##
149
+ ## Convert a template string to a colored string.
150
+ ## Colors are specified with single letters inside
151
+ ## curly braces. Uppercase changes background color.
152
+ ##
153
+ ## w: white, k: black, g: green, l: blue, y: yellow, c: cyan,
154
+ ## m: magenta, r: red, b: bold, u: underline, i: italic,
155
+ ## x: reset (remove background, color, emphasis)
156
+ ##
157
+ ## @example Convert a templated string
158
+ ## Color.template('{Rwb}Warning:{x} {w}you look a little {g}ill{x}')
159
+ ##
160
+ ## @param input [String, Array] The template
161
+ ## string. If this is an array, the
162
+ ## elements will be joined with a
163
+ ## space.
164
+ ##
165
+ ## @return [String] Colorized string
166
+ ##
167
+ def template(input)
168
+ input = input.join(' ') if input.is_a? Array
169
+ fmt = input.gsub(/\{(\w+)\}/) do
170
+ Regexp.last_match(1).split('').map { |c| "%<#{c}>s" }.join('')
171
+ end
172
+
173
+ colors = { w: white, k: black, g: green, l: blue,
174
+ y: yellow, c: cyan, m: magenta, r: red,
175
+ W: bgwhite, K: bgblack, G: bggreen, L: bgblue,
176
+ Y: bgyellow, C: bgcyan, M: bgmagenta, R: bgred,
177
+ b: bold, u: underline, i: italic, x: reset }
178
+
179
+ format(fmt, colors)
180
+ end
136
181
  end
137
182
 
138
183
  ATTRIBUTES.each do |c, v|
139
- eval <<-EOT
184
+ new_method = <<-EOSCRIPT
140
185
  def #{c}(string = nil)
141
186
  result = ''
142
187
  result << "\e[#{v}m" if Doing::Color.coloring?
@@ -152,33 +197,37 @@ module Doing
152
197
  result << "\e[0m" if Doing::Color.coloring?
153
198
  result
154
199
  end
155
- EOT
200
+ EOSCRIPT
201
+
202
+ module_eval(new_method)
203
+
204
+ next unless c =~ /bold/
156
205
 
157
206
  # Accept brightwhite in addition to boldwhite
158
- if c =~ /bold/
159
- eval <<-EOT
160
- def #{c.to_s.sub(/bold/, 'bright')}(string = nil)
161
- result = ''
162
- result << "\e[#{v}m" if Doing::Color.coloring?
163
- if block_given?
164
- result << yield
165
- elsif string.respond_to?(:to_str)
166
- result << string.to_str
167
- elsif respond_to?(:to_str)
168
- result << to_str
169
- else
170
- return result #only switch on
171
- end
172
- result << "\e[0m" if Doing::Color.coloring?
173
- result
207
+ new_method = <<-EOSCRIPT
208
+ def #{c.to_s.sub(/bold/, 'bright')}(string = nil)
209
+ result = ''
210
+ result << "\e[#{v}m" if Doing::Color.coloring?
211
+ if block_given?
212
+ result << yield
213
+ elsif string.respond_to?(:to_str)
214
+ result << string.to_str
215
+ elsif respond_to?(:to_str)
216
+ result << to_str
217
+ else
218
+ return result #only switch on
174
219
  end
175
- EOT
176
- end
220
+ result << "\e[0m" if Doing::Color.coloring?
221
+ result
222
+ end
223
+ EOSCRIPT
224
+
225
+ module_eval(new_method)
177
226
  end
178
227
 
179
228
  # Regular expression that is used to scan for ANSI-sequences while
180
229
  # uncoloring strings.
181
- COLORED_REGEXP = /\e\[(?:(?:[349]|10)[0-7]|[0-9])?m/
230
+ COLORED_REGEXP = /\e\[(?:(?:[349]|10)[0-7]|[0-9])?m/.freeze
182
231
 
183
232
  # Returns an uncolored version of the string, that is all
184
233
  # ANSI-sequences are stripped from the string.
@@ -194,8 +243,6 @@ module Doing
194
243
  end
195
244
  end
196
245
 
197
- module_function
198
-
199
246
  # Returns an array of all Doing::Color attributes as symbols.
200
247
  def attributes
201
248
  ATTRIBUTE_NAMES
@@ -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,11 +2,10 @@
2
2
 
3
3
  require 'tty-progressbar'
4
4
 
5
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'completion'))
6
- require 'string'
7
- require 'fish_completion'
8
- require 'zsh_completion'
9
- require 'bash_completion'
5
+ require_relative 'completion/completion_string'
6
+ require_relative 'completion/fish_completion'
7
+ require_relative 'completion/zsh_completion'
8
+ require_relative 'completion/bash_completion'
10
9
 
11
10
  module Doing
12
11
  # Completion script generator
@@ -23,7 +23,8 @@ module Doing
23
23
  'editors' => {
24
24
  'default' => ENV['DOING_EDITOR'] || ENV['GIT_EDITOR'] || ENV['EDITOR'],
25
25
  'doing_file' => nil,
26
- 'config' => nil
26
+ 'config' => nil,
27
+ 'pager' => nil
27
28
  },
28
29
  'plugins' => {
29
30
  'plugin_path' => File.join(Util.user_home, '.config', 'doing', 'plugins'),
@@ -204,6 +205,7 @@ module Doing
204
205
  real_path = []
205
206
  unless keypath =~ /^[.*]?$/
206
207
  paths = keypath.split(/[:.]/)
208
+ element_count = paths.count
207
209
  while paths.length.positive? && !cfg.nil?
208
210
  path = paths.shift
209
211
  new_cfg = nil
@@ -220,6 +222,8 @@ module Doing
220
222
  end
221
223
 
222
224
  if new_cfg.nil?
225
+ return real_path if real_path[-1] == path && real_path.count == element_count
226
+
223
227
  if distance < 5 && !create
224
228
  return resolve_key_path(keypath, create: false, distance: distance + 1)
225
229
  else
@@ -228,12 +232,12 @@ module Doing
228
232
 
229
233
  resolved = real_path.count.positive? ? "Resolved #{real_path.join('.')}, but " : ''
230
234
  Doing.logger.log_now(:warn, "#{resolved}#{path} is unknown")
231
- new_path = [*real_path, path, *paths].join('.')
235
+ new_path = [*real_path, path, *paths].compact.join('.')
232
236
  Doing.logger.log_now(:warn, "Continuing will create the path #{new_path}")
233
237
  res = Prompt.yn('Key path not found, create it?', default_response: true)
234
238
  raise InvalidArgument, 'Invalid key path' unless res
235
239
 
236
- real_path.push(path).concat(paths)
240
+ real_path.push(path).concat(paths).compact!
237
241
  Doing.logger.debug('Config:', "translated key path #{keypath} to #{real_path.join('.')}")
238
242
  return real_path
239
243
  end
data/lib/doing/errors.rb CHANGED
@@ -2,19 +2,27 @@
2
2
 
3
3
  module Doing
4
4
  module Errors
5
- class UserCancelled < ::StandardError
6
- def initialize(msg = 'Cancelled', topic = 'Exited:')
5
+ class DoingNoTraceError < ::StandardError
6
+ def initialize(msg = nil, level: nil, topic: 'Error:', exit_code: 1)
7
+ level ||= :error
7
8
  Doing.logger.output_results
8
- Doing.logger.log_now(:warn, topic, msg)
9
- Process.exit 1
9
+ if msg
10
+ Doing.logger.log_now(level, topic, msg)
11
+ end
12
+
13
+ Process.exit exit_code
10
14
  end
11
15
  end
12
16
 
13
- class EmptyInput < ::StandardError
17
+ class UserCancelled < DoingNoTraceError
18
+ def initialize(msg = 'Cancelled', topic = 'Exited:')
19
+ super(msg, level: :warn, topic: topic, exit_code: 1)
20
+ end
21
+ end
22
+
23
+ class EmptyInput < DoingNoTraceError
14
24
  def initialize(msg = 'No input', topic = 'Exited:')
15
- Doing.logger.output_results
16
- Doing.logger.log_now(:warn, topic, msg)
17
- Process.exit 1
25
+ super(msg, level: :warn, topic: topic, exit_code: 6)
18
26
  end
19
27
  end
20
28
 
@@ -22,44 +30,47 @@ module Doing
22
30
  def initialize(msg = '')
23
31
  Doing.logger.output_results
24
32
 
25
- super
33
+ super(msg)
26
34
  end
27
35
  end
28
36
 
29
- class WrongCommand < ::StandardError
37
+ class WrongCommand < DoingNoTraceError
30
38
  def initialize(msg = 'wrong command', topic: 'Error:')
31
- Doing.logger.warn(topic, msg)
32
-
33
- super(msg)
39
+ super(msg, level: :warn, topic: topic, exit_code: 2)
34
40
  end
35
41
  end
36
42
 
37
43
  class DoingRuntimeError < ::RuntimeError
38
- def initialize(msg = 'Runtime Error', topic: 'Error:')
44
+ def initialize(msg = 'Runtime Error', exit_code = nil, topic: 'Error:')
39
45
  Doing.logger.output_results
40
46
  Doing.logger.log_now(:error, topic, msg)
41
- Process.exit 1
47
+
48
+ Process.exit exit_code if exit_code
42
49
  end
43
50
  end
44
51
 
45
- class NoResults < ::StandardError
52
+ class NoResults < DoingNoTraceError
46
53
  def initialize(msg = 'No results', topic = 'Exited:')
47
- Doing.logger.output_results
48
- Doing.logger.log_now(:warn, topic, msg)
49
- Process.exit 0
54
+ super(msg, level: :warn, topic: topic, exit_code: 0)
50
55
 
51
56
  end
52
57
  end
53
58
 
54
- class DoingNoTraceError < ::StandardError
55
- def initialize(msg = nil, level = nil, topic = nil)
56
- level ||= :error
57
- Doing.logger.output_results
58
- if msg
59
- Doing.logger.log_now(level, topic, msg)
60
- end
59
+ class HistoryLimitError < DoingNoTraceError
60
+ def initialize(msg, exit_code = 24)
61
+ super(msg, level: :error, topic: 'History:', exit_code: exit_code)
62
+ end
63
+ end
61
64
 
62
- Process.exit 1
65
+ class MissingBackupFile < DoingNoTraceError
66
+ def initialize(msg, exit_code = 26)
67
+ super(msg, level: :error, topic: 'History:', exit_code: exit_code)
68
+ end
69
+ end
70
+
71
+ class InvalidPlugin < DoingRuntimeError
72
+ def initialize(kind = 'output', msg = nil)
73
+ super(%(Invalid #{kind} type (#{msg})), 128, topic: 'Invalid:')
63
74
  end
64
75
  end
65
76
 
@@ -75,6 +86,10 @@ module Doing
75
86
  'Import plugin'
76
87
  when /^e/
77
88
  'Export plugin'
89
+ when /^h/
90
+ 'Hook'
91
+ when /^u/
92
+ 'Unrecognized'
78
93
  else
79
94
  type.to_s
80
95
  end
@@ -82,7 +97,8 @@ module Doing
82
97
  msg = "(#{@type}: #{@plugin}) #{msg}"
83
98
 
84
99
  Doing.logger.log_now(:error, 'Plugin:', msg)
85
- Process.exit 1
100
+
101
+ super(msg)
86
102
  end
87
103
  end
88
104
 
@@ -90,17 +106,17 @@ module Doing
90
106
  InvalidPluginType = Class.new(PluginException)
91
107
  PluginUncallable = Class.new(PluginException)
92
108
 
93
- InvalidArgument = Class.new(DoingRuntimeError)
94
- MissingArgument = Class.new(DoingRuntimeError)
95
- MissingFile = Class.new(DoingRuntimeError)
96
- MissingEditor = Class.new(DoingRuntimeError)
109
+ InvalidArgument = Class.new(DoingNoTraceError)
110
+ MissingArgument = Class.new(DoingNoTraceError)
111
+ MissingFile = Class.new(DoingNoTraceError)
112
+ MissingEditor = Class.new(DoingNoTraceError)
97
113
  NonInteractive = Class.new(StandardError)
98
114
 
99
- NoEntryError = Class.new(DoingRuntimeError)
115
+ NoEntryError = Class.new(DoingNoTraceError)
100
116
 
101
117
  InvalidTimeExpression = Class.new(DoingRuntimeError)
102
- InvalidSection = Class.new(DoingRuntimeError)
103
- InvalidView = Class.new(DoingRuntimeError)
118
+ InvalidSection = Class.new(DoingNoTraceError)
119
+ InvalidView = Class.new(DoingNoTraceError)
104
120
 
105
121
  ItemNotFound = Class.new(DoingRuntimeError)
106
122
  # FatalException = Class.new(::RuntimeError)
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
  ##
data/lib/doing/hooks.rb CHANGED
@@ -15,7 +15,7 @@ module Doing
15
15
  post_entry_removed: [], # wwid, entry.dup
16
16
  pre_export: [], # wwid, format, entries
17
17
  pre_write: [], # wwid, file
18
- post_write: [] # wwid, file
18
+ post_write: [] # file
19
19
  }
20
20
 
21
21
  # map of all hooks and their priorities
@@ -40,10 +40,10 @@ module Doing
40
40
  # register a single hook to be called later, internal API
41
41
  def self.register_one(event, priority, &block)
42
42
  unless @registry[event]
43
- raise Doing::Errors::HookUnavailable, "Invalid hook. Doing only supports #{@registry.keys.inspect}"
43
+ raise Doing::Errors::HookUnavailable.new("Invalid hook. Doing only supports #{@registry.keys.inspect}", 'hook', event)
44
44
  end
45
45
 
46
- raise Doing::Errors::PluginUncallable, 'Hooks must respond to :call' unless block.respond_to? :call
46
+ raise Doing::Errors::PluginUncallable.new('Hooks must respond to :call', 'hook', event) unless block.respond_to? :call
47
47
 
48
48
  Doing.logger.debug('Hook Manager:', "Registered #{event} hook") if ENV['DOING_PLUGIN_DEBUG']
49
49