diff --git a/ansible/roles/emacs/files/base-extensions.el b/ansible/roles/emacs/files/base-extensions.el new file mode 100644 index 0000000..1721911 --- /dev/null +++ b/ansible/roles/emacs/files/base-extensions.el @@ -0,0 +1,84 @@ +;; Eglot recommends pulling the latest of the standard libraries it +;; uses from ELPA if you're not tracking the current emacs development +;; branch. +(use-package xref + :pin gnu + ) + +(use-package eldoc + :pin gnu + :diminish + ) + +;; Other packages + +(use-package emacs + :config + (setq enable-recursive-minibuffers t) + + ;; Filter the M-x list base on the current mode + (setq read-extended-command-predicate #'command-completion-default-include-p) + + ;; Enable triggering completion with the tab key. + (setq tab-always-indent 'complete) + ) + +(use-package dashboard + :config + (dashboard-setup-startup-hook)) + +(use-package ediff + :config + (setq ediff-window-setup-function 'ediff-setup-windows-plain) + (setq-default ediff-highlight-all-diffs 'nil) + (setq ediff-diff-options "-w")) + +(when (version<= "26.0.50" emacs-version ) + (add-hook 'prog-mode-hook 'display-line-numbers-mode) + (add-hook 'prog-mode-hook 'column-number-mode) + ) + +(use-package page-break-lines) + +(use-package recentf + ;; This is an emacs built-in but we're pulling the latest version + :config + (setq recentf-max-saved-items 100) + (setq recentf-save-file (recentf-expand-file-name "~/.emacs.d/private/cache/recentf")) + (recentf-mode 1)) + +;; Persist history over Emacs restarts. Vertico sorts by history position. +(use-package savehist + ;; This is an emacs built-in but we're pulling the latest version + :config + (savehist-mode)) + +(use-package which-key + :diminish + :config + (which-key-mode)) + +(use-package windmove + :config + (windmove-default-keybindings)) + +(setq tramp-default-method "ssh") + +(use-package dockerfile-mode) + +(use-package nginx-mode + :config + (setq nginx-indent-level 4)) + +(use-package systemd + :mode + (("\\.service\\'" . systemd-mode) + ("\\.timer\\'" . systemd-mode)) + ) + +(use-package pkgbuild-mode + :mode + (("PKGBUILD\\'" . pkgbuild-mode)) + ) + +(provide 'base-extensions) diff --git a/ansible/roles/emacs/files/base-functions.el b/ansible/roles/emacs/files/base-functions.el new file mode 100644 index 0000000..cb3a2f9 --- /dev/null +++ b/ansible/roles/emacs/files/base-functions.el @@ -0,0 +1,92 @@ +;; ========== Function to reload current file ================= + +(defun reload-file () + "Revert buffer without confirmation." + (interactive) + (revert-buffer :ignore-auto :noconfirm)) + +;; =========================================================== +;; ============= Run commands ================================ +(defun run-command-on-buffer (cmd &rest args) + "Run a command using the current buffer as stdin and replacing its contents if the command succeeds with the stdout from the command. This is useful for code formatters." + (let ( + (stdout-buffer (generate-new-buffer "tmp-stdout" t)) + (full-cmd (append '(call-process-region nil nil cmd nil stdout-buffer nil) args)) + ) + (unwind-protect + (let ((exit-status (eval full-cmd))) + (if (eq exit-status 0) + (save-excursion + (replace-buffer-contents stdout-buffer) + ) + (message "FAILED running command on buffer %s" (append (list cmd) args)) + ) + ) + (kill-buffer stdout-buffer) + ) + ) + ) + +(defun run-command-in-directory (dir cmd &rest args) + "Run a command in the specified directory. If the directory is nil, the directory of the file is used. The stdout result is trimmed of whitespace and returned." + (let ( + (default-directory (or dir default-directory)) + (stdout-buffer (generate-new-buffer "tmp-stdout" t)) + (full-cmd (append '(call-process cmd nil (list stdout-buffer nil) nil) args)) + ) + (unwind-protect + (let ((exit-status (condition-case nil (eval full-cmd) (file-missing nil)))) + (if (eq exit-status 0) + (progn + (with-current-buffer stdout-buffer + (string-trim (buffer-string)) + ) + ) + ) + ) + (kill-buffer stdout-buffer) + ) + ) + ) + +(defun load-directory (dir) + (let ((load-it (lambda (f) + (load-file (concat (file-name-as-directory dir) f))) + )) + (mapc load-it (directory-files dir nil "\\.el$")))) + +(defun generate-github-link () + "Generate a permalink to the current line." + (interactive) + (let ( + (current-rev (vc-working-revision buffer-file-name)) + (line-number (line-number-at-pos)) + (repository-url (vc-git-repository-url buffer-file-name)) + (relative-path (file-relative-name buffer-file-name (vc-root-dir))) + ) + (save-match-data + (and (string-match "\\(git@github\.com:\\|https://github\.com/\\)\\([^/]+\\)/\\([^.]+\\).git" repository-url) + (let* ( + (gh-org (match-string 2 repository-url)) + (gh-repo (match-string 3 repository-url)) + (full-url (format "https://github.com/%s/%s/blob/%s/%s#L%s" gh-org gh-repo current-rev relative-path line-number)) + ) + (message "%s" full-url) + (kill-new full-url) + ) + ) + ) + ) + ) + +(defmacro when-linux (&rest body) + "Execute only when on Linux." + (declare (indent defun)) + `(when (eq system-type 'gnu/linux) ,@body)) + +(defmacro when-freebsd (&rest body) + "Execute only when on FreeBSD." + (declare (indent defun)) + `(when (eq system-type 'berkeley-unix) ,@body)) + +(provide 'base-functions) diff --git a/ansible/roles/emacs/files/base-global-keys.el b/ansible/roles/emacs/files/base-global-keys.el new file mode 100644 index 0000000..9f479d1 --- /dev/null +++ b/ansible/roles/emacs/files/base-global-keys.el @@ -0,0 +1,12 @@ +;; Add your keys here, as such + +;; Disable the suspend frame hotkeys +(global-unset-key (kbd "C-z")) +(global-unset-key (kbd "C-x C-z")) + +;; dabbrev-expand. Seems to be some sort of dumb-expand. Accidentally hitting it when trying to use M-? +(global-unset-key (kbd "M-/")) + +(global-set-key (kbd "C-x g l") 'generate-github-link) + +(provide 'base-global-keys) diff --git a/ansible/roles/emacs/files/base-theme.el b/ansible/roles/emacs/files/base-theme.el new file mode 100644 index 0000000..c1fd9a0 --- /dev/null +++ b/ansible/roles/emacs/files/base-theme.el @@ -0,0 +1,15 @@ +;; Set theme +(load-theme 'tango-dark t) +(set-face-attribute 'default nil :background "black") +;; Bright yellow highlighting for selected region +(set-face-attribute 'region nil :background "#ffff50" :foreground "black") +;; Bright green cursor to distinguish from yellow region +(set-face-attribute 'cursor nil :background "#ccff66") +;; Hightlight the current line +(set-face-attribute 'line-number-current-line nil :foreground "white") +;; Set default font +(set-face-attribute 'default nil :height 100 :width 'regular :weight 'regular :family "Cascadia Mono") +;; Set fallback font for unicode glyphs +(set-fontset-font t 'emoji (font-spec :name "Noto Color Emoji") nil 'prepend) + +(provide 'base-theme) diff --git a/ansible/roles/emacs/files/base.el b/ansible/roles/emacs/files/base.el new file mode 100644 index 0000000..bc10fb4 --- /dev/null +++ b/ansible/roles/emacs/files/base.el @@ -0,0 +1,106 @@ +(package-initialize) +(add-to-list 'package-archives + '("melpa" . "https://melpa.org/packages/") + ) + +(when (not package-archive-contents) + (package-refresh-contents)) + +(unless (package-installed-p 'use-package) + (package-install 'use-package)) + +(use-package auto-package-update + :ensure t + :config + (setq auto-package-update-delete-old-versions t + auto-package-update-interval 14) + (auto-package-update-maybe)) + +(defconst private-dir (expand-file-name "private" user-emacs-directory)) +(defconst temp-dir (format "%s/cache" private-dir) + "Hostname-based elisp temp directories") + +;; Emacs customizations +(setq-default + inhibit-startup-screen t + initial-scratch-message nil + ;; Send prompts to mini-buffer not the GUI + use-dialog-box nil + confirm-nonexistent-file-or-buffer t + save-interprogram-paste-before-kill t + mouse-yank-at-point t + require-final-newline t + visible-bell nil + ring-bell-function 'ignore + custom-file "~/.emacs.d/.custom.el" + ;; http://ergoemacs.org/emacs/emacs_stop_cursor_enter_prompt.html + minibuffer-prompt-properties + '(read-only t point-entered minibuffer-avoid-prompt face minibuffer-prompt) + + ;; Disable non selected window highlight + cursor-in-non-selected-windows nil + highlight-nonselected-windows nil + ;; PATH + exec-path (append exec-path '("/usr/local/bin/")) + indent-tabs-mode nil + tab-width 4 + inhibit-startup-message t + fringes-outside-margins t + x-select-enable-clipboard t + use-package-always-ensure t + ispell-program-name "aspell" + browse-url-browser-function 'browse-url-generic + browse-url-generic-program "firefox-developer-edition" + frame-title-format '("" invocation-name ": "(:eval (if (buffer-file-name) + (abbreviate-file-name (buffer-file-name)) + "%b"))) + ;; mouse-wheel-progressive-speed nil ;; Don't accelerate mouse wheel + ;; mouse-wheel-scroll-amount '(5 ((shift) . 3)) + use-short-answers t + package-native-compile t + delete-selection-mode t + ) + +(defun assert-directory (p) + (unless (file-exists-p p) (make-directory p t)) + p + ) +(assert-directory (concat temp-dir "/auto-save-list/")) +(setq autoload-directory (concat user-emacs-directory (file-name-as-directory "elisp") (file-name-as-directory "autoload"))) +(add-to-list 'load-path (assert-directory autoload-directory)) + + + +;; Bookmarks +(setq + ;; persistent bookmarks + bookmark-save-flag t + bookmark-default-file (concat temp-dir "/bookmarks")) + +;; Backups enabled, use nil to disable +(setq + history-length 1000 + backup-inhibited nil + make-backup-files nil + auto-save-default nil + auto-save-list-file-name (concat temp-dir "/autosave") + create-lockfiles nil + backup-directory-alist `((".*" . ,(concat temp-dir "/backup/"))) + auto-save-file-name-transforms `((".*" ,(concat temp-dir "/auto-save-list/") t))) + +;; Disable toolbar & menubar +(menu-bar-mode -1) +(when (fboundp 'tool-bar-mode) + (tool-bar-mode -1)) +(when ( fboundp 'scroll-bar-mode) + (scroll-bar-mode -1)) + +(context-menu-mode +1) + +;; Delete trailing whitespace before save +(add-hook 'before-save-hook 'delete-trailing-whitespace) + +(use-package diminish) + +(provide 'base) +;;; base ends here diff --git a/ansible/roles/emacs/files/common-lsp.el b/ansible/roles/emacs/files/common-lsp.el new file mode 100644 index 0000000..860f25b --- /dev/null +++ b/ansible/roles/emacs/files/common-lsp.el @@ -0,0 +1,41 @@ +(use-package eglot + :commands (eglot eglot-ensure) + :bind (:map eglot-mode-map + ;; M-. + ;; ([remap xref-find-definitions] . lsp-ui-peek-find-definitions) + ;; M-? + ;; ([remap xref-find-references] . lsp-ui-peek-find-references) + ("C-c C-a" . eglot-code-actions) + ) + :hook ( + (eglot-managed-mode . (lambda () + (when (eglot-managed-p) + (corfu-mode +1) + ) + )) + ) + :config + ;; Increase garbage collection threshold for performance (default 800000) + (setq gc-cons-threshold 100000000) + + ;; Increase amount of data read from processes, default 4k + (when (>= emacs-major-version 27) + (setq read-process-output-max (* 1024 1024)) ;; 1mb + ) + + (set-face-attribute 'eglot-highlight-symbol-face nil :background "#0291a1" :foreground "black") + (set-face-attribute 'eglot-mode-line nil :inherit 'mode-line :bold nil) + + (use-package consult-eglot + :bind ( + :map eglot-mode-map + ;; C-M-. + ([remap xref-find-apropos] . #'consult-eglot-symbols) + ) + ) + :custom + (eglot-autoshutdown t "Shut down server when last buffer is killed.") + (eglot-sync-connect 0 "Don't block on language server starting.") + ) + +(provide 'common-lsp) diff --git a/ansible/roles/emacs/files/init.el b/ansible/roles/emacs/files/init.el new file mode 100644 index 0000000..69bedab --- /dev/null +++ b/ansible/roles/emacs/files/init.el @@ -0,0 +1,33 @@ +(add-to-list 'load-path (concat user-emacs-directory "elisp")) + +(require 'base) +(require 'base-theme) +(require 'base-extensions) +(require 'base-functions) +(require 'base-global-keys) + +(require 'util-vertico) + +(require 'lang-python) + +(require 'lang-javascript) + +(require 'lang-rust) + +(require 'lang-yaml) + +(require 'lang-org) + +(require 'lang-bash) + +(require 'lang-markdown) + +(require 'lang-lua) + +(require 'lang-terraform) + +(require 'lang-go) + +(require 'lang-c) + +(load-directory autoload-directory) diff --git a/ansible/roles/emacs/files/lang-bash.el b/ansible/roles/emacs/files/lang-bash.el new file mode 100644 index 0000000..b6c73d2 --- /dev/null +++ b/ansible/roles/emacs/files/lang-bash.el @@ -0,0 +1,12 @@ +(require 'common-lsp) +(require 'util-tree-sitter) + +(use-package sh-mode + :ensure nil + :commands sh-mode + :hook ((sh-mode . flycheck-mode) + (sh-mode . tree-sitter-hl-mode) + ) + ) + +(provide 'lang-bash) diff --git a/ansible/roles/emacs/files/lang-c.el b/ansible/roles/emacs/files/lang-c.el new file mode 100644 index 0000000..dcebab6 --- /dev/null +++ b/ansible/roles/emacs/files/lang-c.el @@ -0,0 +1,32 @@ +(require 'common-lsp) +(require 'util-tree-sitter) + +;; To generate a compilation database for the language server, run: +;; intercept-build13 --append make +;; +;; Output: compile_commands.json + +(defun use-clangd () + "Configure eglot to use clangd." + (eglot-ensure) + (defclass my/eglot-c (eglot-lsp-server) () + :documentation + "Own eglot server class.") + + (add-to-list 'eglot-server-programs + '(cc-mode . (my/eglot-c "clangd13"))) + + (tree-sitter-hl-mode) + ) + +(use-package cc-mode + ;; c-mode c++-mode objc-mode java-mode idl-mode pike-mode awk-mode + :commands (c-mode c++-mode) + :pin manual + :hook ( + (c-mode . use-clangd) + (c++-mode . use-clangd) + ) + ) + +(provide 'lang-c) diff --git a/ansible/roles/emacs/files/lang-go.el b/ansible/roles/emacs/files/lang-go.el new file mode 100644 index 0000000..0e77df2 --- /dev/null +++ b/ansible/roles/emacs/files/lang-go.el @@ -0,0 +1,15 @@ +(require 'common-lsp) +(require 'util-tree-sitter) + +(use-package go-mode + :commands go-mode + :hook ( + (go-mode . (lambda () + (eglot-ensure) + (tree-sitter-hl-mode +1) + )) + ;; (before-save . lsp-format-buffer) + ) + ) + +(provide 'lang-go) diff --git a/ansible/roles/emacs/files/lang-javascript.el b/ansible/roles/emacs/files/lang-javascript.el new file mode 100644 index 0000000..ca9b527 --- /dev/null +++ b/ansible/roles/emacs/files/lang-javascript.el @@ -0,0 +1,61 @@ +(require 'common-lsp) +(require 'util-tree-sitter) + +(use-package js + :mode ( + ("\\.js\\'" . js-mode) + ("\\.json\\'" . js-mode) + ) + :commands js-mode + :pin manual + :hook ( + (find-file . (lambda () (when (string= (file-name-extension buffer-file-name) "json") (add-hook 'before-save-hook 'json-fmt-jq nil 'local)))) + (js-mode . (lambda () + (tree-sitter-hl-mode +1) + ) + ) + ) + :config + (setq js-indent-level 2) + ) + +(use-package typescript-mode + :mode ( + ("\\.ts\\'" . typescript-mode) + ("\\.tsx\\'" . typescript-mode) + ) + :hook ( + (typescript-mode . (lambda () + ;; (lsp-register-client + ;; (make-lsp-client :new-connection (lsp-stdio-connection '("deno" "lsp" --compat --unstable --allow-read)) + ;; :major-modes '(typescript-mode) + ;; :server-id 'typescript-ls)) + (eglot-ensure) + (tree-sitter-hl-mode +1) + )) + ;; (before-save . lsp-format-buffer) + ) +) + +(defun json-fmt-jq () + "Run jq." + (run-command-on-buffer "jq" "--monochrome-output" ".") + ) + +(use-package web-mode + :mode (("\\.dust\\'" . web-mode) + ) + :config + (setq web-mode-markup-indent-offset 2) + (setq web-mode-enable-current-element-highlight t) + ) + +;; Define a custom mode for dust so that org-mode handle #+BEGIN_SRC dust blocks +(define-derived-mode dust-mode web-mode "WebDust" + "Major mode for editing dust templates in web-mode." + (web-mode) + (web-mode-set-engine "dust") + ;; (setq web-mode-content-type "html") + ) + +(provide 'lang-javascript) diff --git a/ansible/roles/emacs/files/lang-lua.el b/ansible/roles/emacs/files/lang-lua.el new file mode 100644 index 0000000..7403f05 --- /dev/null +++ b/ansible/roles/emacs/files/lang-lua.el @@ -0,0 +1,21 @@ +(defun lua-format-buffer () + "Run stylua." + (interactive) + (run-command-on-buffer "stylua" "--search-parent-directories" "--stdin-filepath" buffer-file-name "-") + ) + +(use-package lua-mode + :mode + (("\\.lua\\'" . lua-mode) + ("\\.rockspec\\'" . lua-mode)) + :commands lua-mode + :hook ( + (lua-mode . (lambda () + (add-hook 'before-save-hook 'lua-format-buffer nil 'local) + )) + ) + :custom + (lua-indent-level 4) + ) + +(provide 'lang-lua) diff --git a/ansible/roles/emacs/files/lang-markdown.el b/ansible/roles/emacs/files/lang-markdown.el new file mode 100644 index 0000000..48c6882 --- /dev/null +++ b/ansible/roles/emacs/files/lang-markdown.el @@ -0,0 +1,12 @@ +(use-package markdown-mode + :ensure t + :commands (markdown-mode gfm-mode) + :mode (("README\\.md\\'" . gfm-mode) + ("\\.md\\'" . markdown-mode) + ("\\.markdown\\'" . markdown-mode)) + :init (setq markdown-command "multimarkdown")) + +;; For code block editing +(use-package edit-indirect) + +(provide 'lang-markdown) diff --git a/ansible/roles/emacs/files/lang-org.el b/ansible/roles/emacs/files/lang-org.el new file mode 100644 index 0000000..444bcbf --- /dev/null +++ b/ansible/roles/emacs/files/lang-org.el @@ -0,0 +1,77 @@ +(use-package org + :ensure nil + :commands org-mode + :bind ( + ("C-c l" . org-store-link) + ("C-c a" . org-agenda) + ) + :hook ( + (org-mode . (lambda () + (org-indent-mode +1) + )) + ) + :config + (require 'org-tempo) + (setq org-export-latex-listings t) + (setq org-startup-truncated nil) + (setq org-startup-folded nil) + (setq org-src-fontify-natively t + org-src-tab-acts-natively t + org-confirm-babel-evaluate nil + ) + + ;; Only interpret _ and ^ and sub and superscripts if they're of the form _{subscript} and ^{superscript} + (setq org-export-with-sub-superscripts '{}) + ;; Don't include a "validate" link at the bottom of html export + (setq org-html-validation-link nil) + + + (setq org-latex-listings 'minted) + (setq org-latex-minted-options '(("breaklines" "true") + ("breakanywhere" "true") + ("bgcolor" "mintedbg") ("frame" "single") ("framesep" "6pt") ("fontsize" "\\footnotesize"))) + + ;; TODO: There is an option to set the compiler, could be better than manually doing this here https://orgmode.org/manual/LaTeX_002fPDF-export-commands.html + ;; (setq org-latex-compiler "lualatex") + (setq org-latex-pdf-process + '("lualatex -shell-escape -interaction nonstopmode -output-directory %o %f" + "lualatex -shell-escape -interaction nonstopmode -output-directory %o %f" + "lualatex -shell-escape -interaction nonstopmode -output-directory %o %f")) + (add-to-list 'org-latex-packages-alist '("cache=false" "minted")) + (add-to-list 'org-latex-packages-alist '("" "svg")) + (add-to-list 'org-latex-packages-alist '("margin=2cm" "geometry" nil)) + + (add-to-list 'org-src-lang-modes '("dot" . "graphviz-dot")) + + (org-babel-do-load-languages 'org-babel-load-languages + '((shell . t) + (js . t) + (emacs-lisp . t) + (python . t) + (dot . t) + (css . t) + (gnuplot . t) + (sqlite . t) + )) + + (require 'color) + + (let ((bg (face-attribute 'default :background))) + (custom-set-faces + `(org-block ((t (:inherit default :background ,(color-lighten-name bg 15) :extend ,t)))) + `(org-block-begin-line ((t (:inherit default :background ,"#472300" :extend ,t)))) + `(org-block-end-line ((t (:inherit default :background ,"#472300" :extend ,t)))) + )) + ) + +(use-package org-bullets + :commands org-bullets-mode + :hook (org-mode . org-bullets-mode) + :bind ((:map org-mode-map ([remap fill-paragraph] . unfill-toggle))) + ) + +(use-package gnuplot-mode) +(use-package gnuplot) +(use-package graphviz-dot-mode) + +(provide 'lang-org) diff --git a/ansible/roles/emacs/files/lang-python.el b/ansible/roles/emacs/files/lang-python.el new file mode 100644 index 0000000..3162b62 --- /dev/null +++ b/ansible/roles/emacs/files/lang-python.el @@ -0,0 +1,77 @@ +(require 'common-lsp) +(require 'util-tree-sitter) + +(defun python-backspace (arg) + "Special handling of python backspace." + (interactive "*p") + (if mark-active + (backward-delete-char-untabify arg) + (python-indent-dedent-line-backspace arg) + ) + ) + +(defun locate-venv-poetry () + "Find a poetry venv." + (run-command-in-directory nil "poetry" "env" "info" "-p") + ) + +(defun locate-pyproject-directory () + "Adapt lsp-python-ms for poetry." + (let ((pypoetry-file (locate-dominating-file (buffer-file-name) "pyproject.toml"))) + pypoetry-file + ) + ) + + +(defun python-fmt () + "format python." + (python-fmt-black) + (python-fmt-isort) + ) + +(defun python-fmt-black () + "Run black." + (run-command-on-buffer "black" "--quiet" "--fast" "-") + ) + +(defun python-fmt-isort () + "Run isort." + (run-command-on-buffer "isort" "-") + ) + +(defun add-poetry-venv-to-path () + "Add the bin folder in the poetry venv to exec-path." + (let ( + (venv-path (locate-venv-poetry)) + ) + (when venv-path + (make-local-variable 'exec-path) + (add-to-list 'exec-path (concat venv-path "/bin")) + ) + ) + ) + +(use-package python + :mode ("\\.py\\'" . python-mode) + :commands python-mode + :pin manual + :hook ( + (python-mode . (lambda () + (when (executable-find "poetry") + (add-poetry-venv-to-path) + (let ((venv (locate-venv-poetry))) (when venv + (setq eglot-workspace-configuration + (list (cons ':python (list ':venvPath venv ':pythonPath (concat venv "/bin/python"))))) + )) + ) + (eglot-ensure) + + (add-hook 'before-save-hook 'python-fmt nil 'local) + (tree-sitter-hl-mode +1) + )) + ) + :bind ((:map python-mode-map ([backspace] . python-backspace)) + ) + ) + +(provide 'lang-python) diff --git a/ansible/roles/emacs/files/lang-rust.el b/ansible/roles/emacs/files/lang-rust.el new file mode 100644 index 0000000..fa2923c --- /dev/null +++ b/ansible/roles/emacs/files/lang-rust.el @@ -0,0 +1,31 @@ +(require 'common-lsp) +(require 'util-tree-sitter) + +(defun locate-rust-analyzer () + "Find rust-analyzer through rustup." + (run-command-in-directory nil "rustup" "which" "rust-analyzer") + ) + +(use-package rust-mode + :mode "\\.rs\\'" + :hook ( + (rust-mode . (lambda () + (eglot-ensure) + (let ((rust-analyzer-command (locate-rust-analyzer))) + (when rust-analyzer-command + (add-to-list 'eglot-server-programs (cons 'rust-mode (list rust-analyzer-command))) + ) + ) + (when-linux + (tree-sitter-hl-mode +1) + ) + (add-hook 'before-save-hook 'eglot-format-buffer nil 'local) + )) + ) + :config + ;; Add keybindings for interacting with Cargo + (use-package cargo + :hook (rust-mode . cargo-minor-mode)) + ) + +(provide 'lang-rust) diff --git a/ansible/roles/emacs/files/lang-terraform.el b/ansible/roles/emacs/files/lang-terraform.el new file mode 100644 index 0000000..2383576 --- /dev/null +++ b/ansible/roles/emacs/files/lang-terraform.el @@ -0,0 +1,39 @@ +(require 'common-lsp) +(require 'util-tree-sitter) + +(defun terraform-fmt () + "Run terraform fmt." + (run-command-on-buffer "terraform" "fmt" "-") + ) + + +(use-package hcl-mode + :mode (("\\.hcl\\'" . hcl-mode)) + :commands hcl-mode + :custom (hcl-indent-level 2) + :hook ( + (hcl-mode . (lambda () (unless (derived-mode-p 'terraform-mode) (add-hook 'before-save-hook 'terraform-fmt nil 'local)))) + (hcl-mode . tree-sitter-hl-mode) + ) + ) + +(use-package terraform-mode + :mode (("\\.tf\\'" . terraform-mode) + ("\\.tfvars\\'" . terraform-mode)) + :commands terraform-mode + :custom (terraform-indent-level 2) + :hook ( + (terraform-mode . (lambda () + (eglot-ensure) + (defclass my/eglot-terraform (eglot-lsp-server) () + :documentation + "Own eglot server class.") + + (add-to-list 'eglot-server-programs + '(terraform-mode . (my/eglot-terraform "terraform-ls" "serve"))) + (add-hook 'before-save-hook 'eglot-format-buffer nil 'local) + )) + ) + ) + +(provide 'lang-terraform) diff --git a/ansible/roles/emacs/files/lang-yaml.el b/ansible/roles/emacs/files/lang-yaml.el new file mode 100644 index 0000000..25cea73 --- /dev/null +++ b/ansible/roles/emacs/files/lang-yaml.el @@ -0,0 +1,35 @@ +(defun yaml-backspace (arg) + "Special handling of yaml backspace." + (interactive "*p") + (if mark-active + (backward-delete-char-untabify arg) + (yaml-electric-backspace arg) + ) + ) + +(defun yaml-format-buffer () + "Run prettier." + (interactive) + (run-command-on-buffer "prettier" "--stdin-filepath" buffer-file-name) + ) + +(use-package yaml-mode + :mode + (("playbook\\.tmp\\'" . yaml-mode) + ("environments/[^/]*/group_vars/[^/]*\\'" . yaml-mode) + ("environments/[^/]*/host_vars/[^/]*\\'" . yaml-mode) + ) + :hook ( + (yaml-mode . (lambda () + (setq eglot-workspace-configuration + (list (cons ':yaml (list ':hover :json-false :validate :json-false :completion t)))) + (eglot-ensure) + (add-hook 'before-save-hook 'yaml-format-buffer nil 'local) + )) + ) + :bind ( + (:map yaml-mode-map ([backspace] . yaml-backspace)) + ) + ) + +(provide 'lang-yaml) diff --git a/ansible/roles/emacs/files/util-tree-sitter.el b/ansible/roles/emacs/files/util-tree-sitter.el new file mode 100644 index 0000000..2419b2d --- /dev/null +++ b/ansible/roles/emacs/files/util-tree-sitter.el @@ -0,0 +1,7 @@ +(use-package tree-sitter + :commands (tree-sitter-hl-mode) + :config + (use-package tree-sitter-langs) + ) + +(provide 'util-tree-sitter) diff --git a/ansible/roles/emacs/files/util-vertico.el b/ansible/roles/emacs/files/util-vertico.el new file mode 100644 index 0000000..280e2ef --- /dev/null +++ b/ansible/roles/emacs/files/util-vertico.el @@ -0,0 +1,61 @@ +(defun my/minibuffer-delete (arg) + "When looking for files, go up an entire directory with the backspace button if theres no text after the directory." + (interactive "p") + (if minibuffer-completing-file-name + (if (string-match-p ".*/$" (minibuffer-contents)) + (vertico-directory-delete-word arg) + (vertico-directory-delete-char arg)) + (delete-backward-char arg))) + +(use-package vertico + :config + (vertico-mode) + (vertico-mouse-mode) + + ;; Remove prefix when switching to tilde or root ("/") + (setq file-name-shadow-properties '(invisible t intangible t)) + (file-name-shadow-mode +1) + + (set-face-attribute 'vertico-current nil :inherit nil :background "#383b01") + :custom + (vertico-count 20) + ) + +;; Create an ivy-like experience when selecting files. +(use-package vertico-directory + :after vertico + :ensure nil + :bind ( :map vertico-map + ("RET" . vertico-directory-enter) + :map minibuffer-local-map + ("DEL" . my/minibuffer-delete) + ) + ) + +(use-package consult + :custom + (completion-in-region-function #'consult-completion-in-region) + (xref-show-xrefs-function #'consult-xref) + (xref-show-definitions-function #'consult-xref) + (consult-project-root-function #'deadgrep--project-root) + :bind ( + ("C-. s" . consult-ripgrep) + ("C-s" . consult-line) + ("M-g g" . consult-goto-line) + ("C-. e" . consult-flymake) + ) + ) + +(use-package corfu + :commands (corfu-mode global-corfu-mode) + :custom + (corfu-auto t) + ) + +(use-package marginalia + :config (marginalia-mode)) + +(use-package orderless + :custom (completion-styles '(orderless))) + +(provide 'util-vertico) diff --git a/ansible/roles/emacs/tasks/peruser.yaml b/ansible/roles/emacs/tasks/peruser.yaml index 111e886..0a46ec4 100644 --- a/ansible/roles/emacs/tasks/peruser.yaml +++ b/ansible/roles/emacs/tasks/peruser.yaml @@ -1,26 +1,54 @@ - include_role: name: per_user -# - name: Create directories -# file: -# name: "{{ account_homedir.stdout }}/{{ item }}" -# state: directory -# mode: 0700 -# owner: "{{ account_name.stdout }}" -# group: "{{ group_name.stdout }}" -# loop: -# - ".config/foo" +- name: Create directories + file: + name: "{{ account_homedir.stdout }}/{{ item }}" + state: directory + mode: 0700 + owner: "{{ account_name.stdout }}" + group: "{{ group_name.stdout }}" + loop: + - ".emacs.d/elisp" -# - name: Copy files -# copy: -# src: "files/{{ item.src }}" -# dest: "{{ account_homedir.stdout }}/{{ item.dest }}" -# mode: 0600 -# owner: "{{ account_name.stdout }}" -# group: "{{ group_name.stdout }}" -# loop: -# - src: foo.conf -# dest: .config/foo/foo.conf +- name: Configure dotfiles + copy: + src: "files/{{ item.src }}" + dest: "{{ account_homedir.stdout }}/{{ item.dest }}" + mode: 0600 + owner: "{{ account_name.stdout }}" + group: "{{ group_name.stdout }}" + loop: + - src: init.el + dest: .emacs.d/init.el + +- name: Configure elisp files + copy: + src: "files/{{ item }}" + dest: "{{ account_homedir.stdout }}/.emacs.d/elisp/{{ item }}" + mode: 0600 + owner: "{{ account_name.stdout }}" + group: "{{ group_name.stdout }}" + loop: + - base-extensions.el + - base-functions.el + - base-global-keys.el + - base-theme.el + - base.el + - common-lsp.el + - lang-bash.el + - lang-c.el + - lang-go.el + - lang-javascript.el + - lang-lua.el + - lang-markdown.el + - lang-org.el + - lang-python.el + - lang-rust.el + - lang-terraform.el + - lang-yaml.el + - util-vertico.el + - util-tree-sitter.el - import_tasks: tasks/peruser_freebsd.yaml when: 'os_flavor == "freebsd"'