doing 2.1.25 → 2.1.26

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 (119) hide show
  1. checksums.yaml +4 -4
  2. data/.yardoc/checksums +4 -4
  3. data/.yardoc/object_types +0 -0
  4. data/.yardoc/objects/root.dat +0 -0
  5. data/CHANGELOG.md +283 -108
  6. data/Gemfile.lock +1 -1
  7. data/README.md +1 -1
  8. data/bin/commands/add_section.rb +13 -0
  9. data/bin/commands/again.rb +99 -0
  10. data/bin/commands/archive.rb +96 -0
  11. data/bin/commands/cancel.rb +102 -0
  12. data/bin/commands/changes.rb +42 -0
  13. data/bin/commands/choose.rb +9 -0
  14. data/bin/commands/colors.rb +19 -0
  15. data/bin/commands/commands.rb +87 -0
  16. data/bin/commands/commands_accepting.rb +25 -0
  17. data/bin/commands/completion.rb +24 -0
  18. data/bin/commands/config.rb +245 -0
  19. data/bin/commands/done.rb +249 -0
  20. data/bin/commands/finish.rb +149 -0
  21. data/bin/commands/flag.rb +126 -0
  22. data/bin/commands/grep.rb +124 -0
  23. data/bin/commands/import.rb +101 -0
  24. data/bin/commands/install_fzf.rb +17 -0
  25. data/bin/commands/last.rb +114 -0
  26. data/bin/commands/meanwhile.rb +86 -0
  27. data/bin/commands/note.rb +130 -0
  28. data/bin/commands/now.rb +151 -0
  29. data/bin/commands/on.rb +66 -0
  30. data/bin/commands/open.rb +53 -0
  31. data/bin/commands/plugins.rb +23 -0
  32. data/bin/commands/recent.rb +78 -0
  33. data/bin/commands/redo.rb +22 -0
  34. data/bin/commands/reset.rb +106 -0
  35. data/bin/commands/rotate.rb +73 -0
  36. data/bin/commands/sections.rb +11 -0
  37. data/bin/commands/select.rb +123 -0
  38. data/bin/commands/show.rb +231 -0
  39. data/bin/commands/since.rb +64 -0
  40. data/bin/commands/tag.rb +179 -0
  41. data/bin/commands/tag_dir.rb +29 -0
  42. data/bin/commands/tags.rb +93 -0
  43. data/bin/commands/template.rb +61 -0
  44. data/bin/commands/today.rb +65 -0
  45. data/bin/commands/undo.rb +49 -0
  46. data/bin/commands/view.rb +238 -0
  47. data/bin/commands/views.rb +11 -0
  48. data/bin/commands/yesterday.rb +73 -0
  49. data/bin/doing +39 -3641
  50. data/docs/doc/Array.html +1 -1
  51. data/docs/doc/BooleanTermParser/Clause.html +1 -1
  52. data/docs/doc/BooleanTermParser/Operator.html +1 -1
  53. data/docs/doc/BooleanTermParser/Query.html +1 -1
  54. data/docs/doc/BooleanTermParser/QueryParser.html +1 -1
  55. data/docs/doc/BooleanTermParser/QueryTransformer.html +1 -1
  56. data/docs/doc/BooleanTermParser.html +1 -1
  57. data/docs/doc/Doing/Color.html +1 -1
  58. data/docs/doc/Doing/Completion.html +1 -1
  59. data/docs/doc/Doing/Configuration.html +2 -1
  60. data/docs/doc/Doing/Errors/DoingNoTraceError.html +1 -1
  61. data/docs/doc/Doing/Errors/DoingRuntimeError.html +1 -1
  62. data/docs/doc/Doing/Errors/DoingStandardError.html +1 -1
  63. data/docs/doc/Doing/Errors/EmptyInput.html +1 -1
  64. data/docs/doc/Doing/Errors/NoResults.html +1 -1
  65. data/docs/doc/Doing/Errors/PluginException.html +1 -1
  66. data/docs/doc/Doing/Errors/UserCancelled.html +1 -1
  67. data/docs/doc/Doing/Errors/WrongCommand.html +1 -1
  68. data/docs/doc/Doing/Errors.html +1 -1
  69. data/docs/doc/Doing/Hooks.html +1 -1
  70. data/docs/doc/Doing/Item.html +1 -1
  71. data/docs/doc/Doing/Items.html +1 -1
  72. data/docs/doc/Doing/LogAdapter.html +1 -1
  73. data/docs/doc/Doing/Note.html +1 -1
  74. data/docs/doc/Doing/Pager.html +1 -1
  75. data/docs/doc/Doing/Plugins.html +1 -1
  76. data/docs/doc/Doing/Prompt.html +46 -1
  77. data/docs/doc/Doing/Section.html +1 -1
  78. data/docs/doc/Doing/TemplateString.html +1 -1
  79. data/docs/doc/Doing/Types.html +1 -1
  80. data/docs/doc/Doing/Util/Backup.html +1 -1
  81. data/docs/doc/Doing/Util.html +1 -1
  82. data/docs/doc/Doing/WWID.html +1 -1
  83. data/docs/doc/Doing.html +2 -2
  84. data/docs/doc/FalseClass.html +201 -0
  85. data/docs/doc/GLI/Commands/Help.html +1 -1
  86. data/docs/doc/GLI/Commands/MarkdownDocumentListener.html +1 -1
  87. data/docs/doc/GLI/Commands.html +1 -1
  88. data/docs/doc/GLI.html +1 -1
  89. data/docs/doc/Hash.html +1 -1
  90. data/docs/doc/Numeric.html +1 -1
  91. data/docs/doc/Object.html +203 -0
  92. data/docs/doc/PhraseParser/Operator.html +1 -1
  93. data/docs/doc/PhraseParser/PhraseClause.html +1 -1
  94. data/docs/doc/PhraseParser/Query.html +1 -1
  95. data/docs/doc/PhraseParser/QueryParser.html +1 -1
  96. data/docs/doc/PhraseParser/QueryTransformer.html +1 -1
  97. data/docs/doc/PhraseParser/TermClause.html +1 -1
  98. data/docs/doc/PhraseParser.html +1 -1
  99. data/docs/doc/Status.html +1 -1
  100. data/docs/doc/String.html +1 -1
  101. data/docs/doc/Symbol.html +1 -1
  102. data/docs/doc/Time.html +1 -1
  103. data/docs/doc/TrueClass.html +201 -0
  104. data/docs/doc/_index.html +1 -1
  105. data/docs/doc/file.README.html +2 -2
  106. data/docs/doc/index.html +2 -2
  107. data/docs/doc/method_list.html +374 -366
  108. data/docs/doc/top-level-namespace.html +1 -1
  109. data/doing.rdoc +15 -5
  110. data/lib/completion/_doing.zsh +3 -3
  111. data/lib/completion/doing.fish +1 -1
  112. data/lib/doing/changelog/changes.rb +1 -1
  113. data/lib/doing/configuration.rb +1 -0
  114. data/lib/doing/pager.rb +1 -0
  115. data/lib/doing/prompt.rb +8 -0
  116. data/lib/doing/version.rb +1 -1
  117. data/lib/examples/commands/wiki.rb +6 -7
  118. data/lib/helpers/threaded_tests.rb +25 -19
  119. metadata +45 -1
@@ -0,0 +1,29 @@
1
+ # @@tag_dir
2
+ desc 'Set the default tags for the current directory'
3
+ long_desc 'Adds default_tags to a .doingrc file in the current directory. Any entry created in this directory or its
4
+ subdirectories will be tagged with the default tags. You can modify these any time using the `config set` commnand or
5
+ manually editing the .doingrc file.'
6
+ arg_name 'TAG [TAG..]'
7
+ command :tag_dir do |c|
8
+ c.example 'doing tag_dir project1 project2', desc: 'Add @project1 and @project to to any entries in the current directory'
9
+ c.example 'doing tag_dir --remove', desc: 'Clear the default tags for the directory'
10
+
11
+ c.desc 'Remove all default_tags from the local .doingrc'
12
+ c.switch %i[r remove], negatable: false
13
+
14
+ c.action do |global, options, args|
15
+ tags = args.join(' ').gsub(/ *, */, ' ').split(' ')
16
+
17
+ cfg_cmd = commands[:config]
18
+ set_cmd = cfg_cmd.commands[:set]
19
+ set_options = {}
20
+ if options[:remove]
21
+ set_args = ['default_tags']
22
+ set_options[:remove] = true
23
+ else
24
+ set_args = ['default_tags', tags.join(',')]
25
+ end
26
+ action = set_cmd.send(:get_action, nil)
27
+ return action.call(global, set_options, set_args)
28
+ end
29
+ end
@@ -0,0 +1,93 @@
1
+ # @@tags
2
+ desc 'List all tags in the current Doing file'
3
+ arg_name 'MAX_COUNT', optional: true, type: Integer
4
+ command :tags do |c|
5
+ c.desc 'Section'
6
+ c.arg_name 'SECTION_NAME'
7
+ c.flag %i[s section], default_value: 'All'
8
+
9
+ c.desc 'Show count of occurrences'
10
+ c.switch %i[c counts]
11
+
12
+ c.desc 'Output in a single line with @ symbols. Ignored if --counts is specified.'
13
+ c.switch %i[l line]
14
+
15
+ c.desc 'Sort by name or count'
16
+ c.arg_name 'SORT_ORDER'
17
+ c.flag %i[sort], default_value: 'name', must_match: /^(?:n(?:ame)?|c(?:ount)?)$/
18
+
19
+ c.desc 'Sort order (asc/desc)'
20
+ c.arg_name 'ORDER'
21
+ c.flag %i[o order], must_match: REGEX_SORT_ORDER, default_value: 'asc'
22
+
23
+ c.desc 'Get tags for entries matching tags. Combine multiple tags with a comma. Wildcards allowed (*, ?)'
24
+ c.arg_name 'TAG'
25
+ c.flag [:tag]
26
+
27
+ c.desc 'Get tags for items matching search. Surround with
28
+ slashes for regex (e.g. "/query/"), start with a single quote for exact match ("\'query").'
29
+ c.arg_name 'QUERY'
30
+ c.flag [:search]
31
+
32
+ c.desc 'Perform a tag value query ("@done > two hours ago" or "@progress < 50"). May be used multiple times, combined with --bool'
33
+ c.arg_name 'QUERY'
34
+ c.flag [:val], multiple: true, must_match: REGEX_VALUE_QUERY
35
+
36
+ # c.desc '[DEPRECATED] Use alternative fuzzy matching for search string'
37
+ # c.switch [:fuzzy], default_value: false, negatable: false
38
+
39
+ c.desc 'Force exact search string matching (case sensitive)'
40
+ c.switch %i[x exact], default_value: @config.exact_match?, negatable: @config.exact_match?
41
+
42
+ c.desc 'Get tags from items that *don\'t* match search/tag filters'
43
+ c.switch [:not], default_value: false, negatable: false
44
+
45
+ c.desc 'Case sensitivity for search string matching [(c)ase-sensitive, (i)gnore, (s)mart]'
46
+ c.arg_name 'TYPE'
47
+ c.flag [:case], must_match: /^[csi]/, default_value: @settings.dig('search', 'case')
48
+
49
+ c.desc 'Boolean used to combine multiple tags. Use PATTERN to parse + and - as booleans'
50
+ c.arg_name 'BOOLEAN'
51
+ c.flag [:bool], must_match: REGEX_BOOL, default_value: 'PATTERN'
52
+
53
+ c.desc 'Select items to scan from a menu of matching entries'
54
+ c.switch %i[i interactive], negatable: false, default_value: false
55
+
56
+ c.action do |_global, options, args|
57
+ section = @wwid.guess_section(options[:section]) || options[:section].cap_first
58
+ options[:count] = args.count.positive? ? args[0].to_i : 0
59
+
60
+ items = @wwid.filter_items([], opt: options)
61
+
62
+ if options[:interactive]
63
+ items = Doing::Prompt.choose_from_items(items, include_section: options[:section].nil?,
64
+ menu: true,
65
+ header: '',
66
+ prompt: 'Select entries to scan > ',
67
+ multiple: true,
68
+ sort: true,
69
+ show_if_single: true)
70
+ end
71
+
72
+ # items = @wwid.content.in_section(section)
73
+ tags = @wwid.all_tags(items, counts: true)
74
+
75
+ if options[:sort] =~ /^n/i
76
+ tags = tags.sort_by { |tag, count| tag }
77
+ else
78
+ tags = tags.sort_by { |tag, count| count }
79
+ end
80
+
81
+ tags.reverse! if options[:order].normalize_order == 'desc'
82
+
83
+ if options[:counts]
84
+ tags.each { |t, c| puts "#{t} (#{c})" }
85
+ else
86
+ if options[:line]
87
+ puts tags.map { |t, c| t }.to_tags.join(' ')
88
+ else
89
+ tags.each { |t, c| puts "#{t}" }
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,61 @@
1
+ # @@template
2
+ desc 'Output HTML, CSS, and Markdown (ERB) templates for customization'
3
+ long_desc %(
4
+ Templates are printed to STDOUT for piping to a file.
5
+ Save them and use them in the configuration file under export_templates.
6
+ )
7
+ arg_name 'TYPE', must_match: Doing::Plugins.template_regex
8
+ command :template do |c|
9
+ c.example 'doing template haml > ~/styles/my_doing.haml', desc: 'Output the haml template and save it to a file'
10
+
11
+ c.desc 'List all available templates'
12
+ c.switch %i[l list], negatable: false
13
+
14
+ c.desc 'List in single column for completion'
15
+ c.switch %i[c column]
16
+
17
+ c.desc 'Save template to file instead of STDOUT'
18
+ c.switch %i[s save], default_value: false, negatable: false
19
+
20
+ c.desc 'Save template to alternate location'
21
+ c.arg_name 'DIRECTORY'
22
+ c.flag %i[p path], default_value: File.join(Doing::Util.user_home, '.config', 'doing', 'templates')
23
+
24
+ c.action do |_global_options, options, args|
25
+ if options[:list] || options[:column]
26
+ if options[:column]
27
+ $stdout.print Doing::Plugins.plugin_templates.join("\n")
28
+ else
29
+ $stdout.puts "Available templates: #{Doing::Plugins.plugin_templates.join(', ')}"
30
+ end
31
+ return
32
+ end
33
+
34
+ if args.empty?
35
+ type = Doing::Prompt.choose_from(Doing::Plugins.plugin_templates, sorted: false, prompt: 'Select template type > ')
36
+ type.sub!(/ \(.*?\)$/, '').strip!
37
+ options[:save] = Doing::Prompt.yn("Save to #{options[:path]}? (No outputs to STDOUT)", default_response: false)
38
+ else
39
+ type = args[0]
40
+ end
41
+
42
+ raise InvalidPluginType, "No type specified, use `doing template [#{Doing::Plugins.plugin_templates.join('|')}]`" unless type
43
+
44
+ if options[:save]
45
+ Doing::Plugins.template_for_trigger(type, save_to: options[:path])
46
+ else
47
+ $stdout.puts Doing::Plugins.template_for_trigger(type, save_to: nil)
48
+ end
49
+
50
+ # case args[0]
51
+ # when /html|haml/i
52
+ # $stdout.puts @wwid.haml_template
53
+ # when /css/i
54
+ # $stdout.puts @wwid.css_template
55
+ # when /markdown|md|erb/i
56
+ # $stdout.puts @wwid.markdown_template
57
+ # else
58
+ # exit_now! 'Invalid type specified, must be HAML or CSS'
59
+ # end
60
+ end
61
+ end
@@ -0,0 +1,65 @@
1
+ # @@today
2
+ desc 'List entries from today'
3
+ long_desc 'List entries from the current day. Use --before, --after, and
4
+ --from to specify time ranges.'
5
+ command :today do |c|
6
+ c.example 'doing today', desc: 'List all entries with start dates between 12am and 11:59PM for the current day'
7
+ c.example 'doing today --section Later', desc: 'List today\'s entries in the Later section'
8
+ c.example 'doing today --before 3pm --after 12pm', desc: 'List entries with start dates between 12pm and 3pm today'
9
+ c.example 'doing today --output json', desc: 'Output entries from today in JSON format'
10
+
11
+ c.desc 'Specify a 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 'Show elapsed time on entries without @done tag'
19
+ c.switch [:duration]
20
+
21
+ c.desc 'Show time totals at the end of output'
22
+ c.switch [:totals], default_value: false, negatable: false
23
+
24
+ c.desc 'Sort tags by (name|time)'
25
+ default = 'time'
26
+ default = @settings['tag_sort'] || 'name'
27
+ c.arg_name 'KEY'
28
+ c.flag [:tag_sort], must_match: /^(?:name|time)$/i, default_value: default
29
+
30
+ c.desc "Output to export format (#{Doing::Plugins.plugin_names(type: :export)})"
31
+ c.arg_name 'FORMAT'
32
+ c.flag %i[o output]
33
+
34
+ c.desc "Output using a template from configuration"
35
+ c.arg_name 'TEMPLATE_KEY'
36
+ c.flag [:config_template], type: TemplateName, default_value: 'today'
37
+
38
+ c.desc 'Override output format with a template string containing %placeholders'
39
+ c.arg_name 'TEMPLATE_STRING'
40
+ c.flag [:template]
41
+
42
+ c.desc 'View entries before specified time (e.g. 8am, 12:30pm, 15:00)'
43
+ c.arg_name 'TIME_STRING'
44
+ c.flag [:before]
45
+
46
+ c.desc 'View entries after specified time (e.g. 8am, 12:30pm, 15:00)'
47
+ c.arg_name 'TIME_STRING'
48
+ c.flag [:after]
49
+
50
+ c.desc %(
51
+ Time range to show `doing today --from "12pm to 4pm"`
52
+ )
53
+ c.arg_name 'TIME_RANGE'
54
+ c.flag [:from], type: DateRangeString
55
+
56
+ c.action do |_global_options, options, _args|
57
+ raise DoingRuntimeError, %(Invalid output type "#{options[:output]}") if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
58
+
59
+ options[:times] = true if options[:totals]
60
+ options[:sort_tags] = options[:tag_sort] =~ /^n/i
61
+ filter_options = %i[after before duration from section sort_tags totals template config_template].each_with_object({}) { |k, hsh| hsh[k] = options[k] }
62
+
63
+ Doing::Pager.page @wwid.today(options[:times], options[:output], filter_options).chomp
64
+ end
65
+ end
@@ -0,0 +1,49 @@
1
+ # @@undo
2
+ desc 'Undo the last X changes to the Doing file'
3
+ long_desc 'Reverts the last X commands that altered the doing file.
4
+ All changes performed by a single command are undone at once.
5
+
6
+ Specify a number to jump back multiple revisions, or use --select for an interactive menu.'
7
+ arg_name 'COUNT'
8
+ command :undo do |c|
9
+ c.example 'doing undo', desc: 'Undo the most recent change to the doing file'
10
+ c.example 'doing undo 5', desc: 'Undo the last 5 changes to the doing file'
11
+ c.example 'doing undo --interactive', desc: 'Select from a menu of available revisions'
12
+ c.example 'doing undo --redo', desc: 'Undo the last undo command'
13
+
14
+ c.desc 'Specify alternate doing file'
15
+ c.arg_name 'PATH'
16
+ c.flag %i[f file], default_value: @wwid.doing_file
17
+
18
+ c.desc 'Select from recent backups'
19
+ c.switch %i[i interactive], negatable: false
20
+
21
+ c.desc 'Remove old backups, retaining X files'
22
+ c.arg_name 'COUNT'
23
+ c.flag %i[p prune], type: Integer
24
+
25
+ c.desc 'Redo last undo. Note: you cannot undo a redo'
26
+ c.switch %i[r redo]
27
+
28
+ c.action do |_global_options, options, args|
29
+ file = options[:file] || @wwid.doing_file
30
+ count = args.empty? ? 1 : args[0].to_i
31
+ raise InvalidArgument, "Invalid count specified for undo" unless count&.positive?
32
+
33
+ if options[:prune]
34
+ Doing::Util::Backup.prune_backups(file, options[:prune])
35
+ elsif options[:redo]
36
+ if options[:interactive]
37
+ Doing::Util::Backup.select_redo(file)
38
+ else
39
+ Doing::Util::Backup.redo_backup(file, count: count)
40
+ end
41
+ else
42
+ if options[:interactive]
43
+ Doing::Util::Backup.select_backup(file)
44
+ else
45
+ Doing::Util::Backup.restore_last_backup(file, count: count)
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,238 @@
1
+ # @@view
2
+ desc 'Display a user-created view'
3
+ long_desc 'Views are defined in your configuration (use `doing config` to edit).
4
+ Command line options override view configuration.'
5
+ arg_name 'VIEW_NAME'
6
+ command :view do |c|
7
+ c.example 'doing view color', desc: 'Display entries according to config for view "color"'
8
+ c.example 'doing view color --section Archive --count 10', desc: 'Display view "color", overriding some configured settings'
9
+
10
+ c.desc 'Section'
11
+ c.arg_name 'NAME'
12
+ c.flag %i[s section]
13
+
14
+ c.desc 'Count to display'
15
+ c.arg_name 'COUNT'
16
+ c.flag %i[c count], must_match: /^\d+$/, type: Integer
17
+
18
+ c.desc "Output to export format (#{Doing::Plugins.plugin_names(type: :export)})"
19
+ c.arg_name 'FORMAT'
20
+ c.flag %i[o output]
21
+
22
+ c.desc 'Age (oldest|newest)'
23
+ c.arg_name 'AGE'
24
+ c.flag %i[age], default_value: 'newest'
25
+
26
+ c.desc 'Show time intervals on @done tasks'
27
+ c.switch %i[t times], default_value: true, negatable: true
28
+
29
+ c.desc 'Show elapsed time on entries without @done tag'
30
+ c.switch [:duration]
31
+
32
+ c.desc 'Show intervals with totals at the end of output'
33
+ c.switch [:totals], default_value: false, negatable: false
34
+
35
+ c.desc 'Include colors in output'
36
+ c.switch [:color], default_value: true, negatable: true
37
+
38
+ c.desc 'Tag filter, combine multiple tags with a comma. Wildcards allowed (*, ?)'
39
+ c.arg_name 'TAG'
40
+ c.flag [:tag]
41
+
42
+ c.desc 'Perform a tag value query ("@done > two hours ago" or "@progress < 50"). May be used multiple times, combined with --bool'
43
+ c.arg_name 'QUERY'
44
+ c.flag [:val], multiple: true, must_match: REGEX_VALUE_QUERY
45
+
46
+ c.desc 'Tag boolean (AND,OR,NOT). Use PATTERN to parse + and - as booleans'
47
+ c.arg_name 'BOOLEAN'
48
+ c.flag %i[b bool], must_match: REGEX_BOOL, default_value: 'PATTERN'
49
+
50
+ c.desc 'Search filter, surround with slashes for regex (/query/), start with single quote for exact match ("\'query")'
51
+ c.arg_name 'QUERY'
52
+ c.flag [:search]
53
+
54
+ c.desc "Highlight search matches in output. Only affects command line output"
55
+ c.switch %i[h hilite], default_value: @settings.dig('search', 'highlight')
56
+
57
+ # c.desc '[DEPRECATED] Use alternative fuzzy matching for search string'
58
+ # c.switch [:fuzzy], default_value: false, negatable: false
59
+
60
+ c.desc 'Force exact search string matching (case sensitive)'
61
+ c.switch %i[x exact], default_value: @config.exact_match?, negatable: @config.exact_match?
62
+
63
+ c.desc 'Show items that *don\'t* match search string'
64
+ c.switch [:not], default_value: false, negatable: false
65
+
66
+ c.desc 'Case sensitivity for search string matching [(c)ase-sensitive, (i)gnore, (s)mart]'
67
+ c.arg_name 'TYPE'
68
+ c.flag [:case], must_match: /^[csi]/, default_value: @settings.dig('search', 'case')
69
+
70
+ c.desc 'Sort tags by (name|time)'
71
+ c.arg_name 'KEY'
72
+ c.flag [:tag_sort], must_match: /^(?:name|time)$/i
73
+
74
+ c.desc 'Tag sort direction (asc|desc)'
75
+ c.arg_name 'DIRECTION'
76
+ c.flag [:tag_order], must_match: REGEX_SORT_ORDER
77
+
78
+ c.desc 'View 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'
79
+ c.arg_name 'DATE_STRING'
80
+ c.flag [:before], type: DateBeginString
81
+
82
+ c.desc 'View 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'
83
+ c.arg_name 'DATE_STRING'
84
+ c.flag [:after], type: DateEndString
85
+
86
+ c.desc %(
87
+ Date range to show, or a single day to filter date on.
88
+ Date range argument should be quoted. Date specifications can be natural language.
89
+ To specify a range, use "to" or "through": `doing view --from "monday 8am to friday 5pm" view_name`.
90
+
91
+ If values are only time(s) (6am to noon) all dates will be included, but entries will be filtered
92
+ by time of day.
93
+ )
94
+ c.arg_name 'DATE_OR_RANGE'
95
+ c.flag [:from], type: DateRangeString
96
+
97
+ c.desc 'Only show items with recorded time intervals (override view settings)'
98
+ c.switch [:only_timed], default_value: false, negatable: false
99
+
100
+ c.desc 'Select from a menu of matching entries to perform additional operations'
101
+ c.switch %i[i interactive], negatable: false, default_value: false
102
+
103
+ c.action do |global_options, options, args|
104
+ options[:fuzzy] = false
105
+ raise DoingRuntimeError, %(Invalid output type "#{options[:output]}") if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
106
+
107
+ raise InvalidArgument, '--tag and --search can not be used together' if options[:tag] && options[:search]
108
+
109
+ title = if args.empty?
110
+ @wwid.choose_view
111
+ else
112
+ begin
113
+ @wwid.guess_view(args[0])
114
+ rescue WrongCommand => exception
115
+ cmd = commands[:show]
116
+ options[:sort] = 'asc'
117
+ options[:tag_order] = 'asc'
118
+ action = cmd.send(:get_action, nil)
119
+ return action.call(global_options, options, args)
120
+ end
121
+ end
122
+
123
+ if options[:section]
124
+ section = @wwid.guess_section(options[:section]) || options[:section].cap_first
125
+ else
126
+ section = @settings['current_section']
127
+ end
128
+
129
+ view = @wwid.get_view(title)
130
+
131
+ if view
132
+ page_title = view['title'] || title.cap_first
133
+ only_timed = if (view.key?('only_timed') && view['only_timed']) || options[:only_timed]
134
+ true
135
+ else
136
+ false
137
+ end
138
+
139
+ template = view['template'] || nil
140
+ date_format = view['date_format'] || nil
141
+
142
+ tags_color = view['tags_color'] || nil
143
+ tag_filter = false
144
+ if options[:tag]
145
+ tag_filter = { 'tags' => [], 'bool' => 'OR' }
146
+ bool = options[:bool].normalize_bool
147
+ tag_filter['bool'] = bool
148
+ tag_filter['tags'] = if bool == :pattern
149
+ options[:tag]
150
+ else
151
+ options[:tag].gsub(/[, ]+/, ' ').split(' ').map(&:strip)
152
+ end
153
+ elsif view.key?('tags') && view['tags'].good?
154
+ tag_filter = { 'tags' => [], 'bool' => 'OR' }
155
+ bool = view.key?('tags_bool') && !view['tags_bool'].nil? ? view['tags_bool'].normalize_bool : :pattern
156
+ tag_filter['bool'] = bool
157
+ tag_filter['tags'] = if view['tags'].instance_of?(Array)
158
+ bool == :pattern ? view['tags'].join(' ').strip : view['tags'].map(&:strip)
159
+ else
160
+ bool == :pattern ? view['tags'].strip : view['tags'].gsub(/[, ]+/, ' ').split(' ').map(&:strip)
161
+ end
162
+ end
163
+
164
+ # If the -o/--output flag was specified, override any default in the view template
165
+ options[:output] ||= view.key?('output_format') ? view['output_format'] : 'template'
166
+
167
+ count = options[:count] ? options[:count] : view.key?('count') ? view['count'] : 10
168
+
169
+ section = if options[:section]
170
+ section
171
+ else
172
+ view['section'] || @settings['current_section']
173
+ end
174
+ order = view['order']&.normalize_order || 'asc'
175
+
176
+ totals = if options[:totals]
177
+ true
178
+ else
179
+ view['totals'] || false
180
+ end
181
+ tag_order = if options[:tag_order]
182
+ options[:tag_order].normalize_order
183
+ else
184
+ view['tag_order']&.normalize_order || 'asc'
185
+ end
186
+
187
+ options[:times] = true if totals
188
+ output_format = options[:output]&.downcase || 'template'
189
+
190
+ options[:sort_tags] = if options[:tag_sort]
191
+ options[:tag_sort] =~ /^n/i ? true : false
192
+ elsif view.key?('tag_sort')
193
+ view['tag_sort'] =~ /^n/i ? true : false
194
+ else
195
+ false
196
+ end
197
+
198
+ %w[before after from duration].each { |k| options[k.to_sym] = view[k] if view.key?(k) && !options[k.to_sym] }
199
+
200
+ options[:case] = options[:case].normalize_case
201
+
202
+ search = nil
203
+
204
+ if options[:search]
205
+ search = options[:search]
206
+ search.sub!(/^'?/, "'") if options[:exact]
207
+ end
208
+
209
+ options[:age] ||= :newest
210
+
211
+ opts = options.clone
212
+ opts[:age] = options[:age].normalize_age(:newest)
213
+ opts[:count] = count
214
+ opts[:format] = date_format
215
+ opts[:highlight] = options[:color]
216
+ opts[:hilite] = options[:hilite]
217
+ opts[:only_timed] = only_timed
218
+ opts[:order] = order
219
+ opts[:output] = options[:interactive] ? nil : options[:output]
220
+ opts[:output] = output_format
221
+ opts[:page_title] = page_title
222
+ opts[:search] = search
223
+ opts[:section] = section
224
+ opts[:tag_filter] = tag_filter
225
+ opts[:tag_order] = tag_order
226
+ opts[:tags_color] = tags_color
227
+ opts[:template] = template
228
+ opts[:totals] = totals
229
+ opts[:view_template] = title
230
+
231
+ Doing::Pager.page @wwid.list_section(opts)
232
+ elsif title.instance_of?(FalseClass)
233
+ raise UserCancelled, 'Cancelled'
234
+ else
235
+ raise InvalidView, "View #{title} not found in config"
236
+ end
237
+ end
238
+ end
@@ -0,0 +1,11 @@
1
+ # @@views
2
+ desc 'List available custom views'
3
+ command :views do |c|
4
+ c.desc 'List in single column'
5
+ c.switch %i[c column], default_value: false
6
+
7
+ c.action do |_global_options, options, _args|
8
+ joiner = options[:column] ? "\n" : "\t"
9
+ print @wwid.views.join(joiner)
10
+ end
11
+ end
@@ -0,0 +1,73 @@
1
+ # @@yesterday
2
+ desc 'List entries from yesterday'
3
+ long_desc 'Show only entries with start times within the previous 24 hour period. Use --before, --after, and --from to limit to
4
+ time spans within the day.'
5
+ command :yesterday do |c|
6
+ c.example 'doing yesterday', desc: 'List all entries from the previous day'
7
+ c.example 'doing yesterday --after 8am --before 5pm', desc: 'List entries from the previous day between 8am and 5pm'
8
+ c.example 'doing yesterday --totals', desc: 'List entries from previous day, including tag timers'
9
+
10
+ c.desc 'Specify a section'
11
+ c.arg_name 'NAME'
12
+ c.flag %i[s section], default_value: 'All'
13
+
14
+ c.desc "Output to export format (#{Doing::Plugins.plugin_names(type: :export)})"
15
+ c.arg_name 'FORMAT'
16
+ c.flag %i[o output]
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: 'today'
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 time intervals on @done tasks'
27
+ c.switch %i[t times], default_value: true, negatable: true
28
+
29
+ c.desc 'Show elapsed time on entries without @done tag'
30
+ c.switch [:duration]
31
+
32
+ c.desc 'Show time totals at the end of output'
33
+ c.switch [:totals], default_value: false, negatable: false
34
+
35
+ c.desc 'Sort tags by (name|time)'
36
+ default = @settings['tag_sort'] || 'name'
37
+ c.arg_name 'KEY'
38
+ c.flag [:tag_sort], must_match: /^(?:name|time)$/i, default_value: default
39
+
40
+ c.desc 'View entries before specified time (e.g. 8am, 12:30pm, 15:00)'
41
+ c.arg_name 'TIME_STRING'
42
+ c.flag [:before]
43
+
44
+ c.desc 'View entries after specified time (e.g. 8am, 12:30pm, 15:00)'
45
+ c.arg_name 'TIME_STRING'
46
+ c.flag [:after]
47
+
48
+ c.desc 'Time range to show, e.g. `doing yesterday --from "1am to 8am"`'
49
+ c.arg_name 'TIME_RANGE'
50
+ c.flag [:from], must_match: REGEX_TIME_RANGE
51
+
52
+ c.desc 'Tag sort direction (asc|desc)'
53
+ c.arg_name 'DIRECTION'
54
+ c.flag [:tag_order], must_match: REGEX_SORT_ORDER, default_value: 'asc'
55
+
56
+ c.action do |_global_options, options, _args|
57
+ raise DoingRuntimeError, %(Invalid output type "#{options[:output]}") if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
58
+
59
+ options[:sort_tags] = options[:tag_sort] =~ /^n/i
60
+
61
+ if options[:from]
62
+ options[:from] = options[:from].split(/#{REGEX_RANGE_INDICATOR}/).map do |time|
63
+ "yesterday #{time.sub(/(?mi)(^.*?(?=\d+)|(?<=[ap]m).*?$)/, '')}"
64
+ end.join(' to ').split_date_range
65
+ end
66
+
67
+ opt = options.clone
68
+ opt[:tag_order] = options[:tag_order].normalize_order
69
+ opt[:order] = @settings.dig('templates', options[:config_template], 'order')
70
+
71
+ Doing::Pager.page @wwid.yesterday(options[:section], options[:times], options[:output], opt).chomp
72
+ end
73
+ end