diff --git a/modules/tools/dired/config.el b/modules/tools/dired/config.el new file mode 100644 index 000000000..5d572006f --- /dev/null +++ b/modules/tools/dired/config.el @@ -0,0 +1,46 @@ +;;; emacs/dired/config.el + +(setq ;; Always copy/delete recursively + dired-recursive-copies 'always + dired-recursive-deletes 'top + ;; Auto refresh dired, but be quiet about it + global-auto-revert-non-file-buffers t + auto-revert-verbose nil) + +(defun +dired|sort-directories-first () + "List directories first in dired buffers." + (save-excursion + (let (buffer-read-only) + (forward-line 2) ;; beyond dir. header + (sort-regexp-fields t "^.*$" "[ ]*." (point) (point-max)))) + (and (featurep 'xemacs) + (fboundp 'dired-insert-set-properties) + (dired-insert-set-properties (point-min) (point-max))) + (set-buffer-modified-p nil)) +(add-hook 'dired-after-readin-hook '+dired|sort-directories-first) + +;; Automatically create missing directories when creating new files +(defun +dired|create-non-existent-directory () + (let ((parent-directory (file-name-directory buffer-file-name))) + (when (and (not (file-exists-p parent-directory)) + (y-or-n-p (format "Directory `%s' does not exist! Create it?" parent-directory))) + (make-directory parent-directory t)))) +(push '+dired|create-non-existent-directory find-file-not-found-functions) + + +;; +;; Packages +;; + +(@def-package dired-k + :after dired + :config + (setq dired-k-style 'git) + (add-hook 'dired-initial-position-hook 'dired-k) + (add-hook 'dired-after-readin-hook #'dired-k-no-revert)) + +;; Striped dired buffers +(@def-package stripe-buffer + :commands stripe-buffer-mode + :init (add-hook 'dired-mode-hook 'stripe-buffer-mode)) + diff --git a/modules/tools/dired/packages.el b/modules/tools/dired/packages.el new file mode 100644 index 000000000..78cd36383 --- /dev/null +++ b/modules/tools/dired/packages.el @@ -0,0 +1,6 @@ +;; -*- no-byte-compile: t; -*- +;;; emacs/dired/packages.el + +(@package dired-k) +(@package stripe-buffer) + diff --git a/modules/tools/eshell/autoload/eshell.el b/modules/tools/eshell/autoload/eshell.el new file mode 100644 index 000000000..53ca7279f --- /dev/null +++ b/modules/tools/eshell/autoload/eshell.el @@ -0,0 +1,119 @@ +;;; emacs/eshell/autoload/eshell.el + +(require 'eshell) + +;;;###autoload +(defun +eshell/run () + "Open eshell in the current buffer." + (interactive) + (let ((buf (generate-new-buffer eshell-buffer-name))) + (with-current-buffer buf + (unless (eq major-mode 'eshell-mode) (eshell-mode))) + (pop-to-buffer-same-window buf))) + +;;;###autoload +(defun +eshell/popup () + "Open eshell in a popup window." + (interactive) + (let ((buf (get-buffer-create "*eshell:popup*"))) + (with-current-buffer buf + (unless (eq major-mode 'eshell-mode) (eshell-mode))) + (doom-popup-buffer buf))) + +;;;###autoload +(defun +eshell/tab () + "Open eshell in a separate workspace. Requires the (:feature workspaces) +module to be loaded." + (interactive) + (unless (@featurep :feature workspaces) + (user-error ":feature workspaces is required, but disabled")) + (unless (+workspace-get "eshell" t) + (+workspace/new "eshell")) + (if-let (buf (cl-find-if (lambda (it) (string-match-p "^\\*eshell" (buffer-name (window-buffer it)))) + (doom-visible-windows))) + (select-window (get-buffer-window buf)) + (+eshell/run)) + (doom/workspace-display)) + +(defun +eshell--outside-prompt-p (&optional offset) + (< (point) eshell-last-output-end)) + +(defun +eshell--current-git-branch () + (let ((branch (car (loop for match in (split-string (shell-command-to-string "git branch") "\n") + when (string-match "^\*" match) + collect match)))) + (if (not (eq branch nil)) + (concat " [" (substring branch 2) "]") + ""))) + +;;;###autoload +(defun +eshell/split () + (interactive) + (select-window (split-window-vertically)) + (+eshell:run)) + +;;;###autoload +(defun +eshell/vsplit () + (interactive) + (select-window (split-window-horizontally)) + (+eshell:run)) + +;;;###autoload +(defun +eshell/prompt () + (concat (propertize (abbreviate-file-name (eshell/pwd)) 'face 'eshell-prompt) + (propertize (+eshell--current-git-branch) 'face 'font-lock-function-name-face) + (propertize " λ " 'face 'font-lock-constant-face))) + +;;;###autoload +(defun +eshell/evil-append () + (interactive) + (goto-char eshell-last-output-end) + (call-interactively 'evil-append-line)) + +;;;###autoload +(defun +eshell/evil-append-maybe () + (interactive) + (if (+eshell--outside-prompt-p) + (+eshell/evil-append) + (call-interactively 'evil-append))) + +;;;###autoload +(defun +eshell/evil-prepend () + (interactive) + (goto-char eshell-last-output-end) + (call-interactively 'evil-insert)) + +;;;###autoload +(defun +eshell/evil-prepend-maybe () + (interactive) + (if (+eshell--outside-prompt-p) + (+eshell/evil-prepend) + (call-interactively 'evil-insert))) + +;;;###autoload +(defun +eshell/evil-replace-maybe () + (interactive) + (if (+eshell--outside-prompt-p) + (user-error "Cannot edit read-only region") + (call-interactively 'evil-replace))) + +;;;###autoload +(defun +eshell/evil-replace-state-maybe () + (interactive) + (if (+eshell--outside-prompt-p) + (user-error "Cannot edit read-only region") + (call-interactively 'evil-replace-state))) + +;;;###autoload +(defun +eshell/evil-change () + (interactive) + (when (+eshell--outside-prompt-p) + (goto-char eshell-last-output-end)) + (call-interactively 'evil-change)) + +;;;###autoload +(defun +eshell/evil-change-line () + (interactive) + (when (+eshell--outside-prompt-p) + (goto-char eshell-last-output-end)) + (call-interactively 'evil-change-line)) diff --git a/modules/tools/eshell/autoload/evil.el b/modules/tools/eshell/autoload/evil.el new file mode 100644 index 000000000..29f9dc235 --- /dev/null +++ b/modules/tools/eshell/autoload/evil.el @@ -0,0 +1,9 @@ +;;; emacs/eshell/autoload/evil.el + +;;;###autoload (autoload '+eshell:run "emacs/eshell/autoload/evil" nil t) +(evil-define-command +eshell:run (command bang) + (interactive "") + (if bang + (+eshell/run) + (+eshell/popup))) + diff --git a/modules/tools/eshell/config.el b/modules/tools/eshell/config.el new file mode 100644 index 000000000..4d96b6b66 --- /dev/null +++ b/modules/tools/eshell/config.el @@ -0,0 +1,92 @@ +;;; emacs/eshell/config.el + +;; This is highly experimental. I don't use eshell often, so this may need work. + +;; see: +;; + `+eshell/run': open in current buffer +;; + `+eshell/tab': open in separate tab (requires :feature workspaces) +;; + `+eshell/popup': open in a popup + +(defvar +eshell-buffers '() + "TODO") + +(@def-package eshell ; built-in + :init + (setq eshell-directory-name (concat doom-cache-dir "/eshell") + eshell-scroll-to-bottom-on-input 'all + eshell-scroll-to-bottom-on-output 'all + eshell-buffer-shorthand t + ;; em-prompt + eshell-prompt-function '+eshell/prompt + ;; em-glob + eshell-glob-case-insensitive t + eshell-error-if-no-glob t + ;; em-alias + eshell-aliases-file "~/.eshell-aliases") + + :config + (@set :popup "^\\*eshell:popup\\*$" :regexp t :size 25) + (@set :evil-state 'eshell-mode 'insert) + + (defun +eshell|keymap-setup () + "Setup eshell keybindings. This must be done in a hook because eshell +redefines its keys every time `eshell-mode' is enabled." + (@map :map eshell-mode-map + :n "i" '+eshell/evil-prepend-maybe + :n "I" '+eshell/evil-prepend + :n "a" '+eshell/evil-append-maybe + :n "A" '+eshell/evil-append + :n "r" '+eshell/evil-replace-maybe + :n "R" '+eshell/evil-replace-state-maybe + :n "c" '+eshell/evil-change + :n "C" '+eshell/evil-change-line + :i "" 'eshell-pcomplete + :i "C-u" 'eshell-kill-input + :i "SPC" 'self-insert-command + :m "" '+eshell/evil-append + :n [remap evil-window-split] '+eshell/split + :n [remap evil-window-vsplit] '+eshell/vsplit + :n [remap evil-record-macro] 'eshell-life-is-too-much + [remap doom/close-window-or-tab] 'eshell-life-is-too-much)) + (add-hook 'eshell-mode-hook 'doom|eshell-keymap-setup) + + (defun +eshell|cleanup () + "Close window (or workspace) on quit." + (setq +eshell-buffers (delete (current-buffer) +eshell-buffers)) + (cond ((doom-popup-p) + (delete-window)) + ((and (@featurep :feature workspaces) + (string= "eshell" (+workspace-current-name))) + (+workspace/close-window-or-workspace)))) + (add-hook 'eshell-exit-hook 'doom|eshell-cleanup) + + (defun +eshell|init () + "Keep track of eshell buffers." + (add-to-list '+eshell-buffers (current-buffer))) + (add-hook 'eshell-mode-hook 'doom|eshell-init) + + (add-hook 'eshell-mode-hook 'doom-hide-modeline-mode) + + (@add-hook eshell-mode + (add-hook 'evil-insert-state-exit-hook 'hl-line-mode nil t) + (add-hook 'evil-insert-state-entry-hook (lambda () (hl-line-mode -1)) nil t)) + + ;; Aliases + (setq eshell-command-aliases-list + '(("q" "exit") + ("l" "ls -1") + ("ll" "ls -l") + ("la" "ls -la") + ("g" "hub") + ("gs" "hub status --oneline .") + ("gss" "hub status --oneline"))) + + ;; Custom commands + ;; (defun eshell/e (file) + ;; (eshell-eval (cond ((doom/popup-p) + ;; (doom/popup-save (find-file file)) + ;; 0) + ;; (t (find-file file) + ;; 0)))) + ) +