diff --git a/README.org b/README.org index 9b430f1..45c80a4 100644 --- a/README.org +++ b/README.org @@ -3,4 +3,4 @@ This is a modernised GNU Emacs Configuration, an evolution of my old emacs configuration: `https://git.cspark.dev/cspark/Emacs-Configuration` -It is intended to be more focused and clean, using modern more modular/simpler packages or the builtins. +Using more modern packages (Switching from company, lsp mode, EXWM to consult/vertico, eglot mode, EWM for example) and personal built emacs lisp. diff --git a/init.el b/init.el index be72a1d..0587aae 100644 --- a/init.el +++ b/init.el @@ -61,6 +61,14 @@ (`(:propertize (,_ minor-mode-alist . ,_) . ,_) "") (t elem))) mode-line-modes)) +; Hide other items +(line-number-mode -1) +(column-number-mode -1) +(setq mode-line-percent-position nil) +(with-eval-after-load 'evil + (setq evil-mode-line-format nil)) +; Emacs 31 feature only - Collapse minor modes into one +(setq mode-line-collapse-minor-modes '(not)) ;; Clipboard config (setopt select-active-regions nil) @@ -198,36 +206,52 @@ (setq org-hide-emphasis-markers nil)) (with-eval-after-load 'org (with-eval-after-load 'evil - ; Unbind stubborn org mode bindings + ; Unbind stubborn org mode bindings (define-key org-mode-map (kbd "M-h") nil) (define-key org-mode-map (kbd "M-l") nil) (evil-define-key* '(normal) org-mode-map (kbd "M-h") nil) (evil-define-key* '(normal) org-mode-map (kbd "M-l") nil))) (with-eval-after-load 'org-agenda - (setq org-agenda-files '("~/Nextcloud/Agenda")) - ;This is will integrate the Calendar/Diary into Org-Agenda, so you can get access to dates on public holidays etc. Set diary to true: - (setq org-agenda-include-diary t) - ;Ensure done date/closed timestamps are logged - (setq org-log-done 'time) - ;Ensure state changes are logged into logbook - (setq org-log-into-drawer "LOGBOOK") + (with-eval-after-load 'evil + (setq org-agenda-files '("~/Nextcloud/Agenda")) + ;This is will integrate the Calendar/Diary into Org-Agenda, so you can get access to dates on public holidays etc. Set diary to true: + (setq org-agenda-include-diary t) + ;Ensure done date/closed timestamps are logged + (setq org-log-done 'time) + ;Ensure state changes are logged into logbook + (setq org-log-into-drawer "LOGBOOK") - ;Ensure agenda still shows DONE items - (setq org-agenda-skip-scheduled-if-done nil) - (setq agenda-skip-deadline-if-done nil) + ;Ensure agenda still shows DONE items + (setq org-agenda-skip-scheduled-if-done nil) + (setq agenda-skip-deadline-if-done nil) - ;Autostart in day view - (setq org-agenda-span 'day) + ;Autostart in day view + (setq org-agenda-span 'day) - (add-hook 'org-agenda-mode-hook #'(lambda () - (org-agenda-entry-text-mode 1))) + ;Auto save agenda buffers on refresh + (advice-add 'org-agenda-redo :before #'org-save-all-org-buffers) - (define-key org-agenda-mode-map (kbd "C-j") 'evil-next-line) - (define-key org-agenda-mode-map (kbd "C-k") 'evil-previous-line) - (define-key org-agenda-mode-map "d" 'org-agenda-toggle-day-view) - (define-key org-agenda-mode-map "p" 'org-pomodoro)) + (add-hook 'org-agenda-mode-hook #'(lambda () + (org-agenda-entry-text-mode 1))) - ; For Org Pomodoro notification sound + (define-key org-agenda-mode-map (kbd "C-j") 'org-agenda-goto-date) + (define-key org-agenda-mode-map "j" 'evil-next-line) + (define-key org-agenda-mode-map "k" 'evil-previous-line) + + (define-key org-agenda-mode-map (kbd "V") nil) + (define-key org-agenda-mode-map (kbd "V") 'evil-visual-line) + + (define-key org-agenda-mode-map "g" nil) + (define-key org-agenda-mode-map (kbd "g g") 'evil-goto-first-line) + (define-key org-agenda-mode-map (kbd "G") 'evil-goto-line) + + (define-key org-agenda-mode-map "s" 'org-agenda-schedule) + (define-key org-agenda-mode-map "d" 'org-agenda-schedule) + + (define-key org-agenda-mode-map (kbd "C-d") 'org-agenda-toggle-day-view) + (define-key org-agenda-mode-map "p" 'org-pomodoro))) + +; For Org Pomodoro notification sound (use-package sound-wav :ensure t :demand t) @@ -236,13 +260,13 @@ "Play a sound notification and show message ALERT." (sound-wav-play (expand-file-name (concat user-emacs-directory "elpaca/builds/org-pomodoro/resources/bell.wav"))) (message alert)) - ; Org timer custom alert +; Org timer custom alert (defun org-timer-sound-alert () "Sound notification on org timer finish." (sound-alert "Timer done!")) (add-hook 'org-timer-done-hook 'org-timer-sound-alert)) - ; Org Pomodoro +; Org Pomodoro (use-package org-pomodoro :ensure t :after (org org-agenda sound-wav) @@ -260,7 +284,7 @@ :title "Custom Notification" :notifier (lambda (info) - ; 'info' is a plist containing :title, :message, :severity, etc. +; 'info' is a plist containing :title, :message, :severity, etc. (notifications-notify :title "Emacs Alert" :body (plist-get info :message)) @@ -269,7 +293,7 @@ (plist-get info :message))))) (setq alert-default-style 'custom-org-alert-notification)) - ; System notifications of org agenda items +; System notifications of org agenda items (use-package org-wild-notifier :ensure t :after org-agenda @@ -289,14 +313,14 @@ (setq org-wild-notifier-extra-alert-plist '(:persistent t)) (org-wild-notifier-mode)) - ; Upcoming agenda items in modeline +; Upcoming agenda items in modeline (use-package org-upcoming-modeline :ensure t - :after org-agenda ; if you don't want it to start until org has been loaded + :after org-agenda :config (org-upcoming-modeline-mode)) - ; Org Mode Journalling +; Org Mode Journalling (use-package org-journal :ensure t :after org-agenda @@ -306,7 +330,7 @@ (setq org-journal-file-format "%Y%m%d.org")) ;; Org Latex Preview Scale - ; Automatically toggle Org mode LaTeX fragment previews as the cursor enters and exits them +; Automatically toggle Org mode LaTeX fragment previews as the cursor enters and exits them (use-package org-fragtog :ensure t :after org @@ -741,12 +765,17 @@ (setq emms-source-file-directory-tree-function 'emms-source-file-directory-tree-find) (setq emms-cache-set-all-from-mpd t) + (setq emms-browser-makes-thumbnails t) + (setq emms-browser-thumbnail-small-width 100) + + (setq emms-mode-line-length-limit 20) + (emms-all) (emms-player-mpd-connect) (add-hook 'emms-playlist-cleared-hook 'emms-player-mpd-clear) (setq emms-player-list '(emms-player-mpd)) - (setq emms-player-mpd-supported-regexp ".") + (setq emms-player-mpd-supported-regexp "\\`http[s]?://\\|\\.\\([Mm]3[Uu]\\|[Oo][Gg][Gg]\\|[Ff][Ll][Aa][Cc]\\|[Mm][Pp]3\\|[Ww][Aa][Vv]\\|[Mm][Oo][Dd]\\|[Aa][Uu]\\|[Aa][Ii][Ff][Ff]\\|[Oo][Pp][Uu][Ss]\\)\\'") (add-to-list 'emms-info-functions 'emms-info-mpd) (define-key emms-playlist-mode-map (kbd "d") nil) @@ -1053,7 +1082,7 @@ (setq tab-bar-show 1) ; Hide the bar if only 1 tab exists (setq tab-bar-new-tab-choice "*scratch*") - (defun dynamic-tab-ensure-scratch-tab (&rest _) + (defun dynamic-tab--ensure-scratch-tab (&rest _) "Ensure dynamic scratch tab available if there are active buffers." (let* ((tabs (funcall tab-bar-tabs-function)) (tab-names (mapcar (lambda (tab) (alist-get 'name tab)) tabs)) @@ -1081,28 +1110,28 @@ ;; Return to original tab if we were just initializing (tab-bar-select-tab (1+ current-tab)))))) - (defvar dynamic-tab-is-already-redirecting-p nil + (defvar dynamic-tab--is-already-redirecting-p nil "Whether we are already redirecting to scratch tab or not.") - (defun dynamic-tab-redirect-to-scratch-tab () + (defun dynamic-tab--redirect-to-scratch-tab () "Redirect to dedicated scratch tab if scratch buffer accessed on other tab." - (when (and (not dynamic-tab-is-already-redirecting-p) + (when (and (not dynamic-tab--is-already-redirecting-p) (string= (buffer-name) "*scratch*") (string= (buffer-name) "dynamic *scratch*")) - (setq dynamic-tab-is-already-redirecting-p t) + (setq dynamic-tab--is-already-redirecting-p t) (unwind-protect (switch-to-buffer nil) (tab-bar-select-tab-by-name "dynamic *scratch*") - (setq dynamic-tab-is-already-redirecting-p nil)))) + (setq dynamic-tab--is-already-redirecting-p nil)))) - (defvar dynamic-tab-soft-kill-p nil + (defvar dynamic-tab--soft-kill-p nil "When t, do not kill buffers only send them to background unless one tab open.") - (defun dynamic-tab-handle-buffer-check-and-close-tab () + (defun dynamic-tab--handle-buffer-check-and-close-tab () "Check if we need to close the tab and action accordingly." (let* ((is-last-tab (<= (length (tab-bar-tabs)) 2)) (is-last-buffer (<= (length (tab-line-tabs-window-buffers)) 1))) (when (and (not is-last-tab) is-last-buffer) (tab-close)))) - (defun dynamic-tab-handle-tab-cleanup-kill () + (defun dynamic-tab--handle-tab-cleanup-kill () "Check if we can kill buffer and free tab if no more reserved buffers in tab." (let* ((is-last-tab (<= (length (tab-bar-tabs)) 2)) (is-last-buffer (<= (length (tab-line-tabs-window-buffers)) 2)) @@ -1111,27 +1140,27 @@ ;((and is-last-tab (not is-last-buffer)) t) ;((and is-last-tab (not is-last-buffer) (bury-buffer))) - ((and (not is-last-tab) (not is-last-buffer) dynamic-tab-soft-kill-p) (bury-buffer)) + ((and (not is-last-tab) (not is-last-buffer) dynamic-tab--soft-kill-p) (bury-buffer)) (t t)))) - (dynamic-tab-handle-buffer-check-and-close-tab) + (dynamic-tab--handle-buffer-check-and-close-tab) is-marked-to-kill)) - (defun dynamic-tab-block-buffer-kill () + (defun dynamic-tab--block-buffer-kill () "Block buffer from being killed." (message "You cannot kill the dynamic buffer") nil) - (defun dynamic-tab-mark-buffer-unkillable () + (defun dynamic-tab--mark-buffer-unkillable () "Mark buffer as unkillable." - (add-hook 'kill-buffer-query-functions #'dynamic-tab-block-buffer-kill nil t)) - (defun dynamic-tab-mark-buffer-reserved () + (add-hook 'kill-buffer-query-functions #'dynamic-tab--block-buffer-kill nil t)) + (defun dynamic-tab--mark-buffer-reserved () "Mark buffer as reserved." - (add-hook 'kill-buffer-query-functions #'dynamic-tab-handle-tab-cleanup-kill nil t)) - (defun dynamic-tab-unmark-buffer-reserved () + (add-hook 'kill-buffer-query-functions #'dynamic-tab--handle-tab-cleanup-kill nil t)) + (defun dynamic-tab--unmark-buffer-reserved () "Unmark buffer as reserved." - (remove-hook 'kill-buffer-query-functions #'dynamic-tab-handle-tab-cleanup-kill t)) - (defun dynamic-tab-refresh-listeners () + (remove-hook 'kill-buffer-query-functions #'dynamic-tab--handle-tab-cleanup-kill t)) + (defun dynamic-tab--refresh-listeners () "Refresh hooks to listen on reserved buffers." (dolist (buf (tab-line-tabs-window-buffers)) (with-current-buffer buf @@ -1142,36 +1171,60 @@ (cond (is-child-buffer nil) (is-mini-buffer nil) - (is-dynamic-buffer (dynamic-tab-mark-buffer-unkillable)) - (is-scratch-buffer (dynamic-tab-mark-buffer-unkillable)) + (is-dynamic-buffer (dynamic-tab--mark-buffer-unkillable)) + (is-scratch-buffer (dynamic-tab--mark-buffer-unkillable)) - (t (dynamic-tab-mark-buffer-reserved))))))) - (defun dynamic-tab-remove-listeners () + (t (dynamic-tab--mark-buffer-reserved))))))) + (defun dynamic-tab--remove-listeners () "Remove all hooks on reserved buffers." (dolist (buf (buffer-list)) (with-current-buffer buf - (dynamic-tab-unmark-buffer-reserved)))) + (dynamic-tab--unmark-buffer-reserved)))) - (defvar dynamic-tab-last-buffer nil) - (defun dynamic-tab-handle-buffer-switch (&rest _) + (defvar dynamic-tab--last-buffer nil) + (defun dynamic-tab--handle-buffer-switch (&rest _) "Checks to run when buffer is switched." - (unless (eq (current-buffer) dynamic-tab-last-buffer) - (setq dynamic-tab-last-buffer (current-buffer)) + (unless (eq (current-buffer) dynamic-tab--last-buffer) + (setq dynamic-tab--last-buffer (current-buffer)) ;; Put your "on switch" logic here - (dynamic-tab-ensure-scratch-tab) - (dynamic-tab-redirect-to-scratch-tab))) - (defun dynamic-tab-handle-buffer-create (&rest _) + (dynamic-tab--ensure-scratch-tab) + (dynamic-tab--redirect-to-scratch-tab))) + (defun dynamic-tab--handle-buffer-create (&rest _) "Checks to run when buffer is created." - (dynamic-tab-ensure-scratch-tab) - (dynamic-tab-refresh-listeners)) + (dynamic-tab--ensure-scratch-tab) + (dynamic-tab--refresh-listeners)) (defun dynamic-tab-init () "Initialise dynamic tabs." - (add-hook 'window-buffer-change-functions #'dynamic-tab-handle-buffer-switch) - (add-hook 'after-change-major-mode-hook #'dynamic-tab-handle-buffer-create)) + (add-hook 'window-buffer-change-functions #'dynamic-tab--handle-buffer-switch) + (add-hook 'after-change-major-mode-hook #'dynamic-tab--handle-buffer-create)) + + (defun dynamic-tab-move-tab-left () + "Move tab left, no wrapping or moving the dedicated dynamic tab." + (let* ((is-first-tab (= (tab-bar--current-tab-index) 0)) + (is-dynamic-tab-focused (string= (alist-get 'name (tab-bar--current-tab)) "dynamic *scratch*"))) + (when (and (not is-first-tab) + (not is-dynamic-tab-focused)) + (tab-bar-move-tab-backward)))) + (defun dynamic-tab-move-tab-right () + "Move tab right, no wrapping or moving past the dedicated dynamic tab." + (let* ((is-last-tab (= (tab-bar--current-tab-index) (- (length (tab-bar-tabs)) 2))) + (is-dynamic-tab-focused (string= (alist-get 'name (tab-bar--current-tab)) "dynamic *scratch*"))) + (when (and (not is-last-tab) + (not is-dynamic-tab-focused)) + (tab-bar-move-tab 1)))) + + ; Generic tab helper functions + (defun delete-window-override (&rest _) + "Override for delete window, close tab if only one window instead." + (if (<= (length (window-list)) 1) + (progn + (tab-close) + nil) + t)) ;; Helper functions for EWM - ; Switch monitor + ; Switch monitor (defun ewm-switch-to-monitor (target) "Switch focus to target monitor." (interactive) @@ -1179,14 +1232,14 @@ (string= target (cdr (assq 'name (frame-monitor-attributes f))))) (frame-list)))))) - ; Autostart + ; Autostart (defun ewm-autostart-application (target &optional args) "Autostart application if not already started." (unless (zerop (shell-command (concat "pidof " target))) (start-process-shell-command (concat target "-autostart") nil (concat target " " args)))) - ; EWM - Emacs Wayland Manager + ; EWM - Emacs Wayland Manager (use-package ewm :ensure nil :after evil @@ -1228,12 +1281,12 @@ "grim -g \"$(slurp)\" - | wl-copy"))) ("M-w" . (lambda () (interactive) - (setq dynamic-tab-soft-kill-p t) + (setq dynamic-tab--soft-kill-p t) (kill-buffer) - (setq dynamic-tab-soft-kill-p nil))) + (setq dynamic-tab--soft-kill-p nil))) ("C-x k" . (lambda () (interactive) - (setq dynamic-tab-soft-kill-p nil) + (setq dynamic-tab--soft-kill-p nil) (call-interactively 'kill-buffer))) ; Monitor switching, find current focused monitor name via @@ -1254,6 +1307,12 @@ ("M-h" . tab-bar-switch-to-prev-tab) ("M-l" . tab-bar-switch-to-next-tab) + ("M-H" . (lambda () + (interactive) + (dynamic-tab-move-tab-left))) + ("M-L" . (lambda () + (interactive) + (dynamic-tab-move-tab-right))) ("M-k" . windmove-up) ("M-j" . windmove-down)) :config @@ -1264,13 +1323,16 @@ (add-to-list 'ewm-intercept-prefixes ?\M-p) (add-to-list 'ewm-intercept-prefixes ?\M-h) (add-to-list 'ewm-intercept-prefixes ?\M-j) + (add-to-list 'ewm-intercept-prefixes ?\M-H) + (add-to-list 'ewm-intercept-prefixes ?\M-J) (add-to-list 'ewm-intercept-prefixes ?\M-k) (add-to-list 'ewm-intercept-prefixes ?\M-l) (ewm--send-intercept-keys) :init ;; Dynamic Tabs (dynamic-tab-init) - + (advice-add 'delete-window :before-while #'delete-window-override)) + (with-eval-after-load 'ewm ;; Autostart (ewm-autostart-application "qpwgraph") (ewm-autostart-application "kdeconnect-indicator") @@ -1278,7 +1340,7 @@ (ewm-autostart-application "nextcloud") (ewm-autostart-application "wlsunset" "-l 51.5 -L 0.1")) - ; Desktop notifications in Emacs + ; Desktop notifications in Emacs (use-package posframe :ensure t) (use-package ednc @@ -1286,19 +1348,19 @@ :after posframe :init (defun simple-notification-popup (old-notification new-notification) - (unless (eq old-notification new-notification) - (posframe-show - " *simple-notification-popup-alert*" - :string (concat - (ednc-notification-summary new-notification) - "\n" - (ednc-notification-body new-notification)) - :position (point) - :poshandler #'posframe-poshandler-frame-top-right-corner - :background-color "gold" - :foreground-color "black" - :timeout 5))) - + (ignore-errors + (when new-notification + (posframe-show + " *simple-notification-popup-alert*" + :string (concat + (ednc-notification-summary new-notification) + "\n" + (ednc-notification-body new-notification)) + :position (point) + :poshandler #'posframe-poshandler-frame-top-right-corner + :background-color "gold" + :foreground-color "black" + :timeout 5)))) (add-hook 'ednc-notification-presentation-functions #'simple-notification-popup) (ednc-mode)) @@ -1315,6 +1377,12 @@ (setq kdeconnect-devices '(("My Phone" . "378230a2e3c24b9a9904e7ffb0dfeb07"))) (setq kdeconnect-active-device '("My Phone" . "378230a2e3c24b9a9904e7ffb0dfeb07")))) + ;(use-package elcord + ; :ensure t + ; :config + ; (setq elcord-display-buffer-details nil) + ; (elcord-mode))) + (custom-set-variables ;; custom-set-variables was added by Custom. ;; If you edit it by hand, you could mess it up, so be careful.