File: vertico-directory.el

package info (click to toggle)
vertico 2.7-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 404 kB
  • sloc: lisp: 1,709; makefile: 7
file content (136 lines) | stat: -rw-r--r-- 5,267 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
;;; vertico-directory.el --- Ido-like directory navigation for Vertico -*- lexical-binding: t -*-

;; Copyright (C) 2021-2026 Free Software Foundation, Inc.

;; Author: Daniel Mendler <mail@daniel-mendler.de>
;; Maintainer: Daniel Mendler <mail@daniel-mendler.de>
;; Created: 2021
;; Version: 2.7
;; Package-Requires: ((emacs "29.1") (compat "30") (vertico "2.7"))
;; URL: https://github.com/minad/vertico

;; This file is part of GNU Emacs.

;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with this program.  If not, see <https://www.gnu.org/licenses/>.

;;; Commentary:

;; This package is a Vertico extension, which provides Ido-like
;; directory navigation commands.  The commands can be bound in the
;; `vertico-map'.
;;
;; (keymap-set vertico-map "RET" #'vertico-directory-enter)
;; (keymap-set vertico-map "DEL" #'vertico-directory-delete-char)
;; (keymap-set vertico-map "M-DEL" #'vertico-directory-delete-word)
;;
;; Alternatively use `vertico-directory-map' together with
;; `vertico-multiform-mode'.
;;
;; (setq vertico-multiform-categories
;;       '((file (:keymap . vertico-directory-map))))
;; (vertico-multiform-mode)
;;
;; Furthermore a cleanup function for shadowed file paths is provided.
;;
;; (add-hook 'rfn-eshadow-update-overlay-hook #'vertico-directory-tidy)

;;; Code:

(require 'vertico)
(eval-when-compile (require 'subr-x))

;;;###autoload
(defun vertico-directory-enter (&optional arg)
  "Enter directory or exit completion with current candidate.
Exit with current input if prefix ARG is given."
  (interactive "P")
  (if-let* (((not arg))
            ((>= vertico--index 0))
            ((eq 'file (vertico--metadata-get 'category)))
            ;; Check vertico--base for stepwise file path completion
            ((not (equal vertico--base "")))
            (cand (vertico--candidate))
            ((or (string-suffix-p "/" cand)
                 (and (vertico--remote-p cand)
                      (string-suffix-p ":" cand))))
            ;; Handle /./ and /../ manually instead of via `expand-file-name'
            ;; and `abbreviate-file-name', such that we don't accidentally
            ;; perform unwanted substitutions in the existing completion.
            ((progn
               (setq cand (string-replace "/./" "/" cand))
               (unless (string-suffix-p "/../../" cand)
                 (setq cand (replace-regexp-in-string "/[^/|:]+/\\.\\./\\'" "/" cand)))
               (not (equal (minibuffer-contents-no-properties) cand)))))
      (progn
        (delete-minibuffer-contents)
        (insert cand))
    (vertico-exit arg)))

;;;###autoload
(defun vertico-directory-up (&optional n)
  "Delete N names before point."
  (interactive "p")
  (when (and (> (point) (minibuffer-prompt-end))
             (eq 'file (vertico--metadata-get 'category)))
    (let ((path (buffer-substring-no-properties (minibuffer-prompt-end) (point)))
          found)
      (when (string-match-p "\\`~[^/]*/\\'" path)
        (delete-minibuffer-contents)
        (insert (expand-file-name path)))
      (dotimes (_ (or n 1) found)
        (save-excursion
          (let ((end (point)))
            (goto-char (1- end))
            (when (search-backward "/" (minibuffer-prompt-end) t)
              (delete-region (1+ (point)) end)
              (setq found t))))))))

;;;###autoload
(defun vertico-directory-delete-char (n)
  "Delete N directories or chars before point."
  (interactive "p")
  (unless (and (not (and (use-region-p) delete-active-region (= n 1)))
               (eq (char-before) ?/) (vertico-directory-up n))
    (with-no-warnings (delete-backward-char n))))

;;;###autoload
(defun vertico-directory-delete-word (n)
  "Delete N directories or words before point."
  (interactive "p")
  (unless (and (eq (char-before) ?/) (vertico-directory-up n))
    (delete-region (prog1 (point) (backward-word n)) (point))))

;;;###autoload
(defun vertico-directory-tidy ()
  "Tidy shadowed file name, see `rfn-eshadow-overlay'."
  (when (eq this-command #'self-insert-command)
    (dolist (ov '(tramp-rfn-eshadow-overlay rfn-eshadow-overlay))
      (when (and (boundp ov)
                 (setq ov (symbol-value ov))
                 (overlay-buffer ov)
                 (= (point) (point-max))
                 (> (point) (overlay-end ov)))
        (delete-region (overlay-start ov) (overlay-end ov))))))

(defvar-keymap vertico-directory-map
  :doc "File name editing map."
  "RET" #'vertico-directory-enter
  "DEL" #'vertico-directory-delete-char
  "M-DEL" #'vertico-directory-delete-word)

;;;###autoload (autoload 'vertico-directory-map "vertico-directory" nil t 'keymap)
(defalias 'vertico-directory-map vertico-directory-map)

(provide 'vertico-directory)
;;; vertico-directory.el ends here