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.
- checksums.yaml +4 -4
- data/.yardoc/checksums +17 -21
- data/.yardoc/object_types +0 -0
- data/.yardoc/objects/root.dat +0 -0
- data/CHANGELOG.md +325 -102
- data/Dockerfile +5 -5
- data/Dockerfile-2.6 +5 -5
- data/Dockerfile-2.7 +5 -4
- data/Dockerfile-3.0 +5 -4
- data/Gemfile.lock +1 -1
- data/README.md +1 -1
- data/Rakefile +3 -3
- data/bin/commands/add_section.rb +15 -0
- data/bin/commands/again.rb +57 -0
- data/bin/commands/archive.rb +55 -0
- data/bin/commands/cancel.rb +60 -0
- data/bin/commands/changes.rb +73 -0
- data/bin/commands/choose.rb +9 -0
- data/bin/commands/colors.rb +21 -0
- data/bin/commands/commands.rb +89 -0
- data/bin/commands/commands_accepting.rb +76 -0
- data/bin/commands/completion.rb +27 -0
- data/bin/commands/config.rb +245 -0
- data/bin/commands/done.rb +235 -0
- data/bin/commands/finish.rb +126 -0
- data/bin/commands/flag.rb +90 -0
- data/bin/commands/grep.rb +108 -0
- data/bin/commands/import.rb +71 -0
- data/bin/commands/install_fzf.rb +17 -0
- data/bin/commands/last.rb +81 -0
- data/bin/commands/meanwhile.rb +76 -0
- data/bin/commands/note.rb +91 -0
- data/bin/commands/now.rb +145 -0
- data/bin/commands/on.rb +65 -0
- data/bin/commands/open.rb +53 -0
- data/bin/commands/plugins.rb +23 -0
- data/bin/commands/recent.rb +77 -0
- data/bin/commands/redo.rb +26 -0
- data/bin/commands/reset.rb +73 -0
- data/bin/commands/rotate.rb +42 -0
- data/bin/commands/sections.rb +11 -0
- data/bin/commands/select.rb +105 -0
- data/bin/commands/show.rb +185 -0
- data/bin/commands/since.rb +63 -0
- data/bin/commands/tag.rb +149 -0
- data/bin/commands/tag_dir.rb +29 -0
- data/bin/commands/tags.rb +66 -0
- data/bin/commands/template.rb +61 -0
- data/bin/commands/today.rb +64 -0
- data/bin/commands/undo.rb +49 -0
- data/bin/commands/view.rb +201 -0
- data/bin/commands/views.rb +11 -0
- data/bin/commands/yesterday.rb +72 -0
- data/bin/doing +241 -3662
- data/docs/doc/Array.html +13 -449
- data/docs/doc/BooleanTermParser/Clause.html +5 -5
- data/docs/doc/BooleanTermParser/Operator.html +4 -4
- data/docs/doc/BooleanTermParser/Query.html +8 -8
- data/docs/doc/BooleanTermParser/QueryParser.html +2 -2
- data/docs/doc/BooleanTermParser/QueryTransformer.html +2 -2
- data/docs/doc/BooleanTermParser.html +1 -1
- data/docs/doc/Doing/Color.html +65 -59
- data/docs/doc/Doing/Completion.html +2 -2
- data/docs/doc/Doing/Configuration.html +49 -16
- data/docs/doc/Doing/Errors/DoingNoTraceError.html +2 -2
- data/docs/doc/Doing/Errors/DoingRuntimeError.html +2 -2
- data/docs/doc/Doing/Errors/DoingStandardError.html +2 -2
- data/docs/doc/Doing/Errors/EmptyInput.html +2 -2
- data/docs/doc/Doing/Errors/NoResults.html +2 -2
- data/docs/doc/Doing/Errors/PluginException.html +3 -3
- data/docs/doc/Doing/Errors/UserCancelled.html +2 -2
- data/docs/doc/Doing/Errors/WrongCommand.html +2 -2
- data/docs/doc/Doing/Errors.html +1 -1
- data/docs/doc/Doing/Hooks.html +6 -6
- data/docs/doc/Doing/Item.html +50 -16
- data/docs/doc/Doing/Items.html +10 -10
- data/docs/doc/Doing/LogAdapter.html +24 -24
- data/docs/doc/Doing/Note.html +7 -7
- data/docs/doc/Doing/Pager.html +4 -4
- data/docs/doc/Doing/Plugins.html +7 -7
- data/docs/doc/Doing/Prompt.html +59 -14
- data/docs/doc/Doing/Section.html +6 -6
- data/docs/doc/Doing/TemplateString.html +8 -8
- data/docs/doc/Doing/Types.html +46 -1
- data/docs/doc/Doing/Util/Backup.html +10 -10
- data/docs/doc/Doing/Util.html +15 -15
- data/docs/doc/Doing/WWID.html +73 -61
- data/docs/doc/Doing.html +3 -3
- data/docs/doc/FalseClass.html +235 -0
- data/docs/doc/GLI/Commands/Help.html +3 -3
- data/docs/doc/GLI/Commands/MarkdownDocumentListener.html +17 -17
- data/docs/doc/GLI/Commands.html +1 -1
- data/docs/doc/GLI.html +1 -1
- data/docs/doc/Hash.html +45 -11
- data/docs/doc/Numeric.html +5 -5
- data/docs/doc/Object.html +203 -0
- data/docs/doc/PhraseParser/Operator.html +4 -4
- data/docs/doc/PhraseParser/PhraseClause.html +5 -5
- data/docs/doc/PhraseParser/Query.html +10 -10
- data/docs/doc/PhraseParser/QueryParser.html +2 -2
- data/docs/doc/PhraseParser/QueryTransformer.html +2 -2
- data/docs/doc/PhraseParser/TermClause.html +5 -5
- data/docs/doc/PhraseParser.html +1 -1
- data/docs/doc/Status.html +7 -7
- data/docs/doc/String.html +306 -3111
- data/docs/doc/Symbol.html +45 -11
- data/docs/doc/Time.html +6 -6
- data/docs/doc/TrueClass.html +235 -0
- data/docs/doc/_index.html +37 -19
- data/docs/doc/class_list.html +1 -1
- data/docs/doc/file.README.html +2 -2
- data/docs/doc/index.html +2 -2
- data/docs/doc/method_list.html +240 -576
- data/docs/doc/top-level-namespace.html +2 -2
- data/doing.rdoc +297 -169
- data/example_plugin.rb +2 -2
- data/lib/completion/_doing.zsh +35 -31
- data/lib/completion/doing.bash +30 -19
- data/lib/completion/doing.fish +81 -67
- data/lib/doing/array/array.rb +4 -0
- data/lib/doing/array/nested_hash.rb +17 -0
- data/lib/doing/{array.rb → array/tags.rb} +7 -25
- data/lib/doing/changelog/change.rb +26 -11
- data/lib/doing/changelog/changes.rb +16 -4
- data/lib/doing/{array_chronify.rb → chronify/array.rb} +0 -0
- data/lib/doing/chronify/chronify.rb +5 -0
- data/lib/doing/{numeric_chronify.rb → chronify/numeric.rb} +0 -0
- data/lib/doing/{string_chronify.rb → chronify/string.rb} +0 -0
- data/lib/doing/colors.rb +115 -54
- data/lib/doing/configuration.rb +9 -6
- data/lib/doing/good.rb +72 -0
- data/lib/doing/hash.rb +4 -0
- data/lib/doing/help_monkey_patch.rb +6 -5
- data/lib/doing/hooks.rb +3 -3
- data/lib/doing/item.rb +19 -15
- data/lib/doing/items.rb +2 -2
- data/lib/doing/log_adapter.rb +35 -2
- data/lib/doing/normalize.rb +188 -0
- data/lib/doing/pager.rb +1 -0
- data/lib/doing/plugins/export/dayone_export.rb +1 -1
- data/lib/doing/plugins/export/html_export.rb +1 -1
- data/lib/doing/plugins/export/json_export.rb +1 -1
- data/lib/doing/plugins/export/markdown_export.rb +1 -1
- data/lib/doing/plugins/export/template_export.rb +3 -1
- data/lib/doing/plugins/import/calendar_import.rb +1 -1
- data/lib/doing/plugins/import/doing_import.rb +1 -1
- data/lib/doing/plugins/import/timing_import.rb +1 -1
- data/lib/doing/prompt.rb +9 -3
- data/lib/doing/string/highlight.rb +95 -0
- data/lib/doing/string/query.rb +129 -0
- data/lib/doing/string/string.rb +12 -0
- data/lib/doing/string/tags.rb +164 -0
- data/lib/doing/string/transform.rb +168 -0
- data/lib/doing/string/truncate.rb +75 -0
- data/lib/doing/string/url.rb +82 -0
- data/lib/doing/template_string.rb +2 -24
- data/lib/doing/types.rb +9 -0
- data/lib/doing/util.rb +20 -16
- data/lib/doing/version.rb +1 -1
- data/lib/doing/wwid.rb +91 -51
- data/lib/doing.rb +5 -6
- data/lib/examples/commands/wiki.rb +6 -7
- data/lib/examples/plugins/wiki_export/wiki_export.rb +1 -1
- data/lib/helpers/threaded_tests.rb +69 -79
- data/lib/helpers/threaded_tests_string.rb +50 -0
- data/scripts/deploy.rb +107 -0
- data/scripts/runtests.sh +4 -0
- metadata +65 -8
- data/lib/doing/string.rb +0 -765
- 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
|