reddit / emacs
How to put icons into eshell 'ls'?
mclearc · 2022-09-11 17:33
I'm trying to have eshell show icons of files and dirs using 'ls' via all-the-icons. The below code works for line display (e.g. 'ls -l'). But not for the simple column display. Anyone have any ideas what the problem might be?
https://preview.redd.it/17vupxw0m9n91.png?width=3100&format=png&auto=webp&s=16c48e3119f14f69d86bd28b4b1f2e5bdb379bc9
and here's the function:
(defun lem-eshell-icons (f file)
(let* ((name (funcall f file))
(icon (if (eq (cadr file) t)
(all-the-icons-icon-for-dir (car file))
(all-the-icons-icon-for-file (car file)))))
(concat
icon
name
)))
(advice-add 'eshell-ls-decorated-name :around #'lem-eshell-icons)
EDIT: Thanks to organicbookworm I now have the following working code (note this includes iconification and dir marking as well...)
EDIT2: Forgot to include the keymap & related code...(now added).
For the whole eshell config see: [https://github.com/Lambda-Emacs/lambda-emacs/blob/main/.local/lambda-library/lambda-setup/lem-setup-eshell.el](https://github.com/Lambda-Emacs/lambda-emacs/blob/main/.local/lambda-library/lambda-setup/lem-setup-eshell.el)
(defun lem-eshell-prettify (file)
"Add features to listings in `eshell/ls' output.
The features are:
1. Add decoration like 'ls -F':
* Mark directories with a `/'
* Mark executables with a `*'
2. Make each listing into a clickable link to open the
corresponding file or directory.
3. Add icons (requires `all-the-icons`)
This function is meant to be used as advice around
`eshell-ls-annotate', where FILE is the cons describing the file."
(let* ((name (car file))
(icon (if (eq (cadr file) t)
(all-the-icons-icon-for-dir name)
(all-the-icons-icon-for-file name)))
(suffix
(cond
;; Directory
((eq (cadr file) t)
"/")
;; Executable
((and (/= (user-uid) 0) ; root can execute anything
(eshell-ls-applicable (cdr file) 3 #'file-executable-p (car file)))
"*"))))
(cons
(concat " "
icon
" "
(propertize name
'keymap eshell-ls-file-keymap
'mouse-face 'highlight
'file-name (expand-file-name (substring-no-properties (car file)) default-directory))
(when (and suffix (not (string-suffix-p suffix name)))
(propertize suffix 'face 'shadow)))
(cdr file)
)))
(defun eshell-ls-file-at-point ()
"Get the full path of the Eshell listing at point."
(get-text-property (point) 'file-name))
(defun eshell-ls-find-file ()
"Open the Eshell listing at point."
(interactive)
(find-file (eshell-ls-file-at-point)))
(defun eshell-ls-delete-file ()
"Delete the Eshell listing at point."
(interactive)
(let ((file (eshell-ls-file-at-point)))
(when (yes-or-no-p (format "Delete file %s?" file))
(delete-file file 'trash))))
(defvar eshell-ls-file-keymap
(let ((map (make-sparse-keymap)))
(define-key map (kbd "RET") #'eshell-ls-find-file)
(define-key map (kbd "<return>") #'eshell-ls-find-file)
(define-key map [mouse-1] #'eshell-ls-find-file)
(define-key map (kbd "D") #'eshell-ls-delete-file)
map)
"Keys in effect when point is over a file from `eshell/ls'.")
(advice-add #'eshell-ls-annotate :filter-return #'lem-eshell-prettify)
And it looks like this:
https://preview.redd.it/v3ot6rr4lfn91.png?width=3116&format=png&auto=webp&s=eb25ef9c7b77dcbfa8e319bd21bbd4874b46e1b4
​
deaddyfreddy · 2022-09-11 17:57
I just use `dired`+`all-the-icons-dired`
mclearc · 2022-09-11 18:02
Sure - so do I. But I’d like to have the icons in eshell too.
xplosm · 2022-09-11 20:12
Is using `exa` as an `ls` alternative an option? I don’t remember if eshell lets you use other non-lisp alternatives…
mclearc · 2022-09-11 20:20
It is! I discovered yesterday that you can just alias ‘ls’ to it. That’s what I’ll do if I can’t get this working. But I’d prefer a pure elisp solution if possible.
mclearc · 2022-09-11 20:26
I guess I should say that I’m also just confused as to why the code works for ls -a but not for the column view (where the icons are duplicated).
ivotade · 2022-09-11 23:54
If I change the code to this
(defun lem-eshell-icons (f file)
(let* ((name (funcall f file)))
(concat
"A"
name
)))
(advice-add 'eshell-ls-decorated-name :around #'lem-eshell-icons)
the `-l` output is correct, a non-colored `A` followed by the colored name show up. But if it is `ls` witout options, it comes with a non-colored `A`, followed by a collored `A`, and then the colored name. So I guess somewhere in eshell code for naked `ls` it is calling the function twice.
The eshell code is too big, too many indirections, so with my shitty debugging skills, I change the code to
(defun lem-eshell-icons (f file)
(let* ((name (funcall f file)))
(backtrace)
(concat
"A"
name
)))
and execute in a folder with just one file. I confirm my suspicions. The default eshell code is [idempotent](https://en.wikipedia.org/wiki/Idempotence#Idempotent_functions). So I guess you can either fix/change another part of the eshell code or make your function not change the name if it is already changed, if possible.
mclearc · 2022-09-12 00:32
Thanks for this. I’ve done some poking into the em-ls code but haven’t yet figured out what the precise issue is. But I’m glad to see that others are reproducing the problem. If I figure out the issue I’ll certainly post it above.
_viz_ · 2022-09-12 00:49
You might want to reconsider this eye candy since you will be making the output harder to manipulate into a new input.
mclearc · 2022-09-12 00:58
Yeah that’s a fair point, though it would be nice to have at least the option….
gopar · 2022-09-12 01:34
If you use exa, you can use the \`--icons\` param to avoid writing elisp and potentially breaking something:
https://the.exa.website/features/icons
mclearc · 2022-09-12 01:40
Yes, thanks. Like I said above I will probably end up doing this (I already use it in iTerm). But I wanted to be sure I wasn't missing something about doing this in pure elisp.
organic-bookworm · 2022-09-12 07:44
I did a bit of debugging. If you are fine with link *targets* (i.e. `link -> target` lines in `ls -la` output) not being iconified, here's an edited snippet
(defun lem-eshell-icons (file)
(let* ((name (car file))
(icon (if (eq (cadr file) t)
(all-the-icons-icon-for-dir name)
(all-the-icons-icon-for-file name))))
(cons
(concat icon name)
(cdr file)
)))
(advice-add #'eshell-ls-annotate :filter-return #'lem-eshell-icons)
We can do targets too, but that would be less pretty.
mclearc · 2022-09-12 13:32
Looks great! Nice work -- thanks! I'll edit my question with the final code (which includes iconified file names)
_viz_ · 2022-09-12 15:38
You can put them behind a comment character but at that point why even bother?
divinedominion · 2022-09-13 07:10
/u/mclearc Your changed code doesn't work on it's own:
$ ls
Symbol’s value as variable is void: eshell-ls-file-keymap
That's used here:
(propertize name
'keymap eshell-ls-file-keymap
'mouse-face 'highlight
'file-name (expand-file-name (substring-no-properties (car file)) default-directory))
I copied the "make output of ls clickable" code from https://www.emacswiki.org/emacs/EshellEnhancedLS and changed the keymap's name to get it to work somewhat:
(eval-after-load "em-ls"
'(progn
(defun ted-eshell-ls-find-file-at-point (point)
"RET on Eshell's `ls' output to open files."
(interactive "d")
(find-file (buffer-substring-no-properties
(previous-single-property-change point 'help-echo)
(next-single-property-change point 'help-echo))))
(defun pat-eshell-ls-find-file-at-mouse-click (event)
"Middle click on Eshell's `ls' output to open files.
From Patrick Anderson via the wiki."
(interactive "e")
(ted-eshell-ls-find-file-at-point (posn-point (event-end event))))
(let ((map (make-sparse-keymap)))
(define-key map (kbd "RET") 'ted-eshell-ls-find-file-at-point)
(define-key map (kbd "<return>") 'ted-eshell-ls-find-file-at-point)
(define-key map (kbd "<mouse-2>") 'pat-eshell-ls-find-file-at-mouse-click)
(defvar eshell-ls-file-keymap map))
(defadvice eshell-ls-decorated-name (after ted-electrify-ls activate)
"Eshell's `ls' now lets you click or RET on file names to open them."
(add-text-properties 0 (length ad-return-value)
(list 'help-echo "RET, mouse-2: visit this file"
'mouse-face 'highlight
'keymap eshell-ls-file-keymap)
ad-return-value)
ad-return-value)))
mclearc · 2022-09-13 12:35
Thanks -- I had forgotten to include the keymap & related. Now added above, as well as a link to the full eshell config.