Selectrum: Add dedicated command to move up a directory in find-file

Created on 20 Mar 2021  路  11Comments  路  Source: raxod502/selectrum

Ideally I'd like a command that just pushes me up from the cwd. Often when I'm at the end of a path eg: ~/foo/bar-baz bag boom/ my intention with backward-kill-word is to move to the previous directory (~/foo), not to to move to a partial completion of the current directory ~/foo/bar-baz bag.

I have tried to implement this myself.

(defun selectrum-smart-backward-kill-word+ ()
    "A smart variant of `backward-kill-word' for selectrum."
    (interactive)
    (if (eq selectrum--last-command 'find-file)
        (let* ((dirname (or (file-name-directory selectrum--last-input) ""))
               (trailing-slash-p (string-suffix-p dirname "/"))
               (dirname (s-chop-suffix "/" dirname))
               (basename (file-name-nondirectory selectrum--last-input))
               (last-command (if (eq last-command 'selectrum-smart-backward-kill-word+)
                                 'kill-region
                               last-command)))
          (cond
           ((not (string-empty-p basename))
            (if selectrum-backward-kill-word-kills-basename
                (save-excursion
                  (kill-region (point-max) (- (point-max) (length basename))))
              (call-interactively #'backward-kill-word)))
           ;; Handle edge cases with last directory or magic tilde.
           ((string-equal selectrum--last-input "/")
            (kill-region (point-max) (minibuffer-prompt-end)))
           ((string-equal dirname "~")
            (kill-region (point-max) (minibuffer-prompt-end))
            (insert (file-name-directory (expand-file-name "~"))))
           ;; Kill from EOL to the cut off point for the previous directory.
           (t
            (kill-region (point-max)
                         (- (point-max)
                               (-
                                (length dirname)
                                (length (or (file-name-directory dirname) ""))
                                (if trailing-slash-p 0 -1)))))))
      (call-interactively #'backward-kill-word)))

But selectrum has some special corner cases I'm not sure how to handle. Such as when you try changing to your home directory in the middle of a file search and you retain your previous directory in {}.

Screenshot_20210320_035936

Most helpful comment

I am using this (bound to M-<backspace>) by @oantolin:

(defun mu-up-directory (arg)
  "Move up a directory ARGth times."
  (interactive "p")
  (if (string-match-p "/." (minibuffer-contents))
      (zap-up-to-char (- arg) ?/)
    (delete-minibuffer-contents)))

It is not strictly related to selectrum, but it has been serving very well.

[Thank you @oantolin, by the way.]

All 11 comments

To go up a directory you should use backward-kill-sexp (which is bound to C-M-DEL by default). I don't know where {...} is coming from in your example, probably your configuration of file-name-shadow-mode?

I am using this (bound to M-<backspace>) by @oantolin:

(defun mu-up-directory (arg)
  "Move up a directory ARGth times."
  (interactive "p")
  (if (string-match-p "/." (minibuffer-contents))
      (zap-up-to-char (- arg) ?/)
    (delete-minibuffer-contents)))

It is not strictly related to selectrum, but it has been serving very well.

[Thank you @oantolin, by the way.]

@manuel-uberti That is more universal as backward-kill-sexp as it also works outside Selectrum, note that you can also use sexp navigation to navigate between path levels in Selectrum (this also works outside Selectrum most of the time but not when your file names contain spaces).

@clemera

Thanks for pointing out backward-kill-sexp and for letting me know about shadow-file-name-mode (seems to have been turned on automatically). backward-kill-sexp works well enough but when my current path is ~/ or even just {~/} or / I get a kill-region: Text is read-only: #<buffer *Minibuf-1*> which makes me think it's trying to delete the prompt?

@mohkale Thanks, we should fix that, seems to be a side effect of the syntax table modification, the backward-kill-sexp command wants to include the space that is part of the prompt.

Instead of using backward-kill-sexp directly I rebound it to selectrum-backward-kill-sexp in #499 which avoids that behavior. Can this be closed?

@clemera Why do you modify the syntax table?

Sure. After figuring out that file-name-shadow-mode was causing the {...} I've fixed my implementation and it works how I'd like so this issue is no longer needed. Thank you.

@minad
We wanted that sexp commands also work for path names with spaces.

In the light of the present issue, would it make sense to reconsider this decision, since you cannot use the unmodified sexp commands? It seems to me that the cost of allowing path names with spaces is too high now, since it requires two steps: Modifying the syntax table and the modified backward-kill-sexp command you added. I would revert both. I think that would still be acceptable behavior and the sexp commands would still work reasonably well.

Another idea regarding the prompt editing - I always find this kind of weird and annoying that it is possible to enter the prompt.
For this reason I set minibuffer-prompt-properties to '(read-only t cursor-intangible t face minibuffer-prompt). But I wonder if it is also possible to automatically narrow the region for all the minibuffer commands such that commands can never interfere with the prompt?

Basically I just wonder if there is a more general solution to the whole problem, such that it is ensure that sexp commands work and we don't need wrappers like the one you just introduced.

Another idea regarding the prompt editing - I always find this kind of weird and annoying that it is possible to enter the prompt.
For this reason I set minibuffer-prompt-properties to '(read-only t cursor-intangible t face minibuffer-prompt). But I wonder if it is also possible to automatically narrow the region for all the minibuffer commands such that commands can never interfere with the prompt?

I also find it confusing, we have (goto-char (max (point) (minibuffer-prompt-end))) in selectrum--update to prevent this from happening when using Selectrum.

Basically I just wonder if there is a more general solution to the whole problem, such that it is ensure that sexp commands work and we don't need wrappers like the one you just introduced.

A more general solution would be nice, as long as we haven't any I wouldn't like to revert things as I like to keep the behavior we have currently available.

Was this page helpful?
0 / 5 - 0 ratings