doing 2.1.26 → 2.1.30

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 (156) 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 +52 -0
  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 +2 -1
  11. data/README.md +1 -1
  12. data/Rakefile +2 -3
  13. data/bin/commands/add_section.rb +2 -0
  14. data/bin/commands/again.rb +23 -65
  15. data/bin/commands/archive.rb +20 -61
  16. data/bin/commands/cancel.rb +27 -69
  17. data/bin/commands/changes.rb +53 -12
  18. data/bin/commands/colors.rb +4 -2
  19. data/bin/commands/commands.rb +4 -2
  20. data/bin/commands/commands_accepting.rb +62 -11
  21. data/bin/commands/completion.rb +10 -7
  22. data/bin/commands/config.rb +8 -8
  23. data/bin/commands/done.rb +3 -17
  24. data/bin/commands/finish.rb +7 -30
  25. data/bin/commands/flag.rb +15 -51
  26. data/bin/commands/grep.rb +12 -28
  27. data/bin/commands/import.rb +3 -33
  28. data/bin/commands/last.rb +3 -36
  29. data/bin/commands/meanwhile.rb +3 -13
  30. data/bin/commands/note.rb +13 -52
  31. data/bin/commands/now.rb +15 -21
  32. data/bin/commands/on.rb +3 -4
  33. data/bin/commands/open.rb +3 -3
  34. data/bin/commands/recent.rb +3 -4
  35. data/bin/commands/redo.rb +6 -2
  36. data/bin/commands/reset.rb +19 -52
  37. data/bin/commands/rotate.rb +5 -36
  38. data/bin/commands/select.rb +23 -41
  39. data/bin/commands/show.rb +28 -74
  40. data/bin/commands/since.rb +3 -4
  41. data/bin/commands/tag.rb +4 -34
  42. data/bin/commands/tags.rb +5 -32
  43. data/bin/commands/today.rb +3 -4
  44. data/bin/commands/view.rb +36 -73
  45. data/bin/commands/yesterday.rb +4 -5
  46. data/bin/doing +150 -13
  47. data/docs/doc/Array.html +3 -502
  48. data/docs/doc/BooleanTermParser/Clause.html +1 -1
  49. data/docs/doc/BooleanTermParser/Operator.html +1 -1
  50. data/docs/doc/BooleanTermParser/Query.html +1 -1
  51. data/docs/doc/BooleanTermParser/QueryParser.html +1 -1
  52. data/docs/doc/BooleanTermParser/QueryTransformer.html +1 -1
  53. data/docs/doc/BooleanTermParser.html +1 -1
  54. data/docs/doc/Doing/Color.html +62 -56
  55. data/docs/doc/Doing/Completion.html +1 -1
  56. data/docs/doc/Doing/Configuration.html +35 -1
  57. data/docs/doc/Doing/Errors/DoingNoTraceError.html +1 -1
  58. data/docs/doc/Doing/Errors/DoingRuntimeError.html +1 -1
  59. data/docs/doc/Doing/Errors/DoingStandardError.html +1 -1
  60. data/docs/doc/Doing/Errors/EmptyInput.html +1 -1
  61. data/docs/doc/Doing/Errors/NoResults.html +1 -1
  62. data/docs/doc/Doing/Errors/PluginException.html +1 -1
  63. data/docs/doc/Doing/Errors/UserCancelled.html +1 -1
  64. data/docs/doc/Doing/Errors/WrongCommand.html +1 -1
  65. data/docs/doc/Doing/Errors.html +1 -1
  66. data/docs/doc/Doing/Hooks.html +1 -1
  67. data/docs/doc/Doing/Item.html +1 -1
  68. data/docs/doc/Doing/Items.html +2 -2
  69. data/docs/doc/Doing/LogAdapter.html +1 -1
  70. data/docs/doc/Doing/Note.html +2 -2
  71. data/docs/doc/Doing/Pager.html +1 -1
  72. data/docs/doc/Doing/Plugins.html +1 -1
  73. data/docs/doc/Doing/Prompt.html +1 -1
  74. data/docs/doc/Doing/Section.html +1 -1
  75. data/docs/doc/Doing/TemplateString.html +2 -2
  76. data/docs/doc/Doing/Types.html +41 -1
  77. data/docs/doc/Doing/Util/Backup.html +1 -1
  78. data/docs/doc/Doing/Util.html +1 -1
  79. data/docs/doc/Doing/WWID.html +10 -10
  80. data/docs/doc/Doing.html +3 -3
  81. data/docs/doc/FalseClass.html +35 -1
  82. data/docs/doc/GLI/Commands/Help.html +1 -1
  83. data/docs/doc/GLI/Commands/MarkdownDocumentListener.html +1 -1
  84. data/docs/doc/GLI/Commands.html +1 -1
  85. data/docs/doc/GLI.html +1 -1
  86. data/docs/doc/Hash.html +1 -1
  87. data/docs/doc/Object.html +1 -1
  88. data/docs/doc/PhraseParser/Operator.html +1 -1
  89. data/docs/doc/PhraseParser/PhraseClause.html +1 -1
  90. data/docs/doc/PhraseParser/Query.html +1 -1
  91. data/docs/doc/PhraseParser/QueryParser.html +1 -1
  92. data/docs/doc/PhraseParser/QueryTransformer.html +1 -1
  93. data/docs/doc/PhraseParser/TermClause.html +1 -1
  94. data/docs/doc/PhraseParser.html +1 -1
  95. data/docs/doc/Status.html +1 -1
  96. data/docs/doc/String.html +287 -3155
  97. data/docs/doc/Symbol.html +40 -6
  98. data/docs/doc/Time.html +1 -1
  99. data/docs/doc/TrueClass.html +35 -1
  100. data/docs/doc/_index.html +5 -10
  101. data/docs/doc/class_list.html +1 -1
  102. data/docs/doc/file.README.html +2 -2
  103. data/docs/doc/index.html +2 -2
  104. data/docs/doc/method_list.html +278 -678
  105. data/docs/doc/top-level-namespace.html +2 -2
  106. data/doing.gemspec +1 -0
  107. data/doing.rdoc +297 -206
  108. data/lib/completion/_doing.zsh +32 -32
  109. data/lib/completion/doing.bash +30 -30
  110. data/lib/completion/doing.fish +87 -77
  111. data/lib/doing/array/array.rb +4 -0
  112. data/lib/doing/array/nested_hash.rb +17 -0
  113. data/lib/doing/{array.rb → array/tags.rb} +7 -25
  114. data/lib/doing/changelog/change.rb +26 -11
  115. data/lib/doing/changelog/changes.rb +37 -8
  116. data/lib/doing/changelog/version.rb +11 -3
  117. data/lib/doing/{array_chronify.rb → chronify/array.rb} +0 -0
  118. data/lib/doing/chronify/chronify.rb +5 -0
  119. data/lib/doing/{numeric_chronify.rb → chronify/numeric.rb} +0 -0
  120. data/lib/doing/{string_chronify.rb → chronify/string.rb} +0 -0
  121. data/lib/doing/colors.rb +115 -54
  122. data/lib/doing/completion/zsh_completion.rb +5 -0
  123. data/lib/doing/configuration.rb +9 -5
  124. data/lib/doing/good.rb +8 -0
  125. data/lib/doing/help_monkey_patch.rb +6 -5
  126. data/lib/doing/item.rb +5 -5
  127. data/lib/doing/items.rb +2 -2
  128. data/lib/doing/log_adapter.rb +35 -2
  129. data/lib/doing/normalize.rb +188 -0
  130. data/lib/doing/plugins/export/dayone_export.rb +1 -1
  131. data/lib/doing/plugins/export/html_export.rb +1 -1
  132. data/lib/doing/plugins/export/json_export.rb +1 -1
  133. data/lib/doing/plugins/export/markdown_export.rb +1 -1
  134. data/lib/doing/plugins/export/template_export.rb +3 -1
  135. data/lib/doing/prompt.rb +1 -3
  136. data/lib/doing/section.rb +1 -1
  137. data/lib/doing/string/highlight.rb +95 -0
  138. data/lib/doing/string/query.rb +129 -0
  139. data/lib/doing/string/string.rb +12 -0
  140. data/lib/doing/string/tags.rb +164 -0
  141. data/lib/doing/string/transform.rb +168 -0
  142. data/lib/doing/string/truncate.rb +75 -0
  143. data/lib/doing/string/url.rb +82 -0
  144. data/lib/doing/template_string.rb +0 -22
  145. data/lib/doing/types.rb +8 -0
  146. data/lib/doing/util.rb +13 -9
  147. data/lib/doing/version.rb +1 -1
  148. data/lib/doing/wwid.rb +54 -36
  149. data/lib/doing.rb +5 -6
  150. data/lib/examples/plugins/wiki_export/wiki_export.rb +1 -1
  151. data/lib/helpers/threaded_tests.rb +15 -2
  152. data/scripts/deploy.rb +107 -0
  153. data/scripts/runtests.sh +4 -0
  154. metadata +39 -8
  155. data/lib/doing/string.rb +0 -765
  156. data/lib/doing/symbol.rb +0 -28
data/bin/commands/last.rb CHANGED
@@ -21,18 +21,6 @@ command :last do |c|
21
21
  c.desc "Delete the last entry"
22
22
  c.switch %i[d delete], negatable: false, default_value: false
23
23
 
24
- c.desc 'Tag filter, combine multiple tags with a comma. Wildcards allowed (*, ?)'
25
- c.arg_name 'TAG'
26
- c.flag [:tag], type: TagArray
27
-
28
- c.desc 'Tag boolean (AND|OR|NOT). Use PATTERN to parse + and - as booleans'
29
- c.arg_name 'BOOLEAN'
30
- c.flag [:bool], must_match: REGEX_BOOL, default_value: 'PATTERN'
31
-
32
- c.desc 'Search filter, surround with slashes for regex (/query/), start with single quote for exact match ("\'query")'
33
- c.arg_name 'QUERY'
34
- c.flag [:search]
35
-
36
24
  c.desc "Output using a template from configuration"
37
25
  c.arg_name 'TEMPLATE_KEY'
38
26
  c.flag [:config_template], type: TemplateName, default_value: 'last'
@@ -44,38 +32,17 @@ command :last do |c|
44
32
  c.desc "Highlight search matches in output. Only affects command line output"
45
33
  c.switch %i[h hilite], default_value: @settings.dig('search', 'highlight')
46
34
 
47
- c.desc 'Perform a tag value query ("@done > two hours ago" or "@progress < 50"). May be used multiple times, combined with --bool'
48
- c.arg_name 'QUERY'
49
- c.flag [:val], multiple: true, must_match: REGEX_VALUE_QUERY
50
-
51
35
  c.desc 'Show elapsed time if entry is not tagged @done'
52
36
  c.switch [:duration]
53
37
 
54
- # c.desc '[DEPRECATED] Use alternative fuzzy matching for search string'
55
- # c.switch [:fuzzy], default_value: false, negatable: false
56
-
57
- c.desc 'Force exact search string matching (case sensitive)'
58
- c.switch %i[x exact], default_value: @config.exact_match?, negatable: @config.exact_match?
59
-
60
- c.desc 'Show items that *don\'t* match search string or tag filter'
61
- c.switch [:not], default_value: false, negatable: false
62
-
63
- c.desc 'Case sensitivity for search string matching [(c)ase-sensitive, (i)gnore, (s)mart]'
64
- c.arg_name 'TYPE'
65
- c.flag [:case], must_match: /^[csi]/, default_value: @settings.dig('search', 'case')
38
+ add_options(:search, c)
39
+ add_options(:tag_filter, c)
66
40
 
67
41
  c.action do |global_options, options, _args|
68
42
  options[:fuzzy] = false
69
43
  raise InvalidArgument, '--tag and --search can not be used together' if options[:tag] && options[:search]
70
44
 
71
- if options[:tag].nil?
72
- options[:tag] = []
73
- else
74
- options[:tag] = options[:tag]
75
- options[:bool] = options[:bool].normalize_bool
76
- end
77
-
78
- options[:case] = options[:case].normalize_case
45
+ options[:tag] ||= []
79
46
 
80
47
  options[:search] = options[:search].sub(/^'?/, "'") if options[:search] && options[:exact]
81
48
 
@@ -15,24 +15,14 @@ command :meanwhile do |c|
15
15
  c.arg_name 'NAME'
16
16
  c.flag %i[s section]
17
17
 
18
- c.desc "Edit entry with #{Doing::Util.default_editor}"
19
- c.switch %i[e editor], negatable: false, default_value: false
20
-
21
18
  c.desc 'Archive previous @meanwhile entry'
22
19
  c.switch %i[a archive], negatable: false, default_value: false
23
20
 
24
- c.desc 'Backdate start date for new entry to date string [4pm|20m|2h|yesterday noon]'
25
- c.arg_name 'DATE_STRING'
26
- c.flag %i[b back started], type: DateBeginString
27
-
28
- c.desc 'Note'
29
- c.arg_name 'TEXT'
30
- c.flag %i[n note]
31
-
32
- c.desc 'Prompt for note via multi-line input'
33
- c.switch %i[ask], negatable: false, default_value: false
21
+ add_options(:add_entry, c)
34
22
 
35
23
  c.action do |_global_options, options, args|
24
+ @wwid.auto_tag = !options[:noauto]
25
+
36
26
  if options[:back]
37
27
  date = options[:back]
38
28
 
data/bin/commands/note.rb CHANGED
@@ -24,50 +24,19 @@ command :note do |c|
24
24
  c.desc "Replace/Remove last entry's note (default append)"
25
25
  c.switch %i[r remove], negatable: false, default_value: false
26
26
 
27
- c.desc 'Add/remove note from last entry matching tag. Wildcards allowed (*, ?)'
28
- c.arg_name 'TAG'
29
- c.flag [:tag], type: TagArray
30
-
31
- c.desc 'Add/remove note from last entry matching search filter, surround with slashes for regex (e.g. "/query.*/"), start with single quote for exact match ("\'query")'
32
- c.arg_name 'QUERY'
33
- c.flag [:search]
34
-
35
- c.desc 'Perform a tag value query ("@done > two hours ago" or "@progress < 50"). May be used multiple times, combined with --bool'
36
- c.arg_name 'QUERY'
37
- c.flag [:val], multiple: true, must_match: REGEX_VALUE_QUERY
38
-
39
- # c.desc '[DEPRECATED] Use alternative fuzzy matching for search string'
40
- # c.switch [:fuzzy], default_value: false, negatable: false
41
-
42
- c.desc 'Force exact search string matching (case sensitive)'
43
- c.switch %i[x exact], default_value: @config.exact_match?, negatable: @config.exact_match?
44
-
45
- c.desc 'Add note to item that *doesn\'t* match search/tag filters'
46
- c.switch [:not], default_value: false, negatable: false
47
-
48
- c.desc 'Case sensitivity for search string matching [(c)ase-sensitive, (i)gnore, (s)mart]'
49
- c.arg_name 'TYPE'
50
- c.flag [:case], must_match: /^[csi]/, default_value: @settings.dig('search', 'case')
51
-
52
- c.desc 'Boolean (AND|OR|NOT) with which to combine multiple tag filters. Use PATTERN to parse + and - as booleans'
53
- c.arg_name 'BOOLEAN'
54
- c.flag [:bool], must_match: REGEX_BOOL, default_value: 'PATTERN'
55
-
56
27
  c.desc 'Select item for new note from a menu of matching entries'
57
28
  c.switch %i[i interactive], negatable: false, default_value: false
58
29
 
59
30
  c.desc 'Prompt for note via multi-line input'
60
31
  c.switch %i[ask], negatable: false, default_value: false
61
32
 
33
+ add_options(:search, c)
34
+ add_options(:tag_filter, c)
35
+
62
36
  c.action do |_global_options, options, args|
63
37
  options[:fuzzy] = false
64
- if options[:section]
65
- options[:section] = @wwid.guess_section(options[:section]) || options[:section].cap_first
66
- end
67
-
68
- options[:tag_bool] = options[:bool].normalize_bool
69
-
70
- options[:case] = options[:case].normalize_case
38
+ options[:section] = @wwid.guess_section(options[:section]) || options[:section].cap_first if options[:section]
39
+ options[:tag_bool] = options[:bool]
71
40
 
72
41
  if options[:search]
73
42
  search = options[:search]
@@ -78,30 +47,22 @@ command :note do |c|
78
47
  last_entry = @wwid.last_entry(options)
79
48
  old_entry = last_entry.clone
80
49
 
81
- unless last_entry
82
- Doing.logger.warn('Not found:', 'No entry matching parameters was found.')
83
- return
84
- end
50
+ raise NoResults, 'No entry matching parameters was found.' unless last_entry
85
51
 
86
52
  last_note = last_entry.note || Doing::Note.new
87
53
  new_note = Doing::Note.new
88
54
 
89
- if $stdin.stat.size.positive?
90
- new_note.add($stdin.read.strip)
91
- end
92
-
93
- unless args.empty?
94
- new_note.add(args.join(' '))
95
- end
55
+ new_note.add($stdin.read.strip) if $stdin.stat.size.positive?
56
+ new_note.add(args.join(' ')) unless args.empty?
96
57
 
97
58
  if options[:editor]
98
59
  raise MissingEditor, 'No EDITOR variable defined in environment' if Doing::Util.default_editor.nil?
99
60
 
100
- if options[:remove]
101
- input = Doing::Note.new
102
- else
103
- input = last_entry.note || Doing::Note.new
104
- end
61
+ input = if options[:remove]
62
+ Doing::Note.new
63
+ else
64
+ last_entry.note || Doing::Note.new
65
+ end
105
66
 
106
67
  input.add(new_note)
107
68
 
data/bin/commands/now.rb CHANGED
@@ -20,13 +20,6 @@ command %i[now next] do |c|
20
20
  c.arg_name 'NAME'
21
21
  c.flag %i[s section]
22
22
 
23
- c.desc "Edit entry with #{Doing::Util.default_editor}"
24
- c.switch %i[e editor], negatable: false, default_value: false
25
-
26
- c.desc 'Backdate start time [4pm|20m|2h|"yesterday noon"]'
27
- c.arg_name 'DATE_STRING'
28
- c.flag %i[b back started], type: DateBeginString
29
-
30
23
  c.desc %(
31
24
  Set a start and optionally end time as a date range ("from 1pm to 2:30pm").
32
25
  If an end time is provided, a dated @done tag will be added
@@ -37,19 +30,16 @@ command %i[now next] do |c|
37
30
  c.desc 'Timed entry, marks last entry in section as @done'
38
31
  c.switch %i[f finish_last], negatable: false, default_value: false
39
32
 
40
- c.desc 'Include a note'
41
- c.arg_name 'TEXT'
42
- c.flag %i[n note]
43
-
44
- c.desc 'Prompt for note via multi-line input'
45
- c.switch %i[ask], negatable: false, default_value: false
33
+ add_options(:add_entry, c)
46
34
 
47
35
  # c.desc "Edit entry with specified app"
48
36
  # c.arg_name 'editor_app'
49
37
  # # c.flag [:a, :app]
50
38
 
51
39
  c.action do |_global_options, options, args|
52
- raise InvalidArgument, "--back and --from cannot be used together" if options[:back] && options[:from]
40
+ @wwid.auto_tag = !options[:noauto]
41
+
42
+ raise InvalidArgument, '--back and --from cannot be used together' if options[:back] && options[:from]
53
43
 
54
44
  if options[:back]
55
45
  date = options[:back]
@@ -61,13 +51,17 @@ command %i[now next] do |c|
61
51
  end
62
52
  raise InvalidTimeExpression.new('unable to parse date string', topic: 'Parser:') if date.nil?
63
53
 
64
- if options[:section]
65
- section = @wwid.guess_section(options[:section]) || options[:section].cap_first
66
- else
67
- options[:section] = @settings['current_section']
68
- end
69
-
70
- ask_note = options[:ask] && !options[:editor] && args.count.positive? ? Doing::Prompt.read_lines(prompt: 'Add a note') : ''
54
+ section = if options[:section]
55
+ @wwid.guess_section(options[:section]) || options[:section].cap_first
56
+ else
57
+ @settings['current_section']
58
+ end
59
+
60
+ ask_note = if options[:ask] && !options[:editor] && args.count.positive?
61
+ Doing::Prompt.read_lines(prompt: 'Add a note')
62
+ else
63
+ ''
64
+ end
71
65
 
72
66
  if options[:editor]
73
67
  raise MissingEditor, 'No EDITOR variable defined in environment' if Doing::Util.default_editor.nil?
data/bin/commands/on.rb CHANGED
@@ -23,10 +23,9 @@ command :on do |c|
23
23
  c.switch [:totals], default_value: false, negatable: false
24
24
 
25
25
  c.desc 'Sort tags by (name|time)'
26
- default = 'time'
27
- default = @settings['tag_sort'] || 'name'
26
+ default = @settings['tag_sort'].normalize_tag_sort || :name
28
27
  c.arg_name 'KEY'
29
- c.flag [:tag_sort], must_match: /^(?:name|time)$/i, default_value: default
28
+ c.flag [:tag_sort], must_match: REGEX_TAG_SORT, default_value: default, type: TagSortSymbol
30
29
 
31
30
  c.desc "Output to export format (#{Doing::Plugins.plugin_names(type: :export)})"
32
31
  c.arg_name 'FORMAT'
@@ -58,7 +57,7 @@ command :on do |c|
58
57
  Doing.logger.debug('Interpreter:', message)
59
58
 
60
59
  options[:times] = true if options[:totals]
61
- options[:sort_tags] = options[:tag_sort] =~ /^n/i
60
+ options[:sort_tags] = options[:tag_sort]
62
61
 
63
62
  Doing::Pager.page @wwid.list_date([start, finish], options[:section], options[:times], options[:output],
64
63
  { template: options[:template], config_template: options[:config_template], duration: options[:duration], totals: options[:totals], sort_tags: options[:sort_tags] }).chomp
data/bin/commands/open.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # @@open
2
2
  desc 'Open the "doing" file in an editor'
3
- long_desc "`doing open` defaults to using the editors->doing_file setting
3
+ long_desc "`doing open` defaults to using the editors.doing_file setting
4
4
  in #{@config.config_file} (#{Doing::Util.find_default_editor('doing_file')})."
5
5
  command :open do |c|
6
6
  c.example 'doing open', desc: 'Open the doing file in the default editor'
@@ -8,7 +8,7 @@ command :open do |c|
8
8
  c.arg_name 'COMMAND'
9
9
  c.flag %i[e editor]
10
10
 
11
- if `uname` =~ /Darwin/
11
+ if Sys::Platform.mac?
12
12
  c.desc 'Open with app name'
13
13
  c.arg_name 'APP_NAME'
14
14
  c.flag %i[a app]
@@ -29,7 +29,7 @@ command :open do |c|
29
29
 
30
30
  editor = TTY::Which.which(options[:editor])
31
31
  system %(#{editor} "#{File.expand_path(@wwid.doing_file)}")
32
- elsif `uname` =~ /Darwin/
32
+ elsif Sys::Platform.mac?
33
33
  if options[:app]
34
34
  system %(open -a "#{options[:app]}" "#{File.expand_path(@wwid.doing_file)}")
35
35
  elsif options[:bundle_id]
@@ -30,10 +30,9 @@ command :recent do |c|
30
30
  c.switch [:totals], default_value: false, negatable: false
31
31
 
32
32
  c.desc 'Sort tags by (name|time)'
33
- default = 'time'
34
- default = @settings['tag_sort'] || 'name'
33
+ default = @settings['tag_sort'].normalize_tag_sort || :name
35
34
  c.arg_name 'KEY'
36
- c.flag [:tag_sort], must_match: /^(?:name|time)$/i, default_value: default
35
+ c.flag [:tag_sort], must_match: REGEX_TAG_SORT, default_value: default, type: TagSortSymbol
37
36
 
38
37
  c.desc 'Select from a menu of matching entries to perform additional operations'
39
38
  c.switch %i[i interactive], negatable: false, default_value: false
@@ -55,7 +54,7 @@ command :recent do |c|
55
54
  end
56
55
 
57
56
  options[:times] = true if options[:totals]
58
- options[:sort_tags] = options[:tag_sort] =~ /^n/i
57
+ options[:sort_tags] = options[:tag_sort]
59
58
 
60
59
  template = @settings['templates']['recent'].deep_merge(@settings['templates']['default'])
61
60
  tags_color = template.key?('tags_color') ? template['tags_color'] : nil
data/bin/commands/redo.rb CHANGED
@@ -1,5 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # @@redo
2
- long_desc 'Shortcut for `doing undo -r`, reverses the last undo command. You cannot undo a redo'
4
+ desc 'Redo an undo command'
5
+ long_desc 'Shortcut for `doing undo -r`, reverses the last undo command. Specify a count to redo multiple undos'
3
6
  arg_name 'COUNT'
4
7
  command :redo do |c|
5
8
  c.desc 'Specify alternate doing file'
@@ -12,7 +15,8 @@ command :redo do |c|
12
15
  c.action do |_global, options, args|
13
16
  file = options[:file] || @wwid.doing_file
14
17
  count = args.empty? ? 1 : args[0].to_i
15
- raise InvalidArgument, "Invalid count specified for redo" unless count&.positive?
18
+ raise InvalidArgument, 'Invalid count specified for redo' unless count&.positive?
19
+
16
20
  if options[:interactive]
17
21
  Doing::Util::Backup.select_redo(file)
18
22
  else
@@ -20,54 +20,24 @@ command %i[reset begin] do |c|
20
20
  c.desc 'Change start date but do not remove @done (shortcut for --no-resume)'
21
21
  c.switch [:n]
22
22
 
23
- c.desc 'Reset last entry matching tag. Wildcards allowed (*, ?)'
24
- c.arg_name 'TAG'
25
- c.flag [:tag]
26
-
27
- c.desc 'Reset last entry matching search filter, surround with slashes for regex (e.g. "/query.*/"), start with single quote for exact match ("\'query")'
28
- c.arg_name 'QUERY'
29
- c.flag [:search]
30
-
31
- c.desc 'Perform a tag value query ("@done > two hours ago" or "@progress < 50"). May be used multiple times, combined with --bool'
32
- c.arg_name 'QUERY'
33
- c.flag [:val], multiple: true, must_match: REGEX_VALUE_QUERY
34
-
35
- # c.desc '[DEPRECATED] Use alternative fuzzy matching for search string'
36
- # c.switch [:fuzzy], default_value: false, negatable: false
37
-
38
- c.desc 'Force exact search string matching (case sensitive)'
39
- c.switch %i[x exact], default_value: @config.exact_match?, negatable: @config.exact_match?
40
-
41
- c.desc 'Reset items that *don\'t* match search/tag filters'
42
- c.switch [:not], default_value: false, negatable: false
43
-
44
- c.desc 'Case sensitivity for search string matching [(c)ase-sensitive, (i)gnore, (s)mart]'
45
- c.arg_name 'TYPE'
46
- c.flag [:case], must_match: /^[csi]/, default_value: @settings.dig('search', 'case')
47
-
48
- c.desc 'Boolean (AND|OR|NOT) with which to combine multiple tag filters. Use PATTERN to parse + and - as booleans'
49
- c.arg_name 'BOOLEAN'
50
- c.flag [:bool], must_match: REGEX_BOOL, default_value: 'PATTERN'
51
-
52
23
  c.desc 'Select from a menu of matching entries'
53
24
  c.switch %i[i interactive], negatable: false, default_value: false
54
25
 
26
+ add_options(:search, c)
27
+ add_options(:tag_filter, c)
28
+
55
29
  c.action do |global_options, options, args|
56
- if args.count > 0
30
+ if args.count.positive?
57
31
  reset_date = args.join(' ').chronify(guess: :begin)
58
32
  raise InvalidArgument, 'Invalid date string' unless reset_date
33
+
59
34
  else
60
35
  reset_date = Time.now
61
36
  end
62
37
 
63
38
  options[:fuzzy] = false
64
- if options[:section]
65
- options[:section] = @wwid.guess_section(options[:section]) || options[:section].cap_first
66
- end
67
39
 
68
- options[:bool] = options[:bool].normalize_bool
69
-
70
- options[:case] = options[:case].normalize_case
40
+ options[:section] = @wwid.guess_section(options[:section]) || options[:section].cap_first if options[:section]
71
41
 
72
42
  if options[:search]
73
43
  search = options[:search]
@@ -75,25 +45,22 @@ command %i[reset begin] do |c|
75
45
  options[:search] = search
76
46
  end
77
47
 
78
-
79
48
  items = @wwid.filter_items([], opt: options)
80
49
 
81
- if options[:interactive]
82
- last_entry = Doing::Prompt.choose_from_items(items, include_section: options[:section].nil?,
83
- menu: true,
84
- header: '',
85
- prompt: 'Select an entry to start/reset > ',
86
- multiple: false,
87
- sort: false,
88
- show_if_single: true)
89
- else
90
- last_entry = items.reverse.last
91
- end
50
+ last_entry = if options[:interactive]
51
+ Doing::Prompt.choose_from_items(items, include_section: options[:section].nil?,
52
+ menu: true,
53
+ header: '',
54
+ prompt: 'Select an entry to start/reset > ',
55
+ multiple: false,
56
+ sort: false,
57
+ show_if_single: true)
58
+ else
59
+ items.reverse.last
60
+ end
92
61
 
93
- unless last_entry
94
- Doing.logger.warn('Not found:', 'No entry matching parameters was found.')
95
- return
96
- end
62
+
63
+ raise NoResults, 'No entry matching parameters was found.' unless last_entry
97
64
 
98
65
  old_item = last_entry.clone
99
66
 
@@ -16,49 +16,18 @@ command :rotate do |c|
16
16
  c.arg_name 'SECTION_NAME'
17
17
  c.flag %i[s section], default_value: 'All'
18
18
 
19
- c.desc 'Tag filter, combine multiple tags with a comma. Wildcards allowed (*, ?). Added for compatibility with other commands'
20
- c.arg_name 'TAG'
21
- c.flag [:tag]
22
-
23
- c.desc 'Tag boolean (AND|OR|NOT). Use PATTERN to parse + and - as booleans'
24
- c.arg_name 'BOOLEAN'
25
- c.flag [:bool], must_match: REGEX_BOOL, default_value: 'PATTERN'
26
-
27
- c.desc 'Search filter'
28
- c.arg_name 'QUERY'
29
- c.flag [:search]
30
-
31
- c.desc 'Perform a tag value query ("@done > two hours ago" or "@progress < 50"). May be used multiple times, combined with --bool'
32
- c.arg_name 'QUERY'
33
- c.flag [:val], multiple: true, must_match: REGEX_VALUE_QUERY
34
-
35
- # c.desc '[DEPRECATED] Use alternative fuzzy matching for search string'
36
- # c.switch [:fuzzy], default_value: false, negatable: false
37
-
38
- c.desc 'Force exact search string matching (case sensitive)'
39
- c.switch %i[x exact], default_value: @config.exact_match?, negatable: @config.exact_match?
40
-
41
- c.desc 'Rotate items that *don\'t* match search string or tag filter'
42
- c.switch [:not], default_value: false, negatable: false
43
-
44
- c.desc 'Case sensitivity for search string matching [(c)ase-sensitive, (i)gnore, (s)mart]'
45
- c.arg_name 'TYPE'
46
- c.flag [:case], must_match: /^[csi]/, default_value: @settings.dig('search', 'case')
47
-
48
19
  c.desc 'Rotate entries older than date
49
20
  (Flexible date format, e.g. 1/27/2021, 2020-07-19, or Monday 3pm)'
50
21
  c.arg_name 'DATE_STRING'
51
22
  c.flag [:before]
52
23
 
53
- c.action do |_global_options, options, args|
54
- options[:fuzzy] = false
55
- if options[:section] && options[:section] !~ /^all$/i
56
- options[:section] = @wwid.guess_section(options[:section])
57
- end
24
+ add_options(:search, c)
25
+ add_options(:tag_filter, c)
58
26
 
59
- options[:bool] = options[:bool].normalize_bool
27
+ c.action do |_global_options, options, _args|
28
+ options[:fuzzy] = false
60
29
 
61
- options[:case] = options[:case].normalize_case
30
+ options[:section] = @wwid.guess_section(options[:section]) if options[:section] && options[:section] !~ /^all$/i
62
31
 
63
32
  search = nil
64
33
 
@@ -14,9 +14,13 @@ sbtrkt fuzzy-match Items that match s*b*t*r*k*t
14
14
 
15
15
  !fire inverse-exact-match Items that do not include fire'
16
16
  command :select do |c|
17
- c.example 'doing select', desc: 'Select from all entries. A menu of available actions will be presented after confirming the selection.'
18
- c.example 'doing select --editor', desc: 'Select entries from a menu and batch edit them in your default editor'
19
- c.example 'doing select --after "yesterday 12pm" --tag project1', desc: 'Display a menu of entries created after noon yesterday, add @project1 to selected entries'
17
+ c.example 'doing select',
18
+ desc: 'Select from all entries. A menu of actions will be presented after confirming the selection.'
19
+ c.example 'doing select --editor',
20
+ desc: 'Select entries from a menu and batch edit them in your default editor'
21
+ c.example 'doing select --after "yesterday 12pm" --tag project1',
22
+ desc: 'Display a menu of entries created after noon yesterday, add @project1 to selected entries'
23
+
20
24
  c.desc 'Select from a specific section'
21
25
  c.arg_name 'SECTION'
22
26
  c.flag %i[s section]
@@ -38,48 +42,21 @@ command :select do |c|
38
42
  c.arg_name 'SECTION'
39
43
  c.flag %i[m move]
40
44
 
41
- c.desc 'Initial search query for filtering. Matching is fuzzy. For exact matching, start query with a single quote, e.g. `--query "\'search"'
45
+ c.desc 'Initial search query for filtering. Matching is fuzzy. For exact matching, start query with a single quote,
46
+ e.g. `--query "\'search"'
42
47
  c.arg_name 'QUERY'
43
48
  c.flag %i[q query]
44
49
 
45
- c.desc 'Select from entries matching search filter, surround with slashes for regex (e.g. "/query.*/"), start with single quote for exact match ("\'query")'
46
- c.arg_name 'QUERY'
47
- c.flag [:search]
48
-
49
- c.desc 'Perform a tag value query ("@done > two hours ago" or "@progress < 50"). May be used multiple times, combined with --bool'
50
+ c.desc 'Perform a tag value query ("@done > two hours ago" or "@progress < 50").
51
+ May be used multiple times, combined with --bool'
50
52
  c.arg_name 'QUERY'
51
53
  c.flag [:val], multiple: true, must_match: REGEX_VALUE_QUERY
52
54
 
53
- c.desc 'Select from entries older than date. If this is only a time (8am, 1:30pm, 15:00), all dates will be included, but entries will be filtered by time of day'
54
- c.arg_name 'DATE_STRING'
55
- c.flag [:before], type: DateBeginString
56
-
57
- c.desc 'Select from entries newer than date. If this is only a time (8am, 1:30pm, 15:00), all dates will be included, but entries will be filtered by time of day'
58
- c.arg_name 'DATE_STRING'
59
- c.flag [:after], type: DateEndString
60
-
61
- c.desc %(
62
- Date range to show, or a single day to filter date on.
63
- Date range argument should be quoted. Date specifications can be natural language.
64
- To specify a range, use "to" or "through": `doing select --from "monday 8am to friday 5pm"`.
65
-
66
- If values are only time(s) (6am to noon) all dates will be included, but entries will be filtered
67
- by time of day.
68
- )
69
- c.arg_name 'DATE_OR_RANGE'
70
- c.flag [:from], type: DateRangeString
71
-
72
- c.desc 'Force exact search string matching (case sensitive)'
73
- c.switch %i[x exact], default_value: @config.exact_match?, negatable: @config.exact_match?
74
-
75
55
  c.desc 'Select items that *don\'t* match search/tag filters'
76
56
  c.switch [:not], default_value: false, negatable: false
77
57
 
78
- c.desc 'Case sensitivity for search string matching [(c)ase-sensitive, (i)gnore, (s)mart]'
79
- c.arg_name 'TYPE'
80
- c.flag [:case], must_match: /^[csi]/, default_value: @settings.dig('search', 'case')
81
-
82
- c.desc 'Use --no-menu to skip the interactive menu. Use with --query to filter items and act on results automatically. Test with `--output doing` to preview matches'
58
+ c.desc 'Use --no-menu to skip the interactive menu. Use with --query to filter items and act on results automatically.
59
+ Test with `--output doing` to preview matches'
83
60
  c.switch %i[menu], negatable: true, default_value: true
84
61
 
85
62
  c.desc 'Cancel selected items (add @done without timestamp)'
@@ -108,15 +85,20 @@ command :select do |c|
108
85
  c.arg_name 'FORMAT'
109
86
  c.flag %i[o output]
110
87
 
111
- c.desc "Copy selection as a new entry with current time and no @done tag. Only works with single selections. Can be combined with --editor."
88
+ c.desc 'Copy selection as a new entry with current time and no @done tag.
89
+ Only works with single selections. Can be combined with --editor.'
112
90
  c.switch %i[again resume], negatable: false, default_value: false
113
91
 
114
- c.action do |_global_options, options, args|
115
- raise DoingRuntimeError, %(Invalid output type "#{options[:output]}") if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
92
+ add_options(:search, c)
93
+ add_options(:date_filter, c)
116
94
 
117
- raise InvalidArgument, '--no-menu requires --query' if !options[:menu] && !options[:query]
95
+ c.action do |_global_options, options, _args|
96
+ if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
97
+ raise DoingRuntimeError, %(Invalid output type "#{options[:output]}")
98
+
99
+ end
118
100
 
119
- options[:case] = options[:case].normalize_case
101
+ raise InvalidArgument, '--no-menu requires --query' if !options[:menu] && !options[:query]
120
102
 
121
103
  @wwid.interactive(options) # hooked
122
104
  end