Module: Booker::Picker
- Defined in:
- lib/booker/picker.rb
Constant Summary collapse
- DEFAULT =
--delimiter/--with-nth hide the id column: it is noise on screen, and hiding it also takes it out of the match, so typing "2_" does not pull up every bookmark from the second source. --no-sort keeps booker's ordering.
deliberately no --multi: under it fzf reads tab as "mark this and move down" - the one key booker's own completion trained everybody to press - so a couple of taps to scroll would mark bookmarks silently and open them all on the next return.
a plain array literal, not %w: neither the real tab in the delimiter nor the prompt's trailing space survives %w
[ "fzf", "--height=40%", "--reverse", "--no-sort", "--delimiter=\t", "--with-nth=2..", "--prompt=bookmark> " ].freeze
Class Attribute Summary collapse
-
.enabled ⇒ Object
writeonly
nil means decide from the environment; true or false forces it.
Class Method Summary collapse
-
.command ⇒ Object
the configured picker, or fzf when it is on PATH.
- .enabled? ⇒ Boolean
- .executable?(path) ⇒ Boolean
-
.reset! ⇒ Object
#enabled? and #select both ask for the command, so an interactive run worked it out twice; both memos outlive an example, so the suite drops them between them.
-
.select(lines) ⇒ Object
candidates in, the id of what was picked out - or nil for every other outcome: cancelled, nothing matched, no usable finder.
-
.tty? ⇒ Boolean
both ends, not just stdout:
booker foo | lessstill writes to a terminal, but there is no keyboard on the other side of the pipe. -
.which(bin) ⇒ Object
no subshell to
which: this runs before every interactive invocation, and the answer is a PATH scan booker can do itself.
Class Attribute Details
.enabled=(value) ⇒ Object (writeonly)
nil means decide from the environment; true or false forces it. the specs set this, and so does --list - same shape as Colors.enabled=
37 38 39 |
# File 'lib/booker/picker.rb', line 37 def enabled=(value) @enabled = value end |
Class Method Details
.command ⇒ Object
the configured picker, or fzf when it is on PATH. nil when neither is installed, which is the signal to stay on booker's pre-picker behavior
63 64 65 66 67 68 |
# File 'lib/booker/picker.rb', line 63 def command return @command if @command_known @command_known = true @command = resolve_command end |
.enabled? ⇒ Boolean
48 49 50 51 52 53 54 55 |
# File 'lib/booker/picker.rb', line 48 def enabled? return @enabled unless @enabled.nil? # tty? first: it is a pair of syscalls, where #command reads and parses # ~/.booker.yml, and a run that is not interactive never needs to know # whether a finder is installed tty? && !command.nil? end |
.executable?(path) ⇒ Boolean
84 |
# File 'lib/booker/picker.rb', line 84 def executable?(path) = File.executable?(path) && !File.directory?(path) |
.reset! ⇒ Object
#enabled? and #select both ask for the command, so an interactive run worked it out twice; both memos outlive an example, so the suite drops them between them
42 43 44 45 46 |
# File 'lib/booker/picker.rb', line 42 def reset! @enabled = nil @command = nil @command_known = false end |
.select(lines) ⇒ Object
candidates in, the id of what was picked out - or nil for every other outcome: cancelled, nothing matched, no usable finder. one id and not a list, since dropping --multi above only covers the default finder and not one named in ~/.booker.yml
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/booker/picker.rb', line 90 def select(lines) argv = command return nil if argv.nil? io = IO.popen(argv, "r+") feed(io, lines) chosen = io.read io.close # fzf: 0 selected, 1 no match, 2 error, 130 interrupted. a selection # that came back empty counts as a cancel too - that pairs with the # finder exiting 0 the moment it is closed, before it has read anything return nil unless Process.last_status&.exitstatus&.zero? id = chosen.lines.first.to_s.split("\t", 2).first.to_s.strip id unless id.empty? rescue Errno::ENOENT, Interrupt # the binary went away between the PATH check and the spawn, or the # user interrupted booker itself rather than the finder nil end |
.tty? ⇒ Boolean
both ends, not just stdout: booker foo | less still writes to a
terminal, but there is no keyboard on the other side of the pipe
59 |
# File 'lib/booker/picker.rb', line 59 def tty? = $stdin.tty? && $stdout.tty? |
.which(bin) ⇒ Object
no subshell to which: this runs before every interactive invocation,
and the answer is a PATH scan booker can do itself. a name carrying a
separator is a path, exactly as execvp treats it
73 74 75 76 77 78 79 80 81 82 |
# File 'lib/booker/picker.rb', line 73 def which(bin) return executable?(bin) ? bin : nil if bin.include?(File::SEPARATOR) # lazy: the answer is usually in the first few entries, and eagerly # joining all forty PATH directories to throw them away is the one # thing a hand written which is here to avoid ENV.fetch("PATH", "").split(File::PATH_SEPARATOR).lazy .map { |dir| File.join(dir, bin) } .find { |path| executable?(path) } end |