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