doing 2.1.39 → 2.1.42
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/.yardopts +1 -1
- data/CHANGELOG.md +67 -0
- data/Gemfile.lock +1 -1
- data/README.md +1 -1
- data/Rakefile +4 -4
- data/bin/commands/again.rb +1 -3
- data/bin/commands/changes.rb +50 -34
- data/bin/commands/commands.rb +77 -52
- data/bin/commands/commands_accepting.rb +57 -53
- data/bin/commands/config.rb +45 -36
- data/bin/commands/done.rb +1 -18
- data/bin/commands/finish.rb +90 -59
- data/bin/commands/flag.rb +5 -1
- data/bin/commands/grep.rb +3 -14
- data/bin/commands/last.rb +2 -8
- data/bin/commands/meanwhile.rb +13 -6
- data/bin/commands/now.rb +151 -107
- data/bin/commands/on.rb +8 -18
- data/bin/commands/recent.rb +2 -8
- data/bin/commands/reset.rb +24 -1
- data/bin/commands/select.rb +1 -1
- data/bin/commands/show.rb +6 -17
- data/bin/commands/since.rb +1 -12
- data/bin/commands/tag_dir.rb +49 -15
- data/bin/commands/today.rb +2 -13
- data/bin/commands/undo.rb +4 -6
- data/bin/commands/view.rb +1 -1
- data/bin/commands/yesterday.rb +2 -13
- data/bin/doing +15 -8
- data/{Dockerfile → docker/Dockerfile} +3 -1
- data/{Dockerfile-2.6 → docker/Dockerfile-2.6} +2 -2
- data/{Dockerfile-2.7 → docker/Dockerfile-2.7} +2 -2
- data/{Dockerfile-3.0 → docker/Dockerfile-3.0} +2 -2
- data/{bash_profile → docker/bash_profile} +0 -0
- data/{inputrc → docker/inputrc} +0 -0
- data/docs/doc/Array.html +85 -2
- 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/ArrayNestedHash.html +198 -0
- data/docs/doc/Doing/ArrayTags.html +424 -0
- data/docs/doc/Doing/CSVExport.html +266 -0
- data/docs/doc/Doing/CalendarImport.html +232 -0
- data/docs/doc/Doing/Change.html +617 -0
- data/docs/doc/Doing/Changes.html +468 -0
- data/docs/doc/Doing/ChronifyArray.html +347 -0
- data/docs/doc/Doing/ChronifyNumeric.html +271 -0
- data/docs/doc/Doing/ChronifyString.html +682 -0
- data/docs/doc/Doing/Color.html +167 -21
- data/docs/doc/Doing/Completion/BashCompletions.html +445 -0
- data/docs/doc/Doing/Completion/FishCompletions.html +445 -0
- data/docs/doc/Doing/Completion/StringUtils.html +229 -0
- data/docs/doc/Doing/Completion/ZshCompletions.html +445 -0
- data/docs/doc/Doing/Completion.html +17 -3
- data/docs/doc/Doing/Configuration.html +3 -2
- data/docs/doc/Doing/DayOneRenderer.html +383 -0
- data/docs/doc/Doing/DayoneExport.html +290 -0
- data/docs/doc/Doing/DoingImport.html +391 -0
- data/docs/doc/Doing/Entry.html +381 -0
- data/docs/doc/Doing/Errors/DoingNoTraceError.html +7 -3
- data/docs/doc/Doing/Errors/DoingRuntimeError.html +7 -3
- data/docs/doc/Doing/Errors/DoingStandardError.html +1 -1
- data/docs/doc/Doing/Errors/EmptyInput.html +10 -2
- data/docs/doc/Doing/Errors/HistoryLimitError.html +194 -0
- data/docs/doc/Doing/Errors/InvalidPlugin.html +194 -0
- data/docs/doc/Doing/Errors/MissingBackupFile.html +194 -0
- data/docs/doc/Doing/Errors/NoResults.html +10 -2
- data/docs/doc/Doing/Errors/PluginException.html +1 -1
- data/docs/doc/Doing/Errors/UserCancelled.html +10 -2
- data/docs/doc/Doing/Errors/WrongCommand.html +10 -2
- data/docs/doc/Doing/Errors.html +9 -9
- data/docs/doc/Doing/HTMLExport.html +256 -0
- data/docs/doc/Doing/Hooks.html +1 -1
- data/docs/doc/Doing/Item.html +179 -1660
- data/docs/doc/Doing/ItemDates.html +564 -0
- data/docs/doc/Doing/ItemQuery.html +614 -0
- data/docs/doc/Doing/ItemState.html +387 -0
- data/docs/doc/Doing/ItemTags.html +498 -0
- data/docs/doc/Doing/Items.html +581 -15
- data/docs/doc/Doing/JSONExport.html +222 -0
- data/docs/doc/Doing/Logger.html +1 -1
- data/docs/doc/Doing/MarkdownExport.html +266 -0
- data/docs/doc/Doing/MarkdownRenderer.html +383 -0
- data/docs/doc/Doing/Note.html +18 -4
- data/docs/doc/Doing/Pager.html +1 -1
- data/docs/doc/Doing/Plugins.html +181 -76
- data/docs/doc/Doing/Prompt.html +32 -683
- data/docs/doc/Doing/PromptChoose.html +484 -0
- data/docs/doc/Doing/PromptFZF.html +391 -0
- data/docs/doc/Doing/PromptInput.html +572 -0
- data/docs/doc/Doing/PromptSTD.html +293 -0
- data/docs/doc/Doing/PromptYN.html +237 -0
- data/docs/doc/Doing/Section.html +58 -2
- data/docs/doc/Doing/StringHighlight.html +533 -0
- data/docs/doc/Doing/StringNormalize.html +929 -0
- data/docs/doc/Doing/StringQuery.html +725 -0
- data/docs/doc/Doing/StringTags.html +884 -0
- data/docs/doc/Doing/StringTransform.html +599 -0
- data/docs/doc/Doing/StringTruncate.html +448 -0
- data/docs/doc/Doing/StringURL.html +409 -0
- data/docs/doc/Doing/SymbolNormalize.html +341 -0
- data/docs/doc/Doing/TaskPaperExport.html +222 -0
- data/docs/doc/Doing/TemplateExport.html +249 -0
- data/docs/doc/Doing/TemplateString.html +102 -3
- data/docs/doc/Doing/TimingImport.html +285 -0
- data/docs/doc/Doing/Types.html +1 -1
- data/docs/doc/Doing/Util/Backup.html +11 -163
- data/docs/doc/Doing/Util.html +67 -10
- data/docs/doc/Doing/Version.html +523 -0
- data/docs/doc/Doing/WWID/WWIDUtil.html +510 -0
- data/docs/doc/Doing/WWID.html +476 -139
- data/docs/doc/Doing/WWIDDisplay.html +865 -0
- data/docs/doc/Doing/WWIDEditor.html +466 -0
- data/docs/doc/Doing/WWIDFileTools.html +359 -0
- data/docs/doc/Doing/WWIDFilter.html +466 -0
- data/docs/doc/Doing/WWIDGuess.html +299 -0
- data/docs/doc/Doing/WWIDInteractive.html +752 -0
- data/docs/doc/Doing/WWIDModify.html +1078 -0
- data/docs/doc/Doing/WWIDTags.html +302 -0
- data/docs/doc/Doing/WWIDTimers.html +359 -0
- data/docs/doc/Doing/WWIDUtil.html +510 -0
- data/docs/doc/Doing.html +9 -6
- data/docs/doc/FalseClass.html +1 -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/Numeric.html +23 -78
- 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 +58 -633
- data/docs/doc/Symbol.html +9 -224
- data/docs/doc/Time.html +119 -13
- data/docs/doc/TrueClass.html +1 -1
- data/docs/doc/_index.html +348 -4
- 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 +1904 -592
- data/docs/doc/top-level-namespace.html +12 -4
- data/docs/index.md +1 -1
- data/doing.rdoc +67 -15
- data/lib/completion/_doing.zsh +6 -6
- data/lib/completion/doing.bash +10 -10
- data/lib/completion/doing.fish +10 -3
- data/lib/doing/add_options.rb +39 -1
- data/lib/doing/array/array.rb +18 -12
- data/lib/doing/array/cleanup.rb +31 -0
- data/lib/doing/array/nested_hash.rb +1 -1
- data/lib/doing/array/tags.rb +6 -5
- data/lib/doing/changelog/changelog.rb +6 -0
- data/lib/doing/chronify/array.rb +65 -25
- data/lib/doing/chronify/chronify.rb +12 -0
- data/lib/doing/chronify/numeric.rb +3 -2
- data/lib/doing/chronify/string.rb +1 -1
- data/lib/doing/colors.rb +77 -30
- data/lib/doing/completion/completion_string.rb +25 -0
- data/lib/doing/completion.rb +4 -5
- data/lib/doing/configuration.rb +7 -3
- data/lib/doing/errors.rb +51 -35
- data/lib/doing/good.rb +8 -0
- data/lib/doing/hooks.rb +3 -3
- data/lib/doing/item/dates.rb +112 -0
- data/lib/doing/item/item.rb +128 -0
- data/lib/doing/{item.rb → item/query.rb} +2 -353
- data/lib/doing/item/state.rb +59 -0
- data/lib/doing/item/tags.rb +87 -0
- data/lib/doing/items/filter.rb +67 -0
- data/lib/doing/items/items.rb +57 -0
- data/lib/doing/items/modify.rb +36 -0
- data/lib/doing/items/sections.rb +83 -0
- data/lib/doing/items/util.rb +74 -0
- data/lib/doing/normalize.rb +10 -2
- data/lib/doing/note.rb +1 -1
- data/lib/doing/pager.rb +9 -3
- data/lib/doing/plugin_manager.rb +33 -8
- data/lib/doing/plugins/export/markdown_export.rb +4 -2
- data/lib/doing/plugins/export/template_export.rb +4 -4
- data/lib/doing/plugins/import/cal_to_json.scpt +0 -0
- data/lib/doing/plugins/import/doing_import.rb +1 -1
- data/lib/doing/prompt/choose.rb +118 -0
- data/lib/doing/prompt/fzf.rb +84 -0
- data/lib/doing/prompt/input.rb +129 -0
- data/lib/doing/prompt/prompt.rb +41 -0
- data/lib/doing/prompt/std.rb +32 -0
- data/lib/doing/prompt/yn.rb +64 -0
- data/lib/doing/section.rb +4 -0
- data/lib/doing/string/highlight.rb +1 -1
- data/lib/doing/string/query.rb +1 -1
- data/lib/doing/string/string.rb +18 -7
- data/lib/doing/string/tags.rb +14 -3
- data/lib/doing/string/transform.rb +7 -1
- data/lib/doing/string/truncate.rb +1 -1
- data/lib/doing/string/url.rb +1 -1
- data/lib/doing/time.rb +19 -1
- data/lib/doing/util.rb +12 -6
- data/lib/doing/util_backup.rb +62 -57
- data/lib/doing/version.rb +1 -1
- data/lib/doing/wwid/display.rb +396 -0
- data/lib/doing/wwid/editor.rb +214 -0
- data/lib/doing/wwid/filetools.rb +183 -0
- data/lib/doing/wwid/filter.rb +226 -0
- data/lib/doing/wwid/guess.rb +85 -0
- data/lib/doing/wwid/interactive.rb +377 -0
- data/lib/doing/wwid/modify.rb +617 -0
- data/lib/doing/wwid/tags.rb +51 -0
- data/lib/doing/wwid/timers.rb +342 -0
- data/lib/doing/wwid/wwid.rb +121 -0
- data/lib/doing/wwid/wwidutil.rb +101 -0
- data/lib/doing.rb +7 -7
- data/lib/helpers/threaded_tests.rb +1 -0
- metadata +94 -14
- data/lib/doing/changelog.rb +0 -6
- data/lib/doing/completion/string.rb +0 -17
- data/lib/doing/items.rb +0 -196
- data/lib/doing/prompt.rb +0 -330
- data/lib/doing/wwid.rb +0 -2398
data/lib/doing/prompt.rb
DELETED
|
@@ -1,330 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Doing
|
|
4
|
-
# Terminal Prompt methods
|
|
5
|
-
module Prompt
|
|
6
|
-
class << self
|
|
7
|
-
attr_writer :force_answer, :default_answer
|
|
8
|
-
|
|
9
|
-
include Color
|
|
10
|
-
|
|
11
|
-
##
|
|
12
|
-
## Clear the terminal screen
|
|
13
|
-
##
|
|
14
|
-
def clear_screen(msg = nil)
|
|
15
|
-
puts "\e[H\e[2J" if STDOUT.tty?
|
|
16
|
-
puts msg if msg.good?
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def force_answer
|
|
20
|
-
@force_answer ||= nil
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def default_answer
|
|
24
|
-
@default_answer ||= false
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def enter_text(prompt, default_response: '')
|
|
28
|
-
$stdin.reopen('/dev/tty')
|
|
29
|
-
return default_response if @default_answer
|
|
30
|
-
|
|
31
|
-
print "#{yellow(prompt).sub(/:?$/, ':')} #{reset}"
|
|
32
|
-
$stdin.gets.strip
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def read_line(prompt: 'Enter text', completions: [], default_response: '')
|
|
36
|
-
$stdin.reopen('/dev/tty')
|
|
37
|
-
return default_response if @default_answer
|
|
38
|
-
|
|
39
|
-
unless completions.empty?
|
|
40
|
-
completions.sort!
|
|
41
|
-
comp = proc { |s| completions.grep(/^#{Regexp.escape(s)}/) }
|
|
42
|
-
Readline.completion_append_character = ' '
|
|
43
|
-
Readline.completion_proc = comp
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
begin
|
|
47
|
-
Readline.readline("#{yellow(prompt).sub(/:?$/, ':')} #{reset}", true).strip
|
|
48
|
-
rescue Interrupt
|
|
49
|
-
raise UserCancelled
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def read_lines(prompt: 'Enter text', completions: [], default_response: '')
|
|
54
|
-
$stdin.reopen('/dev/tty')
|
|
55
|
-
return default_response if @default_answer
|
|
56
|
-
|
|
57
|
-
completions.sort!
|
|
58
|
-
comp = proc { |s| completions.grep(/^#{Regexp.escape(s)}/) }
|
|
59
|
-
Readline.completion_append_character = ' '
|
|
60
|
-
Readline.completion_proc = comp
|
|
61
|
-
puts format(['%<promptcolor>s%<prompt>s %<textcolor>sEnter a blank line',
|
|
62
|
-
'(%<keycolor>sreturn twice%<textcolor>s)',
|
|
63
|
-
'to end editing and save,',
|
|
64
|
-
'%<keycolor>sCTRL-C%<textcolor>s to cancel%<reset>s'].join(' '),
|
|
65
|
-
{ promptcolor: boldgreen, prompt: prompt.sub(/:?$/, ':'),
|
|
66
|
-
textcolor: yellow, keycolor: boldwhite, reset: reset })
|
|
67
|
-
|
|
68
|
-
res = []
|
|
69
|
-
|
|
70
|
-
begin
|
|
71
|
-
while (line = Readline.readline('> ', true))
|
|
72
|
-
break if line.strip.empty?
|
|
73
|
-
|
|
74
|
-
res << line.chomp
|
|
75
|
-
end
|
|
76
|
-
rescue Interrupt
|
|
77
|
-
raise UserCancelled
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
res.join("\n").strip
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
def request_lines(prompt: 'Enter text', default_response: '')
|
|
84
|
-
$stdin.reopen('/dev/tty')
|
|
85
|
-
return default_response if @default_answer
|
|
86
|
-
|
|
87
|
-
ask_note = []
|
|
88
|
-
reader = TTY::Reader.new(interrupt: -> { raise Errors::UserCancelled }, track_history: false)
|
|
89
|
-
puts "#{boldgreen(prompt.sub(/:?$/, ':'))} #{yellow('Hit return for a new line, ')}#{boldwhite('enter a blank line (')}#{boldyellow('return twice')}#{boldwhite(') to end editing')}"
|
|
90
|
-
loop do
|
|
91
|
-
res = reader.read_line(green('> '))
|
|
92
|
-
break if res.strip.empty?
|
|
93
|
-
|
|
94
|
-
ask_note.push(res)
|
|
95
|
-
end
|
|
96
|
-
ask_note.join("\n").strip
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
##
|
|
100
|
-
## Ask a yes or no question in the terminal
|
|
101
|
-
##
|
|
102
|
-
## @param question [String] The question
|
|
103
|
-
## to ask
|
|
104
|
-
## @param default_response (Bool) default
|
|
105
|
-
## response if no input
|
|
106
|
-
##
|
|
107
|
-
## @return (Bool) yes or no
|
|
108
|
-
##
|
|
109
|
-
def yn(question, default_response: false)
|
|
110
|
-
return @force_answer == :yes ? true : false unless @force_answer.nil?
|
|
111
|
-
|
|
112
|
-
$stdin.reopen('/dev/tty')
|
|
113
|
-
|
|
114
|
-
default = if default_response.is_a?(String)
|
|
115
|
-
default_response =~ /y/i ? true : false
|
|
116
|
-
else
|
|
117
|
-
default_response
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
# if global --default is set, answer default
|
|
121
|
-
return default if @default_answer
|
|
122
|
-
|
|
123
|
-
# if this isn't an interactive shell, answer default
|
|
124
|
-
return default unless $stdout.isatty
|
|
125
|
-
|
|
126
|
-
# clear the buffer
|
|
127
|
-
if ARGV&.length
|
|
128
|
-
ARGV.length.times do
|
|
129
|
-
ARGV.shift
|
|
130
|
-
end
|
|
131
|
-
end
|
|
132
|
-
system 'stty cbreak'
|
|
133
|
-
|
|
134
|
-
cw = white
|
|
135
|
-
cbw = boldwhite
|
|
136
|
-
cbg = boldgreen
|
|
137
|
-
cd = Color.default
|
|
138
|
-
|
|
139
|
-
options = unless default.nil?
|
|
140
|
-
"#{cw}[#{default ? "#{cbg}Y#{cw}/#{cbw}n" : "#{cbw}y#{cw}/#{cbg}N"}#{cw}]#{cd}"
|
|
141
|
-
else
|
|
142
|
-
"#{cw}[#{cbw}y#{cw}/#{cbw}n#{cw}]#{cd}"
|
|
143
|
-
end
|
|
144
|
-
$stdout.syswrite "#{cbw}#{question.sub(/\?$/, '')} #{options}#{cbw}?#{cd} "
|
|
145
|
-
res = $stdin.sysread 1
|
|
146
|
-
puts
|
|
147
|
-
system 'stty cooked'
|
|
148
|
-
|
|
149
|
-
res.chomp!
|
|
150
|
-
res.downcase!
|
|
151
|
-
|
|
152
|
-
return default if res.empty?
|
|
153
|
-
|
|
154
|
-
res =~ /y/i ? true : false
|
|
155
|
-
end
|
|
156
|
-
|
|
157
|
-
def fzf
|
|
158
|
-
@fzf ||= install_fzf
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
def uninstall_fzf
|
|
162
|
-
fzf_bin = File.join(File.dirname(__FILE__), '../helpers/fzf/bin/fzf')
|
|
163
|
-
FileUtils.rm_f(fzf_bin) if File.exist?(fzf_bin)
|
|
164
|
-
Doing.logger.warn('fzf:', "removed #{fzf_bin}")
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
def which_fzf
|
|
168
|
-
fzf_dir = File.join(File.dirname(__FILE__), '../helpers/fzf')
|
|
169
|
-
fzf_bin = File.join(fzf_dir, 'bin/fzf')
|
|
170
|
-
return fzf_bin if File.exist?(fzf_bin)
|
|
171
|
-
|
|
172
|
-
Doing.logger.debug('fzf:', 'Using user-installed fzf')
|
|
173
|
-
TTY::Which.which('fzf')
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
def silence_std(file = '/dev/null')
|
|
177
|
-
$stdout = File.new(file, 'w')
|
|
178
|
-
$stderr = File.new(file, 'w')
|
|
179
|
-
end
|
|
180
|
-
|
|
181
|
-
def restore_std
|
|
182
|
-
$stdout = STDOUT
|
|
183
|
-
$stderr = STDERR
|
|
184
|
-
end
|
|
185
|
-
|
|
186
|
-
def install_fzf(force: false)
|
|
187
|
-
if force
|
|
188
|
-
uninstall_fzf
|
|
189
|
-
elsif which_fzf
|
|
190
|
-
return which_fzf
|
|
191
|
-
end
|
|
192
|
-
|
|
193
|
-
fzf_dir = File.join(File.dirname(__FILE__), '../helpers/fzf')
|
|
194
|
-
FileUtils.mkdir_p(fzf_dir) unless File.directory?(fzf_dir)
|
|
195
|
-
fzf_bin = File.join(fzf_dir, 'bin/fzf')
|
|
196
|
-
return fzf_bin if File.exist?(fzf_bin)
|
|
197
|
-
|
|
198
|
-
prev_level = Doing.logger.level
|
|
199
|
-
Doing.logger.adjust_verbosity({ log_level: :info })
|
|
200
|
-
Doing.logger.log_now(:warn, 'fzf:', 'Compiling and installing fzf -- this will only happen once')
|
|
201
|
-
Doing.logger.log_now(:warn, 'fzf:', 'fzf is copyright Junegunn Choi, MIT License <https://github.com/junegunn/fzf/blob/master/LICENSE>')
|
|
202
|
-
|
|
203
|
-
silence_std
|
|
204
|
-
`'#{fzf_dir}/install' --bin --no-key-bindings --no-completion --no-update-rc --no-bash --no-zsh --no-fish &> /dev/null`
|
|
205
|
-
unless File.exist?(fzf_bin)
|
|
206
|
-
restore_std
|
|
207
|
-
Doing.logger.log_now(:warn, 'Error installing, trying again as root')
|
|
208
|
-
silence_std
|
|
209
|
-
`sudo '#{fzf_dir}/install' --bin --no-key-bindings --no-completion --no-update-rc --no-bash --no-zsh --no-fish &> /dev/null`
|
|
210
|
-
end
|
|
211
|
-
restore_std
|
|
212
|
-
unless File.exist?(fzf_bin)
|
|
213
|
-
Doing.logger.error('fzf:', 'unable to install fzf. You can install manually and Doing will use the system version.')
|
|
214
|
-
Doing.logger.error('fzf:', 'see https://github.com/junegunn/fzf#installation')
|
|
215
|
-
raise RuntimeError.new('Error installing fzf, please report at https://github.com/ttscoff/doing/issues')
|
|
216
|
-
end
|
|
217
|
-
|
|
218
|
-
Doing.logger.info('fzf:', "installed to #{fzf}")
|
|
219
|
-
Doing.logger.adjust_verbosity({ log_level: prev_level })
|
|
220
|
-
fzf_bin
|
|
221
|
-
end
|
|
222
|
-
|
|
223
|
-
##
|
|
224
|
-
## Generate a menu of options and allow user selection
|
|
225
|
-
##
|
|
226
|
-
## @return [String] The selected option
|
|
227
|
-
##
|
|
228
|
-
def choose_from(options, prompt: 'Make a selection: ', multiple: false, sorted: true, fzf_args: [])
|
|
229
|
-
return nil unless $stdout.isatty
|
|
230
|
-
|
|
231
|
-
# fzf_args << '-1' # User is expecting a menu, and even if only one it seves as confirmation
|
|
232
|
-
default_args = []
|
|
233
|
-
default_args << %(--prompt="#{prompt}")
|
|
234
|
-
default_args << "--height=#{options.count + 2}"
|
|
235
|
-
default_args << '--info=inline'
|
|
236
|
-
default_args << '--multi' if multiple
|
|
237
|
-
header = "esc: cancel,#{multiple ? ' tab: multi-select, ctrl-a: select all,' : ''} return: confirm"
|
|
238
|
-
default_args << %(--header="#{header}")
|
|
239
|
-
default_args.concat(fzf_args)
|
|
240
|
-
options.sort! if sorted
|
|
241
|
-
|
|
242
|
-
res = `echo #{Shellwords.escape(options.join("\n"))}|#{fzf} #{default_args.join(' ')}`
|
|
243
|
-
return false if res.strip.size.zero?
|
|
244
|
-
|
|
245
|
-
res
|
|
246
|
-
end
|
|
247
|
-
|
|
248
|
-
##
|
|
249
|
-
## Create an interactive menu to select from a set of Items
|
|
250
|
-
##
|
|
251
|
-
## @param items [Array] list of items
|
|
252
|
-
## @param opt Additional options
|
|
253
|
-
##
|
|
254
|
-
## @option opt [Boolean] :include_section Include section name for each item in menu
|
|
255
|
-
## @option opt [String] :header A custom header string
|
|
256
|
-
## @option opt [String] :prompt A custom prompt string
|
|
257
|
-
## @option opt [String] :query Initial query
|
|
258
|
-
## @option opt [Boolean] :show_if_single Show menu even if there's only one option
|
|
259
|
-
## @option opt [Boolean] :menu Show menu
|
|
260
|
-
## @option opt [Boolean] :sort Sort options
|
|
261
|
-
## @option opt [Boolean] :multiple Allow multiple selections
|
|
262
|
-
## @option opt [Symbol] :case (:sensitive, :ignore, :smart)
|
|
263
|
-
##
|
|
264
|
-
def choose_from_items(items, **opt)
|
|
265
|
-
return items unless $stdout.isatty
|
|
266
|
-
|
|
267
|
-
return nil unless items.count.positive?
|
|
268
|
-
|
|
269
|
-
case_sensitive = opt.fetch(:case, :smart).normalize_case
|
|
270
|
-
header = opt.fetch(:header, 'Arrows: navigate, tab: mark for selection, ctrl-a: select all, enter: commit')
|
|
271
|
-
prompt = opt.fetch(:prompt, 'Select entries to act on > ')
|
|
272
|
-
query = opt.fetch(:query) { opt.fetch(:search, '') }
|
|
273
|
-
include_section = opt.fetch(:include_section, false)
|
|
274
|
-
|
|
275
|
-
pad = items.length.to_s.length
|
|
276
|
-
options = items.map.with_index do |item, i|
|
|
277
|
-
out = [
|
|
278
|
-
format("%#{pad}d", i),
|
|
279
|
-
') ',
|
|
280
|
-
format('%16s', item.date.strftime('%Y-%m-%d %H:%M')),
|
|
281
|
-
' | ',
|
|
282
|
-
item.title
|
|
283
|
-
]
|
|
284
|
-
if include_section
|
|
285
|
-
out.concat([
|
|
286
|
-
' (',
|
|
287
|
-
item.section,
|
|
288
|
-
') '
|
|
289
|
-
])
|
|
290
|
-
end
|
|
291
|
-
out.join('')
|
|
292
|
-
end
|
|
293
|
-
|
|
294
|
-
fzf_args = [
|
|
295
|
-
%(--header="#{header}"),
|
|
296
|
-
%(--prompt="#{prompt.sub(/ *$/, ' ')}"),
|
|
297
|
-
opt.fetch(:multiple) ? '--multi' : '--no-multi',
|
|
298
|
-
'-0',
|
|
299
|
-
'--bind ctrl-a:select-all',
|
|
300
|
-
%(-q "#{query}"),
|
|
301
|
-
'--info=inline'
|
|
302
|
-
]
|
|
303
|
-
fzf_args.push('-1') unless opt.fetch(:show_if_single, false)
|
|
304
|
-
fzf_args << case case_sensitive
|
|
305
|
-
when :sensitive
|
|
306
|
-
'+i'
|
|
307
|
-
when :ignore
|
|
308
|
-
'-i'
|
|
309
|
-
end
|
|
310
|
-
fzf_args << '-e' if opt.fetch(:exact, false)
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
unless opt.fetch(:menu)
|
|
314
|
-
raise InvalidArgument, "Can't skip menu when no query is provided" unless query && !query.empty?
|
|
315
|
-
|
|
316
|
-
fzf_args.concat([%(--filter="#{query}"), opt.fetch(:sort) ? '' : '--no-sort'])
|
|
317
|
-
end
|
|
318
|
-
|
|
319
|
-
res = `echo #{Shellwords.escape(options.join("\n"))}|#{fzf} #{fzf_args.join(' ')}`
|
|
320
|
-
selected = []
|
|
321
|
-
res.split(/\n/).each do |item|
|
|
322
|
-
idx = item.match(/^ *(\d+)\)/)[1].to_i
|
|
323
|
-
selected.push(items[idx])
|
|
324
|
-
end
|
|
325
|
-
|
|
326
|
-
opt.fetch(:multiple) ? selected : selected[0]
|
|
327
|
-
end
|
|
328
|
-
end
|
|
329
|
-
end
|
|
330
|
-
end
|