[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] externals/visual-filename-abbrev d9efa04 1/5: New package: visual
From: |
Stefan Monnier |
Subject: |
[elpa] externals/visual-filename-abbrev d9efa04 1/5: New package: visual-filename-abbrev.el |
Date: |
Sat, 28 Nov 2020 15:04:59 -0500 (EST) |
branch: externals/visual-filename-abbrev
commit d9efa045f5d99092352ec92238a745afaa12cc67
Author: Tassilo Horn <tsdh@gnu.org>
Commit: Tassilo Horn <tsdh@gnu.org>
New package: visual-filename-abbrev.el
---
visual-filename-abbrev.el | 229 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 229 insertions(+)
diff --git a/visual-filename-abbrev.el b/visual-filename-abbrev.el
new file mode 100644
index 0000000..950e95c
--- /dev/null
+++ b/visual-filename-abbrev.el
@@ -0,0 +1,229 @@
+;;; visual-filename-abbrev.el --- Visually abbreviate filenames -*-
lexical-binding: t; -*-
+
+;; Copyright (C) 2019 Free Software Foundation, Inc
+
+;; Author: Tassilo Horn <tsdh@gnu.org>
+;; Maintainer: Tassilo Horn <tsdh@gnu.org>
+;; Keywords: tools
+;; Package-Requires: ((emacs "26.1"))
+;; Version: 0
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs 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.
+
+;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;;
+;; This minor mode abbreviates the directory part of filenames by using
+;; overlays. For example, a longish filename like
+;;
+;; /home/myuser/Documents/Letters/Personal-Family/Letter-to-John.tex
+;;
+;; will be displayed like this:
+;;
+;; /h…/m…/D…/L…/P…-F…/Letter-to-John.tex
+;;
+;; By default, the abbreviate display is disabled when point enters the overlay
+;; so that you can edit the file name normally. Also, abbreviated file names
+;; are only shown if the abbreviation as actually shorter as the original one
+;; (which depends on what you add as replacement).
+;;
+;; There's stuff to customize, just check `M-x customize-group RET
+;; visual-filename-abbrev RET'.
+
+;;; Code:
+
+(require 'subr-x)
+(require 'seq)
+
+(defgroup visual-filename-abbrev nil
+ "Visually abbreviate the directory part of filenames."
+ :group 'tools)
+
+(defcustom visual-filename-abbrev-regex
+ (concat "\\(?:file://\\)?/?"
+ "\\(?:[[:alnum:]@_.-]+/\\)+[[:alnum:]@_.-]*\\.\\w+")
+ "Regexp matching filenames."
+ :group 'visual-filename-abbrev
+ :type 'regexp)
+
+(defcustom visual-filename-abbrev-replace-regex
+ "[.@]?[[:alnum:]]\\([[:alnum:]]\\{2,\\}\\)[-_/.@]"
+ "Regexp which will be visually replaced in filenames.
+All matches of this regexp's group number 1 in the filenames
+matching `visual-filename-abbrev-regex' will be replaced by
+`visual-filename-abbrev-ellipsis'."
+ :group 'visual-filename-abbrev
+ :type 'regexp)
+
+(defcustom visual-filename-abbrev-ellipsis "…"
+ "String displayed instead of group 1 of `visual-filename-abbrev-regex'."
+ :group 'visual-filename-abbrev
+ :type 'string)
+
+(defcustom visual-filename-abbrev-unabbreviate-under-point t
+ "If non-nil, filenames under point are displayed unabbreviated."
+ :group 'visual-filename-abbrev
+ :type 'boolean)
+
+(defun visual-filename-abbrev--get-abbrev (filename)
+ (let ((file (file-name-nondirectory filename))
+ (dir (file-name-directory filename)))
+ (concat
+ (file-name-as-directory
+ (replace-regexp-in-string
+ visual-filename-abbrev-replace-regex
+ visual-filename-abbrev-ellipsis dir nil nil 1))
+ file)))
+
+(defsubst visual-filename-abbrev--get-overlay (pos)
+ (car (seq-filter
+ (lambda (o) (overlay-get o 'visual-filename-abbrev))
+ (overlays-at pos))))
+
+(defun visual-filename-abbrev--abbrev-shorter-p (_buffer _pos filename abbrev)
+ "Return non-nil if ABBREV is shorter than FILENAME.
+Shorter means less characters here."
+ (< (string-width abbrev)
+ (string-width filename)))
+
+(defsubst visual-filename-abbrev--get-visual-width (str font)
+ (seq-reduce (lambda (acc g) (+ acc (aref g 4)))
+ (font-get-glyphs font 0 (length str) str)
+ 0))
+
+(defun visual-filename-abbrev--abbrev-visually-shorter-p (buffer pos filename
abbrev)
+ "Return non-nil if ABBREV's display representation is shorter than FILENAME.
+This takes the font into account."
+ ;; NOTE: The docs say that object in an conditional display spec is always a
+ ;; buffer, but actually it sometimes is a window. See bug#34771.
+ (let ((font (font-at pos (if (windowp buffer)
+ buffer
+ (get-buffer-window buffer)))))
+ (< (visual-filename-abbrev--get-visual-width abbrev font)
+ (visual-filename-abbrev--get-visual-width filename font))))
+
+(defcustom visual-filename-abbrev-predicates
+ (list #'visual-filename-abbrev--abbrev-visually-shorter-p)
+ "A list of predicates inhibiting abbreviation of a filename.
+A filename is only abbreviate if all predicates in this list
+return true.
+
+Each predicate is called with the following four arguments:
+
+ - BUFFER: The buffer holding the abbreviation overlay.
+ - POS: The position in BUFFER of the overlay.
+ - FILE: The filename to be abbreviated.
+ - ABBREV: The abbreviated version of the filename.
+
+These predicates are available:
+
+ - `visual-filename-abbrev--abbrev-shorter-p' ensures that an
+ abbreviation is only shown if it is shorter (in the number of
+ characters) than the original filename. This is fast but
+ doesn't work too good if `visual-filename-abbrev-ellipsis' is
+ displayed wider than what's abbreviater (which depends on the
+ font).
+
+ - `visual-filename-abbrev--abbrev-visually-shorter-p' ensures
+ that an abbreviation is only shown if it is visually shorter
+ than the original filename, i.e., it takes the current font
+ and, e.g., double-width unicode characters into account.
+ This predicate is a bit more expensive to compute."
+ :group 'visual-filename-abbrev
+ :type '(repeat function))
+
+(defun visual-filename-abbrev--abbreviate-p (buffer pos filename abbrev)
+ (seq-every-p (lambda (pred)
+ (funcall pred buffer pos filename abbrev))
+ visual-filename-abbrev-predicates))
+
+(defun visual-filename-abbrev--delete-overlays (beg end)
+ (dolist (ol (overlays-in beg end))
+ (when (overlay-get ol 'visual-filename-abbrev)
+ (delete-overlay ol))))
+
+(defun visual-filename-abbrev--cursor-sensor (window old-pos dir)
+ ;;(message "cs: %S %S %S" window old-pos dir)
+ (when-let ((ol (if (eq dir 'entered)
+ (visual-filename-abbrev--get-overlay (point))
+ (or (visual-filename-abbrev--get-overlay old-pos)
+ (visual-filename-abbrev--get-overlay (if (> (point)
old-pos)
+ (1- old-pos)
+ (1+ old-pos)))))))
+ ;;(message " => %S" ol)
+ (if (eq dir 'entered)
+ (when-let ((d (overlay-get ol 'display)))
+ (overlay-put ol 'visual-filename-abbrev--display-backup d)
+ (overlay-put ol 'display nil))
+ (when-let ((d (overlay-get ol 'visual-filename-abbrev--display-backup)))
+ (overlay-put ol 'display d)
+ (overlay-put ol 'visual-filename-abbrev--display-backup nil)))))
+
+(defun visual-filename-abbrev--place-overlays (start end)
+ (goto-char start)
+ (while (re-search-forward visual-filename-abbrev-regex end t)
+ (let* ((m-beg (match-beginning 0))
+ (m-end (match-end 0))
+ (filename (match-string 0))
+ (abbrev (visual-filename-abbrev--get-abbrev filename)))
+ (when (visual-filename-abbrev--abbreviate-p
+ (current-buffer) (point) filename abbrev)
+ (let ((ol (or (when-let ((o (visual-filename-abbrev--get-overlay
m-beg)))
+ (move-overlay o m-beg m-end)
+ o)
+ (make-overlay m-beg m-end nil t))))
+ (when visual-filename-abbrev-unabbreviate-under-point
+ (overlay-put ol 'cursor-sensor-functions
+ (list #'visual-filename-abbrev--cursor-sensor)))
+ (overlay-put ol 'visual-filename-abbrev t)
+ (overlay-put ol 'evaporate t)
+ (overlay-put ol 'help-echo filename)
+ (overlay-put ol 'display abbrev))))))
+
+(defun visual-filename-abbrev--jit-lock (beg end &optional _old-len)
+ "Function registered for jit-lock."
+ (let ((beg-line (save-excursion (goto-char beg) (line-beginning-position)))
+ (end-line (save-excursion (goto-char end) (line-end-position))))
+ (visual-filename-abbrev--place-overlays beg-line end-line)))
+
+(defvar visual-filename-abbrev--csm-before-activation nil)
+(make-variable-buffer-local 'visual-filename-abbrev--csm-before-activation)
+
+;;###autoload
+(define-minor-mode visual-filename-abbrev-mode
+ "Visually abbreviate the directory part of filenames."
+ nil " VFAbbr" nil
+ (if visual-filename-abbrev-mode
+ (progn
+ (jit-lock-register #'visual-filename-abbrev--jit-lock)
+ (require 'cursor-sensor)
+ ;; Remember if c-s-m has been enabled before we enable it.
+ (setq visual-filename-abbrev--csm-before-activation cursor-sensor-mode)
+ (cursor-sensor-mode)
+ (visual-filename-abbrev--jit-lock (window-start)
+ (window-end)))
+ (jit-lock-unregister #'visual-filename-abbrev--jit-lock)
+ ;; Deactivate it only if it has been disabled before we started it.
+ (when visual-filename-abbrev--csm-before-activation
+ (cursor-sensor-mode -1))
+ (visual-filename-abbrev--delete-overlays 1 (1+ (buffer-size)))))
+
+(provide 'visual-filename-abbrev)
+
+;; Local Variables:
+;; bug-reference-url-format: "https://debbugs.gnu.org/cgi/bugreport.cgi?bug=%s"
+;; End:
+
+;;; visual-filename-abbrev.el ends here