doing 2.1.26 → 2.1.27
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 +14 -19
- data/.yardoc/object_types +0 -0
- data/.yardoc/objects/root.dat +0 -0
- data/CHANGELOG.md +23 -0
- 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 +2 -3
- data/bin/commands/add_section.rb +2 -0
- data/bin/commands/again.rb +23 -65
- data/bin/commands/archive.rb +20 -61
- data/bin/commands/cancel.rb +27 -69
- data/bin/commands/changes.rb +32 -5
- data/bin/commands/colors.rb +4 -2
- data/bin/commands/commands.rb +4 -2
- data/bin/commands/commands_accepting.rb +62 -11
- data/bin/commands/completion.rb +10 -7
- data/bin/commands/config.rb +1 -1
- data/bin/commands/done.rb +3 -17
- data/bin/commands/finish.rb +7 -30
- data/bin/commands/flag.rb +15 -51
- data/bin/commands/grep.rb +12 -28
- data/bin/commands/import.rb +3 -33
- data/bin/commands/last.rb +3 -36
- data/bin/commands/meanwhile.rb +3 -13
- data/bin/commands/note.rb +13 -52
- data/bin/commands/now.rb +15 -21
- data/bin/commands/on.rb +3 -4
- data/bin/commands/recent.rb +3 -4
- data/bin/commands/redo.rb +6 -2
- data/bin/commands/reset.rb +19 -52
- data/bin/commands/rotate.rb +5 -36
- data/bin/commands/select.rb +23 -41
- data/bin/commands/show.rb +28 -74
- data/bin/commands/since.rb +3 -4
- data/bin/commands/tag.rb +4 -34
- data/bin/commands/tags.rb +5 -32
- data/bin/commands/today.rb +3 -4
- data/bin/commands/view.rb +36 -73
- data/bin/commands/yesterday.rb +4 -5
- data/bin/doing +150 -13
- data/docs/doc/Array.html +3 -502
- data/docs/doc/BooleanTermParser/Clause.html +1 -1
- data/docs/doc/BooleanTermParser/Operator.html +1 -1
- data/docs/doc/BooleanTermParser/Query.html +1 -1
- data/docs/doc/BooleanTermParser/QueryParser.html +1 -1
- data/docs/doc/BooleanTermParser/QueryTransformer.html +1 -1
- data/docs/doc/BooleanTermParser.html +1 -1
- data/docs/doc/Doing/Color.html +62 -56
- data/docs/doc/Doing/Completion.html +1 -1
- data/docs/doc/Doing/Configuration.html +35 -1
- data/docs/doc/Doing/Errors/DoingNoTraceError.html +1 -1
- data/docs/doc/Doing/Errors/DoingRuntimeError.html +1 -1
- data/docs/doc/Doing/Errors/DoingStandardError.html +1 -1
- data/docs/doc/Doing/Errors/EmptyInput.html +1 -1
- data/docs/doc/Doing/Errors/NoResults.html +1 -1
- data/docs/doc/Doing/Errors/PluginException.html +1 -1
- data/docs/doc/Doing/Errors/UserCancelled.html +1 -1
- data/docs/doc/Doing/Errors/WrongCommand.html +1 -1
- data/docs/doc/Doing/Errors.html +1 -1
- data/docs/doc/Doing/Hooks.html +1 -1
- data/docs/doc/Doing/Item.html +1 -1
- data/docs/doc/Doing/Items.html +2 -2
- data/docs/doc/Doing/LogAdapter.html +1 -1
- data/docs/doc/Doing/Note.html +2 -2
- data/docs/doc/Doing/Pager.html +1 -1
- data/docs/doc/Doing/Plugins.html +1 -1
- data/docs/doc/Doing/Prompt.html +1 -1
- data/docs/doc/Doing/Section.html +1 -1
- data/docs/doc/Doing/TemplateString.html +2 -2
- data/docs/doc/Doing/Types.html +41 -1
- data/docs/doc/Doing/Util/Backup.html +1 -1
- data/docs/doc/Doing/Util.html +1 -1
- data/docs/doc/Doing/WWID.html +10 -10
- data/docs/doc/Doing.html +3 -3
- data/docs/doc/FalseClass.html +35 -1
- data/docs/doc/GLI/Commands/Help.html +1 -1
- data/docs/doc/GLI/Commands/MarkdownDocumentListener.html +1 -1
- data/docs/doc/GLI/Commands.html +1 -1
- data/docs/doc/GLI.html +1 -1
- data/docs/doc/Hash.html +1 -1
- data/docs/doc/Object.html +1 -1
- data/docs/doc/PhraseParser/Operator.html +1 -1
- data/docs/doc/PhraseParser/PhraseClause.html +1 -1
- data/docs/doc/PhraseParser/Query.html +1 -1
- data/docs/doc/PhraseParser/QueryParser.html +1 -1
- data/docs/doc/PhraseParser/QueryTransformer.html +1 -1
- data/docs/doc/PhraseParser/TermClause.html +1 -1
- data/docs/doc/PhraseParser.html +1 -1
- data/docs/doc/Status.html +1 -1
- data/docs/doc/String.html +287 -3155
- data/docs/doc/Symbol.html +40 -6
- data/docs/doc/Time.html +1 -1
- data/docs/doc/TrueClass.html +35 -1
- data/docs/doc/_index.html +5 -10
- 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 +278 -678
- data/docs/doc/top-level-namespace.html +2 -2
- data/doing.rdoc +277 -175
- data/lib/completion/_doing.zsh +33 -29
- data/lib/completion/doing.bash +30 -19
- data/lib/completion/doing.fish +84 -72
- 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 +13 -3
- 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 +4 -0
- data/lib/doing/good.rb +8 -0
- data/lib/doing/help_monkey_patch.rb +6 -5
- data/lib/doing/item.rb +5 -5
- 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/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/prompt.rb +1 -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 +0 -22
- data/lib/doing/types.rb +8 -0
- data/lib/doing/util.rb +13 -9
- data/lib/doing/version.rb +1 -1
- data/lib/doing/wwid.rb +53 -35
- data/lib/doing.rb +4 -6
- data/lib/examples/plugins/wiki_export/wiki_export.rb +1 -1
- data/lib/helpers/threaded_tests.rb +15 -2
- data/scripts/deploy.rb +107 -0
- data/scripts/runtests.sh +4 -0
- metadata +19 -8
- data/lib/doing/string.rb +0 -765
- data/lib/doing/symbol.rb +0 -28
data/bin/commands/now.rb
CHANGED
|
@@ -20,13 +20,6 @@ command %i[now next] do |c|
|
|
|
20
20
|
c.arg_name 'NAME'
|
|
21
21
|
c.flag %i[s section]
|
|
22
22
|
|
|
23
|
-
c.desc "Edit entry with #{Doing::Util.default_editor}"
|
|
24
|
-
c.switch %i[e editor], negatable: false, default_value: false
|
|
25
|
-
|
|
26
|
-
c.desc 'Backdate start time [4pm|20m|2h|"yesterday noon"]'
|
|
27
|
-
c.arg_name 'DATE_STRING'
|
|
28
|
-
c.flag %i[b back started], type: DateBeginString
|
|
29
|
-
|
|
30
23
|
c.desc %(
|
|
31
24
|
Set a start and optionally end time as a date range ("from 1pm to 2:30pm").
|
|
32
25
|
If an end time is provided, a dated @done tag will be added
|
|
@@ -37,19 +30,16 @@ command %i[now next] do |c|
|
|
|
37
30
|
c.desc 'Timed entry, marks last entry in section as @done'
|
|
38
31
|
c.switch %i[f finish_last], negatable: false, default_value: false
|
|
39
32
|
|
|
40
|
-
c
|
|
41
|
-
c.arg_name 'TEXT'
|
|
42
|
-
c.flag %i[n note]
|
|
43
|
-
|
|
44
|
-
c.desc 'Prompt for note via multi-line input'
|
|
45
|
-
c.switch %i[ask], negatable: false, default_value: false
|
|
33
|
+
add_options(:add_entry, c)
|
|
46
34
|
|
|
47
35
|
# c.desc "Edit entry with specified app"
|
|
48
36
|
# c.arg_name 'editor_app'
|
|
49
37
|
# # c.flag [:a, :app]
|
|
50
38
|
|
|
51
39
|
c.action do |_global_options, options, args|
|
|
52
|
-
|
|
40
|
+
@wwid.auto_tag = !options[:noauto]
|
|
41
|
+
|
|
42
|
+
raise InvalidArgument, '--back and --from cannot be used together' if options[:back] && options[:from]
|
|
53
43
|
|
|
54
44
|
if options[:back]
|
|
55
45
|
date = options[:back]
|
|
@@ -61,13 +51,17 @@ command %i[now next] do |c|
|
|
|
61
51
|
end
|
|
62
52
|
raise InvalidTimeExpression.new('unable to parse date string', topic: 'Parser:') if date.nil?
|
|
63
53
|
|
|
64
|
-
if options[:section]
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
ask_note = options[:ask] && !options[:editor] && args.count.positive?
|
|
54
|
+
section = if options[:section]
|
|
55
|
+
@wwid.guess_section(options[:section]) || options[:section].cap_first
|
|
56
|
+
else
|
|
57
|
+
@settings['current_section']
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
ask_note = if options[:ask] && !options[:editor] && args.count.positive?
|
|
61
|
+
Doing::Prompt.read_lines(prompt: 'Add a note')
|
|
62
|
+
else
|
|
63
|
+
''
|
|
64
|
+
end
|
|
71
65
|
|
|
72
66
|
if options[:editor]
|
|
73
67
|
raise MissingEditor, 'No EDITOR variable defined in environment' if Doing::Util.default_editor.nil?
|
data/bin/commands/on.rb
CHANGED
|
@@ -23,10 +23,9 @@ command :on do |c|
|
|
|
23
23
|
c.switch [:totals], default_value: false, negatable: false
|
|
24
24
|
|
|
25
25
|
c.desc 'Sort tags by (name|time)'
|
|
26
|
-
default = '
|
|
27
|
-
default = @settings['tag_sort'] || 'name'
|
|
26
|
+
default = @settings['tag_sort'].normalize_tag_sort || :name
|
|
28
27
|
c.arg_name 'KEY'
|
|
29
|
-
c.flag [:tag_sort], must_match:
|
|
28
|
+
c.flag [:tag_sort], must_match: REGEX_TAG_SORT, default_value: default, type: TagSortSymbol
|
|
30
29
|
|
|
31
30
|
c.desc "Output to export format (#{Doing::Plugins.plugin_names(type: :export)})"
|
|
32
31
|
c.arg_name 'FORMAT'
|
|
@@ -58,7 +57,7 @@ command :on do |c|
|
|
|
58
57
|
Doing.logger.debug('Interpreter:', message)
|
|
59
58
|
|
|
60
59
|
options[:times] = true if options[:totals]
|
|
61
|
-
options[:sort_tags] = options[:tag_sort]
|
|
60
|
+
options[:sort_tags] = options[:tag_sort]
|
|
62
61
|
|
|
63
62
|
Doing::Pager.page @wwid.list_date([start, finish], options[:section], options[:times], options[:output],
|
|
64
63
|
{ template: options[:template], config_template: options[:config_template], duration: options[:duration], totals: options[:totals], sort_tags: options[:sort_tags] }).chomp
|
data/bin/commands/recent.rb
CHANGED
|
@@ -30,10 +30,9 @@ command :recent do |c|
|
|
|
30
30
|
c.switch [:totals], default_value: false, negatable: false
|
|
31
31
|
|
|
32
32
|
c.desc 'Sort tags by (name|time)'
|
|
33
|
-
default = '
|
|
34
|
-
default = @settings['tag_sort'] || 'name'
|
|
33
|
+
default = @settings['tag_sort'].normalize_tag_sort || :name
|
|
35
34
|
c.arg_name 'KEY'
|
|
36
|
-
c.flag [:tag_sort], must_match:
|
|
35
|
+
c.flag [:tag_sort], must_match: REGEX_TAG_SORT, default_value: default, type: TagSortSymbol
|
|
37
36
|
|
|
38
37
|
c.desc 'Select from a menu of matching entries to perform additional operations'
|
|
39
38
|
c.switch %i[i interactive], negatable: false, default_value: false
|
|
@@ -55,7 +54,7 @@ command :recent do |c|
|
|
|
55
54
|
end
|
|
56
55
|
|
|
57
56
|
options[:times] = true if options[:totals]
|
|
58
|
-
options[:sort_tags] = options[:tag_sort]
|
|
57
|
+
options[:sort_tags] = options[:tag_sort]
|
|
59
58
|
|
|
60
59
|
template = @settings['templates']['recent'].deep_merge(@settings['templates']['default'])
|
|
61
60
|
tags_color = template.key?('tags_color') ? template['tags_color'] : nil
|
data/bin/commands/redo.rb
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
# @@redo
|
|
2
|
-
|
|
4
|
+
desc 'Redo an undo command'
|
|
5
|
+
long_desc 'Shortcut for `doing undo -r`, reverses the last undo command. Specify a count to redo multiple undos'
|
|
3
6
|
arg_name 'COUNT'
|
|
4
7
|
command :redo do |c|
|
|
5
8
|
c.desc 'Specify alternate doing file'
|
|
@@ -12,7 +15,8 @@ command :redo do |c|
|
|
|
12
15
|
c.action do |_global, options, args|
|
|
13
16
|
file = options[:file] || @wwid.doing_file
|
|
14
17
|
count = args.empty? ? 1 : args[0].to_i
|
|
15
|
-
raise InvalidArgument,
|
|
18
|
+
raise InvalidArgument, 'Invalid count specified for redo' unless count&.positive?
|
|
19
|
+
|
|
16
20
|
if options[:interactive]
|
|
17
21
|
Doing::Util::Backup.select_redo(file)
|
|
18
22
|
else
|
data/bin/commands/reset.rb
CHANGED
|
@@ -20,54 +20,24 @@ command %i[reset begin] do |c|
|
|
|
20
20
|
c.desc 'Change start date but do not remove @done (shortcut for --no-resume)'
|
|
21
21
|
c.switch [:n]
|
|
22
22
|
|
|
23
|
-
c.desc 'Reset last entry matching tag. Wildcards allowed (*, ?)'
|
|
24
|
-
c.arg_name 'TAG'
|
|
25
|
-
c.flag [:tag]
|
|
26
|
-
|
|
27
|
-
c.desc 'Reset last entry matching search filter, surround with slashes for regex (e.g. "/query.*/"), start with single quote for exact match ("\'query")'
|
|
28
|
-
c.arg_name 'QUERY'
|
|
29
|
-
c.flag [:search]
|
|
30
|
-
|
|
31
|
-
c.desc 'Perform a tag value query ("@done > two hours ago" or "@progress < 50"). May be used multiple times, combined with --bool'
|
|
32
|
-
c.arg_name 'QUERY'
|
|
33
|
-
c.flag [:val], multiple: true, must_match: REGEX_VALUE_QUERY
|
|
34
|
-
|
|
35
|
-
# c.desc '[DEPRECATED] Use alternative fuzzy matching for search string'
|
|
36
|
-
# c.switch [:fuzzy], default_value: false, negatable: false
|
|
37
|
-
|
|
38
|
-
c.desc 'Force exact search string matching (case sensitive)'
|
|
39
|
-
c.switch %i[x exact], default_value: @config.exact_match?, negatable: @config.exact_match?
|
|
40
|
-
|
|
41
|
-
c.desc 'Reset items that *don\'t* match search/tag filters'
|
|
42
|
-
c.switch [:not], default_value: false, negatable: false
|
|
43
|
-
|
|
44
|
-
c.desc 'Case sensitivity for search string matching [(c)ase-sensitive, (i)gnore, (s)mart]'
|
|
45
|
-
c.arg_name 'TYPE'
|
|
46
|
-
c.flag [:case], must_match: /^[csi]/, default_value: @settings.dig('search', 'case')
|
|
47
|
-
|
|
48
|
-
c.desc 'Boolean (AND|OR|NOT) with which to combine multiple tag filters. Use PATTERN to parse + and - as booleans'
|
|
49
|
-
c.arg_name 'BOOLEAN'
|
|
50
|
-
c.flag [:bool], must_match: REGEX_BOOL, default_value: 'PATTERN'
|
|
51
|
-
|
|
52
23
|
c.desc 'Select from a menu of matching entries'
|
|
53
24
|
c.switch %i[i interactive], negatable: false, default_value: false
|
|
54
25
|
|
|
26
|
+
add_options(:search, c)
|
|
27
|
+
add_options(:tag_filter, c)
|
|
28
|
+
|
|
55
29
|
c.action do |global_options, options, args|
|
|
56
|
-
if args.count
|
|
30
|
+
if args.count.positive?
|
|
57
31
|
reset_date = args.join(' ').chronify(guess: :begin)
|
|
58
32
|
raise InvalidArgument, 'Invalid date string' unless reset_date
|
|
33
|
+
|
|
59
34
|
else
|
|
60
35
|
reset_date = Time.now
|
|
61
36
|
end
|
|
62
37
|
|
|
63
38
|
options[:fuzzy] = false
|
|
64
|
-
if options[:section]
|
|
65
|
-
options[:section] = @wwid.guess_section(options[:section]) || options[:section].cap_first
|
|
66
|
-
end
|
|
67
39
|
|
|
68
|
-
options[:
|
|
69
|
-
|
|
70
|
-
options[:case] = options[:case].normalize_case
|
|
40
|
+
options[:section] = @wwid.guess_section(options[:section]) || options[:section].cap_first if options[:section]
|
|
71
41
|
|
|
72
42
|
if options[:search]
|
|
73
43
|
search = options[:search]
|
|
@@ -75,25 +45,22 @@ command %i[reset begin] do |c|
|
|
|
75
45
|
options[:search] = search
|
|
76
46
|
end
|
|
77
47
|
|
|
78
|
-
|
|
79
48
|
items = @wwid.filter_items([], opt: options)
|
|
80
49
|
|
|
81
|
-
if options[:interactive]
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
50
|
+
last_entry = if options[:interactive]
|
|
51
|
+
Doing::Prompt.choose_from_items(items, include_section: options[:section].nil?,
|
|
52
|
+
menu: true,
|
|
53
|
+
header: '',
|
|
54
|
+
prompt: 'Select an entry to start/reset > ',
|
|
55
|
+
multiple: false,
|
|
56
|
+
sort: false,
|
|
57
|
+
show_if_single: true)
|
|
58
|
+
else
|
|
59
|
+
items.reverse.last
|
|
60
|
+
end
|
|
92
61
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
return
|
|
96
|
-
end
|
|
62
|
+
|
|
63
|
+
raise NoResults, 'No entry matching parameters was found.' unless last_entry
|
|
97
64
|
|
|
98
65
|
old_item = last_entry.clone
|
|
99
66
|
|
data/bin/commands/rotate.rb
CHANGED
|
@@ -16,49 +16,18 @@ command :rotate do |c|
|
|
|
16
16
|
c.arg_name 'SECTION_NAME'
|
|
17
17
|
c.flag %i[s section], default_value: 'All'
|
|
18
18
|
|
|
19
|
-
c.desc 'Tag filter, combine multiple tags with a comma. Wildcards allowed (*, ?). Added for compatibility with other commands'
|
|
20
|
-
c.arg_name 'TAG'
|
|
21
|
-
c.flag [:tag]
|
|
22
|
-
|
|
23
|
-
c.desc 'Tag boolean (AND|OR|NOT). Use PATTERN to parse + and - as booleans'
|
|
24
|
-
c.arg_name 'BOOLEAN'
|
|
25
|
-
c.flag [:bool], must_match: REGEX_BOOL, default_value: 'PATTERN'
|
|
26
|
-
|
|
27
|
-
c.desc 'Search filter'
|
|
28
|
-
c.arg_name 'QUERY'
|
|
29
|
-
c.flag [:search]
|
|
30
|
-
|
|
31
|
-
c.desc 'Perform a tag value query ("@done > two hours ago" or "@progress < 50"). May be used multiple times, combined with --bool'
|
|
32
|
-
c.arg_name 'QUERY'
|
|
33
|
-
c.flag [:val], multiple: true, must_match: REGEX_VALUE_QUERY
|
|
34
|
-
|
|
35
|
-
# c.desc '[DEPRECATED] Use alternative fuzzy matching for search string'
|
|
36
|
-
# c.switch [:fuzzy], default_value: false, negatable: false
|
|
37
|
-
|
|
38
|
-
c.desc 'Force exact search string matching (case sensitive)'
|
|
39
|
-
c.switch %i[x exact], default_value: @config.exact_match?, negatable: @config.exact_match?
|
|
40
|
-
|
|
41
|
-
c.desc 'Rotate items that *don\'t* match search string or tag filter'
|
|
42
|
-
c.switch [:not], default_value: false, negatable: false
|
|
43
|
-
|
|
44
|
-
c.desc 'Case sensitivity for search string matching [(c)ase-sensitive, (i)gnore, (s)mart]'
|
|
45
|
-
c.arg_name 'TYPE'
|
|
46
|
-
c.flag [:case], must_match: /^[csi]/, default_value: @settings.dig('search', 'case')
|
|
47
|
-
|
|
48
19
|
c.desc 'Rotate entries older than date
|
|
49
20
|
(Flexible date format, e.g. 1/27/2021, 2020-07-19, or Monday 3pm)'
|
|
50
21
|
c.arg_name 'DATE_STRING'
|
|
51
22
|
c.flag [:before]
|
|
52
23
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if options[:section] && options[:section] !~ /^all$/i
|
|
56
|
-
options[:section] = @wwid.guess_section(options[:section])
|
|
57
|
-
end
|
|
24
|
+
add_options(:search, c)
|
|
25
|
+
add_options(:tag_filter, c)
|
|
58
26
|
|
|
59
|
-
|
|
27
|
+
c.action do |_global_options, options, _args|
|
|
28
|
+
options[:fuzzy] = false
|
|
60
29
|
|
|
61
|
-
options[:
|
|
30
|
+
options[:section] = @wwid.guess_section(options[:section]) if options[:section] && options[:section] !~ /^all$/i
|
|
62
31
|
|
|
63
32
|
search = nil
|
|
64
33
|
|
data/bin/commands/select.rb
CHANGED
|
@@ -14,9 +14,13 @@ sbtrkt fuzzy-match Items that match s*b*t*r*k*t
|
|
|
14
14
|
|
|
15
15
|
!fire inverse-exact-match Items that do not include fire'
|
|
16
16
|
command :select do |c|
|
|
17
|
-
c.example 'doing select',
|
|
18
|
-
|
|
19
|
-
c.example 'doing select --
|
|
17
|
+
c.example 'doing select',
|
|
18
|
+
desc: 'Select from all entries. A menu of actions will be presented after confirming the selection.'
|
|
19
|
+
c.example 'doing select --editor',
|
|
20
|
+
desc: 'Select entries from a menu and batch edit them in your default editor'
|
|
21
|
+
c.example 'doing select --after "yesterday 12pm" --tag project1',
|
|
22
|
+
desc: 'Display a menu of entries created after noon yesterday, add @project1 to selected entries'
|
|
23
|
+
|
|
20
24
|
c.desc 'Select from a specific section'
|
|
21
25
|
c.arg_name 'SECTION'
|
|
22
26
|
c.flag %i[s section]
|
|
@@ -38,48 +42,21 @@ command :select do |c|
|
|
|
38
42
|
c.arg_name 'SECTION'
|
|
39
43
|
c.flag %i[m move]
|
|
40
44
|
|
|
41
|
-
c.desc 'Initial search query for filtering. Matching is fuzzy. For exact matching, start query with a single quote,
|
|
45
|
+
c.desc 'Initial search query for filtering. Matching is fuzzy. For exact matching, start query with a single quote,
|
|
46
|
+
e.g. `--query "\'search"'
|
|
42
47
|
c.arg_name 'QUERY'
|
|
43
48
|
c.flag %i[q query]
|
|
44
49
|
|
|
45
|
-
c.desc '
|
|
46
|
-
|
|
47
|
-
c.flag [:search]
|
|
48
|
-
|
|
49
|
-
c.desc 'Perform a tag value query ("@done > two hours ago" or "@progress < 50"). May be used multiple times, combined with --bool'
|
|
50
|
+
c.desc 'Perform a tag value query ("@done > two hours ago" or "@progress < 50").
|
|
51
|
+
May be used multiple times, combined with --bool'
|
|
50
52
|
c.arg_name 'QUERY'
|
|
51
53
|
c.flag [:val], multiple: true, must_match: REGEX_VALUE_QUERY
|
|
52
54
|
|
|
53
|
-
c.desc 'Select from entries older than date. If this is only a time (8am, 1:30pm, 15:00), all dates will be included, but entries will be filtered by time of day'
|
|
54
|
-
c.arg_name 'DATE_STRING'
|
|
55
|
-
c.flag [:before], type: DateBeginString
|
|
56
|
-
|
|
57
|
-
c.desc 'Select from entries newer than date. If this is only a time (8am, 1:30pm, 15:00), all dates will be included, but entries will be filtered by time of day'
|
|
58
|
-
c.arg_name 'DATE_STRING'
|
|
59
|
-
c.flag [:after], type: DateEndString
|
|
60
|
-
|
|
61
|
-
c.desc %(
|
|
62
|
-
Date range to show, or a single day to filter date on.
|
|
63
|
-
Date range argument should be quoted. Date specifications can be natural language.
|
|
64
|
-
To specify a range, use "to" or "through": `doing select --from "monday 8am to friday 5pm"`.
|
|
65
|
-
|
|
66
|
-
If values are only time(s) (6am to noon) all dates will be included, but entries will be filtered
|
|
67
|
-
by time of day.
|
|
68
|
-
)
|
|
69
|
-
c.arg_name 'DATE_OR_RANGE'
|
|
70
|
-
c.flag [:from], type: DateRangeString
|
|
71
|
-
|
|
72
|
-
c.desc 'Force exact search string matching (case sensitive)'
|
|
73
|
-
c.switch %i[x exact], default_value: @config.exact_match?, negatable: @config.exact_match?
|
|
74
|
-
|
|
75
55
|
c.desc 'Select items that *don\'t* match search/tag filters'
|
|
76
56
|
c.switch [:not], default_value: false, negatable: false
|
|
77
57
|
|
|
78
|
-
c.desc '
|
|
79
|
-
|
|
80
|
-
c.flag [:case], must_match: /^[csi]/, default_value: @settings.dig('search', 'case')
|
|
81
|
-
|
|
82
|
-
c.desc 'Use --no-menu to skip the interactive menu. Use with --query to filter items and act on results automatically. Test with `--output doing` to preview matches'
|
|
58
|
+
c.desc 'Use --no-menu to skip the interactive menu. Use with --query to filter items and act on results automatically.
|
|
59
|
+
Test with `--output doing` to preview matches'
|
|
83
60
|
c.switch %i[menu], negatable: true, default_value: true
|
|
84
61
|
|
|
85
62
|
c.desc 'Cancel selected items (add @done without timestamp)'
|
|
@@ -108,15 +85,20 @@ command :select do |c|
|
|
|
108
85
|
c.arg_name 'FORMAT'
|
|
109
86
|
c.flag %i[o output]
|
|
110
87
|
|
|
111
|
-
c.desc
|
|
88
|
+
c.desc 'Copy selection as a new entry with current time and no @done tag.
|
|
89
|
+
Only works with single selections. Can be combined with --editor.'
|
|
112
90
|
c.switch %i[again resume], negatable: false, default_value: false
|
|
113
91
|
|
|
114
|
-
|
|
115
|
-
|
|
92
|
+
add_options(:search, c)
|
|
93
|
+
add_options(:date_filter, c)
|
|
116
94
|
|
|
117
|
-
|
|
95
|
+
c.action do |_global_options, options, _args|
|
|
96
|
+
if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
|
|
97
|
+
raise DoingRuntimeError, %(Invalid output type "#{options[:output]}")
|
|
98
|
+
|
|
99
|
+
end
|
|
118
100
|
|
|
119
|
-
options[:
|
|
101
|
+
raise InvalidArgument, '--no-menu requires --query' if !options[:menu] && !options[:query]
|
|
120
102
|
|
|
121
103
|
@wwid.interactive(options) # hooked
|
|
122
104
|
end
|
data/bin/commands/show.rb
CHANGED
|
@@ -18,69 +18,20 @@ command :show do |c|
|
|
|
18
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
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
20
|
|
|
21
|
-
c.desc 'Tag filter, combine multiple tags with a comma. Use `--tag pick` for a menu of available tags. Wildcards allowed (*, ?). Added for compatibility with other commands'
|
|
22
|
-
c.arg_name 'TAG'
|
|
23
|
-
c.flag [:tag], type: TagArray
|
|
24
|
-
|
|
25
|
-
c.desc 'Perform a tag value query ("@done > two hours ago" or "@progress < 50"). May be used multiple times, combined with --bool'
|
|
26
|
-
c.arg_name 'QUERY'
|
|
27
|
-
c.flag [:val], multiple: true, must_match: REGEX_VALUE_QUERY
|
|
28
|
-
|
|
29
|
-
c.desc 'Tag boolean (AND,OR,NOT). Use PATTERN to parse + and - as booleans'
|
|
30
|
-
c.arg_name 'BOOLEAN'
|
|
31
|
-
c.flag %i[b bool], must_match: REGEX_BOOL, default_value: 'PATTERN'
|
|
32
|
-
|
|
33
21
|
c.desc 'Max count to show'
|
|
34
22
|
c.arg_name 'MAX'
|
|
35
23
|
c.flag %i[c count], default_value: 0, must_match: /^\d+$/, type: Integer
|
|
36
24
|
|
|
37
25
|
c.desc 'Age (oldest|newest)'
|
|
38
26
|
c.arg_name 'AGE'
|
|
39
|
-
c.flag %i[a age], default_value:
|
|
40
|
-
|
|
41
|
-
c.desc 'Show 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'
|
|
42
|
-
c.arg_name 'DATE_STRING'
|
|
43
|
-
c.flag [:before], type: DateBeginString
|
|
44
|
-
|
|
45
|
-
c.desc 'Show 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'
|
|
46
|
-
c.arg_name 'DATE_STRING'
|
|
47
|
-
c.flag [:after], type: DateEndString
|
|
48
|
-
|
|
49
|
-
c.desc %(
|
|
50
|
-
Date range to show, or a single day to filter date on.
|
|
51
|
-
Date range argument should be quoted. Date specifications can be natural language.
|
|
52
|
-
To specify a range, use "to" or "through": `doing show --from "monday 8am to friday 5pm"`.
|
|
53
|
-
|
|
54
|
-
If values are only time(s) (6am to noon) all dates will be included, but entries will be filtered
|
|
55
|
-
by time of day.
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
-
c.arg_name 'DATE_OR_RANGE'
|
|
59
|
-
c.flag [:from], type: DateRangeString
|
|
60
|
-
|
|
61
|
-
c.desc 'Search filter, surround with slashes for regex (/query/), start with single quote for exact match ("\'query")'
|
|
62
|
-
c.arg_name 'QUERY'
|
|
63
|
-
c.flag [:search]
|
|
27
|
+
c.flag %i[a age], default_value: :newest, type: AgeSymbol
|
|
64
28
|
|
|
65
29
|
c.desc "Highlight search matches in output. Only affects command line output"
|
|
66
30
|
c.switch %i[h hilite], default_value: @settings.dig('search', 'highlight')
|
|
67
31
|
|
|
68
|
-
# c.desc '[DEPRECATED] Use alternative fuzzy matching for search string'
|
|
69
|
-
# c.switch [:fuzzy], default_value: false, negatable: false
|
|
70
|
-
|
|
71
|
-
c.desc 'Force exact search string matching (case sensitive)'
|
|
72
|
-
c.switch %i[x exact], default_value: @config.exact_match?, negatable: @config.exact_match?
|
|
73
|
-
|
|
74
|
-
c.desc 'Show items that *don\'t* match search/tag/date filters'
|
|
75
|
-
c.switch [:not], default_value: false, negatable: false
|
|
76
|
-
|
|
77
|
-
c.desc 'Case sensitivity for search string matching [(c)ase-sensitive, (i)gnore, (s)mart]'
|
|
78
|
-
c.arg_name 'TYPE'
|
|
79
|
-
c.flag [:case], must_match: /^[csi]/, default_value: @settings.dig('search', 'case')
|
|
80
|
-
|
|
81
32
|
c.desc 'Sort order (asc/desc)'
|
|
82
33
|
c.arg_name 'ORDER'
|
|
83
|
-
c.flag %i[s sort], must_match: REGEX_SORT_ORDER, default_value:
|
|
34
|
+
c.flag %i[s sort], must_match: REGEX_SORT_ORDER, default_value: :asc, type: OrderSymbol
|
|
84
35
|
|
|
85
36
|
c.desc 'Show time intervals on @done tasks'
|
|
86
37
|
c.switch %i[t times], default_value: true, negatable: true
|
|
@@ -92,14 +43,13 @@ command :show do |c|
|
|
|
92
43
|
c.switch [:totals], default_value: false, negatable: false
|
|
93
44
|
|
|
94
45
|
c.desc 'Sort tags by (name|time)'
|
|
95
|
-
default = '
|
|
96
|
-
default = @settings['tag_sort'] || 'name'
|
|
46
|
+
default = @settings['tag_sort'].normalize_tag_sort || :name
|
|
97
47
|
c.arg_name 'KEY'
|
|
98
|
-
c.flag [:tag_sort], must_match:
|
|
48
|
+
c.flag [:tag_sort], must_match: REGEX_TAG_SORT, default_value: default, type: TagSortSymbol
|
|
99
49
|
|
|
100
50
|
c.desc 'Tag sort direction (asc|desc)'
|
|
101
51
|
c.arg_name 'DIRECTION'
|
|
102
|
-
c.flag [:tag_order], must_match: REGEX_SORT_ORDER, default_value:
|
|
52
|
+
c.flag [:tag_order], must_match: REGEX_SORT_ORDER, default_value: :asc, type: OrderSymbol
|
|
103
53
|
|
|
104
54
|
c.desc 'Only show items with recorded time intervals'
|
|
105
55
|
c.switch [:only_timed], default_value: false, negatable: false
|
|
@@ -121,9 +71,17 @@ command :show do |c|
|
|
|
121
71
|
c.desc "Output to export format (#{Doing::Plugins.plugin_names(type: :export)})"
|
|
122
72
|
c.arg_name 'FORMAT'
|
|
123
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
|
+
|
|
124
79
|
c.action do |global_options, options, args|
|
|
125
80
|
options[:fuzzy] = false
|
|
126
|
-
|
|
81
|
+
if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
|
|
82
|
+
raise DoingRuntimeError, %(Invalid output type "#{options[:output]}")
|
|
83
|
+
|
|
84
|
+
end
|
|
127
85
|
|
|
128
86
|
tag_filter = false
|
|
129
87
|
tags = []
|
|
@@ -142,7 +100,7 @@ command :show do |c|
|
|
|
142
100
|
else
|
|
143
101
|
begin
|
|
144
102
|
section = @wwid.guess_section(args[0])
|
|
145
|
-
rescue WrongCommand
|
|
103
|
+
rescue WrongCommand
|
|
146
104
|
cmd = commands[:view]
|
|
147
105
|
action = cmd.send(:get_action, nil)
|
|
148
106
|
return action.call(global_options, options, args)
|
|
@@ -155,8 +113,8 @@ command :show do |c|
|
|
|
155
113
|
if args.length.positive?
|
|
156
114
|
args.each do |arg|
|
|
157
115
|
arg.split(/,/).each do |tag|
|
|
158
|
-
|
|
159
|
-
|
|
116
|
+
tags.push(tag.strip.sub(/^@/, ''))
|
|
117
|
+
end
|
|
160
118
|
end
|
|
161
119
|
end
|
|
162
120
|
else
|
|
@@ -169,13 +127,11 @@ command :show do |c|
|
|
|
169
127
|
options[:times] = true if options[:totals]
|
|
170
128
|
|
|
171
129
|
template = @settings['templates'][options[:config_template]].deep_merge({
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
options[:case] = options[:case].normalize_case
|
|
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
|
+
})
|
|
179
135
|
|
|
180
136
|
if options[:search]
|
|
181
137
|
search = options[:search]
|
|
@@ -188,7 +144,7 @@ command :show do |c|
|
|
|
188
144
|
if tags.good?
|
|
189
145
|
tag_filter = {
|
|
190
146
|
'tags' => tags,
|
|
191
|
-
'bool' => options[:bool]
|
|
147
|
+
'bool' => options[:bool]
|
|
192
148
|
}
|
|
193
149
|
end
|
|
194
150
|
|
|
@@ -198,32 +154,30 @@ command :show do |c|
|
|
|
198
154
|
items = @wwid.filter_items([], opt: options)
|
|
199
155
|
|
|
200
156
|
if options[:menu]
|
|
157
|
+
Doing.logger.benchmark(:menu, :start)
|
|
201
158
|
tag = @wwid.choose_tag(section, items: items, include_all: true)
|
|
202
159
|
raise UserCancelled unless tag
|
|
203
160
|
|
|
204
|
-
# options[:bool] = :and unless tags.empty?
|
|
205
|
-
|
|
206
161
|
tags = tag.split(/ +/).map { |t| t.strip.sub(/^@?/, '') } if tag =~ /^@/
|
|
207
162
|
if tags.good?
|
|
208
163
|
tag_filter = {
|
|
209
164
|
'tags' => tags,
|
|
210
|
-
'bool' => options[:bool]
|
|
165
|
+
'bool' => options[:bool]
|
|
211
166
|
}
|
|
212
167
|
options[:tag_filter] = tag_filter
|
|
213
168
|
end
|
|
169
|
+
Doing.logger.benchmark(:menu, :finish)
|
|
214
170
|
end
|
|
215
171
|
|
|
216
172
|
options[:age] ||= :newest
|
|
217
173
|
|
|
218
174
|
opt = options.clone
|
|
219
|
-
opt[:
|
|
220
|
-
opt[:sort_tags] = options[:tag_sort] =~ /^n/i
|
|
175
|
+
opt[:sort_tags] = options[:tag_sort]
|
|
221
176
|
opt[:count] = options[:count].to_i
|
|
222
177
|
opt[:highlight] = true
|
|
223
178
|
opt[:hilite] = options[:hilite]
|
|
224
|
-
opt[:order] = options[:sort]
|
|
179
|
+
opt[:order] = options[:sort]
|
|
225
180
|
opt[:tag] = nil
|
|
226
|
-
opt[:tag_order] = options[:tag_order].normalize_order
|
|
227
181
|
opt[:tags_color] = template['tags_color']
|
|
228
182
|
|
|
229
183
|
Doing::Pager.page @wwid.list_section(opt, items: items)
|
data/bin/commands/since.rb
CHANGED
|
@@ -21,10 +21,9 @@ command :since do |c|
|
|
|
21
21
|
c.switch [:totals], default_value: false, negatable: false
|
|
22
22
|
|
|
23
23
|
c.desc 'Sort tags by (name|time)'
|
|
24
|
-
default = '
|
|
25
|
-
default = @settings['tag_sort'] || 'name'
|
|
24
|
+
default = @settings['tag_sort'].normalize_tag_sort || :name
|
|
26
25
|
c.arg_name 'KEY'
|
|
27
|
-
c.flag [:tag_sort], must_match:
|
|
26
|
+
c.flag [:tag_sort], must_match: REGEX_TAG_SORT, default_value: default, type: TagSortSymbol
|
|
28
27
|
|
|
29
28
|
c.desc "Output to export format (#{Doing::Plugins.plugin_names(type: :export)})"
|
|
30
29
|
c.arg_name 'FORMAT'
|
|
@@ -56,7 +55,7 @@ command :since do |c|
|
|
|
56
55
|
Doing.logger.debug('Interpreter:', "date interpreted as #{start} through the current time")
|
|
57
56
|
|
|
58
57
|
options[:times] = true if options[:totals]
|
|
59
|
-
options[:sort_tags] = options[:tag_sort]
|
|
58
|
+
options[:sort_tags] = options[:tag_sort]
|
|
60
59
|
|
|
61
60
|
Doing::Pager.page @wwid.list_date([start, finish], options[:section], options[:times], options[:output],
|
|
62
61
|
{ template: options[:template], config_template: options[:config_template], duration: options[:duration], totals: options[:totals], sort_tags: options[:sort_tags] }).chomp
|
data/bin/commands/tag.rb
CHANGED
|
@@ -55,39 +55,12 @@ command :tag do |c|
|
|
|
55
55
|
c.desc 'Autotag entries based on autotag configuration in ~/.config/doing/config.yml'
|
|
56
56
|
c.switch %i[a autotag], negatable: false, default_value: false
|
|
57
57
|
|
|
58
|
-
c.desc 'Tag the last X entries containing TAG.
|
|
59
|
-
Separate multiple tags with comma (--tag=tag1,tag2), combine with --bool. Wildcards allowed (*, ?).'
|
|
60
|
-
c.arg_name 'TAG'
|
|
61
|
-
c.flag [:tag], type: TagArray
|
|
62
|
-
|
|
63
|
-
c.desc 'Tag entries matching search filter, surround with slashes for regex (e.g. "/query.*/"), start with single quote for exact match ("\'query")'
|
|
64
|
-
c.arg_name 'QUERY'
|
|
65
|
-
c.flag [:search]
|
|
66
|
-
|
|
67
|
-
c.desc 'Perform a tag value query ("@done > two hours ago" or "@progress < 50"). May be used multiple times, combined with --bool'
|
|
68
|
-
c.arg_name 'QUERY'
|
|
69
|
-
c.flag [:val], multiple: true, must_match: REGEX_VALUE_QUERY
|
|
70
|
-
|
|
71
|
-
# c.desc '[DEPRECATED] Use alternative fuzzy matching for search string'
|
|
72
|
-
# c.switch [:fuzzy], default_value: false, negatable: false
|
|
73
|
-
|
|
74
|
-
c.desc 'Force exact search string matching (case sensitive)'
|
|
75
|
-
c.switch %i[x exact], default_value: @config.exact_match?, negatable: @config.exact_match?
|
|
76
|
-
|
|
77
|
-
c.desc 'Tag items that *don\'t* match search/tag filters'
|
|
78
|
-
c.switch [:not], default_value: false, negatable: false
|
|
79
|
-
|
|
80
|
-
c.desc 'Case sensitivity for search string matching [(c)ase-sensitive, (i)gnore, (s)mart]'
|
|
81
|
-
c.arg_name 'TYPE'
|
|
82
|
-
c.flag [:case], must_match: /^[csi]/, default_value: @settings.dig('search', 'case')
|
|
83
|
-
|
|
84
|
-
c.desc 'Boolean (AND|OR|NOT) with which to combine multiple tag filters. Use PATTERN to parse + and - as booleans'
|
|
85
|
-
c.arg_name 'BOOLEAN'
|
|
86
|
-
c.flag [:bool], must_match: REGEX_BOOL, default_value: 'PATTERN'
|
|
87
|
-
|
|
88
58
|
c.desc 'Select item(s) to tag from a menu of matching entries'
|
|
89
59
|
c.switch %i[i interactive], negatable: false, default_value: false
|
|
90
60
|
|
|
61
|
+
add_options(:search, c)
|
|
62
|
+
add_options(:tag_filter, c)
|
|
63
|
+
|
|
91
64
|
c.action do |_global_options, options, args|
|
|
92
65
|
options[:fuzzy] = false
|
|
93
66
|
# raise MissingArgument, 'You must specify at least one tag' if args.empty? && !options[:autotag]
|
|
@@ -130,9 +103,6 @@ command :tag do |c|
|
|
|
130
103
|
count = options[:count].to_i
|
|
131
104
|
end
|
|
132
105
|
|
|
133
|
-
options[:case] ||= :smart
|
|
134
|
-
options[:case] = options[:case].normalize_case
|
|
135
|
-
|
|
136
106
|
if options[:search]
|
|
137
107
|
search = options[:search]
|
|
138
108
|
search.sub!(/^'?/, "'") if options[:exact]
|
|
@@ -143,7 +113,7 @@ command :tag do |c|
|
|
|
143
113
|
options[:section] = section
|
|
144
114
|
options[:tag] = search_tags
|
|
145
115
|
options[:tags] = tags
|
|
146
|
-
options[:tag_bool] = options[:bool]
|
|
116
|
+
options[:tag_bool] = options[:bool]
|
|
147
117
|
|
|
148
118
|
if count.zero? && !options[:force]
|
|
149
119
|
matches = @wwid.filter_items([], opt: options).count
|