src/choose.py (84 lines of code) (raw):
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
import curses
import os
import pickle
import sys
from typing import Dict, List, Optional
from pathpicker import logger, output, screen_control, state_files
from pathpicker.curses_api import CursesApi, CursesApiBase
from pathpicker.key_bindings import KeyBindings, read_key_bindings
from pathpicker.line_format import LineBase, LineMatch
from pathpicker.screen import CursesScreen, ScreenBase
from pathpicker.screen_flags import ScreenFlags
LOAD_SELECTION_WARNING = """
WARNING! Loading the standard input and previous selection
failed. This is probably due to a backwards compatibility issue
with upgrading PathPicker or an internal error. Please pipe
a new set of input to PathPicker to start fresh (after which
this error will go away)
"""
def do_program(
stdscr: ScreenBase,
flags: ScreenFlags,
key_bindings: Optional[KeyBindings] = None,
curses_api: Optional[CursesApiBase] = None,
line_objs: Optional[Dict[int, LineBase]] = None,
) -> None:
# curses and lineObjs get dependency injected for
# our tests, so init these if they are not provided
if not key_bindings:
key_bindings = read_key_bindings()
if not curses_api:
curses_api = CursesApi()
if not line_objs:
line_objs = get_line_objs()
output.clear_file()
logger.clear_file()
screen = screen_control.Controller(
flags, key_bindings, stdscr, line_objs, curses_api
)
screen.control()
def get_line_objs() -> Dict[int, LineBase]:
file_path = state_files.get_pickle_file_path()
try:
line_objs: Dict[int, LineBase] = pickle.load(open(file_path, "rb"))
except (OSError, KeyError, pickle.PickleError):
output.append_error(LOAD_SELECTION_WARNING)
output.append_exit()
sys.exit(1)
logger.add_event("total_num_files", len(line_objs))
selection_path = state_files.get_selection_file_path()
if os.path.isfile(selection_path):
set_selections_from_pickle(selection_path, line_objs)
matches = [
line_obj for line_obj in line_objs.values() if isinstance(line_obj, LineMatch)
]
if not matches:
output.write_to_file('echo "No lines matched!";')
output.append_exit()
sys.exit(0)
return line_objs
def set_selections_from_pickle(
selection_path: str, line_objs: Dict[int, LineBase]
) -> None:
try:
selected_indices = pickle.load(open(selection_path, "rb"))
except (OSError, KeyError, pickle.PickleError):
output.append_error(LOAD_SELECTION_WARNING)
output.append_exit()
sys.exit(1)
for index in selected_indices:
if index >= len(line_objs.items()):
error = f"Found index {index} more than total matches"
output.append_error(error)
continue
to_select = line_objs[index]
if isinstance(to_select, LineMatch):
to_select.set_select(True)
else:
error = f"Line {index} was selected but is not LineMatch"
output.append_error(error)
def main(argv: List[str]) -> int:
file_path = state_files.get_pickle_file_path()
if not os.path.exists(file_path):
print("Nothing to do!")
output.write_to_file('echo ":D";')
output.append_exit()
return 0
output.clear_file()
# we initialize our args *before* we move into curses
# so we can benefit from the default argparse
# behavior:
flags = ScreenFlags.init_from_args(argv[1:])
curses.wrapper(lambda x: do_program(CursesScreen(x), flags))
return 0
if __name__ == "__main__":
sys.exit(main(sys.argv))