doing 2.1.25 → 2.1.29

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 (163) hide show
  1. checksums.yaml +4 -4
  2. data/.yardoc/checksums +15 -20
  3. data/.yardoc/object_types +0 -0
  4. data/.yardoc/objects/root.dat +0 -0
  5. data/CHANGELOG.md +322 -108
  6. data/Dockerfile +5 -5
  7. data/Dockerfile-2.6 +5 -5
  8. data/Dockerfile-2.7 +5 -4
  9. data/Dockerfile-3.0 +5 -4
  10. data/Gemfile.lock +1 -1
  11. data/README.md +1 -1
  12. data/Rakefile +2 -3
  13. data/bin/commands/add_section.rb +15 -0
  14. data/bin/commands/again.rb +57 -0
  15. data/bin/commands/archive.rb +55 -0
  16. data/bin/commands/cancel.rb +60 -0
  17. data/bin/commands/changes.rb +83 -0
  18. data/bin/commands/choose.rb +9 -0
  19. data/bin/commands/colors.rb +21 -0
  20. data/bin/commands/commands.rb +89 -0
  21. data/bin/commands/commands_accepting.rb +76 -0
  22. data/bin/commands/completion.rb +27 -0
  23. data/bin/commands/config.rb +245 -0
  24. data/bin/commands/done.rb +235 -0
  25. data/bin/commands/finish.rb +126 -0
  26. data/bin/commands/flag.rb +90 -0
  27. data/bin/commands/grep.rb +108 -0
  28. data/bin/commands/import.rb +71 -0
  29. data/bin/commands/install_fzf.rb +17 -0
  30. data/bin/commands/last.rb +81 -0
  31. data/bin/commands/meanwhile.rb +76 -0
  32. data/bin/commands/note.rb +91 -0
  33. data/bin/commands/now.rb +145 -0
  34. data/bin/commands/on.rb +65 -0
  35. data/bin/commands/open.rb +53 -0
  36. data/bin/commands/plugins.rb +23 -0
  37. data/bin/commands/recent.rb +77 -0
  38. data/bin/commands/redo.rb +26 -0
  39. data/bin/commands/reset.rb +73 -0
  40. data/bin/commands/rotate.rb +42 -0
  41. data/bin/commands/sections.rb +11 -0
  42. data/bin/commands/select.rb +105 -0
  43. data/bin/commands/show.rb +185 -0
  44. data/bin/commands/since.rb +63 -0
  45. data/bin/commands/tag.rb +149 -0
  46. data/bin/commands/tag_dir.rb +29 -0
  47. data/bin/commands/tags.rb +66 -0
  48. data/bin/commands/template.rb +61 -0
  49. data/bin/commands/today.rb +64 -0
  50. data/bin/commands/undo.rb +49 -0
  51. data/bin/commands/view.rb +201 -0
  52. data/bin/commands/views.rb +11 -0
  53. data/bin/commands/yesterday.rb +72 -0
  54. data/bin/doing +241 -3706
  55. data/docs/doc/Array.html +3 -502
  56. data/docs/doc/BooleanTermParser/Clause.html +1 -1
  57. data/docs/doc/BooleanTermParser/Operator.html +1 -1
  58. data/docs/doc/BooleanTermParser/Query.html +1 -1
  59. data/docs/doc/BooleanTermParser/QueryParser.html +1 -1
  60. data/docs/doc/BooleanTermParser/QueryTransformer.html +1 -1
  61. data/docs/doc/BooleanTermParser.html +1 -1
  62. data/docs/doc/Doing/Color.html +62 -56
  63. data/docs/doc/Doing/Completion.html +1 -1
  64. data/docs/doc/Doing/Configuration.html +36 -1
  65. data/docs/doc/Doing/Errors/DoingNoTraceError.html +1 -1
  66. data/docs/doc/Doing/Errors/DoingRuntimeError.html +1 -1
  67. data/docs/doc/Doing/Errors/DoingStandardError.html +1 -1
  68. data/docs/doc/Doing/Errors/EmptyInput.html +1 -1
  69. data/docs/doc/Doing/Errors/NoResults.html +1 -1
  70. data/docs/doc/Doing/Errors/PluginException.html +1 -1
  71. data/docs/doc/Doing/Errors/UserCancelled.html +1 -1
  72. data/docs/doc/Doing/Errors/WrongCommand.html +1 -1
  73. data/docs/doc/Doing/Errors.html +1 -1
  74. data/docs/doc/Doing/Hooks.html +1 -1
  75. data/docs/doc/Doing/Item.html +1 -1
  76. data/docs/doc/Doing/Items.html +2 -2
  77. data/docs/doc/Doing/LogAdapter.html +1 -1
  78. data/docs/doc/Doing/Note.html +2 -2
  79. data/docs/doc/Doing/Pager.html +1 -1
  80. data/docs/doc/Doing/Plugins.html +1 -1
  81. data/docs/doc/Doing/Prompt.html +46 -1
  82. data/docs/doc/Doing/Section.html +1 -1
  83. data/docs/doc/Doing/TemplateString.html +2 -2
  84. data/docs/doc/Doing/Types.html +41 -1
  85. data/docs/doc/Doing/Util/Backup.html +1 -1
  86. data/docs/doc/Doing/Util.html +1 -1
  87. data/docs/doc/Doing/WWID.html +10 -10
  88. data/docs/doc/Doing.html +3 -3
  89. data/docs/doc/FalseClass.html +235 -0
  90. data/docs/doc/GLI/Commands/Help.html +1 -1
  91. data/docs/doc/GLI/Commands/MarkdownDocumentListener.html +1 -1
  92. data/docs/doc/GLI/Commands.html +1 -1
  93. data/docs/doc/GLI.html +1 -1
  94. data/docs/doc/Hash.html +1 -1
  95. data/docs/doc/Numeric.html +1 -1
  96. data/docs/doc/Object.html +203 -0
  97. data/docs/doc/PhraseParser/Operator.html +1 -1
  98. data/docs/doc/PhraseParser/PhraseClause.html +1 -1
  99. data/docs/doc/PhraseParser/Query.html +1 -1
  100. data/docs/doc/PhraseParser/QueryParser.html +1 -1
  101. data/docs/doc/PhraseParser/QueryTransformer.html +1 -1
  102. data/docs/doc/PhraseParser/TermClause.html +1 -1
  103. data/docs/doc/PhraseParser.html +1 -1
  104. data/docs/doc/Status.html +1 -1
  105. data/docs/doc/String.html +287 -3155
  106. data/docs/doc/Symbol.html +40 -6
  107. data/docs/doc/Time.html +1 -1
  108. data/docs/doc/TrueClass.html +235 -0
  109. data/docs/doc/_index.html +5 -10
  110. data/docs/doc/class_list.html +1 -1
  111. data/docs/doc/file.README.html +2 -2
  112. data/docs/doc/index.html +2 -2
  113. data/docs/doc/method_list.html +289 -681
  114. data/docs/doc/top-level-namespace.html +2 -2
  115. data/doing.rdoc +306 -205
  116. data/lib/completion/_doing.zsh +35 -35
  117. data/lib/completion/doing.bash +30 -30
  118. data/lib/completion/doing.fish +88 -78
  119. data/lib/doing/array/array.rb +4 -0
  120. data/lib/doing/array/nested_hash.rb +17 -0
  121. data/lib/doing/{array.rb → array/tags.rb} +7 -25
  122. data/lib/doing/changelog/change.rb +26 -11
  123. data/lib/doing/changelog/changes.rb +31 -4
  124. data/lib/doing/{array_chronify.rb → chronify/array.rb} +0 -0
  125. data/lib/doing/chronify/chronify.rb +5 -0
  126. data/lib/doing/{numeric_chronify.rb → chronify/numeric.rb} +0 -0
  127. data/lib/doing/{string_chronify.rb → chronify/string.rb} +0 -0
  128. data/lib/doing/colors.rb +115 -54
  129. data/lib/doing/configuration.rb +5 -0
  130. data/lib/doing/good.rb +8 -0
  131. data/lib/doing/help_monkey_patch.rb +6 -5
  132. data/lib/doing/item.rb +5 -5
  133. data/lib/doing/items.rb +2 -2
  134. data/lib/doing/log_adapter.rb +35 -2
  135. data/lib/doing/normalize.rb +188 -0
  136. data/lib/doing/pager.rb +1 -0
  137. data/lib/doing/plugins/export/dayone_export.rb +1 -1
  138. data/lib/doing/plugins/export/html_export.rb +1 -1
  139. data/lib/doing/plugins/export/json_export.rb +1 -1
  140. data/lib/doing/plugins/export/markdown_export.rb +1 -1
  141. data/lib/doing/plugins/export/template_export.rb +3 -1
  142. data/lib/doing/prompt.rb +9 -3
  143. data/lib/doing/string/highlight.rb +95 -0
  144. data/lib/doing/string/query.rb +129 -0
  145. data/lib/doing/string/string.rb +12 -0
  146. data/lib/doing/string/tags.rb +164 -0
  147. data/lib/doing/string/transform.rb +168 -0
  148. data/lib/doing/string/truncate.rb +75 -0
  149. data/lib/doing/string/url.rb +82 -0
  150. data/lib/doing/template_string.rb +0 -22
  151. data/lib/doing/types.rb +8 -0
  152. data/lib/doing/util.rb +13 -9
  153. data/lib/doing/version.rb +1 -1
  154. data/lib/doing/wwid.rb +53 -35
  155. data/lib/doing.rb +4 -6
  156. data/lib/examples/commands/wiki.rb +6 -7
  157. data/lib/examples/plugins/wiki_export/wiki_export.rb +1 -1
  158. data/lib/helpers/threaded_tests.rb +39 -20
  159. data/scripts/deploy.rb +107 -0
  160. data/scripts/runtests.sh +4 -0
  161. metadata +63 -8
  162. data/lib/doing/string.rb +0 -765
  163. data/lib/doing/symbol.rb +0 -28
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doing
4
+ ##
5
+ ## URL linking and formatting
6
+ ##
7
+ class ::String
8
+ ##
9
+ ## Turn raw urls into HTML links
10
+ ##
11
+ ## @param opt [Hash] Additional Options
12
+ ##
13
+ ## @option opt [Symbol] :format can be :markdown or
14
+ ## :html (default)
15
+ ##
16
+ def link_urls(**opt)
17
+ fmt = opt.fetch(:format, :html)
18
+ return self unless fmt
19
+
20
+ str = dup
21
+
22
+ str = str.remove_self_links if fmt == :markdown
23
+
24
+ str.replace_qualified_urls(format: fmt).clean_unlinked_urls
25
+ end
26
+
27
+ ## @see #link_urls
28
+ def link_urls!(**opt)
29
+ fmt = opt.fetch(:format, :html)
30
+ replace link_urls(format: fmt)
31
+ end
32
+
33
+ # Remove <self-linked> formatting
34
+ def remove_self_links
35
+ gsub(/<(.*?)>/) do |match|
36
+ m = Regexp.last_match
37
+ if m[1] =~ /^https?:/
38
+ m[1]
39
+ else
40
+ match
41
+ end
42
+ end
43
+ end
44
+
45
+ # Replace qualified urls
46
+ def replace_qualified_urls(**options)
47
+ fmt = options.fetch(:format, :html)
48
+ gsub(%r{(?mi)(?x:
49
+ (?<!["'\[(\\])
50
+ (?<protocol>(?:http|https)://)
51
+ (?<domain>[\w\-]+(?:\.[\w\-]+)+)
52
+ (?<path>[\w\-.,@?^=%&;:/~+#]*[\w\-@^=%&;/~+#])?
53
+ )}) do |_match|
54
+ m = Regexp.last_match
55
+ url = "#{m['domain']}#{m['path']}"
56
+ proto = m['protocol'].nil? ? 'http://' : m['protocol']
57
+ case fmt
58
+ when :terminal
59
+ TTY::Link.link_to("#{proto}#{url}", "#{proto}#{url}")
60
+ when :html
61
+ %(<a href="#{proto}#{url}" title="Link to #{m['domain']}">[#{url}]</a>)
62
+ when :markdown
63
+ "[#{url}](#{proto}#{url})"
64
+ else
65
+ m[0]
66
+ end
67
+ end
68
+ end
69
+
70
+ # Clean up unlinked <urls>
71
+ def clean_unlinked_urls
72
+ gsub(/<(\w+:.*?)>/) do |match|
73
+ m = Regexp.last_match
74
+ if m[1] =~ /<a href/
75
+ match
76
+ else
77
+ %(<a href="#{m[1]}" title="Link to #{m[1]}">[link]</a>)
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -5,28 +5,6 @@ module Doing
5
5
  ## Template string formatting
6
6
  ##
7
7
  class TemplateString < String
8
- class ::String
9
- ##
10
- ## Extract the longest valid color from a string.
11
- ##
12
- ## Allows %colors to bleed into other text and still
13
- ## be recognized, e.g. %greensomething still finds
14
- ## %green.
15
- ##
16
- ## @return [String] a valid color name
17
- ## @api private
18
- def validate_color
19
- valid_color = nil
20
- compiled = ''
21
- split('').each do |char|
22
- compiled += char
23
- valid_color = compiled if Color.attributes.include?(compiled.to_sym)
24
- end
25
-
26
- valid_color
27
- end
28
- end
29
-
30
8
  attr_reader :original
31
9
 
32
10
  include Color
data/lib/doing/types.rb CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  module Doing
4
4
  module Types
5
+ REGEX_CASE = /^[cis].*?$/i.freeze
6
+ REGEX_TAG_SORT = /^(?:n(?:ame)?|t(?:ime)?)$/i.freeze
5
7
  REGEX_BOOL = /^(?:and|all|any|or|not|none|p(?:at(?:tern)?)?)$/i.freeze
6
8
  REGEX_SORT_ORDER = /^(?:a(?:sc)?|d(?:esc)?)$/i.freeze
7
9
  REGEX_VALUE_QUERY = /^(?:!)?@?(?:\S+) +(?:!?[<>=][=*]?|[$*^]=) +(?:.*?)$/.freeze
@@ -22,5 +24,11 @@ module Doing
22
24
  DateRangeString = Class.new(Array)
23
25
  DateRangeOptionalString = Class.new(Array)
24
26
  DateIntervalString = Class.new(DateTime)
27
+ BooleanSymbol = Class.new(Symbol)
28
+ CaseSymbol = Class.new(Symbol)
29
+ AgeSymbol = Class.new(String)
30
+ OrderSymbol = Class.new(Symbol)
31
+ TagSortSymbol = Class.new(Symbol)
32
+ MatchingSymbol = Class.new(Symbol)
25
33
  end
26
34
  end
data/lib/doing/util.rb CHANGED
@@ -170,29 +170,33 @@ module Doing
170
170
 
171
171
  if editor_config[editor_for]
172
172
  editor = editor_config[editor_for]
173
- # Doing.logger.debug('Editor:', "Using #{editor} from config 'editors->#{editor_for}'")
174
- return editor unless editor.good?
173
+ Doing.logger.debug('Editor:', "Using #{editor} from config 'editors.#{editor_for}' for #{editor_for}")
174
+ return editor if editor.good?
175
175
  end
176
176
 
177
177
  if editor_for != 'editor' && editor_config['default']
178
178
  editor = editor_config['default']
179
- # Doing.logger.debug('Editor:', "Using #{editor} from config: 'editors->default'")
180
- return editor unless editor.good?
179
+ Doing.logger.debug('Editor:', "Using #{editor} from config: 'editors.default' for #{editor_for}")
180
+ return editor if editor.good?
181
181
  end
182
182
 
183
183
  editor ||= ENV['DOING_EDITOR'] || ENV['GIT_EDITOR'] || ENV['EDITOR']
184
184
 
185
- unless editor.good?
186
- # Doing.logger.debug('Editor:', "Found editor in environment variables: #{editor}")
185
+ if editor.good?
186
+ Doing.logger.debug('Editor:', "Found editor in environment variables: #{editor} for #{editor_for}")
187
187
  return editor
188
188
  end
189
189
 
190
- Doing.logger.debug('ENV:', 'No EDITOR environment variable, testing available editors')
190
+ Doing.logger.debug('Editor:', 'No EDITOR environment variable, testing available editors')
191
191
  editors = %w[vim vi code subl mate mvim nano emacs]
192
192
  editors.each do |ed|
193
- return TTY::Which.which(ed) if TTY::Which.which(ed)
193
+ try = TTY::Which.which(ed)
194
+ if try
195
+ Doing.logger.debug('Editor:', "Using editor #{try} for #{editor_for}")
196
+ return try
197
+ end
194
198
 
195
- Doing.logger.debug('ENV:', "#{ed} not available")
199
+ Doing.logger.debug('Editor:', "#{ed} not available")
196
200
  end
197
201
 
198
202
  nil
data/lib/doing/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Doing
2
- VERSION = '2.1.25'
2
+ VERSION = '2.1.29'
3
3
  end
data/lib/doing/wwid.rb CHANGED
@@ -659,6 +659,7 @@ module Doing
659
659
  ## @option opt [Array] :val (nil) Array of tag value queries
660
660
  ##
661
661
  def filter_items(items = Items.new, opt: {})
662
+ logger.benchmark(:filter_items, :start)
662
663
  time_rx = /^(\d{1,2}+(:\d{1,2}+)?( *(am|pm))?|midnight|noon)$/
663
664
 
664
665
  if items.nil? || items.empty?
@@ -810,6 +811,8 @@ module Doing
810
811
  output.concat(filtered_items.reverse.slice(0, count))
811
812
  end
812
813
 
814
+ logger.benchmark(:filter_items, :finish)
815
+
813
816
  output
814
817
  end
815
818
 
@@ -1211,6 +1214,7 @@ module Doing
1211
1214
  opt[:sequential] ||= false
1212
1215
  opt[:date] ||= false
1213
1216
  opt[:remove] ||= false
1217
+ opt[:update] ||= false
1214
1218
  opt[:autotag] ||= false
1215
1219
  opt[:back] ||= false
1216
1220
  opt[:unfinished] ||= false
@@ -1323,7 +1327,7 @@ module Doing
1323
1327
  else
1324
1328
  old_title = item.title.dup
1325
1329
  should_date = opt[:date] && item.should_time?
1326
- item.title.tag!('done', remove: true) if tag =~ /done/ && !should_date
1330
+ item.title.tag!('done', remove: true) if tag =~ /done/ && (!should_date || opt[:update])
1327
1331
  item.title.tag!(tag, value: should_date ? done_date.strftime('%F %R') : nil)
1328
1332
  added << tag if old_title != item.title
1329
1333
  end
@@ -1619,6 +1623,7 @@ module Doing
1619
1623
  ## @param opt [Hash] Additional Options
1620
1624
  ##
1621
1625
  def list_section(opt, items: Items.new)
1626
+ logger.benchmark(:list_section, :start)
1622
1627
  opt[:config_template] ||= 'default'
1623
1628
 
1624
1629
  tpl_cfg = @config.dig('templates', opt[:config_template])
@@ -1632,7 +1637,7 @@ module Doing
1632
1637
  cfg.deep_merge({
1633
1638
  'wrap_width' => @config['wrap_width'] || 0,
1634
1639
  'date_format' => @config['default_date_format'],
1635
- 'order' => @config['order'] || 'asc',
1640
+ 'order' => @config['order'] || :asc,
1636
1641
  'tags_color' => @config['tags_color'],
1637
1642
  'duration' => @config['duration'],
1638
1643
  'interval_format' => @config['interval_format']
@@ -1644,8 +1649,8 @@ module Doing
1644
1649
  opt[:age] ||= :newest
1645
1650
  opt[:age] = opt[:age].normalize_age
1646
1651
  opt[:format] ||= cfg['date_format']
1647
- opt[:order] ||= cfg['order'] || 'asc'
1648
- opt[:tag_order] ||= 'asc'
1652
+ opt[:order] ||= cfg['order'] || :asc
1653
+ opt[:tag_order] ||= :asc
1649
1654
  opt[:tags_color] = cfg['tags_color'] || false if opt[:tags_color].nil?
1650
1655
  opt[:template] ||= cfg['template']
1651
1656
 
@@ -1671,7 +1676,7 @@ module Doing
1671
1676
 
1672
1677
  items = filter_items(items, opt: opt)
1673
1678
 
1674
- items.reverse! unless opt[:order] =~ /^d/i
1679
+ items.reverse! unless opt[:order].normalize_order == :desc
1675
1680
 
1676
1681
  if opt[:delete]
1677
1682
  delete_items(items, force: opt[:force])
@@ -1694,6 +1699,7 @@ module Doing
1694
1699
  opt[:output] ||= 'template'
1695
1700
  opt[:wrap_width] ||= @config['templates']['default']['wrap_width'] || 0
1696
1701
 
1702
+ logger.benchmark(:list_section, :finish)
1697
1703
  output(items, title, is_single, opt)
1698
1704
  end
1699
1705
 
@@ -1721,7 +1727,7 @@ module Doing
1721
1727
  destination = guess_section(destination)
1722
1728
 
1723
1729
  if @content.section?(destination) && (@content.section?(section) || archive_all)
1724
- do_archive(section, destination, { count: count, tags: tags, bool: bool, search: options[:search], label: options[:label], before: options[:before] })
1730
+ 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] })
1725
1731
  write(doing_file)
1726
1732
  else
1727
1733
  raise InvalidArgument, 'Either source or destination does not exist'
@@ -1743,7 +1749,7 @@ module Doing
1743
1749
  cfg = @config['templates'][opt[:config_template]].deep_merge(@config['templates']['default'], { extend_existing_arrays: true, sort_merged_arrays: true }).deep_merge({
1744
1750
  'wrap_width' => @config['wrap_width'] || 0,
1745
1751
  'date_format' => @config['default_date_format'],
1746
- 'order' => @config['order'] || 'asc',
1752
+ 'order' => @config['order'] || :asc,
1747
1753
  'tags_color' => @config['tags_color'],
1748
1754
  'duration' => @config['duration'],
1749
1755
  'interval_format' => @config['interval_format']
@@ -1762,7 +1768,7 @@ module Doing
1762
1768
  from: opt[:from],
1763
1769
  format: cfg['date_format'],
1764
1770
  interval_format: opt[:interval_format],
1765
- order: cfg['order'] || 'asc',
1771
+ order: cfg['order'] || :asc,
1766
1772
  output: output,
1767
1773
  section: opt[:section],
1768
1774
  sort_tags: opt[:sort_tags],
@@ -1797,7 +1803,7 @@ module Doing
1797
1803
  list_section({
1798
1804
  section: section,
1799
1805
  count: 0,
1800
- order: 'asc',
1806
+ order: :asc,
1801
1807
  date_filter: dates,
1802
1808
  times: times,
1803
1809
  output: output,
@@ -1863,7 +1869,7 @@ module Doing
1863
1869
  cfg = @config['templates'][opt[:config_template]].deep_merge(@config['templates']['default'], { extend_existing_arrays: true, sort_merged_arrays: true }).deep_merge({
1864
1870
  'wrap_width' => @config['wrap_width'] || 0,
1865
1871
  'date_format' => @config['default_date_format'],
1866
- 'order' => @config['order'] || 'asc',
1872
+ 'order' => @config['order'] || :asc,
1867
1873
  'tags_color' => @config['tags_color'],
1868
1874
  'duration' => @config['duration'],
1869
1875
  'interval_format' => @config['interval_format']
@@ -1879,7 +1885,7 @@ module Doing
1879
1885
  opt[:count] = count
1880
1886
  opt[:format] = cfg['date_format']
1881
1887
  opt[:template] = opt[:template] || cfg['template']
1882
- opt[:order] = 'asc'
1888
+ opt[:order] = :asc
1883
1889
  opt[:times] = times
1884
1890
 
1885
1891
  list_section(opt)
@@ -1896,7 +1902,7 @@ module Doing
1896
1902
  cfg = @config['templates'][options[:config_template]].deep_merge(@config['templates']['default'], { extend_existing_arrays: true, sort_merged_arrays: true }).deep_merge({
1897
1903
  'wrap_width' => @config['wrap_width'] || 0,
1898
1904
  'date_format' => @config['default_date_format'],
1899
- 'order' => @config['order'] || 'asc',
1905
+ 'order' => @config['order'] || :asc,
1900
1906
  'tags_color' => @config['tags_color'],
1901
1907
  'duration' => @config['duration'],
1902
1908
  'interval_format' => @config['interval_format']
@@ -2043,10 +2049,10 @@ module Doing
2043
2049
  ##
2044
2050
  ## @param format [String] return format (html,
2045
2051
  ## json, or text)
2046
- ## @param sort_by_name [Boolean] Sort by name if true, otherwise by time
2047
- ## @param sort_order [String] The sort order (asc or desc)
2052
+ ## @param sort_by [Symbol] Sort by :name or :time
2053
+ ## @param sort_order [Symbol] The sort order (:asc or :desc)
2048
2054
  ##
2049
- def tag_times(format: :text, sort_by_name: false, sort_order: 'asc')
2055
+ def tag_times(format: :text, sort_by: :time, sort_order: :asc)
2050
2056
  return '' if @timers.empty?
2051
2057
 
2052
2058
  max = @timers.keys.sort_by { |k| k.length }.reverse[0].length + 1
@@ -2054,13 +2060,13 @@ module Doing
2054
2060
  total = @timers.delete('All')
2055
2061
 
2056
2062
  tags_data = @timers.delete_if { |_k, v| v == 0 }
2057
- sorted_tags_data = if sort_by_name
2063
+ sorted_tags_data = if sort_by.normalize_tag_sort == :name
2058
2064
  tags_data.sort_by { |k, _v| k }
2059
2065
  else
2060
2066
  tags_data.sort_by { |_k, v| v }
2061
2067
  end
2062
2068
 
2063
- sorted_tags_data.reverse! if sort_order =~ /^asc/i
2069
+ sorted_tags_data.reverse! if sort_order.normalize_order == :asc
2064
2070
  case format
2065
2071
  when :html
2066
2072
 
@@ -2210,9 +2216,9 @@ EOS
2210
2216
  Doing.config_with(ENV['DOING_CONFIG'], { ignore_local: true })
2211
2217
  end
2212
2218
 
2213
- Doing.logger.benchmark(:configure, :start)
2219
+ logger.benchmark(:configure, :start)
2214
2220
  config = Doing.config
2215
- Doing.logger.benchmark(:configure, :finish)
2221
+ logger.benchmark(:configure, :finish)
2216
2222
 
2217
2223
  config.settings['backup_dir'] = ENV['DOING_BACKUP_DIR'] if ENV['DOING_BACKUP_DIR']
2218
2224
  @config = config.settings
@@ -2266,6 +2272,7 @@ EOS
2266
2272
  ## template trigger
2267
2273
  ##
2268
2274
  def output(items, title, is_single, opt)
2275
+ logger.benchmark(:output, :start)
2269
2276
  opt ||= {}
2270
2277
  out = nil
2271
2278
 
@@ -2283,7 +2290,7 @@ EOS
2283
2290
  end
2284
2291
 
2285
2292
  logger.debug('Output:', "#{items.count} #{items.count == 1 ? 'item' : 'items'} shown")
2286
-
2293
+ logger.benchmark(:output, :finish)
2287
2294
  out
2288
2295
  end
2289
2296
 
@@ -2327,26 +2334,37 @@ EOS
2327
2334
  section_items = @content.in_section(section)
2328
2335
  max = section_items.count - count.to_i
2329
2336
 
2337
+ opt[:after] = opt[:from][0] if opt[:from]
2338
+ opt[:before] = opt[:from][1] if opt[:from]
2339
+
2340
+ time_rx = /^(\d{1,2}+(:\d{1,2}+)?( *(am|pm))?|midnight|noon)$/
2341
+
2342
+ if opt[:before].is_a?(String) && opt[:before] =~ time_rx
2343
+ opt[:before] = opt[:before].chronify(guess: :end, future: false)
2344
+ end
2345
+
2346
+ if opt[:after].is_a?(String) && opt[:after] =~ time_rx
2347
+ opt[:after] = opt[:after].chronify(guess: :begin, future: false)
2348
+ end
2349
+
2330
2350
  counter = 0
2331
2351
 
2332
2352
  @content.map do |item|
2333
2353
  break if counter >= max
2334
- if opt[:before]
2335
- time_string = opt[:before]
2336
- cutoff = time_string.chronify(guess: :begin)
2337
- end
2338
2354
 
2339
- if (item.section.downcase != section.downcase && section != /^all$/i) || item.section.downcase == destination.downcase
2340
- item
2341
- elsif ((!tags.empty? && !item.tags?(tags, bool)) || (opt[:search] && !item.search(opt[:search].to_s)) || (opt[:before] && item.date >= cutoff))
2342
- item
2343
- else
2344
- counter += 1
2345
- old_item = item.clone
2346
- item.move_to(destination, label: label, log: false)
2347
- Hooks.trigger :post_entry_updated, self, item, old_item
2348
- item
2349
- end
2355
+ next if item.section.downcase == destination.downcase
2356
+
2357
+ next if item.section.downcase != section.downcase && section != /^all$/i
2358
+
2359
+ next if (opt[:before] && item.date > opt[:before]) || (opt[:after] && item.date < opt[:after])
2360
+
2361
+ next if (!tags.empty? && !item.tags?(tags, bool)) || (opt[:search] && !item.search(opt[:search].to_s))
2362
+
2363
+ counter += 1
2364
+ old_item = item.clone
2365
+ item.move_to(destination, label: label, log: false)
2366
+ Hooks.trigger :post_entry_updated, self, item, old_item
2367
+ item
2350
2368
  end
2351
2369
 
2352
2370
  if counter.positive?
data/lib/doing.rb CHANGED
@@ -28,11 +28,11 @@ require_relative 'doing/hash'
28
28
  require_relative 'doing/types'
29
29
  require_relative 'doing/colors'
30
30
  require_relative 'doing/template_string'
31
- require_relative 'doing/string'
31
+ require_relative 'doing/string/string'
32
32
  require_relative 'doing/time'
33
- require_relative 'doing/array'
33
+ require_relative 'doing/array/array'
34
34
  require_relative 'doing/good'
35
- require_relative 'doing/symbol'
35
+ require_relative 'doing/normalize'
36
36
  require_relative 'doing/util'
37
37
  require_relative 'doing/util_backup'
38
38
  require_relative 'doing/configuration'
@@ -50,9 +50,7 @@ require_relative 'doing/pager'
50
50
  require_relative 'doing/completion'
51
51
  require_relative 'doing/boolean_term_parser'
52
52
  require_relative 'doing/phrase_parser'
53
- require_relative 'doing/array_chronify'
54
- require_relative 'doing/numeric_chronify'
55
- require_relative 'doing/string_chronify'
53
+ require_relative 'doing/chronify/chronify'
56
54
  # require 'doing/markdown_document_listener'
57
55
 
58
56
  # Main doing module
@@ -36,8 +36,7 @@ command :wiki do |c|
36
36
  c.switch [:only_timed], default_value: false, negatable: false
37
37
 
38
38
  c.action do |global, options, args|
39
- wwid = global[:wwid]
40
- tags = wwid.tag_groups([], opt: options)
39
+ tags = @wwid.tag_groups([], opt: options)
41
40
 
42
41
  wiki = Doing::Plugins.plugins.dig(:export, 'wiki', :class)
43
42
 
@@ -46,7 +45,7 @@ command :wiki do |c|
46
45
 
47
46
  raise RuntimeError, 'Missing plugin "wiki"' unless wiki
48
47
 
49
- out = wiki.render(wwid, items, variables: export_options)
48
+ out = wiki.render(@wwid, items, variables: export_options)
50
49
 
51
50
  if out
52
51
  FileUtils.mkdir_p('doing_wiki')
@@ -56,13 +55,13 @@ command :wiki do |c|
56
55
  end
57
56
  end
58
57
 
59
- template = if wwid.config['export_templates']['wiki_index'] && File.exist?(File.expand_path(wwid.config['export_templates']['wiki_index']))
60
- IO.read(File.expand_path(wwid.config['export_templates']['wiki_index']))
58
+ template = if @settings['export_templates']['wiki_index'] && File.exist?(File.expand_path(@settings['export_templates']['wiki_index']))
59
+ IO.read(File.expand_path(@settings['export_templates']['wiki_index']))
61
60
  else
62
61
  wiki.template('wiki_index')
63
62
  end
64
- style = if wwid.config['export_templates']['wiki_css'] && File.exist?(File.expand_path(wwid.config['export_templates']['wiki_css']))
65
- IO.read(File.expand_path(wwid.config['export_templates']['wiki_css']))
63
+ style = if @settings['export_templates']['wiki_css'] && File.exist?(File.expand_path(@settings['export_templates']['wiki_css']))
64
+ IO.read(File.expand_path(@settings['export_templates']['wiki_css']))
66
65
  else
67
66
  wiki.template('wiki_css')
68
67
  end
@@ -74,7 +74,7 @@ module Doing
74
74
  self.template('wiki_css')
75
75
  end
76
76
 
77
- totals = opt[:totals] ? wwid.tag_times(format: :html, sort_by_name: opt[:sort_tags], sort_order: opt[:tag_order]) : ''
77
+ totals = opt[:totals] ? wwid.tag_times(format: :html, sort_by: opt[:sort_tags], sort_order: opt[:tag_order]) : ''
78
78
  engine = Haml::Engine.new(template)
79
79
  Doing.logger.debug('Wiki Export:', "#{items_out.count} items output to #{variables[:page_title]} wiki page")
80
80
  @out = engine.render(Object.new,
@@ -18,17 +18,26 @@ class ThreadedTests
18
18
  start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
19
19
  @results = File.expand_path('results.log')
20
20
 
21
- max_threads = 1000 if max_threads == 0
21
+ max_threads = 1000 if max_threads.to_i == 0
22
22
 
23
23
  c = Doing::Color
24
24
  c.coloring = true
25
25
 
26
- pattern = "test/doing_*#{pattern}*_test.rb"
26
+ shuffle = false
27
+
28
+ unless pattern =~ /shuffle/i
29
+ pattern = "test/doing_*#{pattern}*_test.rb"
30
+ else
31
+ pattern = "test/doing_*_test.rb"
32
+ shuffle = true
33
+ end
27
34
 
28
35
  tests = Dir.glob(pattern)
29
36
 
30
- if max_tests > 0
31
- tests = tests.slice(0, max_tests - 1)
37
+ tests.shuffle! if shuffle
38
+
39
+ if max_tests.to_i > 0
40
+ tests = tests.slice(0, max_tests.to_i - 1)
32
41
  end
33
42
 
34
43
  puts "#{tests.count} test files".boldcyan
@@ -89,24 +98,30 @@ class ThreadedTests
89
98
  finish_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
90
99
 
91
100
  progress.finish
101
+ rescue
102
+ progress.stop
103
+ ensure
104
+ msg = @running_tests.map { |t| t[1].format.uncolor.sub(/^\[:bar\] (.*?):status/, "#{c.bold}#{c.white}\\1#{c.reset}#{t[2]}") }.join("\n")
105
+
106
+ Doing::Prompt.clear_screen(msg)
92
107
 
93
108
  output = []
94
- if @error_out.count.positive?
95
- output << c.boldred("#{@error_out.count} Issues")
96
- else
97
- output << c.green('Success')
98
- end
109
+ output << if @error_out.count.positive?
110
+ c.boldred("#{@error_out.count} Issues")
111
+ else
112
+ c.green('Success')
113
+ end
99
114
  output << c.green("#{@test_total} tests")
100
115
  output << c.cyan("#{@assrt_total} assertions")
101
116
  output << c.yellow("#{(finish_time - start_time).round(3)}s")
102
117
  puts output.join(', ')
103
118
 
104
- puts @error_out.join("\n----\n".boldwhite) if @error_out.count.positive?
105
- rescue
106
- progress.stop
119
+ if @error_out.count.positive?
120
+ res = Doing::Prompt.yn('Display error report?', default_response: false)
121
+ Doing::Pager.paginate = true
122
+ Doing::Pager.page(@error_out.join("\n----\n".boldwhite)) if res
123
+ end
107
124
  end
108
- ensure
109
- FileUtils.rm(@results)
110
125
  end
111
126
 
112
127
  def run_test(s)
@@ -129,10 +144,16 @@ class ThreadedTests
129
144
  end
130
145
 
131
146
  @running_tests.push(s)
132
- out, _err, status = Open3.capture3(ENV, "rake test:#{s[0]} | tee #{@results}", stdin_data: nil)
133
- unless status.success?
134
- m = out.match(/(?<fail>\d+) failures, (?<err>\d+) errors/)
135
- s[2] = ": #{m['fail'].bold.red} #{'failures'.red}, #{m['err'].bold.red} #{'errors'.red}"
147
+ out, _err, status = Open3.capture3(ENV, 'rake', "test:#{s[0]}", stdin_data: nil)
148
+ time = out.match(/^Finished in (?<time>\d+\.\d+) seconds\./)
149
+ count = out.match(/^(?<tests>\d+) tests, (?<assrt>\d+) assertions, (?<fails>\d+) failures, (?<errs>\d+) errors/)
150
+
151
+ unless status.success? && !count['fails'].to_i.positive? && !count['errs'].to_i.positive?
152
+ s[2] = if count
153
+ ": #{count['fails'].bold.red} #{'failures'.red}, #{count['errs'].bold.red} #{'errors'.red}"
154
+ else
155
+ ": #{'Unknown Error'.bold.red}"
156
+ end
136
157
  bar.update(head: '✖'.boldred)
137
158
  bar.advance(head: '✖'.boldred, status: s[2])
138
159
 
@@ -144,8 +165,6 @@ class ThreadedTests
144
165
  Thread.exit
145
166
  end
146
167
 
147
- time = out.match(/^Finished in (?<time>\d+\.\d+) seconds\./)
148
- count = out.match(/^(?<tests>\d+) tests, (?<assrt>\d+) assertions, (?<fails>\d+) failures, (?<errs>\d+) errors/)
149
168
  s[2] = [
150
169
  ': ',
151
170
  count['tests'].green,