commit df36844dcc47f42466b64fc3edc6a34b6ad9442d Author: Adrien Date: Sun Jan 9 21:19:46 2022 +0100 First commit. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..851f527 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +.cache +.org-id-locations +anaconda-mode +ac-comphist.dat +bookmarks +elpa +emms +eshell +org-roam.db +org-roam-db.bak +quelpa +request +recentf~ +transient diff --git a/COPYING.org b/COPYING.org new file mode 100644 index 0000000..8dfd8f8 --- /dev/null +++ b/COPYING.org @@ -0,0 +1,13 @@ +DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE +Version 2, December 2004 + +Copyright (C) 2022 Mathieu Marques + +Everyone is permitted to copy and distribute verbatim or modified +copies of this license document, and changing it is allowed as long +as the name is changed. + +DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. You just DO WHAT THE FUCK YOU WANT TO. diff --git a/README.org b/README.org new file mode 100644 index 0000000..2d14d0e --- /dev/null +++ b/README.org @@ -0,0 +1,9 @@ +#+TITLE: DotEmacs +#+AUTHOR: Adrien SUEUR + +This repository contains my personal Emacs configuration. + +This configuration is based largely on the [[https://github.com/dholm][David Holm]] [[https://github.com/dholm/dotemacs/tree/master][dotemacs]] I have mosly adapted to my workflow: + * removed/disabled packages (still in progress) that didn't seem to be necessary for me, + * Use langage-servers (still in progress) and remove packages like CEDET, ctags, scope,..., + * Use of [[https://en.wikipedia.org/wiki/Zettelkasten][Zettelkasten]] method (org-roam package). diff --git a/epilogue.el b/epilogue.el new file mode 100644 index 0000000..bce42df --- /dev/null +++ b/epilogue.el @@ -0,0 +1,23 @@ +;;; epilogue.el --- Emacs init epilogue +;;; Commentary: +;;; Code: + +(eval-when-compile + (load (expand-file-name "prologue.el" user-emacs-directory)) + (require 'init-emacs)) + +;; Load user's machine-local configuration file, if available. +(when (file-exists-p *user-local-init*) + (load *user-local-init*)) + + +;; Load custom after all packages have been installed. +(when (file-exists-p *user-custom-file*) + (load *user-custom-file*)) + + +;; Disable collection of benchmark data. +(when (featurep 'benchmark-init) + (benchmark-init/deactivate)) + +;;; epilogue.el ends here diff --git a/init.el b/init.el new file mode 100644 index 0000000..e6216ff --- /dev/null +++ b/init.el @@ -0,0 +1,36 @@ +;;; init.el --- Emacs main initialization -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(when (version< "27.1" emacs-version) + ;; Workaround for deprecated parameter referenced by Helm. + (defvar browse-url-mosaic-program "xmosaic")) + +;; Bring in package. +(setq-default + package--init-file-ensured t + package-enable-at-startup nil) +(require 'package) + +;; Load Emacs init prologue. +(eval-and-compile (load (expand-file-name "prologue.el" user-emacs-directory))) + +;; Load utilities. +;;(require 'lib/utils) +;;(require 'lib/hash-tables) +;;(require 'lib/bootstrap) +;;(require 'lib/apps) +;;(require 'lib/introspection) + + +;; Load full configuration. +(require 'init-utilities) +;;(require 'init-vcs) +(require 'init-modes) +(require 'init-apps) + + +;; Load Emacs init epilogue. +(load (expand-file-name "epilogue.el" user-emacs-directory)) +;;; init.el ends here +(put 'dired-find-alternate-file 'disabled nil) diff --git a/lisp/apps/calc.el b/lisp/apps/calc.el new file mode 100644 index 0000000..478f50b --- /dev/null +++ b/lisp/apps/calc.el @@ -0,0 +1,51 @@ +;;; calc.el --- Emacs calculator -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(use-package calc + :commands calc + :init + (user/bind-key-global :apps :calculator 'calc) + :config + (validate-setq + ;; Location of user calculator configuration. + calc-settings-file (path-join *user-data-directory* "calc.el") + ;; Increase calc's undo history. + calc-undo-length 1000 + ;; Use a different face to display sub-formulas. + calc-highlight-selections-with-faces t) + + (use-package calc-units + :ensure calc + :config + ;; Add additional units for bits and bytes. + ;; Stolen from: `https://github.com/dalehagglund/emacs.d/blob/master/calc.el' + (add-many-to-list + 'math-additional-units + '(bit nil "basic unit of information") + '(byte "8 * bit" "eight bits") + '(B "byte" "one byte") + '(KiB "1024 * B" "kibibyte") + '(MiB "1024 * KiB" "mebibyte") + '(GiB "1024 * MiB" "gibibyte") + '(TiB "1024 * GiB" "tebibyte") + '(PiB "1024 * TiB" "pebibyte") + '(EiB "1024 * PiB" "exbibyte") + '(ZiB "1024 * EiB" "zebibyte") + '(YiB "1024 * ZiB" "yobibyte"))) + + ;; Allow yanking using the mouse. + (define-key calc-mode-map [mouse-2] 'calc-yank) + + (use-package easy-convert + :quelpa (easy-convert + :fetcher github + :repo "Frozenlock/easy-convert") + :commands easy-convert-interactive + :init + (autoload 'easy-convert-interactive "easy-convert" nil t) + (user/bind-key-global :apps :convert-unit 'easy-convert-interactive))) + + +(provide 'apps/calc) +;;; calc.el ends here diff --git a/lisp/apps/elfeed.el b/lisp/apps/elfeed.el new file mode 100644 index 0000000..90572fd --- /dev/null +++ b/lisp/apps/elfeed.el @@ -0,0 +1,24 @@ +;;; elfeed.el --- Emacs web feed reader. -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +;; https://github.com/skeeto/elfeed +;; An Emacs web feeds client +(use-package elfeed + :ensure t + :commands elfeed + :init + (user/bind-key-global :apps :feed-reader 'elfeed) + :config + (setq elfeed-feeds + '("http://blog.python.org/feeds/posts/default" + "https://blog.finxter.com/feed" + "https://planetpython.org/rss20.xml" + "http://www.reddit.com/r/emacs/.rss" + "https://sachachua.com/blog/category/emacs-news/feed" + "http://linuxfr.org/journaux.atom" + ))) + +(provide 'apps/elfeed) + +;;; elfeed.el ends here diff --git a/lisp/init-apps.el b/lisp/init-apps.el new file mode 100644 index 0000000..bade228 --- /dev/null +++ b/lisp/init-apps.el @@ -0,0 +1,9 @@ +;;; init-apps.el --- initializes applications -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(load-all-files-from-dir (path-join *user-emacs-lisp-directory* "apps")) + + +(provide 'init-apps) +;;; init-apps.el ends here diff --git a/lisp/init-bindings.el b/lisp/init-bindings.el new file mode 100644 index 0000000..7a14466 --- /dev/null +++ b/lisp/init-bindings.el @@ -0,0 +1,459 @@ +;;; init-bindings.el --- sets up basic Emacs bindings -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(defvar user/global-keymap nil + "Global keymap.") + +(defvar user/global-reverse-keymap nil + "Global reverse keymap, mapping bindings back to functions.") + +(defvar ctl-l-map (make-keymap) + "Default keymap for \\ commands.") + + +;; Set up prefixes for command groups. +(defcustom user/view-prefix (kbd "C-x v") + "Keyboard prefix to use for view commands." + :type 'key-sequence + :group 'user) + +(defcustom user/help-prefix (kbd "C-c h") + "Keyboard prefix to use for help commands." + :type 'key-sequence + :group 'user) + +(defcustom user/documentation-prefix (kbd "C-c d") + "Keyboard prefix to use for documentation commands." + :type 'key-sequence + :group 'user) + +(defcustom user/code-prefix (kbd "C-c c") + "Keyboard prefix to use for code manipulation commands." + :type 'key-sequence + :group 'user) + +(defcustom user/code-eval-prefix (kbd "C-c c e") + "Keyboard prefix to use for code evaluation commands." + :type 'key-sequence + :group 'user) + +(defcustom user/vcs-prefix (kbd "C-c v") + "Keyboard prefix to use for version control commands." + :type 'key-sequence + :group 'user) + +(defcustom user/utilities-prefix (kbd "C-c u") + "Keyboard prefix to use for utility commands." + :type 'key-sequence + :group 'user) + +(defcustom user/apps-prefix (kbd "C-c a") + "Keyboard prefix to use for application commands." + :type 'key-sequence + :group 'user) + + +(defconst user/prefix-list (list "C-x" "C-c" "C-l") + "List of the registered prefix keys.") + + +(defun user/make-key (keys) + "Convert KEYS into the internal Emacs key representation." + (kbd (if (listp keys) + (mapconcat 'identity (mapcar 'eval keys) " ") + keys))) + + +(defun user/get-key (group operation) + "Get the key from GROUP to bind for OPERATION." + (let ((key (cdr (assq operation (cdr (assq group user/global-keymap)))))) + (if key + (user/make-key key) + (error (format "Group %s does not contain key for %s!" + (symbol-name group) (symbol-name operation)))))) + + +(defun user/get-key-function (group operation) + "Get the function bound to GROUP OPERATION." + (car (cdr (assq operation + (cdr (assq group user/global-reverse-keymap)))))) + + +(defun user/bind-key-global (group key function) + "Bind GROUP KEY to FUNCTION globally." + (let ((rev-group (assq group user/global-reverse-keymap))) + (setq user/global-reverse-keymap + (append `((,group . ,(append `((,key ,function)) (cdr rev-group)))) + (delq (assoc group user/global-reverse-keymap) + user/global-reverse-keymap)))) + (global-set-key (user/get-key group key) function)) + + +(defun user/bind-key-local (group key function) + "Bind GROUP KEY to FUNCTION in the current keymap." + (local-set-key (user/get-key group key) function)) + + +(defun user/merge-keymap-groups (overlay base) + "Merge OVERLAY keymap with BASE group." + (let ((group-name (car base)) + (overlay-keys (cdr overlay)) + (base-keys (cdr base))) + `((,group-name . ,(append overlay-keys base-keys))))) + + +(defun user/global-keymap-overlay (overlay) + "Load keymap OVERLAY." + (dolist (ovl-group overlay) + (let ((ovl-gname (car ovl-group)) + (ovl-keys (cdr ovl-group))) + (dolist (ovl-op (cdr ovl-group)) + (let ((ovl-oname (car ovl-op)) + (ovl-key (cdr ovl-op))) + ;; TODO: Check that ovl-oname exists. + (global-set-key (user/make-key ovl-key) + (user/get-key-function ovl-gname ovl-oname)))) + (let ((orig-group (assq ovl-gname user/global-keymap)) + (keymap-without-group (assq-delete-all ovl-gname user/global-keymap))) + (setq user/global-keymap + (append (user/merge-keymap-groups ovl-group orig-group) + keymap-without-group))))) + t) + + +(defun user--global-keymap-config () + "Initialize the global keymap." + (setq + user/global-keymap + '(;;; (Basic keys) ;;; + (:basic . ((:open-file . "C-x C-f") + (:open-file-context . "C-x f") + (:open-file-tramp . "C-x t") + (:view-file . "C-x C-v") + (:open-buffer . "C-x b") + (:open-buffer-context . "C-x M-b") + + (:save . "C-x C-s") + (:save-as . "C-x M-s") + (:close . "C-x k") + (:quit . "C-x C-c") + (:server-edit . "C-x #") + + (:undo . "C-_") + (:redo . "M-_") + + (:forward-line . "C-n") + (:backward-line . "C-p") + (:forward-word . "M-f") + (:backward-word . "M-b") + (:forward-expr . "C-M-f") + (:backward-expr . "C-M-b") + (:del-char-left . "C-h") + (:del-char-right . "C-d") + + (:search-forward . "C-s") + (:search-backward . "C-r") + (:search-files . ("C-l M-f")) + (:swoop . "C-l C-s") + (:swoop-multi . "C-l C-M-s") + + (:selection-start . "C-SPC") + (:selection-expand . "M-=") + (:selection-next . "M-.") + (:selection-prev . "M-,") + (:selection-all . "C-c M-.") + (:selection-edit-lines . "C-c M-e") + + (:select-paragraph . "M-h") + (:select-function . "C-M-h") + (:select-inside . "M-+") + + (:widen . (user/view-prefix "n w")) + (:narrow-to-page . (user/view-prefix "n p")) + (:narrow-to-region . (user/view-prefix "n r")) + (:narrow-to-function . (user/view-prefix "n f")) + + (:copy . "C-x C-w") + (:cut . "C-x C-k") + (:copy-expr . "C-M-w") + (:cut-expr . "C-M-k") + (:paste . "C-y") + (:alternate-paste . "C-M-y") + (:cycle-paste . "M-y") + (:cut-word-left . "C-w") + (:cut-word-right . "M-w") + + (:zoom . (user/view-prefix "z")))) + + ;;; (Emacs) ;;; + (:emacs . ((:describe-bindings . (user/help-prefix "b")) + (:describe-coding . (user/help-prefix "C")) + (:describe-char . (user/help-prefix "c")) + (:describe-face . (user/help-prefix "F")) + (:describe-all-faces . (user/help-prefix "M-f")) + (:describe-function . (user/help-prefix "f")) + (:describe-macro . (user/help-prefix "M-m")) + (:describe-command . (user/help-prefix "i")) + (:describe-key . (user/help-prefix "k")) + (:describe-key-extensive . (user/help-prefix "K")) + (:describe-variable . (user/help-prefix "v")) + (:search-variable-value . (user/help-prefix "V")) + (:describe-language . (user/help-prefix "L")) + (:describe-mode . (user/help-prefix "m")) + (:describe-symbol . (user/help-prefix "s")) + (:describe-syntax . (user/help-prefix "S")) + + (:find-library . (user/help-prefix "l")) + (:find-package . (user/help-prefix "p")) + (:manual . (user/help-prefix "M")) + (:elisp-search . (user/help-prefix "e")) + (:tutorial . (user/help-prefix "t")) + (:where-is . (user/help-prefix "w")) + + (:redraw . "C-l C-l") + (:recenter . "C-l l") + (:fullscreen . "C-c ") + (:text-scale-reset . "C-0") + (:text-scale-increase . "C-+") + (:text-scale-decrease . "C--") + + (:grow-vertical . "C-c C-p") + (:shrink-vertical . "C-c C-n") + (:grow-horizontal . "C-c C-f") + (:shrink-horizontal . "C-c C-b") + (:flop-frame . "C-c C-t") + (:flip-frame . "C-c M-t") + (:rotate-frame-forward . "C-c C-r") + (:rotate-frame-backward . "C-c M-r") + + (:profiler-start . (user/utilities-prefix "p p")) + (:profiler-stop . (user/utilities-prefix "p P")) + (:profiler-report . (user/utilities-prefix "p r")))) + + ;;; (Documentation) ;;; + (:doc . ((:apropos . (user/documentation-prefix "SPC")) + (:manual . (user/documentation-prefix "m")) + + (:describe . (user/documentation-prefix "d")) + (:describe-function . (user/documentation-prefix "f")) + (:describe-variable . (user/documentation-prefix "v")) + + (:dictionary . (user/documentation-prefix "D")) + + (:reference . (user/documentation-prefix "r")))) + + ;;; (Navigation) ;;; + (:nav . ((:context . ("C-l SPC")) + + (:goto-line . ("C-l g")) + (:go-forward . ("C-l f")) + (:go-back . ("C-l b")) + + (:scroll-up . "M-n") + (:scroll-down . "M-p") + + (:context-cycle . ("C-l C-c")) + (:context-forward . ("C-l C-f")) + (:context-backward . ("C-l C-b")) + (:context-up . ("C-l C-p")) + (:context-down . ("C-l C-n")) + + (:next . ("C-l n")) + + (:follow-symbol . ("C-l j")) + (:find-symbol . ("C-l s")) + (:jump-spec-impl . ("C-l i")) + (:references . ("C-l r")) + (:find-references . ("C-l M-r")) + (:find-virtuals . ("C-l v")) + (:switch-spec-impl . ("C-l h")) + (:functions/toc . ("C-l t")) + (:file-dependencies . ("C-l d")) + (:history . ("C-l h")) + (:find-todos . ("C-l M-t")) + + (:open . (user/utilities-prefix "o")))) + + ;;; (Programming) ;;; + (:code . ((:compile . (user/code-prefix "c")) + (:clean . (user/code-prefix "M-c")) + (:run . (user/code-prefix "r")) + (:test . (user/code-prefix "t")) + (:compilation-result . (user/view-prefix "c")) + (:repl . (user/code-prefix "M-r")) + + (:bookmark-prefix . "C-c b") + (:bookmark-toggle . "C-c b v") + (:bookmark-next . "C-c b n") + (:bookmark-prev . "C-c b p") + + (:comment . "M-;") + (:document . (user/code-prefix "=")) + (:join-line . ((if (display-graphic-p) "C-x C-6" "C-x C-^"))) + (:align . ((if (display-graphic-p) "C-x C-5" "C-x C-]"))) + (:fill-paragraph . ((if (display-graphic-p) "C-x C-4" "C-x C-\\"))) + (:tidy . ("C-x =")) + (:whitespace-auto-cleanup . (user/code-prefix "w")) + + (:itemize . (user/code-prefix "b")) + (:enumerate . (user/code-prefix "e")) + + (:complete . "TAB") + (:try-complete . "TAB") + (:auto-complete . "C-TAB") + + (:unwrap-expr . "C-M-d") + + (:context-promote . (user/code-prefix "P")) + (:context-demote . (user/code-prefix "N")) + + (:refactor-rename . (user/code-prefix "M-r")) + (:refactor-extract . (user/code-prefix "M-x")) + (:insert-dependency . (user/code-eval-prefix "M-d")) + (:generate-test . (user/code-prefix "M-t")) + + (:library-list . (user/code-prefix "l")) + (:disassemble . (user/code-prefix "D")) + + (:warnings/errors . (user/code-prefix "E")) + (:spellcheck-word . (user/code-prefix "s")) + (:spellcheck-add-word . (user/code-prefix "S")) + ;; (:thesaurus-lookup . (user/code-prefix "t")) + + (:update-index . (user/code-prefix "i")) + + (:eval-expression . ("C-x C-e")) + (:eval-buffer . (user/code-eval-prefix "b")) + (:eval-function . (user/code-eval-prefix "f")) + (:eval-selection . (user/code-eval-prefix "s")) + + (:macro-expand . (user/code-eval-prefix "m")) + + (:virtual . (user/code-prefix "v")))) + + ;;; (Debugging) ;;; + (:debug . ((:start . (user/code-prefix "d")) + + (:break . (user/code-prefix "b")) + (:trace . (user/code-prefix "T")) + (:break-temporary . (user/code-prefix "t")) + (:watch . (user/code-prefix "w")) + + (:run . (user/code-prefix "r")) + (:continue . (user/code-prefix "c")) + (:continue-stack . (user/code-prefix "f")) + (:continue-until . (user/code-prefix "u")) + (:step . (user/code-prefix "s")) + (:step-instruction . (user/code-prefix "i")) + (:next . (user/code-prefix "n")) + + (:stack-up . (user/code-prefix "p")) + (:stack-down . (user/code-prefix "n")) + (:show-value . (user/code-prefix "p")))) + + ;;; (Version Control) ;;; + (:vcs . ((:clone . (user/vcs-prefix "c")) + (:status . (user/vcs-prefix "s")) + (:history . (user/vcs-prefix "h")) + (:version . (user/vcs-prefix "v")) + (:describe . (user/vcs-prefix "d")) + (:gutter . (user/vcs-prefix "g")) + (:review . (user/vcs-prefix "r")) + + (:add-buffer . (user/vcs-prefix "a")) + (:next-action . (user/vcs-prefix "SPC")) + (:mergetool . (user/vcs-prefix "m")) + + (:search . (user/vcs-prefix "M-s")) + (:find-file . ("C-c p")) + (:time-machine . (user/vcs-prefix "t")))) + + ;;; (Utilities) ;;; + (:util . ((:annotate-buffer . (user/utilities-prefix "a")) + (:draw . (user/utilities-prefix "g")) + (:diff . (user/utilities-prefix "d")) + (:dumb-diff . (user/utilities-prefix "M-d")) + + (:ace-jump-mode . ("C-l a")) + + (:ecb-toggle . (user/utilities-prefix "e")) + + (:google . (user/utilities-prefix "s")) + (:google-at-point . (user/documentation-prefix "s RET")) + (:google-selection . (user/documentation-prefix "s SPC")) + (:stack-overflow-search . (user/documentation-prefix "s")) + + (:notifications . (user/utilities-prefix "n")) + + (:perspective . ("C-x x s")) + (:presentation . (user/utilities-prefix "P")) + + (:popwin-close . (user/view-prefix "0")) + (:popwin-buffer . (user/view-prefix "p")) + (:popwin-messages . (user/view-prefix "m")) + + (:undo-tree . (user/utilities-prefix "u")) + + (:wc-mode . (user/utilities-prefix "w")) + + (:docker . (user/utilities-prefix "d")))) + + ;;; (Applications) ;;; + (:apps . ((:packages . (user/apps-prefix "M-p")) + + (:shell . (user/apps-prefix "s")) + (:processes . (user/apps-prefix "p")) + (:daemons . (user/apps-prefix "M-d")) + (:services . (user/apps-prefix "P")) + + (:agenda . (user/apps-prefix "a")) + (:notes . (user/apps-prefix "n")) + (:todo . (user/apps-prefix "t")) + (:capture-task . (user/apps-prefix "M-t")) + (:information-db . (user/apps-prefix "D")) + + (:browse . (user/apps-prefix "b")) + (:browse-external . (user/apps-prefix "B")) + (:feed-reader . (user/apps-prefix "f")) + (:stack-exchange . (user/apps-prefix "x")) + (:weather . (user/apps-prefix "w")) + (:cheat-sh . (user/apps-prefix "C")) + + (:email . (user/apps-prefix "e")) + (:irc . (user/apps-prefix "i")) + (:instant-messenger . (user/apps-prefix "I")) + + (:ipython-notebook . (user/apps-prefix "N")) + + (:music . (user/apps-prefix "m")) + + (:elnode . (user/apps-prefix "E")) + + (:calculator . (user/apps-prefix "c")) + (:convert-unit . (user/apps-prefix "M-c")) + (:statistics . (user/apps-prefix "R")) + (:sage . (user/apps-prefix "S"))))))) + + +(defun user--bindings-config () + "Initialize key bindings." + (global-unset-key (kbd "C-l")) + (define-prefix-command 'ctl-l-map) + (global-set-key (kbd "C-l") 'ctl-l-map) + + ;; Initialize global keymap. + (user--global-keymap-config) + + ;;; (Bindings) ;;; + ;; Alias C-x C-m to M-x which is a bit awkward to reach. + (global-set-key (kbd "C-x C-m") 'execute-extended-command) + (global-set-key (kbd "C-x m") 'execute-extended-command) + ;; Toggle comments + (global-set-key (kbd "C-x -") 'comment-or-uncomment-region)) + +(user--bindings-config) + + +(provide 'init-bindings) +;;; init-bindings.el ends here diff --git a/lisp/init-constants.el b/lisp/init-constants.el new file mode 100644 index 0000000..2d18ddc --- /dev/null +++ b/lisp/init-constants.el @@ -0,0 +1,44 @@ +;;; init-constants.el --- Set up constants required during initialization -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(require 'lib/path) +(require 'lib/env) +(require 'lib/pkg-config) + + +;;; (Directories) ;;; +(defconst *user-home-directory* + (getenv-or "HOME" (concat (expand-file-name "~") "/")) + "Path to user home directory.") +(defconst *user-local-directory* + (if (getenv "XDG_DATA_HOME") + (path-dirname (getenv "XDG_DATA_HOME")) + (path-join *user-home-directory* ".local")) + "Path to user's local store.") +(defconst *user-config-directory* + (path-join (getenv-or "XDG_CONFIG_HOME" + (path-join *user-home-directory* ".config")) + "emacs") + "Path to user's local cache store.") +(defconst *user-data-directory* + (path-join (getenv-or "XDG_DATA_HOME" + (path-join *user-local-directory* "share")) + "emacs") + "Path to user's local data store.") +(defconst *user-cache-directory* + (path-join (getenv-or "XDG_CACHE_HOME" + (path-join *user-home-directory* ".cache")) + "emacs") + "Path to user's local cache store.") +(defconst *user-documents-directory* + (path-join *user-home-directory* "Documents") + "Path to user's documents directory.") + +(defconst *user-local-init* + (path-join *user-home-directory* ".emacs.local.el") + "Path to user's machine-local configuration file.") + + +(provide 'init-constants) +;;; init-constants.el ends here diff --git a/lisp/init-emacs.el b/lisp/init-emacs.el new file mode 100644 index 0000000..b9d3e41 --- /dev/null +++ b/lisp/init-emacs.el @@ -0,0 +1,40 @@ +;;; init-emacs.el --- initializes basic Emacs settings -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(defconst *user-custom-file* (path-join *user-data-directory* "custom.el")) + +(when (version< emacs-version "25.3") + ;; Plug security hole in enriched text mode. + (eval-after-load "enriched" + '(defun enriched-decode-display-prop (start end &optional param) + (list start end)))) + +;; Improve init performance. +(setq + ;; Increase garbage collection threshold. + gc-cons-threshold (* 128 1024 1024)) + +;; Restore garbage collection threshold while Emacs is idle. +(run-with-idle-timer + 2 nil + (lambda () + (validate-setq + ;; Reduce number of pauses due to garbage collection. + gc-cons-threshold (* 50 1024 1024) + gc-cons-percentage 0.5))) + + +;; Create data and cache directories +(make-directory *user-cache-directory* t) +(make-directory *user-data-directory* t) + +(setq + ;; Lines of history in the message buffer. + message-log-max 10000 + ;; Path to custom-file + custom-file *user-custom-file*) + + +(provide 'init-emacs) +;;; init-emacs.el ends here diff --git a/lisp/init-modes.el b/lisp/init-modes.el new file mode 100644 index 0000000..9852117 --- /dev/null +++ b/lisp/init-modes.el @@ -0,0 +1,9 @@ +;;; init-modes.el --- initializes major modes -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(load-all-files-from-dir (path-join *user-emacs-lisp-directory* "modes")) + + +(provide 'init-modes) +;;; init-modes.el ends here diff --git a/lisp/init-utilities.el b/lisp/init-utilities.el new file mode 100644 index 0000000..30e63e1 --- /dev/null +++ b/lisp/init-utilities.el @@ -0,0 +1,9 @@ +;;; init-utilities.el --- initializes utilities -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(load-all-files-from-dir (path-join *user-emacs-lisp-directory* "utilities")) + + +(provide 'init-utilities) +;;; init-utilities.el ends here diff --git a/lisp/init-ux.el b/lisp/init-ux.el new file mode 100644 index 0000000..e46aa36 --- /dev/null +++ b/lisp/init-ux.el @@ -0,0 +1,9 @@ +;;; init-ux.el --- initializes user experience -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(load-all-files-from-dir (path-join *user-emacs-lisp-directory* "ux")) + + +(provide 'init-ux) +;;; init-ux.el ends here diff --git a/lisp/lib/apps.el b/lisp/lib/apps.el new file mode 100644 index 0000000..6a9a022 --- /dev/null +++ b/lisp/lib/apps.el @@ -0,0 +1,17 @@ +;;; apps.el --- Support functions for applications -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + + +(defun osx-app-installed-p (app) + "Return t if APP is installed." + (when (eq system-type 'darwin) + (let ((lsregister + "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister")) + (and (file-executable-p lsregister) + (not (string-equal "" (shell-command-to-string + (concat lsregister " -dump|grep " app)))))))) + + +(provide 'lib/apps) +;;; apps.el ends here diff --git a/lisp/lib/bootstrap.el b/lisp/lib/bootstrap.el new file mode 100644 index 0000000..de82344 --- /dev/null +++ b/lisp/lib/bootstrap.el @@ -0,0 +1,28 @@ +;;; bootstrap.el --- Helpers for bootstrapping Emacs. -*- lexical-binding: t; -*- +;;; Commentary: + +;; Stolen from Alexx Ott, https://github.com/alexott/dotemacs + +;;; Code: + +(declare-function 'path-join "path") + + +(defun load-all-files-from-dir (dir) + "Load all Emacs Lisp files in DIR." + (dolist (f (directory-files dir)) + (when (and + (file-directory-p (path-join dir f)) + (not (string= "." f)) + (not (string= ".." f))) + (load-all-files-from-dir (path-join dir f))) + (when (and + (not (file-directory-p (path-join dir f))) + (not (string= "bootstrapper.el" f)) + (not (string= ".#" (substring f 0 2))) + (string= ".el" (substring f (- (length f) 3)))) + (load-file (path-join dir f))))) + + +(provide 'lib/bootstrap) +;;; bootstrap.el ends here diff --git a/lisp/lib/env.el b/lisp/lib/env.el new file mode 100644 index 0000000..becdbbd --- /dev/null +++ b/lisp/lib/env.el @@ -0,0 +1,13 @@ +;;; env.el --- support functions for working with environment variables -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(defun getenv-or (env value) + "Fetch the value of ENV or, if it is not set, return VALUE." + (if (getenv env) + (getenv env) + value)) + + +(provide 'lib/env) +;;; env.el ends here diff --git a/lisp/lib/list.el b/lisp/lib/list.el new file mode 100644 index 0000000..2a32bfd --- /dev/null +++ b/lisp/lib/list.el @@ -0,0 +1,46 @@ +;;; list.el --- Emacs list utilities -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(defun add-many-to-list (the-list &rest entries) + "Add to THE-LIST any specified ENTRIES." + (dolist (entry entries) + (add-to-list the-list entry)) + (eval the-list)) + + +(defmacro user/filter-form (form list) + "Return list with elements for which FORM are non-nil in LIST." + (declare (debug (form form))) + (let ((r (make-symbol "result"))) + `(let (,r) + (--each ,list (when ,form (!cons it ,r))) + (nreverse ,r)))) + + +(defun user/filter-list (condp list) + "Return list with elements for which CONDP are non-nil in LIST." + (delq nil + (mapcar (lambda (x) (and (funcall condp x) x)) list))) + + +(defun user/toggle-element (list element) + "Return LIST with ELEMENT removed if present or added if not present." + (if (member element list) + (user/filter-form (not (eq element it)) list) + (cons element list))) + + +(defun user/all-asscs (asslist query) + "A list of all values in ASSLIST corresponding to QUERY (like rassoc)." + (cond + ((null asslist) nil) + (t + (if (equal (cdr (car asslist)) query) + (cons (car (car asslist)) + (user/all-asscs (cdr asslist) query)) + (user/all-asscs (cdr asslist) query))))) + + +(provide 'lib/list) +;;; list.el ends here diff --git a/lisp/lib/net.el b/lisp/lib/net.el new file mode 100644 index 0000000..1aff301 --- /dev/null +++ b/lisp/lib/net.el @@ -0,0 +1,35 @@ +;;; net.el --- Initialize Emacs networking -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(require 'lib/path) +(require 'lib/utils) + + +(defconst *user-url-cache-directory* + (path-join *user-cache-directory* "url") + "Path to user's url data store.") +(defconst *user-nsm-data-directory* + (path-join *user-data-directory* "nsm") + "Path to user's Wanderlust data store.") + + +(with-eval-after-load 'url + (setq + ;; Set up cache directory. + url-configuration-directory *user-url-cache-directory* + url-cookie-file (path-join *user-url-cache-directory* "cookies") + url-history-file (path-join *user-url-cache-directory* "history") + ;; Automatically cache all documents. + url-automatic-caching t)) + +(make-directory *user-nsm-data-directory* t) +(with-eval-after-load 'nsm + (setq + ;; Location of security manager settings. + nsm-settings-file + (path-join *user-nsm-data-directory* "network-security.data"))) + + +(provide 'lib/net) +;;; net.el ends here diff --git a/lisp/lib/packaging.el b/lisp/lib/packaging.el new file mode 100644 index 0000000..e63bd81 --- /dev/null +++ b/lisp/lib/packaging.el @@ -0,0 +1,97 @@ +;;; packaging.el --- initialize package management -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(require 'lib/net) + + +(with-feature 'package + (setq + ;; Configure GNU/Emacs package repositories. + package-archives + '(("GNU ELPA" . "https://elpa.gnu.org/packages/") + ("MELPA Stable" . "http://stable.melpa.org/packages/") + ("MELPA" . "http://melpa.org/packages/") + ("org" . "http://orgmode.org/elpa/")) +;; ("marmalade" . "http://marmalade-repo.org/packages/")) + ;; Prefer MELPA Stable over GNU over MELPA. + package-archive-priorities + '(("MELPA Stable" . 20) + ("GNU ELPA" . 15) + ("MELPA" . 10) + ("org" . 5)))) +;; ("marmalade" . 0)))) + + +;; Bootstrap `use-package'. +(package-initialize) +(unless (and (package-installed-p 'quelpa-use-package) (package-installed-p 'validate)) + (package-refresh-contents) + (package-install 'quelpa-use-package) + (package-install 'validate)) + +(eval-when-compile + ;; Load use-package. + (require 'quelpa-use-package) + (require 'validate)) + +(use-package use-package + :config + (validate-setq + ;; Hooks are verbatim. + use-package-hook-name-suffix nil) + + (use-package quelpa-use-package + :config + (validate-setq + ;; Only use quelpa for custom packages. + quelpa-checkout-melpa-p nil + ;; Only load quelpa on demand. + quelpa-use-package-inhibit-loading-quelpa t) + + ;; Protect quelpa recipes when forcing ensure. + (quelpa-use-package-activate-advice)) + + ;; Support using keys from init-bindings by using (:key ). + (push :bind-wrap (cdr (member :bind use-package-keywords))) + (push :bind*-wrap (cdr (member :bind* use-package-keywords))) + (defun use-package-normalize-bind-wrap (name keyword args) + (let ((arg args) + args*) + (while arg + (let ((x (car arg))) + (cond + ;; ((:key :category :function) . COMMAND) + ((and (consp x) + (consp (car x)) + (equal (caar x) :key)) + (setq args* (nconc args* + (list (cons (apply 'user/get-key (cdar x)) + (cdar arg))))) + (setq arg (cdr arg))) + ;; (KEY . COMMAND) + ((and (consp x) + (or (stringp (car x)) + (vectorp (car x))) + (or (use-package-recognize-function (cdr x) t #'stringp))) + (setq args* (nconc args* (list x))) + (setq arg (cdr arg))) + ;; Nested list. + ((listp x) + (setq args* + (nconc args* (use-package-normalize/:bind-wrap name keyword x))) + (setq arg (cdr arg))) + (t + (setq args* (nconc args* (list x))) + (setq arg (cdr arg)))))) + (use-package-normalize/:bind name keyword args*))) + (defalias 'use-package-normalize/:bind-wrap 'use-package-normalize-bind-wrap) + (defalias 'use-package-normalize/:bind*-wrap 'use-package-normalize-bind-wrap) + (defun use-package-handler/:bind-wrap (name keyword arg rest state) + (use-package-handler/:bind name keyword arg rest state)) + (defun use-package-handler/:bind*-wrap (name keyword arg rest state) + (use-package-handler/:bind name keyword arg rest state 'bind-keys*))) + + +(provide 'lib/packaging) +;;; packaging.el ends here diff --git a/lisp/lib/path.el b/lisp/lib/path.el new file mode 100644 index 0000000..3d37a35 --- /dev/null +++ b/lisp/lib/path.el @@ -0,0 +1,26 @@ +;;; path.el --- support functions for working with paths -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(defun path-abs-buffer () + "Get the current buffer absolute path." + (file-truename (or (buffer-file-name) default-directory))) + + +(defun path-dirname (path) + "Get the parent directory of PATH." + (file-name-directory (directory-file-name path))) + + +(defun path-join (root &rest dirs) + "Join paths together starting at ROOT and proceeding with DIRS. +Ex: (path-join \"/tmp\" \"a\" \"b\" \"c\") => /tmp/a/b/c" + (if (not dirs) + root + (apply 'path-join + (expand-file-name (car dirs) root) + (cdr dirs)))) + + +(provide 'lib/path) +;;; path.el ends here diff --git a/lisp/lib/pkg-config.el b/lisp/lib/pkg-config.el new file mode 100644 index 0000000..2fe63f0 --- /dev/null +++ b/lisp/lib/pkg-config.el @@ -0,0 +1,11 @@ +;;; pkg-config.el --- pkg-config support -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(defun pkg-config-has-p (package) + "Check if PACKAGE is available." + (eq (call-process-shell-command "pkg-config" nil nil nil "--exists" package) 0)) + + +(provide 'lib/pkg-config) +;;; pkg-config.el ends here diff --git a/lisp/lib/string.el b/lisp/lib/string.el new file mode 100644 index 0000000..6b733d3 --- /dev/null +++ b/lisp/lib/string.el @@ -0,0 +1,11 @@ +;;; string.el --- Emacs string functions. -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(defmacro with-face (str &rest properties) + "Print STR using PROPERTIES." + `(propertize ,str 'face (list ,@properties))) + + +(provide 'lib/string) +;;; string.el ends here diff --git a/lisp/lib/utils.el b/lisp/lib/utils.el new file mode 100644 index 0000000..d12d1bc --- /dev/null +++ b/lisp/lib/utils.el @@ -0,0 +1,51 @@ +;;; utils.el --- miscellaneous support functions -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(defmacro try-eval (fn &optional finally) + "Safely evaluate expression FN and run FINALLY after." + (declare (debug t) + (indent 1)) + `(let (retval) + (condition-case-unless-debug ex + (setq retval (progn ,fn)) + ('error + (setq retval (cons 'exception (list ex))))) + ,@finally + retval)) + + +(defun feature-p (feature) + "Check if FEATURE is available." + (or (featurep feature) + (when (functionp 'package-installed-p) + (package-installed-p feature)) + (locate-library (symbol-name feature)))) + + +(defun add-command-switch (handler &rest switch-list) + "Add HANDLER for SWITCH-LIST." + (dolist (switch switch-list) + (add-to-list 'command-switch-alist (cons switch handler)))) + + +(defun add-auto-mode (mode &rest patterns) + "Use `MODE' for all given files matching `PATTERNS'." + (dolist (pattern patterns) + (add-to-list 'auto-mode-alist (cons pattern mode)))) + + +(defun add-magic-mode (mode &rest patterns) + "Use `MODE' for all files containing header `PATTERNS'." + (dolist (pattern patterns) + (add-to-list 'magic-mode-alist (cons pattern mode)))) + + +(defun add-interpreter-mode (mode &rest interpreters) + "Use `MODE' for all files with shebang `INTERPRETERS'." + (dolist (interpreter interpreters) + (add-to-list 'interpreter-mode-alist (cons interpreter mode)))) + + +(provide 'lib/utils) +;;; utils.el ends here diff --git a/lisp/lib/with.el b/lisp/lib/with.el new file mode 100644 index 0000000..8c39592 --- /dev/null +++ b/lisp/lib/with.el @@ -0,0 +1,34 @@ +;;; with.el --- conditional eval wrappers -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(defmacro with-feature (feature &rest body) + "If FEATURE is available, load it and evaluate BODY." + (declare (indent defun)) + `(when (require ,feature nil :noerror) + ,@body)) + + +(defmacro with-function (function &rest body) + "If FUNCTION is available, evaluate BODY." + (declare (indent defun)) + `(when (functionp ,function) + ,@body)) + + +(defmacro with-executable (executable &rest body) + "If EXECUTABLE is available in path, evaluate BODY." + (declare (indent defun)) + `(when (executable-find (symbol-name ,executable)) + ,@body)) + + +(defmacro with-any-executable (executables &rest body) + "If any of EXECUTABLES are available in the path, evaluate BODY." + (declare (indent defun)) + `(when (some (lambda (x) (executable-find (symbol-name x))) ,executables) + ,@body)) + + +(provide 'lib/with) +;;; with.el ends here diff --git a/lisp/modes/c-c++.el b/lisp/modes/c-c++.el new file mode 100644 index 0000000..de3f376 --- /dev/null +++ b/lisp/modes/c-c++.el @@ -0,0 +1,162 @@ +;;; c-c++.el --- initializes C/C++ modes -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(require 'cl) +(require 'lsp) + +(defun user--c-format-before-save () + "C/C++ cleanup and format before save buffer." + (delete-trailing-whitespace) + ;; Format buffer. + (indent-region (point-min) (point-max) nil) + ) + +;; From https://www.emacswiki.org/emacs/CompileCommand +(defun* user--c-get-closest-pathname (&optional (file "Makefile")) + "Determine the pathname of the first instance of FILE starting from the current directory towards root. +This may not do the correct thing in presence of links. If it does not find FILE, then it shall return the name +of FILE in the current directory, suitable for creation" + (let ((root (expand-file-name "/"))) ; the win32 builds should translate this correctly + (expand-file-name file + (loop + for d = default-directory then (expand-file-name ".." d) + if (file-exists-p (expand-file-name file d)) + return d + if (equal d root) + return nil)))) + +(defun user--c-lsp-set-priority (server priority) + (setf (lsp--client-priority (gethash server lsp-clients)) priority)) + +(defun user--c-mode-common-hook () + "C-like languages mode hook." + (add-many-to-list 'c-default-style '(c-mode . "bsd") '(c++-mode . "bsd")) + + (setq tab-width 4) + + ;; Propertize "#if 0" regions as comments. + (font-lock-add-keywords + nil + '((user/c-mode-font-lock-if0 (0 font-lock-comment-face prepend))) + 'add-to-end) + + ;; Change compile-command. + (set + (make-local-variable 'compile-command) + (format "make -C %s" (directory-file-name (file-name-directory (user--c-get-closest-pathname))))) + ;; Avoid to hit enter after compile-command to build. + (setq compilation-read-command nil) + + ;; Separate camel-case into separate words. + (subword-mode t) + + (when (feature-p 'mic-paren) + ;; Match context to open parentheses. + (paren-toggle-open-paren-context t)) + + (setq flycheck-checker-error-threshold 1000) + + (setq flycheck-local-checkers '((lsp . ((next-checkers . (flawfinder)))))) + ;; (flycheck-add-next-checker 'clang-analyzer 'flawfinder) + + (user/smartparens-enable)) + +(defun user/c-mode-font-lock-if0 (limit) + "Propertize '#if 0' regions, up to LIMIT in size, as comments." + (save-restriction + (widen) + (save-excursion + (goto-char (point-min)) + (let ((depth 0) str start start-depth) + (while (re-search-forward "^\\s-*#\\s-*\\(if\\|else\\|endif\\)" limit 'move) + (setq str (match-string 1)) + (if (string= str "if") + (progn + (setq depth (1+ depth)) + (when (and (null start) (looking-at "\\s-+0")) + (setq start (match-end 0) + start-depth depth))) + (when (and start (= depth start-depth)) + (c-put-font-lock-face start (match-beginning 0) 'font-lock-comment-face) + (setq start nil)) + (when (string= str "endif") + (setq depth (1- depth))))) + (when (and start (> depth 0)) + (c-put-font-lock-face start (point) 'font-lock-comment-face))))) + nil) + + +(defun user/c++-header-file-p () + "Return non-nil if in a C++ header." + (and (string-match "\\.h$" (or (buffer-file-name) (buffer-name))) + (save-excursion (re-search-forward "\\_" nil t)))) + +;; https://www.gnu.org/software/emacs/manual/html_mono/ccmode.html +(use-package cc-mode + :defer + :hook + (c-mode-common-hook . user--c-mode-common-hook) + (c-mode-common-hook . hs-minor-mode) + (c-mode-common-hook . lsp-deferred) + (c-mode-common-hook . (lambda () + (add-hook 'before-save-hook #'user--c-format-before-save nil t))) + :init + ;; Detect if inside a C++ header file. + (add-magic-mode 'c++-mode 'user/c++-header-file-p) + :config + (add-many-to-list 'c-default-style '(c-mode . "bsd") '(c++-mode . "bsd") + ) + + ;;; (Packages) ;;; + ;; https://github.com/emacs-mirror/emacs/blob/master/lisp/progmodes/cc-vars.el + ;; user customization variables for CC Mod + (use-package cc-vars + :disabled + :ensure cc-mode + :config + (validate-setq + ;; Support completion using tab. + c-tab-always-indent nil + c-insert-tab-function 'indent-for-tab-command) + ) + + ;; https://github.com/randomphrase/company-c-headers + ;; Auto-completion for C/C++ headers using Company + (use-package company-c-headers + :after (company)) + + ;; https://github.com/emacs-lsp/lsp-mode/blob/master/clients/lsp-clangd.el + ;; LSP clients for the C Languages Family + (use-package lsp-clangd + :after (company lsp) + ;; :defer t + :config + ;; set priority to ensure the use of clangd + (setq company-backends '(company-capf)) + (user--c-lsp-set-priority 'clangd 1)) + + ;; https://github.com/MaskRay/emacs-ccls + ;; Emacs client for ccls, a C/C++ language server + (use-package ccls + :if (executable-find "ccls") + :after lsp + :config + (user--c-lsp-set-priority 'ccls -1) + (validate-setq + ccls-initialization-options + '(:index (:comments 1) :cacheFormat "msgpack"))) + + ;; https://github.com/alexmurray/flycheck-flawfinder + ;; Integrate flawfinder with flycheck to automatically check for possible security weaknesses + ;; within your C/C++ code on the fly. + (use-package flycheck-flawfinder + :disabled + :ensure t + :if (executable-find "flawfinder") + :config + (flycheck-flawfinder-setup)) + ) + +(provide 'modes/c-c++) +;;; c-c++.el ends here diff --git a/lisp/modes/dot.el b/lisp/modes/dot.el new file mode 100644 index 0000000..7dac2e6 --- /dev/null +++ b/lisp/modes/dot.el @@ -0,0 +1,15 @@ +;;; markdown --- initializes Dot modes -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +;; https://github.com/ppareit/graphviz-dot-mode +;; Emacs mode for the DOT language, used by graphviz. +(use-package graphviz-dot-mode + :init + (use-package company-graphviz-dot) + :config + (setq graphviz-dot-preview-extension "svg") + (setq graphviz-dot-indent-width 2)) + +(provide 'modes/dot) +;;; dot.el ends here diff --git a/lisp/modes/fundamental.el b/lisp/modes/fundamental.el new file mode 100644 index 0000000..8f564dd --- /dev/null +++ b/lisp/modes/fundamental.el @@ -0,0 +1,76 @@ +;;; fundamental.el --- Base mode of all other major modes -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(defun user--fundamental-mode-hook () + "Fundamental mode hook." + + (with-feature 'undo-tree + (undo-tree-mode t)) + + (auto-fill-mode t) + + ;; Enable whitespace mode globally. + ;;(whitespace-mode t) + + (with-feature 'rainbow-delimiters + (rainbow-delimiters-mode t)) + + ;; Enable dtrt-indent to attempt to identify the indentation rules used. + (with-eval-after-load 'dtrt-indent + (dtrt-indent-mode t)) + + ;;; (Bindings) ;;; + (user/bind-key-local :code :align 'align-current) + (when (feature-p 'helm) + (user/bind-key-local :nav :functions/toc 'helm-imenu))) + + +(defun user--fundamental-mode-config () + "Initialize Emacs fundamental mode." + (validate-setq + ;; When using fill-paragraph or auto-fill-mode break lines at 80 characters by + ;; default. + fill-column 120) + + ;;; (Packages) ;;; + ;;; https://github.com/Fanael/rainbow-delimiters + (use-package rainbow-delimiters + :ensure t) + ;; https://www.emacswiki.org/emacs/MicParen + (use-package mic-paren + :ensure t + :config + (paren-activate)) + ;; https://github.com/Lindydancer/dynamic-spaces + (use-package dynamic-spaces + :disabled + :config + (dynamic-spaces-global-mode t)) + ;; https://github.com/terlar/indent-info.el + (use-package indent-info + :ensure t + :config + (global-indent-info-mode t)) + ;; https://github.com/shawcm/goldendict-emacs + (use-package goldendict + :disabled + :if (executable-find "goldendict") + :bind-wrap + ((:key :doc :dictionary) . goldendict-dwim)) + ;; https://www.emacswiki.org/emacs/FillAdapt + (use-package filladapt + :disabled) + + ;; https://github.com/fritzgrabo/cascading-dir-locals + ;; Emacs: Apply all (!) .dir-locals.el from root to current directory. + (use-package cascading-dir-locals + :ensure t + :config + (cascading-dir-locals-mode 1))) + +(user--fundamental-mode-config) + + +(provide 'modes/fundamental) +;;; fundamental.el ends here diff --git a/lisp/modes/javascript.el b/lisp/modes/javascript.el new file mode 100644 index 0000000..7e397d0 --- /dev/null +++ b/lisp/modes/javascript.el @@ -0,0 +1,112 @@ +;;; javascript.el --- initializes JavaScript modes -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(defun user--javascript-mode-common-hook () + "JavaScript common mode hook." + ;; ;; Load CEDET + ;; (user--javascript-mode-cedet-hook) + + ;; (user/gnu-global-enable) + + (flycheck-mode t) + + ;; (tern-mode t) + ) + + +(defun user--javascript-mode-hook () + "JavaScript mode hook." + (user--javascript-mode-common-hook)) + + +(defun user--inferior-js-mode-hook () + "Inferior JavaScript mode hook." + ;; Support ANSI colors. + (ansi-color-for-comint-mode-on)) + + +(defun user--js2-mode-hook () + "JS2 mode hook." + (user--javascript-mode-common-hook) + ;; Enable smart indentation + (smart-tabs-mode t) + ;; Enable Flycheck + (flycheck-mode t)) + + +;; (defun user--javascript-mode-cedet-hook () +;; "JavaScript CEDET support hook." +;; (with-feature 'semantic/wisent/javascript +;; (wisent-javascript-setup-parser) +;; ;; (user--cedet-hook) +;; )) + +;; +;; +(use-package js + :defer + :hook ((javascript-mode-hook . user--javascript-mode-hook) + (inferior-js-mode-hook . user--inferior-javascript-mode-hook))) + + ;; https://github.com/mooz/js2-mode + ;; Improved JavaScript editing mode for GNU Emacs +(use-package js2-mode + :defer + :mode "\.[m]js$" + ;; :mode "\.ts$" + :magic "#!/usr/bin/env node" + :hook (js2-mode-hook . user--js2-mode-hook) + :config + (validate-setq + ;; ;; Configure indentation + ;; (setq js2-enter-indents-newline t) + ;; (setq js2-auto-indent-p t) + ;; Idle timeout before reparsing buffer + js2-idle-timer-delay 0.5 + ;; Disable error parsing in favor of Flycheck + js2-strict-missing-semi-warning nil) + + ;; https://github.com/redguardtoo/js-comint + ;; js-comint will send the code from Emacs into node.js or rhino + (use-package js-comint + :disabled + :config + (validate-setq + ;; Set JavaScript inferior. + inferior-js-program-command + (cond + ((executable-find "js") (executable-find "js")) + ((executable-find "node") + (concat (executable-find "node") " --interactive")) + (t "java org.mozilla.javascript.tools.shell.Main"))) + + ;; Workaround for Node.js prompt. + (setenv "NODE_NO_READLINE" "1")) + + ;; https://github.com/prettier/prettier-emacs + ;; Minor mode to format JS code on file save + (use-package prettier-js + :disabled) + + ;; https://emacs-lsp.github.io/lsp-mode/page/lsp-typescript-javascript/ + (use-package lsp-javascript-typescript + :disabled + :if (executable-find "javascript-typescript-langserver") + :hook (js2-mode-hook . lsp-javascript-typescript-enable)) + + ;; https://github.com/torgeir/helm-js-codemod.el + ;; A helm interface for running js-codemod.el + (use-package helm-js-codemod + :disabled + :if (executable-find "jscodeshift")) + + ;; https://github.com/js-emacs/xref-js2 + ;; Jump to references/definitions using ag & js2-mode's AST in Emacs + (use-package xref-js2 + :init + (add-hook 'xref-backend-functions #'xref-js2-xref-backend nil t))) + + +(provide 'modes/javascript) +;;; javascript.el ends here diff --git a/lisp/modes/makefile.el b/lisp/modes/makefile.el new file mode 100644 index 0000000..74ae449 --- /dev/null +++ b/lisp/modes/makefile.el @@ -0,0 +1,32 @@ +;;; makefile.el --- Initializes Makefile mode -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +;; Sets some decent defaults for makefile-mode +(defun user--makefile-mode-hook () + "Initialize makefile mode." + (setq + ;; Use tabs for indent. + indent-tabs-mode t) + ;; Disable whitespace mode settings that don't make sense in makefiles. + (user/whitespace-disable-style '(indentation space-after-tab)) + ;; Separate camel-case into separate words. + (subword-mode t) + ;; Support for documentation in Doxygen format. + ;; (with-feature 'doxymacs + ;; (doxymacs-mode t)) + + (with-feature 'makefile-executor + (makefile-executor-mode t))) + +(use-package make-mode + :defer + :mode ("\.\(mak\|mif\|wat\)$" . makefile-mode) + :init + (add-hook 'makefile-mode-hook 'user--makefile-mode-hook) + :config + (use-package makefile-executor)) + + +(provide 'modes/makefile) +;;; makefile.el ends here diff --git a/lisp/modes/markdown.el b/lisp/modes/markdown.el new file mode 100644 index 0000000..88a103c --- /dev/null +++ b/lisp/modes/markdown.el @@ -0,0 +1,91 @@ +;;; markdown --- initializes Markdown modes -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(defconst *user-flymd-cache-directory* + (path-join *user-cache-directory* "flymd") + "Path to user's FLYMD cache store.") + + +(defun user--markdown-mode-hook () + "Markdown mode hook." + (user/smartparens-enable) + + ;; org-mode table editing tools. + (orgtbl-mode t)) + +;; https://github.com/jrblevin/markdown-mode +;; Emacs Markdown Mode +(use-package markdown-mode + :defer + :hook (markdown-mode-hook . user--markdown-mode-hook) + :mode (("README\\.md\\'" . gfm-mode) + ("\\.md\\'" . markdown-mode) + ("\\.markdown\\'" . markdown-mode)) + :config + (setq markdown-fontify-code-blocks-natively t) + (setq markdown-command "pandoc -f gfm --highlight-style kate") + (setq markdown-css-paths (list (path-join user-emacs-directory "markdown_style.css"))) + (setq markdown-xhtml-body-preamble "\
") + + ;; Thanks to https://gist.github.com/yryozo/5807243 + ;; Orgtbl Translator function for the GitHub-Flavored-Markdown(GFM) + ;; Usage Example: + ;; + ;; + ;; + ;; + ;; + (defun orgtbl-to-gfm (table params) + "Convert the Orgtbl mode TABLE to GitHub Flavored Markdown." + (let* ((alignment (mapconcat (lambda (x) (if x "|--:" "|---")) + org-table-last-alignment "")) + (params2 + (list + :splice t + :hline (concat alignment "|") + :lstart "| " :lend " |" :sep " | "))) + (orgtbl-to-generic table (org-combine-plists params2 params)))) + + ;; https://github.com/mmark-md/flycheck-mmark + ;; Flycheck checker for the MMark markdown processor + (use-package flycheck-mmark + :disabled + :if (executable-find "mmark") + :hook (flycheck-mode-hook . flycheck-mmark-setup)) + + ;; https://github.com/polymode/poly-markdown + ;; Polymode for markdown-mode + (use-package poly-markdown + :after polymode + :hook (markdown-mode-hook . poly-markdown-mode)) + + ;; https://github.com/niku/markdown-preview-eww + ;; Realtime markdown preview by eww + (use-package markdown-preview-eww) + + ;; https://github.com/ajtulloch/mkdown.el + ;; A small library that improves the look of Markdown previews from Emacs, using the style of mkdown.com. + (use-package mkdown + :disabled + :config + (add-to-list 'markdown-css-paths mkdown-css-file-name)) + + + ;; https://github.com/shime/emacs-livedown + ;; Emacs plugin for Livedown. + (use-package livedown + :disabled + :if (executable-find "livedown") + :quelpa (livedown + :fetcher github + :repo "shime/emacs-livedown") + :init + (require 'livedown))) + + +(provide 'modes/markdown) +;;; markdown.el ends here diff --git a/lisp/modes/org.el b/lisp/modes/org.el new file mode 100644 index 0000000..ce202ee --- /dev/null +++ b/lisp/modes/org.el @@ -0,0 +1,954 @@ +;;; org.el --- Org mode support -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(defconst *user-org-data-directory* + (path-join *user-data-directory* "org") + "Path to user's org data store.") + +(defconst *user-org-cache-directory* + (path-join *user-cache-directory* "org") + "Path to user's org cache store.") + +(defvar user/org-mobile-sync-timer nil + "Timer used for syncing OrgMobile.") + +(defvar user/org-mobile-sync-secs (* 60 20) + "Interval of OrgMobile sync in seconds.") + + +(defun user--org-mode-hook () + "Org mode hook." + (unless (derived-mode-p 'text-mode) + (user--text-mode-hook)) + + (with-feature 'org-sticky-header + ;; Enable sticky org mode header. + (org-sticky-header-mode t)) + + ;; Highlighting lines that are too long since it often causes false + ;; positives on URLs. + (user/whitespace-disable-style '(lines lines-tail)) + + (rainbow-delimiters-mode-disable) + + (setq + ;; Proper filling of org-mode text, form: + ;; * http://lists.gnu.org/archive/html/emacs-orgmode/2008-01/msg00375.html + paragraph-separate + "\f\\|\\*+ \\|[ ]*$\\| [ \t]*[:|]\\|^[ \t]+\\[[0-9]\\{4\\}-" + paragraph-start + (concat "\f\\|[ ]*$\\|\\*+ \\|\f\\|[ \t]*\\([-+*][ \t]+\\|" + "[0-9]+[.)][ \t] +\\)\\|[ \t]*[:|]\\|" + "^[ \t]+\\[[0-9]\\{4\\}-")) + + (with-feature 'org-table-sticky-header + ;; Enable sticky headers for tables. + (org-table-sticky-header-mode t)) + + (user/smartparens-enable) + + (org-bullets-mode t) + + ;;; (Bindings) ;;; + (user/bind-key-local :basic :open-buffer-context 'org-iswitchb) + (user/bind-key-local :basic :narrow-to-page 'org-narrow-to-subtree) + (user/bind-key-local :basic :narrow-to-region 'org-narrow-to-block) + (user/bind-key-local :basic :narrow-to-function 'org-narrow-to-element) + (user/bind-key-local :code :context-promote 'org-shiftup) + (user/bind-key-local :code :context-demote 'org-shiftdown)) + + +(defun user--org-agenda-finalize-hook () + "Org agenda display hook." + ;; Enable appointment notifications. + (org-agenda-to-appt t)) + + +(defun user--org-load-hook () + "Org-mode loaded hook." + (when (not noninteractive) + ;; Resume clocking tasks when Emacs is restarted. + (org-clock-persistence-insinuate)) + + ;; Load modules. + (when org-modules-loaded + (org-load-modules-maybe 'force)) + + ;; ;; Load Babel languages. + ;; (org-babel-do-load-languages 'org-babel-load-languages + ;; org-babel-load-languages)) + ) + + +(defun user/org-open-at-point (&optional arg) + "Like `org-open-at-point' but will use external browser with prefix ARG." + (interactive "P") + (if (not arg) + (org-open-at-point) + (let ((browse-url-browser-function #'browse-url-default-browser)) + (org-open-at-point)))) + + +(defun user/org-annotate-file-storage-file () + "Get the path to the annotation storage file." + (or (with-project-root project-root (path-abs-buffer) + (path-join project-root (concat (user/proj-name project) ".org"))) + org-annotate-file-storage-file)) + + +(defun user/org-annotate-file () + "Annotate the current buffer." + (interactive) + (with-feature 'org-annotate-file + (let ((storage-file (user/org-annotate-file-storage-file)) + (popwin-config '(:position :bottom))) + (popwin:display-buffer-1 (org-annotate-file-show-section storage-file) + :default-config-keywords popwin-config)))) + + +;; (defun user/org-mobile-sync-pull-and-push () +;; "Sync OrgMobile directory." +;; (org-mobile-pull) +;; (org-mobile-push) +;; (with-eval-after-load 'sauron +;; (sauron-add-event 'my 3 "Called org-mobile-pull and org-mobile-push"))) + + +;; (defun user/org-mobile-sync-start () +;; "Start automated `org-mobile-push'." +;; (interactive) +;; (setq user/org-mobile-sync-timer +;; (run-with-idle-timer user/org-mobile-sync-secs t +;; 'user/org-mobile-sync-pull-and-push))) + + +;; (defun user/org-mobile-sync-stop () +;; "Stop automated `org-mobile-push'." +;; (interactive) +;; (cancel-timer user/org-mobile-sync-timer)) + + +(use-package org + :init + ;; Create data and cache stores. + (make-directory *user-org-data-directory* t) + (make-directory *user-org-cache-directory* t) + ;; Fix for EIN if org hasn't been setup yet. + (autoload 'org-add-link-type "org" "" t) + :hook + ((org-load-hook . user--org-load-hook) + (org-mode-hook . user--org-mode-hook)) + :bind + (:map org-mode-map + ("C-c C-o" . user/org-open-at-point)) + :config + ;; https://github.com/sabof/org-bullets + ;; utf-8 bullets for org-mode + (use-package org-bullets) + + ;; (use-package org-plus-contrib + ;; :ensure t + ;; :no-require t) + + (validate-setq + ;; Org data store. + org-directory *user-org-data-directory* + ;; Notes data store. +;; org-default-notes-file (path-join *user-org-data-directory* "refile.org") + ;; Pressing return on a link follows it. + org-return-follows-link t + ;; Log time for TODO state changes. +;; org-log-done 'time + ;; Log time when rescheduling an entry. +;; org-log-reschedule 'time +;; org-log-redeadline 'time + ;; Round clock times to 15 minute increments. +;; org-time-stamp-rounding-minutes (quote (1 15)) + ;; Log drawer state changes. +;; org-log-into-drawer t + ;; Allow single letter commands at beginning of headlines. + ;; org-use-speed-commands t + + ;; Don't use the image width when inlining + org-image-actual-width nil + ;; Fontify code blocks by default. + org-src-fontify-natively t + ;; Tab should operate according to local mode. + org-src-tab-acts-natively t + ;; Disable the indentation increases by one space in a demotion command + org-adapt-indentation nil + ;; Don't preserve source code indentation so it can be adapted to document. + org-src-preserve-indentation nil + org-edit-src-content-indentation 0 + ;; Prevent editing of invisible regions. + org-catch-invisible-edits 'error + ;; Allow fast state transitions. +;; org-use-fast-todo-selection 'auto + ;; Do not record timestamp when using S-cursor to change state. +;; org-treat-S-cursor-todo-selection-as-state-change nil + ;; Start in folded view. + org-startup-folded t + ;; Enable speed commands. + org-use-speed-commands t + org-speed-commands-user + '(("0" . 'delete-window) + ("1" . 'delete-other-windows) + ("2" . 'split-window-vertically) + ("3" . 'split-window-horizontally) + ("h" . 'hide-other) + ("s" . 'org-save-all-org-buffers) + ("z" . 'org-add-note) + ("N" . 'org-narrow-to-subtree) + ("W" . 'widen) + ("m" . 'org-mark-subtree))) + + (when (eq default-terminal-coding-system 'utf-8) + (validate-setq + ;; Prettify content using UTF-8. + org-pretty-entities t)) + + ;; (setq org-todo-keyword-faces + ;; (quote (("DONE" :foreground "red" :weight bold)))) + + ;; TODO + ;; Incompatible with validate-setq. +;; (setq + ;; State transitions (http://doc.norang.ca/org-mode.html). +;; org-todo-keywords +;; (quote ((sequence "TODO(t)" "NEXT(n)" "|" "DONE(d)") +;; (sequence "WAITING(w@/!)" "HOLD(h@/!)" "|" "CANCELLED(c@/!)" "PHONE" +;; "MEETING"))) + ;; Triggered state changes. +;; org-todo-state-tags-triggers +;; (quote (("CANCELLED" ("CANCELLED" . t)) +;; ("WAITING" ("WAITING" . t)) +;; ("HOLD" ("WAITING") ("HOLD" . t)) +;; (done ("WAITING") ("HOLD")) +;; ("TODO" ("WAITING") ("CANCELLED") ("HOLD")) +;; ("NEXT" ("WAITING") ("CANCELLED") ("HOLD")) +;; ("DONE" ("WAITING") ("CANCELLED") ("HOLD"))))) + + ;; (add-many-to-list + ;; 'org-modules + ;; ;; File attachment manager. + ;; 'org-attach + ;; ;; Link to BibTeX entries. + ;; 'org-bibtex + ;; ;; Link to tags. + ;; 'org-ctags + ;; ;; Link to articles and messages in Gnus. + ;; 'org-gnus + ;; ;; Habit tracking. + ;; 'org-habit + ;; ;; Support links to info pages. + ;; 'org-info + ;; ;; Support links to man pages. + ;; 'org-man + ;; ;; Export org buffer to MIME email message. + ;; 'org-mime + ;; ;; Allow external applications to talk to org. + ;; 'org-protocol + ;; ;; Embed source code in org-mode. + ;; 'org-src) + + ;; (when (feature-p 'bbdb) + ;; (add-to-list 'org-modules 'org-bbdb)) + ;; (when (feature-p 'emacs-w3m) + ;; (add-to-list 'org-modules 'org-w3m)) + ;; (when (feature-p 'wanderlust) + ;; (add-to-list 'org-modules 'org-wl)) + ;; (with-executable 'git + ;; (add-to-list 'org-modules 'org-git-link)) + + ;; (setq org-agenda-files '("~/org/GPE_OBIF.org")) + ;; (setq org-default-notes-file "~/org/GPE_OBIF.org" initial-buffer-choice org-default-notes-file) + (setq org-duration-format (quote h:mm)) + (setq org-hierarchical-todo-statistics nil) + (setq org-startup-folded "folded") + (setq org-todo-keywords '((sequence "TODO" "|" "DONE" "REJECTED"))) + + (when (display-graphic-p) + (validate-setq + ;; Display inline images when starting up. + org-startup-with-inline-images t)) + + ;; https://orgmode.org/manual/Refile-and-Copy.html + (use-package org-refile + :ensure nil + :config + (validate-setq + ;; Allow refile to create parent tasks, with confirmation. + org-refile-allow-creating-parent-nodes 'confirm + ;; Cache refile operations for performance. + org-refile-use-cache t)) + + ;; https://www.gnu.org/software/emacs/manual/html_node/org/Tables.html + (use-package org-table + :ensure nil + :config + ;; https://github.com/cute-jumper/org-table-sticky-header + ;; A minor mode to show the sticky header for org-mode tables. + (use-package org-table-sticky-header)) + + ;; https://orgmode.org/manual/Capture.html + ;; https://www.labri.fr/perso/nrougier/GTD/index.html + (use-package org-capture + :ensure nil + :init + (user/bind-key-global :apps :capture-task 'org-capture) + :config + ;; Incompatible with validate-setq. + ;; TODO + ;; (setq + ;; Capture templates. + ;; org-capture-templates + ;; (quote (("t" "todo" entry (file org-default-notes-file) + ;; "* TODO %?\n%U\n%a\n" :clock-in t :clock-resume t) + ;; ("r" "respond" entry (file org-default-notes-file) + ;; "* NEXT Respond to %:from on %:subject\nSCHEDULED: %t\n%U\n%a\n" + ;; :clock-in t :clock-resume t :immediate-finish t) + ;; ("n" "note" entry (file org-default-notes-file) + ;; "* %? :NOTE:\n%U\n%a\n" :clock-in t :clock-resume t) + ;; ("j" "Journal" entry + ;; (file+datetree (path-join *user-org-data-directory* "diary.org")) + ;; "* %?\n%U\n" :clock-in t :clock-resume t) + ;; ("w" "org-protocol" entry (file org-default-notes-file) + ;; "* TODO Review %c\n%U\n" :immediate-finish t) + ;; ("m" "Meeting" entry (file org-default-notes-file) + ;; "* MEETING with %? :MEETING:\n%U" :clock-in t :clock-resume t) + ;; ("p" "Phone call" entry (file "~/git/org/refile.org") + ;; "* PHONE %? :PHONE:\n%U" :clock-in t :clock-resume t) + ;; ("h" "Habit" entry (file org-default-notes-file) + ;; (concat "* NEXT %?\n%U\n%a\n" + ;; "SCHEDULED: " + ;; "%(format-time-string \"<%Y-%m-%d %a .+1d/3d>\")\n:" + ;; "PROPERTIES:\n:STYLE: habit\n" + ;; ":REPEAT_TO_STATE: NEXT\n:END:\n"))))) + + ;; https://github.com/tkf/org-mode/blob/master/lisp/org-id.el + ;; Global identifiers for Org-mode entries + (use-package org-id + :disabled + :ensure nil + :config + (validate-setq + org-id-locations-file + (path-join *user-org-data-directory* "org-id-locations"))) + + ;; https://github.com/grugrut/helm-books/tree/625aadec1541a5ca36951e4ce1301f4b6fe2bf3f + ;; Book search interface for emacs helm. + (use-package helm-books + :disabled + :config + (add-to-list + 'org-capture-templates + '("b" "book memo" entry + (file (concat org-directory "book.org")) + "* %(helm-books)"))) + + ;; https://github.com/Chobbes/org-chef + ;; A package for making a cookbook and managing recipes with org-mode. + (use-package org-chef + :disabled + :config + (add-to-list + 'org-capture-templates + `("c" "Cookbook" entry + (file ,(path-join *user-org-data-directory* "cookbook.org")) + "%(org-chef-get-recipe-from-url)" + :empty-lines 1))) + + ;; https://github.com/waymondo/org-repo-todo + ;; Simple repository todo management with org-mode + (use-package org-repo-todo)) + + ;; https://github.com/emacs-mirror/emacs/blob/master/lisp/org/ob-core.el + ;; Working with Code Blocks + (use-package ob-core + :ensure nil + :config + (validate-setq + ;; Don't ask for validation. + org-confirm-babel-evaluate nil) + + (add-many-to-list + 'org-babel-load-languages + ;; Emacs Lisp + '(emacs-lisp . t) + ;; Shell script + '(shell . t)) + + (let ((to-load '((emacs-lisp . t) (shell . t)))) + (with-executable 'g++ + (push '(C . t) to-load) + ;; Use of lsp-clangd for C/C++ + (setq org-babel-C++-compiler "bear g++")) + (with-executable 'dot + (push '(dot . t) to-load)) + (with-executable 'ghc + (push '(haskell . t) to-load)) + (with-executable 'gnuplot + (push '(gnuplot . t) to-load)) + (with-executable 'latex + (push '(latex . t) to-load)) + (with-executable 'perl + (push '(perl . t) to-load)) + (with-executable 'python + (push '(python . t) to-load)) + (with-executable 'R + (push '(R . t) to-load)) + (with-executable 'ruby + (push '(ruby . t) to-load)) + + (when (feature-p 'plantuml-mode) + ;; https://github.com/skuro/plantuml-mode + ;; A major mode for editing PlantUML sources in Emacs + (use-package ob-plantuml + :ensure nil + :after modes/plantuml + :config + (validate-setq + org-plantuml-jar-path *user-plantuml-jar-path*)) + (push '(plantuml . t) to-load)) + + (org-babel-do-load-languages 'org-babel-load-languages to-load)) + + ;; https://github.com/dfeich/helm-lib-babel/tree/41bc0cdea8a604c6c8dc83ed5066644d33688fad + ;; Emacs helm extension for inserting a reference to an org source block function + (use-package helm-lib-babel) + ;; https://github.com/astahlman/ob-async + ;; Asynchronous src_block execution for org-babel + (use-package ob-async + :defer + :config + (add-to-list + ;; Execute org-babel asynchronously. + 'org-ctrl-c-ctrl-c-hook 'ob-async-org-babel-execute-src-block)) + + ;; https://github.com/pope/ob-go + ;; Org-Babel support for evaluating go code + (use-package ob-go + :if (executable-find "go") + :init (add-to-list 'org-babel-load-languages '(go . t))) + + ;; https://github.com/zweifisch/ob-http + ;; Org-Babel support for evaluating http + (use-package ob-http + :init (add-to-list 'org-babel-load-languages '(http . t))) + + ;; https://github.com/micanzhang/ob-rust + ;; Org-Babel support for evaluating Rust code + (use-package ob-rust + :if (executable-find "rustc") + :init (add-to-list 'org-babel-load-languages '(rust . t))) + + ;; https://github.com/krisajenkins/ob-translate + ;; Allows you to translate blocks of text within org-mode + (use-package ob-translate + :disabled + :init (add-to-list 'org-babel-load-languages '(translate . t))) + + ;; https://github.com/andrmuel/ob-uart + ;; Org babel support for UART communication + (use-package ob-uart + :disabled + :init (add-to-list 'org-babel-load-languages '(uart . t))) + + ;; https://github.com/ahendriksen/ob-tmux + ;; Ob-tmux is an Emacs library that allows org mode to evaluate code blocks in a tmux session. + (use-package ob-tmux + :if (executable-find "tmux") + :init (add-to-list 'org-babel-load-languages '(tmux . t)))) + + ;; https://github.com/emacs-mirror/emacs/blob/master/lisp/org/ox-org.el + ;; Org Back-End for Org Export Engine + (use-package ox + :ensure nil + :config + (validate-setq + ;; Export as UTF-8. + org-export-coding-system 'utf-8) + + ;; Org export modules to load by default. + (add-many-to-list + 'org-export-backends + ;; Ascii support. + 'ascii + ;; HTML. + 'html + ;; OpenDocument Text support. + 'odt) + + (with-executable 'latex + (add-many-to-list + 'org-export-backends + ;; Beamer presentation export. + 'beamer + ;; Plain LaTeX export. + 'latex)) + + ;; https://github.com/tomalexander/orgmode-mediawiki/tree/a9327150293e370e500ba55bddfe5fc435c6bf9b + ;; A mediawiki export for Emacs org-mode + (use-package ox-mediawiki + :config (add-to-list 'org-export-backends 'mediawiki)) + ;; https://github.com/larstvei/ox-gfm + ;; Github Flavored Markdown Back-End for Org Export Engine + (use-package ox-gfm + :config (add-to-list 'org-export-backends 'gfm)) + ;; https://github.com/stig/ox-jira.el + ;; Org-mode export backend for JIRA markup + (use-package ox-jira + :disabled + :config (add-to-list 'org-export-backends 'jira)) + ;; https://github.com/kawabata/ox-pandoc + ;; Another org-mode exporter via pandoc + (use-package ox-pandoc + :if (executable-find "pandoc") + :pin "MELPA" + :config (add-to-list 'org-export-backends 'pandoc)) + ;; https://github.com/choppsv1/org-rfc-export/tree/1a49535cf927cd52ffa05c815b890888c4addf86 + ;; Org-mode export back-end for creating internet-drafts and RFCs using xml2rfc. + (use-package ox-rfc + :disabled + :config (add-to-list 'org-export-backends 'rfc))) + + ;; Load org agenda. + (add-to-list 'org-modules 'org-agenda) + + (add-to-list 'org-modules 'org) + + ;; https://orgmode.org/manual/Org-Mobile.html + (use-package org-mobile + :disabled + :ensure nil + :config + (validate-setq + ;; Location of TODO items to sync. + org-mobile-inbox-for-pull org-default-notes-file + ;; MobileOrg sync directory. + org-mobile-directory (path-join *user-org-data-directory* "mobile") + ;; Custom agenda view. + org-mobile-force-id-on-agenda-items nil)) + + ;; https://github.com/ifree/org-onenote + ;; Post org file to onenote + (use-package org-onenote + :disabled) + + ;;; (Packages) ;;; + ;; https://github.com/unhammer/org-rich-yank + ;; Rich text clipboard for org-mode: Paste into a #+BEGIN_SRC block of correct mode, with link to where it came from + (use-package org-rich-yank + :bind-wrap + (:map org-mode-map + ((:key :basic :alternate-paste) . org-rich-yank))) + + ;; https://github.com/tkf/org-mode/blob/master/lisp/org-clock.el + ;; The time clocking code for Org-mode + (use-package org-clock + :ensure nil + :config + (validate-setq + ;; Clock data store. + org-clock-persist-file (path-join *user-org-cache-directory* + "org-clock-save.el")) + + (when (not noninteractive) + ;; When running in batch, don't setup time tracking. + (validate-setq + ;; Resume clocking task on clock-in if the clock is open. + org-clock-in-resume t + ;; Save clock data and state changes and notes in the LOGBOOK drawer. + org-clock-into-drawer t + ;; Remove clock line if time is zero. + org-clock-out-remove-zero-time-clocks t + ;; Stop clock when entry is marked as DONE. + org-clock-out-when-done t + ;; Show the amount of time spent on the current task today. + org-clock-mode-line-total 'today + ;; Resume clock when reopening Emacs. + org-clock-persist t + ;; Enable auto clock resolution for finding open clocks. + org-clock-auto-clock-resolution 'when-no-clock-is-running + ;; Include current clocking task in clock reports. + org-clock-report-include-clocking-task t))) + + ;; https://github.com/alphapapa/org-sticky-header + ;; Show off-screen Org heading at top of window + (use-package org-sticky-header) + + ;; https://github.com/daimrod/org-sync + ;; Synchronize Org documents with external services + (use-package org-sync + :disabled + :config + (validate-setq + ;; Org sync cache store. + org-sync-cache-file (path-join *user-org-cache-directory* "org-sync-cache")) + + ;; Redmine module. + (load "org-sync-redmine") + ;; GitHub module. + (load "org-sync-github")) + + ;; https://github.com/dengste/org-caldav + ;; Caldav sync for Emacs orgmode + (use-package org-caldav + :disabled + :config + (validate-setq + ;; Path to state synchronization file. + org-caldav-save-directory (path-join *user-org-cache-directory* "org-caldav") + ;; Path to inbox file. + org-caldav-inbox (path-join *user-org-data-directory* "org-caldav-inbox.org") + ;; Path to backup file. + org-caldav-backup-file (path-join *user-org-data-directory* "org-caldav-backup.org") + ;; Link to org agenda. + org-caldav-files org-agenda-files + ;; Ask before deleting entries on server. + org-caldav-delete-calendar-entries 'ask) + + ;; Ensure that state synchronization directory exists. + (make-directory org-caldav-save-directory t)) + + ;; https://github.com/alphapapa/org-web-tools + ;; View, capture, and archive Web pages in Org-mode + (use-package org-web-tools) + ;; https://github.com/facetframer/orgnav + ;; Quickly navigate and search your emacs org trees; use this navigation to capture and organize + (use-package orgnav) + ;; https://github.com/rlister/org-present + ;; Ultra-minimalist presentation minor-mode for Emacs org-mode + (use-package org-present + :disabled + :config + (with-eval-after-load 'org-present + (add-hook 'org-present-mode-hook + (lambda () + (org-present-big) + (org-display-inline-images) + (org-present-hide-cursor) + (org-present-read-only))) + (add-hook 'org-present-mode-quit-hook + (lambda () + (org-present-small) + (org-remove-inline-images) + (org-present-show-cursor) + (org-present-read-write))))) + + ;; https://github.com/alphapapa/org-rifle + ;; Rifle through your Org-mode buffers and acquire your target + (use-package helm-org-rifle) + ;; https://github.com/weirdNox/org-noter + ;; Emacs document annotator, using Org-mode + (use-package org-noter + :disabled) + ;; https://github.com/louietan/anki-editor + ;; Emacs minor mode for making Anki cards with Org + (use-package anki-editor) + ;; https://github.com/dfeich/org-screenshot + ;; screenshots integrated with emacs org mode attachments + (use-package org-attach-screenshot + :disabled) + ;; https://github.com/harrybournis/org-fancy-priorities + ;; Display Org Mode priorities as custom strings + (use-package org-fancy-priorities + :hook (org-mode-hook . org-fancy-priorities-mode)) + ;; https://github.com/calvinwyoung/org-autolist + ;; Making it even easier to edit lists in org-mode! + (use-package org-autolist + :hook (org-mode-hook . org-autolist-mode)) + ;; https://github.com/abo-abo/org-download + ;; Drag and drop images to Emacs org-mode + (use-package org-download) + ;; https://github.com/tarsius/org-elisp-help + ;; Org links to emacs-lisp documentation + (use-package org-elisp-help) + ;; https://github.com/emacsjanitors/org-fstree + ;; Include a filesystem subtree into an org file + (use-package org-fstree + :disabled) + ;; https://github.com/flexibeast/org-vcard + ;; Export and import vCards from within GNU Emacs' Org mode. + (use-package org-vcard + :disabled + :config + (validate-setq + org-vcard-custom-styles-dir (path-join *user-org-data-directory* "org-vcard-styles"))) + + ;; https://github.com/gizmomogwai/org-kanban + ;; Kanban table for org-mode + ;; Enables `#+BEGIN: kanban' for producing a kanban table. + (use-package org-kanban + :disabled)) + +;; https://orgmode.org/manual/Agenda-Commands.html +(use-package org-agenda + :ensure org-plus-contrib + :defer + :hook (org-agenda-finalize-hook . user--org-agenda-finalize-hook) + :bind-wrap + (((:key :apps :agenda) . org-agenda) + ((:key :apps :todo) . org-todo-list)) + :config + (let ((agenda-data-store (path-join *user-org-data-directory* "agendas"))) + (validate-setq + ;; Agenda data store. + org-agenda-files `(,agenda-data-store)) + ;; Ensure that agenda data store exists. + (make-directory agenda-data-store t)) + + (validate-setq + org-agenda-breadcrumbs-separator " ❱ " + ;; Ignore agenda files that are unavailable. + org-agenda-skip-unavailable-files t + ;; Restore window configuration when done with the agenda. + org-agenda-restore-windows-after-quit t + ;; Start on Monday. + org-agenda-start-on-weekday 1 + ;; Show month by default. +;; org-agenda-span 'month + ;; Don't display scheduled todos. +;; org-agenda-todo-ignore-scheduled 'future + ;; Don't show nested todos. +;; org-agenda-todo-list-sublevels nil + ;; Don't dim blocked tasks. + org-agenda-dim-blocked-tasks nil + ;; Compact block agenda view. + ;; org-agenda-compact-blocks t + ;; Include Emacs' Diary in org-agenda. + org-agenda-include-diary t + ;; Switch window when opening org-agenda. + ;; org-agenda-window-setup 'other-window + ;; Display indirect buffers in the "current" window. + org-indirect-buffer-display 'current-window + ;; Reset all custom commands. + org-agenda-custom-commands nil) + + (add-many-to-list + 'org-agenda-custom-commands + ;; `("T", "Daily Timesheet" + ;; (my-org-timeline) + ;; )) + `("a" "Agenda" + ((agenda "" + ((org-agenda-prefix-format "%-12:c%?-12t% b% s") + (org-genda-list)))))) + ;; ( + ;; (org-agenda-repeating-timestamp-show-all nil) + ;; (org-agenda-remove-tags t) + ;; (org-agenda-overriding-header "⚡ Calendar") + ;; ;; (org-agenda-prefix-format "%?-12t% s") + ;; (org-agenda-prefix-format " %?-12t% s") + ;; (org-agenda-todo-keyword-format "") + ;; (my-org-timeline) + ;; ))))) + ;; '("a" "Agenda" org-agenda-list) + ;; '("c" . "COLLECT...") + ;; '("cb" "CollectBox" ((alltodo ""))) + ;; '("f" . "FOCUS...") + ;; '("f." "Today" + ;; ((agenda "" + ;; ((org-agenda-entry-types '(:timestamp :sexp)) + ;; (org-agenda-overriding-header + ;; (concat "CALENDAR Today" + ;; (format-time-string "%a %d" (current-time)))) + ;; (org-agenda-span 'day))) + ;; (tags-todo "LEVEL=1+REFILE" + ;; ((org-agenda-overriding-header "COLLECTBOX (Unscheduled)"))) + ;; (tags-todo "DEADLINE=\"<+0d>\"" + ;; ((org-agenda-overriding-header "DUE TODAY") + ;; (org-agenda-skip-function + ;; '(org-agenda-skip-entry-if 'notedeadline)) + ;; (org-agenda-sorting-strategy '(priority-down)))) + ;; (tags-todo "DEADLINE<\"<+0d>\"" + ;; ((org-agenda-overriding-header "OVERDUE") + ;; (org-agenda-skip-function + ;; '(org-agenda-skip-entry-if 'notedeadline)) + ;; (org-agenda-sorting-strategy '(priority-down)))) + ;; (agenda "" + ;; ((org-agenda-entry-types '(:scheduled)) + ;; (org-agenda-overriding-header "SCHEDULED") + ;; (org-agenda-skip-function + ;; '(org-agenda-skip-entry-if 'todo 'done)) + ;; (org-agenda-sorting-strategy + ;; '(priority-down time-down)) + ;; (org-agenda-span 'day) + ;; (org-agenda-start-on-weekday nil) + ;; (org-agenda-time-grid nil))) + ;; (todo "DONE" + ;; ((org-agenda-overriding-header "COMPLETED")))) + ;; ((org-agenda-format-date "") + ;; (org-agenda-start-with-clockreport-mode nil))) + ;; '("fh" "Hotlist" + ;; ((tags-todo "DEADLINE<\"<+0d>\"" + ;; ((org-agenda-overriding-header "OVERDUE"))) + ;; (tags-todo "DEADLINE>=\"<+0d>\"+DEADLINE<=\"<+1w>\"" + ;; ((org-agenda-overriding-header "DUE IN NEXT 7 DAYS"))) + ;; (tags-todo "DEADLINE=\"\"+FLAGGED|DEADLINE>\"<+1w>\"+FLAGGED" + ;; ((org-agenda-overriding-header "FLAGGED")))) + ;; ((org-agenda-todo-ignore-scheduled 'future))) + ;; '("r" . "REVIEW...") + ;; '("ra" . "All Tasks...") + ;; '("rad" "All Tasks (grouped by Due Date)" + ;; ((tags-todo "DEADLINE<\"<+0d>\"" + ;; ((org-agenda-overriding-header "OVERDUE") + ;; (org-agenda-skip-function + ;; '(org-agenda-skip-entry-if 'notdeadline)))) + ;; (tags-todo "DEADLINE=\"<+0d>\"" + ;; ((org-agenda-overriding-header "DUE TODAY") + ;; (org-agenda-skip-function + ;; '(org-agenda-skip-entry-if 'notdeadline)))) + ;; (tags-todo "DEADLINE=\"<+1d>\"" + ;; ((org-agenda-overriding-header "DUE TOMORROW") + ;; (org-agenda-skip-function + ;; '(org-agenda-skip-entry-if 'notdeadline)))) + ;; (tags-todo "DEADLINE>\"<+1d>\"+DEADLINE<=\"<+7d>\"" + ;; ((org-agenda-overriding-header "DUE WITHIN A WEEK") + ;; (org-agenda-skip-function + ;; '(org-agenda-skip-entry-if 'notdeadline)))) + ;; (tags-todo "DEADLINE>\"<+7d>\"+DEADLINE<=\"<+28d>\"" + ;; ((org-agenda-overriding-header "DUE WITHIN A MONTH") + ;; (org-agenda-skip-function + ;; '(org-agenda-skip-entry-if 'notdeadline)))) + ;; (tags-todo "DEADLINE>\"<+28d>\"" + ;; ((org-agenda-overriding-header "DUE LATER") + ;; (org-agenda-skip-function + ;; '(org-agenda-skip-entry-if 'notdeadline))) ) + ;; (tags-todo "TODO={WAIT}" + ;; ((org-agenda-overriding-header "WAITING FOR") + ;; (org-agenda-skip-function + ;; '(org-agenda-skip-entry-if 'deadline)))) + ;; (todo "" + ;; ((org-agenda-overriding-header "WAITING FOR") + ;; (org-agenda-skip-function + ;; '(org-agenda-skip-entry-if 'deadline))))) + ;; ((org-agenda-sorting-strategy '(priority-down)) + ;; (org-agenda-write-buffer-name "All Tasks (grouped by Due Date)")) + ;; "~/Documents/Org/all-tasks-by-due-date.pdf") + ;; '("ra1" "All Tasks with a due date" + ;; ((alltodo "")) + ;; ((org-agenda-overriding-header "All Tasks (sorted by Due Date)") + ;; (org-agenda-skip-function + ;; '(org-agenda-skip-entry-if 'notdeadline)) + ;; (org-agenda-sorting-strategy '(deadline-up)))) + ;; '("rt" . "Timesheet...") + ;; '("rtw" "Weekly Timesheet" + ;; ((agenda "")) + ;; ( + ;; ;; (org-agenda-format-date "") + ;; (org-agenda-overriding-header "WEEKLY TIMESHEET") + ;; (org-agenda-skip-function '(org-agenda-skip-entry-if 'timestamp)) + ;; (org-agenda-span 'week) + ;; (org-agenda-start-on-weekday 1) + ;; (org-agenda-start-with-clockreport-mode t) + ;; (org-agenda-time-grid nil))) + ;; '("rc" . "Calendar...") + ;; '("rc7" "Events and appointments for 7 days" + ;; ((agenda "")) + ;; ((org-agenda-entry-types '(:timestamp :sexp)) + ;; ;; (org-agenda-overriding-header "Calendar for 7 days") + ;; ;; (org-agenda-repeating-timestamp-show-all t) + ;; (org-agenda-span 'week) + ;; (org-agenda-format-date "\n%a %d") + ;; ;; (org-agenda-date-weekend ... new face ...) + ;; (org-agenda-time-grid nil))) + ;; '("rw" "Weekly review" + ;; ((tags "CATEGORY={@REFILE}&LEVEL<=2" + ;; ((org-agenda-overriding-header "NEW TASKS"))) + ;; (agenda "" + ;; ((org-agenda-clockreport-mode t) + ;; (org-agenda-format-date + ;; (concat "\n" + ;; "%Y-%m-%d" " %a " + ;; (make-string (window-width) ?_))) + ;; (org-agenda-overriding-header "PAST WEEK") + ;; (org-agenda-prefix-format " %?-11t %i %-12:c% s") + ;; (org-agenda-show-log 'clockcheck) + ;; (org-agenda-span 7) + ;; (org-agenda-start-day "-1w") + ;; (org-deadline-warning-days 0))) + ;; (agenda "" + ;; ((org-agenda-overriding-header "NEXT MONTH") + ;; (org-agenda-span 'month) + ;; (org-agenda-start-day "+0d") + ;; (org-deadline-warning-days 0))) + ;; (todo "PROJECT" + ;; ((org-agenda-overriding-header "PROJECT LIST"))) + ;; (todo "DONE|PROJECTDONE" + ;; ((org-agenda-overriding-header + ;; "Candidates to be archived")))))) + + ;; (when (not noninteractive) + ;; ;; When running in batch, don't setup windows. + ;; (validate-setq + ;; ;; Show agenda in current window. + ;; org-agenda-window-setup 'current-window)) + ) + ;; https://github.com/emacs-mirror/emacs/blob/master/lisp/org/org-habit.el + ;; The habit tracking code for Org + (use-package org-habit + :ensure nil + :config + (validate-setq + ;; Position the habit graph to the right. + org-habit-graph-column 50)) + + ;; https://github.com/alphapapa/org-super-agenda + ;; Supercharge your Org daily/weekly agenda by grouping items + (use-package org-super-agenda + :disabled + :config + (add-to-list + 'org-agenda-custom-commands + '("u" "Super view" + ((agenda "" ((org-super-agenda-groups + '((:name "Today" + :time-grid t))))) + (todo "" ((org-agenda-overriding-header "") + (org-super-agenda-groups + '((:name "Projects" + :children todo) + (:discard (:anything t))))))))) + + (org-super-agenda-mode)) + + ;; https://github.com/spegoraro/org-alert + ;; System notifications of org agenda items + (use-package org-alert + :after alert + :config + (org-alert-enable)) + + ;; https://github.com/Malabarba/org-agenda-property + ;; Display org properties in the agenda buffer + (use-package org-agenda-property) + + ;; https://github.com/Fuco1/org-timeline + ;; Add graphical view of agenda to agenda buffer + (use-package org-timeline + :disabled + :hook (org-agenda-finalize-hook . org-timeline-insert-timeline)) + +;; https://github.com/tkf/org-mode/blob/master/contrib/lisp/org-annotate-file.el +;; Annotate a file with org syntax +(use-package org-annotate-file + :disabled + :ensure org-plus-contrib + :defer + :init + (autoload 'org-annotate-file "org-annotate-file" nil t) + :bind-wrap + ((:key :util :annotate-buffer) . user/org-annotate-file) + :config + (validate-setq + ;; Annotations data store. + org-annotate-file-storage-file (path-join *user-org-data-directory* + "annotations.org") + ;; Add link to current line number. + org-annotate-file-add-search t)) + + +(provide 'modes/org) +;;; org.el ends here diff --git a/lisp/modes/prog.el b/lisp/modes/prog.el new file mode 100644 index 0000000..c7ac3f1 --- /dev/null +++ b/lisp/modes/prog.el @@ -0,0 +1,73 @@ +;;; prog.el --- setup shared defaults for programming modes -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(defun user--prog-mode-hook () + "Programming mode hook." + (user--fundamental-mode-hook) + + (when (user-flyspell-p) + ;; Protect against missing dictionary. + (try-eval + ;; Run spell-checker in programming mode. + (flyspell-prog-mode))) + + ;; Buttonize links. + ;;(goto-address-prog-mode t) + + ;;(outline-minor-mode t) + + (validate-setq + ;; When using fill-paragraph or auto-fill-mode break lines at 80 characters by + ;; default. + fill-column 120) + + ;; Try to enable completion system. + (cond + ;; ((user/auto-complete-p) (auto-complete-mode t)) + ((user/company-mode-p) (company-mode t))) + + ;;; (Bindings) ;;; + (user/bind-key-local :code :comment (if (feature-p 'comment-dwim-2) + 'comment-dwim-2 + 'comment-dwim))) + + +(use-package prog-mode + :ensure nil + :hook (prog-mode-hook . user--prog-mode-hook) + :config + ;;; (Packages) ;;; + ;; https://github.com/emacs-mirror/emacs/blob/master/lisp/progmodes/subword.el + ;; Handling capitalized subwords in a nomenclature + (use-package subword + :disabled + :ensure nil + :diminish subword-mode) + ;; https://github.com/remyferre/comment-dwim-2 + ;; A replacement for the emacs' built-in command `comment-dwim' + (use-package comment-dwim-2) + ;; https://github.com/emacsorphanage/quickrun + ;; Run command quickly. This packages is inspired quickrun.vim + (use-package quickrun + :bind-wrap + (:map prog-mode-map + ((:key :code :eval-buffer) . quickrun) + ((:key :code :eval-selection) . quickrun-region))) + ;; https://github.com/vincekd/comment-tags + ;; Emacs package to highlight and manage comment tags like TODO, BUG, FIXME, etc. + (use-package comment-tags + :diminish comment-tags-mode + :hook (prog-mode-hook . comment-tags-mode) + :config + (setq + comment-tags/keymap-prefix (user/get-key :nav :find-todos))) + ;; https://github.com/ignacy/idle-highlight-in-visible-buffers-mode + ;; An Emacs minor mode that highlights current word in all visible buffers + (use-package idle-highlight-in-visible-buffers-mode + :disabled + :hook (prog-mode-hook . idle-highlight-in-visible-buffers-mode))) + + +(provide 'modes/prog) +;;; prog.el ends here diff --git a/lisp/modes/python.el b/lisp/modes/python.el new file mode 100644 index 0000000..e8dfe6e --- /dev/null +++ b/lisp/modes/python.el @@ -0,0 +1,163 @@ +;;; python.el --- initializes Python modes -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: +(require 'lsp-pylsp) + +(defun user--python-format-before-save () + "Python cleanup and format before save buffer." + (lsp-format-buffer) + (delete-trailing-whitespace) +) + +(defun user--python-mode-hook () + "Python mode hook." + + ;; lsp python customization + (lsp-register-custom-settings + '(("pylsp.plugins.pyls_mypy.enabled" t t) + ;; ("pylsp.plugins.flake8.maxLineLength" 120) + ;; ("pylsp.plugins.pycodestyle.maxLineLength" 120) + ("pylsp.plugins.yapf.enabled" t t) + ("pylsp.plugins.pylint.enabled" t t))) + ;; ("pyls.plugins.pyls_mypy.live_mode" nil t) + ;; ("pyls.plugins.pyls_black.enabled" t t) + ;; ("pyls.plugins.pyls_isort.enabled" t t) + ;; ("pyls.plugins.flake8.enabled" t t))) + + (setq lsp-pylsp-plugins-flake8-max-line-length 120) + (setq lsp-pylsp-plugins-pycodestyle-max-line-length 120) + + ;; Enable virtualenv support. + (when(feature-p 'pyvenv) + (pyvenv-mode t)) + + (when(feature-p 'anaconda-mode) + (anaconda-mode t)) + + ;; Enable smart parenthesis handling. + (user/smartparens-enable) + + ;; Separate camel-case into separate words + (subword-mode t) + + ;; ElDoc shows function documentation as you type + (eldoc-mode t) + + ;; Select pylint for ckecing + ;; (setq flycheck-checker nil) + ;; (setq-default flycheck-disabled-checkers '(lsp)) + ;; (flycheck-add-mode 'python-flake8 'python-mode) + ;; (flycheck-select-checker 'python-pycheckers) + + ;; (flycheck-add-next-checker 'lsp 'python-pycheckers) + + ;; (setq-local lsp-pylsp-plugins-pylint-enabled nil) + + ;; (with-eval-after-load "lsp-mode" + ;; (add-to-list 'lsp-disabled-clients 'pyls) + ;; (add-to-list 'lsp-enabled-clients 'pylsp)) + + ;; (Bindings) ;; + ;; (when(feature-p 'nose) + ;; (user/bind-key-local: code: test 'nosetests-all)) + ;; (when(feature-p 'pyvenv) + ;; (user/bind-key-local: code: virtual 'pyvenv-workon)) + ;; (when(feature-p 'lsp-pyright) + ;; (require 'lsp-pyright))) +) + +(use-package python + :if (executable-find "python") + :defer + :mode ("SCon\(struct\|script\)$" . python-mode) + :interpreter ("python[0-9.]*" . python-mode) + :hook + (python-mode-hook . lsp) + (python-mode-hook . user--python-mode-hook) + (python-mode-hook . (lambda () + (add-hook 'before-save-hook #'user--python-format-before-save nil t))) + + :config + (validate-setq + ;; Don't try to guess the indentation. + python-indent-guess-indent-offset nil) + + (with-executable 'ipython3 + (validate-setq + ;; Set IPython as default interpreter. + python-shell-interpreter "ipython3" + python-shell-interpreter-args "" + python-shell-prompt-regexp "In \\[[0-9]+\\]: " + python-shell-prompt-output-regexp "Out\\[[0-9]+\\]: " + python-shell-completion-setup-code "from IPython.core.completerlib import module_completion" + python-shell-completion-module-string-code "';'.join(module_completion('''%s'''))\n" + python-shell-completion-string-code "';'.join(get_ipython().Completer.all_completions('''%s'''))\n")) + + (with-executable 'bpython + (defun user/bpython-term() + "Launch or switch to a `bpython' buffer." + (interactive) + (if (not (get-buffer "*bpython*")) + (progn + (ansi-term "bpython" "bpython")) + (switch-to-buffer "*bpython*")))) + + ;; (Packages) ;; + ;; https://github.com/pythonic-emacs/anaconda-mode + ;; Code navigation, documentation lookup and completion for Python. + (use-package anaconda-mode) + ;; https://github.com/tsgates/pylookup + ;; Emacs mode for searching python documents with convenience + (use-package pylookup + :disabled + :quelpa(pylookup + :fetcher github + :repo "tsgates/pylookup")) + ;; https://github.com/syl20bnr/nose.el + ;; This gives a bunch of functions that handle running nosetests on a particular buffer or part of a buffer. + (use-package nose + :disabled) + ;; https://github.com/tkf/emacs-python-environment + ;; Python virtualenv API for Emacs Lisp + (use-package python-environment + :disabled + :config + (validate-setq + ;; Locate of Python environment store. + python-environment-directory(path-join *user-cache-directory* + "python-environment"))) + ;; https://github.com/pwalsh/pipenv.el + ;; A Pipenv porcelain inside Emacs. + (use-package pipenv + :disabled + :if (executable-find "pipenv") + :hook(python-mode-hook . pipenv-mode)) + + ;; https://github.com/msherry/flycheck-pycheckers + ;; Multiple syntax checker for Python in Emacs, using Flycheck + (use-package flycheck-pycheckers + :disabled + :after flycheck + :hook (flycheck-mode-hook . flycheck-pycheckers-setup) + :config (setq flycheck-pycheckers-checkers (remove 'mypy2 flycheck-pycheckers-checkers)) + ) + ;; https://github.com/chocoelho/flycheck-prospector + ;; flycheck support for prospector + (use-package flycheck-prospector + :disabled + :if (executable-find "prospector") + :hook(flycheck-mode-hook . flycheck-prospector-setup)) + ;; https://github.com/jgosmann/pylint-venv + ;; Make pylint respect virtualenvs. + (use-package pyvenv + :ensure t + :config + (pyvenv-mode 1)) + + ;; https://github.com/emacsorphanage/helm-pydoc + ;; helm-pydoc.el is pydoc helm interface + (use-package helm-pydoc + :pin "MELPA")) + +(provide 'modes/python) +;;; python.el ends here diff --git a/lisp/modes/shell.el b/lisp/modes/shell.el new file mode 100644 index 0000000..fe6e389 --- /dev/null +++ b/lisp/modes/shell.el @@ -0,0 +1,74 @@ +;;; shell.el --- initializes shell modes -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(defun user--sh-mode-hook () + "Initialize mode for shell script editing." + (validate-setq + ;; Indent with four spaces. + sh-basic-offset 4 + sh-indentation 4)) + + +(defun user--shell-mode-common-hook () + "Shell mode common hook." + (with-feature 'ansi-color + ;; Enable ANSI colors for comint. + (ansi-color-for-comint-mode-on)) + + (with-feature 'shelldoc + (shelldoc-minor-mode-on))) + + +(defun user--shell-mode-hook () + "Initialize mode for interactive shell." + (user--shell-mode-common-hook) + + (validate-setq + ;; Set up to use Bash with input echoing. + explicit-shell-file-name "bash" + explicit-bash-args '("-c" "export EMACS=; stty echo; bash") + comint-process-echoes t)) + +(use-package shell + :defer + :hook ((sh-mode-hook . user--sh-mode-hook) + (shell-mode-hook . user--shell-mode-hook)) + :config + ;;; (Packages) ;;; + ;; https://github.com/wilkystyle/lsp-sh + ;; Bash support for lsp-mode using Mads Hartmann's bash-language-server + (use-package lsp-sh + :if (executable-find "bash-language-server") + :hook (sh-mode-hook . lsp-sh-enable)) + ;; https://github.com/szermatt/emacs-bash-completion + ;; Add programmable bash completion to Emacs shell-mode + (use-package bash-completion) + ;; https://github.com/Alexander-Miller/company-shell + ;; Company mode completion backends for your shell scripting + (use-package company-shell + :after (company) + :config + (add-to-list 'company-backends '(company-shell company-shell-env))) + ;; https://github.com/charlesdaniels/shelldoc + ;; A tool for generating ReST documentation from shell scripts + (use-package shelldoc + :disabled) + ;; (use-package shell-command) + ;; https://github.com/cuonglm/flycheck-checkbashisms + ;; Flycheck linter for checkbashisms + (use-package flycheck-checkbashisms + :if (executable-find "checkbashisms") + :config + (flycheck-checkbashisms-setup)) + ;; https://github.com/alexmurray/flycheck-bashate + ;; Integrate bashate with flycheck to automatically check the style of your bash scripts on the fly. + (use-package flycheck-bashate + :disabled + :if (executable-find "bashate") + :config + (flycheck-bashate-setup))) + + +(provide 'modes/shell) +;;; shell.el ends here diff --git a/lisp/modes/text.el b/lisp/modes/text.el new file mode 100644 index 0000000..8495fd6 --- /dev/null +++ b/lisp/modes/text.el @@ -0,0 +1,69 @@ +;;; text.el --- text mode support -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(defun user--text-mode-hook () + "Text mode hook." + (user--fundamental-mode-hook) + + (user/smartparens-enable) + + (rainbow-delimiters-mode-disable) + + (validate-setq + ;; Colons are followed by two spaces. + colon-double-space t + ;; Sentences end after two spaces. + sentence-end-double-space t + ;; When using fill-paragraph or auto-fill-mode break lines at 120 characters + ;; by default. + fill-column 120 + ;; Indent using spaces. + indent-tabs-mode nil) + + (when (user-flyspell-p) + ;; Protect against missing dictionary. + (try-eval + ;; Run spell-checker in programming mode. + (flyspell-prog-mode))) + + (when (and (feature-p 'flycheck-vale) + (executable-find "vale")) + (flycheck-vale-setup)) + + ;; TODO + ;; (with-feature 'pandoc-mode + ;; (pandoc-mode t)) + + ;; Nicely wrap long lines. + (visual-line-mode t) + + ;;; (Bindings) ;;; + (user/bind-key-local :code :fill-paragraph 'fill-paragraph)) + +(use-package text-mode + :ensure nil + :defer + :hook (text-mode-hook . user--text-mode-hook) + :config + ;; https://github.com/abingham/flycheck-vale + ;; Flycheck integration for the vale natural language linter + (use-package flycheck-vale + :if (executable-find "vale") + :config + ;; TODO + ;; (add-to-list 'flycheck-vale-modes 'org-mode) + ) + + ;; https://github.com/zzkt/smog + ;; Analyse the writing style, word use and readability of prose in Emacs. + (use-package smog + :if (executable-find "style")) + + ;; https://github.com/emacs-straight/ftable + ;; This package provides some convenient commands for filling a table. + (use-package ftable)) + + +(provide 'modes/text) +;;; text.el ends here diff --git a/lisp/modes/typescript.el b/lisp/modes/typescript.el new file mode 100644 index 0000000..9549881 --- /dev/null +++ b/lisp/modes/typescript.el @@ -0,0 +1,40 @@ +;;; typescript.el --- Initializes TypeScript mode -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(defun typescript-use-eslint-from-node-modules () + "Use local eslint from node_modules before global." + (let* ((root (locate-dominating-file + (or (buffer-file-name) default-directory) + "node_modules")) + (eslint (and root + (expand-file-name "node_modules/eslint/bin/eslint.js" + root)))) + (when (and eslint (file-executable-p eslint)) + (setq-local flycheck-javascript-eslint-executable eslint)))) + +(defun user--tide-mode-hook () + "." + (interactive) + (message "user--tide-mode-hook BEG") + (comment-tags-mode t) + (tide-setup) + (flycheck-mode +1) + (setq flycheck-check-syntax-automatically '(save mode-enabled)) + (eldoc-mode +1) + (company-mode +1) + (typescript-use-eslint-from-node-modules) + (message "user--tide-mode-hook END") + ) + +(use-package tide + :ensure t + :after (typescript-mode company flycheck) + :hook ( + (typescript-mode . user--tide-mode-hook) + (typescript-mode . tide-hl-identifier-mode) + (before-save . tide-format-before-save)) + :config (setq tide-completion-enable-autoimport-suggestions t)) + +(provide 'modes/typescript) +;; typescript.el ends here diff --git a/lisp/modes/vuejs.el b/lisp/modes/vuejs.el new file mode 100644 index 0000000..b960e4f --- /dev/null +++ b/lisp/modes/vuejs.el @@ -0,0 +1,51 @@ +;;; markdown --- initializes VueJs modes -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(defun user--web-html-hook () + "." + (message "user--web-html-hook BEG !!!") + (message "user--web-html-hook END !!!") + ) + +(defun user--web-vue-hook () + "." + (message "user--web-vue-hook BEG !!!!") + (user--tide-mode-hook) + (flycheck-add-mode 'javascript-eslint 'web-mode) + (flycheck-select-checker 'javascript-eslint) + (add-to-list (make-local-variable 'company-backends) + '(comany-tide company-web-html company-css company-files)) + (message "user--web-vue-hook END !!!!") + ) + +(defun user--web-format-before-save() + "Python cleanup and format before save buffer." + (delete-trailing-whitespace) +) + +(use-package web-mode + :ensure t + :mode ("\\.html\\'" "\\.vue\\'") + :hook ((web-mode-hook . (lambda() + (cond ((equal web-mode-content-type "html") + (user--web-html-hook)) + ((member web-mode-content-type '("vue")) + (user--web-vue-hook)) + ))) + (web-mode-hook . (lambda () + (add-hook 'before-save-hook #'user--web-format-before-save nil t)))) + :config + (setq web-mode-markup-indent-offset 4) + (setq web-mode-css-indent-offset 4) + (setq web-mode-code-indent-offset 4) + (setq web-mode-enable-current-element-highlight t) + (setq web-mode-enable-css-colorization t) + (setq web-mode-content-types-alist '(("vue" . "\\.vue\\'"))) + ;; + ;; + (use-package company-web + :ensure t)) + +(provide 'modes/vuejs) +;; vuejs.el ends here diff --git a/lisp/modes/whitespace.el b/lisp/modes/whitespace.el new file mode 100644 index 0000000..9da63a0 --- /dev/null +++ b/lisp/modes/whitespace.el @@ -0,0 +1,97 @@ +;;; whitespace.el --- whitespace mode support -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(defvar user/whitespace-mode-suppressed nil + "Tracks whether `whitespace-mode' is currently being suppressed.") +(make-variable-buffer-local 'user/prev-whitespace-mode-suppressed) + + +(defun user--whitespace-mode-hook () + "Whitespace mode hook." + ;;; (Bindings) ;;; + (user/bind-key-local :code :whitespace-auto-cleanup + 'user/toggle-whitespace-auto-cleanup)) + + +(defun user/whitespace-mode-suppress (suppress) + "If SUPPRESS is non-nil, disable `whitespace-mode' in current mode." + (when (boundp 'whitespace-mode) + (if suppress + (when (and whitespace-mode (not user/whitespace-mode-suppressed)) + (setq user/whitespace-mode-suppressed t) + (whitespace-mode -1)) + (when user/whitespace-mode-suppressed + (setq user/whitespace-mode-suppressed nil) + (whitespace-mode 1))))) + + +(defun user/whitespace-disable-style (styles) + "Disable STYLES in current mode." + (when (boundp 'whitespace-style) + (let ((options (cl-intersection styles whitespace-style))) + (when (and (boundp 'global-whitespace-mode) global-whitespace-mode) + (global-whitespace-toggle-options options)) + (when (and (boundp 'whitespace-mode) whitespace-mode) + (whitespace-toggle-options options))))) + + +(defun user/toggle-whitespace-auto-cleanup () + "Toggle the state of automatic whitespace cleanup in current buffer." + (interactive) + (setq whitespace-action (user/toggle-element whitespace-action 'auto-cleanup)) + (message + (concat "Whitespace cleanup " + (if (member 'auto-cleanup whitespace-action) "enabled" "disabled")))) + +;; https://github.com/emacs-mirror/emacs/blob/master/lisp/whitespace.el +;; minor mode to visualize TAB, (HARD) SPACE, NEWLINE +(use-package whitespace + :diminish whitespace-mode + :init + (add-hook 'whitespace-mode-hook 'user--whitespace-mode-hook) + :config + (validate-setq + ;; Maximum allowed line length. + whitespace-line-column 120 + ;; Cleanup whitespace errors on save. + whitespace-action '(auto-cleanup) + ;; Kinds of whitespace to visualize. + whitespace-style + '(;; Visualize using faces. + face + ;; Mark any tabs. + tab-mark + ;; Empty lines at beginning or end of buffer. + empty + ;; Trailing whitespace. + trailing + ;; Lines that extend beyond `whitespace-line-column.' + lines + ;; Wrong kind of indentation (tab when spaces and vice versa.) + indentation + ;; Mixture of space and tab on the same line. + space-before-tab space-after-tab)) + + (when (eq default-terminal-coding-system 'utf-8) + ;; Don't use validate-setq due to :inline not being supported. + (setq + ;; Special characters for displaying whitespace. + whitespace-display-mappings + '(;; 32 SPACE, 183 MIDDLE DOT 「·」, 46 FULL STOP 「.」 + (space-mark ?\s [183] [46]) + ;; 10 LINE FEED, 9166 RETURN SYMBOL 「⏎」, 36 DOLLAR SIGN 「$」 + (newline-mark ?\n [9166 10] [36 10]) + ;; 9 TAB, 9655 WHITE RIGHT-POINTING TRIANGLE 「▷」, 92 9 CHARACTER TABULATION 「\t」 + (tab-mark ?\t [9655 9] [92 9])))) + + (defadvice whitespace-cleanup (around whitespace-cleanup-indent-tab + activate) + "Fix whitespace-cleanup indent-tabs-mode bug." + (let ((whitespace-indent-tabs-mode indent-tabs-mode) + (whitespace-tab-width tab-width)) + ad-do-it))) + + +(provide 'modes/whitespace) +;;; whitespace.el ends here diff --git a/lisp/utilities/abbrev.el b/lisp/utilities/abbrev.el new file mode 100644 index 0000000..5519e48 --- /dev/null +++ b/lisp/utilities/abbrev.el @@ -0,0 +1,16 @@ +;;; abbrev.el --- Configure Emacs abbreviations -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: +;;; Doc: https://www.emacswiki.org/emacs/AbbrevMode + +(use-package abbrev + :disabled + :ensure nil + :diminish abbrev-mode + :config + (validate-setq + abbrev-file-name (path-join *user-data-directory* "abbrev_defs"))) + + +(provide 'utilities/abbrev) +;;; abbrev.el ends here diff --git a/lisp/utilities/ag.el b/lisp/utilities/ag.el new file mode 100644 index 0000000..a068649 --- /dev/null +++ b/lisp/utilities/ag.el @@ -0,0 +1,32 @@ +;;; ag.el --- interface to The Silver Searcher -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: https://github.com/emacsorphanage/helm-ag + +(with-executable 'ag + (when (feature-p 'helm) + (use-package helm-ag + :defer + :config + (validate-setq + ;; Insert word at point as search term. + helm-ag-insert-at-point 'word))) + (use-package ag + :defer + :init + (if (and (feature-p 'projectile) + (fboundp 'projectile-ag)) + (global-set-key [remap find-grep] 'projectile-ag) + (global-set-key [remap find-grep] 'ag)) + :config + (validate-setq + ag-project-root-function + '(lambda () + (with-project project (path-buffer-abs) + (user/proj-root project)))) + + ;; Search inside compressed files. + (add-to-list 'ag-arguments "--search-zip"))) + + +(provide 'utilities/ag) +;;; ag.el ends here diff --git a/lisp/utilities/alert.el b/lisp/utilities/alert.el new file mode 100644 index 0000000..b6b1b86 --- /dev/null +++ b/lisp/utilities/alert.el @@ -0,0 +1,26 @@ +;;; alert.el --- Emacs notifications. -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: https://github.com/jwiegley/alert + +(defun user/alert-style () + "Get the preferred alert style." + (cond + ((eq system-type 'darwin) 'growl) + ((executable-find "notify-send") 'libnotify) + ((executable-find "dbus-send") 'notifications) + (t 'mode-line))) + +(use-package alert + :defer + :config + ;; Undiagnosed issue with validate-setq. + (setq + ;; Send alerts to alert buffer. + alert-default-style (user/alert-style)) + (validate-setq + ;; Disable log. + alert-log-messages nil)) + + +(provide 'utilities/alert) +;;; alert.el ends here diff --git a/lisp/utilities/auth-source.el b/lisp/utilities/auth-source.el new file mode 100644 index 0000000..22ed444 --- /dev/null +++ b/lisp/utilities/auth-source.el @@ -0,0 +1,28 @@ +;;; auth-source.el --- Configure Emacs authentication sources -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: https://github.com/emacs-mirror/emacs/blob/master/lisp/auth-source.el +;;; Doc: https://www.gnu.org/software/emacs/manual/html_mono/auth.html + +(use-package auth-source + :disabled + :ensure nil + :defer + :config + (validate-setq + auth-sources + `(,(path-join *user-data-directory* "authinfo.gpg") + + ,(path-join *user-data-directory* "authinfo"))) + + (dolist (source auth-sources) + (when (file-exists-p source) + (set-file-modes source #o0600))) + + (when (eq system-type 'darwin) + (add-many-to-list 'auth-sources + 'macos-keychain-internet + 'macos-keychain-generic))) + + +(provide 'utilities/auth-source) +;;; auth-source.el ends here diff --git a/lisp/utilities/bookmarks.el b/lisp/utilities/bookmarks.el new file mode 100644 index 0000000..3a8ab65 --- /dev/null +++ b/lisp/utilities/bookmarks.el @@ -0,0 +1,98 @@ +;;; bookmarks.el --- Bookmarks in Emacs -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: +;;; Doc: https://www.emacswiki.org/emacs/VisibleBookmarks + +(defconst *visible-bookmarks-data-file* (path-join *user-data-directory* + "visible-bookmarks")) +(defconst *bookmark+-data-file* (path-join *user-data-directory* "bookmarks")) +(defconst *bookmark+-menu-state-file* (path-join *user-cache-directory* + "bookmark-menu-state.el")) + +(defun bookmark--local-directory-bookmarks-to-zsh () + "Store Emacs bookmarks in ZSH bookmarks file." + (interactive) + (when (and (require 'tramp nil t) + (require 'bookmark nil t)) + (set-buffer (find-file-noselect "~/.zsh_bookmarks" t t)) + (delete-region (point-min) (point-max)) + (insert "# -*- mode:sh -*-\n") + (let (collect-names) + (mapc (lambda (item) + (let ((name (replace-regexp-in-string "-" "_" (car item))) + (file (cdr (assoc 'filename + (if (cddr item) item (cadr item)))))) + (when (and (not (tramp-tramp-file-p file)) + (file-directory-p file)) + (setq collect-names (cons (concat "~" name) collect-names)) + (insert (format "%s=\"%s\"\n" name (expand-file-name file)))))) + bookmark-alist) + (insert ": " (mapconcat 'identity collect-names " ") "\n")) + (let ((backup-inhibited t)) (save-buffer)) + (kill-buffer (current-buffer)))) + + +(use-package bm + :defer + :init + (user/bind-key-global :code :bookmark-toggle 'bm-toggle) + (user/bind-key-global :code :bookmark-next 'bm-next) + (user/bind-key-global :code :bookmark-prev 'bm-previous) + :config + (validate-setq + ;; Persistent bookmarks. + bm-repository-file *visible-bookmarks-data-file* + bm-buffer-persistence t) + + ;; Restore bookmarks on file find. + (add-hook 'find-file-hooks 'bm-buffer-restore) + ;; Save bookmarks when killing buffer. + (add-hook 'kill-buffer-hook 'bm-buffer-save) + ;; Save all bookmarks on exit. + (add-hook 'kill-emacs-hook 'bm-save) + ;; Update repository when saving file. + (add-hook 'after-save-hook 'bm-buffer-save) + ;; Restore bookmarks when buffer is reverted. + (add-hook 'after-revert-hook 'bm-buffer-restore)) + +(use-package bookmark + :disabled + :defer + :init + ;; Bind bookmarks to C-c b + (global-set-key (user/get-key :code :bookmark-prefix) 'bookmark-map) + :config + (validate-setq + ;; Enable versioned backups. + bookmark-version-control t + bookmark-save-flag 1 + ;; Put the repository in the data directory. + bookmark-default-file *bookmark+-data-file*) + + ;; Share Emacs directory bookmarks with ZSH. + (defadvice bookmark-write-file + (after local-directory-bookmarks-to-zsh-advice activate) + (bookmark--local-directory-bookmarks-to-zsh)) + + (use-package bookmark+ + :disabled + :config + (validate-setq + ;; Save bookmarks after ten updates. + bmkp-count-multi-mods-as-one-flag t) + + ;;; (Bindings) ;;; + (define-key bookmark-map (kbd "l") 'bookmark-jump) + (define-key bookmark-map (kbd "e") 'bmkp-edit-bookmark-record) + (define-key bookmark-map (kbd "t") 'bmkp-add-tags) + + (use-package bookmark+-bmu + :ensure bookmark+ + :config + (validate-setq + ;; Put the menu state in the cache directory. + bmkp-bmenu-state-file *bookmark+-menu-state-file*)))) + + +(provide 'utilities/bookmarks) +;;; bookmarks.el ends here diff --git a/lisp/utilities/compile.el b/lisp/utilities/compile.el new file mode 100644 index 0000000..ed78a0d --- /dev/null +++ b/lisp/utilities/compile.el @@ -0,0 +1,74 @@ +;;; compile.el --- sets up Emacs compile support -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(defun user--compilation-mode-hook () + "Compilation mode hook.") + + +(defun user--compilation-filter-hook () + "Hook for filtering compilation output." + ;; Temporarily make buffer writable. + (let ((inhibit-read-only t)) + ;; Colorize compilation output. + (ansi-color-apply-on-region (point-min) (point-max)))) + + +(defun user/compile () + "Compile current context." + (interactive) + (let ((ede-proj (user/proj-from-path user/ede-proj (path-abs-buffer)))) + (cond + (ede-proj (user/proj-build ede-proj)) + ((feature-p 'flex-compile) (call-interactively 'flex-compile-compile)) + ((fboundp 'mode-compile) (call-interactively 'mode-compile)) + (t (call-interactively 'compile))))) + +;; https://www.emacswiki.org/emacs/CompileCommand +(use-package compile + :defer + :init + (user/bind-key-global :code :compile 'user/compile) + :hook ((compilation-mode-hook . user--compilation-mode-hook) + (compilation-filter-hook . user--compilation-filter-hook)) + :config + (validate-setq + ;; Prevent input in compilation buffer. + compilation-disable-input nil + ;; Automatically scroll output. + compilation-scroll-output t) + + (with-eval-after-load 'popwin + (add-to-list + 'popwin:special-display-config + ;; Don't select compilation window when shown + '(compilation-mode :height 20 :dedicated t))) + + ;; https://github.com/plandes/flex-compile + (use-package flex-compile + :disabled + :pin "MELPA") + + ;; https://www.gnu.org/software/emacs/manual/html_node/emacs/Compilation-Mode.html + (use-package mode-compile + :defer + :config + ;; Ensure byte-run has been loaded or mode-compile will override + ;; `define-obsolete-variable-alias'. + (when (load "byte-run.el" nil :noerror) + (validate-setq + ;; Set a sane compilation frame name. + mode-compile-other-frame-name "*compilation*" + ;; Run make with low priority and use multiple processes. + mode-compile-make-program "nice make" + mode-compile-default-make-options "-k -j" + ;; Save the current buffer on compilation. + mode-compile-always-save-buffer-p t) + + (with-executable 'clang + (add-to-list 'cc-compilers-list "clang") + (add-to-list 'c++-compilers-list "clang++"))))) + + +(provide 'utilities/compile) +;;; compile.el ends here diff --git a/lisp/utilities/coverage.el b/lisp/utilities/coverage.el new file mode 100644 index 0000000..4c841fc --- /dev/null +++ b/lisp/utilities/coverage.el @@ -0,0 +1,11 @@ +;;; coverage.el --- Code coverage tools -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: https://github.com/AdamNiederer/cov + +;; TODO: Update to use cov for all modes +;; TODO: Add pycoverage management +(use-package cov + :defer) + +(provide 'utilities/coverage) +;;; coverage.el ends here diff --git a/lisp/utilities/ctable.el b/lisp/utilities/ctable.el new file mode 100644 index 0000000..a8a8153 --- /dev/null +++ b/lisp/utilities/ctable.el @@ -0,0 +1,13 @@ +;;; ctable --- Table component for Emacs -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: https://github.com/kiwanami/emacs-ctable + +(use-package ctable + :disabled + :defer + :init + (autoload 'make-ctbl:cmodel "ctable")) + + +(provide 'utilities/ctable) +;;; ctable.el ends here diff --git a/lisp/utilities/dap.el b/lisp/utilities/dap.el new file mode 100644 index 0000000..6316809 --- /dev/null +++ b/lisp/utilities/dap.el @@ -0,0 +1,26 @@ +;;; dap.el --- Debug adapter protocol. -*- lexical-binding: t; -*- +;;; Commentary: Emacs client/library for Debug Adapter Protocol is a wire protocol for communication between client and Debug Server. +;;; Code: https://github.com/emacs-lsp/dap-mode + +(use-package dap-mode + :disabled + :pin "MELPA" + :hook ((lsp-mode-hook . dap-mode) + (lsp-mode-hook . dap-ui-mode)) + :bind-wrap + (:map lsp-mode-map + ((:key :debug :break) . dap-breakpoint-toggle) + ((:key :debug :step) . dap-step-in) + ((:key :debug :next) . dap-next) + ((:key :debug :run) . dap-debug) + ((:key :debug :continue) . dap-continue) + + ((:key :debug :show-value) . dap-ui-inspect-thing-at-point)) + :config + (validate-setq + ;; Location of persistent breakpoints. + dap-breakpoints-file (path-join *user-cache-directory* "dap-breakpoints"))) + + +(provide 'utilities/dap) +;;; dap.el ends here diff --git a/lisp/utilities/dash.el b/lisp/utilities/dash.el new file mode 100644 index 0000000..2270e45 --- /dev/null +++ b/lisp/utilities/dash.el @@ -0,0 +1,12 @@ +;;; dash.el --- Dash documentation reading -*- lexical-binding: t; -*- +;;; Commentary: This package provides an elisp interface to query and show documenation using Dash docsets. +;;; Code: https://github.com/dash-docs-el/dash-docs + +(use-package dash-docs + :if (executable-find "sqlite3") + :bind-wrap + ((:key :doc :reference) . helm-dash-at-point)) + + +(provide 'utilities/dash) +;;; dash.el ends here diff --git a/lisp/utilities/dired.el b/lisp/utilities/dired.el new file mode 100644 index 0000000..e49ffed --- /dev/null +++ b/lisp/utilities/dired.el @@ -0,0 +1,88 @@ +;;; dired.el --- Configuration for dired -*- lexical-binding: t; -*- +;;; Commentary: Dired is the main mode for Emacs file-manager operations. The name “Dired” stands for “directory editor”. +;;; Code: +;;; Docs: https://www.emacswiki.org/emacs/DiredMode + +(defun user--dired-mode-hook () + "Mode hook for dired." + (with-feature 'async + ;; Asynchronous operations in dired. + (dired-async-mode t))) + +(use-package dired + :ensure nil + :defer + :config + (validate-setq + ;; Always copy recursively without asking. + dired-recursive-copies 'always + ;; Ask once when recursively deleting a directory. + dired-recursive-deletes 'top + ;; Allow dired to be smart about operations. + dired-dwim-target t + ;; Default flags for ls. + dired-listing-switches "-alh") + + ;;; (Packages) ;;; + ;; https://github.com/purcell/diredfl + ;; Extra font lock rules for a more colourful dired + (use-package diredfl + :disabled) + ;; https://github.com/emacsorphanage/dired-k + ;; highlights dired buffer like k. + (use-package dired-k + :disabled) + ;; https://github.com/clemera/dired-git-info + ;; This Emacs packages provides a minor mode which shows git information inside the dired buffer + (use-package dired-git-info + :disabled + :bind (:map dired-mode-map + (")" . dired-git-info-mode))) + ;; https://github.com/jwiegley/emacs-async + ;; async.el is a module for doing asynchronous processing in Emacs. + (use-package async) + ;; https://github.com/juan-leon/dired-efap + ;; dired-efap.el allows the user to edit the filename at point, by hitting a key (like f2) or double-clicking it. + (use-package dired-efap + :config + ;;; (Bindings) ;;; + (define-key dired-mode-map [R] 'dired-efap) + (when (display-graphic-p) + (define-key dired-mode-map [down-mouse-1] 'dired-efap-click)) + (with-eval-after-load 'dired + ;; Load dired-efap when dired is loaded. + (require 'dired-efap))) + ;; https://github.com/jtbm37/all-the-icons-dired + ;; This adds dired support to all-the-icons. + (use-package all-the-icons-dired + :if window-system) + ;; https://github.com/stsquad/dired-rsync + ;; This package adds a single command dired-rsync which allows the user to copy marked files in a dired buffer via rsync. + (use-package dired-rsync + :disabled + :if (executable-find "rsync") + :bind (:map dired-mode-map + ("C-c C-r" . dired-rsync))) + ;; https://github.com/vifon/dired-recent.el + ;; A history of paths visited with Emacs dired. + (use-package dired-recent + :disabled + :config + (validate-setq + ;; Path to history database. + dired-recent-directories-file (path-join *user-cache-directory* "dired-history")) + + (dired-recent-mode t)) + + ;;; (Bindings) ;;; + ;; Do not open new buffers when going down or up a directory. + (define-key dired-mode-map (kbd "") 'dired-find-alternate-file) + (define-key dired-mode-map (kbd "^") (lambda () + (interactive) + (find-alternate-file ".."))) + (when (display-graphic-p) + (define-key dired-mode-map [double-mouse-1] 'dired-find-file))) + + +(provide 'utilities/dired) +;;; dired.el ends here diff --git a/lisp/utilities/direx.el b/lisp/utilities/direx.el new file mode 100644 index 0000000..72013a3 --- /dev/null +++ b/lisp/utilities/direx.el @@ -0,0 +1,24 @@ +;;; direx.el --- a simple directory explorer -*- lexical-binding: t; -*- +;;; Commentary: direx.el is a simple directory explorer. It also works as a generic tree explore library. +;;; Code: https://github.com/emacsorphanage/direx + +(use-package direx + :defer + :init + (user/bind-key-global :basic :open-file-context + 'direx-project:jump-to-project-root-other-window) + :config + (validate-setq + ;; Use the functionality from utilities/project to locate project root + direx-project:project-root-predicate-functions + '((lambda (path) + (with-project-root project-root path + (and path (equal (file-truename path) (file-truename project-root))))))) + + (with-eval-after-load 'popwin + (push '(direx:direx-mode :position left :width 30 :dedicated t) + popwin:special-display-config))) + + +(provide 'utilities/direx) +;;; direx.el ends here diff --git a/lisp/utilities/docker.el b/lisp/utilities/docker.el new file mode 100644 index 0000000..40f1ce4 --- /dev/null +++ b/lisp/utilities/docker.el @@ -0,0 +1,17 @@ +;;; docker.el --- Configure Emacs docker support -*- lexical-binding: t; -*- +;;; Commentary : Emacs integration for Docker! +;;; Code: https://github.com/Silex/docker.el + +(use-package docker + :disabled + :if (executable-find "docker") + :diminish docker-mode + :bind-wrap ((:key :util :docker) . docker) + :config + ;; https://github.com/emacs-pe/docker-tramp.el + ;; docker-tramp.el offers a TRAMP method for Docker containers. + (use-package docker-tramp)) + + +(provide 'utilities/docker) +;;; docker.el ends here diff --git a/lisp/utilities/ecb.el b/lisp/utilities/ecb.el new file mode 100644 index 0000000..8b91bde --- /dev/null +++ b/lisp/utilities/ecb.el @@ -0,0 +1,92 @@ +;;; ecb --- Emacs Code Browser -*- lexical-binding: t; -*- +;;; Commentary: Emacs Code Browser +;;; Code: https://github.com/ecb-home/ecb + +(defvar user/ecb-active nil "Current state of ECB.") + + +(defun user--ecb-activate-hook () + "ECB activation hook." + (setq user/ecb-active t) + + ;; Popwin conflicts with ECB. + (popwin-mode -1) + + ;; Close compile window if open + (when (ecb-compile-window-live-p) + (ecb-toggle-compile-window)) + + ;;; (Bindings) ;;; + (user/bind-key-local :basic :zoom 'ecb-toggle-ecb-windows) + + (user/bind-key-local :nav :context 'ecb-goto-window-edit1) + (user/bind-key-local :basic :open-file-context 'ecb-goto-window-directories) + (user/bind-key-local :nav :history 'ecb-goto-window-history) + (user/bind-key-local :nav :functions/toc 'ecb-goto-window-methods) + (user/bind-key-local :code :compilation-result 'user/ecb-toggle-compile-window)) + + +(defun user--ecb-deactivate-hook () + "ECB deactivation hook." + (setq user/ecb-active nil) + (popwin-mode t)) + + +(defun user/ecb-toggle-active () + "Toggle ECB state." + (interactive) + (if user/ecb-active + (ecb-deactivate) + (ecb-activate))) + + +(defun user/ecb-toggle-compile-window () + "Toggle the presence of the ECB compilation window." + (interactive) + (if (and (ecb-compile-window-live-p) + (not (ecb-point-in-compile-window))) + (ecb-goto-window-compilation) + (progn + (ecb-toggle-compile-window) + (when (ecb-compile-window-live-p) + (ecb-goto-window-compilation))))) + +(when (version<= emacs-version "24.4") + (use-package ecb + :defer + :init + (add-hook 'ecb-activate-hook 'user--ecb-activate-hook) + (add-hook 'ecb-deactivate-hook 'user--ecb-deactivate-hook) + (user/bind-key-global :util :ecb-toggle 'user/ecb-toggle-active) + :config + ;; ECB version checking code is very old so that it thinks that the latest + ;; CEDET/Emacs is not new enough when in fact it is years newer than the + ;; latest version that it is aware of. So simply bypass the version check. + (validate-setq + ecb-version-check nil + ecb-tip-of-the-day nil + ;; ECB layout. + ecb-layout-name "left6" + ecb-layout-window-sizes '(("left6" + (ecb-directories-buffer-name 0.17 . 0.41) + (ecb-sources-buffer-name 0.17 . 0.21) + (ecb-methods-buffer-name 0.17 . 0.41) + (ecb-history-buffer-name 0.17 . 0.35))) + ecb-show-sources-in-directories-buffer 'always + ecb-compile-window-height 12) + + (defadvice ecb-check-requirements (around no-version-check activate compile) + "AROUND NO-VERSION-CHECK ACTIVATE COMPILE" + (if (or (< emacs-major-version 23) + (and (= emacs-major-version 23) + (< emacs-minor-version 3))) + ad-do-it)) + + (when (display-graphic-p) + (with-eval-after-load 'ecb-face + ;; Use a slightly smaller face for the ECB tree-buffers. + (set-face-attribute 'ecb-default-general-face nil :height 0.8))))) + + +(provide 'utilities/ecb) +;;; ecb.el ends here diff --git a/lisp/utilities/ediff.el b/lisp/utilities/ediff.el new file mode 100644 index 0000000..861534c --- /dev/null +++ b/lisp/utilities/ediff.el @@ -0,0 +1,85 @@ +;;; ediff.el --- Configuration for ediff -*- lexical-binding: t; -*- +;;; Commentary: A comprehensive visual interface to Unix diff and patch utilities +;;; Code: +;;; Doc: https://www.gnu.org/software/emacs/manual/html_mono/ediff.html + +(eval-when-compile + (require 'cl)) + + +(defun user--ediff-mode-hook () + "Ediff mode hook." + (setq + ;; Don't wrap long lines. + truncate-lines t)) + + +(defun user--ediff-startup-hook () + "Ediff startup hook." + (validate-setq + ;; Split window differently depending on frame width. + ediff-split-window-function (if (> (frame-width) (* 2 80)) + 'split-window-horizontally + 'split-window-vertically)) + + ;; Go to the first difference on startup. + (ediff-next-difference)) + + +(defun user/ediff-mergetool () + "Launch ediff as mergetool." + (defun ediff-write-merge-buffer () + "Write merge buffer to file." + (let ((file ediff-merge-store-file)) + (set-buffer ediff-buffer-C) + (write-region (point-min) (point-max) file) + (message "Merge buffer saved in: %s" file) + (set-buffer-modified-p nil) + (sit-for 1))) + + (validate-setq + ediff-quit-hook 'kill-emacs + ediff-quit-merge-hook 'ediff-write-merge-buffer) + + (let ((local (pop command-line-args-left)) + (remote (pop command-line-args-left)) + (base (pop command-line-args-left)) + (merged (pop command-line-args-left))) + (lexical-let (;; Show only conflicts. + (ediff-show-clashes-only t)) + (ediff-merge-files-with-ancestor local remote base nil merged)))) + + +(defun user/ediff-difftool () + "Launch ediff as difftool." + (let ((local (pop command-line-args-left)) + (remote (pop command-line-args-left))) + (ediff local remote))) + +(use-package ediff + :defer + :init + ;; Go to first difference on start. + (add-hook 'ediff-startup-hook 'user--ediff-startup-hook) + :config + (validate-setq + ;; Ignore changes in whitespace. + ediff-diff-options "-w" + ediff-ignore-similar-regions t) + + (user/bind-key-global :util :diff 'ediff) + + ;; window manipulation utilities + (use-package ediff-wind + :ensure nil + :config + (validate-setq + ;; Don't create a separate frame for ediff. + ediff-window-setup-function 'ediff-setup-windows-plain)) + ;; https://github.com/fourier/ztree + ;; Ztree is a project dedicated to implementation of several text-tree applications + (use-package ztree)) + + +(provide 'utilities/ediff) +;;; ediff.el ends here diff --git a/lisp/utilities/eldoc.el b/lisp/utilities/eldoc.el new file mode 100644 index 0000000..59161a5 --- /dev/null +++ b/lisp/utilities/eldoc.el @@ -0,0 +1,14 @@ +;;; eldoc.el --- Show docstrings in echo area -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: https://github.com/emacs-mirror/emacs/blob/master/lisp/emacs-lisp/eldoc.el + +(use-package eldoc + :diminish eldoc-mode + :config + (use-package eldoc-eval + :config + (eldoc-in-minibuffer-mode t))) + + +(provide 'utilities/eldoc) +;;; eldoc.el ends here diff --git a/lisp/utilities/emacs-everywhere.el b/lisp/utilities/emacs-everywhere.el new file mode 100644 index 0000000..43f6769 --- /dev/null +++ b/lisp/utilities/emacs-everywhere.el @@ -0,0 +1,11 @@ +;;; emacs-everywhere.el --- Integrate Emacs with any application -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: https://github.com/tecosaur/emacs-everywhere + +(use-package emacs-everywhere + :disabled + :ensure t) + + +(provide 'utilities/emacs-everywhere) +;;; emacs-everywhere.el ends here diff --git a/lisp/utilities/emms.el b/lisp/utilities/emms.el new file mode 100644 index 0000000..c444f34 --- /dev/null +++ b/lisp/utilities/emms.el @@ -0,0 +1,15 @@ +;;; emms --- Emacs Multimedia System -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: https://www.gnu.org/software/emms/ + +(use-package emms + :commands (emms-streams) + :config + (emms-standard) + (emms-default-players) + (emms-mode-line nil) + (require 'emms-streams) + (setq emms-streams-file (path-join *user-data-directory* "streams.emms"))) + +(provide 'utilities/emms) +;;; emms.el ends here diff --git a/lisp/utilities/epc.el b/lisp/utilities/epc.el new file mode 100644 index 0000000..409e620 --- /dev/null +++ b/lisp/utilities/epc.el @@ -0,0 +1,11 @@ +;;; epc.el --- RPC stack for Emacs -*- lexical-binding: t; -*- +;;; Commentary: This program is an asynchronous RPC stack for Emacs +;;; Code: https://github.com/kiwanami/emacs-epc + +(use-package epc + :disabled + :defer) + + +(provide 'utilities/epc) +;;; epc.el ends here diff --git a/lisp/utilities/flycheck.el b/lisp/utilities/flycheck.el new file mode 100644 index 0000000..4eb0f96 --- /dev/null +++ b/lisp/utilities/flycheck.el @@ -0,0 +1,87 @@ +;;; flycheck.el --- flycheck configuration -*- lexical-binding: t; -*- +;;; Commentary: https://www.flycheck.org/en/latest/ +;;; Code: https://github.com/flycheck/flycheck + +(defun user--flycheck-mode-hook () + "Flycheck mode hook." + ;;; (Bindings) ;;; + (user/bind-key-local :code :warnings/errors 'helm-flycheck) + (user/bind-key-local :nav :next 'flycheck-next-error)) + + +(defun user/ede-flycheck-setup () + "Configure `flycheck' using `ede'." + (when (and ede-object + (slot-exists-p ede-object 'compilation) + (oref ede-object compilation)) + (let* ((comp (oref ede-object compilation)) + (cmd (get-command-line comp))) + (setq + flycheck-clang-includes (get-includes comp) + flycheck-clang-definitions (get-defines comp) + flycheck-clang-include-path (oref comp include-path-common)) + + (when (string-match " -std=\\([^ ]+\\)" cmd) + (setq + flycheck-clang-language-standard (match-string 1 cmd) + flycheck-gcc-language-standard (match-string 1 cmd))) + (when (string-match " -stdlib=\\([^ ]+\\)" cmd) + (setq flycheck-clang-standard-library (match-string 1 cmd))) + (when (string-match " -fms-extensions " cmd) + (setq flycheck-clang-ms-extensions t)) + (when (string-match " -fno-exceptions " cmd) + (setq + flycheck-clang-no-exceptions t + flycheck-gcc-no-exceptions t)) + (when (string-match " -fopenmp" cmd) + (setq flycheck-gcc-openmp t)) + (when (string-match " -fno-rtti " cmd) + (setq + flycheck-clang-no-rtti t + flycheck-gcc-no-rtti t)) + (when (string-match " -fblocks " cmd) + (setq flycheck-clang-blocks t))))) + + +(use-package flycheck + :commands global-flycheck-mode + :preface + (defvar-local flycheck-local-checkers nil) + (defun +flycheck-checker-get(fn checker property) + (or (alist-get property (alist-get checker flycheck-local-checkers)) + (funcall fn checker property))) + (advice-add 'flycheck-checker-get :around '+flycheck-checker-get) + :hook + ((flycheck-mode-hook . user--flycheck-mode-hook) + (ede-minor-mode-hook . user/ede-flycheck-setup) + (ede-compdb-project-rescan-hook . user/ede-flycheck-setup)) + :config + (validate-setq + ;; Wait five seconds before starting checker + flycheck-idle-change-delay 5.0) + + (with-eval-after-load 'popwin + ;; Use popwin for Flycheck error list. + (push '(flycheck-error-list-mode :stick t) popwin:special-display-config)) + + ;;; (Packages) ;;; + ;; https://github.com/flycheck/flycheck-pos-tip + ;; This Flycheck extension shows errors under point in pos-tip popups. + (use-package flycheck-pos-tip + :if window-system + :config + ;; Make sure flycheck-pos-tip is loaded. + (require 'flycheck-pos-tip nil t)) + ;; https://github.com/gexplorer/flycheck-indicator + ;; An Emacs minor-mode to get a fancy mode line indicator for Flycheck. + (use-package flycheck-indicator + :hook (flycheck-mode-hook . flycheck-indicator-mode)) + ;; https://github.com/yasuyk/helm-flycheck + ;; Show flycheck errors with helm. + (use-package helm-flycheck) + + (global-flycheck-mode t)) + + +(provide 'utilities/flycheck) +;;; flycheck.el ends here diff --git a/lisp/utilities/google-this.el b/lisp/utilities/google-this.el new file mode 100644 index 0000000..c96c75a --- /dev/null +++ b/lisp/utilities/google-this.el @@ -0,0 +1,15 @@ +;;; google-this.el --- Google item under point -*- lexical-binding: t; -*- +;;; Commentary: A set of emacs functions and bindings to google under point. +;;; Code: https://github.com/Malabarba/emacs-google-this + +(use-package google-this + :defer + :diminish google-this-mode + :init + (user/bind-key-global :util :google 'google-search) + (user/bind-key-global :util :google-at-point 'google-this) + (user/bind-key-global :util :google-selection 'google-region)) + + +(provide 'utilities/google-this) +;;; google-this.el ends here diff --git a/lisp/utilities/helm.el b/lisp/utilities/helm.el new file mode 100644 index 0000000..4a99556 --- /dev/null +++ b/lisp/utilities/helm.el @@ -0,0 +1,238 @@ +;;; helm.el --- improved Emacs control -*- lexical-binding: t; -*- +;;; Commentary: Helm is an Emacs framework for incremental completions and narrowing selections +;;; Code: https://github.com/emacs-helm/helm + +(defun user/helm-apropos () + "A context-aware helm apropos." + (interactive) + (let ((buffer-name "*helm-apropos*")) + (cond + ((derived-mode-p 'emacs-lisp-mode) (helm-apropos)) + ((derived-mode-p 'sh-mode) (helm-other-buffer + '(helm-source-man-pages + helm-source-info-pages) buffer-name)) + ((derived-mode-p 'c-mode-common) (helm-other-buffer + '(helm-source-man-pages) buffer-name)) + ((derived-mode-p 'python-mode) (helm-pydoc)) + (t (message (format "Apropos is unavailable for %S" major-mode)))))) + + +(defun user/helm-navigate () + "A context-aware helm navigation aid." + (interactive) + (cond + ((derived-mode-p 'prog-mode) (user/helm-navigate-prog)) + (t (user/helm-navigate-generic)))) + + +(defun user/helm-navigate-prog () + "A context-aware helm for programming modes." + (interactive) + (let ((helm-sources '(helm-source-buffers-list)) + (current-file (or (buffer-file-name) default-directory))) + (with-feature 'helm-misc + ;; FIXMEs. + (add-to-list 'helm-sources 'helm-source-fixme) + ;; Emacs lisp. + (add-to-list 'helm-sources 'helm-source-emacs-source-defun) + (add-to-list 'helm-sources 'helm-source-emacs-lisp-expectations) + (add-to-list 'helm-sources 'helm-source-emacs-lisp-toplevels)) + + (with-project project current-file + ;; Bookmarks. + (add-to-list 'helm-sources 'helm-source-bookmarks) + ;; Semantic. + (with-feature 'helm-semantic + (when (user/proj-from-path user/ede-proj current-file) + (add-to-list 'helm-sources 'helm-source-semantic)))) + + (helm-other-buffer helm-sources "*helm-navigate-prog*"))) + + +(defun user/helm-navigate-generic () + "A somewhat context-aware generic helm." + (interactive) + (condition-case nil + (let ((helm-sources '(helm-source-buffers-list + helm-source-recentf + helm-source-file-name-history + helm-source-file-cache + helm-source-buffer-not-found + helm-source-man-pages + helm-source-info-pages))) + (cond + ((eq system-type 'darwin) + (progn + (add-to-list 'helm-sources 'helm-source-mac-spotlight))) + ((eq system-type 'gnu/linux) + (progn + (add-to-list 'helm-sources 'helm-source-tracker-search)))) + (helm-other-buffer helm-sources "*helm-navigate-generic*")) + ;; Fall back to helm-mini if an error occurs in one of the sources. + (error (helm-mini)))) + + +(defun user/helm-mode () + "Start helm-mode." + (helm-mode t) + + (with-feature 'helm-descbinds + (helm-descbinds-mode t)) + + ;; Filter out boring buffers. + (dolist (pattern + (list "\\*clang-complete" "\\*CEDET global" "\\*tramp/scpc" + "\\*epc con" "\\*Pymacs" "\\*Completions\\*")) + (add-to-list 'helm-boring-buffer-regexp-list pattern)) + + ;; Filter out boring files. + (dolist (pattern + (list "\\.elc$" "\\.pyc$" "^#.+#$" "^G[R]TAGS$" "^GPATH$" "^ID$")) + (add-to-list 'helm-boring-file-regexp-list pattern))) + +(use-package helm + :diminish helm-mode + ;; :ensure + ;; Since Helm depends on `eieio', enable it after package initialization. + :hook (after-init-hook . user/helm-mode) + :init + (user/bind-key-global :nav :context 'user/helm-navigate) + (user/bind-key-global :doc :apropos 'user/helm-apropos) + (user/bind-key-global :emacs :elisp-search 'helm-info-elisp) + :config + (validate-setq + ;; Idle delays. + helm-input-idle-delay 0.0 + ;; Limit the number of candidates per source to a reasonable amount. + helm-candidate-number-limit 75) + + (with-eval-after-load 'popwin + (add-to-list + 'popwin:special-display-config + '("helm" :regexp t :height 0.4 :position bottom))) + + ;;; (Packages) ;;; + ;; https://github.com/emacs-helm/helm-descbinds + ;; Helm Descbinds provides an interface to emacs’ describe-bindings making the currently active key bindings interactively searchable with helm. + (use-package helm-descbinds + :defer) + ;; https://github.com/emacsorphanage/helm-swoop + ;; List match lines to another buffer, which is able to squeeze by any words you input + (use-package helm-swoop + :defer + :init + (user/bind-key-global :basic :swoop 'helm-swoop) + (user/bind-key-global :basic :swoop-multi 'helm-multi-swoop) + :config + (validate-setq + ;; Split window vertically when swooping. + helm-swoop-split-direction 'split-window-horizontally) + + ;;; (Bindings) ;;; + (define-key isearch-mode-map + (user/get-key :basic :swoop) 'helm-swoop-from-isearch) + (with-eval-after-load 'helm-swoop + ;; From helm-swoop to helm-multi-swoop-all. + (define-key helm-swoop-map + (user/get-key :basic :swoop) + 'helm-multi-swoop-all-from-helm-swoop))) + ;; https://github.com/emacs-helm/helm/blob/master/helm-adaptive.el + ;; Adaptive Sorting of Candidates + (use-package helm-adaptive + :ensure helm + :config + (validate-setq + ;; Put adaptive history in cache directory. + helm-adaptive-history-file (path-join *user-cache-directory* "helm-adaptive-history"))) + + (use-package helm-command + :ensure helm + :bind* ([remap execute-extended-command] . helm-M-x)) + + (use-package helm-files + :ensure helm + :bind* (([remap find-file] . helm-find-files) + :map helm-find-files-map + ("C-k" . helm-ff-persistent-delete)) + :config + ;; `helm-recentf-fuzzy-match' is set via Customize + ;; Reason: https://emacs.stackexchange.com/a/106/5514 + (validate-setq + helm-ff-file-name-history-use-recentf t + ;; Don't prompt for new buffer. + helm-ff-newfile-prompt-p nil + helm-input-idle-delay 0.1 + ;; Don't show boring files. + helm-ff-skip-boring-files t + ;; Search for library in `require' and `declare-function' sexp. + helm-ff-search-library-in-sexp t + ;; Auto-complete in find-files. + helm-ff-auto-update-initial-value t)) + + (use-package helm-misc + :ensure helm + :bind* ([remap switch-to-buffer] . helm-mini)) + + (use-package helm-buffers + :ensure helm + :bind (:map helm-buffer-map + ("C-k" . helm-buffer-run-kill-persistent)) + :config + (validate-setq + ;; Use fuzzy matching for buffer names. + helm-buffers-fuzzy-matching t + ;; Don't check if remote files exist. + helm-buffer-skip-remote-checking t)) + + (use-package helm-ring + :ensure helm + :bind* (([remap yank-pop] . helm-show-kill-ring) + ("C-c SPC" . helm-all-mark-rings))) + + (use-package helm-imenu + :ensure helm + :bind (("C-c n i" . helm-imenu-in-all-buffers) + ("C-c n t" . helm-imenu)) + :config + (validate-setq + helm-imenu-fuzzy-match t) + ;; Incompatible with validate-setq. + (setq + helm-imenu-execute-action-at-once-if-one nil)) + + (use-package helm-bookmark + :ensure helm + :defer + :bind ("C-x r l" . helm-filtered-bookmarks)) + + (use-package helm-pages + :ensure helm + :defer + :bind ("C-c n P" . helm-pages)) + + (use-package helm-eval + :ensure helm + :defer + :bind (("C-c h M-:" . helm-eval-expression-with-eldoc) + ("C-c h *" . helm-calcul-expression))) + + (use-package helm-external + :ensure helm + :defer + :bind ("C-c h x" . helm-run-external-command)) + + (use-package helm-build-command + :defer + :quelpa (helm-build-command + :fetcher github + :repo "tkf/helm-build-command")) + + (use-package helm-icons + :if (display-graphic-p) + :disabled + :config + (helm-icons-enable))) + + +(provide 'utilities/helm) +;;; helm.el ends here diff --git a/lisp/utilities/hideshow.el b/lisp/utilities/hideshow.el new file mode 100644 index 0000000..664ddde --- /dev/null +++ b/lisp/utilities/hideshow.el @@ -0,0 +1,28 @@ +;;; hideshow.el --- Configure hide show -*- lexical-binding: t; -*- +;;; Commentary: Hideshow mode is a buffer-local minor mode that allows you to selectively display portions of a program +;;; Code: https://github.com/emacs-mirror/emacs/blob/master/lisp/progmodes/hideshow.el + +(defun user--hs-minor-mode-hook () + "Minor mode hook for Hide Show." + ;;; (Bindings) ;;; + (local-set-key (kbd "C-c SPC") 'user/hs-toggle-level) + (local-set-key (kbd "C-c ") 'hs-show-block) + (local-set-key (kbd "C-c ") 'hs-hide-block) + (local-set-key (kbd "C-c ") 'hs-hide-all) + (local-set-key (kbd "C-c ") 'hs-show-all)) + + +(defun user/hs-toggle-level () + "Toggle hide/show for level at point." + (interactive) + (hs-show-block) + (hs-hide-level 1)) + + +(use-package hideshow + :init + (add-hook 'hs-minor-mode-hook 'user--hs-minor-mode-hook)) + + +(provide 'utilities/hideshow) +;;; hideshow.el ends here diff --git a/lisp/utilities/ibuffer.el b/lisp/utilities/ibuffer.el new file mode 100644 index 0000000..02ccb93 --- /dev/null +++ b/lisp/utilities/ibuffer.el @@ -0,0 +1,25 @@ +;;; ibuffer --- improved buffer management -*- lexical-binding: t; -*- +;;; Commentary: buffer is an advanced replacement for BufferMenu, which lets you operate on buffers much in the same manner as Dired. +;;; Code: + +(defun user--ibuffer-hook () + "Hook for improved buffer." + (ibuffer-vc-set-filter-groups-by-vc-root) + (unless (eq ibuffer-sorting-mode 'filename/process) + (ibuffer-do-sort-by-filename/process))) + +(use-package ibuffer + :bind* (([remap list-buffers] . ibuffer)) + :init + (add-hook 'ibuffer-hook 'user--ibuffer-hook) + :config + (validate-setq + ibuffer-filter-group-name-face 'font-lock-doc-face) + ;; https://github.com/purcell/ibuffer-vc + ;; Group buffers in ibuffer list by VC project + (use-package ibuffer-vc + :defer)) + + +(provide 'utilities/ibuffer) +;;; ibuffer.el ends here diff --git a/lisp/utilities/ido.el b/lisp/utilities/ido.el new file mode 100644 index 0000000..c6ddbcc --- /dev/null +++ b/lisp/utilities/ido.el @@ -0,0 +1,33 @@ +;;; ido.el --- interactively do things -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: https://github.com/emacs-mirror/emacs/blob/master/lisp/ido.el + +(defun user--ido-mode-hook () + "Mode hook for ido." + (when (feature-p 'flx) + (flx-ido-mode t)) + (when (feature-p 'ido-vertical-mode) + (ido-vertical-mode t))) + +(use-package ido + :defer + :config + (validate-setq + ;; Enable fuzzy matching + ido-enable-flex-matching t + ;; Remember buffers that have been open + ido-use-virtual-buffers t + ;; Allow the same buffer to be opened in different windows + ido-default-buffer-method 'selected-window) + ;; https://github.com/lewang/flx + ;; Matching engine + (use-package flx + :config + (validate-setq + ;; Flex has its own highlights. + ido-use-faces nil)) + (use-package ido-vertical-mode)) + + +(provide 'utilities/ido) +;;; ido.el ends here diff --git a/lisp/utilities/lsp.el b/lisp/utilities/lsp.el new file mode 100644 index 0000000..62c7f62 --- /dev/null +++ b/lisp/utilities/lsp.el @@ -0,0 +1,51 @@ +;;; lsp.el --- Language server protocol. -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: https://github.com/emacs-lsp/lsp-mode + +(defun user--lsp-mode-hook () + "Mode hook for LSP minor modes." + (message "user--lsp-mode-hook")) + +(use-package lsp-mode + :pin "MELPA" + :commands lsp + :hook ((lsp-after-open-hook . lsp-enable-imenu) + (lsp-mode-hook . user--lsp-mode-hook)) + :config + (validate-setq + ;; Automatically try to figure out project root. + lsp-auto-guess-root t + ;; Location of persistent LSP session. + lsp-session-file (path-join *user-cache-directory* "lsp-session") + ;; Disable yasnippet integration. + lsp-enable-snippet nil + ;; Don't watch files for changes, prevents freeze during compilation. + lsp-enable-file-watchers nil + ;; Disable headerline + lsp-headerline-breadcrumb-enable nil + ;; Increase the amount of data that can be read from a language server. + read-process-output-max (* 3 1024 1024)) + + ;; https://github.com/emacs-lsp/lsp-ui/ + ;; This package contains all the higher level UI modules of lsp-mode, like flycheck support and code lenses. + (use-package lsp-ui + :hook (lsp-mode-hook . lsp-ui-mode) + :config + ;; https://emacs-lsp.github.io/lsp-mode/tutorials/how-to-turn-off/ + (validate-setq + lsp-modeline-code-actions-enable nil + lsp-ui-sideline-enable nil + lsp-ui-doc-delay 3 + lsp-ui-doc-position 'bottom)) + + (use-package lsp-treemacs + :ensure t + :defer t + ;; :after (lsp treemacs) + :custom + (lsp-treemacs-sync-mode 1) + ) + ) + +(provide 'utilities/lsp) +;;; lsp.el ends here diff --git a/lisp/utilities/pandoc.el b/lisp/utilities/pandoc.el new file mode 100644 index 0000000..940b838 --- /dev/null +++ b/lisp/utilities/pandoc.el @@ -0,0 +1,19 @@ +;;; pandoc.el --- Interface to Pandoc -*- lexical-binding: t; -*- +;;; Commentary: An Emacs mode for interacting with Pandoc +;;; Code: https://github.com/joostkremers/pandoc-mode + +(defun user--pandoc-mode-hook () + "Pandoc mode hook." + (pandoc-load-default-settings)) + + +(use-package pandoc-mode + :if (executable-find "pandoc") + :defer + :diminish pandoc-mode + :init + (add-hook 'pandoc-mode 'user--pandoc-mode-hook)) + + +(provide 'utilities/pandoc) +;;; pandoc.el ends here diff --git a/lisp/utilities/polymode.el b/lisp/utilities/polymode.el new file mode 100644 index 0000000..414f7ad --- /dev/null +++ b/lisp/utilities/polymode.el @@ -0,0 +1,10 @@ +;;; polymode.el --- Multiple major modes in a single buffer. -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: https://github.com/polymode/polymode + +(use-package polymode + :defer) + + +(provide 'utilities/polymode) +;;; polymode.el ends here diff --git a/lisp/utilities/presentation.el b/lisp/utilities/presentation.el new file mode 100644 index 0000000..b09440b --- /dev/null +++ b/lisp/utilities/presentation.el @@ -0,0 +1,22 @@ +;;; presentation.el --- Emacs presentation mode -*- lexical-binding: t; -*- +;;; Commentary: Presentation mode is a global minor mode to zoom characters +;;; Code: https://github.com/zonuexe/emacs-presentation-mode + +(defun user--presentation-on-hook () + "Hook executed when enabling presentation mode." + ;; Disable Helm. + (helm-mode -1)) + +(defun user--presentation-off-hook () + "Hook executed when disabling presentation mode." + ;; Enable Helm. + (helm-mode 1)) + +(use-package presentation + :hook ((presentation-on-hook . user--presentation-on-hook) + (presentation-off-hook . user--presentation-off-hook)) + :bind-wrap ((:key :util :presentation) . presentation-mode)) + + +(provide 'utilities/presentation) +;;; presentation.el ends here diff --git a/lisp/utilities/profiler.el b/lisp/utilities/profiler.el new file mode 100644 index 0000000..d79c43d --- /dev/null +++ b/lisp/utilities/profiler.el @@ -0,0 +1,30 @@ +;;; profiler.el --- Configure Emacs profiler -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: +;;; Doc: https://www.gnu.org/software/emacs/manual/html_node/elisp/Profiling.html + +(defun user--profiler-report-mode-hook () + "Profiler report mode hook." + ;;; (Bindings) ;;; + (user/bind-key-local :basic :save 'profiler-report-write-profile) + (user/bind-key-local :basic :save-as 'profiler-report-write-profile)) + +(use-package profiler + :defer + :init + (add-hook 'profiler-report-mode-hook 'user--profiler-report-mode-hook) + + ;;; (Bindings) ;;; + (user/bind-key-global :emacs :profiler-start 'profiler-start) + (user/bind-key-global :emacs :profiler-stop 'profiler-stop) + (user/bind-key-global :emacs :profiler-report 'profiler-report) + :config + (validate-setq + ;; The maximum number distinct of call-stacks to save. + profiler-log-size 100000 + ;; Maximum call-stack depth to record. + profiler-max-stack-depth 32)) + + +(provide 'utilities/profiler) +;;; profiler.el ends here diff --git a/lisp/utilities/project.el b/lisp/utilities/project.el new file mode 100644 index 0000000..c04cb6c --- /dev/null +++ b/lisp/utilities/project.el @@ -0,0 +1,133 @@ +;;; project.el --- helpers for working with projects -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(with-eval-after-load 'eieio + (defclass user/proj () + () + "Project class.") + + (cl-defgeneric user/proj-name ((project user/proj)) + "Get the PROJECT name.") + + (cl-defgeneric user/proj-root ((project user/proj)) + "Get the PROJECT root path.") + + (cl-defgeneric user/proj-include-paths ((project user/proj)) + "Get the include paths for PROJECT.") + + (cl-defgeneric user/proj-build ((project user/proj)) + "Build PROJECT.") + + (cl-defmethod user/proj-name ((project user/proj)) + (file-name-nondirectory + (directory-file-name (user/proj-root project)))) + + (cl-defmethod user/proj-include-paths ((project user/proj))) + + (with-eval-after-load 'ede + (defclass user/ede-proj (user/proj) + ((ede :initarg :ede :initform nil + :documentation "EDE project instance.")) + "EDE project class.") + + (cl-defmethod user/proj-from-path :static ((project user/ede-proj) &rest args) + "Constructor for EDE project at path in ARGS." + (when (featurep 'ede) + (let ((ede-proj (ede-current-project (expand-file-name (first args))))) + (when (cl-some (lambda (args) + (and (featurep (first args)) + (funcall (second args) ede-proj))) + '((ede/cpp-root ede-cpp-root-project-p) + (ede/generic ede-generic-makefile-project-p) + (ede/generic ede-generic-cmake-project-p) + (ede/linux ede-linux-project-p) + (ede/compdb ede-compdb-project-p))) + (make-instance 'user/ede-proj :ede ede-proj))))) + + (cl-defmethod user/proj-name ((project user/ede-proj)) + (ede-name (oref project :ede))) + + (cl-defmethod user/proj-root ((project user/ede-proj)) + (ede-project-root-directory (oref project :ede))) + + (cl-defmethod user/proj-include-paths ((project user/ede-proj)) + (let ((root-path (user/proj-root project)) + (include-paths (oref (oref project :ede) include-path))) + (mapcar #'(lambda (path) (expand-file-name path root-path)) + include-paths))) + + (cl-defmethod user/proj-build ((project user/ede-proj)) + (let ((ede-proj (oref project :ede))) + (project-compile-project + ede-proj + (read-string "Build command: " (oref ede-proj compile-command)))))) + + (with-eval-after-load 'projectile + (defclass user/projectile-proj (user/proj) + ((root-path :initarg :root-path + :type string + :documentation "Project root path.")) + "Projectile project class.") + + (cl-defmethod user/proj-from-path :static ((project user/projectile-proj) &rest args) + "Constructor for projectile project at path in ARGS." + (let ((default-directory (file-name-as-directory (first args))) + (projectile-require-project-root nil)) + (when (and (feature-p 'projectile) (projectile-project-p)) + (make-instance 'user/projectile-proj :root-path (projectile-project-root))))) + + (cl-defmethod user/proj-root ((project user/projectile-proj)) + (oref project :root-path))) + + (with-eval-after-load 'vc + (defclass user/vc-proj (user/proj) + ((root-path :initarg :root-path + :type string + :documentation "Project root path.")) + "VC project class.") + + (cl-defmethod user/proj-from-path :static ((project user/vc-proj) &rest args) + "Constructor for VC project at path in ARGS." + (let* ((vc-backend (and (fboundp 'vc-responsible-backend) + (ignore-errors + (vc-responsible-backend + (file-truename (first args)))))) + (root-path (and vc-backend + (vc-call-backend + vc-backend 'root (file-truename (first args)))))) + (when root-path + (make-instance 'user/vc-proj :root-path root-path)))) + + (cl-defmethod user/proj-root ((project user/vc-proj)) + (oref project :root-path))) + + (cl-defmethod user/proj-from-path :static ((project user/proj) &rest args) + "Constructor for project at path in ARGS." + (let ((f (lambda (proj-type) + (when (fboundp proj-type) + (funcall #'user/proj-from-path proj-type (first args))))) + (proj-types '(user/ede-proj user/projectile-proj user/vc-proj))) + (cl-some f proj-types)))) + + +(defmacro with-project (project &optional path &rest body) + "Bind PROJECT for PATH and evaluate BODY if project exists." + (declare (indent defun) + (debug let)) + `(let ((,project (user/proj-from-path user/proj (or ,path (path-abs-buffer))))) + (when ,project + ,@body))) + + +(defmacro with-project-root (project-root &optional path &rest body) + "Bind PROJECT-ROOT for PATH and evaluate BODY if project exists." + (declare (indent defun) + (debug let)) + `(with-project project (or ,path (path-abs-buffer)) + (let ((,project-root (user/proj-root project))) + ,@body))) + + +(provide 'utilities/project) +;;; project.el ends here diff --git a/lisp/utilities/projectile.el b/lisp/utilities/projectile.el new file mode 100644 index 0000000..4a107f8 --- /dev/null +++ b/lisp/utilities/projectile.el @@ -0,0 +1,33 @@ +;;; projectile.el --- Projectile project management -*- lexical-binding: t; -*- +;;; Commentary: Projectile is a project interaction library for Emacs +;;; Code: https://github.com/bbatsov/projectile + +(use-package projectile + :diminish projectile-mode + :defer + :init + ;;; (Bindings) ;;; + (user/bind-key-global :basic :open-file-context 'projectile-find-file) + :config + (validate-setq + ;; Projectile bookmarks. + projectile-known-projects-file (path-join *user-data-directory* + "projectile-bookmarks.eld") + ;; Projectile cache store. + projectile-cache-file (path-join *user-cache-directory* "projectile") + ;; Use default completion that will usually be provided by Helm. + projectile-completion-system 'default) + + (with-eval-after-load 'smart-mode-line + (validate-setq + ;; Enable in smart mode line. + sml/use-projectile-p 'after-prefixes)) + + (with-executable 'ctags-exuberant + (validate-setq + ;; Default to exuberant ctags. + projectile-tags-command "ctags-exuberant -Re %s"))) + + +(provide 'utilities/projectile) +;;; projectile.el ends here diff --git a/lisp/utilities/sauron.el b/lisp/utilities/sauron.el new file mode 100644 index 0000000..1c2b073 --- /dev/null +++ b/lisp/utilities/sauron.el @@ -0,0 +1,21 @@ +;;; sauron.el --- Emacs event tracker -*- lexical-binding: t; -*- +;;; Commentary: sauron is an emacs mode for keeping track of events happening in the (emacs) world around you. +;;; Code: https://github.com/djcb/sauron + +(use-package sauron + :defer + :bind-wrap + ((:key :util :notifications) . sauron-toggle-hide-show) + :config + (validate-setq + ;; Display sauron in current frame. + sauron-separate-frame nil) + + (with-eval-after-load 'alert + (add-hook 'sauron-event-added-functions 'sauron-alert-el-adapter)) + + (sauron-start-hidden)) + + +(provide 'utilities/sauron) +;;; sauron.el ends here diff --git a/lisp/utilities/smartparens.el b/lisp/utilities/smartparens.el new file mode 100644 index 0000000..5198561 --- /dev/null +++ b/lisp/utilities/smartparens.el @@ -0,0 +1,165 @@ +;;; smartparens.el --- Set up smartparens. -*- lexical-binding: t; -*- +;;; Commentary: Smartparens is a minor mode for dealing with pairs in Emacs. +;;; Code: https://github.com/Fuco1/smartparens + +(defun user/smartparens-enable () + "Enable smartparens in current mode." + (when (feature-p 'smartparens) + (show-smartparens-mode t) + (smartparens-mode t))) + +(defun user--sp-org-point-in-checkbox-p (_id action _context) + "Check if smartparens ACTION is inside checkbox." + (when (eq action 'insert) + (sp--looking-at-p "\\s-*]"))) + +(defun user--sp-cxx-point-is-template-p (id action context) + "Return t if point with ID using ACTION in CONTEXT is in the right place for C++ angle-brackets." + (and (sp-in-code-p id action context) + (sp-point-after-word-p id action context))) + +(defun user--sp-cc-point-after-include-p (id action context) + "Return t if point with ID using ACTION in CONTEXT is in an #include." + (and (sp-in-code-p id action context) + (save-excursion + (goto-char (line-beginning-position)) + (looking-at-p "[ ]*#include[^<]+")))) + +(defun user--sp-haskell-after-symbol-p (_id action _context) + "Return t if point using ACTION is after a symbol." + (when (eq action 'insert) + (save-excursion + (backward-char 1) + (looking-back "\\sw\\|\\s_\\|\\s'")))) + +(defun user--sp-gfm-skip-asterisk (_ms position _me) + "Return t if asterisk should be skipped at point with POSITION." + (save-excursion + (goto-char position) + (save-match-data (looking-at "^\\* ")))) + +(defun user--sp-php-handle-docstring (&rest _ignored) + "Handle PHP docstrings with smartparens." + (-when-let (line (save-excursion + (forward-line) + (thing-at-point 'line))) + (cond + ((string-match-p "function" line) + (save-excursion + (insert "\n") + (let ((args (save-excursion + (forward-line) + (my-php-get-function-args)))) + (--each args + (insert (format "* @param %s\n" it))))) + (insert "* ")) + ((string-match-p ".*class\\|interface" line) + (save-excursion (insert "\n*\n* @author\n")) + (insert "* "))) + (let ((o (sp--get-active-overlay))) + (indent-region (overlay-start o) (overlay-end o))))) + +(use-package smartparens + :diminish smartparens-mode + :bind-wrap + (:map smartparens-mode-map + ([remap kill-line] . sp-kill-hybrid-sexp) + ((:key :basic :selection-next) . sp-select-next-thing-exchange) + ((:key :basic :selection-prev) . sp-select-previous-thing) + ((:key :basic :forward-word) . sp-forward-symbol) + ((:key :basic :backward-word) . sp-backward-symbol) + ((:key :basic :forward-expr) . sp-forward-sexp) + ((:key :basic :backward-expr) . sp-backward-sexp) + ((:key :code :unwrap-expr) . sp-unwrap-sexp) + ((:key :code :comment) . sp-comment) + ((:key :basic :cut-expr) . sp-kill-sexp) + ((:key :basic :copy-expr) . sp-copy-sexp)) + :config + (validate-setq + ;; Don't kill trailing whitespace with `sp-hybrid-kill'. + sp-hybrid-kill-excessive-whitespace nil) + + ;; Don't insert pairs automatically if point is at a word. + (sp-pair "{" nil + :post-handlers '(("||\n[i]" "RET") ("| " " ")) + :unless '(sp-point-before-word-p sp-point-before-same-p) + :wrap "C-{") + (sp-pair "(" nil + :post-handlers '(("||\n[i]" "RET") ("| " " ")) + :unless '(sp-point-before-word-p sp-point-before-same-p) + :wrap "C-(") + (sp-pair "[" nil + :post-handlers '(("| " " ")) + :unless '(sp-point-before-word-p sp-point-before-same-p)) + (sp-pair "\"" nil :unless '(sp-point-before-word-p sp-point-after-word-p sp-point-before-same-p)) + (sp-pair "'" nil :unless '(sp-point-before-word-p sp-point-after-word-p sp-point-before-same-p)) + + (sp-with-modes '(c-mode c++-mode objc-mode) + ;; Automatically add another newline before closing curly brace on enter. + (sp-local-pair "{" nil :post-handlers '(("||\n[i]" "RET"))) + (sp-local-pair "/*" "*/" :post-handlers '((" | " "SPC") ("* ||\n[i]" "RET"))) + + (sp-local-pair "<" ">" :when '(user--sp-cxx-point-is-template-p user--sp-cc-point-after-include-p)) + (sp-local-pair "/*" "*/" :post-handlers '(("||\n[i]" "RET") ("| " "SPC"))) + ;; Doxygen blocks. + (sp-local-pair "/**" "*/" :post-handlers '(("||\n[i]" "RET") ("||\n[i]" "SPC"))) + (sp-local-pair "/*!" "*/" :post-handlers '(("||\n[i]" "RET") ("[d-1]< | " "SPC")))) + + (sp-with-modes '(css-mode scss-mode less-css-mode stylus-mode) + (sp-local-pair "/*" "*/" :post-handlers '(("[d-3]||\n[i]" "RET") ("| " "SPC")))) + + (sp-with-modes '(haskell-mode) + (sp-local-pair "'" nil :unless '(user--sp-haskell-after-symbol-p)) + (sp-local-pair "\\(" nil :actions nil)) + + (sp-with-modes '(javascript-mode js2-mode js3-mode rjsx-mode) + (sp-local-pair "/* " " */" :post-handlers '(("| " "SPC")))) + + (sp-with-modes '(sh-mode markdown-mode gfm-mode rts-mode python-mode cython-mode) + (sp-local-pair "`" nil :unless '(sp-point-before-word-p sp-point-after-word-p sp-point-before-same-p))) + + (sp-with-modes '(markdown-mode gfm-mode rst-mode) + (sp-local-pair "*" "*" :wrap "C-*" :skip-match 'user--sp-gfm-skip-asterisk) + (sp-local-tag "2" "**" "**") + (sp-local-tag "<" "<_>" "" :transform 'sp-match-sgml-tags)) + + (sp-with-modes '(tex-mode plain-tex-mode latex-mode) + (sp-local-tag "i" "\"<" "\">") + (sp-local-pair "$" nil :unless '(sp-point-before-word-p)) + (sp-local-pair "\\[" " \\]") + (sp-local-pair "\\(" " \\)") + (sp-local-pair "\\{" " \\}") + (sp-local-pair "\\left(" " \\right)") + (sp-local-pair "\\left\\{" " \\right\\}")) + + (sp-with-modes '(org-mode) + (sp-local-pair "*" "*" + :post-handlers '(("[d1]" "SPC")) + :unless '(sp-point-after-word-p sp-point-before-word-p sp-point-at-bol-p)) + (sp-local-pair "_" "_" + :post-handlers '(("[d1]" "SPC")) + :unless '(sp-point-after-word-p sp-point-before-word-p)) + (sp-local-pair "/" "/" + :post-handlers '(("[d1]" "SPC")) + :unless '(sp-point-after-word-p sp-point-before-word-p user--sp-org-point-in-checkbox-p)) + (sp-local-pair "~" "~" + :post-handlers '(("[d1]" "SPC")) + :unless '(sp-point-after-word-p sp-point-before-word-p)) + (sp-local-pair "=" "=" + :post-handlers '(("[d1]" "SPC")) + :unless '(sp-point-after-word-p sp-point-before-word-p)) + (sp-local-pair "«" "»")) + + (sp-with-modes '(php-mode) + (sp-local-pair "/**" "*/" + :post-handlers '(("| " "SPC") (user--sp-php-handle-docstring "RET"))) + (sp-local-pair "/*." ".*/" :post-handlers '(("| " "SPC"))) + (sp-local-pair "{" nil :post-handlers '(("||\n[i]" "RET"))) + (sp-local-pair "(" nil :prefix "\\(\\sw\\|\\s_\\)*")) + + (sp-with-modes '(scala-mode) + (sp-local-pair "'" nil :actions nil))) + + +(provide 'utilities/smartparens) +;;; smartparens.el ends here diff --git a/lisp/utilities/spelling.el b/lisp/utilities/spelling.el new file mode 100644 index 0000000..5cd6de1 --- /dev/null +++ b/lisp/utilities/spelling.el @@ -0,0 +1,127 @@ +;;; flyspell.el --- spell checking on the fly -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(defconst *user-auto-dictionary-buffer-limit* (* 128 1024) + "Maximum buffer size for automatic dictionary detection.") + + +(defun user--flyspell-mode-common-hook () + "Hook for fly spell common mode." + (if (> (buffer-size) *user-auto-dictionary-buffer-limit*) + ;; Disables automatic dictionary detection in large buffers. + (guess-language-mode -1) + (with-feature 'flyspell-lazy + (guess-language-mode t) + ;; Enables flyspell lazy mode for small buffers. + (flyspell-lazy-mode t))) + + (with-feature 'auto-correct + (auto-correct-mode t)) + + ;;; (Bindings) ;;; + (user/bind-key-local :code :spellcheck-word 'ispell-word) + (user/bind-key-local :code :spellcheck-add-word 'user/flyspell-add-word-to-dict)) + + +(defun user--flyspell-mode-hook () + "Hook for fly spell mode." + (user--flyspell-mode-common-hook)) + + +(defun user--flyspell-prog-mode-hook () + "Hook for fly spell prog mode." + (user--flyspell-mode-common-hook)) + + +(defun user/flyspell-add-word-to-dict () + "Add the word at the current location to the private dictionary without question." + (interactive) + ;; Use the correct dictionary. + (flyspell-accept-buffer-local-defs) + (setq opoint (point-marker)) + (let ((cursor-location (point)) + (word (flyspell-get-word nil))) + (if (consp word) + (let ((start (car (cdr word))) + (end (car (cdr (cdr word)))) + (word (car word))) + ;; The word is incorrect, we have to propose a replacement. + (flyspell-do-correct 'save nil word cursor-location start end opoint))) + (ispell-pdict-save t))) + +(defun user-flyspell-p () + "Check if flyspell is available." + (or (executable-find "ispell") + (executable-find "aspell") + (executable-find "hunspell")) + nil) + +(when (user-flyspell-p) + (use-package flyspell + :ensure nil + :diminish flyspell-mode + :hook ((flyspell-mode-hook . user--flyspell-mode-hook) + (flyspell-prog-mode-hook . user--flyspell-prog-mode-hook)) + :config + (validate-setq + ;; Be silent when checking words. + flyspell-issue-message-flag nil) + + (cond + (;; Disable Hunspell due to issues on some machines. + (and nil (executable-find "hunspell")) + (validate-setq + ispell-program-name "hunspell" + ispell-really-hunspell t + ispell-extra-args '("-a" "-i" "utf-8"))) + ((executable-find "aspell") + (progn + (validate-setq + ispell-program-name "aspell" + ispell-really-aspell t + ;; Improve performance by reducing suggestions. + ispell-extra-args '("--sug-mode=ultra" "--dont-suggest")) + (when (boundp 'flyspell-list-command) + (validate-setq + flyspell-list-command "--list"))))) + ;; http://elpa.gnu.org/packages/auto-correct.html + ;; Remembers and automatically fixes past corrections + (use-package auto-correct + :diminish auto-correct-mode) + ;; https://github.com/pronobis/helm-flyspell + ;; Helm extension for correcting words with Flyspell. + (use-package helm-flyspell + :bind ("C-c c s" . helm-flyspell-correct)) + ;; https://github.com/rolandwalker/flyspell-lazy + ;; This package reduces the amount of work done by flyspell. + ;; Instead of checking instantly as you type, spelling will be checked when Emacs has been idle for a short time. + (use-package flyspell-lazy + :config + (validate-setq + ;; Idle timeout before running spell check on region. + flyspell-lazy-idle-seconds 10 + ;; Idle timeout before running spell check on entire buffer. + flyspell-lazy-window-idle-seconds 60)) + ;; https://github.com/tmalsburg/guess-language.el + ;; Emacs minor mode for robust automatic language detection + ;; TODO: Add fr + (use-package guess-language + :diminish guess-language-mode + :config + (validate-setq + ;; By default only guess English. + guess-language-languages '(en))) + ;; https://github.com/ppold/.emacs.d/blob/master/site-lisp/rw-hunspell.el + ;; special functions for Hunspell in ispell.el + (use-package rw-hunspell + :if (executable-find "hunspell") + :config + (with-eval-after-load 'ispell + (when ispell-really-hunspell + ;; Initialize `rw-hunspell` if Hunspell is in use. + (rw-hunspell-setup)))))) + + +(provide 'utilities/flyspell) +;;; flyspell.el ends here diff --git a/lisp/utilities/term.el b/lisp/utilities/term.el new file mode 100644 index 0000000..a117911 --- /dev/null +++ b/lisp/utilities/term.el @@ -0,0 +1,31 @@ +;;; term.el --- Initialize the Emacs terminal -*- lexical-binding: t; -*- +;;; Commentary: A terminal in Emacs +;;; Code: Cross-referencing commands + +(defun user--term-mode-hook () + "Term mode hook." + (user--shell-mode-common-hook)) + + +(defun user--term-exec-hook () + "Term startup hook." + ;; Automatically close buffer when finished. + (let* ((buf (current-buffer)) + (proc (get-buffer-process buf))) + (set-process-sentinel + proc + `(lambda (process event) + (if (string= event "finished\n") + (kill-buffer ,buf)))))) + +(use-package term + :defer + :init + (add-hook 'term-exec-hook 'user--term-exec-hook) + (add-hook 'term-mode-hook 'user--term-mode-hook) + :config + (define-key term-raw-map (kbd "C-c C-y") 'term-paste)) + + +(provide 'utilities/term) +;;; term.el ends here diff --git a/lisp/utilities/tramp.el b/lisp/utilities/tramp.el new file mode 100644 index 0000000..4452a44 --- /dev/null +++ b/lisp/utilities/tramp.el @@ -0,0 +1,65 @@ +;;; tramp --- remote file access -*- lexical-binding: t; -*- +;;; Commentary: https://www.emacswiki.org/emacs/TrampMode +;;; Code: + +(use-package tramp + :ensure nil + :after dash + :commands + (tramp-tramp-file-p + tramp-check-proper-method-and-host) + :config + (validate-setq + ;; Auto save storage. + tramp-auto-save-directory (path-join *user-auto-save-directory* "tramp") + ;; Default file transfer method. + tramp-default-method "ssh" + ;; Cache passwords. + password-cache t + password-cache-expiry 1000 + ;; SSH is properly configured to share connections. + tramp-use-ssh-controlmaster-options nil + ;; Skip looking for dir-local on remote system to speed up tramp. + enable-remote-dir-locals nil + ;; Preserve PATH on remote host. + tramp-remote-path (delete 'tramp-default-remote-path tramp-remote-path)) + + ;; Preserve PATH on remote host. + (add-to-list 'tramp-remote-path 'tramp-own-remote-path) + + (tramp-set-completion-function + "ssh" (mapcar + (lambda (x) (list 'tramp-parse-sconfig x)) + (-remove + (lambda (x) (not (file-exists-p x))) + `(,(path-join "/" "etc" "ssh_config") + ,(path-join "/" "etc" "ssh" "ssh_config") + ,(path-join *user-home-directory* ".ssh" "config"))))) + + (unless (fboundp 'tramp-compat-split-string) + ;; Workaround for Python mode depending on the deleted TRAMP + ;; function `tramp-compat-split-string'. + (defun tramp-compat-split-string (string pattern) + "Like `split-string' but omit empty strings. +In Emacs, (split-string \"/foo/bar\" \"/\") returns (\"foo\" \"bar\"). +This is, the first, empty, element is omitted. In XEmacs, the first +element is not omitted." + (split-string string pattern 'omit))) + ;; http://web.mit.edu/Emacs/source/emacs/lisp/net/tramp-cache.el + ;; file information caching for Tramp + (use-package tramp-cache + :ensure tramp + :config + (validate-setq + ;; Persistency files. + tramp-persistency-file-name + (path-join *user-cache-directory* "tramp"))) + ;; https://github.com/masasam/emacs-helm-tramp + ;; Tramp helm interface for ssh server and docker and vagrant + (use-package helm-tramp + :init + (user/bind-key-global :basic :open-file-tramp 'helm-tramp))) + + +(provide 'utilities/tramp) +;;; tramp.el ends here diff --git a/lisp/utilities/transient.el b/lisp/utilities/transient.el new file mode 100644 index 0000000..184e9be --- /dev/null +++ b/lisp/utilities/transient.el @@ -0,0 +1,22 @@ +;;; transient.el --- Configuration for transient. -*- lexical-binding: t; -*- +;;; Commentary: Taking inspiration from prefix keys and prefix arguments, +;;; Transient implements a similar abstraction involving a prefix command, infix arguments and suffix commands +;;; Code: https://github.com/magit/transient + +(use-package transient + :ensure nil + :config + ;; Using `validate-setq' here will cause loading to file if the + ;; history file does not exist. + (setq + ;; Location of transient files. + transient-history-file (path-join *user-cache-directory* "transient" "history.el") + transient-values-file (path-join *user-cache-directory* "transient" "values.el") + transient-levels-file (path-join *user-cache-directory* "transient" "levels.el")) + + ;; Create the transient cache folder. + (make-directory (path-join *user-cache-directory* "transient") t)) + + +(provide 'utilities/transient) +;;; transient.el ends here diff --git a/lisp/utilities/yasnippet.el b/lisp/utilities/yasnippet.el new file mode 100644 index 0000000..8555970 --- /dev/null +++ b/lisp/utilities/yasnippet.el @@ -0,0 +1,16 @@ +;;; yasnippet.el --- Configuration for yasnippet -*- lexical-binding: t: -*- +;;; Commentary: YASnippet is a template system for Emacs +;;; Code: +;;; Docs: https://github.com/joaotavora/yasnippet + +(use-package yasnippet + :ensure t + :config + (use-package yasnippet-snippets + :ensure t) + (define-key yas-minor-mode-map (kbd "") nil) + (define-key yas-minor-mode-map (kbd "TAB") nil) + (define-key yas-minor-mode-map (kbd "C-c y") yas-maybe-expand) + (yas-global-mode 1)) + +;;; yasnippet.el ends here diff --git a/lisp/utilities/zettelkasten.el b/lisp/utilities/zettelkasten.el new file mode 100644 index 0000000..92e8d39 --- /dev/null +++ b/lisp/utilities/zettelkasten.el @@ -0,0 +1,48 @@ +;;; zettelkasten.el --- zettelkasten method -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(use-package org-roam + :ensure t + :init + (setq org-roam-v2-ack t) + :custom + (org-roam-directory "~/.local/share/emacs/org/roam") + :bind (("C-c n f" . org-roam-node-find) + ("C-c n r" . org-roam-node-random) + (:map org-mode-map + (("C-c n i" . org-roam-node-insert) + ("C-c n o" . org-id-get-create) + ("C-c n t" . org-roam-tag-add) + ("C-c n a" . org-roam-alias-add) + ("C-c n l" . org-roam-buffer-toggle)))) + :config + ;; org-roam-setup is obsolete, use org-roam-db-autosync-enable instead. + (org-roam-db-autosync-enable)) + +;; Add modified and creation timestamps to the org-roam property drawer. +;; https://github.com/ThomasFKJorna/org-roam-timestamps +(use-package org-roam-timestamps + :ensure t + :after org-roam + :config + ;; Overwrite the previous timestamps instead of keeping a list of timestamps. + (setq org-roam-timestamps-remember-timestamps nil) + (org-roam-timestamps-mode)) + +;; A graphical frontend for exploring your org-roam Zettelkasten. +;; https://github.com/org-roam/org-roam-ui +(use-package org-roam-ui + :ensure t + :after org-roam + :config + (setq + ;; Sync the Emacs theme + org-roam-ui-sync-theme t + ;; TODO: + org-roam-ui-follow t + ;; Enable open in default browser + org-roam-ui-open-on-start t) + ) + +;;; zettelkasten.el ends here diff --git a/lisp/ux/backups.el b/lisp/ux/backups.el new file mode 100644 index 0000000..98b4c5d --- /dev/null +++ b/lisp/ux/backups.el @@ -0,0 +1,44 @@ +;;; backups.el --- Emacs backup system -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +;; Set up the backups directory. +(defconst *user-backup-directory* (path-join *user-cache-directory* "backups")) +;; Set up the autosaves directory. +(defconst *user-auto-save-directory* (path-join *user-cache-directory* "auto-saves")) +;; Emacs will create the backup dir automatically, but not the autosaves dir. +(make-directory *user-auto-save-directory* t) + + +(defun user--backups-config () + "Initialize Emacs backup system." + (validate-setq + ;; Put backups in the cache directory + backup-directory-alist `((".*" . ,*user-backup-directory*)) + ;; Version-control backup files + version-control t + ;; Keep 16 new versions and 2 old versions + kept-new-versions 6 + kept-old-versions 2 + ;; Delete old versions without asking + delete-old-versions t + ;; Always backup by copy + backup-by-copying t) + + (validate-setq + ;; Auto-save every minute or 300 events + auto-save-interval 300 + auto-save-timeout 60 + ;; Always auto-save buffers. + auto-save-default t + ;; Put autosave files (ie #foo#) and backup files (ie foo~) into a cache dir. + auto-save-file-name-transforms `((".*" ,(concat *user-auto-save-directory* "/\\1") t)) + ;; Put session backups into the cache directory. + auto-save-list-file-prefix (path-join *user-auto-save-directory* ".saves-"))) + + +(user--backups-config) + + +(provide 'ux/backups) +;;; backups.el ends here diff --git a/lisp/ux/buffers.el b/lisp/ux/buffers.el new file mode 100644 index 0000000..81a956b --- /dev/null +++ b/lisp/ux/buffers.el @@ -0,0 +1,55 @@ +;;; buffers.el --- Configure Emacs buffers -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(defun user/kill-matching-buffers (regexp &optional exclude-p) + "Kill buffers whose name match the specified REGEXP but not EXCLUDE-P." + (interactive "sKill buffers matching this regular expression: \nP") + (dolist (buffer (buffer-list)) + (let ((name (buffer-name buffer))) + (when (and name (not (string-equal name "")) + (/= (aref name 0) ?\s) + (string-match regexp name)) + (unless (and exclude-p (funcall exclude-p buffer)) + (kill-buffer buffer)))))) + + +(defun user/kill-all-buffers () + "Close all open buffers." + (interactive) + (mapc 'kill-buffer (buffer-list)) + (switch-to-buffer "*scratch*")) + + +(defun user/kill-other-buffers () + "Close all open buffers except current one." + (interactive) + (mapc 'kill-buffer (delq (current-buffer) (buffer-list)))) + + +(defun user--buffers-config () + "Initialize Emacs buffers. +;; The library uniquify overrides Emacs’ default mechanism for making buffer names unique" + (use-package uniquify + :ensure nil + :config + (validate-setq + ;; Set up uniquify's style. + uniquify-buffer-name-style 'reverse + uniquify-separator " • " + uniquify-after-kill-buffer-p t + uniquify-ignore-buffers-re "^\\*")) + + ;;; (Bindings) ;;; + (user/bind-key-global :basic :open-file 'find-file) + (user/bind-key-global :basic :open-buffer 'switch-to-buffer) + (user/bind-key-global :basic :save 'save-buffer) + (user/bind-key-global :basic :save-as 'write-file) + (user/bind-key-global :basic :close 'kill-buffer) + (user/bind-key-global :basic :quit 'save-buffers-kill-terminal)) + +(user--buffers-config) + + +(provide 'ux/buffers) +;;; buffers.el ends here diff --git a/lisp/ux/cache.el b/lisp/ux/cache.el new file mode 100644 index 0000000..cbb5366 --- /dev/null +++ b/lisp/ux/cache.el @@ -0,0 +1,15 @@ +;;; cache.el --- Configure Emacs persistent caches -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +;; https://github.com/sigma/pcache +;; Persistent caching for Emacs +(use-package pcache + :pin "MELPA" + :init + (setq-default + pcache-directory (path-join *user-cache-directory* "pcache"))) + + +(provide 'ux/cache) +;;; cache.el ends here diff --git a/lisp/ux/coding.el b/lisp/ux/coding.el new file mode 100644 index 0000000..ad5ba7d --- /dev/null +++ b/lisp/ux/coding.el @@ -0,0 +1,29 @@ +;;; coding.el --- Coding system configuration -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(defun user--coding-config () + "Initialize coding system." + ;; Prefer UTF-8 if there is a choice + (prefer-coding-system 'utf-8) + (when (eq default-terminal-coding-system 'utf-8) + ;; Set coding systems to UTF-8 + (set-default-coding-systems 'utf-8) + (set-terminal-coding-system 'utf-8) + (set-keyboard-coding-system 'utf-8) + (when (eq window-system 'x) + ;; Treat X11 clipboard input as UTF-8 string first; compound text next, + ;; etc. + (validate-setq + x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING)))) + + ;;; (Bindings) ;;; + (user/bind-key-global :emacs :describe-coding 'describe-coding-system) + (user/bind-key-global :emacs :describe-char 'describe-char) + (user/bind-key-global :emacs :describe-language 'describe-language-environment)) + +(user--coding-config) + + +(provide 'ux/coding) +;;; coding.el ends here diff --git a/lisp/ux/completion.el b/lisp/ux/completion.el new file mode 100644 index 0000000..7f7ab8b --- /dev/null +++ b/lisp/ux/completion.el @@ -0,0 +1,281 @@ +;;; completion.el --- Configures automatic code completion -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(eval-when-compile + (require 'cl)) + +(defun user--company-mode-hook () + "Company mode hook." + (message "user--company-mode-hook") + (when (derived-mode-p 'prog-mode) + (define-key (current-local-map) (user/get-key :code :complete) 'company-indent-or-complete-common)) + + ;; ;; (user/complete-at-point-install) + + ;; ;;; (Bindings) ;;; + ;; ;; (when (current-local-map) + ;; ;; (define-key (current-local-map) (user/get-key :code :auto-complete) 'company-complete)) + ;; (when (current-local-map) + ;; (define-key (current-local-map) (user/get-key :code :complete) 'company-complete)) + + ;; (when (current-local-map) + ;; (define-key (current-local-map) (user/get-key :code :complete) 'company-indent-or-complete-common)) + ) + +(defun user/company-mode-p () + "True if auto-complete should be used." + (and (boundp 'global-company-mode) + global-company-mode)) + + +;; (defun user/complete-at-point-install () +;; "Install completion backend in complete-at-point." +;; (setq +;; ;; Insert completion backend into `completion-at-point'. +;; completion-at-point-functions +;; (cons 'user/complete-at-point +;; (remove 'user/complete-at-point completion-at-point-functions)))) + + +;; (defun user/company-check-expansion () +;; "Check if expression can be expanded by company." +;; (save-excursion +;; (if (looking-at "\\_>") t +;; (backward-char 1) +;; (if (looking-at "\\.") t +;; (backward-char 1) +;; (if (looking-at "->") t nil))))) + + +;; (defun user/complete-at-point () +;; "Complete thing at point using completion backend." +;; (cond +;; ((minibufferp) (minibuffer-complete)) +;; ;; ((and (boundp 'auto-complete-mode) auto-complete-mode) #'auto-complete) +;; ((and (boundp 'company-mode) company-mode (user/company-check-expansion)) #'company-complete-common) +;; (t #'indent-for-tab-command))) + + +;; (defun ac-pcomplete () +;; "Auto-complete source for pcomplete." +;; ;; eshell uses `insert-and-inherit' to insert a \t if no completion +;; ;; can be found, but this must not happen as auto-complete source +;; (cl-flet ((insert-and-inherit (&rest args))) +;; ;; this code is stolen from `pcomplete' in pcomplete.el +;; (let* (tramp-mode ;; do not automatically complete remote stuff +;; (pcomplete-stub) +;; (pcomplete-show-list t) ;; inhibit patterns like * being deleted +;; pcomplete-seen pcomplete-norm-func +;; pcomplete-args pcomplete-last pcomplete-index +;; (pcomplete-autolist pcomplete-autolist) +;; (pcomplete-suffix-list pcomplete-suffix-list) +;; (candidates (pcomplete-completions)) +;; (beg (pcomplete-begin)) +;; ;; note, buffer text and completion argument may be +;; ;; different because the buffer text may bet transformed +;; ;; before being completed (e.g. variables like $HOME may be +;; ;; expanded) +;; (buftext (buffer-substring beg (point))) +;; (arg (nth pcomplete-index pcomplete-args))) +;; ;; we auto-complete only if the stub is non-empty and matches +;; ;; the end of the buffer text +;; (when (and (not (zerop (length pcomplete-stub))) +;; (or (string= pcomplete-stub ; Emacs 23 +;; (substring buftext +;; (max 0 +;; (- (length buftext) +;; (length pcomplete-stub))))) +;; (string= pcomplete-stub ; Emacs 24 +;; (substring arg +;; (max 0 +;; (- (length arg) +;; (length pcomplete-stub))))))) +;; ;; Collect all possible completions for the stub. Note that +;; ;; `candidates` may be a function, that's why we use +;; ;; `all-completions`. +;; (let* ((cnds (all-completions pcomplete-stub candidates)) +;; (bnds (completion-boundaries pcomplete-stub +;; candidates +;; nil +;; "")) +;; (skip (- (length pcomplete-stub) (car bnds)))) +;; ;; We replace the stub at the beginning of each candidate by +;; ;; the real buffer content. +;; (mapcar #'(lambda (cand) (concat buftext (substring cand skip))) +;; cnds)))))) + +;; (defvar ac-source-pcomplete +;; '((candidates . ac-pcomplete))) + + +;; (defun add-ac-sources (&rest sources) +;; "Add SOURCES for auto-complete after it has been loaded." +;; (when (user/auto-complete-p) +;; (dolist (source sources) +;; (if (boundp 'ac-sources) +;; (add-to-list 'ac-sources source) +;; (error "Declaration of ac-sources is missing!"))))) + + +;; (defun add-ac-modes (&rest major-modes) +;; "Add MAJOR-MODES for auto-complete after it has been loaded." +;; (when (user/auto-complete-p) +;; (dolist (mode major-modes) +;; (if (boundp 'ac-modes) +;; (add-to-list 'ac-modes mode) +;; (error "Declaration of ac-modes is missing!"))))) + + +;; (defun company-complete-common-or-selection () +;; "Insert the common part of all candidates, or the selection." +;; (interactive) +;; (when (company-manual-begin) +;; (let ((tick (buffer-chars-modified-tick))) +;; (call-interactively 'company-complete-common) +;; (when (eq tick (buffer-chars-modified-tick)) +;; (let ((company-selection-wrap-around t)) +;; (call-interactively 'company-complete-selection)))))) + + + +;; (defun add-company-sources (&rest sources) +;; "Add SOURCES for company completion in current mode, if it has been loaded." +;; (when (user/company-mode-p) +;; (when (boundp 'company-backends) +;; (setq-local +;; company-backends +;; (append sources company-backends))))) + + +(defun user--completion-config () + "Initialize automatic code completion." + (setq + ;; Activate completion on tab. + tab-always-indent 'complete + ;; Cycle completions in minibuffer below a certain threshold. + completion-cycle-threshold 5) + + ;; Allow completion of acronyms and initialisms. + (add-to-list 'completion-styles 'initials t) + + ;;; (Packages) ;;; + ;; (use-package auto-complete + ;; :disabled + ;; :requires popup fuzzy + ;; :defer + ;; :quelpa (auto-complete + ;; :fetcher github + ;; :repo "auto-complete/auto-complete") + ;; :diminish auto-complete-mode + ;; :init + ;; (add-hook 'flymake-mode-hook 'ac-flyspell-workaround) + ;; (add-hook 'auto-complete-mode-hook 'user--auto-complete-mode-hook) + ;; :config + ;; (with-feature 'auto-complete-config + ;; ;; Load default configuration. + ;; (ac-config-default) + ;; ;; Don't forcibly enable auto-complete. + ;; (global-auto-complete-mode -1)) + + ;; (validate-setq + ;; ;; Limit the number of candidates. + ;; ac-candidate-limit 40 + ;; ;; Delay until narrowing completions. + ;; ac-delay 0.5 + ;; ;; Do not trigger completion automatically. + ;; ac-auto-start nil + ;; ;; Use fuzzy matching. + ;; ac-fuzzy-enable t + ;; ac-use-fuzzy t + ;; ;; Do not pop up menu automatically. + ;; ac-auto-show-menu nil + ;; ;; Allow normal navigation keys in menu. + ;; ac-use-menu-map t + ;; ;; Do not auto-expand common candidates. + ;; ac-expand-on-auto-complete nil + ;; ;; Show quick help popup after half a second. + ;; ac-use-quick-help t + ;; ac-quick-help-delay 0.5 + ;; ;; Store the completion history in the cache directory. + ;; ac-comphist-file (path-join *user-cache-directory* "ac-comphist.dat")) + + ;; ;; Set up auto-complete for lisp-interaction- and ielm-mode. + ;; (dolist (hook (list 'lisp-interaction-mode-hook + ;; 'ielm-mode-hook)) + ;; (add-hook hook 'ac-emacs-lisp-mode-setup)) + + ;; ;;; (Bindings) ;;; + ;; (ac-set-trigger-key (user/get-key :code :try-complete)) + ;; (validate-setq + ;; ac-completing-map + ;; (let ((map (make-sparse-keymap))) + ;; ;; Expand on tab. + ;; (define-key map (user/get-key :code :try-complete) 'ac-expand-common) + ;; ;; Complete on enter. + ;; (define-key map (user/get-key :code :complete) 'ac-complete) + ;; ;; Bind configured auto complete key. + ;; (define-key map (user/get-key :code :auto-complete) 'auto-complete) + ;; ;; Scroll quick-help using M-n/p. + ;; (define-key map (kbd "C-M-n") 'ac-quick-help-scroll-down) + ;; (define-key map (kbd "C-M-p") 'ac-quick-help-scroll-up) + ;; map))) + + (use-package company + :diminish company-mode + :hook (company-mode-hook . user--company-mode-hook) + :config + (validate-setq + ;; Do not trigger completion automatically. + ;; company-idle-delay nil + ;; Complete immediately. + company-minimum-prefix-length 0 + ;; Show commonly used matches first. + company-transformers '(company-sort-by-occurrence) + ;; Align annotations to the right of tooltip. + company-tooltip-align-annotations t + ;; Active company frontends. + ;; company-frontends + ;; '(company-pseudo-tooltip-unless-just-one-frontend + ;; company-preview-frontend + ;; company-echo-metadata-frontend) + ) + + ;; (define-key company-active-map (user/get-key :code :complete) 'company-complete-selection) + ;; (define-key company-active-map + ;; (user/get-key :code :try-complete) 'company-complete-common-or-selection) + ;; (define-key company-active-map + ;; (user/get-key :basic :forward-line) 'company-select-next) + ;; (define-key company-active-map + ;; (user/get-key :basic :backward-line) 'company-select-previous) + ;; (define-key company-active-map (user/get-key :code :complete) 'company-search-candidates) + + (use-package company-dabbrev-code + :ensure company + :config + (validate-setq + ;; Complete even outside of code. + company-dabbrev-code-everywhere t)) + (use-package company-flx + :after (company) + :config + (company-flx-mode t)) + (use-package company-quickhelp + :if window-system + :after (company) + :hook (company-mode-hook . company-quickhelp-mode) + :config + (validate-setq + ;; Show quick help popup after half a second. + company-quickhelp-delay 0.5)) + (use-package company-box + :if (display-graphic-p) + :hook (company-mode . company-box-mode)) + + (global-company-mode t))) + +(user--completion-config) + + +(provide 'ux/completion) +;;; completion.el ends here diff --git a/lisp/ux/editing.el b/lisp/ux/editing.el new file mode 100644 index 0000000..b1cdc44 --- /dev/null +++ b/lisp/ux/editing.el @@ -0,0 +1,130 @@ +;;; editing.el --- Configure Emacs editing -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(defun beginning-or-indentation () + "Move cursor to beginning of line or to its indentation." + (interactive) + (if (bolp) + (back-to-indentation) + (beginning-of-line))) + + +(defun point-in-comment () + "Determine if the point is inside a comment." + (interactive) + (let ((syn (syntax-ppss))) + (and (nth 8 syn) + (not (nth 3 syn))))) + + +(defun end-of-line-or-code (arg) + "Move point to end of line or forward ARG. + +If already there, move back to end of code. By 'end of code' we +mean before a possible comment. Comments are recognized in any +mode that sets `syntax-ppss' properly." + (interactive "P") + (let ((eol (save-excursion + (move-end-of-line arg) + (point)))) + (cond ((= (point) eol) + (move-end-of-line arg) + (while (point-in-comment) + (backward-char)) + (skip-chars-backward " \t")) + (t (move-end-of-line arg))))) + +(defun user--editing-config () + "Initialize editing in Emacs." + (validate-setq + ;; Increase history. + history-length 1000) + + ;; Enable narrowing. + (put 'narrow-to-region 'disabled nil) + (put 'narrow-to-page 'disabled nil) + (put 'narrow-to-defun 'disabled nil) + + ;; Enable case modification. + (put 'upcase-region 'disabled nil) + (put 'downcase-region 'disabled nil) + + ;;; (Packages) ;;; + (use-package simple + :ensure nil + :diminish auto-fill-function) + + (when (eq window-system 'ns) + (use-package ns-win + :ensure nil + :config + (validate-setq + ;; Swap command and option on MacOS X. + mac-option-modifier 'alt + mac-command-modifier 'meta + ;; Use right option key for writing special characters. + mac-right-option-modifier nil))) + + ;; Fix https://github.com/rolandwalker/unicode-fonts/issues/21 + ;; (use-package unicode-fonts + ;; :if (eq default-terminal-coding-system 'utf-8) + ;; ;; Ensure `pcache-directory' has been set first. + ;; :after ux/cache + ;; :config + ;; (require 'persistent-soft) + ;; (unicode-fonts-setup)) + (load-file (path-join *user-cache-directory* "unicode-fonts-setup.el")) + + (when (feature-p 'helm) + (use-package helm-unicode + :if (eq default-terminal-coding-system 'utf-8) + :bind ("C-c h 8" . helm-unicode))) + + (use-package selected + :ensure t + :diminish selected-minor-mode + :commands selected-minor-mode + :init + (setq + selected-org-mode-map (make-sparse-keymap)) + (selected-global-mode t) + :bind (:map selected-keymap + ;; Region. + ("q" . selected-off) + ("e" . er/expand-region) + ;; Modification. + ("f" . fill-region) + ("u" . upcase-region) + ("d" . downcase-region) + ("s" . sort-lines) + ("m" . apply-macro-to-region-lines) + ;; Information. + ("w" . count-words-region) + ;; Motion. + ("p" . move-text-up) + ("n" . move-text-down) + :map selected-org-mode-map + ("t" . org-table-convert-region))) + + ;;; (Bindings) ;;; + (global-set-key [remap move-beginning-of-line] 'beginning-or-indentation) + (global-set-key [remap move-end-of-line] 'end-of-line-or-code) + (user/bind-key-global :basic :forward-word 'forward-word) + (user/bind-key-global :basic :backward-word 'backward-word) + (user/bind-key-global :basic :forward-expr 'forward-sexp) + (user/bind-key-global :basic :backward-expr 'backward-sexp) + (user/bind-key-global :basic :del-char-left 'delete-backward-char) + (user/bind-key-global :basic :del-char-right 'delete-char) + (user/bind-key-global :basic :widen 'widen) + (user/bind-key-global :basic :narrow-to-page 'narrow-to-page) + (user/bind-key-global :basic :narrow-to-region 'narrow-to-region) + (user/bind-key-global :basic :narrow-to-function 'narrow-to-defun) + (user/bind-key-global :code :join-line 'join-line) + (user/bind-key-global :code :fill-paragraph 'fill-paragraph)) + +(user--editing-config) + + +(provide 'ux/editing) +;;; editing.el ends here diff --git a/lisp/ux/frames.el b/lisp/ux/frames.el new file mode 100644 index 0000000..a65bac4 --- /dev/null +++ b/lisp/ux/frames.el @@ -0,0 +1,108 @@ +;;; frames.el --- Configure behavior of Emacs frames -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(defun user--window-configuration-change-hook () +;; "Window configuration change hook." +;; (setq +;; ;; Only split frame if it occupies at least 2/3 of the current screen width. +;; split-width-threshold (* (/ (window-total-width (frame-root-window)) 3) 2))) +) + +(defun user/iconify-or-deiconify-frame () + "Iconify the selected frame, or deiconify if it's currently an icon." + (interactive) + (if (display-graphic-p) + (iconify-or-deiconify-frame) + (suspend-frame))) + + +(defun user/close-and-kill-current-window () + "If multiple windows are open, close the current one and kill the buffer." + (interactive) + (kill-this-buffer) + (if (not (one-window-p)) + (delete-window))) + + +(defun user/close-and-kill-other-window () + "If multiple windows are open, close the next one and kill its buffer." + (interactive) + (other-window 1) + (kill-this-buffer) + (if (not (one-window-p)) + (delete-window))) + +(progn + (validate-setq + ;; Inhibit GUI features + use-file-dialog nil + ;; Don't split frames horizontally. + split-height-threshold nil) + + ;; Remove all the mouse-assisted frames + (when (fboundp 'scroll-bar-mode) (scroll-bar-mode -1)) + (when (fboundp 'tool-bar-mode) (tool-bar-mode -1)) + (when (fboundp 'menu-bar-mode) (menu-bar-mode -1)) + + ;;; (Hooks) ;;; + (add-to-list 'window-configuration-change-hook + 'user--window-configuration-change-hook) + + ;;; (Bindings) ;;; + (when (display-graphic-p) + (user/bind-key-global :emacs :fullscreen 'toggle-frame-fullscreen)) + + (user/bind-key-global :emacs :grow-vertical 'enlarge-window) + (user/bind-key-global :emacs :shrink-vertical 'shrink-window) + (user/bind-key-global :emacs :grow-horizontal 'enlarge-window-horizontally) + (user/bind-key-global :emacs :shrink-horizontal 'shrink-window-horizontally) + + ;;; (Packages) ;;; + ;; https://github.com/tomterl/fullframe + ;; Advice commands to execute fullscreen, restoring the window setup when exiting + (use-package fullframe) + ;; https://github.com/emacsorphanage/transpose-frame/blob/master/transpose-frame.el + ;; Transpose windows arrangement in a frame + (use-package transpose-frame + :defer + :init + (user/bind-key-global :emacs :flip-frame 'flip-frame) + (user/bind-key-global :emacs :flop-frame 'flop-frame) + (user/bind-key-global :emacs :rotate-frame-forward 'rotate-frame-clockwise) + (user/bind-key-global :emacs :rotate-frame-backward + 'rotate-frame-anticlockwise)) + ;; https://github.com/cyrus-and/zoom + ;; Fixed and automatic balanced window layout for Emacs + (use-package zoom + :diminish zoom-mode + :config + (validate-setq + ;; Ignored buffers. + zoom-ignored-buffer-name-regexps + '("^*calc") + ;; Ignore predicate definitions. + zoom-ignore-predicates + '((lambda () (> (count-lines (point-min) (point-max)) 20)))) + + (zoom-mode t)) + + (use-package frame + :ensure nil + :config + ;; Enable blinking cursor + (blink-cursor-mode)) + + ;; + ;; + (use-package framemove + :quelpa (framemove + :fetcher github + :repo "emacsmirror/framemove") + :init + (windmove-default-keybindings) + (setq framemove-hook-into-windmove t))) + + +(provide 'ux/frames) +;;; frames.el ends here diff --git a/lisp/ux/gc.el b/lisp/ux/gc.el new file mode 100644 index 0000000..c740347 --- /dev/null +++ b/lisp/ux/gc.el @@ -0,0 +1,11 @@ +;;; gc.el --- Garbage collector settings -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(use-package gcmh + :config + (gcmh-mode t)) + + +(provide 'ux/gc) +;;; gc.el ends here diff --git a/lisp/ux/help.el b/lisp/ux/help.el new file mode 100644 index 0000000..06aece6 --- /dev/null +++ b/lisp/ux/help.el @@ -0,0 +1,115 @@ +;;; help.el --- Emacs help -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(use-package help-mode + :ensure nil + :defer + :config + (use-package help-fns + :ensure nil + :init + (user/bind-key-global :emacs :describe-variable 'describe-variable) + (user/bind-key-global :emacs :describe-syntax 'describe-syntax)) + + (use-package helpful + :init + (user/bind-key-global :emacs :describe-function 'helpful-function) + (user/bind-key-global :emacs :describe-macro 'helpful-macro) + (user/bind-key-global :emacs :describe-command 'helpful-command) + :config + (with-eval-after-load 'popwin + ;; Use popwin for helpful buffer. + (add-to-list + 'popwin:special-display-config + ;; Don't select compilation window when shown + '(helpful-mode :position bottom :height 20))))) + +(use-package apropos + :ensure nil + :defer + :init + (user/bind-key-global :emacs :search-variable-value 'apropos-value)) + +(use-package info-look + :ensure nil + :defer + :init + (user/bind-key-global :emacs :describe-symbol 'info-lookup-symbol)) + +(use-package help + :ensure nil + :defer + :init + (user/bind-key-global :emacs :describe-bindings 'describe-bindings) + (user/bind-key-global :emacs :describe-key 'describe-key) + (user/bind-key-global :emacs :where-is 'where-is) + (user/bind-key-global :emacs :describe-mode 'describe-mode) + + (use-package helm-describe-modes + :init + (global-set-key [remap describe-mode] #'helm-describe-modes))) + +(use-package find-func + :ensure nil + :defer + :init + (user/bind-key-global :emacs :find-library 'find-library)) + +(use-package finder + :ensure nil + :defer + :init + (user/bind-key-global :emacs :find-package 'finder-by-keyword)) + +(use-package menu-bar + :ensure nil + :defer + :init + (user/bind-key-global :emacs :elisp-search 'elisp-index-search)) + +(use-package tutorial + :ensure nil + :defer + :init + (user/bind-key-global :emacs :tutorial 'help-with-tutorial)) + +(use-package which-key + :diminish which-key-mode + :config + (validate-setq + ;; Number of seconds until help is shown. + which-key-idle-delay 1.0 + which-key-special-keys + '("SPC" + "TAB" + "RET" + "DLT" ; delete key + "BS" ; backspace key + "ESC")) + + (when (eq default-terminal-coding-system 'utf-8) + (add-many-to-list + 'which-key-replacement-alist + '(("TAB" . nil) . ("↹" . nil)) + '(("RET" . nil) . ("⏎" . nil)) + '(("DEL" . nil) . ("⌫" . nil)) + '(("deletechar" . nil) . ("⌦" . nil)) + '(("DEL" . nil) . ("⇤" . nil)) + '(("SPC" . nil) . ("␣" . nil)))) + + (which-key-add-key-based-replacements + user/view-prefix "view" + user/help-prefix "help" + user/documentation-prefix "doc" + user/code-prefix "code" + user/code-eval-prefix "eval" + user/vcs-prefix "vcs" + user/utilities-prefix "utils" + user/apps-prefix "apps") + + (which-key-mode t)) + + +(provide 'ux/help) +;;; help.el ends here diff --git a/lisp/ux/indentation.el b/lisp/ux/indentation.el new file mode 100644 index 0000000..a9c3573 --- /dev/null +++ b/lisp/ux/indentation.el @@ -0,0 +1,18 @@ +;;; indentation.el --- Configure Emacs indentation behavior -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(use-package dtrt-indent + :config + (validate-setq + dtrt-indent-verbosity 0 + global-mode-string (delq 'dtrt-indent-mode-line-info global-mode-string))) + +(use-package smart-tabs-mode + :quelpa (smart-tabs-mode + :fetcher github + :repo "jcsalomon/smarttabs")) + + +(provide 'ux/indentation) +;;; indentation.el ends here diff --git a/lisp/ux/kill-yank.el b/lisp/ux/kill-yank.el new file mode 100644 index 0000000..b17ec53 --- /dev/null +++ b/lisp/ux/kill-yank.el @@ -0,0 +1,91 @@ +;;; kill-yank.el --- Configuration for copying and pasting of data -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(use-package mouse + :ensure nil + :config + (validate-setq + ;; Mouse selection should not automatically go to kill ring. + mouse-drag-copy-region nil) + (when (eq window-system 'x) + (validate-setq + ;; Don't inject mouse selection into X11 clipboard. + mouse-drag-copy-region nil) + + ;; Set middle mouse button to paste from primary X11 selection. + (global-set-key [mouse-2] 'mouse-yank-primary))) + +(use-package select + :if window-system + :ensure nil + :config + (validate-setq + ;; Do not interact with X11 primary selection. + select-enable-primary nil + ;; Make kill/yank interact with X11 clipboard selection. + select-enable-clipboard t + ;; Active region should set primary X11 selection. + select-active-regions t)) + +(use-package menu-bar + :ensure nil + :config + ;; Rebind to new clipboard functions when available. + (when (fboundp 'clipboard-kill-region) + (global-set-key [remap kill-region] 'clipboard-kill-region)) + (when (fboundp 'clipboard-kill-ring-save) + (global-set-key [remap kill-ring-save] 'clipboard-kill-ring-save)) + (when (fboundp 'clipboard-yank) + (global-set-key [remap yank] 'clipboard-yank))) + +(use-package expand-region + :bind-wrap + (((:key :basic :selection-expand) . er/expand-region) + ((:key :basic :select-paragraph) . er/mark-paragraph) + ((:key :basic :select-function) . er/mark-defun) + ((:key :basic :select-inside) . er/mark-inside-pairs))) + +(use-package multiple-cursors + :defer + :bind-wrap + (((:key :basic :selection-next) . mc/mark-next-like-this) + ((:key :basic :selection-prev) . mc/mark-previous-like-this) + ((:key :basic :selection-all) . mc/mark-all-like-this) + ((:key :basic :selection-edit-lines) . mc/edit-lines))) + +(use-package rect-mark + :defer + :quelpa (rect-mark + :fetcher url + :url "https://www.emacswiki.org/emacs/download/rect-mark.el")) + +(use-package simple + :ensure nil + :bind-wrap + (;; Delete words with C/M-w and rebind kill/yank region to C-x C-k/C-x C-w. + ((:key :basic :cut-word-left) . backward-kill-word) + ((:key :basic :cut-word-right) . kill-word) + ;; Set up basic copy/paste + ((:key :basic :selection-start) . set-mark-command) + ((:key :basic :copy) . kill-ring-save) + ((:key :basic :cut) . kill-region) + ((:key :basic :paste) . yank) + ((:key :basic :cycle-paste) . yank-pop)) + :config + (validate-setq + ;; Increase the maximum number of saved kill ring entries. + kill-ring-max 200 + ;; Do not store duplicates in kill ring. + kill-do-not-save-duplicates t + ;; Save clipboard before killing it. + save-interprogram-paste-before-kill t)) + +(use-package move-text + :defer) + +(user/bind-key-global :basic :cut-expr 'kill-sexp) + + +(provide 'ux/kill-yank) +;;; kill-yank.el ends here diff --git a/lisp/ux/mode-line.el b/lisp/ux/mode-line.el new file mode 100644 index 0000000..c066dd0 --- /dev/null +++ b/lisp/ux/mode-line.el @@ -0,0 +1,51 @@ +;;; mode-line.el --- Mode line configuration -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(defun user--modeline-config () + "Initialize Emacs mode line." + + ;; Show row and column numbers. + (validate-setq + line-number-mode t + column-number-mode t + display-time-mode nil) + + ;;; (Packages) ;;; + ;; Display the current time and system load. + (use-package time + :disabled + :config + (validate-setq + display-time-24hr-format t + display-time-day-and-date t) + (display-time)) + + ;; Display battery status. + (use-package battery + :disabled + :config + (when (and (functionp battery-status-function) + (not (string-match-p "N/A" (battery-format "%B" (funcall battery-status-function))))) + (validate-setq battery-mode-line-format + (format " [%s%s%s]" "%b%p%" " (%t)" + (if (string-match-p "N/A" (battery-format "%d" (funcall battery-status-function))) + "" + " %d°C"))) + (display-battery-mode t))) + + (use-package doom-modeline + :hook (after-init-hook . doom-modeline-init) + :config + (validate-setq + doom-modeline-height 18)) + + (use-package mode-line-bell + :config + (mode-line-bell-mode t))) + +(user--modeline-config) + + +(provide 'ux/mode-line) +;;; mode-line.el ends here diff --git a/lisp/ux/navigation.el b/lisp/ux/navigation.el new file mode 100644 index 0000000..2ff3703 --- /dev/null +++ b/lisp/ux/navigation.el @@ -0,0 +1,60 @@ +;;; navigation.el --- Set up Emacs buffer navigation -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(defun user/scroll-up () + "Scroll page up without moving point." + (interactive) + (scroll-down 1)) + + +(defun user/scroll-down () + "Scroll page down without moving point." + (interactive) + (scroll-up 1)) + + +(defun user--navigation-config () + "Set up Emacs buffer navigation." + ;; Enable mouse in iTerm2 + (when (eq system-type 'darwin) + (with-feature 'mouse + (xterm-mouse-mode t) + (defun track-mouse (e)))) + + ;;; (Bindings) ;;; + (user/bind-key-global :nav :scroll-up 'user/scroll-up) + (user/bind-key-global :nav :scroll-down 'user/scroll-down) + (user/bind-key-global :nav :goto-line 'goto-line) + (user/bind-key-global :nav :go-back 'pop-global-mark) + + ;; https://github.com/abo-abo/avy + ;; Jump to things in Emacs tree-style + (use-package avy + :bind ("M-s" . avy-goto-char) + :config + (avy-setup-default)) + + ;; https://github.com/jcs-elpa/goto-line-preview + ;; Preview line when executing goto-line command. + (use-package goto-line-preview + :disabled + :config + (user/bind-key-global :nav :goto-line 'goto-line-preview)) + + ;; https://github.com/magnars/smart-forward.el + ;; smart-forward gives you semantic navigation, building on expand-region + (use-package smart-forward + :disabled + :defer + :init + (user/bind-key-global :nav :context-forward 'smart-forward) + (user/bind-key-global :nav :context-backward 'smart-backward) + (user/bind-key-global :nav :context-up 'smart-up) + (user/bind-key-global :nav :context-down 'smart-down))) + +(user--navigation-config) + + +(provide 'ux/navigation) +;;; navigation.el ends here diff --git a/lisp/ux/paths.el b/lisp/ux/paths.el new file mode 100644 index 0000000..0577edd --- /dev/null +++ b/lisp/ux/paths.el @@ -0,0 +1,12 @@ +;;; paths.el --- Configure system paths in Emacs -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(use-package exec-path-from-shell + :if (memq window-system '(mac ns x)) + :config + (exec-path-from-shell-initialize)) + + +(provide 'ux/paths) +;;; paths.el ends here diff --git a/lisp/ux/popups.el b/lisp/ux/popups.el new file mode 100644 index 0000000..802fe4f --- /dev/null +++ b/lisp/ux/popups.el @@ -0,0 +1,35 @@ +;;; popups.el --- Configure Emacs popups -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(defun user--popups-config () + "Initialize Emacs popups." + (validate-setq + ;; Timeout for messages shown in minibuffer. + minibuffer-message-timeout 5) + + ;;; (Packages) ;;; + (use-package popup + :config + ;; Install workaround for whitespace-mode bug. + (with-eval-after-load 'modes/whitespace + (defadvice popup-draw (before user/turn-off-whitespace activate compile) + "Turn off whitespace mode before showing popup." + (user/whitespace-mode-suppress t)) + + (defadvice popup-delete (after user/restore-whitespace activate compile) + "Restore previous whitespace mode when deleting popup." + (user/whitespace-mode-suppress nil)))) + + (use-package popwin + :init + (user/bind-key-global :util :popwin-close 'popwin:close-popup-window) + (user/bind-key-global :util :popwin-buffer 'popwin:popup-buffer) + :config + (popwin-mode t))) + +(user--popups-config) + + +(provide 'ux/popups) +;;; popups.el ends here diff --git a/lisp/ux/prompts.el b/lisp/ux/prompts.el new file mode 100644 index 0000000..99e1f34 --- /dev/null +++ b/lisp/ux/prompts.el @@ -0,0 +1,30 @@ +;;; prompts.el --- Configure Emacs prompting -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(defun user--prompts-config () + "Initialize Emacs prompting." + (validate-setq + ;; Always follow links to version controlled files. + vc-follow-symlinks t + ;; Always ask before killing Emacs. + confirm-kill-emacs 'y-or-n-p) + + ;; Use shorter y/n prompts instead of yes/no. + (fset 'yes-or-no-p 'y-or-n-p) + + (when (display-graphic-p) + (validate-setq + ;; Don't use graphical dialog boxes when prompting. + use-dialog-box nil)) + + (defadvice save-buffers-kill-emacs (around no-query-kill-emacs activate) + "Prevent \"Active processes exist\" query when you quit Emacs." + (with-feature 'cl-lib + (cl-flet ((process-list ())) ad-do-it)))) + +(user--prompts-config) + + +(provide 'ux/prompts) +;;; prompts.el ends here diff --git a/lisp/ux/rendering.el b/lisp/ux/rendering.el new file mode 100644 index 0000000..14b1669 --- /dev/null +++ b/lisp/ux/rendering.el @@ -0,0 +1,53 @@ +;;; rendering.el --- Configure Emacs user interface rendering -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(defconst *user-fast-font-lock-buffer-limit* (* 128 1024) + "Maximum buffer size for maximum font lock decorations.") + + +(defun user--rendering-find-file-hook () + "Rendering hook for `find-file'." + (when (> (buffer-size) *user-fast-font-lock-buffer-limit*) + (setq + ;; Reduce the level of fontification. + font-lock-maximum-decoration nil) + + (font-lock-refresh-defaults))) + + +(defun user--rendering-config () + "Setup Emacs user interface rendering." + (with-eval-after-load 'font-lock + ;; Allow reduction of decoration level in large buffers. + (make-variable-buffer-local 'font-lock-maximum-decoration)) + + (validate-setq + ;; Redraw the entire screen before checking for pending input events. + ;; This will improve performance in general but might degrade performance of + ;; key repeat. + redisplay-dont-pause t + ;; Use the maximum amount of decorations by default. + font-lock-maximum-decoration t + ;; Always use JIT mode for font-lock. + font-lock-support-mode 'jit-lock-mode + ;; Set up font-lock JIT mode to do background parsing. + jit-lock-stealth-time 1.0 + jit-lock-stealth-nice 0.03 + jit-lock-stealth-load 200 + jit-lock-stealth-verbose nil + jit-lock-chunk-size 500 + ;; Do not reorder text during rendering. + bidi-display-reordering nil) + + ;;; (Hooks) ;;; + (add-hook 'find-file-hook 'user--rendering-find-file-hook) + + ;;; (Bindings) ;;; + (user/bind-key-global :emacs :redraw 'redraw-display)) + +(user--rendering-config) + + +(provide 'ux/rendering) +;;; rendering.el ends here diff --git a/lisp/ux/scrolling.el b/lisp/ux/scrolling.el new file mode 100644 index 0000000..0ae46c0 --- /dev/null +++ b/lisp/ux/scrolling.el @@ -0,0 +1,34 @@ +;;; scrolling.el --- Configure scrolling in Emacs buffers -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(defun user--scrolling-config () + "Configure Emacs buffer scrolling." + (validate-setq + ;; Set distance in line from margin before scrolling commences. + scroll-margin 5 + ;; Prevent cursor from jumping to center of window on scroll. + scroll-conservatively 100000 + ;; Try to maintain screen position when scrolling entire pages. + scroll-preserve-screen-position t) + + ;;; (Bindings) ;;; + (user/bind-key-global :emacs :recenter 'recenter)) + +(use-package mwheel + :ensure nil + :if window-system + :config + (validate-setq + ;; Scroll five lines when using mouse wheel. + mouse-wheel-scroll-amount '(5 ((shift) . 5)) + ;; Use constant speed when scrolling with mouse wheel. + mouse-wheel-progressive-speed nil + ;; Scroll the window that the cursor is over. + mouse-wheel-follow-mouse t)) + +(user--scrolling-config) + + +(provide 'ux/scrolling) +;;; scrolling.el ends here diff --git a/lisp/ux/search-replace.el b/lisp/ux/search-replace.el new file mode 100644 index 0000000..df34fd2 --- /dev/null +++ b/lisp/ux/search-replace.el @@ -0,0 +1,60 @@ +;;; search-replace.el --- Configuration for searching and replacing -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(defun user--search-replace-config () + "Initialize Emacs search and replace." + (validate-setq + ;; Highlight all visible matches. + search-highlight t + query-replace-highlight t + ;; Perform certain commands only on the marked region. + transient-mark-mode t) + + ;;; (Bindings) ;;; + ;; Use regular expression searches by default. + (user/bind-key-global :basic :search-forward 'isearch-forward-regexp) + (user/bind-key-global :basic :search-backward 'isearch-backward-regexp) + (user/bind-key-global :basic :search-files 'find-grep) + + ;;; (Packages) ;;; + (use-package visual-regexp + :defer + :bind* (([remap query-replace-regexp] . vr/query-replace) + ([remap replace-regexp] . vr/replace))) + + (use-package replace-with-inflections + :bind (:map search-map + ("n" . query-replace-names-with-inflections))) + + (use-package anzu + :defer + :diminish anzu-mode + :config + (global-anzu-mode t)) + + (use-package grep + :defer + :config + (validate-setq + ;; Highlight matches when using grep. + grep-highlight-matches t) + + (when (eq system-type 'darwin) + (-when-let (gnu-find (executable-find "gfind")) + (validate-setq find-program gnu-find))) + (-when-let (gnu-xargs (executable-find "gxargs")) + (validate-setq xargs-program gnu-xargs)) + + (use-package wgrep + :defer + :config + (use-package wgrep-helm) + (use-package wgrep-ag + :if (executable-find "ag"))))) + +(user--search-replace-config) + + +(provide 'ux/search-replace) +;;; search-replace.el ends here diff --git a/lisp/ux/server.el b/lisp/ux/server.el new file mode 100644 index 0000000..cdf4e58 --- /dev/null +++ b/lisp/ux/server.el @@ -0,0 +1,35 @@ +;;; server.el --- Emacs server setup -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(defun user/server-save () + "Save and quickly exit from server edit mode." + (interactive) + (save-buffer) + (server-edit)) + + +(defun user--server-after-init-hook () + "Initialize Emacs server after init has completed." + (with-feature 'server + (unless (server-running-p) + (server-start) + + (when (and (display-graphic-p) + (feature-p 'edit-server)) + (edit-server-start))))) + + +(use-package server + ;; Emacs clients regularly causes Emacs to crash on Darwin. + :disabled + :if (not (eq system-type 'darwin)) + :ensure nil + :hook (after-init-hook . user--server-after-init-hook) + :config + (use-package edit-server + :if window-system)) + + +(provide 'ux/server) +;;; server.el ends here diff --git a/lisp/ux/sessions.el b/lisp/ux/sessions.el new file mode 100644 index 0000000..df02d3f --- /dev/null +++ b/lisp/ux/sessions.el @@ -0,0 +1,40 @@ +;;; sessions.el --- Set up Emacs to remember things between sessions -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(use-package recentf + ;; Prevent entries from loading tramp resulting in the stable + ;; version of CEDET being loaded before devel. + :hook (after-init-hook . recentf-mode) + :config + (validate-setq + recentf-max-saved-items 1000 + recentf-exclude '("/elpa/" "/tmp/") + recentf-save-file (path-join *user-cache-directory* "recentf"))) + +(use-package savehist + :init + ;; Must be set before enabling savehist so validate cannot be used + ;; here. + (setq-default + savehist-file (path-join *user-cache-directory* "savehist") + ;; Save minibuffer history. + savehist-save-minibuffer-history t + ;; Additional history to save. + savehist-additional-variables '(search-ring regexp-search-ring kill-ring) + ;; Autosave every once in a while. + savehist-autosave-interval 180) + + (savehist-mode t)) + +(use-package saveplace + :config + (validate-setq + ;; Location of saveplace cache store. + save-place-file (path-join *user-cache-directory* "saveplace") + ;; Enable. + save-place-mode t)) + + +(provide 'ux/sessions) +;;; sessions.el ends here diff --git a/lisp/ux/startup.el b/lisp/ux/startup.el new file mode 100644 index 0000000..fbd76dc --- /dev/null +++ b/lisp/ux/startup.el @@ -0,0 +1,18 @@ +;;; startup.el --- Configure Emacs startup behavior -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +;; startup.el has no `provide' statement. +(progn + (validate-setq + ;; Do not show the splash screen or message + inhibit-startup-screen t + inhibit-startup-echo-area-message (getenv "USER")) + + (use-package dashboard + :config + (dashboard-setup-startup-hook))) + + +(provide 'ux/startup) +;;; startup.el ends here diff --git a/lisp/ux/symbols.el b/lisp/ux/symbols.el new file mode 100644 index 0000000..3ca134b --- /dev/null +++ b/lisp/ux/symbols.el @@ -0,0 +1,19 @@ +;;; symbols.el --- Configure how Emacs handles certain symbols -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(use-package page-break-lines + :diminish page-break-lines-mode + :config + (global-page-break-lines-mode t)) + +;; (with-eval-after-load 'ux/coding +;; (when (eq default-terminal-coding-system 'utf-8) +;; (use-package pretty-mode-plus))) + +(use-package all-the-icons + :if window-system) + + +(provide 'ux/symbols) +;;; symbols.el ends here diff --git a/lisp/ux/theme.el b/lisp/ux/theme.el new file mode 100644 index 0000000..207ec2c --- /dev/null +++ b/lisp/ux/theme.el @@ -0,0 +1,63 @@ +;;; theme.el --- Configure Emacs theme -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(use-package custom + :ensure nil + :config + + ;; Use 20210731.818 release. + (use-package doom-themes + :config + (setq doom-themes-enable-bold t ; if nil, bold is universally disabled + doom-themes-enable-italic t) ; if nil, italics is universally disabled + (load-theme 'doom-material t) + (custom-set-faces + `(outline-1 ((t (:foreground ,(doom-color 'yellow))))) + `(outline-2 ((t (:foreground ,(doom-color 'orange))))) + `(outline-3 ((t (:foreground ,(doom-color 'dark-blue))))) + `(outline-4 ((t (:foreground ,(doom-color 'teal))))) + `(markdown-header-face-1 ((t (:inherit 'outline-1)))) + `(markdown-header-face-2 ((t (:inherit 'outline-2)))) + `(markdown-header-face-3 ((t (:inherit 'outline-3)))) + `(markdown-header-face-4 ((t (:inherit 'outline-4)))) + `(org-drawer ((t (:foreground ,(doom-lighten 'base6 .30))))) + `(font-lock-comment-face ((t (:foreground ,(doom-lighten 'base6 .20))))) + `(org-default ((t (:inherit 'default)))) + `(org-table ((t (:foreground ,(doom-lighten 'base2 0.70))))) + `(org-agenda-done ((t (:inherit 'org-default))))) + )) + +(use-package faces + :ensure nil + :defer + :bind-wrap + (((:key :emacs :describe-face) . describe-face) + ((:key :emacs :describe-all-faces) . list-faces-display)) + :config + (when (display-graphic-p) + (cond + ((eq system-type 'darwin) + (set-face-attribute 'default nil :family "Menlo" :height 110 :weight 'normal)) + ((eq system-type 'windows-nt) + (set-face-attribute 'default nil :family "Consolas" :height 100 :weight 'normal)) + ((eq system-type 'gnu/linux) + (set-face-attribute 'default nil :foundry "DejaVu Sans Mono" :family "Monospace" + :height 90 :weight 'normal))))) + +(use-package ns-auto-titlebar + :if (eq system-type 'darwin) + :config + (ns-auto-titlebar-mode)) + +(use-package face-remap + :ensure nil + :bind-wrap + (((:key :emacs :text-scale-reset) . (lambda () (interactive) + (text-scale-set 0))) + ((:key :emacs :text-scale-increase) . text-scale-increase) + ((:key :emacs :text-scale-decrease) . text-scale-decrease))) + + +(provide 'ux/theme) +;;; theme.el ends here diff --git a/lisp/ux/tree.el b/lisp/ux/tree.el new file mode 100644 index 0000000..3c32919 --- /dev/null +++ b/lisp/ux/tree.el @@ -0,0 +1,19 @@ +;;; tree.el --- Configures tree representation -*- lexical-bindings: t -*- +;;; Commentary: +;;; Code: + +;; (defun treemacs--split-window-advice (original-split-function &rest args) +;; (-let [w (treemacs--is-visible?)] +;; (unwind-protect +;; (progn +;; (when w (set-window-parameter w 'window-side treemacs-position)) +;; (apply original-split-function args)) +;; (when w (set-window-parameter w 'window-side treemacs-position))))) +;; (advice-add 'split-window-right :around #'treemacs--split-window-advice) + +(use-package treemacs + :ensure t + :defer t) + +(provide 'ux/tree) +;;; tree.el ends here. diff --git a/lisp/ux/undo.el b/lisp/ux/undo.el new file mode 100644 index 0000000..ed9d402 --- /dev/null +++ b/lisp/ux/undo.el @@ -0,0 +1,46 @@ +;;; undo.el --- Configure Emacs undo -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +(defconst *user-undo-tree-cache-directory* + (path-join *user-cache-directory* "undo-tree") + "Path to user's undo-tree cache store.") + + +;;; (Bindings) ;;; +(user/bind-key-global :basic :undo 'undo) + +(use-package undo-tree + :bind-wrap + (([remap undo] . undo-tree-undo) + ([remap redo] . undo-tree-redo) + ((:key :basic :undo) . undo-tree-undo) + ((:key :basic :redo) . undo-tree-redo) + ((:key :util :undo-tree) . undo-tree-visualize)) + :config + ;; Ensure that cache store exists. + (make-directory *user-undo-tree-cache-directory* t) + + (validate-setq + ;; Set up undo history cache store. + undo-tree-history-directory-alist + `((".*" . ,*user-undo-tree-cache-directory*)) + ;; Persistent undo history. + undo-tree-auto-save-history t + ;; Don't display in mode-line. + undo-tree-mode-lighter "" + ;; Display time stamps in visualizer by default. + undo-tree-visualizer-timestamps t + ;; Display diffs in visualizer by default. + undo-tree-visualizer-diff t) + + ;; Compress undo history. + (defadvice undo-tree-make-history-save-file-name + (after undo-tree activate) + (validate-setq ad-return-value (concat ad-return-value ".gz"))) + + (global-undo-tree-mode t)) + + +(provide 'ux/undo) +;;; undo.el ends here diff --git a/lisp/ux/windows.el b/lisp/ux/windows.el new file mode 100644 index 0000000..db09124 --- /dev/null +++ b/lisp/ux/windows.el @@ -0,0 +1,11 @@ +;;; windows.el --- Configure behavior of Emacs windows -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +;; https://www.emacswiki.org/emacs/WindMove +;; Move point from window to window using Shift and the arrow keys. +(when (fboundp 'windmove-default-keybindings) + (windmove-default-keybindings)) + +(provide 'ux/windows) +;;; windows.el ends here diff --git a/prologue.el b/prologue.el new file mode 100644 index 0000000..75ed5c5 --- /dev/null +++ b/prologue.el @@ -0,0 +1,43 @@ +;;; prologue.el --- Emacs init prologue +;;; Commentary: +;;; Code: + +(eval-when-compile + (defconst *user-emacs-lisp-directory* + (expand-file-name "lisp" user-emacs-directory) + "Path to user Emacs Lisp directory.") + + ;; Allow Emacs to find configuration files. + (add-to-list 'load-path *user-emacs-lisp-directory*)) + + +;; Bring in constants used throughout initialization. +(require 'init-constants) +;; Initialize basic Emacs functionality. +(require 'init-emacs) + + +;; Load Emacs utilities. +(require 'lib/list) +(require 'lib/string) +(require 'lib/with) +(require 'lib/utils) + +;; Set up package management. +(require 'lib/packaging) + +;; Install benchmark-init if present. +(use-package benchmark-init + :ensure t) + +;; Helper functions for bootstrapping Emacs. +(require 'lib/bootstrap) + +;; Helper functions for other applications. +(require 'lib/apps) + +;; Load Emacs user experience settings. +(require 'init-bindings) +(require 'init-ux) + +;;; prologue.el ends here