doing 2.1.7 → 2.1.11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.yardoc/checksums +9 -9
- data/.yardoc/object_types +0 -0
- data/.yardoc/objects/root.dat +0 -0
- data/CHANGELOG.md +39 -1
- data/Dockerfile +9 -0
- data/Dockerfile-2.6 +9 -0
- data/Dockerfile-2.7 +8 -0
- data/Dockerfile-3.0 +8 -0
- data/Gemfile.lock +1 -1
- data/README.md +1 -1
- data/Rakefile +51 -6
- data/bin/doing +2098 -1944
- data/docs/doc/Array.html +1 -1
- 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 +1 -1
- data/docs/doc/Doing/Completion.html +1 -1
- data/docs/doc/Doing/Configuration.html +2 -2
- 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 +1 -1
- data/docs/doc/Doing/LogAdapter.html +1 -1
- data/docs/doc/Doing/Note.html +1 -1
- data/docs/doc/Doing/Pager.html +1 -1
- data/docs/doc/Doing/Plugins.html +1 -1
- data/docs/doc/Doing/Prompt.html +132 -18
- data/docs/doc/Doing/Section.html +1 -1
- data/docs/doc/Doing/TemplateString.html +2 -2
- data/docs/doc/Doing/Util/Backup.html +79 -2
- data/docs/doc/Doing/Util.html +1 -1
- data/docs/doc/Doing/WWID.html +90 -77
- data/docs/doc/Doing.html +2 -2
- 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/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 +97 -1
- data/docs/doc/Symbol.html +36 -2
- data/docs/doc/Time.html +1 -1
- data/docs/doc/_index.html +1 -1
- data/docs/doc/file.README.html +2 -2
- data/docs/doc/index.html +2 -2
- data/docs/doc/method_list.html +299 -235
- data/docs/doc/top-level-namespace.html +1 -1
- data/docs/index.md +1 -1
- data/doing.rdoc +9 -2
- data/generate_completions.sh +1 -3
- data/lib/completion/_doing.zsh +1 -1
- data/lib/completion/doing.bash +2 -2
- data/lib/completion/doing.fish +2 -1
- data/lib/doing/completion/bash_completion.rb +2 -2
- data/lib/doing/completion/fish_completion.rb +2 -2
- data/lib/doing/completion/zsh_completion.rb +2 -2
- data/lib/doing/completion.rb +12 -2
- data/lib/doing/configuration.rb +19 -9
- data/lib/doing/hooks.rb +10 -5
- data/lib/doing/items.rb +16 -1
- data/lib/doing/log_adapter.rb +1 -0
- data/lib/doing/pager.rb +2 -20
- data/lib/doing/plugins/import/calendar_import.rb +5 -0
- data/lib/doing/plugins/import/doing_import.rb +2 -0
- data/lib/doing/plugins/import/timing_import.rb +5 -0
- data/lib/doing/prompt.rb +47 -8
- data/lib/doing/string.rb +20 -0
- data/lib/doing/symbol.rb +4 -0
- data/lib/doing/util_backup.rb +38 -8
- data/lib/doing/version.rb +1 -1
- data/lib/doing/wwid.rb +211 -106
- data/lib/doing.rb +1 -0
- data/lib/examples/plugins/hooks.rb +31 -0
- data/scripts/generate_bash_completions.rb +2 -2
- data/scripts/sort_commands.rb +59 -0
- metadata +7 -3
- data/lib/helpers/fuzzyfilefinder +0 -0
@@ -102,7 +102,7 @@
|
|
102
102
|
</div>
|
103
103
|
|
104
104
|
<div id="footer">
|
105
|
-
Generated on
|
105
|
+
Generated on Tue Dec 21 14:44:17 2021 by
|
106
106
|
<a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
107
107
|
0.9.26 (ruby-3.0.1).
|
108
108
|
</div>
|
data/docs/index.md
CHANGED
@@ -6,7 +6,7 @@ _If you're one of the rare people like me who find this useful, feel free to [bu
|
|
6
6
|
|
7
7
|
|
8
8
|
|
9
|
-
The current version of `doing` is 2.1.
|
9
|
+
The current version of `doing` is 2.1.9.
|
10
10
|
|
11
11
|
Find all of the documentation in the [doing wiki](https://github.com/ttscoff/doing/wiki).
|
12
12
|
|
data/doing.rdoc
CHANGED
@@ -5,7 +5,7 @@ record of what you've been doing, complete with tag-based time tracking. The
|
|
5
5
|
command line tool allows you to add entries, annotate with tags and notes, and
|
6
6
|
view your entries with myriad options, with a focus on a "natural" language syntax.
|
7
7
|
|
8
|
-
v2.1.
|
8
|
+
v2.1.10
|
9
9
|
|
10
10
|
=== Global Options
|
11
11
|
=== --config_file arg
|
@@ -397,7 +397,7 @@ File to write output to
|
|
397
397
|
Shell to generate for (bash, zsh, fish)
|
398
398
|
|
399
399
|
[Default Value] zsh
|
400
|
-
[Must Match] (?i-mx:^[bzf](?:[ai]?sh)
|
400
|
+
[Must Match] (?i-mx:^(?:[bzf](?:[ai]?sh)?|all)$)
|
401
401
|
|
402
402
|
|
403
403
|
==== Command: <tt>config </tt>
|
@@ -2155,6 +2155,13 @@ View entries newer than date. If this is only a time (8am, 1:30pm, 15:00), all d
|
|
2155
2155
|
[Default Value] None
|
2156
2156
|
|
2157
2157
|
|
2158
|
+
===== --age AGE
|
2159
|
+
|
2160
|
+
Age (oldest|newest)
|
2161
|
+
|
2162
|
+
[Default Value] newest
|
2163
|
+
|
2164
|
+
|
2158
2165
|
===== -b|--bool BOOLEAN
|
2159
2166
|
|
2160
2167
|
Tag boolean (AND,OR,NOT). Use PATTERN to parse + and - as booleans.
|
data/generate_completions.sh
CHANGED
@@ -1,5 +1,3 @@
|
|
1
1
|
#!/bin/bash
|
2
2
|
|
3
|
-
bundle exec bin/doing completion --type
|
4
|
-
bundle exec bin/doing completion --type bash --file lib/completion/doing.bash
|
5
|
-
bundle exec bin/doing completion --type zsh --file lib/completion/_doing.zsh
|
3
|
+
bundle exec bin/doing completion --type all
|
data/lib/completion/_doing.zsh
CHANGED
@@ -201,7 +201,7 @@ function _doing() {
|
|
201
201
|
args=( {-f,--file=}"[Specify alternate doing file]" {-i,--interactive}"[Select from recent backups]" {-p,--prune=}"[Remove old backups]" {-r,--redo}"[Redo last undo]" )
|
202
202
|
;;
|
203
203
|
view)
|
204
|
-
args=( "(--after=)--after=}[View entries newer than date]" {-b,--bool=}"[Tag boolean]" "(--before=)--before=}[View entries older than date]" {-c,--count=}"[Count to display]" "(--case=)--case=}[Case sensitivity for search string matching [(c)ase-sensitive]" "(--color)--color}[Include colors in output]" "(--duration)--duration}[Show elapsed time on entries without @done tag]" "(--from=)--from=}[Date range to show]" {-i,--interactive}"[Select from a menu of matching entries to perform additional operations]" "(--not)--not}[Show items that *dont* match search stringt* match search string]" {-o,--output=}"[Output to export format]" "(--only_timed)--only_timed}[Only show items with recorded time intervals]" {-s,--section=}"[Section]" "(--search=)--search=}[Search filter]" {-t,--times}"[Show time intervals on @done tasks]" "(--tag=)--tag=}[Tag filter]" "(--tag_order=)--tag_order=}[Tag sort direction]" "(--tag_sort=)--tag_sort=}[Sort tags by]" "(--totals)--totals}[Show intervals with totals at the end of output]" {-x,--exact}"[Force exact search string matching]" )
|
204
|
+
args=( "(--after=)--after=}[View entries newer than date]" "(--age=)--age=}[Age]" {-b,--bool=}"[Tag boolean]" "(--before=)--before=}[View entries older than date]" {-c,--count=}"[Count to display]" "(--case=)--case=}[Case sensitivity for search string matching [(c)ase-sensitive]" "(--color)--color}[Include colors in output]" "(--duration)--duration}[Show elapsed time on entries without @done tag]" "(--from=)--from=}[Date range to show]" {-i,--interactive}"[Select from a menu of matching entries to perform additional operations]" "(--not)--not}[Show items that *dont* match search stringt* match search string]" {-o,--output=}"[Output to export format]" "(--only_timed)--only_timed}[Only show items with recorded time intervals]" {-s,--section=}"[Section]" "(--search=)--search=}[Search filter]" {-t,--times}"[Show time intervals on @done tasks]" "(--tag=)--tag=}[Tag filter]" "(--tag_order=)--tag_order=}[Tag sort direction]" "(--tag_sort=)--tag_sort=}[Sort tags by]" "(--totals)--totals}[Show intervals with totals at the end of output]" {-x,--exact}"[Force exact search string matching]" )
|
205
205
|
;;
|
206
206
|
views)
|
207
207
|
args=( {-c,--column}"[List in single column]" )
|
data/lib/completion/doing.bash
CHANGED
@@ -366,9 +366,9 @@ local words=$(doing views)
|
|
366
366
|
IFS="$OLD_IFS"
|
367
367
|
|
368
368
|
if [[ "$token" == --* ]]; then
|
369
|
-
COMPREPLY=( $( compgen -W '--after --bool --before --count --case --color --duration --from --interactive --not --output --only_timed --section --search --times --tag --tag_order --tag_sort --totals --exact' -- $token ) )
|
369
|
+
COMPREPLY=( $( compgen -W '--after --age --bool --before --count --case --color --duration --from --interactive --not --output --only_timed --section --search --times --tag --tag_order --tag_sort --totals --exact' -- $token ) )
|
370
370
|
elif [[ "$token" == -* ]]; then
|
371
|
-
COMPREPLY=( $( compgen -W '-b -c -i -o -s -t -x --after --bool --before --count --case --color --duration --from --interactive --not --output --only_timed --section --search --times --tag --tag_order --tag_sort --totals --exact' -- $token ) )
|
371
|
+
COMPREPLY=( $( compgen -W '-b -c -i -o -s -t -x --after --age --bool --before --count --case --color --duration --from --interactive --not --output --only_timed --section --search --times --tag --tag_order --tag_sort --totals --exact' -- $token ) )
|
372
372
|
else
|
373
373
|
local nocasematchWasOff=0
|
374
374
|
shopt nocasematch >/dev/null || nocasematchWasOff=1
|
data/lib/completion/doing.fish
CHANGED
@@ -371,6 +371,7 @@ complete -c doing -l interactive -s i -f -n '__fish_doing_using_command undo' -
|
|
371
371
|
complete -c doing -l prune -s p -f -r -n '__fish_doing_using_command undo' -d Remove\ old\ backups
|
372
372
|
complete -c doing -l redo -s r -f -n '__fish_doing_using_command undo' -d Redo\ last\ undo
|
373
373
|
complete -c doing -l after -f -r -n '__fish_doing_using_command view' -d View\ entries\ newer\ than\ date
|
374
|
+
complete -c doing -l age -f -r -n '__fish_doing_using_command view' -d Age
|
374
375
|
complete -c doing -l bool -s b -f -r -n '__fish_doing_using_command view' -d Tag\ boolean
|
375
376
|
complete -c doing -l before -f -r -n '__fish_doing_using_command view' -d View\ entries\ older\ than\ date
|
376
377
|
complete -c doing -l count -s c -f -r -n '__fish_doing_using_command view' -d Count\ to\ display
|
@@ -414,5 +415,5 @@ complete -f -c doing -s b -l bool -x -n '__fish_doing_using_command again resume
|
|
414
415
|
complete -f -c doing -l case -x -n '__fish_doing_using_command again resume archive move cancel finish grep search import last mark flag note reset begin rotate select show show tag tags tags view' -a 'case-sensitive ignore smart'
|
415
416
|
complete -f -c doing -l tag_sort -x -n '__fish_doing_using_command grep search on recent show since today view yesterday' -a 'name time'
|
416
417
|
complete -f -c doing -l tag_order -x -n '__fish_doing_using_command show view yesterday' -a 'asc desc'
|
417
|
-
complete -f -c doing -s a -l age -x -n '__fish_doing_using_command show' -a 'oldest newest'
|
418
|
+
complete -f -c doing -s a -l age -x -n '__fish_doing_using_command show view' -a 'oldest newest'
|
418
419
|
complete -f -c doing -s s -l section -x -n '__fish_doing_using_command again resume autotag cancel done did finish grep search import last mark flag meanwhile note now next on recent reset begin rotate select since tag tags today view wiki yesterday' -a '(__fish_doing_complete_sections)'
|
@@ -9,7 +9,7 @@ module Doing
|
|
9
9
|
logic = []
|
10
10
|
|
11
11
|
@commands.each_with_index do |cmd, i|
|
12
|
-
@bar.advance
|
12
|
+
@bar.advance(status: cmd[:commands].first)
|
13
13
|
|
14
14
|
data = get_help_sections(cmd[:commands].first)
|
15
15
|
|
@@ -167,7 +167,7 @@ module Doing
|
|
167
167
|
data = get_help_sections
|
168
168
|
@global_options = parse_options(data[:global_options])
|
169
169
|
@commands = parse_commands(data[:commands])
|
170
|
-
@bar = TTY::ProgressBar.new("\033[0;0;33mGenerating Bash completions: \033[0;35;40m[:bar]\033[0m", total: @commands.count, bar_format: :blade)
|
170
|
+
@bar = TTY::ProgressBar.new("\033[0;0;33mGenerating Bash completions: \033[0;35;40m[:bar] :status\033[0m", total: @commands.count, bar_format: :blade, status: 'Reading subcommands')
|
171
171
|
@bar.resize(25)
|
172
172
|
end
|
173
173
|
|
@@ -132,7 +132,7 @@ module Doing
|
|
132
132
|
need_section = []
|
133
133
|
|
134
134
|
@commands.each_with_index do |cmd, i|
|
135
|
-
@bar.advance
|
135
|
+
@bar.advance(status: cmd[:commands].first)
|
136
136
|
data = get_help_sections(cmd[:commands].first)
|
137
137
|
|
138
138
|
if data[:synopsis].join(' ').strip.split(/ /).last =~ /(path|file)/i
|
@@ -200,7 +200,7 @@ module Doing
|
|
200
200
|
data = get_help_sections
|
201
201
|
@global_options = parse_options(data[:global_options])
|
202
202
|
@commands = parse_commands(data[:commands])
|
203
|
-
@bar = TTY::ProgressBar.new("\033[0;0;33mGenerating Fish completions: \033[0;35;40m[:bar]\033[0m", total: @commands.count, bar_format: :blade)
|
203
|
+
@bar = TTY::ProgressBar.new("\033[0;0;33mGenerating Fish completions: \033[0;35;40m[:bar] :status\033[0m", total: @commands.count, bar_format: :blade, status: 'processing subcommands')
|
204
204
|
@bar.resize(25)
|
205
205
|
end
|
206
206
|
|
@@ -96,7 +96,7 @@ module Doing
|
|
96
96
|
out = []
|
97
97
|
|
98
98
|
@commands.each_with_index do |cmd, i|
|
99
|
-
@bar.advance
|
99
|
+
@bar.advance(status: cmd[:commands].first)
|
100
100
|
|
101
101
|
data = get_help_sections(cmd[:commands].first)
|
102
102
|
option_arr = []
|
@@ -127,7 +127,7 @@ module Doing
|
|
127
127
|
data = get_help_sections
|
128
128
|
@global_options = parse_options(data[:global_options])
|
129
129
|
@commands = parse_commands(data[:commands])
|
130
|
-
@bar = TTY::ProgressBar.new(" \033[0;0;33mGenerating Zsh completions: \033[0;35;40m[:bar]\033[0m", total: @commands.count, bar_format: :blade)
|
130
|
+
@bar = TTY::ProgressBar.new(" \033[0;0;33mGenerating Zsh completions: \033[0;35;40m[:bar] :status\033[0m", total: @commands.count, bar_format: :blade, status: 'processing subcommands')
|
131
131
|
@bar.resize(25)
|
132
132
|
end
|
133
133
|
|
data/lib/doing/completion.rb
CHANGED
@@ -19,11 +19,21 @@ module Doing
|
|
19
19
|
# @param file [String] Path to save to, or 'stdout'
|
20
20
|
#
|
21
21
|
def generate_completion(type: 'zsh', file: 'stdout')
|
22
|
+
if type =~ /^all$/i
|
23
|
+
Doing.logger.log_now(:warn, 'Generating:', 'all completion types, will use default paths')
|
24
|
+
generate_completion(type: 'fish', file: 'lib/completion/doing.fish')
|
25
|
+
Doing.logger.warn('File written:', "fish completions written to lib/completion/doing.fish")
|
26
|
+
generate_completion(type: 'zsh', file: 'lib/completion/_doing.zsh')
|
27
|
+
Doing.logger.warn('File written:', "zsh completions written to lib/completion/_doing.zsh")
|
28
|
+
generate_completion(type: 'bash', file: 'lib/completion/doing.bash')
|
29
|
+
Doing.logger.warn('File written:', "bash completions written to lib/completion/doing.bash")
|
30
|
+
return
|
31
|
+
end
|
22
32
|
|
23
33
|
generator = case type.to_s
|
24
|
-
when /^f/
|
34
|
+
when /^f/i
|
25
35
|
FishCompletions.new
|
26
|
-
when /^b/
|
36
|
+
when /^b/i
|
27
37
|
BashCompletions.new
|
28
38
|
else
|
29
39
|
ZshCompletions.new
|
data/lib/doing/configuration.rb
CHANGED
@@ -31,8 +31,8 @@ module Doing
|
|
31
31
|
},
|
32
32
|
'doing_file' => '~/.local/share/doing/what_was_i_doing.md',
|
33
33
|
'backup_dir' => '~/.local/share/doing/doing_backup',
|
34
|
-
'current_section' => 'Currently',
|
35
34
|
'history_size' => 15,
|
35
|
+
'current_section' => 'Currently',
|
36
36
|
'paginate' => false,
|
37
37
|
'never_time' => [],
|
38
38
|
'never_finish' => [],
|
@@ -152,17 +152,23 @@ module Doing
|
|
152
152
|
##
|
153
153
|
## @return [String] file path
|
154
154
|
##
|
155
|
-
def choose_config
|
155
|
+
def choose_config(create: false)
|
156
156
|
return @config_file if @force_answer
|
157
157
|
|
158
|
-
if @additional_configs.count.positive?
|
158
|
+
if @additional_configs.count.positive? || create
|
159
159
|
choices = [@config_file].concat(@additional_configs)
|
160
|
+
choices.push('Create a new .doingrc in the current directory') if create && !File.exist?('.doingrc')
|
160
161
|
res = Doing::Prompt.choose_from(choices.uniq.sort.reverse,
|
161
162
|
sorted: false,
|
162
163
|
prompt: 'Local configs found, select which to update > ')
|
163
164
|
|
164
165
|
raise UserCancelled, 'Cancelled' unless res
|
165
166
|
|
167
|
+
if res =~ /^Create a new/
|
168
|
+
res = File.expand_path('.doingrc')
|
169
|
+
FileUtils.touch(res)
|
170
|
+
end
|
171
|
+
|
166
172
|
res.strip || @config_file
|
167
173
|
else
|
168
174
|
@config_file
|
@@ -249,7 +255,8 @@ module Doing
|
|
249
255
|
# defaults.
|
250
256
|
#
|
251
257
|
def from(user_config)
|
252
|
-
Util.deep_merge_hashes(DEFAULTS, Configuration[user_config].stringify_keys)
|
258
|
+
# Util.deep_merge_hashes(DEFAULTS, Configuration[user_config].stringify_keys)
|
259
|
+
Configuration[user_config].stringify_keys.deep_merge(DEFAULTS, { extend_existing_arrays: true, sort_merged_arrays: true })
|
253
260
|
end
|
254
261
|
|
255
262
|
##
|
@@ -320,8 +327,8 @@ module Doing
|
|
320
327
|
|
321
328
|
Hooks.trigger :post_config, self
|
322
329
|
|
323
|
-
|
324
|
-
config = Util.deep_merge_hashes(config, local_config) unless @ignore_local
|
330
|
+
config = local_config.deep_merge(config, { extend_existing_arrays: true, sort_merged_arrays: true }) unless @ignore_local
|
331
|
+
# config = Util.deep_merge_hashes(config, local_config) unless @ignore_local
|
325
332
|
|
326
333
|
Hooks.trigger :post_local_config, self
|
327
334
|
|
@@ -397,7 +404,7 @@ module Doing
|
|
397
404
|
|
398
405
|
begin
|
399
406
|
additional_configs.each do |cfg|
|
400
|
-
local_configs.deep_merge(Util.safe_load_file(cfg))
|
407
|
+
local_configs.deep_merge(Util.safe_load_file(cfg), { extend_existing_arrays: true, sort_merged_arrays: true })
|
401
408
|
end
|
402
409
|
rescue StandardError
|
403
410
|
Doing.logger.error('Config:', 'Error reading local configuration(s)')
|
@@ -416,15 +423,18 @@ module Doing
|
|
416
423
|
end
|
417
424
|
|
418
425
|
begin
|
426
|
+
|
419
427
|
user_config = Util.safe_load_file(config_file)
|
428
|
+
raise StandardError, 'Invalid config file format' unless user_config.is_a?(Hash)
|
429
|
+
|
420
430
|
if user_config.key?('html_template')
|
421
431
|
user_config['export_templates'] ||= {}
|
422
|
-
user_config['export_templates'].deep_merge(user_config.delete('html_template'))
|
432
|
+
user_config['export_templates'].deep_merge(user_config.delete('html_template'), { extend_existing_arrays: true, sort_merged_arrays: true })
|
423
433
|
end
|
424
434
|
|
425
435
|
user_config['include_notes'] = user_config.delete(':include_notes') if user_config.key?(':include_notes')
|
426
436
|
|
427
|
-
user_config.deep_merge(DEFAULTS)
|
437
|
+
user_config.deep_merge(DEFAULTS, { extend_existing_arrays: true, sort_merged_arrays: true })
|
428
438
|
rescue StandardError => e
|
429
439
|
Doing.logger.error('Config:', 'Error reading default configuration')
|
430
440
|
Doing.logger.error('Error:', e.message)
|
data/lib/doing/hooks.rb
CHANGED
@@ -6,11 +6,16 @@ module Doing
|
|
6
6
|
DEFAULT_PRIORITY = 20
|
7
7
|
|
8
8
|
@registry = {
|
9
|
-
post_config: [],
|
10
|
-
post_local_config: [],
|
11
|
-
post_read: [],
|
12
|
-
|
13
|
-
|
9
|
+
post_config: [], # wwid
|
10
|
+
post_local_config: [], # wwid
|
11
|
+
post_read: [], # wwid
|
12
|
+
pre_entry_add: [], # wwid, new_entry
|
13
|
+
post_entry_added: [], # wwid, new_entry.dup
|
14
|
+
post_entry_updated: [], # wwid, entry
|
15
|
+
post_entry_removed: [], # wwid, entry.dup
|
16
|
+
pre_export: [], # wwid, format, entries
|
17
|
+
pre_write: [], # wwid, file
|
18
|
+
post_write: [] # wwid, file
|
14
19
|
}
|
15
20
|
|
16
21
|
# map of all hooks and their priorities
|
data/lib/doing/items.rb
CHANGED
@@ -69,7 +69,7 @@ module Doing
|
|
69
69
|
if section =~ /^all$/i
|
70
70
|
dup
|
71
71
|
else
|
72
|
-
items = Items.new.concat(select { |item| item.section == section })
|
72
|
+
items = Items.new.concat(select { |item| !item.nil? && item.section == section })
|
73
73
|
items.add_section(section, log: false)
|
74
74
|
items
|
75
75
|
end
|
@@ -84,6 +84,7 @@ module Doing
|
|
84
84
|
deleted = delete(item)
|
85
85
|
Doing.logger.count(:deleted)
|
86
86
|
Doing.logger.info('Entry deleted:', deleted.title) if single
|
87
|
+
deleted
|
87
88
|
end
|
88
89
|
|
89
90
|
##
|
@@ -111,6 +112,20 @@ module Doing
|
|
111
112
|
end
|
112
113
|
end
|
113
114
|
|
115
|
+
##
|
116
|
+
## Return Items containing items that don't exist in receiver
|
117
|
+
##
|
118
|
+
## @param items [Items] Receiver
|
119
|
+
##
|
120
|
+
def diff(items)
|
121
|
+
diff = Items.new
|
122
|
+
each do |item|
|
123
|
+
res = items.select { |i| i.equal?(item) }
|
124
|
+
diff.push(item) unless res.count.positive?
|
125
|
+
end
|
126
|
+
diff
|
127
|
+
end
|
128
|
+
|
114
129
|
# Output sections and items in Doing file format
|
115
130
|
def to_s
|
116
131
|
out = []
|
data/lib/doing/log_adapter.rb
CHANGED
data/lib/doing/pager.rb
CHANGED
@@ -64,22 +64,8 @@ module Doing
|
|
64
64
|
|
65
65
|
private
|
66
66
|
|
67
|
-
def command_exist?(command)
|
68
|
-
exts = ENV.fetch("PATHEXT", "").split(::File::PATH_SEPARATOR)
|
69
|
-
if Pathname.new(command).absolute?
|
70
|
-
::File.exist?(command) ||
|
71
|
-
exts.any? { |ext| ::File.exist?("#{command}#{ext}")}
|
72
|
-
else
|
73
|
-
ENV.fetch("PATH", "").split(::File::PATH_SEPARATOR).any? do |dir|
|
74
|
-
file = ::File.join(dir, command)
|
75
|
-
::File.exist?(file) ||
|
76
|
-
exts.any? { |ext| ::File.exist?("#{file}#{ext}") }
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
67
|
def git_pager
|
82
|
-
|
68
|
+
TTY::Which.exist?('git') ? `#{TTY::Which.which('git')} config --get-all core.pager` : nil
|
83
69
|
end
|
84
70
|
|
85
71
|
def pagers
|
@@ -91,11 +77,7 @@ module Doing
|
|
91
77
|
execs = commands.empty? ? pagers : commands
|
92
78
|
execs
|
93
79
|
.compact.map(&:strip).reject(&:empty?).uniq
|
94
|
-
.find { |cmd|
|
95
|
-
end
|
96
|
-
|
97
|
-
def exec_available?(*commands)
|
98
|
-
!find_executable(*commands).nil?
|
80
|
+
.find { |cmd| TTY::Which.exist?(cmd.split.first) }
|
99
81
|
end
|
100
82
|
|
101
83
|
def which_pager
|
@@ -73,7 +73,12 @@ module Doing
|
|
73
73
|
dups = filtered - new_items.count
|
74
74
|
Doing.logger.info(%(Skipped #{dups} items with overlapping times)) if dups.positive?
|
75
75
|
|
76
|
+
new_items.map { |item| Hooks.trigger :pre_entry_add, self, item }
|
77
|
+
|
76
78
|
wwid.content.concat(new_items)
|
79
|
+
|
80
|
+
new_items.map { |item| Hooks.trigger :post_entry_added, self, item.dup }
|
81
|
+
|
77
82
|
Doing.logger.info(%(Imported #{new_items.count} items to #{section}))
|
78
83
|
end
|
79
84
|
|
@@ -80,7 +80,9 @@ module Doing
|
|
80
80
|
|
81
81
|
imported.each do |item|
|
82
82
|
wwid.content.add_section(item.section) unless wwid.content.section?(item.section)
|
83
|
+
Hooks.trigger :pre_entry_add, self, item
|
83
84
|
wwid.content.push(item)
|
85
|
+
Hooks.trigger :post_entry_added, self, item.dup
|
84
86
|
end
|
85
87
|
|
86
88
|
Doing.logger.info('Imported:', "#{imported.count} items")
|
@@ -77,7 +77,12 @@ module Doing
|
|
77
77
|
dups = filtered - new_items.count
|
78
78
|
Doing.logger.debug('Skipped:' , %(#{dups} items with overlapping times)) if dups.positive?
|
79
79
|
|
80
|
+
new_items.map { |item| Hooks.trigger :pre_entry_add, self, item }
|
81
|
+
|
80
82
|
wwid.content.concat(new_items)
|
83
|
+
|
84
|
+
new_items.map { |item| Hooks.trigger :post_entry_added, self, item.dup }
|
85
|
+
|
81
86
|
Doing.logger.info('Imported:', %(#{new_items.count} items to #{section}))
|
82
87
|
end
|
83
88
|
|
data/lib/doing/prompt.rb
CHANGED
@@ -86,7 +86,38 @@ module Doing
|
|
86
86
|
@fzf ||= install_fzf
|
87
87
|
end
|
88
88
|
|
89
|
-
def
|
89
|
+
def uninstall_fzf
|
90
|
+
fzf_bin = File.join(File.dirname(__FILE__), '../helpers/fzf/bin/fzf')
|
91
|
+
FileUtils.rm_f(fzf_bin) if File.exist?(fzf_bin)
|
92
|
+
Doing.logger.warn('fzf:', "removed #{fzf_bin}")
|
93
|
+
end
|
94
|
+
|
95
|
+
def which_fzf
|
96
|
+
fzf_dir = File.join(File.dirname(__FILE__), '../helpers/fzf')
|
97
|
+
fzf_bin = File.join(fzf_dir, 'bin/fzf')
|
98
|
+
return fzf_bin if File.exist?(fzf_bin)
|
99
|
+
|
100
|
+
Doing.logger.debug('fzf:', 'Using user-installed fzf')
|
101
|
+
TTY::Which.which('fzf')
|
102
|
+
end
|
103
|
+
|
104
|
+
def silence_std(file = '/dev/null')
|
105
|
+
$stdout = File.new(file, 'w')
|
106
|
+
$stderr = File.new(file, 'w')
|
107
|
+
end
|
108
|
+
|
109
|
+
def restore_std
|
110
|
+
$stdout = STDOUT
|
111
|
+
$stderr = STDERR
|
112
|
+
end
|
113
|
+
|
114
|
+
def install_fzf(force: false)
|
115
|
+
if force
|
116
|
+
uninstall_fzf
|
117
|
+
elsif which_fzf
|
118
|
+
return which_fzf
|
119
|
+
end
|
120
|
+
|
90
121
|
fzf_dir = File.join(File.dirname(__FILE__), '../helpers/fzf')
|
91
122
|
FileUtils.mkdir_p(fzf_dir) unless File.directory?(fzf_dir)
|
92
123
|
fzf_bin = File.join(fzf_dir, 'bin/fzf')
|
@@ -94,17 +125,25 @@ module Doing
|
|
94
125
|
|
95
126
|
prev_level = Doing.logger.level
|
96
127
|
Doing.logger.adjust_verbosity({ log_level: :info })
|
97
|
-
Doing.logger.log_now(:warn, 'Compiling and installing fzf -- this will only happen once')
|
98
|
-
Doing.logger.log_now(:warn, 'fzf is copyright Junegunn Choi, MIT License <https://github.com/junegunn/fzf/blob/master/LICENSE>')
|
128
|
+
Doing.logger.log_now(:warn, 'fzf:', 'Compiling and installing fzf -- this will only happen once')
|
129
|
+
Doing.logger.log_now(:warn, 'fzf:', 'fzf is copyright Junegunn Choi, MIT License <https://github.com/junegunn/fzf/blob/master/LICENSE>')
|
99
130
|
|
100
|
-
|
131
|
+
silence_std
|
132
|
+
`'#{fzf_dir}/install' --bin --no-key-bindings --no-completion --no-update-rc --no-bash --no-zsh --no-fish &> /dev/null`
|
101
133
|
unless File.exist?(fzf_bin)
|
134
|
+
restore_std
|
102
135
|
Doing.logger.log_now(:warn, 'Error installing, trying again as root')
|
103
|
-
|
136
|
+
silence_std
|
137
|
+
`sudo '#{fzf_dir}/install' --bin --no-key-bindings --no-completion --no-update-rc --no-bash --no-zsh --no-fish &> /dev/null`
|
138
|
+
end
|
139
|
+
restore_std
|
140
|
+
unless File.exist?(fzf_bin)
|
141
|
+
Doing.logger.error('fzf:', 'unable to install fzf. You can install manually and Doing will use the system version.')
|
142
|
+
Doing.logger.error('fzf:', 'see https://github.com/junegunn/fzf#installation')
|
143
|
+
raise RuntimeError.new('Error installing fzf, please report at https://github.com/ttscoff/doing/issues')
|
104
144
|
end
|
105
|
-
raise RuntimeError.new('Error installing fzf, please report at https://github.com/ttscoff/doing/issues') unless File.exist?(fzf_bin)
|
106
145
|
|
107
|
-
Doing.logger.info(
|
146
|
+
Doing.logger.info('fzf:', "installed to #{fzf}")
|
108
147
|
Doing.logger.adjust_verbosity({ log_level: prev_level })
|
109
148
|
fzf_bin
|
110
149
|
end
|
@@ -189,7 +228,7 @@ module Doing
|
|
189
228
|
%(-q "#{query}"),
|
190
229
|
'--info=inline'
|
191
230
|
]
|
192
|
-
fzf_args.push('-1') unless opt.fetch(:show_if_single)
|
231
|
+
fzf_args.push('-1') unless opt.fetch(:show_if_single, false)
|
193
232
|
fzf_args << case case_sensitive
|
194
233
|
when :sensitive
|
195
234
|
'+i'
|
data/lib/doing/string.rb
CHANGED
@@ -263,6 +263,26 @@ module Doing
|
|
263
263
|
number == 1 ? self : "#{self}s"
|
264
264
|
end
|
265
265
|
|
266
|
+
##
|
267
|
+
## Convert an age string to a qualified type
|
268
|
+
##
|
269
|
+
## @return [Symbol] :oldest or :newest
|
270
|
+
##
|
271
|
+
def normalize_age!(default = :newest)
|
272
|
+
replace normalize_age(default)
|
273
|
+
end
|
274
|
+
|
275
|
+
def normalize_age(default = :newest)
|
276
|
+
case self
|
277
|
+
when /^o/i
|
278
|
+
:oldest
|
279
|
+
when /^n/i
|
280
|
+
:newest
|
281
|
+
else
|
282
|
+
default
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
266
286
|
##
|
267
287
|
## Convert a sort order string to a qualified type
|
268
288
|
##
|
data/lib/doing/symbol.rb
CHANGED
data/lib/doing/util_backup.rb
CHANGED
@@ -20,6 +20,34 @@ module Doing
|
|
20
20
|
backups[limit..-1].each do |file|
|
21
21
|
FileUtils.rm(File.join(backup_dir, file))
|
22
22
|
end
|
23
|
+
|
24
|
+
clear_redo(filename)
|
25
|
+
end
|
26
|
+
|
27
|
+
##
|
28
|
+
## Delete all redo files
|
29
|
+
##
|
30
|
+
## @param limit Maximum number of backups to retain
|
31
|
+
##
|
32
|
+
def clear_redo(filename)
|
33
|
+
filename ||= Doing.config.settings['doing_file']
|
34
|
+
backups = Dir.glob("undone*___#{File.basename(filename)}", base: backup_dir).sort.reverse
|
35
|
+
backups.each do |file|
|
36
|
+
FileUtils.rm(File.join(backup_dir, file))
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
##
|
41
|
+
## Retrieve the most recent backup
|
42
|
+
##
|
43
|
+
## @param filename The filename
|
44
|
+
## @return [String] filename
|
45
|
+
##
|
46
|
+
def last_backup(filename = nil, count: 1)
|
47
|
+
filename ||= Doing.config.settings['doing_file']
|
48
|
+
|
49
|
+
backup = get_backups(filename).slice(count - 1)
|
50
|
+
backup.nil? ? nil : File.join(backup_dir, backup)
|
23
51
|
end
|
24
52
|
|
25
53
|
##
|
@@ -33,15 +61,13 @@ module Doing
|
|
33
61
|
Doing.logger.benchmark(:restore_backup, :start)
|
34
62
|
filename ||= Doing.config.settings['doing_file']
|
35
63
|
|
36
|
-
|
37
|
-
raise DoingRuntimeError, 'End of undo history' if
|
38
|
-
|
39
|
-
backup_file = File.join(backup_dir, result)
|
64
|
+
backup_file = last_backup(filename, count: count)
|
65
|
+
raise DoingRuntimeError, 'End of undo history' if backup_file.nil?
|
40
66
|
|
41
67
|
save_undone(filename)
|
42
68
|
FileUtils.mv(backup_file, filename)
|
43
69
|
prune_backups_after(File.basename(backup_file))
|
44
|
-
Doing.logger.warn('File update:', "restored from #{
|
70
|
+
Doing.logger.warn('File update:', "restored from #{backup_file}")
|
45
71
|
Doing.logger.benchmark(:restore_backup, :finish)
|
46
72
|
end
|
47
73
|
|
@@ -103,6 +129,8 @@ module Doing
|
|
103
129
|
arr.push("#{d.time_ago}\t#{File.join(backup_dir, file)}")
|
104
130
|
end
|
105
131
|
|
132
|
+
raise DoingRuntimeError, 'No backup files to load' if options.empty?
|
133
|
+
|
106
134
|
backup_file = show_menu(options, filename)
|
107
135
|
idx = undones.index(File.basename(backup_file))
|
108
136
|
skipped = undones.slice!(idx, undones.count - idx)
|
@@ -135,8 +163,10 @@ module Doing
|
|
135
163
|
arr.push("#{d.time_ago}\t#{File.join(backup_dir, file)}")
|
136
164
|
end
|
137
165
|
|
166
|
+
raise DoingRuntimeError, 'No backup files to load' if options.empty?
|
167
|
+
|
138
168
|
backup_file = show_menu(options, filename)
|
139
|
-
write_to_file(File.join(backup_dir, "undone___#{File.basename(filename)}"), IO.read(filename), backup: false)
|
169
|
+
Util.write_to_file(File.join(backup_dir, "undone___#{File.basename(filename)}"), IO.read(filename), backup: false)
|
140
170
|
FileUtils.mv(backup_file, filename)
|
141
171
|
prune_backups_after(File.basename(backup_file))
|
142
172
|
Doing.logger.warn('File update:', "restored from #{backup_file}")
|
@@ -215,10 +245,10 @@ module Doing
|
|
215
245
|
Time.now.strftime('%Y-%m-%d_%H.%M.%S')
|
216
246
|
end
|
217
247
|
|
218
|
-
def get_backups(filename = nil)
|
248
|
+
def get_backups(filename = nil, include_forward: false)
|
219
249
|
filename ||= Doing.config.settings['doing_file']
|
220
250
|
backups = Dir.glob("*___#{File.basename(filename)}", base: backup_dir).sort.reverse
|
221
|
-
backups.delete_if { |f| f =~ /^undone/ }
|
251
|
+
backups.delete_if { |f| f =~ /^undone/ } unless include_forward
|
222
252
|
end
|
223
253
|
|
224
254
|
def save_undone(filename = nil)
|
data/lib/doing/version.rb
CHANGED