;;; shrink-path.el --- fish-style path -*- lexical-binding: t; -*- ;; Copyright (C) 2017 Benjamin Andresen ;; Author: Benjamin Andresen ;; Version: 0.3.1 ;; Package-Version: 20190208.1335 ;; Package-Commit: c14882c8599aec79a6e8ef2d06454254bb3e1e41 ;; URL: https://gitlab.com/bennya/shrink-path.el ;; Package-Requires: ((emacs "24") (s "1.6.1") (dash "1.8.0") (f "0.10.0")) ;; This file is NOT 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, 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 GNU Emacs; see the file LICENSE. If not, write to the ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, ;; Boston, MA 02110-1301, USA. ;;; Commentary: ;; Provides functions that offer fish shell[1] path truncation. ;; Directory /usr/share/emacs/site-lisp => /u/s/e/site-lisp ;; ;; Also includes utility functions that make integration in eshell or the ;; modeline easier. ;; ;; [1] https://fishshell.com/ ;;; Code: (require 'dash) (require 's) (require 'f) (require 'rx) (defun shrink-path--truncate (str) "Return STR's first character or first two characters if hidden." (substring str 0 (if (s-starts-with? "." str) 2 1))) (defun shrink-path--dirs-internal (full-path &optional truncate-all) "Return fish-style truncated string based on FULL-PATH. Optional parameter TRUNCATE-ALL will cause the function to truncate the last directory too." (let* ((home (expand-file-name "~")) (path (replace-regexp-in-string (s-concat "^" home) "~" full-path)) (split (s-split "/" path 'omit-nulls)) (split-len (length split)) shrunk) (->> split (--map-indexed (if (= it-index (1- split-len)) (if truncate-all (shrink-path--truncate it) it) (shrink-path--truncate it))) (s-join "/") (setq shrunk)) (s-concat (unless (s-matches? (rx bos (or "~" "/")) shrunk) "/") shrunk (unless (s-ends-with? "/" shrunk) "/")))) (defun shrink-path-dirs (&optional path truncate-tail) "Given PATH return fish-styled shrunken down path. TRUNCATE-TAIL will cause the function to truncate the last directory too." (let* ((path (or path default-directory)) (path (f-full path))) (cond ((s-equals? (f-short path) "/") "/") ((s-matches? (rx bos (or "~" "/") eos) "~/")) (t (shrink-path--dirs-internal path truncate-tail))))) (defun shrink-path-expand (str &optional absolute-p) "Return expanded path from STR if found or list of matches on multiple. The path referred to by STR has to exist for this to work. If ABSOLUTE-P is t the returned path will be absolute." (let* ((str-split (s-split "/" str 'omit-nulls)) (head (car str-split))) (if (= (length str-split) 1) (s-concat "/" str-split) (--> (-drop 1 str-split) ;; drop head (-map (lambda (e) (s-concat e "*")) it) (-drop-last 1 it) ;; drop tail as it may not exist (s-join "/" it) (s-concat (if (s-equals? head "~") "~/" head) it) (f-glob it) (-map (lambda (e) (s-concat e "/" (-last-item str-split))) it) (if absolute-p (-map #'f-full it) (-map #'f-abbrev it)) (if (= (length it) 1) (car it) it))))) (defun shrink-path-prompt (&optional pwd) "Return cons of BASE and DIR for PWD. If PWD isn't provided will default to `default-directory'." (let* ((pwd (or pwd default-directory)) (shrunk (shrink-path-dirs pwd)) (split (--> shrunk (s-split "/" it 'omit-nulls))) base dir) (setq dir (or (-last-item split) "/")) (setq base (if (s-equals? dir "/") "" (s-chop-suffix (s-concat dir "/") shrunk))) (cons base dir))) (defun shrink-path-file (file &optional truncate-tail) "Return FILE's shrunk down path and filename. TRUNCATE-TAIL controls if the last directory should also be shortened." (let ((filename (f-filename file)) (dirname (f-dirname file))) (s-concat (shrink-path-dirs dirname truncate-tail) filename))) (defun shrink-path-file-expand (str &optional exists-p absolute-p) "Return STR's expanded filename. The path referred to by STR has to exist for this to work. If EXISTS-P is t the filename also has to exist. If ABSOLUTE-P is t the returned path will be absolute." (let ((expanded (shrink-path-expand str absolute-p))) (if (and expanded exists-p) (if (f-exists? expanded) expanded) expanded))) (defun shrink-path-file-mixed (shrink-path rel-path filename) "Returns list of mixed truncated file name locations. Consists of SHRINK-PATH's parent, SHRINK-PATH basename, relative REL-PATH and FILENAME. For use in modeline or prompts, etc." (let ((shrunk-dirs (shrink-path-prompt shrink-path)) sp-parent sp-rel rel-rel nd-file) (when (f-descendant-of? filename shrink-path) (when shrunk-dirs (setq sp-parent (car shrunk-dirs) sp-rel (cdr shrunk-dirs))) (setq rel-rel (if (or (f-same? rel-path shrink-path) (s-equals? (f-relative rel-path shrink-path) ".")) nil (f-relative rel-path shrink-path))) (setq nd-file (file-name-nondirectory filename)) (list sp-parent sp-rel rel-rel nd-file)))) (provide 'shrink-path) ;;; shrink-path.el ends here