Class: Marvi::Renderer::Curses::FilePicker

Inherits:
Object
  • Object
show all
Defined in:
lib/marvi/renderer/curses/file_picker.rb

Overview

Candidate discovery, filtering, and tab-completion behind the o (open file) picker. Pure filesystem/string logic with no curses calls, so it can be tested headlessly.

Two modes, decided per keystroke from the typed input:

  • list mode: filter documents scanned under the base directory; every whitespace-separated term must match somewhere in the path.
  • path mode (input starts with /, ~, ./ or ../): complete the typed path against the filesystem, shell-style.

Constant Summary collapse

SCAN_GLOB =
"**/*.{md,markdown,mdown,mkd,txt}"
MAX_FILES =
2000

Instance Method Summary collapse

Constructor Details

#initialize(base_dir = Dir.pwd) ⇒ FilePicker

Returns a new instance of FilePicker.



19
20
21
# File 'lib/marvi/renderer/curses/file_picker.rb', line 19

def initialize(base_dir = Dir.pwd)
  @base_dir = base_dir
end

Instance Method Details

#all_filesObject

Documents under the base directory, capped to keep the picker snappy in huge trees. Dir.glob skips hidden directories by default.



25
26
27
28
29
30
# File 'lib/marvi/renderer/curses/file_picker.rb', line 25

def all_files
  @all_files ||= Dir.glob(SCAN_GLOB, base: @base_dir)
    .reject { |path| File.directory?(File.join(@base_dir, path)) }
    .sort
    .first(MAX_FILES)
end

#candidates(input) ⇒ Object



36
37
38
# File 'lib/marvi/renderer/curses/file_picker.rb', line 36

def candidates(input)
  path_mode?(input) ? path_candidates(input) : filter(input)
end

#complete(input) ⇒ Object

Longest unambiguous extension of the typed path. A lone directory match gains a trailing slash so the next Tab descends into it.



52
53
54
55
56
57
58
# File 'lib/marvi/renderer/curses/file_picker.rb', line 52

def complete(input)
  cands = path_candidates(input)
  return input if cands.empty?

  prefix = common_prefix(cands)
  (prefix.length > input.length) ? prefix : input
end

#filter(input) ⇒ Object



40
41
42
43
44
45
46
47
48
# File 'lib/marvi/renderer/curses/file_picker.rb', line 40

def filter(input)
  terms = input.downcase.split
  return all_files if terms.empty?

  all_files.select do |path|
    haystack = path.downcase
    terms.all? { |term| haystack.include?(term) }
  end
end

#path_mode?(input) ⇒ Boolean

Returns:

  • (Boolean)


32
33
34
# File 'lib/marvi/renderer/curses/file_picker.rb', line 32

def path_mode?(input)
  input.start_with?("/", "~", "./", "../")
end