├── .gitignore ├── .travis.yml ├── README.org ├── eshell-z-test.el └── eshell-z.el /.gitignore: -------------------------------------------------------------------------------- 1 | *.elc -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: generic 2 | env: 3 | matrix: 4 | - EMACS=emacs25 5 | - EMACS=emacs-snapshot 6 | 7 | install: 8 | - if [ "$EMACS" = 'emacs25' ]; then 9 | sudo add-apt-repository -y ppa:ubuntu-elisp/ppa && 10 | sudo apt-get -qq update && 11 | sudo apt-get -qq -f install && 12 | sudo apt-get -qq install emacs25; 13 | fi 14 | - if [ "$EMACS" = 'emacs-snapshot' ]; then 15 | sudo add-apt-repository -y ppa:ubuntu-elisp/ppa && 16 | sudo apt-get -qq update && 17 | sudo apt-get -qq -f install && 18 | sudo apt-get -qq install emacs-snapshot; 19 | fi 20 | - $EMACS --version 21 | 22 | script: 23 | - $EMACS -Q --batch -f batch-byte-compile eshell-z.el 24 | - $EMACS -Q --batch -L . -l ert -l eshell-z-test -f ert-run-tests-batch-and-exit 25 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | * =eshell-z= [[https://travis-ci.org/xuchunyang/eshell-z][https://travis-ci.org/xuchunyang/eshell-z.svg?branch=master]] [[https://melpa.org/#/eshell-z][https://melpa.org/packages/eshell-z-badge.svg]] [[https://stable.melpa.org/#/eshell-z][https://stable.melpa.org/packages/eshell-z-badge.svg]] 2 | ** Introduction 3 | The =eshell-z= package is an Emacs port of [[https://github.com/rupa/z][z]]. 4 | It keeps track of where you’ve been and how many commands you invoke there, 5 | and provides a convenient way to jump to the directories you actually 6 | use. =eshell-z= and =z= can work together by sharing the same data file. 7 | 8 | ** Table of Contents :TOC@4: 9 | - [[#eshell-z][=eshell-z=]] 10 | - [[#introduction][Introduction]] 11 | - [[#install][Install]] 12 | - [[#melpa][MELPA]] 13 | - [[#manually][Manually]] 14 | - [[#setup][Setup]] 15 | - [[#usage][Usage]] 16 | - [[#notations][Notations]] 17 | - [[#aging][Aging]] 18 | - [[#frecency][Frecency]] 19 | - [[#common][Common]] 20 | - [[#tab-completion][Tab Completion]] 21 | - [[#see-also][See also]] 22 | 23 | ** Install 24 | *** MELPA 25 | After setting up [[http://melpa.org][MELPA]] as a repository, use =M-x package-install eshell-z= or 26 | your preferred method. 27 | 28 | *** Manually 29 | Add eshell-z to your =load-path=. Something like 30 | 31 | #+BEGIN_SRC emacs-lisp 32 | (add-to-list 'load-path "path/to/eshell-z") 33 | #+END_SRC 34 | 35 | ** Setup 36 | To use this package, add following code to your init.el or .emacs 37 | #+BEGIN_SRC emacs-lisp 38 | (add-hook 'eshell-mode-hook 39 | (defun my-eshell-mode-hook () 40 | (require 'eshell-z))) 41 | #+END_SRC 42 | 43 | ** Usage 44 | #+BEGIN_SRC 45 | ~ $ z -h 46 | usage: z [-chlrtx] [regex1 regex2 ... regexn] 47 | 48 | -c, --current estrict matches to subdirectories of the current directory 49 | -h, --help show a brief help message 50 | -l, --list list only 51 | -r, --rank match by rank only 52 | -t, --time match by recent access only 53 | -x, --delete remove the current directory from the datafile 54 | 55 | examples: 56 | 57 | z foo cd to most frecent dir matching foo 58 | z foo bar cd to most frecent dir matching foo, then bar 59 | z -r foo cd to highest ranked dir matching foo 60 | z -t foo cd to most recently accessed dir matching foo 61 | z -l foo list all dirs matching foo (by frecency) 62 | #+END_SRC 63 | 64 | ** Notations 65 | 66 | *NOTICE* _This section is copied from the manpage of [[https://github.com/rupa/z][z]]._ 67 | 68 | *** Aging 69 | The rank of directories maintained by z undergoes aging based on a simple 70 | formula. The rank of each entry is incremented every time it is accessed. When 71 | the sum of ranks is over 9000, all ranks are multiplied by 0.99. Entries with a 72 | rank lower than 1 are forgotten. 73 | 74 | *** Frecency 75 | Frecency is a portmanteau of 'recent' and 'frequency'. It is a weighted rank 76 | that depends on how often and how recently something occurred. As far as I know, 77 | Mozilla came up with the term. 78 | 79 | To z, a directory that has low ranking but has been accessed recently will 80 | quickly have higher rank than a directory accessed frequently a long time 81 | ago. 82 | 83 | Frecency is determined at runtime. 84 | 85 | *** Common 86 | When multiple directories match all queries, and they all have a common prefix, 87 | z will cd to the shortest matching directory, without regard to priority. This 88 | has been in effect, if undocumented, for quite some time, but should probably 89 | be configurable or reconsidered. 90 | 91 | ** Tab Completion 92 | z supports basic tab completion, press TAB to complete on options and 93 | directories. This is implemented with =pcomplete=. 94 | 95 | ** See also 96 | - [[https://github.com/rupa/z][rupa/z]] 97 | - [[https://github.com/wting/autojump][wting/autojump]] and [[https://github.com/coldnew/eshell-autojump][coldnew/eshell-autojump]] 98 | -------------------------------------------------------------------------------- /eshell-z-test.el: -------------------------------------------------------------------------------- 1 | ;;; eshell-z-test.el --- tests for eshell-z 2 | 3 | ;; Copyright (C) 2015 Chunyang Xu 4 | 5 | ;; Author: Chunyang Xu 6 | 7 | ;; This program is free software; you can redistribute it and/or modify 8 | ;; it under the terms of the GNU General Public License as published by 9 | ;; the Free Software Foundation, either version 3 of the License, or 10 | ;; (at your option) any later version. 11 | 12 | ;; This program is distributed in the hope that it will be useful, 13 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | ;; GNU General Public License for more details. 16 | 17 | ;; You should have received a copy of the GNU General Public License 18 | ;; along with this program. If not, see . 19 | 20 | (require 'ert) 21 | (require 'eshell-z) 22 | 23 | (defvar user-home-path (getenv "HOME")) 24 | 25 | (ert-deftest eshell-z--expand-directory-name () 26 | (should (equal (eshell-z--expand-directory-name "~") 27 | user-home-path)) 28 | (should (equal (eshell-z--expand-directory-name "~/.emacs.d/") 29 | (expand-file-name (concat user-home-path "/" ".emacs.d"))))) 30 | 31 | (ert-deftest eshell-z--directory-within-p () 32 | (should (equal (eshell-z--directory-within-p "~/.emacs.d/elpa" "~/.emacs.d") t)) 33 | (should (equal (eshell-z--directory-within-p "/tmp/" "/tmp") t)) 34 | (should (equal (eshell-z--directory-within-p "~/tmp" "/tmp") nil)) 35 | (should (equal (eshell-z--directory-within-p "foobar" "/foo") nil))) 36 | 37 | (ert-deftest eshell-z--common-root () 38 | (let ((dirs '("/Users/xcy/repos/mu/lib" 39 | "/Users/xcy/repos/mu" 40 | "/Users/xcy/repos/mu/guile"))) 41 | (should (equal (eshell-z--common-root dirs) "/Users/xcy/repos/mu")))) 42 | -------------------------------------------------------------------------------- /eshell-z.el: -------------------------------------------------------------------------------- 1 | ;;; eshell-z.el --- cd to frequent directory in eshell -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2015, 2016, 2017 Chunyang Xu 4 | 5 | ;; Author: Chunyang Xu 6 | ;; Package-Requires: ((cl-lib "0.5")) 7 | ;; Keywords: convenience 8 | ;; Version: 0.4 9 | ;; Homepage: https://github.com/xuchunyang/eshell-z 10 | 11 | ;; This program is free software; you can redistribute it and/or modify 12 | ;; it under the terms of the GNU General Public License as published by 13 | ;; the Free Software Foundation, either version 3 of the License, or 14 | ;; (at your option) any later version. 15 | 16 | ;; This program is distributed in the hope that it will be useful, 17 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | ;; GNU General Public License for more details. 20 | 21 | ;; You should have received a copy of the GNU General Public License 22 | ;; along with this program. If not, see . 23 | 24 | ;;; Commentary: 25 | ;; 26 | ;; `eshell-z.el' is an Emacs port of z(1) . 27 | ;; 28 | ;; It keeps track of where you have been and how many commands you invoke there, 29 | ;; and provides a convenient way to jump to the directories you actually 30 | ;; use. 31 | ;; 32 | ;; `eshell-z.el' and z(1) can work together by sharing the same data file. 33 | ;; 34 | ;; Usage: 35 | ;; 36 | ;; ~ $ z -h 37 | ;; usage: z [-chlrtx] [regex1 regex2 ... regexn] 38 | ;; 39 | ;; -c, --current estrict matches to subdirectories of the current directory 40 | ;; -h, --help show a brief help message 41 | ;; -l, --list list only 42 | ;; -r, --rank match by rank only 43 | ;; -t, --time match by recent access only 44 | ;; -x, --delete remove the current directory from the datafile 45 | ;; 46 | ;; examples: 47 | ;; 48 | ;; z foo cd to most frecent dir matching foo 49 | ;; z foo bar cd to most frecent dir matching foo, then bar 50 | ;; z -r foo cd to highest ranked dir matching foo 51 | ;; z -t foo cd to most recently accessed dir matching foo 52 | ;; z -l foo list all dirs matching foo (by frecency) 53 | ;; 54 | ;; Install: 55 | ;; 56 | ;; You can install this package from Melpa and Melpa-stable with package.el, 57 | ;; that is, ~M-x package-install RET eshell-z RET~. Or you can also install it 58 | ;; manually by add eshell-z.el to your `load-path', something like 59 | ;; 60 | ;; (add-to-list 'load-path "path/to/eshell-z.el") 61 | ;; 62 | ;; Setup: 63 | ;; 64 | ;; To use this package, add following code to your init.el or .emacs 65 | ;; 66 | ;; (add-hook 'eshell-mode-hook 67 | ;; (defun my-eshell-mode-hook () 68 | ;; (require 'eshell-z))) 69 | 70 | 71 | ;;; Code: 72 | 73 | (require 'cl-lib) 74 | (require 'eshell) 75 | (require 'em-dirs) 76 | (require 'pcomplete) 77 | 78 | (defgroup eshell-z nil 79 | "Eshell z customizations." 80 | :group 'eshell) 81 | 82 | (defcustom eshell-z-freq-dir-hash-table-file-name 83 | (or (getenv "_Z_DATA") 84 | (expand-file-name "~/.z")) 85 | "If non-nil, name of the file to read/write the freq-dir-hash-table. 86 | If it is nil, the freq-dir-hash-table will not be written to disk." 87 | :type 'file 88 | :group 'eshell-z) 89 | 90 | (defcustom eshell-z-exclude-dirs '("/tmp/" "~/.emacs.d/elpa") 91 | "A list of directory trees to exclude." 92 | :type '(repeat (choice string)) 93 | :group 'eshell-z) 94 | 95 | (defcustom eshell-z-change-dir-function 96 | (lambda (dir) 97 | (eshell-kill-input) 98 | (goto-char (point-max)) 99 | (insert 100 | (format "cd '%s'" dir)) 101 | (eshell-send-input)) 102 | "Function to control how the directory should be changed." 103 | :type 'function 104 | :group 'eshell-z) 105 | 106 | (defvar eshell-z-freq-dir-hash-table nil 107 | "The frequent directory that Eshell was in.") 108 | 109 | (defvar eshell-z-change-dir-hook nil 110 | "Hook run just before eshell-z calls eshell/cd.") 111 | 112 | (defun eshell-z--now () 113 | "Number of seconds since epoch as a string." 114 | (format-time-string "%s")) 115 | 116 | (defun eshell-z--read-freq-dir-hash-table () 117 | "Set `eshell-z-freq-dir-hash-table' from a history file." 118 | (let ((file eshell-z-freq-dir-hash-table-file-name)) 119 | (cond 120 | ((or (null file) 121 | (equal file "") 122 | (file-directory-p file) 123 | (not (file-readable-p file))) 124 | nil) 125 | (t 126 | (setq eshell-z-freq-dir-hash-table 127 | (let ((m (make-hash-table :test 'equal))) 128 | (mapc (lambda (elt) 129 | (let* ((entries (split-string elt "|")) 130 | (key (car entries)) 131 | (rank (string-to-number (cadr entries))) 132 | (time (car (last entries)))) 133 | (puthash key (cons key (list :rank rank :time time)) 134 | m))) 135 | (with-temp-buffer 136 | (let ((jka-compr-compression-info-list nil)) 137 | (insert-file-contents file)) 138 | (split-string (buffer-string) "\n" t))) 139 | m)))))) 140 | 141 | ;; Same as `hash-table-values' of `subr-x.el' in Emacs 24.4+ 142 | (defsubst eshell-z--hash-table-values (hash-table) 143 | "Return a list of values in HASH-TABLE." 144 | (let ((values '())) 145 | (maphash (lambda (_k v) (push v values)) hash-table) 146 | values)) 147 | 148 | (defun eshell-z--write-freq-dir-hash-table () 149 | "Write `eshell-z-freq-dir-hash-table' to a history file." 150 | (let ((file eshell-z-freq-dir-hash-table-file-name)) 151 | (cond 152 | ((or (null file) 153 | (equal file "") 154 | (null eshell-z-freq-dir-hash-table) 155 | (zerop (hash-table-count eshell-z-freq-dir-hash-table))) 156 | nil) 157 | ((and (file-exists-p file) 158 | (not (file-directory-p file)) 159 | (not (file-writable-p file))) 160 | (message "Cannot write freq-dir-hash-table file %s" file)) 161 | (t 162 | (with-temp-buffer 163 | (insert 164 | (mapconcat 165 | (lambda (val) 166 | (let ((dir (car val)) 167 | (rank (number-to-string (plist-get (cdr val) :rank))) 168 | (time (plist-get (cdr val) :time))) 169 | (format "%s|%s|%s" dir rank time))) 170 | (eshell-z--hash-table-values eshell-z-freq-dir-hash-table) "\n")) 171 | (insert "\n") 172 | (let ((jka-compr-compression-info-list nil)) 173 | (write-region (point-min) (point-max) file nil 'silent))))))) 174 | 175 | (defun eshell-z--expand-directory-name (directory) 176 | "Expand and remove ending slash of DIRECTORY." 177 | (expand-file-name (directory-file-name directory))) 178 | 179 | (defun eshell-z--directory-within-p (directory root) 180 | "Return non-nil if DIRECTORY is a sub-directory of ROOT or ROOT itself." 181 | (let ((root (eshell-z--expand-directory-name root)) 182 | (directory (eshell-z--expand-directory-name directory))) 183 | (if (string= root directory) 184 | t 185 | (let ((len1 (length root)) 186 | (len2 (length directory))) 187 | (if (< len2 len1) 188 | nil 189 | (if (and (string= root (substring directory 0 len1)) 190 | (= (aref directory len1) ?/)) 191 | t 192 | nil)))))) 193 | 194 | (defun eshell-z--common-root (dirs) 195 | "Return one directory of DIRS which is the root of all the rest directories, if any." 196 | (let ((root (car 197 | (sort 198 | (copy-sequence dirs) 199 | (lambda (s1 s2) (< (length s1) (length s2))))))) 200 | (if (cl-every 201 | (lambda (elt) (eshell-z--directory-within-p elt root)) 202 | dirs) 203 | root))) 204 | 205 | (defun eshell-z--add () 206 | "Add entry." 207 | (if eshell-z-freq-dir-hash-table-file-name 208 | (eshell-z--read-freq-dir-hash-table)) 209 | (unless eshell-z-freq-dir-hash-table 210 | (setq eshell-z-freq-dir-hash-table (make-hash-table :test 'equal))) 211 | (let ((current-directory (eshell-z--expand-directory-name default-directory))) 212 | (unless (or 213 | ;; $HOME isn't worth matching 214 | (string= current-directory (eshell-z--expand-directory-name "~")) 215 | ;; don't track excluded directory trees 216 | (cl-some (lambda (root) 217 | (and (stringp root) 218 | (eshell-z--directory-within-p 219 | current-directory root))) 220 | eshell-z-exclude-dirs)) 221 | (let* ( 222 | ;; Remove end slash, z doesn't use it 223 | (key current-directory) 224 | (val (gethash key eshell-z-freq-dir-hash-table))) 225 | (if val 226 | (puthash key (cons key 227 | (list :rank (1+ (plist-get (cdr val) :rank)) 228 | :time (eshell-z--now))) 229 | eshell-z-freq-dir-hash-table) 230 | (puthash key (cons key 231 | (list :rank 1 232 | :time (eshell-z--now))) 233 | eshell-z-freq-dir-hash-table))))) 234 | 235 | (if eshell-z-freq-dir-hash-table-file-name 236 | (eshell-z--write-freq-dir-hash-table))) 237 | 238 | (defvar eshell-z--remove-p nil) 239 | 240 | (defun eshell-z--remove () 241 | "Remove entry." 242 | (if eshell-z--remove-p 243 | (progn 244 | (unless eshell-z-freq-dir-hash-table 245 | (setq eshell-z-freq-dir-hash-table (make-hash-table :test 'equal))) 246 | (remhash (eshell-z--expand-directory-name default-directory) 247 | eshell-z-freq-dir-hash-table) 248 | (if eshell-z-freq-dir-hash-table-file-name 249 | (eshell-z--write-freq-dir-hash-table)) 250 | (setq eshell-z--remove-p nil)))) 251 | 252 | ;; FIXME: It's much better to provide a minor mode to handle this 253 | (add-hook 'eshell-post-command-hook #'eshell-z--add) 254 | (add-hook 'eshell-post-command-hook #'eshell-z--remove 'append) 255 | 256 | (defun eshell-z--frecent (value) 257 | "Calculate rank of a VALUE of `eshell-z-freq-dir-hash-table'. 258 | Base on frequency and time." 259 | (let* ((rank (plist-get (cdr value) :rank)) 260 | (time (eshell-z--time value)) 261 | (dx (- (string-to-number (eshell-z--now)) time))) 262 | (cond ((< dx 3600) (* rank 4)) 263 | ((< dx 86400) (* rank 2)) 264 | ((< dx 604800) (/ rank 2.0)) 265 | (t (/ rank 4.0))))) 266 | 267 | (defun eshell-z--rank (value) 268 | "Get rank of a VALUE of `eshell-z-freq-dir-hash-table'." 269 | (plist-get (cdr value) :rank)) 270 | 271 | (defun eshell-z--time (value) 272 | "Get time of a VALUE of `eshell-z-freq-dir-hash-table'." 273 | (string-to-number (plist-get (cdr value) :time))) 274 | 275 | (defun eshell-z--float-to-string (number) 276 | "Format number for the list option." 277 | (let* ((int (truncate number)) 278 | (result (if (= int number) int 279 | number))) 280 | (if (integerp result) 281 | (format "%-10d" result) 282 | (format "%-10.1f" result)))) 283 | 284 | (defun eshell-z--ensure-hash-table () 285 | "Ensure `eshell-z-freq-dir-hash-table' is a hash table, not nil." 286 | (unless eshell-z-freq-dir-hash-table 287 | (if eshell-z-freq-dir-hash-table-file-name 288 | (eshell-z--read-freq-dir-hash-table))) 289 | 290 | (unless eshell-z-freq-dir-hash-table 291 | (setq eshell-z-freq-dir-hash-table (make-hash-table :test 'equal)))) 292 | 293 | (defun eshell-z--cd (value) 294 | "Invokes eshell/cd, running any hooks in eshell-z-change-dir-hook first." 295 | (run-hooks 'eshell-z-change-dir-hook) 296 | (eshell/cd value)) 297 | 298 | (defun eshell/z (&rest args) 299 | "cd to frequent directory in eshell." 300 | (eshell-z--ensure-hash-table) 301 | (eshell-eval-using-options 302 | "z" args 303 | '((?c "current" nil current 304 | "estrict matches to subdirectories of the current directory") 305 | (?h "help" nil nil "show a brief help message") 306 | (?l "list" nil list "list only") 307 | (?r "rank" nil rank-only "match by rank only") 308 | (?t "time" nil time-only "match by recent access only") 309 | (?x "delete" nil delete "remove the current directory from the datafile" ) 310 | :usage "[-chlrtx] [regex1 regex2 ... regexn]" 311 | :post-usage "examples: 312 | 313 | z foo cd to most frecent dir matching foo 314 | z foo bar cd to most frecent dir matching foo, then bar 315 | z -r foo cd to highest ranked dir matching foo 316 | z -t foo cd to most recently accessed dir matching foo 317 | z -l foo list all dirs matching foo (by frecency) 318 | ") 319 | (if delete 320 | (setq eshell-z--remove-p t) 321 | (let ((paths (sort (eshell-z--hash-table-values eshell-z-freq-dir-hash-table) 322 | (if rank-only 323 | (lambda (elt1 elt2) 324 | (> (eshell-z--rank elt1) 325 | (eshell-z--rank elt2))) 326 | (if time-only 327 | (lambda (elt1 elt2) 328 | (> (eshell-z--time elt1) 329 | (eshell-z--time elt2))) 330 | (lambda (elt1 elt2) 331 | (> (eshell-z--frecent elt1) 332 | (eshell-z--frecent elt2))))))) 333 | (current-directory (eshell-z--expand-directory-name 334 | default-directory))) 335 | (if list 336 | (let ((matches 337 | (nreverse 338 | (cl-remove-if-not 339 | (lambda (elt) 340 | (string-match 341 | (mapconcat #'identity 342 | (if current 343 | (append (list current-directory) args) 344 | args) ".*") 345 | (car elt))) 346 | paths)))) 347 | (let ((common-root (eshell-z--common-root (mapcar #'car matches)))) 348 | (when common-root 349 | (eshell-print (format "%-10s %s\n" "common:" common-root)))) 350 | ;; Display all matches 351 | (eshell-print 352 | (mapconcat 353 | (lambda (elt) 354 | (format 355 | "%s %s" 356 | (eshell-z--float-to-string 357 | (if rank-only (eshell-z--rank elt) 358 | (if time-only (- (eshell-z--time elt) 359 | (string-to-number (eshell-z--now))) 360 | (eshell-z--frecent elt)))) 361 | (car elt))) 362 | matches "\n"))) 363 | (if (null args) 364 | (eshell-z--cd (list (completing-read "pattern " paths nil t))) 365 | (let ((path (car args))) 366 | (if (numberp path) 367 | (setq path (number-to-string path))) 368 | ;; if we hit enter on a completion just go there 369 | (if (file-accessible-directory-p path) 370 | (eshell-z--cd (list path)) 371 | (let* ((matches 372 | (cl-remove-if-not 373 | (lambda (elt) 374 | (string-match 375 | (mapconcat #'identity 376 | (if current 377 | (append (list current-directory) args) 378 | args) ".*") 379 | (car elt))) 380 | paths)) 381 | (newdir (or (eshell-z--common-root (mapcar #'car matches)) 382 | (caar matches)))) 383 | (if (and newdir (file-accessible-directory-p newdir)) 384 | (eshell-z--cd (list newdir)))))))))) 385 | nil)) 386 | 387 | (defun pcomplete/z () 388 | "Completion for the `z' command." 389 | (while t 390 | (if (pcomplete-match "^-" 0) 391 | (cond 392 | ;; Long options 393 | ((pcomplete-match "^--" 0) 394 | (pcomplete-here* '("--current" "--help" "--list" "--rank" "--time" "--delete"))) 395 | ;; Short options 396 | (t (pcomplete-opt "chlrtx"))) 397 | (pcomplete-here* (eshell-z--hash-table-values 398 | eshell-z-freq-dir-hash-table))))) 399 | 400 | (defvar ivy-sort-functions-alist) 401 | 402 | ;;;###autoload 403 | (defun eshell-z (dir) 404 | "Switch to eshell and change directory to DIR." 405 | (interactive 406 | (list (let ((paths 407 | (sort (progn 408 | (eshell-z--ensure-hash-table) 409 | (eshell-z--hash-table-values eshell-z-freq-dir-hash-table)) 410 | (lambda (elt1 elt2) 411 | (> (eshell-z--frecent elt1) 412 | (eshell-z--frecent elt2))))) 413 | (ivy-sort-functions-alist nil)) 414 | (completing-read "pattern " paths nil t)))) 415 | (let ((eshell-buffer (if (eq major-mode 'eshell-mode) 416 | (buffer-name) 417 | "*eshell*"))) 418 | (if (get-buffer eshell-buffer) 419 | (switch-to-buffer eshell-buffer) 420 | (call-interactively 'eshell)) 421 | (unless (get-buffer-process (current-buffer)) 422 | (funcall eshell-z-change-dir-function dir)))) 423 | 424 | (provide 'eshell-z) 425 | ;;; eshell-z.el ends here 426 | --------------------------------------------------------------------------------