doing 2.1.41 → 2.1.44
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/CHANGELOG.md +67 -0
- data/Gemfile.lock +1 -1
- data/README.md +1 -1
- data/bin/commands/again.rb +1 -3
- data/bin/commands/changes.rb +60 -34
- data/bin/commands/commands.rb +77 -52
- data/bin/commands/commands_accepting.rb +57 -53
- data/bin/commands/config.rb +2 -2
- data/bin/commands/finish.rb +94 -68
- data/bin/commands/flag.rb +5 -1
- data/bin/commands/grep.rb +12 -2
- data/bin/commands/last.rb +2 -0
- data/bin/commands/now.rb +151 -107
- data/bin/commands/on.rb +20 -5
- data/bin/commands/recent.rb +4 -1
- data/bin/commands/show.rb +8 -0
- data/bin/commands/since.rb +6 -2
- data/bin/commands/template.rb +14 -25
- data/bin/commands/today.rb +4 -1
- data/bin/commands/undo.rb +4 -6
- data/bin/commands/view.rb +36 -73
- data/bin/commands/views.rb +102 -5
- data/bin/commands/yesterday.rb +3 -1
- data/bin/doing +31 -4
- data/docs/doc/Array.html +14 -3
- 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/ArrayCleanup.html +316 -0
- data/docs/doc/Doing/ArrayNestedHash.html +1 -1
- data/docs/doc/Doing/ArrayTags.html +1 -1
- data/docs/doc/Doing/CSVExport.html +1 -1
- data/docs/doc/Doing/CalendarImport.html +1 -1
- data/docs/doc/Doing/Change.html +74 -3
- data/docs/doc/Doing/Changes.html +3 -3
- data/docs/doc/Doing/ChronifyArray.html +1 -1
- data/docs/doc/Doing/ChronifyNumeric.html +1 -1
- data/docs/doc/Doing/ChronifyString.html +1 -1
- data/docs/doc/Doing/Color.html +1 -1
- data/docs/doc/Doing/Completion/BashCompletions.html +1 -1
- data/docs/doc/Doing/Completion/FishCompletions.html +1 -1
- data/docs/doc/Doing/Completion/StringUtils.html +1 -1
- data/docs/doc/Doing/Completion/ZshCompletions.html +1 -1
- data/docs/doc/Doing/Completion.html +1 -1
- data/docs/doc/Doing/Configuration.html +82 -2
- data/docs/doc/Doing/DayOneRenderer.html +1 -1
- data/docs/doc/Doing/DayoneExport.html +1 -1
- data/docs/doc/Doing/DoingImport.html +1 -1
- data/docs/doc/Doing/Entry.html +109 -4
- 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/HistoryLimitError.html +1 -1
- data/docs/doc/Doing/Errors/InvalidPlugin.html +1 -1
- data/docs/doc/Doing/Errors/MissingBackupFile.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/HTMLExport.html +1 -1
- data/docs/doc/Doing/Hooks.html +1 -1
- data/docs/doc/Doing/Item.html +1 -1
- data/docs/doc/Doing/ItemDates.html +1 -1
- data/docs/doc/Doing/ItemQuery.html +1 -1
- data/docs/doc/Doing/ItemState.html +1 -1
- data/docs/doc/Doing/ItemTags.html +1 -1
- data/docs/doc/Doing/Items.html +2 -1
- data/docs/doc/Doing/JSONExport.html +1 -1
- data/docs/doc/Doing/Logger.html +1 -1
- data/docs/doc/Doing/MarkdownExport.html +1 -1
- data/docs/doc/Doing/Note.html +3 -2
- data/docs/doc/Doing/Pager.html +1 -1
- data/docs/doc/Doing/Plugins.html +181 -76
- data/docs/doc/Doing/Prompt.html +1 -1
- data/docs/doc/Doing/PromptChoose.html +1 -1
- data/docs/doc/Doing/PromptFZF.html +1 -1
- data/docs/doc/Doing/PromptInput.html +1 -1
- data/docs/doc/Doing/PromptSTD.html +1 -1
- data/docs/doc/Doing/PromptYN.html +1 -1
- data/docs/doc/Doing/Section.html +1 -1
- data/docs/doc/Doing/StringHighlight.html +1 -1
- data/docs/doc/Doing/StringNormalize.html +35 -1
- data/docs/doc/Doing/StringQuery.html +1 -1
- data/docs/doc/Doing/StringTags.html +1 -1
- data/docs/doc/Doing/StringTransform.html +35 -1
- data/docs/doc/Doing/StringTruncate.html +1 -1
- data/docs/doc/Doing/StringURL.html +1 -1
- data/docs/doc/Doing/SymbolNormalize.html +1 -1
- data/docs/doc/Doing/TaskPaperExport.html +1 -1
- data/docs/doc/Doing/TemplateExport.html +1 -1
- data/docs/doc/Doing/TemplateString.html +3 -3
- data/docs/doc/Doing/TimingImport.html +1 -1
- data/docs/doc/Doing/Types.html +23 -18
- data/docs/doc/Doing/Util/Backup.html +2 -156
- data/docs/doc/Doing/Util.html +66 -9
- data/docs/doc/Doing/Version.html +1 -1
- data/docs/doc/Doing/WWID.html +84 -3
- data/docs/doc/Doing.html +4 -4
- data/docs/doc/FalseClass.html +11 -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 +461 -6
- data/docs/doc/Numeric.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 +5 -5
- data/docs/doc/Symbol.html +1 -1
- data/docs/doc/Time.html +68 -3
- data/docs/doc/TrueClass.html +11 -1
- data/docs/doc/_index.html +16 -9
- 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 +529 -417
- data/docs/doc/top-level-namespace.html +11 -1
- data/doing.rdoc +169 -13
- data/lib/completion/_doing.zsh +13 -13
- data/lib/completion/doing.bash +22 -22
- data/lib/completion/doing.fish +24 -1
- data/lib/doing/add_options.rb +48 -1
- data/lib/doing/array/array.rb +2 -0
- data/lib/doing/array/cleanup.rb +31 -0
- data/lib/doing/changelog/change.rb +13 -5
- data/lib/doing/changelog/changes.rb +11 -2
- data/lib/doing/changelog/entry.rb +9 -2
- data/lib/doing/configuration.rb +28 -3
- data/lib/doing/good.rb +18 -1
- data/lib/doing/hash.rb +126 -22
- data/lib/doing/normalize.rb +13 -0
- data/lib/doing/note.rb +1 -1
- data/lib/doing/pager.rb +9 -3
- data/lib/doing/plugin_manager.rb +30 -5
- data/lib/doing/prompt/choose.rb +1 -1
- data/lib/doing/prompt/input.rb +1 -1
- data/lib/doing/string/transform.rb +6 -0
- data/lib/doing/types.rb +9 -8
- data/lib/doing/util.rb +12 -6
- data/lib/doing/util_backup.rb +55 -48
- data/lib/doing/version.rb +1 -1
- data/lib/doing/wwid/display.rb +4 -1
- data/lib/doing/wwid/editor.rb +6 -3
- data/lib/doing/wwid/interactive.rb +10 -20
- data/lib/doing/wwid/modify.rb +2 -0
- data/lib/doing/wwid/wwid.rb +21 -3
- data/lib/doing.rb +12 -3
- metadata +4 -2
data/bin/commands/finish.rb
CHANGED
|
@@ -1,43 +1,97 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
# @@finish
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
c.example 'doing finish --search "a specific entry" --at "yesterday 3pm"', desc: 'Search for an entry containing string and set its @done time to yesterday at 3pm'
|
|
4
|
+
module Doing
|
|
5
|
+
# finish command methods
|
|
6
|
+
class FinishCommand
|
|
7
|
+
def initialize(wwid)
|
|
8
|
+
@wwid = wwid
|
|
9
|
+
end
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
def add_examples(cmd)
|
|
12
|
+
cmd.example 'doing finish', desc: 'Mark the last entry @done'
|
|
13
|
+
cmd.example 'doing finish --auto --section Later 10', desc: 'Add @done to any unfinished entries in the last 10 in Later, setting the finish time based on the start time of the task after it'
|
|
14
|
+
cmd.example 'doing finish --search "a specific entry" --at "yesterday 3pm"', desc: 'Search for an entry containing string and set its @done time to yesterday at 3pm'
|
|
15
|
+
end
|
|
12
16
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
17
|
+
def add_options(cmd)
|
|
18
|
+
cmd.desc 'Include date'
|
|
19
|
+
cmd.switch [:date], negatable: true, default_value: true
|
|
16
20
|
|
|
17
|
-
|
|
18
|
-
|
|
21
|
+
cmd.desc 'Backdate completed date to date string [4pm|20m|2h|yesterday noon]'
|
|
22
|
+
cmd.arg_name 'DATE_STRING'
|
|
23
|
+
cmd.flag %i[b back started], type: DateBeginString
|
|
19
24
|
|
|
20
|
-
|
|
21
|
-
|
|
25
|
+
cmd.desc 'Overwrite existing @done tag with new date'
|
|
26
|
+
cmd.switch %i[update], negatable: false, default_value: false
|
|
22
27
|
|
|
23
|
-
|
|
24
|
-
|
|
28
|
+
cmd.desc 'Remove @done tag'
|
|
29
|
+
cmd.switch %i[r remove], negatable: false, default_value: false
|
|
25
30
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
--auto overrides the --date and --back parameters.)
|
|
29
|
-
c.switch [:auto], negatable: false, default_value: false
|
|
31
|
+
cmd.desc 'Finish last entry (or entries) not already marked @done'
|
|
32
|
+
cmd.switch %i[u unfinished], negatable: false, default_value: false
|
|
30
33
|
|
|
31
|
-
|
|
32
|
-
|
|
34
|
+
cmd.desc %(Auto-generate finish dates from next entry's start time.
|
|
35
|
+
Automatically generate completion dates 1 minute before next item (in any section) began.
|
|
36
|
+
--auto overrides the --date and --back parameters.)
|
|
37
|
+
cmd.switch [:auto], negatable: false, default_value: false
|
|
33
38
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
c.flag %i[s section]
|
|
39
|
+
cmd.desc 'Archive entries'
|
|
40
|
+
cmd.switch %i[a archive], negatable: false, default_value: false
|
|
37
41
|
|
|
38
|
-
|
|
39
|
-
|
|
42
|
+
cmd.desc 'Section'
|
|
43
|
+
cmd.arg_name 'NAME'
|
|
44
|
+
cmd.flag %i[s section]
|
|
40
45
|
|
|
46
|
+
cmd.desc 'Select item(s) to finish from a menu of matching entries'
|
|
47
|
+
cmd.switch %i[i interactive], negatable: false, default_value: false
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def handle_from(options)
|
|
51
|
+
options[:from] = options[:from].split(/#{REGEX_RANGE_INDICATOR}/).map do |time|
|
|
52
|
+
time =~ REGEX_TIME ? "today #{time.sub(/(?mi)(^.*?(?=\d+)|(?<=[ap]m).*?$)/, '')}" : time
|
|
53
|
+
end.join(' to ').split_date_range
|
|
54
|
+
start_date, finish_date = options[:from]
|
|
55
|
+
finish_date ||= Time.now
|
|
56
|
+
[start_date, finish_date]
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def handle_date_options(options)
|
|
60
|
+
if options[:took]
|
|
61
|
+
took = options[:took]
|
|
62
|
+
raise InvalidTimeExpression, 'Unable to parse date string for --took' if took.nil?
|
|
63
|
+
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
if options[:at]
|
|
67
|
+
finish_date = options[:at]
|
|
68
|
+
finish_date = finish_date.chronify(guess: :begin) if finish_date.is_a? String
|
|
69
|
+
raise InvalidTimeExpression, 'Unable to parse date string for --at' if finish_date.nil?
|
|
70
|
+
|
|
71
|
+
start_date = options[:took] ? finish_date - took : nil
|
|
72
|
+
elsif options[:back]
|
|
73
|
+
start_date = options[:back]
|
|
74
|
+
finish_date = options[:took] ? start_date + took : Time.now
|
|
75
|
+
|
|
76
|
+
raise InvalidTimeExpression, 'Unable to parse date string' if start_date.nil?
|
|
77
|
+
|
|
78
|
+
else
|
|
79
|
+
start_date = options[:took] ? Time.now - took : nil
|
|
80
|
+
finish_date = Time.now
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
[start_date, finish_date]
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
desc 'Mark last X entries as @done'
|
|
89
|
+
long_desc 'Marks the last X entries with a @done tag and current date. Does not alter already completed entries.'
|
|
90
|
+
arg_name 'COUNT', optional: true
|
|
91
|
+
command :finish do |c|
|
|
92
|
+
cmd = Doing::FinishCommand.new(@wwid)
|
|
93
|
+
cmd.add_examples(c)
|
|
94
|
+
cmd.add_options(c)
|
|
41
95
|
add_options(:search, c)
|
|
42
96
|
add_options(:tag_filter, c)
|
|
43
97
|
add_options(:finish_entry, c)
|
|
@@ -46,55 +100,27 @@ command :finish do |c|
|
|
|
46
100
|
options[:fuzzy] = false
|
|
47
101
|
unless options[:auto]
|
|
48
102
|
if options[:from]
|
|
49
|
-
|
|
50
|
-
time =~ REGEX_TIME ? "today #{time.sub(/(?mi)(^.*?(?=\d+)|(?<=[ap]m).*?$)/, '')}" : time
|
|
51
|
-
end.join(' to ').split_date_range
|
|
52
|
-
start_date, finish_date = options[:from]
|
|
53
|
-
finish_date ||= Time.now
|
|
103
|
+
start_date, finish_date = cmd.handle_from(options)
|
|
54
104
|
else
|
|
55
|
-
|
|
56
|
-
took = options[:took]
|
|
57
|
-
raise InvalidTimeExpression, 'Unable to parse date string for --took' if took.nil?
|
|
58
|
-
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
if options[:at]
|
|
62
|
-
finish_date = options[:at]
|
|
63
|
-
finish_date = finish_date.chronify(guess: :begin) if finish_date.is_a? String
|
|
64
|
-
raise InvalidTimeExpression, 'Unable to parse date string for --at' if finish_date.nil?
|
|
65
|
-
|
|
66
|
-
start_date = options[:took] ? finish_date - took : nil
|
|
67
|
-
elsif options[:back]
|
|
68
|
-
start_date = options[:back]
|
|
69
|
-
finish_date = options[:took] ? start_date + took : Time.now
|
|
70
|
-
|
|
71
|
-
raise InvalidTimeExpression, 'Unable to parse date string' if start_date.nil?
|
|
72
|
-
|
|
73
|
-
else
|
|
74
|
-
start_date = options[:took] ? Time.now - took : nil
|
|
75
|
-
finish_date = Time.now
|
|
76
|
-
end
|
|
105
|
+
start_date, finish_date = cmd.handle_date_options(options)
|
|
77
106
|
end
|
|
78
|
-
|
|
79
|
-
|
|
80
107
|
end
|
|
81
108
|
|
|
82
|
-
|
|
83
|
-
tags = []
|
|
84
|
-
else
|
|
85
|
-
tags = options[:tag]
|
|
86
|
-
end
|
|
109
|
+
tags = options[:tag] || []
|
|
87
110
|
|
|
88
111
|
raise InvalidArgument, 'Only one argument allowed' if args.length > 1
|
|
89
112
|
|
|
90
|
-
|
|
113
|
+
unless args.empty? || args[0] =~ /\d+/
|
|
114
|
+
raise InvalidArgument, 'Invalid argument (specify number of recent items to mark @done)'
|
|
91
115
|
|
|
92
|
-
if options[:interactive]
|
|
93
|
-
count = 0
|
|
94
|
-
else
|
|
95
|
-
count = args[0] ? args[0].to_i : 1
|
|
96
116
|
end
|
|
97
117
|
|
|
118
|
+
count = if options[:interactive]
|
|
119
|
+
0
|
|
120
|
+
else
|
|
121
|
+
args[0] ? args[0].to_i : 1
|
|
122
|
+
end
|
|
123
|
+
|
|
98
124
|
search = nil
|
|
99
125
|
|
|
100
126
|
if options[:search]
|
data/bin/commands/flag.rb
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
# @@mark @@flag
|
|
2
4
|
desc 'Mark last entry as flagged'
|
|
3
5
|
command %i[mark flag] do |c|
|
|
4
6
|
c.example 'doing flag', desc: 'Add @flagged to the last entry created'
|
|
5
7
|
c.example 'doing mark', desc: 'mark is an alias for flag'
|
|
6
8
|
c.example 'doing flag --tag project1 --count 2', desc: 'Add @flagged to the last 2 entries tagged @project1'
|
|
7
|
-
c.example 'doing flag --interactive --search "/(develop|cod)ing/"',
|
|
9
|
+
c.example 'doing flag --interactive --search "/(develop|cod)ing/"',
|
|
10
|
+
desc: 'Find entries matching regular expression and create a menu allowing multiple selections,
|
|
11
|
+
selected items will be @flagged'
|
|
8
12
|
|
|
9
13
|
c.desc 'Section'
|
|
10
14
|
c.arg_name 'SECTION_NAME'
|
data/bin/commands/grep.rb
CHANGED
|
@@ -25,7 +25,7 @@ command %i[grep search] do |c|
|
|
|
25
25
|
c.desc 'Case sensitivity for search string matching [(c)ase-sensitive, (i)gnore, (s)mart]'
|
|
26
26
|
c.arg_name 'TYPE'
|
|
27
27
|
c.flag [:case], must_match: REGEX_CASE,
|
|
28
|
-
default_value: Doing.
|
|
28
|
+
default_value: Doing.setting('search.case', :smart).normalize_case,
|
|
29
29
|
type: CaseSymbol
|
|
30
30
|
|
|
31
31
|
c.desc "Highlight search matches in output. Only affects command line output"
|
|
@@ -44,11 +44,15 @@ command %i[grep search] do |c|
|
|
|
44
44
|
add_options(:tag_filter, c)
|
|
45
45
|
add_options(:date_filter, c)
|
|
46
46
|
add_options(:time_display, c)
|
|
47
|
+
add_options(:save, c)
|
|
47
48
|
|
|
48
49
|
c.action do |_global_options, options, args|
|
|
49
50
|
options[:fuzzy] = false
|
|
50
51
|
|
|
51
|
-
|
|
52
|
+
if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
|
|
53
|
+
raise InvalidPlugin.new('output', options[:output])
|
|
54
|
+
|
|
55
|
+
end
|
|
52
56
|
|
|
53
57
|
template = Doing.setting(['templates', options[:config_template]]).deep_merge(Doing.settings)
|
|
54
58
|
tags_color = template.key?('tags_color') ? template['tags_color'] : nil
|
|
@@ -66,5 +70,11 @@ command %i[grep search] do |c|
|
|
|
66
70
|
options[:tags_color] = tags_color
|
|
67
71
|
|
|
68
72
|
Doing::Pager.page @wwid.list_section(options)
|
|
73
|
+
if options[:save]
|
|
74
|
+
options[:before] = Doing.original_options[:date_begin] if Doing.original_options[:date_begin].good?
|
|
75
|
+
options[:after] = Doing.original_options[:date_end] if Doing.original_options[:date_end].good?
|
|
76
|
+
options[:from] = Doing.original_options[:date_range] if Doing.original_options[:date_range].good?
|
|
77
|
+
Doing.config.save_view(options.to_view, options[:save].downcase)
|
|
78
|
+
end
|
|
69
79
|
end
|
|
70
80
|
end
|
data/bin/commands/last.rb
CHANGED
|
@@ -30,6 +30,7 @@ command :last do |c|
|
|
|
30
30
|
add_options(:output_template, c, default_template: 'last')
|
|
31
31
|
add_options(:search, c)
|
|
32
32
|
add_options(:tag_filter, c)
|
|
33
|
+
add_options(:save, c)
|
|
33
34
|
|
|
34
35
|
c.action do |global_options, options, _args|
|
|
35
36
|
options[:fuzzy] = false
|
|
@@ -70,6 +71,7 @@ command :last do |c|
|
|
|
70
71
|
val: options[:val]
|
|
71
72
|
})
|
|
72
73
|
Doing::Pager::page last.strip if last
|
|
74
|
+
Doing.config.save_view(options.to_view, options[:save].downcase) if options[:save]
|
|
73
75
|
end
|
|
74
76
|
end
|
|
75
77
|
end
|
data/bin/commands/now.rb
CHANGED
|
@@ -1,141 +1,185 @@
|
|
|
1
1
|
# @@now @@next
|
|
2
|
-
desc 'Add an entry'
|
|
3
|
-
long_desc %(Record what you're starting now, or backdate the start time using natural language.
|
|
4
|
-
|
|
5
|
-
A parenthetical at the end of the entry will be converted to a note.
|
|
6
2
|
|
|
7
|
-
|
|
3
|
+
module Doing
|
|
4
|
+
# Methods for the now command
|
|
5
|
+
class NowCommand
|
|
6
|
+
def initialize(wwid)
|
|
7
|
+
@wwid = wwid
|
|
8
|
+
end
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
c.example 'doing now -e', desc: "Open #{Doing::Util.default_editor} to input an entry and optional note"
|
|
14
|
-
c.example 'doing now working on a new project', desc: 'Add a new entry at the current time'
|
|
15
|
-
c.example 'doing now debugging @project2', desc: 'Add an entry with a tag'
|
|
16
|
-
c.example 'doing now adding an entry (with a note)', desc: 'Parenthetical at end is converted to note'
|
|
17
|
-
c.example 'doing now --back 2pm A thing I started at 2:00 and am still doing...', desc: 'Backdate an entry'
|
|
18
|
-
|
|
19
|
-
c.desc 'Section'
|
|
20
|
-
c.arg_name 'NAME'
|
|
21
|
-
c.flag %i[s section]
|
|
22
|
-
|
|
23
|
-
c.desc %(
|
|
24
|
-
Set a start and optionally end time as a date range ("from 1pm to 2:30pm").
|
|
25
|
-
If an end time is provided, a dated @done tag will be added
|
|
26
|
-
)
|
|
27
|
-
c.arg_name 'TIME_RANGE'
|
|
28
|
-
c.flag [:from], type: DateRangeString
|
|
29
|
-
|
|
30
|
-
c.desc 'Timed entry, marks last entry in section as @done'
|
|
31
|
-
c.switch %i[f finish_last], negatable: false, default_value: false
|
|
10
|
+
def setup(cmd)
|
|
11
|
+
add_examples(cmd)
|
|
12
|
+
add_options(cmd)
|
|
13
|
+
end
|
|
32
14
|
|
|
33
|
-
|
|
15
|
+
def process_now_options(options)
|
|
16
|
+
raise InvalidArgument, '--back and --from cannot be used together' if options[:back] && options[:from]
|
|
34
17
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
18
|
+
if options[:back]
|
|
19
|
+
options[:date] = options[:back]
|
|
20
|
+
elsif options[:from]
|
|
21
|
+
options[:date], finish_date = options[:from]
|
|
22
|
+
options[:done] = finish_date
|
|
23
|
+
else
|
|
24
|
+
options[:date] = Time.now
|
|
25
|
+
end
|
|
26
|
+
raise InvalidTimeExpression.new('unable to parse date string', topic: 'Parser:') if options[:date].nil?
|
|
38
27
|
|
|
39
|
-
|
|
40
|
-
|
|
28
|
+
options[:section] = if options[:section]
|
|
29
|
+
@wwid.guess_section(options[:section]) || options[:section].cap_first
|
|
30
|
+
else
|
|
31
|
+
Doing.setting('current_section')
|
|
32
|
+
end
|
|
41
33
|
|
|
42
|
-
|
|
34
|
+
options[:ask_note] = if options[:ask] && !options[:editor] && options[:has_args]
|
|
35
|
+
Doing::Prompt.read_lines(prompt: 'Add a note')
|
|
36
|
+
else
|
|
37
|
+
''
|
|
38
|
+
end
|
|
43
39
|
|
|
44
|
-
|
|
45
|
-
date = options[:back]
|
|
46
|
-
elsif options[:from]
|
|
47
|
-
date, finish_date = options[:from]
|
|
48
|
-
options[:done] = finish_date
|
|
49
|
-
else
|
|
50
|
-
date = Time.now
|
|
40
|
+
options
|
|
51
41
|
end
|
|
52
|
-
raise InvalidTimeExpression.new('unable to parse date string', topic: 'Parser:') if date.nil?
|
|
53
|
-
|
|
54
|
-
section = if options[:section]
|
|
55
|
-
@wwid.guess_section(options[:section]) || options[:section].cap_first
|
|
56
|
-
else
|
|
57
|
-
Doing.setting('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
|
|
65
42
|
|
|
66
|
-
|
|
43
|
+
def now_with_editor(options, args)
|
|
67
44
|
raise MissingEditor, 'No EDITOR variable defined in environment' if Doing::Util.default_editor.nil?
|
|
68
45
|
|
|
69
|
-
input = date.strftime('%F %R | ')
|
|
46
|
+
input = options[:date].strftime('%F %R | ')
|
|
70
47
|
input += args.join(' ') unless args.empty?
|
|
71
48
|
input += " @done(#{options[:done].strftime('%F %R')})" if options[:done]
|
|
72
49
|
input += "\n#{options[:note]}" if options[:note]
|
|
73
|
-
input += "\n#{ask_note}" if ask_note.good?
|
|
50
|
+
input += "\n#{options[:ask_note]}" if options[:ask_note].good?
|
|
74
51
|
input = @wwid.fork_editor(input).strip
|
|
75
52
|
|
|
76
53
|
d, title, note = @wwid.format_input(input)
|
|
77
54
|
raise EmptyInput, 'No content' unless title.good?
|
|
78
55
|
|
|
79
|
-
|
|
80
|
-
ask_note = Doing::Prompt.read_lines(prompt: 'Add a note')
|
|
81
|
-
note.add(ask_note) if ask_note.good?
|
|
82
|
-
end
|
|
56
|
+
note = ask_note(options, note, prompt: false)
|
|
83
57
|
|
|
84
|
-
date = d.nil? ? date : d
|
|
85
|
-
|
|
86
|
-
|
|
58
|
+
options[:date] = d.nil? ? options[:date] : d
|
|
59
|
+
opts = { note: note, back: options[:date], timed: options[:finish_last] }
|
|
60
|
+
@wwid.add_item(title.cap_first, options[:section], opts)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def now_with_args(options, args)
|
|
87
64
|
d, title, note = @wwid.format_input(args.join(' '))
|
|
88
|
-
date = d.nil? ? date : d
|
|
89
|
-
|
|
90
|
-
note
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
65
|
+
options[:date] = d.nil? ? options[:date] : d
|
|
66
|
+
|
|
67
|
+
note = ask_note(options, note, prompt: false)
|
|
68
|
+
|
|
69
|
+
opts = { note: note, back: options[:date], timed: options[:finish_last] }
|
|
70
|
+
entry = @wwid.add_item(title.cap_first, options[:section], opts)
|
|
71
|
+
return unless options[:done] && entry.should_finish?
|
|
72
|
+
|
|
73
|
+
entry.should_time? ? entry.tag('done', value: options[:done]) : entry.tag('done')
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def now_with_stdin(global_options, options, _args)
|
|
77
|
+
d, title, note = @wwid.format_input(global_options[:stdin])
|
|
78
|
+
|
|
102
79
|
unless d.nil?
|
|
103
80
|
Doing.logger.debug('Parser:', 'Date detected in input, overriding command line values')
|
|
104
|
-
date = d
|
|
105
|
-
end
|
|
106
|
-
note.add(options[:note]) if options[:note]
|
|
107
|
-
if ask_note.empty? && options[:ask]
|
|
108
|
-
ask_note = Doing::Prompt.read_lines(prompt: 'Add a note')
|
|
109
|
-
note.add(ask_note) if ask_note.good?
|
|
110
|
-
end
|
|
111
|
-
entry = @wwid.add_item(title.cap_first, section, { note: note, back: date, timed: options[:finish_last] })
|
|
112
|
-
if options[:done] && entry.should_finish?
|
|
113
|
-
if entry.should_time?
|
|
114
|
-
entry.tag('done', value: options[:done])
|
|
115
|
-
else
|
|
116
|
-
entry.tag('done')
|
|
117
|
-
end
|
|
81
|
+
options[:date] = d
|
|
118
82
|
end
|
|
119
|
-
|
|
83
|
+
|
|
84
|
+
note = ask_note(options, note, prompt: false)
|
|
85
|
+
|
|
86
|
+
opts = { note: note, back: options[:date], timed: options[:finish_last] }
|
|
87
|
+
entry = @wwid.add_item(title.cap_first, options[:section], opts)
|
|
88
|
+
return unless options[:done] && entry.should_finish?
|
|
89
|
+
|
|
90
|
+
entry.should_time? ? entry.tag('done', value: options[:done]) : entry.tag('done')
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def interactive_now(options, _args)
|
|
120
94
|
tags = @wwid.all_tags(@wwid.content)
|
|
121
|
-
|
|
95
|
+
puts Doing::Color.boldgreen('Add a new entry. Tab will autocomplete known tags. Ctrl-c to cancel.')
|
|
122
96
|
title = Doing::Prompt.read_line(prompt: 'Entry content', completions: tags)
|
|
123
97
|
raise EmptyInput, 'You must provide content when creating a new entry' unless title.good?
|
|
124
98
|
|
|
125
|
-
note =
|
|
99
|
+
note = ask_note(options, prompt: true)
|
|
100
|
+
|
|
101
|
+
opts = { note: note, back: options[:date], timed: options[:finish_last] }
|
|
102
|
+
entry = @wwid.add_item(title.cap_first, options[:section], opts)
|
|
103
|
+
return unless options[:done] && entry.should_finish?
|
|
104
|
+
|
|
105
|
+
if entry.should_time?
|
|
106
|
+
entry.tag('done', value: options[:done])
|
|
107
|
+
else
|
|
108
|
+
entry.tag('done')
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
private
|
|
113
|
+
|
|
114
|
+
def ask_note(options, note = nil, prompt: false)
|
|
115
|
+
note ||= Doing::Note.new
|
|
126
116
|
note.add(options[:note]) if options[:note]
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
if options[:done] && entry.should_finish?
|
|
133
|
-
if entry.should_time?
|
|
134
|
-
entry.tag('done', value: options[:done])
|
|
135
|
-
else
|
|
136
|
-
entry.tag('done')
|
|
137
|
-
end
|
|
117
|
+
|
|
118
|
+
res = prompt ? Doing::Prompt.yn('Add a note', default_response: false) : false
|
|
119
|
+
|
|
120
|
+
if options[:ask_note].empty? && (res || options[:ask])
|
|
121
|
+
options[:ask_note] = Doing::Prompt.read_lines(prompt: 'Enter note')
|
|
138
122
|
end
|
|
123
|
+
|
|
124
|
+
note.add(options[:ask_note]) if options[:ask_note].good?
|
|
125
|
+
note
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def add_examples(cmd)
|
|
129
|
+
cmd.example 'doing now', desc: 'Create a new entry with interactive prompts'
|
|
130
|
+
cmd.example 'doing now -e', desc: "Open #{Doing::Util.default_editor} to input an entry and optional note"
|
|
131
|
+
cmd.example 'doing now working on a new project', desc: 'Add a new entry at the current time'
|
|
132
|
+
cmd.example 'doing now debugging @project2', desc: 'Add an entry with a tag'
|
|
133
|
+
cmd.example 'doing now adding an entry (with a note)', desc: 'Parenthetical at end is converted to note'
|
|
134
|
+
cmd.example 'doing now --back 2pm A thing I started at 2:00 and am still doing...', desc: 'Backdate an entry'
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def add_options(cmd)
|
|
138
|
+
cmd.desc 'Section'
|
|
139
|
+
cmd.arg_name 'NAME'
|
|
140
|
+
cmd.flag %i[s section]
|
|
141
|
+
|
|
142
|
+
cmd.desc %(Set a start and optionally end time as a date range ("from 1pm to 2:30pm").
|
|
143
|
+
If an end time is provided, a dated @done tag will be added)
|
|
144
|
+
cmd.arg_name 'TIME_RANGE'
|
|
145
|
+
cmd.flag [:from], type: DateRangeString
|
|
146
|
+
|
|
147
|
+
cmd.desc 'Timed entry, marks last entry in section as @done'
|
|
148
|
+
cmd.switch %i[f finish_last], negatable: false, default_value: false
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
desc 'Add an entry'
|
|
154
|
+
long_desc %(Record what you're starting now, or backdate the start time using natural language.
|
|
155
|
+
|
|
156
|
+
A parenthetical at the end of the entry will be converted to a note.
|
|
157
|
+
|
|
158
|
+
Run without arguments to create a new entry interactively.
|
|
159
|
+
|
|
160
|
+
Run with --editor to create a new entry using #{Doing::Util.default_editor}.)
|
|
161
|
+
arg_name 'ENTRY'
|
|
162
|
+
command %i[now next] do |c|
|
|
163
|
+
cmd = Doing::NowCommand.new(@wwid)
|
|
164
|
+
cmd.setup(c)
|
|
165
|
+
add_options(:add_entry, c)
|
|
166
|
+
|
|
167
|
+
# c.desc "Edit entry with specified app"
|
|
168
|
+
# c.arg_name 'editor_app'
|
|
169
|
+
# # c.flag [:a, :app]
|
|
170
|
+
c.action do |global_options, options, args|
|
|
171
|
+
Doing.auto_tag = !options[:noauto]
|
|
172
|
+
options[:has_args] = args.count.positive?
|
|
173
|
+
options = cmd.process_now_options(options)
|
|
174
|
+
|
|
175
|
+
if options[:editor]
|
|
176
|
+
cmd.now_with_editor(options, args)
|
|
177
|
+
elsif args.length.positive?
|
|
178
|
+
cmd.now_with_args(options, args)
|
|
179
|
+
elsif global_options[:stdin]
|
|
180
|
+
cmd.now_with_stdin(global_options, options, args)
|
|
181
|
+
else
|
|
182
|
+
cmd.interactive_now(options, args)
|
|
139
183
|
end
|
|
140
184
|
|
|
141
185
|
@wwid.write(@wwid.doing_file)
|
data/bin/commands/on.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
# @@on
|
|
2
4
|
desc 'List entries for a date'
|
|
3
5
|
long_desc %(Date argument can be natural language. "thursday" would be interpreted as "last thursday,"
|
|
@@ -18,16 +20,18 @@ command :on do |c|
|
|
|
18
20
|
add_options(:search, c)
|
|
19
21
|
add_options(:tag_filter, c)
|
|
20
22
|
add_options(:time_filter, c)
|
|
23
|
+
add_options(:save, c)
|
|
21
24
|
|
|
22
25
|
c.action do |_global_options, options, args|
|
|
23
|
-
|
|
26
|
+
if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
|
|
27
|
+
raise InvalidPlugin.new('output', options[:output])
|
|
28
|
+
|
|
29
|
+
end
|
|
24
30
|
|
|
25
31
|
raise MissingArgument, 'Missing date argument' if args.empty?
|
|
26
32
|
|
|
27
33
|
date_string = args.join(' ').strip
|
|
28
|
-
if date_string =~ /^tod(?:ay)?/i
|
|
29
|
-
date_string = 'midnight to today 23:59'
|
|
30
|
-
end
|
|
34
|
+
date_string = 'midnight to today 23:59' if date_string =~ /^tod(?:ay)?/i
|
|
31
35
|
|
|
32
36
|
start, finish = date_string.split_date_range
|
|
33
37
|
|
|
@@ -40,6 +44,17 @@ command :on do |c|
|
|
|
40
44
|
options[:times] = true if options[:totals]
|
|
41
45
|
options[:sort_tags] = options[:tag_sort]
|
|
42
46
|
|
|
43
|
-
Doing::Pager.page @wwid.list_date([start, finish],
|
|
47
|
+
Doing::Pager.page @wwid.list_date([start, finish],
|
|
48
|
+
options[:section],
|
|
49
|
+
options[:times],
|
|
50
|
+
options[:output],
|
|
51
|
+
options).chomp
|
|
52
|
+
|
|
53
|
+
if options[:save]
|
|
54
|
+
options[:before] = Doing.original_options[:date_end] if Doing.original_options[:date_end].good?
|
|
55
|
+
options[:after] = Doing.original_options[:date_begin] if Doing.original_options[:date_begin].good?
|
|
56
|
+
options[:from] = Doing.original_options[:date_range] if Doing.original_options[:date_range].good?
|
|
57
|
+
Doing.config.save_view(options.to_view, options[:save].downcase)
|
|
58
|
+
end
|
|
44
59
|
end
|
|
45
60
|
end
|
data/bin/commands/recent.rb
CHANGED
|
@@ -17,6 +17,7 @@ command :recent do |c|
|
|
|
17
17
|
|
|
18
18
|
add_options(:output_template, c, default_template: 'recent')
|
|
19
19
|
add_options(:time_display, c)
|
|
20
|
+
add_options(:save, c)
|
|
20
21
|
|
|
21
22
|
c.action do |global_options, options, args|
|
|
22
23
|
section = @wwid.guess_section(options[:section]) || options[:section].cap_first
|
|
@@ -53,7 +54,9 @@ command :recent do |c|
|
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
Doing::Pager::page @wwid.recent(count, section.cap_first, opts)
|
|
56
|
-
|
|
57
|
+
opts[:count] = count
|
|
58
|
+
opts[:title] = options[:title]
|
|
59
|
+
Doing.config.save_view(opts.to_view, options[:save].downcase) if options[:save]
|
|
57
60
|
end
|
|
58
61
|
end
|
|
59
62
|
end
|
data/bin/commands/show.rb
CHANGED
|
@@ -47,6 +47,7 @@ command :show do |c|
|
|
|
47
47
|
add_options(:search, c)
|
|
48
48
|
add_options(:tag_filter, c)
|
|
49
49
|
add_options(:date_filter, c)
|
|
50
|
+
add_options(:save, c)
|
|
50
51
|
|
|
51
52
|
c.action do |global_options, options, args|
|
|
52
53
|
options[:fuzzy] = false
|
|
@@ -153,5 +154,12 @@ command :show do |c|
|
|
|
153
154
|
opt[:tags_color] = template['tags_color']
|
|
154
155
|
|
|
155
156
|
Doing::Pager.page @wwid.list_section(opt, items: items)
|
|
157
|
+
|
|
158
|
+
if options[:save]
|
|
159
|
+
opt[:before] = Doing.original_options[:date_begin] if Doing.original_options[:date_begin].good?
|
|
160
|
+
opt[:after] = Doing.original_options[:date_end] if Doing.original_options[:date_end].good?
|
|
161
|
+
opt[:from] = Doing.original_options[:date_range] if Doing.original_options[:date_range].good?
|
|
162
|
+
Doing.config.save_view(opt.to_view, options[:save].downcase)
|
|
163
|
+
end
|
|
156
164
|
end
|
|
157
165
|
end
|