home

My NixOS systems configurations.
Log | Files | Refs | LICENSE

config-shells.el (17369B)


      1 ;;; config-shells.el --- -*- lexical-binding: t; -*-
      2 ;;; Commentary:
      3 ;;; Shell scripting
      4 ;;; Code:
      5 
      6 (use-package shell
      7   :commands (shell)
      8   :bind (("<f1>"      . shell)
      9          (:map shell-mode-map
     10                ("<tab>" . completion-at-point)))
     11   :config
     12   (setq-default explicit-shell-file-name "zsh"
     13                 shell-file-name "zsh")
     14   (unbind-key "C-c C-l" shell-mode-map)
     15   (bind-key "C-c C-l" #'counsel-shell-history shell-mode-map))
     16 
     17 (defun run-in-compile (base-cmd &rest ARGS)
     18   "Use `compile' to run the BASE-CMD and ARGS, from eshell."
     19   (compile (concat base-cmd " " (apply #'concat ARGS))))
     20 
     21 ;; TODO: understand and rework eshell completion
     22 (use-package eshell
     23   :commands (eshell eshell-here)
     24   :bind* ("C-x m t" . eshell-here)
     25   :config
     26   (defun eshell/make (&rest ARGS)
     27     "Shortcut to more easily run builds in a compile buffer"
     28     (cond ((or (file-exists-p "Makefile")
     29 	       (file-exists-p "makefile"))
     30 	   (run-in-compile "make" ARGS))
     31 	  ((file-exists-p "build.zig")
     32 	   (run-in-compile "zig build"))
     33 	  (t "No supported build system found.")))
     34   (defun eshell-here ()
     35     "Open EShell in the directory associated with the current buffer's file.
     36 The EShell is renamed to match that directory to make multiple windows easier."
     37     (interactive)
     38     (let* ((parent (if (buffer-file-name)
     39                        (file-name-directory (buffer-file-name))
     40                      default-directory))
     41            (name   (car (last (split-string parent "/" t)))))
     42       (eshell "new")
     43       (rename-buffer (concat "*eshell: " name "*"))))
     44 
     45   ;; Handy aliases
     46   (defalias 'ff 'find-file)
     47   (defalias 'emacs 'find-file)
     48   (defalias 'e 'find-file)
     49   (defalias 'ec 'find-file)
     50   (defalias 'd 'dired)
     51 
     52   (defun eshell/gs (&rest args)
     53     (magit-status (pop args) nil)
     54     (eshell/echo))                      ; The echo command suppresses output
     55 
     56   (defun eshell/cdg ()
     57     "Change directory to the project's root."
     58     (eshell/cd (locate-dominating-file default-directory ".git")))
     59 
     60   (defun eshell/extract (file)
     61     "One universal command to extract FILE (for bz2, gz, rar, etc.)"
     62     (eshell-command-result (format "%s %s" (cond ((string-match-p ".*\.tar.bz2" file)
     63                                                   "tar xzf")
     64                                                  ((string-match-p ".*\.tar.gz" file)
     65                                                   "tar xzf")
     66                                                  ((string-match-p ".*\.bz2" file)
     67                                                   "bunzip2")
     68                                                  ((string-match-p ".*\.rar" file)
     69                                                   "unrar x")
     70                                                  ((string-match-p ".*\.gz" file)
     71                                                   "gunzip")
     72                                                  ((string-match-p ".*\.tar" file)
     73                                                   "tar xf")
     74                                                  ((string-match-p ".*\.tbz2" file)
     75                                                   "tar xjf")
     76                                                  ((string-match-p ".*\.tgz" file)
     77                                                   "tar xzf")
     78                                                  ((string-match-p ".*\.zip" file)
     79                                                   "unzip")
     80                                                  ((string-match-p ".*\.jar" file)
     81                                                   "unzip")
     82                                                  ((string-match-p ".*\.Z" file)
     83                                                   "uncompress")
     84                                                  (t
     85                                                   (error "Don't know how to extract %s" file)))
     86                                    file)))
     87 
     88   ;; From https://karthinks.com/software/jumping-directories-in-eshell/
     89   (defun eshell/j (&optional regexp)
     90     "Navigate to a previously visited directory in eshell, or to
     91 any directory proferred by `consult-dir'."
     92     (let ((eshell-dirs (delete-dups
     93                         (mapcar 'abbreviate-file-name
     94                                 (ring-elements eshell-last-dir-ring)))))
     95       (cond
     96        ((and (not regexp) (featurep 'consult-dir))
     97         (let* ((consult-dir--source-eshell `(:name "Eshell"
     98                                                    :narrow ?e
     99                                                    :category file
    100                                                    :face consult-file
    101                                                    :items ,eshell-dirs))
    102                (consult-dir-sources (cons consult-dir--source-eshell
    103                                           consult-dir-sources)))
    104           (eshell/cd (substring-no-properties
    105                       (consult-dir--pick "Switch directory: ")))))
    106        (t (eshell/cd (if regexp (eshell-find-previous-directory regexp)
    107                        (completing-read "cd: " eshell-dirs)))))))
    108 
    109   (add-hook
    110    'eshell-mode-hook
    111    (lambda ()
    112      (let ((ls (if (executable-find "exa") "exa" "ls")))
    113        (eshell/alias "ls" (concat ls " $*"))
    114        (eshell/alias "ll" (concat ls " -l $*"))
    115        (eshell/alias "l" (concat ls " -lah $*")))
    116      (eshell-smart-initialize)
    117      (eshell-dirs-initialize)
    118      (bind-keys :map eshell-mode-map
    119                 ("C-c C-l"                . counsel-esh-history)
    120                 ([remap eshell-pcomplete] . completion-at-point)
    121                 )))
    122 
    123   ;; Use system su/sudo
    124   (with-eval-after-load "em-unix"
    125     '(progn
    126        (unintern 'eshell/su nil)
    127        (unintern 'eshell/sudo nil)))
    128 
    129   (add-hook 'eshell-mode-hook #'with-editor-export-editor))
    130 
    131 (use-package em-prompt
    132   :after eshell
    133   :config
    134   (defun vde/eshell-quit-or-delete-char (arg)
    135     "Use C-d to either delete forward char or exit EShell."
    136     (interactive "p")
    137     (if (and (eolp) (looking-back eshell-prompt-regexp nil nil))
    138         (progn
    139           (eshell-life-is-too-much))
    140       (delete-char arg)))
    141 
    142   (add-hook 'eshell-mode-hook
    143             (lambda ()
    144               (bind-key "C-d"
    145                         #'vde/eshell-quit-or-delete-char eshell-mode-map))))
    146 
    147 (use-package esh-mode
    148   :disabled
    149   :after eshell
    150   :bind (:map eshell-mode-map
    151               ("<tab>" . vde/esh-mode-completion-at-point))
    152   :config
    153   (setq-default eshell-scroll-to-bottom-on-input 'all)
    154   (defun vde/esh-mode-completion-at-point ()
    155     "Same as `completion-at-point' except for some commands."
    156     (interactive)
    157     ;; unbinding pcomplete/make gives a chance to `bash-completion'
    158     ;; to complete make rules. Bash-completion is indeed more
    159     ;; powerfull than `pcomplete-make'.
    160     (cl-letf (((symbol-function 'pcomplete/make) nil))
    161       (completion-at-point))))
    162 
    163 (use-package em-smart
    164   :after eshell)
    165 (use-package em-dirs
    166   :after eshell)
    167 
    168 (use-package em-cmpl
    169   :after eshell
    170   :hook (eshell-mode . eshell-cmpl-initialize)
    171   :config
    172   (defun my/eshell-bash-completion ()
    173     (let ((bash-completion-nospace t))
    174       (while (pcomplete-here
    175               (nth 2 (bash-completion-dynamic-complete-nocomint
    176                       (save-excursion (eshell-bol) (point))
    177                       (point)))))))
    178   (when (require 'bash-completion nil t)
    179     (setq eshell-default-completion-function #'my/eshell-bash-completion))
    180 
    181   (add-to-list 'eshell-command-completions-alist
    182                '("gunzip" "gz\\'"))
    183   (add-to-list 'eshell-command-completions-alist
    184                '("tar" "\\(\\.tar|\\.tgz\\|\\.tar\\.gz\\)\\'")))
    185 
    186 (use-package em-hist
    187   :after eshell
    188   :config (setq eshell-hist-ignoredups t))
    189 
    190 (use-package em-tramp
    191   :after eshell)
    192 
    193 (use-package em-term
    194   :after eshell
    195   :config
    196   (add-to-list 'eshell-visual-commands "ssh")
    197   (add-to-list 'eshell-visual-commands "htop")
    198   (add-to-list 'eshell-visual-commands "top")
    199   (add-to-list 'eshell-visual-commands "tail")
    200   (add-to-list 'eshell-visual-commands "npm")
    201   (add-to-list 'eshell-visual-commands "ncdu"))
    202 
    203 (use-package em-banner
    204   :after eshell
    205   :config
    206   (setq eshell-banner-message "
    207   Welcome to the Emacs
    208 
    209                          _/                  _/  _/
    210       _/_/      _/_/_/  _/_/_/      _/_/    _/  _/
    211    _/_/_/_/  _/_/      _/    _/  _/_/_/_/  _/  _/
    212   _/            _/_/  _/    _/  _/        _/  _/
    213    _/_/_/  _/_/_/    _/    _/    _/_/_/  _/  _/
    214 
    215 "))
    216 
    217 (use-package eshell-prompt-extras
    218   :after eshell
    219   :custom
    220   (eshell-highlight-prompt nil)
    221   (eshell-prompt-function 'vde-theme-lambda)
    222   :config
    223   (setq epe-path-style 'fish
    224 	epe-fish-path-max-len 20)
    225   (defun vde-kubernetes-current-context ()
    226     "Return the current context"
    227     (if (not (string-empty-p (getenv "KUBECONFIG")))
    228         (epe-trim-newline (shell-command-to-string (concat
    229                                                     "env KUBECONFIG="
    230                                                     (getenv "KUBECONFIG")
    231                                                     " kubectl config current-context")))
    232       (epe-trim-newline (shell-command-to-string "kubectl config current-context"))))
    233   (defun vde-kubernetes-p ()
    234     "If you have kubectl install and a config set,
    235 using either KUBECONFIG or ~/.kube/config"
    236     (and (eshell-search-path "kubectl")
    237          (not (string-empty-p (vde-kubernetes-current-context)))
    238          (not (string-match-p "error: current-context is not set" (vde-kubernetes-current-context)))))
    239   ;; From epe-theme-lambda
    240   (defun vde-theme-lambda ()
    241     "A eshell-prompt lambda theme."
    242     (setq eshell-prompt-regexp "^[^#\nλ]*[#λ] ")
    243     (concat
    244      (when (epe-remote-p)
    245        (epe-colorize-with-face
    246         (concat (epe-remote-user) "@" (epe-remote-host) " ")
    247         'epe-remote-face))
    248      (when (and epe-show-python-info (bound-and-true-p venv-current-name))
    249        (epe-colorize-with-face (concat "(" venv-current-name ") ") 'epe-venv-face))
    250      (let ((f (cond ((eq epe-path-style 'fish) 'epe-fish-path)
    251                     ((eq epe-path-style 'single) 'epe-abbrev-dir-name)
    252                     ((eq epe-path-style 'full) 'abbreviate-file-name))))
    253        (epe-colorize-with-face (funcall f (eshell/pwd)) 'epe-dir-face))
    254      (when (epe-git-p)
    255        (concat
    256         (epe-colorize-with-face ":" 'epe-dir-face)
    257         (epe-colorize-with-face
    258          (concat (epe-git-branch)
    259                  (epe-git-dirty)
    260                  (epe-git-untracked)
    261                  (let ((unpushed (epe-git-unpushed-number)))
    262                    (unless (= unpushed 0)
    263                      (concat ":" (number-to-string unpushed)))))
    264          'epe-git-face)))
    265      (when (vde-kubernetes-p)
    266        (concat (epe-colorize-with-face " (" 'epe-dir-face)
    267                (epe-colorize-with-face (vde-kubernetes-current-context) 'epe-dir-face)
    268                (epe-colorize-with-face ")" 'epe-dir-face)))
    269      (epe-colorize-with-face " λ" 'epe-symbol-face)
    270      (epe-colorize-with-face (if (= (user-uid) 0) "#" "") 'epe-sudo-symbol-face)
    271      " ")))
    272 
    273 (use-package eat
    274   :init (setq eat-kill-buffer-on-exit t
    275 	      eat-enable-yank-to-terminal t)
    276   :hook ((eshell-mode . eat-eshell-mode)
    277 	 (eshell-mode . eat-eshell-visual-command-mode)))
    278 
    279 (use-package xterm-color
    280   :after eshell
    281   :init
    282   ;; (setq comint-output-filter-functions
    283   ;;       (remove 'ansi-color-process-output comint-output-filter-functions))
    284   (add-hook 'shell-mode-hook
    285             (lambda ()
    286               ;; Disable font-locking in this buffer to improve performance
    287               (font-lock-mode -1)
    288               ;; Prevent font-locking from being re-enabled in this buffer
    289               (make-local-variable 'font-lock-function)
    290               (setq font-lock-function (lambda (_) nil))
    291               (add-hook 'comint-preoutput-filter-functions 'xterm-color-filter nil t)))
    292   (add-hook 'eshell-before-prompt-hook
    293             (lambda ()
    294               (setenv "TERM" "xterm-256color")
    295               (setq xterm-color-preserve-properties t)))
    296   (add-to-list 'eshell-preoutput-filter-functions 'xterm-color-filter)
    297   (setq eshell-output-filter-functions (remove 'eshell-handle-ansi-color eshell-output-filter-functions))
    298   (setq compilation-environment '("TERM=xterm-256color")))
    299 
    300 (use-package vterm
    301   :commands (vterm vde/vterm-toggle)
    302   :bind (("C-c t v" . vde/vterm-toggle)
    303          ("C-c t r" . vde/run-in-vterm))
    304   :custom
    305   (vterm-kill-buffer-on-exit t)
    306   :config
    307   (defun vde/vterm-tramp-get-method-parameter (method param)
    308     "Return the method parameter PARAM.
    309 If the `tramp-methods' entry does not exist, return NIL."
    310     (let ((entry (assoc param (assoc method tramp-methods))))
    311       (when entry (cadr entry))))
    312   (add-hook 'vterm-set-title-functions 'vterm--rename-buffer-as-title)
    313   ;; TODO: hook into projectile-run-vterm instead
    314   ;; Also, look into vterm-toggle way of doing things.. I thing it is trying to be too smart about it..
    315   ;; I prefer an easy projectile integration (or projects integration)
    316   (defun vde/vterm ()
    317     ""
    318     (interactive)
    319     (let* ((dir (expand-file-name default-directory))
    320            cd-cmd cur-host vterm-dir vterm-host cur-user cur-port remote-p cur-method login-cmd)
    321       (if (ignore-errors (file-remote-p dir))
    322           (with-parsed-tramp-file-name dir nil
    323             (setq remote-p t)
    324             (setq cur-host host)
    325             (setq cur-method (tramp-find-method method user cur-host))
    326             (setq cur-user (or (tramp-find-user cur-method user cur-host) ""))
    327             (setq cur-port (or port ""))
    328             (setq dir localname))
    329         (setq cur-host (system-name)))
    330       (setq login-cmd (vde/vterm-tramp-get-method-parameter cur-method 'tramp-login-program))
    331       (setq cd-cmd (concat " cd " (shell-quote-argument dir)))
    332       (setq shell-buffer (format "vterm %s %s" cur-host dir))
    333       (if (buffer-live-p shell-buffer)
    334           (switch-to-buffer shell-buffer)
    335         (progn
    336           (message (format "buffer '%s' doesn't exists" shell-buffer))
    337           (vterm shell-buffer)
    338           (with-current-buffer shell-buffer
    339             (message (format "%s" remote-p))
    340             (when remote-p
    341               (let* ((method (if (string-equal login-cmd "ssh") "ssh" cur-method))
    342                      (login-opts (vde/vterm-tramp-get-method-parameter method 'tramp-login-args))
    343                      (login-shell (vde/vterm-tramp-get-method-parameter method 'tramp-remote-shell))
    344                      (login-shell-args (tramp-get-sh-extra-args login-shell))
    345                      ;; (vterm-toggle-tramp-get-method-parameter cur-method 'tramp-remote-shell)
    346                      (spec (format-spec-make
    347 			                ?h cur-host ?u cur-user ?p cur-port ?c ""
    348 			                ?l (concat login-shell " " login-shell-args)))
    349                      (cmd
    350                       (concat login-cmd " "
    351                               (mapconcat
    352 		                       (lambda (x)
    353 			                     (setq x (mapcar (lambda (y) (format-spec y spec)) x))
    354 			                     (unless (member "" x) (string-join x " ")))
    355 		                       login-opts " "))))
    356                 (vterm-send-string cmd)
    357                 (vterm-send-return)))
    358             (vterm-send-string cd-cmd)
    359             (vterm-send-return))))))
    360   (defun vde/vterm-toggle ()
    361     "Toggle between the main vterm buffer and the current buffer.
    362 If you are in a vterm buffer, switch the window configuration
    363 back to your code buffers. Otherwise, create at least one vterm
    364 buffer if it doesn't exist already, and switch to it. On every
    365 toggle, the current window configuration is saved in a register."
    366     (interactive)
    367     (if (eq major-mode 'vterm-mode)
    368         (jump-to-register ?W)
    369       ;; Save current window config and jump to shell
    370       (window-configuration-to-register ?W)
    371       (condition-case nil
    372           (jump-to-register ?Z)
    373         (error
    374          (vterm)
    375          (when (= (length (window-list)) 2)
    376            (other-window 1)
    377            (vterm 1)
    378            (other-window 1))))
    379       (window-configuration-to-register ?Z)))
    380   (buffer-name)
    381   (defun vde/run-in-vterm ()
    382     (interactive)
    383     (with-current-buffer "vterm"
    384       (vterm-send-string (read-string "Command: "))
    385       (vterm-send-C-j))))
    386 
    387 (use-package multi-vterm
    388   :commands (multi-vterm multi-vterm-projectile multi-vterm-dedicated-toggle)
    389   :bind (("C-c t t" . multi-vterm-dedicated-toggle)
    390          ("C-c t p" . multi-vterm-prev)
    391          ("C-c t n" . multi-vterm-next)
    392          ("C-c t s" . multi-vterm)))
    393 ;; for fish in ansi-term
    394 (add-hook 'term-mode-hook 'toggle-truncate-lines)
    395 
    396 (use-package tramp
    397   :defer t
    398   :config
    399   (setq-default tramp-use-ssh-controlmaster-options nil ; Don't override SSH config.
    400                 tramp-default-method "ssh") ; ssh is faster than scp and supports ports.
    401   (add-to-list 'tramp-remote-path "/run/current-system/sw/bin")
    402   (add-to-list 'tramp-remote-path "/etc/profiles/per-user/root/bin/")
    403   (add-to-list 'tramp-remote-path "/etc/profiles/per-user/vincent/bin/")
    404   (add-to-list 'tramp-remote-path "~/.nix-profile/bin")
    405   (add-to-list 'tramp-remote-path "~/bin")
    406   (add-to-list 'tramp-remote-path 'tramp-own-remote-path))
    407 
    408 (defun generic-term-init ()
    409   (visual-line-mode -1)
    410   (setq-local global-hl-line-mode nil)
    411   (setq-local scroll-margin 0))
    412 
    413 (add-hook 'term-mode-hook #'generic-term-init)
    414 (add-hook 'shell-mode-hook #'generic-term-init)
    415 (add-hook 'eshell-mode-hook #'generic-term-init)
    416 
    417 (provide 'config-shells)
    418 ;;; config-shells.el ends here