diff options
| -rw-r--r-- | eshell-conf.el | 262 |
1 files changed, 147 insertions, 115 deletions
diff --git a/eshell-conf.el b/eshell-conf.el index 1013383..3e95e8d 100644 --- a/eshell-conf.el +++ b/eshell-conf.el @@ -247,16 +247,37 @@ See `eshell-j' for the actual functionality and for the format of ARGS." (eshell-eval-using-options "j" args - '((?r "recent" exclusive use-recent-p - "Find in recent directories instead of symbolic links.") - (?a "all" nil use-recent-p - "Find in recent directories, \ -symbolic links, and in claude session directories.") + '((?m "marks" nil use-marks-p + "Find in the marks directory (the default).") + (?r "recent" nil use-recent-p + "Find in recent directories.") (?s "session" nil use-session-p "Find in directories of Claude sessions.") + (?a "all" nil use-all-p "All sources included.") + (?l "list" nil list-p + "List the candidates instead of jumping.") (?h "help" nil nil "Print this help message.") - :usage "[-hras] [short-cut]") - (eshell-j args use-recent-p use-session-p))) + :usage "[-hmrsal] [short-cut]" + :post-usage "Without any of [-mrsa], only marks are the sources. +For listing candidates, the style of a line is determiney by the source: +\"NAME -> PATH\" for a mark, +\"PATH\" for a recent directory, and +\"TITLE: PATH\" for a claude session.") + (eshell-j + args + (cond + (use-all-p (list 'marks 'recent 'session)) + ((or use-marks-p use-recent-p use-session-p) + (delq nil (list (cond (use-marks-p 'marks)) + (cond (use-recent-p 'recent)) + (cond (use-session-p 'session))))) + ((list 'marks))) + list-p))) + +(defface eshell-j-mark-name + '((((background dark)) :foreground "sky blue") + (t :foreground "DeepSkyBlue4")) + "Face for the display of names of marks as candidates for `eshell-j'.") ;;;###autoload (defun eshell/mark (&rest args) @@ -343,111 +364,131 @@ supposed to leave the global state alone as well. (defvar eshell-mark-directory (expand-file-name "~/.marks") "The directory that stores links to other directories.") +(load-file "~/scripts/claude/claude-session.el") + ;;;###autoload -(defun eshell-j (&optional short-cut use-recent-p use-session-p) - "Jump to SHORT-CUT. -Where this jumps to is determined by the symbolic links in the -directory `eshell-mark-directory'. If USE-RECENT-P is non-nil, then -also include recent directories in the list of candidates. Moreover, -if USE-RECENT-P is 'exclusive, then only list the recent -directories as candidates, unless there are no recent -directories, in which case it falls back to use the marks as the -candidates. - -If USE-SESSION-P and USE-RECENT-P are non-nil, then also include the -claude session directories in the list of candidates. - -If USE-RECENT-P is non-nil and USE-RECENT-P is nil, only the claude -session directories are in the list of candidates." - (let* ((all-p (eq use-recent-p t)) - (mark-directory eshell-mark-directory) +(defun eshell-j (&optional short-cut sources list-p) + "Jump to SHORT-CUT, or list the candidates if LIST-P is non-nil. +SOURCES is a list of the symbols `marks', `recent', and `session', +specifying the sources of candidates. Unknown symbols are ignored. If +no known symbol is present, use `marks' as the default source. + +The sources are described below: + +- `marks': the symbolic links in the `eshell-mark-directory'. +- `recent': the directories in `eshell-last-dir-ring'. +- `session': the directories of claude sessions. See + `claude-session-candidates' to be precise. + +Marks and recent directories are matched with SHORT-CUT by the paths, +and sessions are matched by the display, including the title. If all +the candidates are in the same directory, jump to that directory +directly; otherwise use `completing-read' to choose one destination. + +If LIST-P is non-nil, return a string listing one line for one +candidate, in the following format: + +\"NAME -> PATH\" for a mark, +\"PATH\" for a recent directory, and +\"TITLE: PATH\" for a claude session." + (let* ((accepted-sources (list 'marks 'recent 'session)) + ;; filter out unknown source symbols. + (sources (delq + nil + (mapcar + (lambda (source) + (cond ((memq source accepted-sources) source))) + sources))) + (sources (cond (sources) ((cons 'marks nil)))) (short-cut (eshell-flatten-and-stringify short-cut)) - (links (delq nil - (mapcar - (function - (lambda (name) - (cond - ((or (string= "." (file-name-nondirectory name)) - (string= ".." (file-name-nondirectory name))) - nil) - (name)))) - (directory-files mark-directory t short-cut t)))) - (candidates (cond - ((and use-recent-p - (not (eq use-recent-p 'exclusive)) - (ring-p eshell-last-dir-ring) - (not (ring-empty-p eshell-last-dir-ring))) - (append (mapcar (function - (lambda (file) - (cons (file-name-nondirectory file) - file))) - links) - (cond - ((string-match-p short-cut mark-directory) - (list (cons mark-directory mark-directory)))) - (mapcar (function (lambda (file) (cons file file))) - (ring-elements eshell-last-dir-ring)))) - ((and use-recent-p - (eq use-recent-p 'exclusive) - (ring-p eshell-last-dir-ring) - (not (ring-empty-p eshell-last-dir-ring))) - (mapcar (function (lambda (file) (cons file file))) - (ring-elements eshell-last-dir-ring))) - ((append - (mapcar (function - (lambda (file) - (cons (file-name-nondirectory file) - file))) - links) - (cond - ((string-match-p short-cut mark-directory) - (list (cons mark-directory mark-directory)))))))) - ;; Delete duplicate items - (candidates - (durand-eshell-delete-dups - candidates - :test #'string= - ;; In Haskell this woule be a simple function composition. - :key (lambda (ls) (file-truename (cdr ls))))) - ;; Delete non-matching ones - (candidates - (delq - nil - (mapcar - (lambda (cand) - (cond ((string-match-p short-cut (cdr cand)) cand))) - candidates))) - ;; also include session candidates if required - (sessions + (mark-directory eshell-mark-directory) + (directory-key (lambda (cand) + (directory-file-name + (file-truename (cdr cand))))) + (path-matcher + (lambda (cand) (string-match-p short-cut (cdr cand)))) + (marks + (cond + ((memq 'marks sources) + (let ((links + (delq + nil + (mapcar + (lambda (name) + (cond + ((or (string= + "." (file-name-nondirectory name)) + (string= + ".." (file-name-nondirectory name))) + nil) + (name))) + (directory-files + mark-directory t short-cut t))))) + (append + (mapcar + (lambda (file) + (cons + (propertize + (file-name-nondirectory file) + 'face 'eshell-j-mark-name) + file)) + links) + (cond + ((string-match-p short-cut mark-directory) + (cons + (cons mark-directory mark-directory) + nil)))))))) + (recent (cond - ((or all-p use-session-p) (claude-session-candidates)))) - ;; filter sessions + ((and (memq 'recent sources) + (ring-p eshell-last-dir-ring) + (not (ring-empty-p eshell-last-dir-ring))) + (delq + nil + (mapcar + (lambda (file) + (cond + ((string-match-p short-cut file) + (cons file file)))) + (ring-elements eshell-last-dir-ring)))))) + (recent (durand-eshell-delete-dups + recent :test #'string= :key directory-key)) (sessions - (delq - nil - (mapcar - (lambda (session) - (cond - ((string-match-p short-cut (car session)) session))) - sessions))) - ;; merging - (candidates (cond - (use-recent-p (append candidates sessions)) - (use-session-p sessions) - (candidates)))) + ((memq 'session sources) + (delq + nil + (mapcar + (lambda (session) + (cond + ((string-match-p short-cut (car session)) session))) + (claude-session-candidates)))))) + (candidates (append marks recent sessions))) (cond ((null candidates) (user-error "No candidates matching %s found" short-cut)) - ((let ((dirs-of-cands - (durand-eshell-delete-dups - candidates - :key (lambda (cand) - (directory-file-name - (file-truename (cdr cand)))) - :test #'string=))) - (null (cdr dirs-of-cands))) - ;; Only one directory candidate + (list-p + (concat + (mapconcat + (lambda (cand) + (concat + (car cand) + " -> " + (file-truename (cdr cand)) + "\n")) + marks "") + (mapconcat + (lambda (cand) (concat (car cand) "\n")) + recent "") + (mapconcat + (lambda (cand) (concat (car cand) "\n")) + sessions ""))) + ;; jump if only one destination is present + ((null + (cdr + (durand-eshell-delete-dups + candidates + :key directory-key :test #'string=))) (let ((eshell-last-arguments (cons (file-truename (cdar candidates)) nil))) (eshell/cd (car eshell-last-arguments)))) @@ -456,17 +497,8 @@ session directories are in the list of candidates." (file-truename (cdr (assoc - (let ((candidates - (delq - nil - (mapcar - (lambda (cand) - (cond - ((string-match-p short-cut (car cand)) - cand))) - candidates)))) - (completing-read - "Choose a link: " candidates nil t)) + (completing-read + "Choose a link: " candidates nil t) candidates #'string=))) nil))) (eshell/cd (car eshell-last-arguments))))))) |
