doing 2.1.24 → 2.1.28

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 (170) hide show
  1. checksums.yaml +4 -4
  2. data/.yardoc/checksums +17 -21
  3. data/.yardoc/object_types +0 -0
  4. data/.yardoc/objects/root.dat +0 -0
  5. data/CHANGELOG.md +325 -102
  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 +3 -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 +73 -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 -3662
  55. data/docs/doc/Array.html +13 -449
  56. data/docs/doc/BooleanTermParser/Clause.html +5 -5
  57. data/docs/doc/BooleanTermParser/Operator.html +4 -4
  58. data/docs/doc/BooleanTermParser/Query.html +8 -8
  59. data/docs/doc/BooleanTermParser/QueryParser.html +2 -2
  60. data/docs/doc/BooleanTermParser/QueryTransformer.html +2 -2
  61. data/docs/doc/BooleanTermParser.html +1 -1
  62. data/docs/doc/Doing/Color.html +65 -59
  63. data/docs/doc/Doing/Completion.html +2 -2
  64. data/docs/doc/Doing/Configuration.html +49 -16
  65. data/docs/doc/Doing/Errors/DoingNoTraceError.html +2 -2
  66. data/docs/doc/Doing/Errors/DoingRuntimeError.html +2 -2
  67. data/docs/doc/Doing/Errors/DoingStandardError.html +2 -2
  68. data/docs/doc/Doing/Errors/EmptyInput.html +2 -2
  69. data/docs/doc/Doing/Errors/NoResults.html +2 -2
  70. data/docs/doc/Doing/Errors/PluginException.html +3 -3
  71. data/docs/doc/Doing/Errors/UserCancelled.html +2 -2
  72. data/docs/doc/Doing/Errors/WrongCommand.html +2 -2
  73. data/docs/doc/Doing/Errors.html +1 -1
  74. data/docs/doc/Doing/Hooks.html +6 -6
  75. data/docs/doc/Doing/Item.html +50 -16
  76. data/docs/doc/Doing/Items.html +10 -10
  77. data/docs/doc/Doing/LogAdapter.html +24 -24
  78. data/docs/doc/Doing/Note.html +7 -7
  79. data/docs/doc/Doing/Pager.html +4 -4
  80. data/docs/doc/Doing/Plugins.html +7 -7
  81. data/docs/doc/Doing/Prompt.html +59 -14
  82. data/docs/doc/Doing/Section.html +6 -6
  83. data/docs/doc/Doing/TemplateString.html +8 -8
  84. data/docs/doc/Doing/Types.html +46 -1
  85. data/docs/doc/Doing/Util/Backup.html +10 -10
  86. data/docs/doc/Doing/Util.html +15 -15
  87. data/docs/doc/Doing/WWID.html +73 -61
  88. data/docs/doc/Doing.html +3 -3
  89. data/docs/doc/FalseClass.html +235 -0
  90. data/docs/doc/GLI/Commands/Help.html +3 -3
  91. data/docs/doc/GLI/Commands/MarkdownDocumentListener.html +17 -17
  92. data/docs/doc/GLI/Commands.html +1 -1
  93. data/docs/doc/GLI.html +1 -1
  94. data/docs/doc/Hash.html +45 -11
  95. data/docs/doc/Numeric.html +5 -5
  96. data/docs/doc/Object.html +203 -0
  97. data/docs/doc/PhraseParser/Operator.html +4 -4
  98. data/docs/doc/PhraseParser/PhraseClause.html +5 -5
  99. data/docs/doc/PhraseParser/Query.html +10 -10
  100. data/docs/doc/PhraseParser/QueryParser.html +2 -2
  101. data/docs/doc/PhraseParser/QueryTransformer.html +2 -2
  102. data/docs/doc/PhraseParser/TermClause.html +5 -5
  103. data/docs/doc/PhraseParser.html +1 -1
  104. data/docs/doc/Status.html +7 -7
  105. data/docs/doc/String.html +306 -3111
  106. data/docs/doc/Symbol.html +45 -11
  107. data/docs/doc/Time.html +6 -6
  108. data/docs/doc/TrueClass.html +235 -0
  109. data/docs/doc/_index.html +37 -19
  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 +240 -576
  114. data/docs/doc/top-level-namespace.html +2 -2
  115. data/doing.rdoc +297 -169
  116. data/example_plugin.rb +2 -2
  117. data/lib/completion/_doing.zsh +35 -31
  118. data/lib/completion/doing.bash +30 -19
  119. data/lib/completion/doing.fish +81 -67
  120. data/lib/doing/array/array.rb +4 -0
  121. data/lib/doing/array/nested_hash.rb +17 -0
  122. data/lib/doing/{array.rb → array/tags.rb} +7 -25
  123. data/lib/doing/changelog/change.rb +26 -11
  124. data/lib/doing/changelog/changes.rb +16 -4
  125. data/lib/doing/{array_chronify.rb → chronify/array.rb} +0 -0
  126. data/lib/doing/chronify/chronify.rb +5 -0
  127. data/lib/doing/{numeric_chronify.rb → chronify/numeric.rb} +0 -0
  128. data/lib/doing/{string_chronify.rb → chronify/string.rb} +0 -0
  129. data/lib/doing/colors.rb +115 -54
  130. data/lib/doing/configuration.rb +9 -6
  131. data/lib/doing/good.rb +72 -0
  132. data/lib/doing/hash.rb +4 -0
  133. data/lib/doing/help_monkey_patch.rb +6 -5
  134. data/lib/doing/hooks.rb +3 -3
  135. data/lib/doing/item.rb +19 -15
  136. data/lib/doing/items.rb +2 -2
  137. data/lib/doing/log_adapter.rb +35 -2
  138. data/lib/doing/normalize.rb +188 -0
  139. data/lib/doing/pager.rb +1 -0
  140. data/lib/doing/plugins/export/dayone_export.rb +1 -1
  141. data/lib/doing/plugins/export/html_export.rb +1 -1
  142. data/lib/doing/plugins/export/json_export.rb +1 -1
  143. data/lib/doing/plugins/export/markdown_export.rb +1 -1
  144. data/lib/doing/plugins/export/template_export.rb +3 -1
  145. data/lib/doing/plugins/import/calendar_import.rb +1 -1
  146. data/lib/doing/plugins/import/doing_import.rb +1 -1
  147. data/lib/doing/plugins/import/timing_import.rb +1 -1
  148. data/lib/doing/prompt.rb +9 -3
  149. data/lib/doing/string/highlight.rb +95 -0
  150. data/lib/doing/string/query.rb +129 -0
  151. data/lib/doing/string/string.rb +12 -0
  152. data/lib/doing/string/tags.rb +164 -0
  153. data/lib/doing/string/transform.rb +168 -0
  154. data/lib/doing/string/truncate.rb +75 -0
  155. data/lib/doing/string/url.rb +82 -0
  156. data/lib/doing/template_string.rb +2 -24
  157. data/lib/doing/types.rb +9 -0
  158. data/lib/doing/util.rb +20 -16
  159. data/lib/doing/version.rb +1 -1
  160. data/lib/doing/wwid.rb +91 -51
  161. data/lib/doing.rb +5 -6
  162. data/lib/examples/commands/wiki.rb +6 -7
  163. data/lib/examples/plugins/wiki_export/wiki_export.rb +1 -1
  164. data/lib/helpers/threaded_tests.rb +69 -79
  165. data/lib/helpers/threaded_tests_string.rb +50 -0
  166. data/scripts/deploy.rb +107 -0
  167. data/scripts/runtests.sh +4 -0
  168. metadata +65 -8
  169. data/lib/doing/string.rb +0 -765
  170. data/lib/doing/symbol.rb +0 -28
@@ -0,0 +1,53 @@
1
+ # @@open
2
+ desc 'Open the "doing" file in an editor'
3
+ long_desc "`doing open` defaults to using the editors->doing_file setting
4
+ in #{@config.config_file} (#{Doing::Util.find_default_editor('doing_file')})."
5
+ command :open do |c|
6
+ c.example 'doing open', desc: 'Open the doing file in the default editor'
7
+ c.desc 'Open with editor command (e.g. vim, mate)'
8
+ c.arg_name 'COMMAND'
9
+ c.flag %i[e editor]
10
+
11
+ if `uname` =~ /Darwin/
12
+ c.desc 'Open with app name'
13
+ c.arg_name 'APP_NAME'
14
+ c.flag %i[a app]
15
+
16
+ c.desc 'Open with app bundle id'
17
+ c.arg_name 'BUNDLE_ID'
18
+ c.flag %i[b bundle_id]
19
+ end
20
+
21
+ c.action do |_global_options, options, _args|
22
+ params = options.clone
23
+ params.delete_if do |k, v|
24
+ k.instance_of?(String) || v.nil? || v == false
25
+ end
26
+
27
+ if options[:editor]
28
+ raise MissingEditor, "Editor #{options[:editor]} not found" unless Doing::Util.exec_available(options[:editor].split(/ /).first)
29
+
30
+ editor = TTY::Which.which(options[:editor])
31
+ system %(#{editor} "#{File.expand_path(@wwid.doing_file)}")
32
+ elsif `uname` =~ /Darwin/
33
+ if options[:app]
34
+ system %(open -a "#{options[:app]}" "#{File.expand_path(@wwid.doing_file)}")
35
+ elsif options[:bundle_id]
36
+ system %(open -b "#{options[:bundle_id]}" "#{File.expand_path(@wwid.doing_file)}")
37
+ elsif Doing::Util.find_default_editor('doing_file')
38
+ editor = Doing::Util.find_default_editor('doing_file')
39
+ if Doing::Util.exec_available(editor.split(/ /).first)
40
+ system %(#{editor} "#{File.expand_path(@wwid.doing_file)}")
41
+ else
42
+ system %(open -a "#{editor}" "#{File.expand_path(@wwid.doing_file)}")
43
+ end
44
+ else
45
+ system %(open "#{File.expand_path(@wwid.doing_file)}")
46
+ end
47
+ else
48
+ raise MissingEditor, 'No EDITOR variable defined in environment' if Doing::Util.default_editor.nil?
49
+
50
+ system %(#{Doing::Util.default_editor} "#{File.expand_path(@wwid.doing_file)}")
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,23 @@
1
+ # @@plugins
2
+ desc 'List installed plugins'
3
+ long_desc %(Lists available plugins, including user-installed plugins.
4
+
5
+ Export plugins are available with the `--output` flag on commands that support it.
6
+
7
+ Import plugins are available using `doing import --type PLUGIN`.
8
+ )
9
+ command :plugins do |c|
10
+ c.example 'doing plugins', desc: 'List all plugins'
11
+ c.example 'doing plugins -t import', desc: 'List all import plugins'
12
+
13
+ c.desc 'List plugins of type (import, export)'
14
+ c.arg_name 'TYPE'
15
+ c.flag %i[t type], must_match: /^(?:[iea].*)$/i, default_value: 'all'
16
+
17
+ c.desc 'List in single column for completion'
18
+ c.switch %i[c column], negatable: false, default_value: false
19
+
20
+ c.action do |_global_options, options, _args|
21
+ Doing::Plugins.list_plugins(options)
22
+ end
23
+ end
@@ -0,0 +1,77 @@
1
+ # @@recent
2
+ desc 'List recent entries'
3
+ default_value 10
4
+ arg_name 'COUNT'
5
+ command :recent do |c|
6
+ c.example 'doing recent', desc: 'Show the 10 most recent entries across all sections'
7
+ c.example 'doing recent 20', desc: 'Show the 20 most recent entries across all sections'
8
+ c.example 'doing recent --section Currently 20', desc: 'List the 20 most recent entries from the Currently section'
9
+ c.example 'doing recent --interactive 20', desc: 'Create a menu from the 20 most recent entries to perform batch actions on'
10
+
11
+ c.desc 'Section'
12
+ c.arg_name 'NAME'
13
+ c.flag %i[s section], default_value: 'All'
14
+
15
+ c.desc 'Show time intervals on @done tasks'
16
+ c.switch %i[t times], default_value: true, negatable: true
17
+
18
+ c.desc "Output using a template from configuration"
19
+ c.arg_name 'TEMPLATE_KEY'
20
+ c.flag [:config_template], type: TemplateName, default_value: 'recent'
21
+
22
+ c.desc 'Override output format with a template string containing %placeholders'
23
+ c.arg_name 'TEMPLATE_STRING'
24
+ c.flag [:template]
25
+
26
+ c.desc 'Show elapsed time on entries without @done tag'
27
+ c.switch [:duration]
28
+
29
+ c.desc 'Show intervals with totals at the end of output'
30
+ c.switch [:totals], default_value: false, negatable: false
31
+
32
+ c.desc 'Sort tags by (name|time)'
33
+ default = @settings['tag_sort'].normalize_tag_sort || :name
34
+ c.arg_name 'KEY'
35
+ c.flag [:tag_sort], must_match: REGEX_TAG_SORT, default_value: default, type: TagSortSymbol
36
+
37
+ c.desc 'Select from a menu of matching entries to perform additional operations'
38
+ c.switch %i[i interactive], negatable: false, default_value: false
39
+
40
+ c.action do |global_options, options, args|
41
+ section = @wwid.guess_section(options[:section]) || options[:section].cap_first
42
+
43
+ unless global_options[:version]
44
+ if @settings['templates']['recent'].key?('count')
45
+ config_count = @settings['templates']['recent']['count'].to_i
46
+ else
47
+ config_count = 10
48
+ end
49
+
50
+ if options[:interactive]
51
+ count = 0
52
+ else
53
+ count = args.empty? ? config_count : args[0].to_i
54
+ end
55
+
56
+ options[:times] = true if options[:totals]
57
+ options[:sort_tags] = options[:tag_sort]
58
+
59
+ template = @settings['templates']['recent'].deep_merge(@settings['templates']['default'])
60
+ tags_color = template.key?('tags_color') ? template['tags_color'] : nil
61
+
62
+ opts = {
63
+ sort_tags: options[:sort_tags],
64
+ tags_color: tags_color,
65
+ times: options[:times],
66
+ totals: options[:totals],
67
+ interactive: options[:interactive],
68
+ duration: options[:duration],
69
+ config_template: options[:config_template],
70
+ template: options[:template]
71
+ }
72
+
73
+ Doing::Pager::page @wwid.recent(count, section.cap_first, opts)
74
+
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @@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'
6
+ arg_name 'COUNT'
7
+ command :redo do |c|
8
+ c.desc 'Specify alternate doing file'
9
+ c.arg_name 'PATH'
10
+ c.flag %i[f file], default_value: @wwid.doing_file
11
+
12
+ c.desc 'Select from an interactive menu'
13
+ c.switch %i[i interactive]
14
+
15
+ c.action do |_global, options, args|
16
+ file = options[:file] || @wwid.doing_file
17
+ count = args.empty? ? 1 : args[0].to_i
18
+ raise InvalidArgument, 'Invalid count specified for redo' unless count&.positive?
19
+
20
+ if options[:interactive]
21
+ Doing::Util::Backup.select_redo(file)
22
+ else
23
+ Doing::Util::Backup.redo_backup(file, count: count)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,73 @@
1
+ # @@reset @@begin
2
+ desc 'Reset the start time of an entry'
3
+ long_desc 'Update the start time of the last entry or the last entry matching a tag/search filter.
4
+ If no argument is provided, the start time will be reset to the current time.
5
+ If a date string is provided as an argument, the start time will be set to the parsed result.'
6
+ arg_name 'DATE_STRING', optional: true
7
+ command %i[reset begin] do |c|
8
+ c.example 'doing reset', desc: 'Reset the start time of the last entry to the current time'
9
+ c.example 'doing reset --tag project1', desc: 'Reset the start time of the most recent entry tagged @project1 to the current time'
10
+ c.example 'doing reset 3pm', desc: 'Reset the start time of the last entry to 3pm of the current day'
11
+ c.example 'doing begin --tag todo --resume', desc: 'alias for reset. Updates the last @todo entry to the current time, removing @done tag.'
12
+
13
+ c.desc 'Limit search to section'
14
+ c.arg_name 'NAME'
15
+ c.flag %i[s section], default_value: 'All'
16
+
17
+ c.desc 'Resume entry (remove @done)'
18
+ c.switch %i[r resume], default_value: true
19
+
20
+ c.desc 'Change start date but do not remove @done (shortcut for --no-resume)'
21
+ c.switch [:n]
22
+
23
+ c.desc 'Select from a menu of matching entries'
24
+ c.switch %i[i interactive], negatable: false, default_value: false
25
+
26
+ add_options(:search, c)
27
+ add_options(:tag_filter, c)
28
+
29
+ c.action do |global_options, options, args|
30
+ if args.count.positive?
31
+ reset_date = args.join(' ').chronify(guess: :begin)
32
+ raise InvalidArgument, 'Invalid date string' unless reset_date
33
+
34
+ else
35
+ reset_date = Time.now
36
+ end
37
+
38
+ options[:fuzzy] = false
39
+
40
+ options[:section] = @wwid.guess_section(options[:section]) || options[:section].cap_first if options[:section]
41
+
42
+ if options[:search]
43
+ search = options[:search]
44
+ search.sub!(/^'?/, "'") if options[:exact]
45
+ options[:search] = search
46
+ end
47
+
48
+ items = @wwid.filter_items([], opt: options)
49
+
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
61
+
62
+
63
+ raise NoResults, 'No entry matching parameters was found.' unless last_entry
64
+
65
+ old_item = last_entry.clone
66
+
67
+ @wwid.reset_item(last_entry, date: reset_date, resume: options[:resume])
68
+ Doing::Hooks.trigger :post_entry_updated, @wwid, last_entry, old_item
69
+ # new_entry = Doing::Item.new(last_entry.date, last_entry.title, last_entry.section, new_note)
70
+
71
+ @wwid.write(@wwid.doing_file)
72
+ end
73
+ end
@@ -0,0 +1,42 @@
1
+ # @@rotate
2
+ desc 'Move entries to archive file'
3
+ long_desc 'As your doing file grows, commands can get slow. Given that your historical data (and your archive section)
4
+ probably aren\'t providing any useful insights a year later, use this command to "rotate" old entries out to an archive
5
+ file. You\'ll still have access to all historical data, but it won\'t be slowing down daily operation.'
6
+ command :rotate do |c|
7
+ c.example 'doing rotate', desc: 'Move all entries in doing file to a dated secondary file'
8
+ c.example 'doing rotate --section Archive --keep 10', desc: 'Move entries in the Archive section to a secondary file, keeping the most recent 10 entries'
9
+ c.example 'doing rotate --tag project1,done --bool AND', desc: 'Move entries tagged @project1 and @done to a secondary file'
10
+
11
+ c.desc 'How many items to keep in each section (most recent)'
12
+ c.arg_name 'X'
13
+ c.flag %i[k keep], must_match: /^\d+$/, type: Integer
14
+
15
+ c.desc 'Section to rotate'
16
+ c.arg_name 'SECTION_NAME'
17
+ c.flag %i[s section], default_value: 'All'
18
+
19
+ c.desc 'Rotate entries older than date
20
+ (Flexible date format, e.g. 1/27/2021, 2020-07-19, or Monday 3pm)'
21
+ c.arg_name 'DATE_STRING'
22
+ c.flag [:before]
23
+
24
+ add_options(:search, c)
25
+ add_options(:tag_filter, c)
26
+
27
+ c.action do |_global_options, options, _args|
28
+ options[:fuzzy] = false
29
+
30
+ options[:section] = @wwid.guess_section(options[:section]) if options[:section] && options[:section] !~ /^all$/i
31
+
32
+ search = nil
33
+
34
+ if options[:search]
35
+ search = options[:search]
36
+ search.sub!(/^'?/, "'") if options[:exact]
37
+ options[:search] = search
38
+ end
39
+
40
+ @wwid.rotate(options)
41
+ end
42
+ end
@@ -0,0 +1,11 @@
1
+ # @@sections
2
+ desc 'List sections'
3
+ command :sections do |c|
4
+ c.desc 'List in single column'
5
+ c.switch %i[c column], negatable: false, default_value: false
6
+
7
+ c.action do |_global_options, options, _args|
8
+ joiner = options[:column] ? "\n" : "\t"
9
+ print @wwid.content.section_titles.join(joiner)
10
+ end
11
+ end
@@ -0,0 +1,105 @@
1
+ # @@select
2
+ desc 'Display an interactive menu to perform operations'
3
+ long_desc 'List all entries and select with typeahead fuzzy matching.
4
+
5
+ Multiple selections are allowed, hit tab to add the highlighted entry to the
6
+ selection, and use ctrl-a to select all visible items. Return processes the
7
+ selected entries.
8
+
9
+ Search in the menu by typing:
10
+
11
+ sbtrkt fuzzy-match Items that match s*b*t*r*k*t
12
+
13
+ \'wild exact-match (quoted) Items that include wild
14
+
15
+ !fire inverse-exact-match Items that do not include fire'
16
+ command :select do |c|
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
+
24
+ c.desc 'Select from a specific section'
25
+ c.arg_name 'SECTION'
26
+ c.flag %i[s section]
27
+
28
+ c.desc 'Tag selected entries'
29
+ c.arg_name 'TAG'
30
+ c.flag %i[t tag]
31
+
32
+ c.desc 'Reverse -c, -f, --flag, and -t (remove instead of adding)'
33
+ c.switch %i[r remove], negatable: false
34
+
35
+ # c.desc 'Add @done to selected item(s), using start time of next item as the finish time'
36
+ # c.switch %i[a auto], negatable: false, default_value: false
37
+
38
+ c.desc 'Archive selected items'
39
+ c.switch %i[a archive], negatable: false, default_value: false
40
+
41
+ c.desc 'Move selected items to section'
42
+ c.arg_name 'SECTION'
43
+ c.flag %i[m move]
44
+
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"'
47
+ c.arg_name 'QUERY'
48
+ c.flag %i[q query]
49
+
50
+ c.desc 'Perform a tag value query ("@done > two hours ago" or "@progress < 50").
51
+ May be used multiple times, combined with --bool'
52
+ c.arg_name 'QUERY'
53
+ c.flag [:val], multiple: true, must_match: REGEX_VALUE_QUERY
54
+
55
+ c.desc 'Select items that *don\'t* match search/tag filters'
56
+ c.switch [:not], default_value: false, negatable: false
57
+
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'
60
+ c.switch %i[menu], negatable: true, default_value: true
61
+
62
+ c.desc 'Cancel selected items (add @done without timestamp)'
63
+ c.switch %i[c cancel], negatable: false, default_value: false
64
+
65
+ c.desc 'Delete selected items'
66
+ c.switch %i[d delete], negatable: false, default_value: false
67
+
68
+ c.desc 'Edit selected item(s)'
69
+ c.switch %i[e editor], negatable: false, default_value: false
70
+
71
+ c.desc 'Add @done with current time to selected item(s)'
72
+ c.switch %i[f finish], negatable: false, default_value: false
73
+
74
+ c.desc 'Add flag to selected item(s)'
75
+ c.switch %i[flag], negatable: false, default_value: false
76
+
77
+ c.desc 'Perform action without confirmation'
78
+ c.switch %i[force], negatable: false, default_value: false
79
+
80
+ c.desc 'Save selected entries to file using --output format'
81
+ c.arg_name 'FILE'
82
+ c.flag %i[save_to]
83
+
84
+ c.desc "Output entries to format (#{Doing::Plugins.plugin_names(type: :export)})"
85
+ c.arg_name 'FORMAT'
86
+ c.flag %i[o output]
87
+
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.'
90
+ c.switch %i[again resume], negatable: false, default_value: false
91
+
92
+ add_options(:search, c)
93
+ add_options(:date_filter, c)
94
+
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
100
+
101
+ raise InvalidArgument, '--no-menu requires --query' if !options[:menu] && !options[:query]
102
+
103
+ @wwid.interactive(options) # hooked
104
+ end
105
+ end
@@ -0,0 +1,185 @@
1
+ # @@show
2
+ desc 'List all entries'
3
+ long_desc %(
4
+ The argument can be a section name, @tag(s) or both.
5
+ "pick" or "choose" as an argument will offer a section menu. Run with `--menu` to get a menu of available tags.
6
+
7
+ Show tags by passing @tagname arguments. Multiple tags can be combined, and you can specify the boolean used to
8
+ combine them with `--bool (AND|OR|NOT)`. You can also use @+tagname to require a tag to match, or @-tagname to ignore
9
+ entries containing tagname. +/- operators require `--bool PATTERN` (which is the default).
10
+ )
11
+ arg_name '[SECTION|@TAGS]'
12
+ command :show do |c|
13
+ c.example 'doing show Currently', desc: 'Show entries in the Currently section'
14
+ c.example 'doing show @project1', desc: 'Show entries tagged @project1'
15
+ c.example 'doing show Later @doing', desc: 'Show entries from the Later section tagged @doing'
16
+ c.example 'doing show @oracle @writing --bool and', desc: 'Show entries tagged both @oracle and @writing'
17
+ c.example 'doing show Currently @devo --bool not', desc: 'Show entries in Currently NOT tagged @devo'
18
+ c.example 'doing show Ideas @doing --from "mon to fri"', desc: 'Show entries tagged @doing from the Ideas section added between monday and friday of the current week.'
19
+ c.example 'doing show --interactive Later @doing', desc: 'Create a menu from entries from the Later section tagged @doing to perform batch actions'
20
+
21
+ c.desc 'Max count to show'
22
+ c.arg_name 'MAX'
23
+ c.flag %i[c count], default_value: 0, must_match: /^\d+$/, type: Integer
24
+
25
+ c.desc 'Age (oldest|newest)'
26
+ c.arg_name 'AGE'
27
+ c.flag %i[a age], default_value: :newest, type: AgeSymbol
28
+
29
+ c.desc "Highlight search matches in output. Only affects command line output"
30
+ c.switch %i[h hilite], default_value: @settings.dig('search', 'highlight')
31
+
32
+ c.desc 'Sort order (asc/desc)'
33
+ c.arg_name 'ORDER'
34
+ c.flag %i[s sort], must_match: REGEX_SORT_ORDER, default_value: :asc, type: OrderSymbol
35
+
36
+ c.desc 'Show time intervals on @done tasks'
37
+ c.switch %i[t times], default_value: true, negatable: true
38
+
39
+ c.desc 'Show elapsed time on entries without @done tag'
40
+ c.switch [:duration]
41
+
42
+ c.desc 'Show intervals with totals at the end of output'
43
+ c.switch [:totals], default_value: false, negatable: false
44
+
45
+ c.desc 'Sort tags by (name|time)'
46
+ default = @settings['tag_sort'].normalize_tag_sort || :name
47
+ c.arg_name 'KEY'
48
+ c.flag [:tag_sort], must_match: REGEX_TAG_SORT, default_value: default, type: TagSortSymbol
49
+
50
+ c.desc 'Tag sort direction (asc|desc)'
51
+ c.arg_name 'DIRECTION'
52
+ c.flag [:tag_order], must_match: REGEX_SORT_ORDER, default_value: :asc, type: OrderSymbol
53
+
54
+ c.desc 'Only show items with recorded time intervals'
55
+ c.switch [:only_timed], default_value: false, negatable: false
56
+
57
+ c.desc "Output using a template from configuration"
58
+ c.arg_name 'TEMPLATE_KEY'
59
+ c.flag [:config_template], type: TemplateName, default_value: 'default'
60
+
61
+ c.desc 'Override output format with a template string containing %placeholders'
62
+ c.arg_name 'TEMPLATE_STRING'
63
+ c.flag [:template]
64
+
65
+ c.desc 'Select section or tag to display from a menu'
66
+ c.switch %i[m menu], negatable: false, default_value: false
67
+
68
+ c.desc 'Select from a menu of matching entries to perform additional operations'
69
+ c.switch %i[i interactive], negatable: false, default_value: false
70
+
71
+ c.desc "Output to export format (#{Doing::Plugins.plugin_names(type: :export)})"
72
+ c.arg_name 'FORMAT'
73
+ c.flag %i[o output]
74
+
75
+ add_options(:search, c)
76
+ add_options(:tag_filter, c)
77
+ add_options(:date_filter, c)
78
+
79
+ c.action do |global_options, options, args|
80
+ options[:fuzzy] = false
81
+ if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
82
+ raise DoingRuntimeError, %(Invalid output type "#{options[:output]}")
83
+
84
+ end
85
+
86
+ tag_filter = false
87
+ tags = []
88
+
89
+ if args.length.positive?
90
+ case args[0]
91
+ when /^all$/i
92
+ section = 'All'
93
+ args.shift
94
+ when /^(choose|pick)$/i
95
+ section = @wwid.choose_section(include_all: true)
96
+
97
+ args.shift
98
+ when /^[@+-]/
99
+ section = 'All'
100
+ else
101
+ begin
102
+ section = @wwid.guess_section(args[0])
103
+ rescue WrongCommand
104
+ cmd = commands[:view]
105
+ action = cmd.send(:get_action, nil)
106
+ return action.call(global_options, options, args)
107
+ end
108
+
109
+ raise InvalidSection, "No such section: #{args[0]}" unless section
110
+
111
+ args.shift
112
+ end
113
+ if args.length.positive?
114
+ args.each do |arg|
115
+ arg.split(/,/).each do |tag|
116
+ tags.push(tag.strip.sub(/^@/, ''))
117
+ end
118
+ end
119
+ end
120
+ else
121
+ section = options[:menu] ? @wwid.choose_section(include_all: true) : @settings['current_section']
122
+ section ||= 'All'
123
+ end
124
+
125
+ tags.concat(options[:tag]) if options[:tag]
126
+
127
+ options[:times] = true if options[:totals]
128
+
129
+ template = @settings['templates'][options[:config_template]].deep_merge({
130
+ 'wrap_width' => @settings['wrap_width'] || 0,
131
+ 'date_format' => @settings['default_date_format'],
132
+ 'order' => @settings['order']&.normalize_order || :asc,
133
+ 'tags_color' => @settings['tags_color']
134
+ })
135
+
136
+ if options[:search]
137
+ search = options[:search]
138
+ search.sub!(/^'?/, "'") if options[:exact]
139
+ options[:search] = search
140
+ end
141
+
142
+ options[:section] = section
143
+
144
+ if tags.good?
145
+ tag_filter = {
146
+ 'tags' => tags,
147
+ 'bool' => options[:bool]
148
+ }
149
+ end
150
+
151
+ options[:tag_filter] = tag_filter
152
+ options[:tag] = nil
153
+
154
+ items = @wwid.filter_items([], opt: options)
155
+
156
+ if options[:menu]
157
+ Doing.logger.benchmark(:menu, :start)
158
+ tag = @wwid.choose_tag(section, items: items, include_all: true)
159
+ raise UserCancelled unless tag
160
+
161
+ tags = tag.split(/ +/).map { |t| t.strip.sub(/^@?/, '') } if tag =~ /^@/
162
+ if tags.good?
163
+ tag_filter = {
164
+ 'tags' => tags,
165
+ 'bool' => options[:bool]
166
+ }
167
+ options[:tag_filter] = tag_filter
168
+ end
169
+ Doing.logger.benchmark(:menu, :finish)
170
+ end
171
+
172
+ options[:age] ||= :newest
173
+
174
+ opt = options.clone
175
+ opt[:sort_tags] = options[:tag_sort]
176
+ opt[:count] = options[:count].to_i
177
+ opt[:highlight] = true
178
+ opt[:hilite] = options[:hilite]
179
+ opt[:order] = options[:sort]
180
+ opt[:tag] = nil
181
+ opt[:tags_color] = template['tags_color']
182
+
183
+ Doing::Pager.page @wwid.list_section(opt, items: items)
184
+ end
185
+ end
@@ -0,0 +1,63 @@
1
+ # @since
2
+ desc 'List entries since a date'
3
+ long_desc %(Date argument can be natural language and are always interpreted as being in the past. "thursday" would be interpreted as "last thursday,"
4
+ and "2d" would be interpreted as "two days ago.")
5
+ arg_name 'DATE_STRING'
6
+ command :since do |c|
7
+ c.example 'doing since 7/30', desc: 'List all entries created since 12am on 7/30 of the current year'
8
+ c.example 'doing since "monday 3pm" --output json', desc: 'Show entries since 3pm on Monday of the current week, output in JSON format'
9
+
10
+ c.desc 'Section'
11
+ c.arg_name 'NAME'
12
+ c.flag %i[s section], default_value: 'All'
13
+
14
+ c.desc 'Show time intervals on @done tasks'
15
+ c.switch %i[t times], default_value: true, negatable: true
16
+
17
+ c.desc 'Show elapsed time on entries without @done tag'
18
+ c.switch [:duration]
19
+
20
+ c.desc 'Show time totals at the end of output'
21
+ c.switch [:totals], default_value: false, negatable: false
22
+
23
+ c.desc 'Sort tags by (name|time)'
24
+ default = @settings['tag_sort'].normalize_tag_sort || :name
25
+ c.arg_name 'KEY'
26
+ c.flag [:tag_sort], must_match: REGEX_TAG_SORT, default_value: default, type: TagSortSymbol
27
+
28
+ c.desc "Output to export format (#{Doing::Plugins.plugin_names(type: :export)})"
29
+ c.arg_name 'FORMAT'
30
+ c.flag %i[o output]
31
+
32
+ c.desc "Output using a template from configuration"
33
+ c.arg_name 'TEMPLATE_KEY'
34
+ c.flag [:config_template], type: TemplateName, default_value: 'default'
35
+
36
+ c.desc 'Override output format with a template string containing %placeholders'
37
+ c.arg_name 'TEMPLATE_STRING'
38
+ c.flag [:template]
39
+
40
+ c.action do |_global_options, options, args|
41
+ raise DoingRuntimeError, %(Invalid output type "#{options[:output]}") if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
42
+
43
+ raise MissingArgument, 'Missing date argument' if args.empty?
44
+
45
+ date_string = args.join(' ')
46
+
47
+ date_string.sub!(/(day) (\d)/, '\1 at \2')
48
+ date_string.sub!(/(\d+)d( ago)?/, '\1 days ago')
49
+
50
+ start = date_string.chronify(guess: :begin)
51
+ finish = Time.now
52
+
53
+ raise InvalidTimeExpression, 'Unrecognized date string' unless start
54
+
55
+ Doing.logger.debug('Interpreter:', "date interpreted as #{start} through the current time")
56
+
57
+ options[:times] = true if options[:totals]
58
+ options[:sort_tags] = options[:tag_sort]
59
+
60
+ Doing::Pager.page @wwid.list_date([start, finish], options[:section], options[:times], options[:output],
61
+ { template: options[:template], config_template: options[:config_template], duration: options[:duration], totals: options[:totals], sort_tags: options[:sort_tags] }).chomp
62
+ end
63
+ end