From f34007c65a247c662ffbc76e44dc80632cce7017 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 11 Jul 2025 21:47:11 +0530 Subject: [PATCH] Handle multiple save files when entering save file names --- kittens/choose_files/footer.go | 19 ++++++- kittens/choose_files/main.go | 11 ++-- kittens/choose_files/save-file.go | 83 ++++++++++++++++++++----------- 3 files changed, 76 insertions(+), 37 deletions(-) diff --git a/kittens/choose_files/footer.go b/kittens/choose_files/footer.go index 31fe1c28a..90f484011 100644 --- a/kittens/choose_files/footer.go +++ b/kittens/choose_files/footer.go @@ -24,6 +24,12 @@ func (h *Handler) draw_footer() (num_lines int, err error) { lines := []string{} screen_width := h.screen_size.width sctx := style.Context{AllowEscapeCodes: true} + if h.state.screen == SAVE_FILE { + m := h.state.filter_map + h.state.filter_map = nil + defer func() { h.state.filter_map = m }() + } + if len(h.state.filter_map)+len(h.state.selections) > 0 { buf := strings.Builder{} pos := 0 @@ -36,10 +42,16 @@ func (h *Handler) draw_footer() (num_lines int, err error) { lines = append(lines, buf.String()) pos = 0 buf.Reset() + } else { + buf.WriteString(presep) + pos += sz } - buf.WriteString(presep) - pos += sz sz = wcswidth.Stringwidth(text) + if sz+pos >= screen_width { + lines = append(lines, buf.String()) + pos = 0 + buf.Reset() + } if sfunc != nil { text = sfunc(text) } @@ -94,6 +106,9 @@ func (h *Handler) draw_footer() (num_lines int, err error) { } if len(lines) > 0 { h.lp.MoveCursorTo(1, h.screen_size.height-len(lines)+1) + if h.state.screen == SAVE_FILE { + h.lp.ClearToEndOfScreen() + } h.lp.QueueWriteString(strings.Join(lines, "\r\n")) } return len(lines), err diff --git a/kittens/choose_files/main.go b/kittens/choose_files/main.go index 8fe9a3827..d7065a79c 100644 --- a/kittens/choose_files/main.go +++ b/kittens/choose_files/main.go @@ -120,12 +120,11 @@ type State struct { global_ignores ignorefiles.IgnoreFile keyboard_shortcuts []*config.KeyAction - save_file_cdir string - selections []string - current_idx CollectionIndex - last_render render_state - mouse_state tui.MouseState - redraw_needed bool + selections []string + current_idx CollectionIndex + last_render render_state + mouse_state tui.MouseState + redraw_needed bool } func (s State) ShowHidden() bool { return s.show_hidden } diff --git a/kittens/choose_files/save-file.go b/kittens/choose_files/save-file.go index c518a568c..f59dcf065 100644 --- a/kittens/choose_files/save-file.go +++ b/kittens/choose_files/save-file.go @@ -19,12 +19,18 @@ func (h *Handler) complete_save_prompt(before_cursor, after_cursor string) *cli. if idx := strings.Index(path, string(os.PathSeparator)); idx > -1 { prefix = filepath.Dir(path) + string(os.PathSeparator) } - if !filepath.IsAbs(path) { - path = filepath.Join(h.state.CurrentDir(), path) - } - dir := filepath.Dir(path) - if strings.HasSuffix(before_cursor, string(os.PathSeparator)) { + dir := "" + if path == "" { + path = h.state.CurrentDir() dir = path + } else { + if !filepath.IsAbs(path) { + path = filepath.Join(h.state.CurrentDir(), path) + } + dir = filepath.Dir(path) + if strings.HasSuffix(before_cursor, string(os.PathSeparator)) { + dir = path + } } entries, err := os.ReadDir(dir) if err != nil { @@ -59,57 +65,76 @@ func (h *Handler) current_save_file_path() string { if ans != "" { ans = utils.Expanduser(ans) if !filepath.IsAbs(ans) { - ans = filepath.Join(h.state.save_file_cdir, ans) + ans = filepath.Join(h.state.CurrentDir(), ans) } } return ans } func (h *Handler) save_file_name_handle_key(ev *loop.KeyEvent) (err error) { - switch { - case ev.MatchesPressOrRepeat("esc") || ev.MatchesPressOrRepeat("ctrl+c"): - h.state.selections = nil - h.state.screen = NORMAL + ac := h.shortcut_tracker.Match(ev, h.state.keyboard_shortcuts) + if ac != nil { + switch ac.Name { + case "accept": + if p := h.current_save_file_path(); p != "" { + h.state.selections = append(h.state.selections, p) + h.lp.Quit(0) + } else { + h.lp.Beep() + } + ev.Handled = true + return nil + case "select": + if p := h.current_save_file_path(); p != "" { + h.state.selections = append(h.state.selections, p) + } + ev.Handled = true + h.rl.ResetText() + return h.draw_screen() + case "quit": + h.state.screen = NORMAL + ev.Handled = true + return h.draw_screen() + } + } + if err = h.rl.OnKeyEvent(ev); err == nil { err = h.draw_screen() - case ev.MatchesPressOrRepeat("enter"): - if p := h.current_save_file_path(); p != "" { - h.state.selections = []string{p} - h.lp.Quit(0) - } else { - h.lp.Beep() - } - default: - if err = h.rl.OnKeyEvent(ev); err == nil { - err = h.draw_screen() - } } return } -func (h *Handler) initialize_save_file_name(use_fname_when_no_selections string) { +func (h *Handler) initialize_save_file_name(fname string) { h.state.screen = SAVE_FILE h.rl.ResetText() - cdir := h.state.CurrentDir() - fname := use_fname_when_no_selections - if len(h.state.selections) > 0 { + if len(h.state.selections) > 0 && fname == "" { if q, err := filepath.Abs(h.state.selections[0]); err == nil { if s, err := os.Stat(q); err == nil { if s.IsDir() == h.state.mode.OnlyDirs() { - cdir = filepath.Dir(q) - fname = filepath.Base(q) + if fname, err = filepath.Rel(h.state.CurrentDir(), q); err != nil { + fname = q + } } } } } h.rl.SetText(fname) - h.state.save_file_cdir = cdir } func (h *Handler) draw_save_file_name_screen() (err error) { + h.lp.AllowLineWrapping(true) desc := utils.IfElse(h.state.mode == SELECT_SAVE_FILE, "file", "directory") h.lp.Println("Enter the name of the", desc, "below, relative to:") - h.lp.Println(h.lp.SprintStyled("fg=green", h.state.save_file_cdir)) + h.lp.Println(h.lp.SprintStyled("fg=green", h.state.CurrentDir())) + if h.state.mode.AllowsMultipleSelection() { + h.lp.Println("Use shift+enter (or whatever you mapped the select action to) to enter multiple filenames") + } h.lp.Println() h.rl.RedrawNonAtomic() + h.lp.AllowLineWrapping(false) + if len(h.state.selections) > 0 { + h.lp.SaveCursorPosition() + h.draw_footer() + h.lp.RestoreCursorPosition() + } return }