diff --git a/kittens/choose_files/main.go b/kittens/choose_files/main.go index a2f10cbf1..d4dc3e944 100644 --- a/kittens/choose_files/main.go +++ b/kittens/choose_files/main.go @@ -15,8 +15,7 @@ import ( "github.com/kovidgoyal/kitty/tools/utils" ) -// TODO: Comboboxes, multifile selections, save file name, file/dir modes. Make -// window title conditional on mode +// TODO: Comboboxes, multifile selections, save file name, file/dir modes var _ = fmt.Print var debugprintln = tty.DebugPrintln @@ -27,6 +26,54 @@ type ScorePattern struct { val float64 } +type Mode int + +const ( + SELECT_SINGLE_FILE Mode = iota + SELECT_MULTIPLE_FILES + SELECT_SAVE_FILE + SELECT_SAVE_DIR + SELECT_SINGLE_DIR + SELECT_MULTIPLE_DIRS + SELECT_SAVE_DIR_FOR_FILES // select a dir for saving one or more pre-sent filenames, must be an existing one +) + +func (m Mode) AllowsMultipleSelection() bool { + switch m { + case SELECT_MULTIPLE_FILES, SELECT_MULTIPLE_DIRS: + return true + } + return false +} + +func (m Mode) OnlyDirs() bool { + switch m { + case SELECT_SINGLE_DIR, SELECT_MULTIPLE_DIRS, SELECT_SAVE_DIR, SELECT_SAVE_DIR_FOR_FILES: + return true + } + return false +} + +func (m Mode) WindowTitle() string { + switch m { + case SELECT_SINGLE_FILE: + return "Choose an existing file" + case SELECT_MULTIPLE_FILES: + return "Choose one or more existing files" + case SELECT_SAVE_FILE: + return "Choose a file to save" + case SELECT_SAVE_DIR: + return "Choose a directory to save" + case SELECT_SINGLE_DIR: + return "Choose an existing directory" + case SELECT_MULTIPLE_DIRS: + return "Choose one or more directories" + case SELECT_SAVE_DIR_FOR_FILES: + return "Choose a directory to save multiple files in" + } + return "" +} + type State struct { base_dir string current_dir string @@ -34,6 +81,8 @@ type State struct { multiselect bool score_patterns []ScorePattern search_text string + mode Mode + window_title string current_idx int num_of_matches_at_last_render int @@ -67,6 +116,12 @@ func (s *State) SetCurrentIndex(val int) { s.current_idx = max(0, val) } func (s State) CurrentDir() string { return utils.IfElse(s.current_dir == "", s.BaseDir(), s.current_dir) } +func (s State) WindowTitle() string { + if s.window_title == "" { + return s.mode.WindowTitle() + } + return s.window_title +} type ScreenSize struct { width, height, cell_width, cell_height, width_px, height_px int @@ -81,7 +136,7 @@ type Handler struct { func (h *Handler) draw_screen() (err error) { matches, in_progress := h.get_results() - h.lp.SetWindowTitle("Select a file") // TODO: make this conditional on mode + h.lp.SetWindowTitle(h.state.WindowTitle()) h.lp.StartAtomicUpdate() defer h.lp.EndAtomicUpdate() h.lp.ClearScreen() @@ -175,7 +230,7 @@ func sub(a, b float64) float64 { return a - b } func add(a, b float64) float64 { return a + b } func div(a, b float64) float64 { return a / b } -func (h *Handler) set_state_from_config(conf *Config) (err error) { +func (h *Handler) set_state_from_config(conf *Config, opts *Options) (err error) { h.state = State{} fmap := map[string]func(float64, float64) float64{ "*=": mult, "+=": add, "-=": sub, "/=": div} @@ -196,6 +251,24 @@ func (h *Handler) set_state_from_config(conf *Config) (err error) { } } + switch opts.Mode { + case "file": + h.state.mode = SELECT_SINGLE_FILE + case "files": + h.state.mode = SELECT_MULTIPLE_FILES + case "save-file": + h.state.mode = SELECT_SAVE_FILE + case "dir": + h.state.mode = SELECT_SINGLE_DIR + case "dirs": + h.state.mode = SELECT_MULTIPLE_DIRS + case "save-dir": + h.state.mode = SELECT_SAVE_DIR + case "dir-for-files": + h.state.mode = SELECT_SAVE_DIR_FOR_FILES + default: + h.state.mode = SELECT_SINGLE_FILE + } return } @@ -211,7 +284,7 @@ func main(_ *cli.Command, opts *Options, args []string) (rc int, err error) { return 1, err } handler := Handler{lp: lp} - if err = handler.set_state_from_config(conf); err != nil { + if err = handler.set_state_from_config(conf, opts); err != nil { return 1, err } switch len(args) { diff --git a/kittens/choose_files/main.py b/kittens/choose_files/main.py index 7a00418ca..cd4565493 100644 --- a/kittens/choose_files/main.py +++ b/kittens/choose_files/main.py @@ -48,6 +48,11 @@ completion=type:file ext:conf group:"Config files" kwds:none,NONE {config_help} +--mode +type=choices +choices=file,files,save-file,dir,save-dir,dirs,dir-for-files +default=file +The type of object(s) to select '''.format(config_help=CONFIG_HELP.format(conf_name='diff', appname=appname)).format diff --git a/kittens/choose_files/scan.go b/kittens/choose_files/scan.go index fb4a22c47..db7b22b38 100644 --- a/kittens/choose_files/scan.go +++ b/kittens/choose_files/scan.go @@ -36,6 +36,7 @@ type ScanCache struct { mutex sync.Mutex root_dir, search_text string in_progress bool + only_dirs bool matches []*ResultItem } @@ -158,6 +159,10 @@ func (sc *ScanCache) scan_dir(abspath string, patterns []string, positions []pos } is_last := pattern == "" || len(patterns) <= 1 for i, n := range names { + e := entries[i] + if sc.only_dirs && !e.IsDir() { + continue + } child_abspath := filepath.Join(abspath, n) if pattern == "" || scores[i].Score > 0 { npos[len(positions)] = pos_in_name{name: n, positions: scores[i].Positions} @@ -165,7 +170,7 @@ func (sc *ScanCache) scan_dir(abspath string, patterns []string, positions []pos r := &ResultItem{score: score + scores[i].Score, dir_entry: entries[i], abspath: child_abspath} r.finalize(npos) ans = append(ans, r) - } else if entries[i].IsDir() { + } else if e.IsDir() { ans = append(ans, sc.scan_dir(child_abspath, patterns[1:], npos, scores[i].Score+score)...) } } @@ -217,23 +222,25 @@ func (h *Handler) get_results() (ans []*ResultItem, in_progress bool) { } cd := h.state.CurrentDir() st := h.state.SearchText() + only_dirs := h.state.mode.OnlyDirs() if st != "" { st = filepath.Clean(st) } - if sc.root_dir == cd && sc.search_text == st { + if sc.root_dir == cd && sc.search_text == st && sc.only_dirs == only_dirs { return sc.matches, sc.in_progress } sc.in_progress = true sc.matches = nil sc.root_dir = cd sc.search_text = st + sc.only_dirs = only_dirs sp := h.state.ScorePatterns() go func() { defer h.lp.RecoverFromPanicInGoRoutine() results := sc.scan(cd, st, sp) sc.mutex.Lock() defer sc.mutex.Unlock() - if cd == sc.root_dir && st == sc.search_text { + if cd == sc.root_dir && st == sc.search_text && sc.only_dirs == only_dirs { sc.matches = results sc.in_progress = false h.lp.WakeupMainThread()