├── packages ├── setup-html-to-hiccup.el ├── setup-yaml-mode.el ├── setup-terraform-mode.el ├── setup-dockerfile-mode.el ├── setup-markdown-mode.el ├── setup-expand-region.el ├── setup-restclient.el ├── setup-js-mode.el ├── setup-css-mode.el ├── setup-whitespace-cleanup-mode.el ├── setup-prodigy.el ├── setup-diff-hl.el ├── setup-smartparens.el ├── setup-babashka.el ├── setup-multiple-cursors.el ├── setup-which-key.el ├── setup-flycheck.el ├── setup-deadgrep.el ├── setup-zoom-frm.el ├── setup-perspective.el ├── setup-undo-fu.el ├── setup-tagedit.el ├── setup-dired.el ├── setup-org-mode.el ├── setup-projectile.el ├── setup-consult.el ├── setup-kaocha-runner.el ├── setup-lsp-mode.el ├── setup-yasnippet.el ├── setup-vertico.el ├── setup-rgrep.el ├── setup-paredit.el ├── setup-clj-refactor.el ├── setup-makefile-mode.el ├── setup-magit.el ├── setup-cider.el ├── setup-clojure-mode.el └── setup-hippie.el ├── snippets ├── clojure-mode │ ├── gave.yasnippet │ ├── pakke.yasnippet │ ├── mc.yasnippet │ ├── dquery.yasnippet │ ├── dquery_entities.yasnippet │ └── mtdo.yasnippet ├── emacs-lisp-mode │ ├── req │ ├── autoload │ ├── ends │ ├── use-package.yasnippet │ └── provide.yasnippet └── js-mode │ ├── try │ ├── for │ ├── for-in │ ├── function.yasnippet │ └── immediately-invoked-function-expression.yasnippet ├── users ├── teodorlu │ ├── mikrobloggeriet.el │ ├── teodorlu-haskell.el │ ├── zig.el │ ├── rust.el │ ├── neil-quickadd.el │ ├── tplay.el │ └── teodorlu.el ├── magnars │ ├── my-prodigy-processes.el │ ├── magnars.el │ ├── adventur.el │ └── what-the-emacs-d.el ├── mathias │ └── mathias.el ├── christian │ └── christian.el └── sigmund │ ├── sigmund.el │ └── jetbrains-mono.el ├── .gitignore ├── settings ├── packages.el ├── user-specific-settings.el ├── setup-straight.el ├── extra-keybindings.el ├── fast-startup.el ├── appearance.el ├── tooling.el ├── norwegian-mac.el ├── significant-other.el ├── indented-yank.el ├── mattilsynet.el ├── windows.el ├── buffers.el ├── navigation.el ├── i18n-edn.el ├── clj-auto-refer-mode.el ├── cider-run.el ├── sane-defaults.el ├── css-completions.el ├── clj-clean-namespace.el ├── multifiles.el ├── editing.el └── matnyttig.el ├── todo.org ├── default-black-theme.el ├── init.el ├── custom.el ├── .mc-lists.el └── README.md /packages/setup-html-to-hiccup.el: -------------------------------------------------------------------------------- 1 | (use-package html-to-hiccup 2 | :defer t) 3 | -------------------------------------------------------------------------------- /packages/setup-yaml-mode.el: -------------------------------------------------------------------------------- 1 | (use-package yaml-mode 2 | :defer t) 3 | 4 | (provide 'setup-yaml-mode) 5 | -------------------------------------------------------------------------------- /snippets/clojure-mode/gave.yasnippet: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: gave 3 | # key: gav 4 | # -- 5 | 🎁 -------------------------------------------------------------------------------- /snippets/clojure-mode/pakke.yasnippet: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: pakke 3 | # key: pak 4 | # -- 5 | 📦 -------------------------------------------------------------------------------- /snippets/emacs-lisp-mode/req: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: req 3 | # key: req 4 | # -- 5 | (require '$0) -------------------------------------------------------------------------------- /packages/setup-terraform-mode.el: -------------------------------------------------------------------------------- 1 | (use-package terraform-mode 2 | :defer t) 3 | 4 | (provide 'setup-terraform-mode) 5 | -------------------------------------------------------------------------------- /snippets/emacs-lisp-mode/autoload: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: autoload 3 | # key: auto 4 | # -- 5 | ;;;###autoload -------------------------------------------------------------------------------- /packages/setup-dockerfile-mode.el: -------------------------------------------------------------------------------- 1 | (use-package dockerfile-mode 2 | :defer t) 3 | 4 | (provide 'setup-dockerfile-mode) 5 | -------------------------------------------------------------------------------- /snippets/js-mode/try: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: try 3 | # key: try 4 | # -- 5 | try { 6 | $0 7 | } catch (e) { 8 | } -------------------------------------------------------------------------------- /snippets/js-mode/for: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: for 3 | # key: for 4 | # -- 5 | for (var i = 0, l = ${num}; i < l; i++) {$0} -------------------------------------------------------------------------------- /snippets/clojure-mode/mc.yasnippet: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: MT designsystemet 3 | # key: mc 4 | # -- 5 | :class (mtds/classes $0) -------------------------------------------------------------------------------- /snippets/emacs-lisp-mode/ends: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: ends 3 | # key: ends 4 | # -- 5 | ;;; `(buffer-file-name-body)`.el ends here$0 -------------------------------------------------------------------------------- /snippets/emacs-lisp-mode/use-package.yasnippet: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: use-package 3 | # key: up 4 | # -- 5 | (use-package $0) 6 | 7 | -------------------------------------------------------------------------------- /snippets/emacs-lisp-mode/provide.yasnippet: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: provide 3 | # key: pro 4 | # -- 5 | (provide '`(buffer-file-name-body)`)$0 -------------------------------------------------------------------------------- /snippets/js-mode/for-in: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: for-in 3 | # key: forin 4 | # -- 5 | for (var key in ${1:obj}) { 6 | if ($1.hasOwnProperty(key)) { 7 | $0 8 | } 9 | } -------------------------------------------------------------------------------- /snippets/js-mode/function.yasnippet: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: function 3 | # key: f 4 | # -- 5 | function ${1:`(snippet--function-name)`}($2) {$0}`(snippet--function-punctuation)` -------------------------------------------------------------------------------- /snippets/clojure-mode/dquery.yasnippet: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: Datomic query 3 | # key: dq 4 | # -- 5 | (d/q '[:find [?e ...] 6 | :where 7 | [?e $0]] 8 | $1) -------------------------------------------------------------------------------- /users/teodorlu/mikrobloggeriet.el: -------------------------------------------------------------------------------- 1 | (defun mikrobloggeriet-deploy () 2 | (interactive) 3 | (let ((default-directory "~/dev/iterate/mikrobloggeriet")) 4 | (shell-command "garden deploy"))) 5 | -------------------------------------------------------------------------------- /packages/setup-markdown-mode.el: -------------------------------------------------------------------------------- 1 | (use-package markdown 2 | :ensure nil 3 | :defer t 4 | 5 | :init 6 | (add-hook 'markdown-mode-hook 'auto-fill-mode)) 7 | 8 | (provide 'setup-markdown-mode) 9 | -------------------------------------------------------------------------------- /snippets/js-mode/immediately-invoked-function-expression.yasnippet: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: immediately-invoked-function-expression 3 | # key: ff 4 | # -- 5 | (function () { 6 | $0 7 | }()); 8 | -------------------------------------------------------------------------------- /packages/setup-expand-region.el: -------------------------------------------------------------------------------- 1 | (use-package expand-region 2 | :straight t 3 | :defer t 4 | :bind (("C-@" . er/expand-region) 5 | ("C-'" . er/expand-region))) 6 | 7 | (provide 'setup-expand-region) 8 | -------------------------------------------------------------------------------- /packages/setup-restclient.el: -------------------------------------------------------------------------------- 1 | (use-package restclient 2 | :defer t) 3 | 4 | (add-to-list 'auto-mode-alist '("\\.restclient$" . restclient-mode)) 5 | (add-hook 'restclient-mode-hook 'turn-on-smartparens-mode) 6 | 7 | (provide 'setup-restclient) 8 | -------------------------------------------------------------------------------- /snippets/clojure-mode/dquery_entities.yasnippet: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: Datomic query with enitities 3 | # key: dqe 4 | # -- 5 | (d/q-entities '[:find [?e ...] 6 | :where 7 | [?e $0]] 8 | $1) -------------------------------------------------------------------------------- /snippets/clojure-mode/mtdo.yasnippet: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: MT do 3 | # key: mtdo 4 | # -- 5 | (do 6 | (def ctx (:system/ctx integrant.repl.state/system)) 7 | (def app-db (d/db (:app/conn ctx))) 8 | (def matrikkel-db (d/db (:matrikkel/conn ctx)))) -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /auto-save-list 2 | /backups 3 | /elpa 4 | /recentf 5 | /transient 6 | /.lsp-session-v1 7 | /projects 8 | /cache/ 9 | /projectile-bookmarks.eld 10 | /history 11 | /straight/ 12 | /tramp 13 | /.cache 14 | /url/ 15 | /workspace/ 16 | /eshell 17 | projectile.cache -------------------------------------------------------------------------------- /users/teodorlu/teodorlu-haskell.el: -------------------------------------------------------------------------------- 1 | ;; Rudimentary support for Haskell 2 | ;; 3 | ;; Requires an LSP binary: 4 | ;; 5 | ;; brew install haskell-language-server 6 | 7 | (use-package haskell-mode) 8 | (use-package lsp-haskell) 9 | (require 'lsp-mode) 10 | (add-hook 'haskell-mode-hook #'lsp) 11 | (add-hook 'haskell-literate-mode-hook #'lsp) 12 | -------------------------------------------------------------------------------- /packages/setup-js-mode.el: -------------------------------------------------------------------------------- 1 | (use-package js-mode 2 | :ensure nil 3 | :defer t 4 | 5 | :custom 6 | (js-indent-level 2)) 7 | 8 | ;; Add CommonJS and ES6 Module JS extensions to the js-mode list 9 | (add-to-list 'auto-mode-alist '("\\.cjs$" . js-mode)) 10 | (add-to-list 'auto-mode-alist '("\\.mjs$" . js-mode)) 11 | 12 | (provide 'setup-js-mode) 13 | -------------------------------------------------------------------------------- /settings/packages.el: -------------------------------------------------------------------------------- 1 | (require 'package) 2 | (add-to-list 'package-archives '("melpa" . "http://melpa.org/packages/") t) 3 | (package-initialize) 4 | 5 | (setq packages-dir (expand-file-name "packages" user-emacs-directory)) 6 | (add-to-list 'load-path packages-dir) 7 | 8 | (require 'use-package-ensure) 9 | (setq use-package-always-ensure t) 10 | 11 | (provide 'packages) 12 | -------------------------------------------------------------------------------- /packages/setup-css-mode.el: -------------------------------------------------------------------------------- 1 | (add-hook 'css-mode-hook 'lsp) 2 | 3 | (setq css-fontify-colors nil) 4 | 5 | (use-package lsp-tailwindcss 6 | :init 7 | (setq lsp-tailwindcss-add-on-mode t) 8 | (setq lsp-css-lint-unknown-at-rules "ignore") ;; ignore @tailwind and @apply (and all other such errors, oops) 9 | ) 10 | 11 | (require 'lsp-tailwindcss) 12 | 13 | (provide 'setup-css-mode) 14 | -------------------------------------------------------------------------------- /packages/setup-whitespace-cleanup-mode.el: -------------------------------------------------------------------------------- 1 | ;; whitespace-cleanup-mode.el 2 | ;; 3 | ;; A minor mode that will intelligently call whitespace-cleanup before buffers 4 | ;; are saved. 5 | 6 | (use-package whitespace-cleanup-mode 7 | :diminish whitespace-cleanup-mode 8 | :defer 2 9 | :init 10 | (global-whitespace-cleanup-mode)) 11 | 12 | (provide 'setup-whitespace-cleanup-mode) 13 | -------------------------------------------------------------------------------- /users/magnars/my-prodigy-processes.el: -------------------------------------------------------------------------------- 1 | (when (file-exists-p "~/projects/www.parens-of-the-dead.com") 2 | (prodigy-define-service 3 | :name "www.parens-of-the-dead.com" 4 | :port 3334 5 | :command "lein" 6 | :args '("ring" "server-headless") 7 | :cwd "~/projects/www.parens-of-the-dead.com" 8 | :stop-signal 'sigkill 9 | :kill-process-buffer-on-stop t)) 10 | 11 | (provide 'my-prodigy-processes) 12 | -------------------------------------------------------------------------------- /users/mathias/mathias.el: -------------------------------------------------------------------------------- 1 | ;; theme 2 | ;; (use-package vscode-dark-plus-theme 3 | ;; :ensure t 4 | ;; :config 5 | ;; (load-theme 'vscode-dark-plus t)) 6 | 7 | (set-face-attribute 'default nil :height 150) 8 | 9 | ;; Zig 10 | (load 11 | (expand-file-name "users/teodorlu/zig.el" user-emacs-directory)) 12 | 13 | (global-set-key (kbd "M-") 'beginning-of-buffer) 14 | (global-set-key (kbd "M-") 'end-of-buffer) 15 | -------------------------------------------------------------------------------- /users/christian/christian.el: -------------------------------------------------------------------------------- 1 | ;; Custom own settings 2 | 3 | (require 'mattilsynet) 4 | 5 | ;; Configure pair-programming-mode 6 | ;; 7 | ;; Suggest these first: 8 | (setq my/pair-programming-usual-suspects '("Magnar Sveen " 9 | "Magnar Sveen ")) 10 | ;; 11 | ;; Don't suggest these: 12 | (setq my/pair-programming-myself '("Christian Johansen")) 13 | -------------------------------------------------------------------------------- /users/teodorlu/zig.el: -------------------------------------------------------------------------------- 1 | ;; Zig is still not released as a stable release. Certain Internet guides 2 | ;; recommending following the latest unstable Zig release. I choose to follow 3 | ;; the latest tagged release instead. For that, we can install with Homebrew. 4 | ;; 5 | ;; Prerequisites: 6 | ;; 7 | ;; brew install zig 8 | ;; brew install zls 9 | 10 | (use-package zig-mode) 11 | (require 'lsp-mode) 12 | (add-hook 'zig-mode-hook #'lsp-deferred) 13 | -------------------------------------------------------------------------------- /packages/setup-prodigy.el: -------------------------------------------------------------------------------- 1 | (use-package prodigy 2 | :defer t 3 | 4 | :bind (("C-c C-x C-p" . prodigy) 5 | (:map prodigy-mode-map 6 | ("q" . prodigy-quit))) 7 | 8 | :config 9 | (wrap-fullscreen prodigy)) 10 | 11 | (defun prodigy-quit () 12 | (interactive) 13 | (let ((prev my/previous-window-configuration)) 14 | (quit-window) 15 | (when prev (register-val-jump-to prev nil)))) 16 | 17 | (provide 'setup-prodigy) 18 | -------------------------------------------------------------------------------- /users/teodorlu/rust.el: -------------------------------------------------------------------------------- 1 | ;; Prerequisites: rustup and rust-analyzer.x 2 | ;; 3 | ;; 1. Install Rust toochain with rustup 4 | ;; 5 | ;; curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh 6 | ;; 7 | ;; Note: if you just press enter, .zprofile and .profile is automatically edited. 8 | ;; 9 | ;; 2. Install language server for Rust 10 | ;; 11 | ;; brew install rust-analyzer 12 | 13 | (use-package rust-mode) 14 | (add-hook 'rust-mode-hook #'lsp-deferred) 15 | -------------------------------------------------------------------------------- /packages/setup-diff-hl.el: -------------------------------------------------------------------------------- 1 | ;; diff-hl-mode 2 | ;; 3 | ;; Highlights uncommitted changes on the left side of the window (area also 4 | ;; known as the "gutter"), allows you to jump between and revert them 5 | ;; selectively. 6 | 7 | (use-package diff-hl 8 | :defer 2 9 | :config 10 | (global-diff-hl-mode) 11 | (add-hook 'magit-pre-refresh-hook 'diff-hl-magit-pre-refresh) 12 | (add-hook 'magit-post-refresh-hook 'diff-hl-magit-post-refresh)) 13 | 14 | (provide 'setup-diff-hl) 15 | -------------------------------------------------------------------------------- /packages/setup-smartparens.el: -------------------------------------------------------------------------------- 1 | ;; Smartparens 2 | ;; 3 | ;; Smartparens is a minor mode for dealing with pairs in Emacs. Like a 4 | ;; paredit-lite for non-lisps. 5 | 6 | (use-package smartparens 7 | :defer 2 8 | :init 9 | (require 'smartparens-config) 10 | (setq sp-autoescape-string-quote nil) 11 | (--each '(css-mode-hook 12 | js-mode-hook 13 | markdown-mode) 14 | (add-hook it 'turn-on-smartparens-mode))) 15 | 16 | (provide 'setup-smartparens) 17 | -------------------------------------------------------------------------------- /packages/setup-babashka.el: -------------------------------------------------------------------------------- 1 | (defun babashka-tasks () 2 | (s-lines (s-trim (shell-command-to-string (concat "bb -e " (shell-quote-argument "(run! println (keys (:tasks (read-string (slurp \"bb.edn\")))))")))))) 3 | 4 | (defun babashka-run-task () 5 | (interactive) 6 | (let* ((default-directory (projectile-project-root)) 7 | (task (completing-read "Select task > " (babashka-tasks)))) 8 | (async-shell-command (concat "bb " (shell-quote-argument task))) 9 | (shell-command (concat "bb " (shell-quote-argument task))))) 10 | 11 | (provide 'setup-babashka) 12 | -------------------------------------------------------------------------------- /settings/user-specific-settings.el: -------------------------------------------------------------------------------- 1 | ;; To add your own settings, add them to .el-files in the users directory. 2 | 3 | ;; Evaluate user-settings-dir below if you're unsure of where to put them. 4 | 5 | (setq user-settings-dir 6 | (concat user-emacs-directory "users/" user-login-name)) 7 | 8 | (add-to-list 'load-path user-settings-dir) 9 | 10 | (when (file-exists-p user-settings-dir) 11 | (dolist (file (directory-files user-settings-dir t "^[^#].*el$")) 12 | (when (file-regular-p file) 13 | (load file)))) 14 | 15 | (provide 'user-specific-settings) 16 | -------------------------------------------------------------------------------- /users/magnars/magnars.el: -------------------------------------------------------------------------------- 1 | ;; Custom own settings 2 | 3 | (global-set-key (kbd "C-") (λ (with-perspective "org" (find-file "~/Dropbox/org/adventur/adventur-clj.org")))) 4 | 5 | (require 'mattilsynet) 6 | 7 | ;; Configure pair-programming-mode 8 | ;; 9 | ;; Suggest these first: 10 | (setq my/pair-programming-usual-suspects '("Christian Johansen " 11 | "Christian Johansen ")) 12 | ;; 13 | ;; Don't suggest these: 14 | (setq my/pair-programming-myself '("Magnar Sveen")) 15 | -------------------------------------------------------------------------------- /packages/setup-multiple-cursors.el: -------------------------------------------------------------------------------- 1 | (use-package multiple-cursors 2 | :defer t 3 | :bind (("C-S-c C-S-c" . mc/edit-lines) 4 | ("C-S-c C-a" . mc/edit-beginnings-of-lines) 5 | ("C-S-c C-e" . mc/edit-ends-of-lines) 6 | ("C-æ" . mc/mark-next-like-this) 7 | ("M-æ" . mc/mark-all-dwim) 8 | ("C-å" . mc/mark-previous-like-this) 9 | ("C-æ" . mc/mark-next-like-this) 10 | ("C-Æ" . mc/mark-more-like-this-extended) 11 | ("M-å" . mc/mark-all-in-region) 12 | ("H-0" . mc/insert-numbers))) 13 | 14 | (provide 'setup-multiple-cursors) 15 | -------------------------------------------------------------------------------- /settings/setup-straight.el: -------------------------------------------------------------------------------- 1 | (defvar bootstrap-version) 2 | (let ((bootstrap-file 3 | (expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory)) 4 | (bootstrap-version 6)) 5 | (unless (file-exists-p bootstrap-file) 6 | (with-current-buffer 7 | (url-retrieve-synchronously 8 | "https://raw.githubusercontent.com/radian-software/straight.el/develop/install.el" 9 | 'silent 'inhibit-cookies) 10 | (goto-char (point-max)) 11 | (eval-print-last-sexp))) 12 | (load bootstrap-file nil 'nomessage)) 13 | 14 | (provide 'setup-straight) 15 | -------------------------------------------------------------------------------- /users/magnars/adventur.el: -------------------------------------------------------------------------------- 1 | ;; Set up Adventur Delux editing mode 2 | 3 | (straight-use-package 4 | '(adventur-mode :type git :host github :protocol ssh :repo "magnars/adventur-mode")) 5 | 6 | (autoload 'adventur-mode "adventur-mode") 7 | (add-to-list 'auto-mode-alist '("\\.adv$" . adventur-mode)) 8 | 9 | (global-set-key (kbd "C-x p n") (λ (projectile-switch-project-by-name "~/projects/no-adventur"))) 10 | (global-set-key (kbd "C-x p m") (λ (require 'adventur-mode) 11 | (with-perspective "eventyr" (find-file "~/projects/eventyr/master/notat.org")))) 12 | 13 | (setq magit-bind-magit-project-status nil) 14 | -------------------------------------------------------------------------------- /packages/setup-which-key.el: -------------------------------------------------------------------------------- 1 | ;; which-key 2 | ;; 3 | ;; a minor mode for Emacs that displays the key bindings following your 4 | ;; currently entered incomplete command (a prefix) in a popup. 5 | 6 | (use-package which-key 7 | :defer 2 8 | :init 9 | ;; Wait 3 seconds before opening normally. 10 | (setq which-key-idle-delay 3) 11 | 12 | ;; Allow F1 to trigger which-key before it is done automatically 13 | (setq which-key-show-early-on-C-h t) 14 | 15 | ;; Refresh quickly once it is open 16 | (setq which-key-idle-secondary-delay 0.05) 17 | 18 | :config 19 | (which-key-mode) 20 | 21 | :diminish which-key-mode) 22 | 23 | (provide 'setup-which-key) 24 | -------------------------------------------------------------------------------- /packages/setup-flycheck.el: -------------------------------------------------------------------------------- 1 | (use-package flycheck 2 | :diminish flycheck-mode 3 | :defer 2 4 | :config (setq-default flycheck-disabled-checkers '(emacs-lisp-checkdoc emacs-lisp html-tidy)) 5 | :init 6 | ;; Eagerly re-check whenever there are errors. 7 | ;; When there are no errors, we're happy to wait for a save. 8 | (add-hook 'flycheck-after-syntax-check-hook 9 | (lambda () 10 | (setq-local flycheck-check-syntax-automatically 11 | (if flycheck-current-errors 12 | '(save idle-change mode-enabled) 13 | '(save mode-enabled))))) 14 | 15 | (global-flycheck-mode)) 16 | 17 | (provide 'setup-flycheck) 18 | -------------------------------------------------------------------------------- /settings/extra-keybindings.el: -------------------------------------------------------------------------------- 1 | ;; No need to kill emacs that easily. 2 | ;; The mnemonic is C-x REALLY QUIT 3 | (global-set-key (kbd "C-x r q") 'save-buffers-kill-terminal) 4 | (global-set-key (kbd "C-x C-c") 'delete-frame) 5 | 6 | ;; Search for more than just commands 7 | (global-set-key (kbd " a") 'apropos) 8 | 9 | ;; Open url at point in a browser 10 | (global-set-key (kbd "C-x M-g") 'browse-url-at-point) 11 | 12 | ;; Make suspend-frame less convenient 13 | ;; Do nothing on C-z, since I apparently repeatedly mistakenly type it 14 | (global-unset-key (kbd "C-z")) 15 | (global-set-key (kbd "C-x C-z") 'shell) 16 | 17 | ;; A convenient alternative to C-u as universal argument 18 | (global-set-key (kbd "s-u") 'universal-argument) 19 | 20 | (provide 'extra-keybindings) 21 | -------------------------------------------------------------------------------- /packages/setup-deadgrep.el: -------------------------------------------------------------------------------- 1 | ;; Deadgrep 2 | ;; 3 | ;; The fast, beautiful text search that your Emacs deserves. 4 | 5 | (use-package deadgrep 6 | :bind (("M-s s" . deadgrep) 7 | (:map deadgrep-mode-map 8 | ("q" . deadgrep-quit))) 9 | 10 | :config 11 | (wrap-fullscreen deadgrep)) 12 | 13 | (use-package wgrep 14 | :bind ((:map deadgrep-mode-map 15 | ("e" . wgrep-change-to-wgrep-mode)))) 16 | 17 | (use-package wgrep-deadgrep 18 | :hook ((deadgrep-finished . wgrep-deadgrep-setup))) 19 | 20 | ;; Consider deadgrep-edit-mode as an alternative to wgrep. 21 | 22 | (defun deadgrep-quit () 23 | (interactive) 24 | (let ((prev my/previous-window-configuration)) 25 | (quit-window) 26 | (when prev (register-val-jump-to prev nil)))) 27 | 28 | (provide 'setup-deadgrep) 29 | -------------------------------------------------------------------------------- /todo.org: -------------------------------------------------------------------------------- 1 | * The allmighty wishlist [5/6] 2 | ** DONE tailwindcss lsp-mode? 3 | ** TODO tailwindcss lsp-mode in hiccup? 4 | ** DONE clj-clean-namespace: support comments on same line, don't bail, maintain 5 | ** DONE clj-clean-namespace: bail when seeing comments on own line: 6 | (ns portfolio.components.reagent 7 | (:require 8 | ;; If you are using an older version of react use the following: 9 | #_[portfolio.reagent :refer-macros [defscene]] 10 | ;; For react versions 18+ use the following: 11 | ;; This is due to the new API https://www.metosin.fi/blog/reagent-towards-react-18/ 12 | [portfolio.reagent-18 :refer-macros [defscene]] 13 | [portfolio.components.reagent-component :as rc])) 14 | 15 | ** DONE Keybinding s-m to run targets from Makefile 16 | ** DONE core-async-mode ... also for clojure.test? -> generalize 17 | -------------------------------------------------------------------------------- /users/sigmund/sigmund.el: -------------------------------------------------------------------------------- 1 | ;; Custom own settings 2 | 3 | ;;Configure pair-programming-mode 4 | (setq my/pair-programming-myself '("Sigmund Hansen")) 5 | 6 | ;; PlantUML 7 | (use-package plantuml-mode) 8 | (use-package flycheck-plantuml) 9 | 10 | (setq plantuml-jar-path 11 | (concat (expand-file-name "~/bin/") 12 | "plantuml.jar")) 13 | (setq plantuml-default-exec-mode 'jar) 14 | (add-to-list 'auto-mode-alist '("\\.plantuml\\'" . plantuml-mode)) 15 | 16 | (with-eval-after-load 'org 17 | (add-to-list 'org-src-lang-modes '("plantuml" . plantuml))) 18 | 19 | (with-eval-after-load 'flycheck 20 | (require 'flycheck-plantuml) 21 | (flycheck-plantuml-setup)) 22 | 23 | ;; Side by side ediff 24 | (setq ediff-split-window-function 'split-window-vertically) 25 | 26 | ;; Configure font 27 | (require 'jetbrains-mono) 28 | -------------------------------------------------------------------------------- /packages/setup-zoom-frm.el: -------------------------------------------------------------------------------- 1 | ;; Zoom FRM 2 | ;; 3 | ;; Change text size for entire frame, not just a single buffer, lol 4 | 5 | (straight-use-package 6 | '(zoom-frm :type git :host github :repo "emacsmirror/zoom-frm")) 7 | 8 | (global-set-key (kbd "C-x C-+") 'zoom-frm-in) 9 | (global-set-key (kbd "C-x C--") 'zoom-frm-out) 10 | (global-set-key (kbd "C-x C-0") 'zoom-frm-unzoom) 11 | 12 | ;; DO NOT TEXT SCALE ADJUST!!! 13 | (global-unset-key (kbd "s-+")) 14 | (global-unset-key (kbd "s--")) 15 | (global-unset-key (kbd "s-0")) 16 | (global-unset-key (kbd "s-=")) 17 | 18 | (defun insert-en-dash () 19 | (interactive) 20 | (insert "–")) 21 | 22 | (defun insert-em-dash () 23 | (interactive) 24 | (insert "—")) 25 | 26 | (global-set-key (kbd "s--") 'insert-en-dash) 27 | (global-set-key (kbd "s-_") 'insert-em-dash) 28 | 29 | (provide 'setup-zoom-frm) 30 | -------------------------------------------------------------------------------- /default-black-theme.el: -------------------------------------------------------------------------------- 1 | (deftheme default-black) 2 | 3 | (custom-theme-set-faces 4 | 'default-black 5 | '(default ((t (:inherit nil :stipple nil :background "Black" :foreground "White" :inverse-video nil :box nil :strike-t*hrough nil :overline nil :underline nil :slant normal :weight normal :width normal :height 105)))) 6 | '(highlight ((((class color) (min-colors 88) (background dark)) (:background "#111111")))) 7 | '(region ((nil (:background "#464740")))) 8 | '(hl-line ((nil (:background "#222222")))) 9 | '(yas-field-highlight-face ((nil (:background "#333399")))) 10 | '(js2-function-param-face ((t (:foreground "LightGoldenrod")))) 11 | '(font-lock-warning-face ((nil (:foreground "#ff6666")))) 12 | '(show-paren-match ((nil (:background "#333399")))) 13 | '(show-paren-mismatch ((((class color)) (:background "red"))))) 14 | 15 | (provide-theme 'default-black) 16 | -------------------------------------------------------------------------------- /packages/setup-perspective.el: -------------------------------------------------------------------------------- 1 | ;; Perspective 2 | ;; 3 | ;; Provides multiple named workspaces (or "perspectives") in Emacs, similar to 4 | ;; multiple desktops in window managers like Awesome and XMonad, and Spaces on 5 | ;; the Mac. 6 | 7 | (use-package perspective 8 | :bind (("C-x C-b" . persp-ibuffer) 9 | ("C-x b" . persp-switch-to-buffer*) 10 | ("C-x k" . persp-kill-buffer*)) 11 | :custom 12 | (persp-mode-prefix-key (kbd "C-x x")) 13 | :init 14 | (persp-mode)) 15 | 16 | ;; Avoid popping ediff up in separate window, it breaks perspective 17 | (setq ediff-window-setup-function #'ediff-setup-windows-plain) 18 | 19 | ;; Macro to open perspective with `name' and evaluate `body' 20 | (defmacro with-perspective (name &rest body) 21 | `(let ((initialize (not (gethash ,name (perspectives-hash)))) 22 | (current-perspective (persp-curr))) 23 | (persp-switch ,name) 24 | (when initialize ,@body) 25 | (set-frame-parameter nil 'persp--last current-perspective))) 26 | 27 | (provide 'setup-perspective) 28 | -------------------------------------------------------------------------------- /packages/setup-undo-fu.el: -------------------------------------------------------------------------------- 1 | ;; Undo Fu 2 | ;; 3 | ;; Simple, stable linear undo with redo for Emacs. Unlike undo-tree, does not 4 | ;; mess with Emacs internals. We still get visualisation of the tree structure 5 | ;; via vundo. In addition, undo-fu-session stores undo history across Emacs sessions. 6 | 7 | (use-package undo-fu 8 | :config 9 | ;; Increase undo history limits to reduce likelihood of data loss 10 | (setq undo-limit 400000 ; 400kb (default is 160kb) 11 | undo-strong-limit 3000000 ; 3mb (default is 240kb) 12 | undo-outer-limit 48000000) ; 48mb (default is 24mb) 13 | 14 | :bind (([remap undo] . undo-fu-only-undo) 15 | ([remap redo] . undo-fu-only-redo) 16 | ("C-_" . undo-fu-only-undo) 17 | ("M-_" . undo-fu-only-redo) 18 | ("C-M-_" . undo-fu-only-redo-all))) 19 | 20 | (use-package vundo 21 | :config 22 | (setq vundo-glyph-alist vundo-unicode-symbols 23 | vundo-compact-display t) 24 | 25 | :bind (("C-x u" . vundo))) 26 | 27 | (provide 'setup-undo-fu) 28 | -------------------------------------------------------------------------------- /settings/fast-startup.el: -------------------------------------------------------------------------------- 1 | ;; Startup optimizations 2 | ;; 3 | ;; https://www.reddit.com/r/emacs/comments/3kqt6e/2_easy_little_known_steps_to_speed_up_emacs_start/ 4 | 5 | ;; Set garbage collection threshold 6 | 7 | (setq gc-cons-threshold (* 1024 1024 100)) 8 | 9 | ;; Set file-name-handler-alist 10 | 11 | (setq file-name-handler-alist-original file-name-handler-alist) 12 | (setq file-name-handler-alist nil) 13 | 14 | ;; Keep tabs on startup time 15 | 16 | (add-hook 'emacs-startup-hook 17 | (lambda () 18 | (message "Emacs ready in %s with %d garbage collections." 19 | (format "%.2f seconds" 20 | (float-time 21 | (time-subtract after-init-time before-init-time))) 22 | gcs-done))) 23 | 24 | (run-with-idle-timer 25 | 5 nil 26 | (lambda () 27 | (setq gc-cons-threshold (* 1024 1024 20)) 28 | (setq file-name-handler-alist file-name-handler-alist-original) 29 | (makunbound 'file-name-handler-alist-original))) 30 | 31 | (provide 'fast-startup) 32 | -------------------------------------------------------------------------------- /settings/appearance.el: -------------------------------------------------------------------------------- 1 | ;; Turn off mouse interface early in startup to avoid momentary display 2 | (if (fboundp 'menu-bar-mode) (menu-bar-mode -1)) 3 | (if (fboundp 'tool-bar-mode) (tool-bar-mode -1)) 4 | (if (fboundp 'scroll-bar-mode) (scroll-bar-mode -1)) 5 | 6 | ;; No splash screen please ... jeez 7 | (setq inhibit-startup-message t) 8 | 9 | ;; Color all language features 10 | (setq font-lock-maximum-decoration t) 11 | 12 | ;; Highlight current line 13 | (global-hl-line-mode 1) 14 | 15 | ;; Include entire file path in title 16 | (setq frame-title-format '(buffer-file-name "%f" ("%b"))) 17 | 18 | ;; Be less obnoxious 19 | (blink-cursor-mode -1) 20 | (tooltip-mode -1) 21 | 22 | ;; Set theme 23 | (load-theme 'default-black) 24 | 25 | (set-face-attribute 'default nil :font "-apple-Monaco-medium-normal-normal-*-15-*-*-*-m-0-iso10646-1") 26 | 27 | ;; Don't beep. Just blink the modeline on errors. 28 | (setq ring-bell-function (lambda () 29 | (invert-face 'mode-line) 30 | (run-with-timer 0.05 nil 'invert-face 'mode-line))) 31 | 32 | (provide 'appearance) 33 | -------------------------------------------------------------------------------- /packages/setup-tagedit.el: -------------------------------------------------------------------------------- 1 | (use-package tagedit 2 | :straight t 3 | :defer t) 4 | 5 | (add-hook 'html-mode-hook 'tagedit-mode) 6 | 7 | (with-eval-after-load 'mhtml-mode 8 | ;; Paredit lookalikes 9 | (define-key html-mode-map (kbd "s-") 'tagedit-forward-slurp-tag) 10 | (define-key html-mode-map (kbd "C-)") 'tagedit-forward-slurp-tag) 11 | (define-key html-mode-map (kbd "s-") 'tagedit-forward-barf-tag) 12 | (define-key html-mode-map (kbd "C-}") 'tagedit-forward-barf-tag) 13 | (define-key html-mode-map (kbd "M-r") 'tagedit-raise-tag) 14 | (define-key html-mode-map (kbd "s-s") 'tagedit-splice-tag) 15 | (define-key html-mode-map (kbd "M-S") 'tagedit-split-tag) 16 | (define-key html-mode-map (kbd "M-J") 'tagedit-join-tags) 17 | (define-key html-mode-map (kbd "M-?") 'tagedit-convolute-tags) 18 | 19 | ;; No paredit equivalents 20 | (define-key html-mode-map (kbd "M-k") 'tagedit-kill-attribute) 21 | (define-key html-mode-map (kbd "s-") 'tagedit-toggle-multiline-tag) 22 | 23 | ;; Automatic insertion of <> while typing 24 | (tagedit-add-experimental-features)) 25 | 26 | (provide 'setup-tagedit) 27 | -------------------------------------------------------------------------------- /packages/setup-dired.el: -------------------------------------------------------------------------------- 1 | (with-eval-after-load 'dired 2 | (require 'dired-x) 3 | 4 | ;; Make dired less verbose, toggle with ( 5 | (add-hook 'dired-mode-hook 'dired-hide-details-mode) 6 | 7 | ;; Move files between split panes 8 | (setq dired-dwim-target t) 9 | 10 | ;; C-a is nicer in dired if it moves back to start of files 11 | (define-key dired-mode-map (kbd "C-a") 'dired-back-to-start-of-files) 12 | 13 | ;; Mirror OSX folder navigation 14 | (define-key dired-mode-map (kbd "M-") 'dired-jump) 15 | (define-key dired-mode-map (kbd "M-") 'dired-find-file) 16 | 17 | ;; Delete files with k 18 | (define-key dired-mode-map (kbd "k") 'dired-do-delete)) 19 | 20 | ;; Nicer navigation also in writeable dired 21 | (with-eval-after-load 'wdired 22 | (define-key wdired-mode-map (kbd "C-a") 'dired-back-to-start-of-files) 23 | (define-key wdired-mode-map (vector 'remap 'beginning-of-buffer) 'dired-back-to-top) 24 | (define-key wdired-mode-map (vector 'remap 'end-of-buffer) 'dired-jump-to-bottom)) 25 | 26 | (defun dired-back-to-start-of-files () 27 | (interactive) 28 | (backward-char (- (current-column) 2))) 29 | 30 | (provide 'setup-dired) 31 | -------------------------------------------------------------------------------- /settings/tooling.el: -------------------------------------------------------------------------------- 1 | (use-package s) 2 | (use-package dash) 3 | (use-package diminish) 4 | 5 | ;; Shorthand for interactive lambdas 6 | (defmacro λ (&rest body) 7 | `(lambda () 8 | (interactive) 9 | ,@body)) 10 | 11 | (global-set-key (kbd "s-l") (λ (insert "\u03bb"))) 12 | 13 | ;; Set up a keybinding for the very next command invocation 14 | (defun one-shot-keybinding (key command) 15 | (set-temporary-overlay-map 16 | (let ((map (make-sparse-keymap))) 17 | (define-key map (kbd key) command) 18 | map) 19 | t)) 20 | 21 | ;; Instrument a `command' to store the current window configuration in 22 | ;; `register' and then going fullscreen. 23 | (defmacro wrap-fullscreen (command) 24 | `(defadvice ,command (around ,(intern (concat "wrap-" (symbol-name command) "-fullscreen")) activate) 25 | (let ((my/prev (list (current-window-configuration) (point-marker)))) 26 | ad-do-it 27 | (delete-other-windows) 28 | (setq-local my/previous-window-configuration my/prev)))) 29 | 30 | (defvar my/previous-window-configuration nil) 31 | 32 | ;; No need to remind me about eldoc-mode all the time 33 | (diminish 'eldoc-mode) 34 | 35 | (provide 'tooling) 36 | -------------------------------------------------------------------------------- /users/teodorlu/neil-quickadd.el: -------------------------------------------------------------------------------- 1 | ;; Copied from https://github.com/teodorlu/neil-quickadd/blob/8c6c4a1b6a17acc52f1a26616358a2952246cb3e/neil-quickadd.el#L22 2 | 3 | (require 'projectile) 4 | (require 's) 5 | 6 | (defun neil-quickadd--binary () 7 | "Which neil binary name to use" 8 | "neil-quickadd") 9 | 10 | (defun neil-quickadd () 11 | "Add a deps.edn dependency." 12 | (interactive) 13 | (let* ((libs (s-lines (s-trim (shell-command-to-string (s-concat (neil-quickadd--binary) " libs"))))) 14 | (selected (completing-read "add lib > " libs))) 15 | (projectile-run-shell-command-in-root (s-concat "neil dep add " selected)))) 16 | 17 | (defun neil-quickadd-blacklist () 18 | "Blacklist a deps.edn dependency." 19 | (interactive) 20 | (let* ((libs (s-lines (s-trim (shell-command-to-string (s-concat (neil-quickadd--binary) " libs"))))) 21 | (selected (completing-read "blacklist lib > " libs))) 22 | (shell-command-to-string (s-concat (neil-quickadd--binary) " blacklist-lib " selected)))) 23 | 24 | (defun neil-quickadd-upgrade () 25 | "Upgrade deps.edn dependencies." 26 | (interactive) 27 | (projectile-run-shell-command-in-root "neil dep upgrade")) 28 | 29 | (provide 'neil-quickadd) 30 | -------------------------------------------------------------------------------- /packages/setup-org-mode.el: -------------------------------------------------------------------------------- 1 | (use-package org 2 | :ensure nil 3 | :defer t 4 | 5 | :custom 6 | (org-todo-keyword-faces 7 | '(("DONE" . (:foreground "green" :weight bold)))) 8 | 9 | :bind (:map org-mode-map 10 | ("M-+" . org-shiftright) 11 | ("C-S-" . org-metadown) 12 | ("C-S-" . org-metaup) 13 | ("s-" . org-metareturn-hook)) 14 | 15 | :config 16 | (unbind-key "S-" org-mode-map) 17 | (unbind-key "S-" org-mode-map) 18 | (unbind-key "S-" org-mode-map) 19 | (unbind-key "S-" org-mode-map) 20 | 21 | ;; Update [/] cookies after a selection of commands 22 | (--each '(org-yank 23 | org-kill-line 24 | kill-whole-line 25 | duplicate-current-line-or-region) 26 | (advice-add it :after 'my/org-update-parent-cookie))) 27 | 28 | (defun my/org-update-parent-cookie (&rest _) 29 | (when (equal major-mode 'org-mode) 30 | (save-excursion 31 | (ignore-errors 32 | (forward-char 1) 33 | (org-back-to-heading) 34 | (org-update-parent-todo-statistics))) 35 | (save-excursion 36 | (ignore-errors 37 | (forward-char -1) 38 | (org-back-to-heading) 39 | (org-update-parent-todo-statistics))))) 40 | 41 | (provide 'setup-org-mode) 42 | -------------------------------------------------------------------------------- /packages/setup-projectile.el: -------------------------------------------------------------------------------- 1 | ;; Projectile 2 | ;; 3 | ;; A project interaction library. It provides a nice set of features operating 4 | ;; on a project level without introducing external dependencies 5 | 6 | (use-package projectile 7 | :diminish projectile-mode 8 | :commands (projectile-switch-project-by-name) 9 | 10 | :bind-keymap 11 | (("s-p" . projectile-command-map)) 12 | 13 | :bind 14 | ("C-x p p" . projectile-switch-project) 15 | ("C-x p e" . my/projectile-switch-project-to-emacs) 16 | 17 | :config 18 | (projectile-mode +1) 19 | (define-key projectile-command-map (kbd "s-p") #'projectile-switch-project) 20 | 21 | (setq projectile-ignored-project-function 'my/ignore-project?) 22 | 23 | (require 'setup-perspective) 24 | (setq projectile-switch-project-action 'switch-perspective+find-file)) 25 | 26 | (defun current-project-name () 27 | (cadr (reverse (split-string (projectile-project-root) "/")))) 28 | 29 | (defun switch-perspective+find-file () 30 | (with-perspective (current-project-name) 31 | (projectile-find-file))) 32 | 33 | (defun my/ignore-project? (file-name) 34 | (s-contains? ".gitlibs" file-name)) 35 | 36 | (defun my/projectile-switch-project-to-emacs () 37 | (interactive) 38 | (projectile-switch-project-by-name "~/.emacs.d/")) 39 | 40 | (provide 'setup-projectile) 41 | -------------------------------------------------------------------------------- /packages/setup-consult.el: -------------------------------------------------------------------------------- 1 | ;; Consult 2 | ;; 3 | ;; Provides search and navigation commands based on the Emacs completion 4 | ;; function completing-read. Completion allows you to quickly select an item 5 | ;; from a list of candidates. 6 | 7 | (use-package consult 8 | :bind (("C-x f" . consult-recent-file) 9 | ("C-x C-i" . consult-imenu) 10 | ("C-x M-i" . consult-imenu-multi) 11 | ("C-x i" . consult-outline) 12 | ("C-x C-y" . consult-yank-from-kill-ring) 13 | ("C-v" . consult-line) 14 | ("M-v" . consult-line-multi) 15 | ("M-g g" . consult-goto-line) 16 | ("M-g M-g" . consult-goto-line) 17 | ("M-y" . consult-yank-pop) 18 | ("M-s k" . consult-keep-lines)) 19 | 20 | :after (perspective) 21 | 22 | :config 23 | ;; Show only perspective-buffers with consult-buffer 24 | (consult-customize consult--source-buffer :hidden t :default nil) 25 | (add-to-list 'consult-buffer-sources persp-consult-source)) 26 | 27 | (use-package consult-flycheck 28 | :bind (("M-g f" . consult-flycheck))) 29 | 30 | (setq completion-in-region-function 31 | (lambda (&rest args) 32 | (apply (if vertico-mode 33 | #'consult-completion-in-region 34 | #'completion--in-region) 35 | args))) 36 | 37 | (provide 'setup-consult) 38 | -------------------------------------------------------------------------------- /init.el: -------------------------------------------------------------------------------- 1 | ;; Add settings to load-path 2 | (add-to-list 'load-path (expand-file-name "settings" user-emacs-directory)) 3 | 4 | ;; Optimize startup of Emacs 5 | (require 'fast-startup) 6 | 7 | ;; Keep emacs Custom-settings in separate file, not appended to init.el 8 | (setq custom-file (expand-file-name "custom.el" user-emacs-directory)) 9 | (load custom-file) 10 | 11 | ;; Set up appearance early 12 | (require 'appearance) 13 | 14 | ;; Configure the package manager 15 | (require 'packages) 16 | 17 | ;; Configure Emacs for Norwegian OSX, lol 18 | (when (string= "darwin" system-type) 19 | (require 'norwegian-mac)) 20 | 21 | ;; Lets start with a smattering of sanity 22 | (require 'sane-defaults) 23 | 24 | ;; Set up tooling for the rest of the configuration 25 | (require 'tooling) 26 | 27 | ;; Add utilities 28 | (require 'navigation) 29 | (require 'editing) 30 | (require 'buffers) 31 | (require 'windows) 32 | (require 'extra-keybindings) 33 | (require 'indented-yank) 34 | 35 | ;; Set up Straight (for packages on github) 36 | (require 'setup-straight) 37 | 38 | ;; Load all packages 39 | (dolist (file (directory-files packages-dir t "^[^#].*el$")) 40 | (when (file-regular-p file) 41 | (load file))) 42 | 43 | ;; Project-specific settings 44 | (require 'matnyttig) 45 | 46 | ;; Conclude init by setting up specifics for the current user 47 | (require 'user-specific-settings) 48 | -------------------------------------------------------------------------------- /settings/norwegian-mac.el: -------------------------------------------------------------------------------- 1 | ;; change command to meta, and ignore option to use weird Norwegian keyboard 2 | (setq mac-option-modifier 'super) 3 | (setq mac-command-modifier 'meta) 4 | (setq ns-function-modifier 'hyper) 5 | (setq mac-right-option-modifier nil) 6 | 7 | ;; Norwegian mac-keyboard alt-keys 8 | (define-key key-translation-map (kbd "s-8") (kbd "[")) 9 | (define-key key-translation-map (kbd "s-(") (kbd "{")) 10 | (define-key key-translation-map (kbd "s-9") (kbd "]")) 11 | (define-key key-translation-map (kbd "s-)") (kbd "}")) 12 | (define-key key-translation-map (kbd "s-7") (kbd "|")) 13 | (define-key key-translation-map (kbd "s-/") (kbd "\\")) 14 | (define-key key-translation-map (kbd "M-s-7") (kbd "M-|")) 15 | 16 | (defun insert-backslash () 17 | (interactive) 18 | (insert "\\")) 19 | 20 | ;; Insert backslash, no questions asked 21 | (global-set-key (kbd "H-7") 'insert-backslash) 22 | 23 | ;; Move to OSX trash folder when deleting stuff 24 | (setq trash-directory "~/.Trash/emacs") 25 | 26 | ;; Use aspell for spell checking: brew install aspell --lang=en 27 | (setq ispell-program-name "/opt/homebrew/bin/aspell") 28 | 29 | ;; Use GNU ls - install with: brew install xz coreutils 30 | (setq insert-directory-program "gls") 31 | 32 | ;; Setup environment variables from the user's shell. 33 | (use-package exec-path-from-shell 34 | :init 35 | (exec-path-from-shell-initialize)) 36 | 37 | (provide 'norwegian-mac) 38 | -------------------------------------------------------------------------------- /custom.el: -------------------------------------------------------------------------------- 1 | (custom-set-variables 2 | ;; custom-set-variables was added by Custom. 3 | ;; If you edit it by hand, you could mess it up, so be careful. 4 | ;; Your init file should contain only one such instance. 5 | ;; If there is more than one, they won't work right. 6 | '(custom-safe-themes 7 | '("05692bda554c178fafe15cc3e6ab09539e7db4846eb9bb6272b97068c055a903" default)) 8 | '(package-selected-packages 9 | '(diff-hl exec-path-from-shell whitespace-cleanup-mode clj-refactor kaocha-runner marginalia which-key cider diminish move-text undo-fu-session vundo undo-fu consult-flycheck orderless consult flycheck datomic-snippets yasnippet projectile wgrep-deadgrep wgrep deadgrep vertico-directory vertico lsp-mode clojure-mode s multiple-cursors expand-region paredit magit)) 10 | '(safe-local-variable-values 11 | '((cider-clojure-cli-aliases . "-A:dev") 12 | (cider-figwheel-main-default-options . ":ui") 13 | (cider-preferred-build-tool . clojure-cli) 14 | (cider-default-cljs-repl . figwheel-main) 15 | (cider-clojure-cli-aliases . "-A:ui") 16 | (flycheck-disabled-checkers emacs-lisp-checkdoc)))) 17 | (custom-set-faces 18 | ;; custom-set-faces was added by Custom. 19 | ;; If you edit it by hand, you could mess it up, so be careful. 20 | ;; Your init file should contain only one such instance. 21 | ;; If there is more than one, they won't work right. 22 | '(highlight ((t (:background "dark magenta"))))) 23 | -------------------------------------------------------------------------------- /settings/significant-other.el: -------------------------------------------------------------------------------- 1 | ;; Significant Other 2 | ;; 3 | ;; Many files come in pairs, like tests and source files, header files and 4 | ;; implementations, components and their devcards. 5 | ;; 6 | ;; This impromptu package-to-be helps set up functions to jump between 7 | ;; significant other files. 8 | ;; 9 | ;; See setup-clojure-mode for example usage. 10 | 11 | (require 'dash) 12 | 13 | (setq significant-other-find-fn 14 | (lambda () 15 | (message "Significant other not configured for this mode.") 16 | nil)) 17 | 18 | (defun significant-other-find-existing () 19 | (-first 'file-exists-p (funcall significant-other-find-fn))) 20 | 21 | (defun significant-other-jump (arg) 22 | (interactive "P") 23 | (if-let (file (significant-other-find-existing)) 24 | (find-file file) 25 | (when-let (file (car (funcall significant-other-find-fn))) 26 | (if arg 27 | (progn (find-file file) (save-buffer)) 28 | (ido-find-file-in-dir (file-name-directory file)))))) 29 | 30 | (defmacro with-significant-others (binding &rest mappings) 31 | (declare (indent 1)) 32 | `(setq-local 33 | significant-other-find-fn 34 | (lambda () 35 | (let ((,binding (buffer-file-name))) 36 | (cond 37 | ,@(--map 38 | `((string-match-p ,(car it) ,binding) 39 | ,(cadr it)) 40 | mappings)))))) 41 | 42 | (global-set-key (kbd "s-j") 'significant-other-jump) 43 | 44 | (provide 'significant-other) 45 | -------------------------------------------------------------------------------- /users/sigmund/jetbrains-mono.el: -------------------------------------------------------------------------------- 1 | ;; Configure JetBrains Mono 2 | (set-face-attribute 'default nil :family "JetBrains Mono") 3 | (use-package ligature 4 | :config 5 | (ligature-set-ligatures 'prog-mode '("--" "---" "==" "===" "!=" "!==" "=!=" 6 | "=:=" "=/=" "<=" ">=" "&&" "&&&" "&=" "++" "+++" "***" ";;" "!!" 7 | "??" "???" "?:" "?." "?=" "<:" ":<" ":>" ">:" "<:<" "<>" "<<<" ">>>" 8 | "<<" ">>" "||" "-|" "_|_" "|-" "||-" "|=" "||=" "##" "###" "####" 9 | "#{" "#[" "]#" "#(" "#?" "#_" "#_(" "#:" "#!" "#=" "^=" "<$>" "<$" 10 | "$>" "<+>" "<+" "+>" "<*>" "<*" "*>" "" "/>" "" "->" "->>" "<<-" "<-" "<=<" "=<<" "<<=" "<==" "<=>" "<==>" 12 | "==>" "=>" "=>>" ">=>" ">>=" ">>-" ">-" "-<" "-<<" ">->" "<-<" "<-|" 13 | "<=|" "|=>" "|->" "<->" "<~~" "<~" "<~>" "~~" "~~>" "~>" "~-" "-~" 14 | "~@" "[||]" "|]" "[|" "|}" "{|" "[<" ">]" "|>" "<|" "||>" "<||" 15 | "|||>" "<|||" "<|>" "..." ".." ".=" "..<" ".?" "::" ":::" ":=" "::=" 16 | ":?" ":?>" "//" "///" "/*" "*/" "/=" "//=" "/==" "@_" "__" "???" 17 | "<:<" ";;;")) 18 | (global-ligature-mode t)) 19 | 20 | (provide 'jetbrains-mono) 21 | -------------------------------------------------------------------------------- /settings/indented-yank.el: -------------------------------------------------------------------------------- 1 | ;; Make indenting yanked text the default for programming modes. 2 | ;; Use C-S-y to yank unindented. 3 | 4 | (require 'dash) 5 | 6 | (global-set-key (kbd "C-S-y") 'yank-unindented) 7 | 8 | (defvar yank-indent-modes '(prog-mode 9 | sgml-mode 10 | js2-mode) 11 | "Modes in which to indent regions that are yanked (or yank-popped)") 12 | 13 | (defvar yank-advised-indent-threshold 1000 14 | "Threshold (# chars) over which indentation does not automatically occur.") 15 | 16 | (defun yank-advised-indent-function (beg end) 17 | "Do indentation, as long as the region isn't too large." 18 | (if (<= (- end beg) yank-advised-indent-threshold) 19 | (indent-region beg end nil))) 20 | 21 | (defadvice yank (after yank-indent activate) 22 | "If current mode is one of 'yank-indent-modes, indent yanked text (with prefix arg don't indent)." 23 | (if (and (not (ad-get-arg 0)) 24 | (--any? (derived-mode-p it) yank-indent-modes)) 25 | (let ((transient-mark-mode nil)) 26 | (yank-advised-indent-function (region-beginning) (region-end))))) 27 | 28 | (defadvice yank-pop (after yank-pop-indent activate) 29 | "If current mode is one of 'yank-indent-modes, indent yanked text (with prefix arg don't indent)." 30 | (if (and (not (ad-get-arg 0)) 31 | (member major-mode yank-indent-modes)) 32 | (let ((transient-mark-mode nil)) 33 | (yank-advised-indent-function (region-beginning) (region-end))))) 34 | 35 | (defun yank-unindented () 36 | (interactive) 37 | (yank 1)) 38 | 39 | (provide 'indented-yank) 40 | -------------------------------------------------------------------------------- /packages/setup-kaocha-runner.el: -------------------------------------------------------------------------------- 1 | ;; Kaocha Runner 2 | ;; 3 | ;; An emacs package for running Kaocha tests via CIDER. 4 | 5 | (use-package kaocha-runner 6 | :after (cider-mode) 7 | :commands (kaocha-runner--run-tests) 8 | :bind (:map clojure-mode-map 9 | ("C-c k t" . kaocha-runner-run-test-at-point) 10 | ("C-c k r" . kaocha-runner-run-tests) 11 | ("C-c k a" . kaocha-runner-run-all-tests) 12 | ("C-c k w" . kaocha-runner-show-warnings) 13 | ("C-c k h" . kaocha-runner-hide-windows))) 14 | 15 | (defun kaocha-runner--is-test? (s) 16 | (string-match-p "/test/.+\.clj" s)) 17 | 18 | (defun kaocha-runner--significant-other-find-existing-test () 19 | (--first 20 | (and (file-exists-p it) 21 | (kaocha-runner--is-test? it)) 22 | (funcall significant-other-find-fn))) 23 | 24 | (defun kaocha-runner-run-relevant-tests () 25 | (interactive) 26 | (when (cljr--project-depends-on-p "kaocha") 27 | (if (kaocha-runner--is-test? (buffer-file-name)) 28 | (kaocha-runner--run-tests 29 | (kaocha-runner--testable-sym (cider-current-ns) nil nil) 30 | nil t) 31 | (let ((original-buffer (current-buffer))) 32 | (save-window-excursion 33 | (when-let ((file (kaocha-runner--significant-other-find-existing-test))) 34 | (find-file file) 35 | (kaocha-runner--run-tests 36 | (kaocha-runner--testable-sym (cider-current-ns) nil nil) 37 | nil t original-buffer))))))) 38 | 39 | (add-hook 'cider-file-loaded-hook #'kaocha-runner-run-relevant-tests) 40 | 41 | (provide 'setup-kaocha-runner) 42 | -------------------------------------------------------------------------------- /settings/mattilsynet.el: -------------------------------------------------------------------------------- 1 | (with-eval-after-load 'lsp-mode 2 | (add-to-list 'lsp-file-watch-ignored-directories "[/\\\\]docker/build")) 3 | 4 | (prodigy-define-service 5 | :name "Datomic transactor" 6 | :tags '(matnyttig datomic) 7 | :command "make" 8 | :args '("start-transactor") 9 | :cwd "~/work/matnyttig" 10 | :stop-signal 'sigkill 11 | :kill-process-buffer-on-stop t) 12 | 13 | (prodigy-define-service 14 | :name "NATS server" 15 | :tags '(matnyttig nats) 16 | :command "nats-server" 17 | :args '("--jetstream" "-sd=data/matnyttig-nats") 18 | :cwd "~/" 19 | :stop-signal 'sigkill 20 | :kill-process-buffer-on-stop t) 21 | 22 | (prodigy-define-service 23 | :name "Shadow-CLJS watcher" 24 | :tags '(matnyttig cljs) 25 | :command "npx" 26 | :args '("shadow-cljs" "watch" "client") 27 | :cwd "~/work/matnyttig" 28 | :stop-signal 'sigkill 29 | :kill-process-buffer-on-stop t) 30 | 31 | (prodigy-define-service 32 | :name "Launchpad" 33 | :tags '(matnyttig) 34 | :command "make" 35 | :args '("launch") 36 | :cwd "~/work/matnyttig" 37 | :stop-signal 'sigkill 38 | :kill-process-buffer-on-stop t) 39 | 40 | (prodigy-define-service 41 | :name "Jaeger" 42 | :tags '(matnyttig otel) 43 | :command "docker" 44 | :args (list "run" "--rm" 45 | "-e" "SPAN_STORAGE_TYPE=badger" 46 | "-e" "BADGER_EPHEMERAL=false" 47 | "-e" "BADGER_DIRECTORY_VALUE=/badger/data" 48 | "-e" "BADGER_DIRECTORY_KEY=/badger/key" 49 | "-v" (concat (expand-file-name "~/data/badger") ":/badger") 50 | "-p" "16686:16686" 51 | "-p" "4318:4318" 52 | "-p" "4317:4317" 53 | "jaegertracing/all-in-one" 54 | "--collector.otlp.enabled=true") 55 | :cwd "~/" 56 | :stop-signal 'sigkill 57 | :kill-process-buffer-on-stop t) 58 | 59 | (provide 'mattilsynet) 60 | -------------------------------------------------------------------------------- /users/magnars/what-the-emacs-d.el: -------------------------------------------------------------------------------- 1 | (use-package htmlize :defer t) 2 | 3 | (defun wte--unique-filename (stub &optional index) 4 | (setq index (or index 1)) 5 | (let ((filename (concat "~/stuff/what-the-emacsd/resources/posts/" 6 | stub 7 | ".el" 8 | (if (< index 10) "-0" "-") 9 | (number-to-string index) 10 | ".html"))) 11 | (if (file-exists-p filename) 12 | (wte--unique-filename stub (1+ index)) 13 | filename))) 14 | 15 | (defun what-the-emacsd-post (beg end) 16 | (interactive "r") 17 | (let ((example (with-current-buffer (htmlize-region beg end) 18 | (search-forward "
")
19 |                     (setq beg (point))
20 |                     (search-forward "
") 21 | (forward-char -7) 22 | (buffer-substring beg (point)))) 23 | (filename (wte--unique-filename (buffer-file-name-body))) 24 | (timestamp (floor (time-to-seconds (current-time))))) 25 | (find-file filename) 26 | (insert (format " 27 | 28 |

29 | 30 |
31 | 32 |
%s
33 | 34 |
35 | 36 |

37 | " timestamp example)) 38 | (goto-char 25) 39 | (local-set-key (kbd "C-c C-c") 'what-the-emacsd-publish) 40 | (local-set-key (kbd "C-c C-q") 'what-the-emacsd-done))) 41 | 42 | (defun what-the-emacsd-publish () 43 | (interactive) 44 | (let ((filename (file-name-nondirectory (buffer-file-name)))) 45 | (save-buffer) 46 | (compile 47 | (format "git add %s && git commit -m %S && git push && curl -i http://whattheemacsd.com/site/build" 48 | filename 49 | filename) t))) 50 | 51 | (defun what-the-emacsd-done () 52 | (interactive) 53 | (when-let ((prev my/previous-window-configuration)) 54 | (when prev (register-val-jump-to prev nil)))) 55 | 56 | (wrap-fullscreen what-the-emacsd-post) 57 | 58 | (provide 'what-the-emacs-d) 59 | -------------------------------------------------------------------------------- /packages/setup-lsp-mode.el: -------------------------------------------------------------------------------- 1 | (use-package lsp-mode 2 | :hook ((clojure-mode . lsp) 3 | (clojurescript-mode . lsp) 4 | (clojurec-mode . lsp) 5 | (lsp-mode . lsp-enable-which-key-integration)) 6 | :diminish " lsp" 7 | 8 | :bind ((:map lsp-mode-map 9 | ("s-l w l" . lsp-workspace-show-log))) 10 | 11 | :init 12 | (setq lsp-headerline-breadcrumb-enable nil) ;; Don't need file path in my buffer 13 | (setq lsp-lens-enable nil) ;; Hide clutter (reference and test counts) 14 | (setq lsp-enable-indentation nil) ;; use clojure-mode indentation 15 | (setq lsp-eldoc-enable-hover nil) ;; use CIDER eldoc 16 | (setq lsp-modeline-code-actions-enable nil) ;; Don't clutter modeline 17 | (setq lsp-modeline-diagnostics-enable nil) ;; Don't clutter modeline, jeez 18 | (setq lsp-completion-provider :none) ;; Skip company-mode 19 | (setq lsp-enable-symbol-highlighting nil) ;; Don't highlight current symbol 20 | 21 | (setq lsp-apply-edits-after-file-operations nil) ;; Disable broken lsp feature: https://github.com/clojure-lsp/clojure-lsp/issues/1813 22 | 23 | ;; To consider 24 | ;; 25 | ;; (setq lsp-enable-completion-at-point nil) ;; CIDER vs LSP? 26 | ;; (remove-hook 'completion-at-point-functions #'cider-complete-at-point t) 27 | 28 | :config 29 | (advice-add 'lsp--info :around #'my/silence-some-lsp-info-messages) 30 | (add-hook 'lsp-completion-mode-hook 'my/use-lsp-completion-only-as-fallback)) 31 | 32 | (defun my/use-lsp-completion-only-as-fallback () 33 | (when (-contains? completion-at-point-functions #'lsp-completion-at-point) 34 | (remove-hook 'completion-at-point-functions #'tags-completion-at-point-function t) 35 | (remove-hook 'completion-at-point-functions #'lsp-completion-at-point t) 36 | (remove-hook 'completion-at-point-functions t t) 37 | (add-to-list 'completion-at-point-functions #'lsp-completion-at-point t) 38 | (add-to-list 'completion-at-point-functions t t))) 39 | 40 | (defun my/silence-some-lsp-info-messages (orig-fn &rest args) 41 | (unless (or (string-equal (car args) "Connected to %s.") 42 | (string-equal (car args) "Disconnected")) 43 | (apply orig-fn args))) 44 | 45 | (provide 'setup-lsp-mode) 46 | -------------------------------------------------------------------------------- /packages/setup-yasnippet.el: -------------------------------------------------------------------------------- 1 | (use-package yasnippet 2 | :diminish yas-minor-mode 3 | :defer t 4 | 5 | :bind ((:map yas-keymap 6 | ("" . yas-exit-all-snippets) 7 | ("C-e" . yas/goto-end-of-active-field) 8 | ("C-a" . yas/goto-start-of-active-field))) 9 | 10 | :config 11 | ;; Use only own snippets, do not use bundled ones 12 | (setq yas-snippet-dirs '("~/.emacs.d/snippets")) 13 | 14 | ;; No dropdowns please, yas 15 | (setq yas-prompt-functions '(yas-ido-prompt yas-completing-prompt)) 16 | 17 | ;; No need to be so verbose 18 | (setq yas-verbosity 1) 19 | 20 | ;; Wrap around region 21 | (setq yas-wrap-around-region t) 22 | 23 | ;; Use yasnippet everywhere 24 | (yas-global-mode 1)) 25 | 26 | (use-package datomic-snippets 27 | :config 28 | (datomic-snippets-initialize)) 29 | 30 | ;; Inter-field navigation 31 | (defun yas/goto-end-of-active-field () 32 | (interactive) 33 | (let* ((snippet (car (yas--snippets-at-point))) 34 | (position (yas--field-end (yas--snippet-active-field snippet)))) 35 | (if (= (point) position) 36 | (move-end-of-line 1) 37 | (goto-char position)))) 38 | 39 | (defun yas/goto-start-of-active-field () 40 | (interactive) 41 | (let* ((snippet (car (yas--snippets-at-point))) 42 | (position (yas--field-start (yas--snippet-active-field snippet)))) 43 | (if (= (point) position) 44 | (move-beginning-of-line 1) 45 | (goto-char position)))) 46 | 47 | ;; Snippet helpers 48 | (defun buffer-file-name-body () 49 | "Buffer file name stripped of directory and extension" 50 | (if (buffer-file-name) 51 | (file-name-nondirectory (file-name-sans-extension (buffer-file-name))) 52 | (cadr (reverse (split-string (dired-current-directory) "/"))))) 53 | 54 | ;;; Snippet helpers: javascript 55 | (defun js-method-p () 56 | (save-excursion 57 | (word-search-backward "function") 58 | (looking-back ": "))) 59 | 60 | (defun js-function-declaration-p () 61 | (save-excursion 62 | (word-search-backward "function") 63 | (looking-back "^\\s *"))) 64 | 65 | (defun snippet--function-punctuation () 66 | (if (js-method-p) 67 | (when (not (looking-at "[ \n\t\r]*[},]")) 68 | (insert ",")) 69 | (unless (js-function-declaration-p) 70 | (if (looking-at "$") (insert ";"))))) 71 | 72 | (defun snippet--function-name () 73 | (if (js-function-declaration-p) "name" "")) 74 | 75 | 76 | (provide 'setup-yasnippet) 77 | -------------------------------------------------------------------------------- /packages/setup-vertico.el: -------------------------------------------------------------------------------- 1 | ;; Vertico 2 | ;; 3 | ;; Provides a performant and minimalistic vertical completion UI based on the 4 | ;; default completion system. 5 | 6 | (use-package vertico 7 | :bind (:map vertico-map 8 | ("M-" . vertico-exit-input)) 9 | 10 | :init 11 | (vertico-mode)) 12 | 13 | (use-package vertico-directory 14 | :after vertico 15 | :ensure nil ;; comes with vertico 16 | ;; More convenient directory navigation commands 17 | :bind (:map vertico-map 18 | ("RET" . vertico-directory-enter) 19 | ("C-d" . vertico-directory-enter) 20 | ("DEL" . vertico-directory-delete-char) 21 | ("M-DEL" . vertico-directory-delete-word)) 22 | 23 | ;; Cleans up path when moving directories with shadowed paths syntax, e.g. 24 | ;; cleans ~/foo/bar/// to /, and ~/foo/bar/~/ to ~/. 25 | :hook (rfn-eshadow-update-overlay . vertico-directory-tidy)) 26 | 27 | ;; Camel Case 28 | 29 | (defun orderless-camel-case (component) 30 | (orderless--separated-by '(zero-or-more nonl) 31 | (--map 32 | `(or (seq word-boundary ,it) 33 | ,(s-capitalize it)) 34 | (split-string component " ")))) 35 | 36 | ;; Use + to join parts of same long word 37 | 38 | (defun orderless-long-words-plus (component) 39 | (orderless--separated-by '(one-or-more word) 40 | (split-string component "+"))) 41 | 42 | ;; Use the `orderless' completion style. 43 | (use-package orderless 44 | :init 45 | ;; Configure a custom style dispatcher (see the Consult wiki) 46 | ;; (setq orderless-style-dispatchers '(+orderless-consult-dispatch orderless-affix-dispatch) 47 | ;; orderless-component-separator #'orderless-escapable-split-on-space) 48 | (setq completion-styles '(orderless basic) 49 | completion-category-defaults nil 50 | completion-category-overrides '((file (styles partial-completion))) 51 | orderless-matching-styles '(orderless-prefixes orderless-initialism orderless-camel-case orderless-long-words-plus))) 52 | 53 | ;; Persist minibuffer history over Emacs restarts. Vertico sorts from history. 54 | (use-package savehist 55 | :init 56 | (savehist-mode)) 57 | 58 | ;; Enable rich annotations using the Marginalia package 59 | (use-package marginalia 60 | :bind (:map minibuffer-local-map 61 | ("M-A" . marginalia-cycle)) 62 | 63 | :init (marginalia-mode) 64 | 65 | :config 66 | ;; No need for rich annotations when just switching buffers 67 | (setcdr (assq 'buffer marginalia-annotator-registry) 68 | '(none marginalia-annotate-buffer builtin))) 69 | 70 | (provide 'setup-vertico) 71 | -------------------------------------------------------------------------------- /settings/windows.el: -------------------------------------------------------------------------------- 1 | ;; Manipulating windows, ie. the squares with buffers in them 2 | 3 | (global-set-key (kbd "C-x -") 'toggle-window-split) 4 | (global-set-key (kbd "C-c -") 'rotate-windows) 5 | 6 | (defun toggle-window-split () 7 | (interactive) 8 | (if (= (count-windows) 2) 9 | (let* ((this-win-buffer (window-buffer)) 10 | (next-win-buffer (window-buffer (next-window))) 11 | (this-win-edges (window-edges (selected-window))) 12 | (next-win-edges (window-edges (next-window))) 13 | (this-win-2nd (not (and (<= (car this-win-edges) 14 | (car next-win-edges)) 15 | (<= (cadr this-win-edges) 16 | (cadr next-win-edges))))) 17 | (splitter 18 | (if (= (car this-win-edges) 19 | (car (window-edges (next-window)))) 20 | 'split-window-horizontally 21 | 'split-window-vertically))) 22 | (delete-other-windows) 23 | (let ((first-win (selected-window))) 24 | (funcall splitter) 25 | (if this-win-2nd (other-window 1)) 26 | (set-window-buffer (selected-window) this-win-buffer) 27 | (set-window-buffer (next-window) next-win-buffer) 28 | (select-window first-win) 29 | (if this-win-2nd (other-window 1)))))) 30 | 31 | (defun rotate-windows (count) 32 | "Rotate your windows. 33 | Dedicated windows are left untouched. Giving a negative prefix 34 | argument makes the windows rotate backwards." 35 | (interactive "p") 36 | (let* ((non-dedicated-windows (cl-remove-if 'window-dedicated-p (window-list))) 37 | (num-windows (length non-dedicated-windows)) 38 | (i 0) 39 | (step (+ num-windows count))) 40 | (cond ((not (> num-windows 1)) 41 | (message "You can't rotate a single window!")) 42 | (t 43 | (dotimes (counter (- num-windows 1)) 44 | (let* ((next-i (% (+ step i) num-windows)) 45 | 46 | (w1 (elt non-dedicated-windows i)) 47 | (w2 (elt non-dedicated-windows next-i)) 48 | 49 | (b1 (window-buffer w1)) 50 | (b2 (window-buffer w2)) 51 | 52 | (s1 (window-start w1)) 53 | (s2 (window-start w2))) 54 | (set-window-buffer w1 b2) 55 | (set-window-buffer w2 b1) 56 | (set-window-start w1 s2) 57 | (set-window-start w2 s1) 58 | (setq i next-i))))))) 59 | 60 | (provide 'windows) 61 | -------------------------------------------------------------------------------- /packages/setup-rgrep.el: -------------------------------------------------------------------------------- 1 | (require 'grep) 2 | 3 | (global-set-key (kbd "M-s M-s") 'rgrep-fullscreen) 4 | 5 | (defvar rgrep-default-search-patterns 6 | '("*.el" "*.clj" "*.cljs" "*.clj*" "*.edn" "*.md" "*.css")) 7 | 8 | (defun rgrep-fullscreen (regexp &optional files dir confirm) 9 | "Open grep in full screen, saving windows." 10 | (interactive 11 | (progn 12 | (grep-compute-defaults) 13 | (cond 14 | ((and grep-find-command (equal current-prefix-arg '(16))) 15 | (list (read-from-minibuffer "Run: " grep-find-command 16 | nil nil 'grep-find-history))) 17 | ((not grep-find-template) 18 | (error "grep.el: No `grep-find-template' available")) 19 | (t (let* ((regexp (grep-read-regexp)) 20 | (my-extension (when (buffer-file-name) 21 | (when-let ((my-extension (file-name-extension (buffer-file-name)))) 22 | (concat "*." my-extension)))) 23 | (files (completing-read (format "Search for %s in files matching: " regexp) 24 | (if my-extension 25 | (cons my-extension (--remove (equal it my-extension) rgrep-default-search-patterns)) 26 | rgrep-default-search-patterns) 27 | nil nil nil nil my-extension)) 28 | (dir (ido-read-directory-name "Base directory: " 29 | nil default-directory t)) 30 | (confirm (equal current-prefix-arg '(4)))) 31 | (list regexp files dir confirm)))))) 32 | (window-configuration-to-register ?$) 33 | (rgrep regexp files dir confirm) 34 | (switch-to-buffer "*grep*") 35 | (delete-other-windows) 36 | (goto-char (point-min))) 37 | 38 | (defun rgrep-quit-window () 39 | (interactive) 40 | (kill-buffer) 41 | (jump-to-register ?$)) 42 | 43 | (defun rgrep-goto-file-and-close-rgrep () 44 | (interactive) 45 | (compile-goto-error) 46 | (kill-buffer "*grep*") 47 | (delete-other-windows) 48 | (message "Type C-x r j $ to return to pre-rgrep windows.")) 49 | 50 | ;; Don't recurse into some directories 51 | (add-to-list 'grep-find-ignored-directories "target") 52 | (add-to-list 'grep-find-ignored-directories "node_modules") 53 | (add-to-list 'grep-find-ignored-directories "vendor") 54 | 55 | ;; Add custom keybindings 56 | (define-key grep-mode-map "q" 'rgrep-quit-window) 57 | (define-key grep-mode-map (kbd "C-") 'rgrep-goto-file-and-close-rgrep) 58 | (define-key grep-mode-map (kbd "C-x C-s") 'wgrep-save-all-buffers) 59 | 60 | ;; Use same keybinding as occur 61 | (setq wgrep-enable-key "e") 62 | 63 | (provide 'rgrep) 64 | -------------------------------------------------------------------------------- /settings/buffers.el: -------------------------------------------------------------------------------- 1 | ;; manipulating buffers and the files that are in them 2 | 3 | ;; Rename or delete current buffer file 4 | (global-set-key (kbd "C-x C-r") 'rename-current-buffer-file) 5 | (global-set-key (kbd "C-x C-k") 'delete-current-buffer-file) 6 | 7 | ;; Copy the file path for the current buffer 8 | (global-set-key (kbd "C-x M-w") 'copy-current-file-path) 9 | 10 | ;; Touch the buffer (save without changing) 11 | (global-set-key (kbd "C-x t") 'touch-buffer-file) 12 | 13 | ;; Create new frame 14 | (define-key global-map (kbd "C-x C-n") 'make-frame-command) 15 | 16 | ;; Bury buffer 17 | (global-set-key (kbd "s-y") 'bury-buffer) 18 | 19 | ;; Toggle two most recent buffers 20 | (global-set-key (kbd "s-b") 'mode-line-other-buffer) 21 | 22 | ;; Eval buffer 23 | (global-set-key (kbd "C-c C-k") 'eval-buffer) 24 | 25 | ;; Create scratch buffer 26 | (global-set-key (kbd "C-c b") 'create-scratch-buffer) 27 | 28 | ;;;; Implementations 29 | 30 | (defun rename-current-buffer-file () 31 | "Renames current buffer and file it is visiting." 32 | (interactive) 33 | (let ((name (buffer-name)) 34 | (filename (buffer-file-name))) 35 | (if (not (and filename (file-exists-p filename))) 36 | (error "Buffer '%s' is not visiting a file!" name) 37 | (let ((new-name (read-file-name "New name: " filename))) 38 | (if (get-buffer new-name) 39 | (error "A buffer named '%s' already exists!" new-name) 40 | (rename-file filename new-name 1) 41 | (rename-buffer new-name) 42 | (set-visited-file-name new-name) 43 | (set-buffer-modified-p nil) 44 | (message "File '%s' successfully renamed to '%s'" 45 | name (file-name-nondirectory new-name))))))) 46 | 47 | (defun delete-current-buffer-file () 48 | "Removes file connected to current buffer and kills buffer." 49 | (interactive) 50 | (let ((filename (buffer-file-name)) 51 | (buffer (current-buffer)) 52 | (name (buffer-name))) 53 | (if (not (and filename (file-exists-p filename))) 54 | (ido-kill-buffer) 55 | (when (yes-or-no-p "Are you sure you want to remove this file? ") 56 | (delete-file filename) 57 | (kill-buffer buffer) 58 | (message "File '%s' successfully removed" filename))))) 59 | 60 | (defun copy-current-file-path () 61 | "Add current file path to kill ring. 62 | Limits the filename to project root if possible." 63 | (interactive) 64 | (let ((filename (buffer-file-name))) 65 | (kill-new filename) 66 | (message "Copied file path %s" filename))) 67 | 68 | (defun create-scratch-buffer () 69 | "Create a new scratch buffer to work in. (could be *scratch* - *scratchX*)" 70 | (interactive) 71 | (let ((n 0) 72 | bufname) 73 | (while (progn 74 | (setq bufname (concat "*scratch" 75 | (if (= n 0) "" (int-to-string n)) 76 | "*")) 77 | (setq n (1+ n)) 78 | (get-buffer bufname))) 79 | (switch-to-buffer (get-buffer-create bufname)) 80 | (emacs-lisp-mode))) 81 | 82 | (defun touch-buffer-file () 83 | (interactive) 84 | (insert " ") 85 | (backward-delete-char 1) 86 | (save-buffer)) 87 | 88 | (provide 'buffers) 89 | -------------------------------------------------------------------------------- /packages/setup-paredit.el: -------------------------------------------------------------------------------- 1 | (use-package paredit 2 | :hook ((clojure-mode . paredit-mode) 3 | (cider-repl-mode . paredit-mode) 4 | (emacs-lisp-mode . paredit-mode) 5 | (lisp-data-mode . paredit-mode)) 6 | :diminish " ()" 7 | 8 | :config 9 | ;; Rebind nasty paredit keys 10 | (--each '(("M-s" "s-s" paredit-splice-sexp) 11 | ("M-" "s-" paredit-splice-sexp-killing-backward) 12 | ("M-" "s-" paredit-splice-sexp-killing-forward) 13 | ("C-" "s-" paredit-forward-slurp-sexp) 14 | ("C-" "s-" paredit-forward-barf-sexp) 15 | ("C-M-" "s-S-" paredit-backward-slurp-sexp) 16 | ("C-M-" "s-S-" paredit-backward-barf-sexp)) 17 | (-let [(original replacement command) it] 18 | (define-key paredit-mode-map (read-kbd-macro original) nil) 19 | (define-key paredit-mode-map (read-kbd-macro replacement) command))) 20 | 21 | ;; Make paredit work with delete-selection-mode 22 | (put 'paredit-forward-delete 'delete-selection 'supersede) 23 | (put 'paredit-backward-delete 'delete-selection 'supersede) 24 | (put 'paredit-newline 'delete-selection t) 25 | 26 | ;; Enable `paredit-mode' in the minibuffer, during `eval-expression'. 27 | (add-hook 'minibuffer-setup-hook 'conditionally-enable-paredit-mode) 28 | 29 | :bind ((:map paredit-mode-map 30 | ("C-w" . paredit-kill-region-or-backward-word) 31 | ("M-C-" . backward-kill-sexp) 32 | ("C-d" . paredit-forward-delete) 33 | ("M-(" . paredit-wrap-round) 34 | ("M-)" . paredit-wrap-round-from-behind) 35 | ("M-s-8" . paredit-wrap-square) 36 | ("M-s-9" . paredit-wrap-square-from-behind) 37 | ("M-s-(" . paredit-wrap-curly) 38 | ("M-s-)" . paredit-wrap-curly-from-behind)))) 39 | 40 | (defun paredit-wrap-round-from-behind () 41 | (interactive) 42 | (forward-sexp -1) 43 | (paredit-wrap-round) 44 | (insert " ") 45 | (forward-char -1)) 46 | 47 | (defun paredit-wrap-square-from-behind () 48 | (interactive) 49 | (forward-sexp -1) 50 | (paredit-wrap-square)) 51 | 52 | (defun paredit-wrap-curly-from-behind () 53 | (interactive) 54 | (forward-sexp -1) 55 | (paredit-wrap-curly)) 56 | 57 | (defun paredit-kill-region-or-backward-word () 58 | (interactive) 59 | (if (region-active-p) 60 | (kill-region (region-beginning) (region-end)) 61 | (paredit-backward-kill-word))) 62 | 63 | (defun conditionally-enable-paredit-mode () 64 | (when (or (eq this-command 'eval-expression) 65 | (eq this-command 'cider-run-in-dev-namespace)) 66 | (paredit-mode 1) 67 | (disable-inconvenient-paredit-keybindings-in-minor-mode))) 68 | 69 | (defun disable-inconvenient-paredit-keybindings-in-minor-mode () 70 | (let ((oldmap (cdr (assoc 'paredit-mode minor-mode-map-alist))) 71 | (newmap (make-sparse-keymap))) 72 | (set-keymap-parent newmap oldmap) 73 | (define-key newmap (kbd "RET") nil) 74 | (make-local-variable 'minor-mode-overriding-map-alist) 75 | (push `(paredit-mode . ,newmap) minor-mode-overriding-map-alist))) 76 | 77 | (provide 'setup-paredit) 78 | -------------------------------------------------------------------------------- /settings/navigation.el: -------------------------------------------------------------------------------- 1 | ;; Moving the cursor around 2 | 3 | ;; Like isearch, but adds region (if any) to history and deactivates mark 4 | (global-set-key (kbd "C-s") 'isearch-forward-use-region) 5 | (global-set-key (kbd "C-r") 'isearch-backward-use-region) 6 | 7 | ;; Navigate between windows using shift + arrow key 8 | (global-set-key (kbd "S-") 'windmove-right) 9 | (global-set-key (kbd "S-") 'windmove-left) 10 | (global-set-key (kbd "S-") 'windmove-up) 11 | (global-set-key (kbd "S-") 'windmove-down) 12 | 13 | ;: Keep them windows nice and balanced 14 | (global-set-key (kbd "C-x 0") 'my/delete-window) 15 | (global-set-key (kbd "C-x 3") 'my/split-window-right) 16 | 17 | ;; Move cursor back to indentation 18 | (global-set-key (kbd "M-i") 'back-to-indentation) 19 | 20 | ;; Navigate paragraphs 21 | (global-set-key (kbd "M-p") 'backward-paragraph) 22 | (global-set-key (kbd "M-n") 'forward-paragraph) 23 | 24 | ;; Move more quickly 25 | (global-set-key (kbd "C-S-n") (λ (ignore-errors (forward-line 5) (recenter)))) 26 | (global-set-key (kbd "C-S-p") (λ (ignore-errors (forward-line -5) (recenter)))) 27 | (global-set-key (kbd "C-S-f") (λ (ignore-errors (forward-char 5) (recenter)))) 28 | (global-set-key (kbd "C-S-b") (λ (ignore-errors (backward-char 5) (recenter)))) 29 | 30 | ;; Readily navigate in modes with significant whitespace 31 | (global-set-key (kbd "H-n") 'goto-next-line-with-same-indentation) 32 | (global-set-key (kbd "H-p") 'goto-prev-line-with-same-indentation) 33 | 34 | ;; Where was I again? 35 | (global-set-key (kbd "M-B") 'goto-last-modification) 36 | 37 | ;; No more scrolling surprises 38 | (global-unset-key (kbd "C-v")) 39 | (global-unset-key (kbd "M-v")) 40 | 41 | ;; Implementations 42 | 43 | (use-package windmove) 44 | 45 | (defun isearch-forward-use-region () 46 | (interactive) 47 | (when (region-active-p) 48 | (add-to-history 'search-ring (buffer-substring (region-beginning) 49 | (region-end))) 50 | (deactivate-mark)) 51 | (call-interactively 'isearch-forward)) 52 | 53 | (defun isearch-backward-use-region () 54 | (interactive) 55 | (when (region-active-p) 56 | (add-to-history 'search-ring (buffer-substring (region-beginning) 57 | (region-end))) 58 | (deactivate-mark)) 59 | (call-interactively 'isearch-backward)) 60 | 61 | (defun goto-next-line-with-same-indentation () 62 | (interactive) 63 | (back-to-indentation) 64 | (re-search-forward (s-concat "^" (s-repeat (current-column) " ") "[^ \t\r\n\v\f]") 65 | nil nil (if (= 0 (current-column)) 2 1)) 66 | (back-to-indentation)) 67 | 68 | (defun goto-prev-line-with-same-indentation () 69 | (interactive) 70 | (back-to-indentation) 71 | (re-search-backward (s-concat "^" (s-repeat (current-column) " ") "[^ \t\r\n\v\f]")) 72 | (back-to-indentation)) 73 | 74 | (defun goto-last-modification () 75 | (interactive) 76 | (undo-fu-only-undo) 77 | (undo-fu-only-redo)) 78 | 79 | (defun my/delete-window () 80 | (interactive) 81 | (delete-window) 82 | (balance-windows)) 83 | 84 | (defun my/split-window-right () 85 | (interactive) 86 | (split-window-right) 87 | (windmove-right) 88 | (balance-windows)) 89 | 90 | (provide 'navigation) 91 | -------------------------------------------------------------------------------- /settings/i18n-edn.el: -------------------------------------------------------------------------------- 1 | ;; Edit multiple i18n.edn files at once with multifile.el 2 | 3 | (require 'multifiles) 4 | (require 'tooling) 5 | (require 's) 6 | 7 | (defun i18n-edn--find-current-i18n-header () 8 | (save-excursion 9 | (unless (looking-at "#:") 10 | (unless (search-backward "#:" nil t) 11 | (when (search-forward "#:" nil t) 12 | (forward-char -2)))) 13 | (when (looking-at "#:") 14 | (let* ((beg (point)) 15 | (end (progn (paredit-forward) (point)))) 16 | (buffer-substring-no-properties beg end))))) 17 | 18 | (defun i18n-edn--find-files-with-same-extension () 19 | "Find all files in the current directory with the same extension as the current file." 20 | (let* ((current-file (buffer-file-name)) 21 | (current-dir (file-name-directory current-file)) 22 | (extension (file-name-extension current-file)) 23 | (search-pattern (concat "\\." extension "$"))) 24 | (directory-files current-dir nil search-pattern))) 25 | 26 | (defun i18n-edn--cleanup () 27 | (when (get-buffer "*i18n-multifile*") 28 | (with-current-buffer "*i18n-multifile*" 29 | (delete-region (point-min) (point-max))) 30 | (kill-buffer "*i18n-multifile*"))) 31 | 32 | (defvar i18n-heading 33 | ";; Editing files in %s 34 | ;; 35 | ;; C-x C-s to save all buffers, but keep this open. 36 | ;; C-c C-c to save all buffers and close this. 37 | ;; C-c C-q to just close this (changes will not be saved or undone) 38 | 39 | ") 40 | 41 | (defun i18n-edn-edit-in-multifile () 42 | (interactive) 43 | (if-let ((header (i18n-edn--find-current-i18n-header))) 44 | (progn 45 | (i18n-edn--cleanup) 46 | (let ((root-dir (s-chop-prefix 47 | (projectile-project-root) 48 | (file-name-directory (buffer-file-name))))) 49 | (save-excursion 50 | (switch-to-buffer-other-window "*i18n-multifile*") 51 | (insert (format i18n-heading root-dir)))) 52 | (--each (i18n-edn--find-files-with-same-extension) 53 | (with-current-buffer (find-file-noselect it) 54 | (save-excursion 55 | (goto-char (point-min)) 56 | (when (search-forward header nil t) 57 | (paredit-backward) 58 | (let* ((beg (point)) 59 | (end (progn (paredit-forward 2) (point)))) 60 | (with-current-buffer "*i18n-multifile*" 61 | (insert (format ";; %s\n" it))) 62 | (mf/mirror-region-in-multifile beg end "*i18n-multifile*")))))) 63 | (switch-to-buffer-other-window "*i18n-multifile*") 64 | (goto-line 6) 65 | (define-key multifiles-minor-mode-map (kbd "C-c C-c") 'i18n-edn-save-and-close) 66 | (define-key multifiles-minor-mode-map (kbd "C-c C-q") 'i18n-edn-close)) 67 | (message "No i18n header found. i18n-edn expects to find namespaced maps."))) 68 | 69 | (wrap-fullscreen i18n-edn-edit-in-multifile) 70 | 71 | (defun i18n-edn-close () 72 | (interactive) 73 | (let ((prev my/previous-window-configuration)) 74 | (i18n-edn--cleanup) 75 | (when prev (register-val-jump-to prev nil)))) 76 | 77 | (defun i18n-edn-save-and-close () 78 | (interactive) 79 | (mf/save-original-buffers) 80 | (i18n-edn-close)) 81 | 82 | (provide 'i18n-edn) 83 | -------------------------------------------------------------------------------- /.mc-lists.el: -------------------------------------------------------------------------------- 1 | ;; This file is automatically generated by the multiple-cursors extension. 2 | ;; It keeps track of your preferences for running commands with multiple cursors. 3 | 4 | (setq mc/cmds-to-run-for-all 5 | '( 6 | backward-kill-sexp 7 | backward-sexp 8 | change-number-at-point 9 | changes 10 | cider-eval-last-sexp 11 | cider-load-buffer 12 | clj-hippie-expand-no-case-fold 13 | cljr-raise-sexp 14 | cljr-slash 15 | cljr-splice-sexp-killing-backward 16 | clojure-backward-logical-sexp 17 | clojure-convert-collection-to-list 18 | clojure-convert-collection-to-set 19 | clojure-convert-collection-to-vector 20 | clojure-forward-logical-sexp 21 | clojure-thread-first-all 22 | clojure-toggle-keyword-string 23 | clojure-unwind 24 | comment-dwim 25 | completion-at-point 26 | copy-region-or-current-line 27 | cycle-spacing 28 | duplicate-current-line-or-region 29 | electric-newline-and-maybe-indent 30 | fill-paragraph 31 | forward-sexp 32 | inc-number-at-point 33 | indent-for-tab-command 34 | isearch-forward-use-region 35 | kill-region-or-backward-word 36 | kill-sentence 37 | kill-sexp 38 | kmacro-start-macro-or-insert-counter 39 | markdown-beginning-of-line 40 | markdown-end-of-line 41 | markdown-outdent-or-delete 42 | move-text-up 43 | open-line-and-indent 44 | open-line-below 45 | org-beginning-of-line 46 | org-delete-char 47 | org-end-of-line 48 | org-self-insert-command 49 | org-shiftright 50 | org-yank 51 | paredit-C-j 52 | paredit-C-j 53 | paredit-backward-down 54 | paredit-backward-kill-word 55 | paredit-backward-slurp-sexp 56 | paredit-backward-up 57 | paredit-close-curly 58 | paredit-close-round 59 | paredit-close-square 60 | paredit-comment-dwim 61 | paredit-doublequote 62 | paredit-forward 63 | paredit-forward-barf-sexp 64 | paredit-forward-delete 65 | paredit-forward-down 66 | paredit-forward-kill-word 67 | paredit-forward-slurp-sexp 68 | paredit-forward-up 69 | paredit-join-sexps 70 | paredit-kill 71 | paredit-kill-region-or-backward-word 72 | paredit-open-curly 73 | paredit-open-round 74 | paredit-open-square 75 | paredit-raise-sexp 76 | paredit-semicolon 77 | paredit-splice-sexp 78 | paredit-splice-sexp-killing-backward 79 | paredit-split-sexp 80 | paredit-wrap-round 81 | paredit-wrap-square 82 | reposition-window 83 | sort-lines 84 | subtract-number-at-point 85 | tagedit-insert-quote 86 | transpose-sexps 87 | upstream 88 | wdired--self-insert 89 | wgrep-mark-deletion 90 | yas-expand 91 | )) 92 | 93 | (setq mc/cmds-to-run-once 94 | '( 95 | beginning-of-buffer 96 | cleanup-buffer 97 | end-of-buffer 98 | find-file 99 | magit-status 100 | mouse-set-region 101 | persp-switch-to-buffer* 102 | wgrep-change-to-wgrep-mode 103 | xref-find-definitions 104 | )) 105 | -------------------------------------------------------------------------------- /users/teodorlu/tplay.el: -------------------------------------------------------------------------------- 1 | ;; A light Emacs interface for https://github.com/teodorlu/play.teod.eu 2 | 3 | ;; This code is based on older, messier code. 4 | ;; 5 | ;; Approach this time around: move in smaller steps. Don't copy the whole thing, 6 | ;; just the parts that are needed. 7 | 8 | (require 's) 9 | 10 | (defun tplay-clean () 11 | (interactive) 12 | (let ((default-directory "~/repo/teodorlu/play.teod.eu")) 13 | (shell-command-to-string "./play.clj makefile && make clean && make") 14 | (shell-command-to-string "./play.clj reindex"))) 15 | 16 | (defun tplay-create* (page-slug title form lang body) 17 | "Low-level function for creating pages. 18 | 19 | PAGE-SLUG: eg \"rudyard-kipling\" 20 | TITLE: eg \"Rudyard Kipling\" 21 | FORM: eg \":remote-reference\" or \"nil\" 22 | LANG: eg \":en\" or \":no\" 23 | BODY: nil, or the content of the document to create" 24 | (let ((default-directory "~/repo/teodorlu/play.teod.eu")) 25 | (shell-command-to-string (s-concat "./play.clj create-page" 26 | " :slug " page-slug 27 | " :title " (shell-quote-argument title) 28 | " :form " (shell-quote-argument form) 29 | " :lang " (shell-quote-argument lang) 30 | (when body 31 | (s-concat " :body " (shell-quote-argument body))))))) 32 | 33 | (defmacro comment (&rest body) nil) 34 | 35 | (comment 36 | (teod-play-create* "the-lollercoasters" "The Lollercoasters" "nil" ":en" "hello there") 37 | ) 38 | 39 | (defun tplay-create () 40 | (interactive) 41 | (let* ((page-slug (read-string "Page slug: ")) 42 | (title (read-string "Page title: ")) 43 | (form (completing-read ":form? > " '(":remote-reference" "nil"))) 44 | (lang (completing-read ":lang? > " '(":en" ":no"))) 45 | (default-directory "~/repo/teodorlu/play.teod.eu")) 46 | (tplay-create* page-slug title form lang nil) 47 | (tplay-clean) 48 | (switch-to-buffer (find-file-noselect page-slug)))) 49 | 50 | (defun tplay-youtube-embed () 51 | "Embed a youtube-video from its ID." 52 | (interactive) 53 | (let* ((youtube-video-id (read-string "Youtube video ID (like SxdOUGdseq4): "))) 54 | (cond ((eq major-mode 'org-mode) (insert (s-concat 55 | "#+begin_export html" 56 | "\n" 57 | "" 60 | "\n" 61 | "#+end_export" 62 | "\n"))) 63 | (t (message (s-concat "sorry: youtube links for " (symbol-name major-mode) " is not yet supported.")))))) 64 | 65 | (defun tplay-link () 66 | "Insert page link" 67 | (interactive) 68 | (let* ((default-directory "~/repo/teodorlu/play.teod.eu") 69 | (pages (s-with "bb -x tplay.linkui/page-titles" 70 | shell-command-to-string 71 | s-trim 72 | s-lines)) 73 | (page-title (completing-read "Select link> " pages)) 74 | (link (s-with (concat "bb -x tplay.linkui/title-to-link" " :title " (shell-quote-argument page-title)) 75 | shell-command-to-string 76 | s-trim))) 77 | (insert link))) 78 | 79 | (require 'parseedn) 80 | 81 | (parseedn-read-str ":select") 82 | (parseedn-read-str "{:select 1}") 83 | -------------------------------------------------------------------------------- /settings/clj-auto-refer-mode.el: -------------------------------------------------------------------------------- 1 | ;; clj-auto-refer-mode.el 2 | ;; 3 | ;; Requires and refers well-known functions and macros for you. 4 | 5 | (require 'dash) 6 | (require 'cider) 7 | (require 'clj-refactor) 8 | (require 'clj-clean-namespace) 9 | 10 | (defvar auto-refer-packages nil) 11 | ;; see setup-clj-refactor.el for usage (for now) 12 | 13 | (defun auto-refer--in-comment? () 14 | (nth 4 (syntax-ppss))) 15 | 16 | (defun auto-refer--in-string? () 17 | (nth 3 (syntax-ppss))) 18 | 19 | (defun auto-refer--find-usages (package) 20 | (let ((symbols-re (concat (regexp-opt '("(" "[")) 21 | (regexp-opt (-concat (plist-get package :functions) 22 | (plist-get package :macros)) 23 | 'symbols))) 24 | result) 25 | (save-excursion 26 | (goto-char (point-min)) 27 | (while (re-search-forward symbols-re nil t) 28 | (unless (or (auto-refer--in-comment?) 29 | (auto-refer--in-string?)) 30 | (!cons (cider-symbol-at-point) result)))) 31 | (-distinct result))) 32 | 33 | (defun auto-refer--remove-from-ns (type s) 34 | (cljr--goto-ns) 35 | (when (cljr--search-forward-within-sexp (concat "(" type)) 36 | (skip-syntax-forward " >") 37 | (while (not (looking-at ")")) 38 | (if (looking-at (regexp-quote (concat "[" s))) 39 | (cljr--delete-sexp) 40 | (paredit-forward)) 41 | (skip-syntax-forward " >")))) 42 | 43 | (defun auto-refer--update-clj-namespace (package) 44 | (let ((ns (plist-get package :ns))) 45 | (save-excursion 46 | (cljr--goto-ns) 47 | (unless (cljr--search-forward-within-sexp (concat ns " :as")) 48 | (let ((usages (auto-refer--find-usages package))) 49 | (auto-refer--remove-from-ns ":require" ns) 50 | (when usages 51 | (cljr--insert-in-ns ":require") 52 | (insert (concat "[" ns " :refer [")) 53 | (apply 'insert (->> usages 54 | (-sort 'string<) 55 | (-interpose " "))) 56 | (insert "]]")))) 57 | (clj-cn-sort-ns)))) 58 | 59 | (defun auto-refer--update-cljs-namespace (package) 60 | (let* ((ns (or (plist-get package :cljs-ns) 61 | (plist-get package :ns))) 62 | (macro-ns (or (plist-get package :cljs-macro-ns) 63 | ns)) 64 | (functions (plist-get package :functions)) 65 | (macros (plist-get package :macros))) 66 | (save-excursion 67 | (cljr--goto-ns) 68 | (unless (cljr--search-forward-within-sexp (concat ns " :as")) 69 | (let* ((usages (auto-refer--find-usages package)) 70 | (used-fns (--filter (member it functions) usages)) 71 | (used-macros (--filter (member it macros) usages))) 72 | (auto-refer--remove-from-ns ":require" ns) 73 | (auto-refer--remove-from-ns ":require-macros" macro-ns) 74 | (when used-fns 75 | (cljr--insert-in-ns ":require") 76 | (just-one-space) 77 | (insert (concat "[" ns " :refer [")) 78 | (apply 'insert (->> used-fns 79 | (-sort 'string<) 80 | (-interpose " "))) 81 | (insert "]]")) 82 | (when used-macros 83 | (cljr--insert-in-ns ":require-macros") 84 | (just-one-space) 85 | (insert (concat "[" macro-ns " :refer [")) 86 | (apply 'insert (->> used-macros 87 | (-sort 'string<) 88 | (-interpose " "))) 89 | (insert "]]")) 90 | (clj-cn-sort-ns)))))) 91 | 92 | (defun auto-refer-update-namespace () 93 | (interactive) 94 | (cond ((s-ends-with? ".clj" (buffer-file-name)) 95 | (--each auto-refer-packages 96 | (auto-refer--update-clj-namespace it))) 97 | 98 | ((s-ends-with? ".cljs" (buffer-file-name)) 99 | (--each auto-refer-packages 100 | (auto-refer--update-cljs-namespace it))))) 101 | 102 | (define-minor-mode auto-refer-mode 103 | "Auto refer mode" 104 | :lighter "" 105 | (if auto-refer-mode 106 | (add-hook 'before-save-hook 'auto-refer-update-namespace t t) 107 | (remove-hook 'before-save-hook 'auto-refer-update-namespace t))) 108 | 109 | (provide 'clj-auto-refer-mode) 110 | -------------------------------------------------------------------------------- /settings/cider-run.el: -------------------------------------------------------------------------------- 1 | ;;; cider-run.el --- A package for running commands via CIDER. -*- lexical-binding: t; -*- 2 | 3 | (require 'kaocha-runner) 4 | (require 'projectile) 5 | 6 | (defvar cider-run--out-buffer "*cider-run-output*") 7 | (defvar ns-regex "^(ns \\([^\s]+\\)") 8 | 9 | (defun cider-run--find-comment-forms () 10 | (when (re-search-forward "^(comment ;; s-:" nil t) 11 | (let (forms beg end) 12 | (while (ignore-errors 13 | (forward-sexp) (setq end (point)) 14 | (backward-sexp) (setq beg (point)) 15 | (!cons (buffer-substring-no-properties beg end) forms) 16 | (forward-sexp) 17 | t)) 18 | forms))) 19 | 20 | (defun cider-run--find-dev-namespace-and-comment-forms () 21 | "Find and return the first Clojure namespace in dev.clj files under dev/ and its subdirectories." 22 | (let ((project-root (projectile-project-root))) 23 | (when project-root 24 | (let ((find-cmd (format "find %sdev -type f -name dev.clj" project-root)) 25 | ns 26 | forms) 27 | (with-temp-buffer 28 | (call-process-shell-command find-cmd nil t) 29 | (goto-char (point-min)) 30 | (while (and (not ns) (not (eobp))) 31 | (let ((file (buffer-substring (line-beginning-position) (line-end-position)))) 32 | (with-temp-buffer 33 | (insert-file-contents file) 34 | (goto-char (point-min)) 35 | (when (re-search-forward ns-regex nil t) 36 | (setq ns (match-string 1)) 37 | (setq forms (cider-run--find-comment-forms))))) 38 | (forward-line 1))) 39 | (cons (s-trim ns) 40 | forms))))) 41 | 42 | (defun cider-run--run-in-repl (ns invocation) 43 | (interactive) 44 | (kaocha-runner--clear-buffer cider-run--out-buffer) 45 | (let ((buffer (cider-current-repl 'clj 'ensure))) 46 | (cider-nrepl-request:eval 47 | invocation 48 | (let ((original-buffer (current-buffer)) 49 | (any-errors? nil) 50 | (done? nil)) 51 | (lambda (response) 52 | (let ((showing? (get-buffer cider-run--out-buffer))) 53 | (nrepl-dbind-response response (value out err status) 54 | (unless done? 55 | (when (and (or out err) showing?) 56 | (with-current-buffer cider-run--out-buffer 57 | (insert (propertize (or out err) 'face 58 | (if out 59 | 'cider-repl-stdout-face 60 | 'cider-repl-stderr-face)))) 61 | (kaocha-runner--with-window cider-run--out-buffer original-buffer 62 | (window-resize nil (- (max 6 63 | (min 15 (1+ (line-number-at-pos (point-max))))) 64 | (window-height))) 65 | (goto-char (point-max)) 66 | (recenter (- -1 (min (max 0 scroll-margin) 67 | (truncate (/ (window-body-height) 4.0)))) t))) 68 | (when out 69 | (ignore-errors 70 | (cider-repl-emit-stdout buffer out))) 71 | (when err 72 | (setq any-errors? t) 73 | (ignore-errors 74 | (cider-repl-emit-stderr buffer err))) 75 | (when value 76 | (message "%s" value)) 77 | (when (and status (member "done" status)) 78 | (setq done? t) 79 | (when (and (not any-errors?) showing?) 80 | (run-with-timer 1 nil 'cider-run--kill-out-buffer)))))))) 81 | ns nil nil nil buffer))) 82 | 83 | (defun cider-run--kill-out-buffer () 84 | (kaocha-runner--hide-window cider-run--out-buffer) 85 | (kill-buffer cider-run--out-buffer)) 86 | 87 | (defun cider-run-in-dev-namespace () 88 | (interactive) 89 | (if-let ((ns+forms (cider-run--find-dev-namespace-and-comment-forms))) 90 | (cider-run--run-in-repl (car ns+forms) 91 | (completing-read (format "Eval in %s: " (car ns+forms)) 92 | (or (cdr ns+forms) 93 | cider-run-in-dev-namespace-default-suggestions))) 94 | (message "No dev namespace found"))) 95 | 96 | (defvar cider-run-in-dev-namespace-default-suggestions 97 | '("(start)" "(reset)")) 98 | 99 | (provide 'cider-run) 100 | -------------------------------------------------------------------------------- /settings/sane-defaults.el: -------------------------------------------------------------------------------- 1 | ;; Auto refresh buffers 2 | (use-package autorevert 3 | :defer 2 4 | :config (global-auto-revert-mode 1)) 5 | 6 | ;; Also auto refresh dired, but be quiet about it 7 | (setq global-auto-revert-non-file-buffers t) 8 | (setq auto-revert-verbose nil) 9 | 10 | ;; Show keybinding prefixes faster 11 | (setq echo-keystrokes 0.1) 12 | 13 | ;; Move files to trash when deleting 14 | (setq delete-by-moving-to-trash t) 15 | 16 | ;; Shift is more useful as a modifier 17 | (setq shift-select-mode nil) 18 | 19 | ;; Transparently open compressed files 20 | (auto-compression-mode t) 21 | 22 | ;; Answering just 'y' or 'n' will do 23 | (defalias 'yes-or-no-p 'y-or-n-p) 24 | 25 | ;; UTF-8 please 26 | (setq locale-coding-system 'utf-8) ; pretty 27 | (set-terminal-coding-system 'utf-8) ; pretty 28 | (set-keyboard-coding-system 'utf-8) ; pretty 29 | (set-selection-coding-system 'utf-8) ; please 30 | (prefer-coding-system 'utf-8) ; with sugar on top 31 | 32 | ;; Remove text in active region if inserting text 33 | (use-package delsel 34 | :defer 1 35 | :config (delete-selection-mode 1)) 36 | 37 | ;; Always display column numbers 38 | (setq column-number-mode t) 39 | 40 | ;; 80 chars is a good width. 41 | (set-default 'fill-column 80) 42 | 43 | ;; Save a list of recent files visited. (open recent file with C-x f) 44 | (use-package recentf 45 | :defer 1 ;; Loads after 1 second of idle time. 46 | :config (recentf-mode 1) 47 | :custom (recentf-max-saved-items 100)) ;; just 20 is too recent 48 | 49 | ;; Undo/redo window configuration with C-c / 50 | (use-package winner 51 | :defer 1 52 | :config (winner-mode 1)) 53 | 54 | ;; Never insert tabs 55 | (set-default 'indent-tabs-mode nil) 56 | 57 | ;; Show me empty lines after buffer end 58 | (set-default 'indicate-empty-lines t) 59 | 60 | ;; Easily navigate sillycased words 61 | (use-package subword 62 | :defer 1 63 | :config (global-subword-mode 1) 64 | :diminish subword-mode) 65 | 66 | ;; Don't visually break lines for me, please 67 | (setq-default truncate-lines t) 68 | 69 | ;; Sentences do not need double spaces to end. Period. 70 | (set-default 'sentence-end-double-space nil) 71 | 72 | ;; Add parts of each file's directory to the buffer name if not unique 73 | (use-package uniquify 74 | :ensure nil 75 | :defer 2 ;; Loads after 2 seconds of idle time. 76 | :custom (uniquify-buffer-name-style 'forward)) 77 | 78 | ;; Start Emacs server for emacsclient 79 | (use-package server 80 | :defer 2 81 | :config (unless (server-running-p) 82 | (server-start))) 83 | 84 | ;; Show more than 4 levels when evaling expressions 85 | (setq eval-expression-print-level 100) 86 | 87 | ;; Offer to create parent directories if they do not exist 88 | ;; http://iqbalansari.github.io/blog/2014/12/07/automatically-create-parent-directories-on-visiting-a-new-file-in-emacs/ 89 | (defun my-create-non-existent-directory () 90 | (let ((parent-directory (file-name-directory buffer-file-name))) 91 | (when (and (not (file-exists-p parent-directory)) 92 | (y-or-n-p (format "Directory `%s' does not exist! Create it?" parent-directory))) 93 | (make-directory parent-directory t)))) 94 | 95 | (add-to-list 'find-file-not-found-functions 'my-create-non-existent-directory) 96 | 97 | ;; Don't zoom individual buffers 98 | (global-unset-key (kbd "C-x C-+")) 99 | 100 | ;; Disable zooming with pinch / mouse wheel 101 | ;; 102 | ;; In addition to zooming with `C-x C-+` and `C-x C--`, there's a different, 103 | ;; buffer specific zoom that zooms insanel fast. 104 | (global-set-key (kbd "") 'ignore) 105 | (global-set-key (kbd "C-") 'mwheel-scroll) 106 | (global-set-key (kbd "C-") 'mwheel-scroll) 107 | 108 | ;; Write backup files to own directory 109 | (setq backup-directory-alist 110 | `(("." . ,(expand-file-name 111 | (concat user-emacs-directory "backups"))))) 112 | 113 | ;; Make backups of files, even when they're in version control 114 | (setq vc-make-backup-files t) 115 | 116 | ;; We use git. Don't try all the other VC systems in existence first. 117 | (setq vc-handled-backends '(Git)) 118 | 119 | ;; Don't write lock-files, I'm the only one here 120 | (setq create-lockfiles nil) 121 | 122 | ;; Write all autosave files in the tmp dir 123 | (setq auto-save-file-name-transforms 124 | `((".*" ,temporary-file-directory t))) 125 | 126 | ;; No electric indent 127 | (setq electric-indent-mode nil) 128 | 129 | ;; Automatically prompt for sudo in write protected files 130 | (use-package auto-sudoedit 131 | :defer 1 132 | :config (auto-sudoedit-mode 1)) 133 | 134 | (provide 'sane-defaults) 135 | -------------------------------------------------------------------------------- /packages/setup-clj-refactor.el: -------------------------------------------------------------------------------- 1 | (use-package clj-refactor 2 | :diminish clj-refactor-mode 3 | 4 | :custom 5 | (cljr-favor-prefix-notation nil) 6 | (cljr-favor-private-functions nil) 7 | (cljr-insert-newline-after-require nil) 8 | (cljr-assume-language-context "clj") 9 | 10 | :config 11 | (cljr-add-keybindings-with-modifier "C-s-") 12 | (define-key clojure-mode-map (kbd "C-S-M-u") (λ (cljr--goto-toplevel))) 13 | (define-key clojure-mode-map (kbd "C->") 'cljr-thread) 14 | (define-key clojure-mode-map (kbd "C-<") 'cljr-unwind) 15 | 16 | (setq cljr-clojure-test-declaration "[clojure.test :refer [deftest is testing]]") 17 | (setq cljr-cljs-clojure-test-declaration cljr-clojure-test-declaration) 18 | (setq cljr-cljc-clojure-test-declaration cljr-clojure-test-declaration) 19 | 20 | (add-hook 'clojure-mode-hook 'clj-refactor-mode) 21 | 22 | (require 'clj-auto-refer-mode) 23 | (setq auto-refer-packages 24 | '((:ns "clojure.core.async" 25 | :functions ("!" ">!!" "admix" "alt!" "alt!!" "alts!" "alts!!" "buffer" 26 | "chan" "close!" "do-alts" "dropping-buffer" "mix" "mult" "offer!" 27 | "onto-chan" "pipe" "pipeline" "pipeline-async" "pipeline-blocking" 28 | "poll!" "pub" "put!" "sliding-buffer" "solo-mode" "sub" "take!" 29 | "tap" "thread" "thread-call" "timeout" "to-chan" "unblocking-buffer?" 30 | "unmix" "unmix-all" "unsub" "unsub-all" "untap" "untap-all") 31 | :macros ("go" "go-loop") 32 | :cljs-ns "cljs.core.async" 33 | :cljs-macro-ns "cljs.core.async.macros") 34 | 35 | (:ns "clojure.test" 36 | :macros ("deftest" "testing" "is" "are")))) 37 | (add-hook 'clojure-mode-hook 'auto-refer-mode) 38 | 39 | (advice-add 'cljr-update-project-dependency :around #'my/cljr-handle-git-sha-deps)) 40 | 41 | (require 'json) 42 | 43 | (defun my/fetch-latest-github-commits (user repo num) 44 | (let* ((url (format "https://api.github.com/repos/%s/%s/commits?per_page=%s" user repo num)) 45 | (buffer (url-retrieve-synchronously url)) 46 | (json-array-type 'list)) 47 | (with-current-buffer buffer 48 | (goto-char (point-min)) 49 | (re-search-forward "^$") 50 | (delete-region (point) (point-min)) 51 | (let ((data (json-read))) 52 | (kill-buffer) 53 | (mapcar (lambda (commit) 54 | (let ((sha (cdr (assoc 'sha commit))) 55 | (message (cdr (assoc 'message (assoc 'commit commit)))) 56 | (timestamp (cdr (assoc 'date (assoc 'committer (assoc 'commit commit)))))) 57 | (list :sha sha :message message :timestamp timestamp))) 58 | data))))) 59 | 60 | (defun my/clj-refactor-upgrade-git-sha-dep () 61 | (interactive) 62 | (when (cljr--looking-at-dependency-p) 63 | (save-excursion 64 | (let ((kind (match-string-no-properties 2)) 65 | (url (match-string-no-properties 4))) 66 | (when (and (s-equals? ":git" kind) 67 | (s-contains? "github.com" url)) 68 | (-let [(_ user repo) (s-match "github.com/\\([^/]+\\)/\\([^/.]+\\).git" url)] 69 | (let ((commit (car (my/fetch-latest-github-commits user repo 1)))) 70 | (when (search-forward ":sha \"") 71 | (let ((old-sha (progn (looking-at "[a-z0-9]+") 72 | (match-string-no-properties 0)))) 73 | (if (s-equals? old-sha (plist-get commit :sha)) 74 | (message "Already at newest revision") 75 | (progn 76 | (delete-char (length old-sha)) 77 | (insert (plist-get commit :sha)) 78 | (message "Bumped to revision from %s\n%s" 79 | (plist-get commit :timestamp) 80 | (plist-get commit :message))))))))))))) 81 | 82 | (defun my/cljr-handle-git-sha-deps (orig-fn &rest args) 83 | (unless (my/clj-refactor-upgrade-git-sha-dep) 84 | (apply orig-fn args))) 85 | 86 | (defun cljr--add-test-declarations () 87 | (save-excursion 88 | (let* ((ns (clojure-find-ns)) 89 | (source-ns (cljr--find-source-ns-of-test-ns ns (buffer-file-name)))) 90 | (cljr--insert-in-ns ":require") 91 | (when source-ns 92 | (insert "[" source-ns " :as " 93 | (car (last (s-split "\\." source-ns))) "]")) 94 | (cljr--insert-in-ns ":require") 95 | (insert (cond 96 | ((cljr--project-depends-on-p "midje") 97 | cljr-midje-test-declaration) 98 | ((cljr--project-depends-on-p "expectations") 99 | cljr-expectations-test-declaration) 100 | ((cljr--cljs-file-p) 101 | cljr-cljs-clojure-test-declaration) 102 | ((cljr--cljc-file-p) 103 | cljr-cljc-clojure-test-declaration) 104 | (t cljr-clojure-test-declaration)))) 105 | (indent-region (point-min) (point-max)))) 106 | 107 | (provide 'setup-clj-refactor) 108 | -------------------------------------------------------------------------------- /packages/setup-makefile-mode.el: -------------------------------------------------------------------------------- 1 | ;;; Inject some Emacs into makefile-mode. 2 | 3 | (defun my/tab-indent (n) 4 | (back-to-indentation) 5 | (delete-horizontal-space) 6 | (--dotimes n 7 | (insert "\t"))) 8 | 9 | (defun my/in-process-of-adding-commands-to-rule? () 10 | "This will indicate indentation of the current line if we are 11 | working on a rule, but only if there is a blank line below. 12 | Otherwise we would add a TAB to all the blank lines between rules 13 | when cleaning the buffer." 14 | (interactive) 15 | (save-excursion 16 | (let ((current-line (thing-at-point 'line t)) 17 | (above-line nil) 18 | (below-line nil)) 19 | (forward-line -1) 20 | (setq above-line (thing-at-point 'line t)) 21 | (forward-line 2) 22 | (setq below-line (thing-at-point 'line t)) 23 | (and (equal "\n" current-line) ;; Current line is blank 24 | (or (string-match-p "^\t" above-line) ;; Above line is indented with a tab 25 | (string-match-p "^[^ \t\n#]+:" above-line)) ;; or is a rule definition 26 | (equal "\n" below-line))))) ;; Below line is blank 27 | 28 | (defun my/makefile-indent-line () 29 | "Indent current line as Makefile code." 30 | (interactive) 31 | (let* ((savep (point)) 32 | (indent-col 33 | (save-excursion 34 | (back-to-indentation) 35 | (when (>= (point) savep) (setq savep nil)) 36 | (beginning-of-line) 37 | (cond 38 | ((my/in-process-of-adding-commands-to-rule?) 1) 39 | 40 | ((or (looking-at "^[ \t]*$") ;; Blank line 41 | (looking-at "^[ \t]*#") ;; Comment line 42 | (looking-at "^[^ \t\n#]+ ?=") ;; Variable definition 43 | (looking-at "^[A-Z_]+$") ;; Variable definition in progress 44 | (looking-at "^[^ \t\n#]+:")) ;; Rule definition 45 | 0) 46 | (t 1))))) 47 | (if (null indent-col) 48 | 'noindent 49 | (if savep 50 | (save-excursion (my/tab-indent indent-col)) 51 | (my/tab-indent indent-col))))) 52 | 53 | (add-hook 'makefile-mode-hook 54 | (lambda () 55 | (setq-local indent-line-function 'my/makefile-indent-line))) 56 | 57 | (defun makefile-find-targets () 58 | "Find all targets in a Makefile." 59 | (let (targets) 60 | (save-excursion 61 | (goto-char (point-min)) 62 | (while (re-search-forward "^\\([^:#\t\n]+?\\):" nil t) 63 | (let ((target (match-string-no-properties 1))) 64 | (unless (s-equals? target ".PHONY") 65 | (setq targets (cons (string-trim target) targets)))))) 66 | (reverse targets))) 67 | 68 | (defun shorten-path (path) 69 | "Shortens the file PATH by replacing the home directory with ~." 70 | (let ((home (expand-file-name "~"))) 71 | (if (string-prefix-p home path) 72 | (concat "~" (substring path (length home))) 73 | path))) 74 | 75 | (defvar makefile--previous-window-configuration nil) 76 | (defvar makefile--previous-target nil) 77 | 78 | (defun makefile-invoke-target (&optional repeat?) 79 | (interactive) 80 | (let* ((file (concat (projectile-project-root) "Makefile")) 81 | (short-dir (shorten-path (projectile-project-root))) 82 | (default-directory (projectile-project-root)) 83 | (make-buffer-name (concat "*Make " (projectile-project-name) "*")) 84 | (prev (if (get-buffer make-buffer-name) 85 | (with-current-buffer make-buffer-name 86 | makefile--previous-window-configuration) 87 | (list (current-window-configuration) (point-marker)))) 88 | (target (or (and repeat? (with-current-buffer make-buffer-name 89 | makefile--previous-target)) 90 | (completing-read (format "Make in %s: " short-dir) 91 | (--map 92 | (concat "make " it) 93 | (with-temp-buffer 94 | (insert-file-contents file) 95 | (makefile-find-targets))))))) 96 | (if (file-exists-p file) 97 | (progn 98 | (async-shell-command target make-buffer-name) 99 | (unless (s-equals? (buffer-name) make-buffer-name) 100 | (switch-to-buffer-other-window make-buffer-name)) 101 | (setq-local makefile--previous-window-configuration prev) 102 | (setq-local makefile--previous-target target) 103 | (read-only-mode) 104 | (local-set-key (kbd "m") 'makefile-invoke-target) 105 | (local-set-key (kbd "g") (λ (makefile-invoke-target t))) 106 | (local-set-key (kbd "q") (λ (let ((conf makefile--previous-window-configuration)) 107 | (kill-buffer) 108 | (when conf (register-val-jump-to conf nil)))))) 109 | (message "No Makefile found in %s" short-dir)))) 110 | 111 | (global-set-key (kbd "s-m") 'makefile-invoke-target) 112 | 113 | (provide 'setup-makefile-mode) 114 | -------------------------------------------------------------------------------- /settings/css-completions.el: -------------------------------------------------------------------------------- 1 | (require 'cider) 2 | (require 'dash) 3 | (require 's) 4 | (require 'projectile) 5 | 6 | (defun cssc/find-css-files-in-project () 7 | (let ((root (projectile-project-root))) 8 | (->> (projectile-dir-files root) 9 | (--filter (s-ends-with? ".css" it)) 10 | (--map (concat root it))))) 11 | 12 | (defun cssc/read-file-into-string (filename) 13 | (with-temp-buffer 14 | (insert-file-contents filename) 15 | (buffer-string))) 16 | 17 | (defun cssc/read-project-css-file-contents () 18 | (apply #'concat 19 | (-map #'cssc/read-file-into-string 20 | (cssc/find-css-files-in-project)))) 21 | 22 | (setq cssc/well-known-file-endings 23 | '("jpg" "jpeg" "png" "svg")) 24 | 25 | (defun cssc/extract-css-class-names (string) 26 | "Extract all CSS class names from STRING." 27 | (let ((pattern "\\.[a-zA-Z][a-zA-Z0-9_-]+\\b") 28 | (pos 0) 29 | (hash (make-hash-table :test 'equal))) 30 | (while (string-match pattern string pos) 31 | (setq pos (match-end 0)) 32 | (puthash (substring (match-string 0 string) 1) t hash)) 33 | (--each cssc/well-known-file-endings 34 | (remhash it hash)) 35 | (hash-table-keys hash))) 36 | 37 | (defun cssc/inside-clojure-class-structure? () 38 | (or 39 | ;; matches {:class :bar} 40 | (save-excursion (ignore-errors (paredit-backward 2) 41 | (looking-at ":class"))) 42 | ;; matches {:class [:bar]} 43 | (save-excursion (ignore-errors (paredit-backward-up) 44 | (paredit-backward) 45 | (looking-at ":class"))) 46 | ;; matches (into class [:bar]) 47 | (save-excursion (ignore-errors (paredit-backward-up 2) 48 | (paredit-backward) 49 | (looking-at ":class"))))) 50 | 51 | (defun cssc/find-hiccup-class-name-position () 52 | "Are we completing a Clojure keyword with dots in it, like :div.foo.bar ?" 53 | (when-let ((bounds (bounds-of-thing-at-point 'symbol))) 54 | (when (not (or (cider-in-string-p) 55 | (cider-in-comment-p))) 56 | (let ((s (buffer-substring-no-properties (car bounds) 57 | (cdr bounds)))) 58 | (when (s-starts-with? ":" s) 59 | (when-let ((last-dot-index (string-match-p "\\.[^\\.]+$" s))) 60 | (list 61 | (+ last-dot-index (car bounds)) 62 | (cdr bounds) 63 | "."))))))) 64 | 65 | (defun cssc/find-keyword-class-name-position () 66 | "Are we completing a Clojure keyword inside some structure assigning to :class?" 67 | (when-let ((bounds (bounds-of-thing-at-point 'symbol))) 68 | (when (not (or (cider-in-string-p) 69 | (cider-in-comment-p))) 70 | (let ((s (buffer-substring-no-properties (car bounds) 71 | (cdr bounds)))) 72 | (when (and (s-starts-with? ":" s) 73 | (cssc/inside-clojure-class-structure?)) 74 | (list 75 | (car bounds) 76 | (cdr bounds) 77 | ":")))))) 78 | 79 | (defun cssc/find-string-class-name-position () 80 | "Are we completing a symbol in a string inside some structure assigning to :class?" 81 | (when-let ((bounds (bounds-of-thing-at-point 'symbol))) 82 | (when (and (cider-in-string-p) 83 | (cssc/inside-clojure-class-structure?)) 84 | (list 85 | (car bounds) 86 | (cdr bounds) 87 | "")))) 88 | 89 | (defun cssc/find-html-class-name-position () 90 | (when-let ((bounds (bounds-of-thing-at-point 'symbol))) 91 | (when (and (nth 3 (syntax-ppss)) 92 | (save-excursion 93 | (goto-char (nth 8 (syntax-ppss))) 94 | (looking-back "class=" (- (point) 10)))) 95 | (list 96 | (car bounds) 97 | (cdr bounds) 98 | "")))) 99 | 100 | (defvar cssc/find-class-name-position-fns ()) 101 | (make-variable-buffer-local 'cssc/find-class-name-position-fns) 102 | 103 | (defun cssc/css-classes-completion-at-point () 104 | (when-let ((res (--some (funcall it) cssc/find-class-name-position-fns))) 105 | (-let [(beg end prefix) res] 106 | (list beg end 107 | (--map (concat prefix it) 108 | (cssc/extract-css-class-names 109 | (cssc/read-project-css-file-contents))))))) 110 | 111 | (defun cssc/enable-for-clojure () 112 | "Sets up current buffer for clojure css completions. Run in a hook." 113 | (add-to-list 'completion-at-point-functions 114 | #'cssc/css-classes-completion-at-point) 115 | (setq cssc/find-class-name-position-fns 116 | (list #'cssc/find-hiccup-class-name-position 117 | #'cssc/find-keyword-class-name-position 118 | #'cssc/find-string-class-name-position))) 119 | 120 | (defun cssc/enable-for-html () 121 | (add-to-list 'completion-at-point-functions 122 | #'cssc/css-classes-completion-at-point) 123 | (setq cssc/find-class-name-position-fns 124 | (list #'cssc/find-html-class-name-position))) 125 | 126 | (defun cssc/skip-in-front-of-the-completion-chain () 127 | (when (-contains? completion-at-point-functions 128 | #'cssc/css-classes-completion-at-point) 129 | (remove-hook 'completion-at-point-functions 130 | #'cssc/css-classes-completion-at-point 131 | t) 132 | (add-to-list 'completion-at-point-functions 133 | #'cssc/css-classes-completion-at-point))) 134 | 135 | (add-hook 'lsp-completion-mode-hook 'cssc/skip-in-front-of-the-completion-chain) 136 | (add-hook 'cider-mode-hook 'cssc/skip-in-front-of-the-completion-chain) 137 | 138 | (provide 'css-completions) 139 | -------------------------------------------------------------------------------- /users/teodorlu/teodorlu.el: -------------------------------------------------------------------------------- 1 | (require 'matnyttig) 2 | 3 | ;; Auto-installed clojure-lsp has given me pain, so I use Homebrew. 4 | ;; This means `which clojure-lsp` finds the clojure-lsp that Emacs calls to. 5 | (setq lsp-clojure-custom-server-command '("clojure-lsp")) 6 | 7 | ;; Don't auto-wrap lines, I like one line per sentence 8 | ;; https://sive.rs/1s 9 | (remove-hook 'markdown-mode-hook 'auto-fill-mode) 10 | 11 | ;; A quicker save is helpful for non-interactive languages that require saving files to reload code 12 | (global-set-key (kbd "C-ø") 'save-buffer) 13 | 14 | (defun teodorlu-magnar-emacs-cheat-sheet () 15 | (interactive) 16 | (find-file (expand-file-name "~/.emacs.d/README.md"))) 17 | 18 | (defun teodorlu-today () 19 | (interactive) 20 | (insert (format-time-string "%Y-%m-%d"))) 21 | 22 | (defun teodorlu-current-time () 23 | (shell-command-to-string "echo -n `date \"+%H:%M\"`")) 24 | 25 | (defun teodorlu-now () 26 | (interactive) 27 | (insert (shell-command-to-string "echo -n `date \"+%H:%M\"`"))) 28 | 29 | (defun teodorlu-insert-en-dash () 30 | (interactive) 31 | (insert "-")) 32 | 33 | (defun teodorlu-insert-em-dash () 34 | (interactive) 35 | (insert "—")) 36 | 37 | (defun teodorlu-add-clerk () 38 | (interactive) 39 | (cider-interactive-eval "(clojure.repl.deps/add-lib 'io.github.nextjournal/clerk)")) 40 | 41 | (defun teodorlu-add-kaocha () 42 | (interactive) 43 | (cider-interactive-eval "(clojure.repl.deps/add-lib 'lambdaisland/kaocha)")) 44 | 45 | (defun clerk-tap-viewer () 46 | (interactive) 47 | (cider-interactive-eval "(nextjournal.clerk/show! 'nextjournal.clerk.tap)")) 48 | 49 | (defun clerk-serve () 50 | (interactive) 51 | (cider-interactive-eval "((requiring-resolve 'nextjournal.clerk/serve!) {:port 7799})")) 52 | 53 | (defun clerk-serve-browse () 54 | (interactive) 55 | (cider-interactive-eval "((requiring-resolve 'nextjournal.clerk/serve!) {:browse true :port 7799})")) 56 | 57 | (defun clerk-halt () 58 | (interactive) 59 | (cider-interactive-eval "(nextjournal.clerk/halt!)")) 60 | 61 | (defun teodorlu-clojure-remove-namespace () 62 | (interactive) 63 | (cider-interactive-eval "(remove-ns (symbol (str *ns*)))")) 64 | 65 | (defun teodorlu-garden-deploy () 66 | (interactive) 67 | (projectile-run-shell-command-in-root "garden deploy")) 68 | 69 | (use-package clay) 70 | (use-package go-mode) 71 | ;; Husk å installere gopls 72 | ;; go install golang.org/x/tools/gopls@latest 73 | (add-hook 'go-mode-hook 'lsp-deferred) 74 | 75 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 76 | ;; English/Norwegian spell check on Mac with Homebrew and Hunspell 77 | ;; 78 | ;; 1. Install hunspell 79 | ;; 80 | ;; brew install hunspell 81 | ;; 82 | ;; 2. Clone the hunspell dictionary repo to a temporary location 83 | ;; 84 | ;; dir="${HOME}/tmp/temp-$(date "+%Y-%m-%d")" 85 | ;; mkdir -p $dir 86 | ;; cd $dir 87 | ;; git clone git://anongit.freedesktop.org/libreoffice/dictionaries --depth=1 88 | ;; 89 | ;; 3. Install hunspell dictionaries for your languages of choice. 90 | ;; 91 | ;; cp -r dictionaries/en/* ~/Library/Spelling/ 92 | ;; cp -r dictionaries/no/* ~/Library/Spelling/ 93 | ;; 94 | ;; 4. Set Emacs to use hunspell for spelling 95 | ;; 96 | (setenv "DICPATH" (concat (getenv "HOME") "/Library/Spelling")) 97 | (setq ispell-program-name "/opt/homebrew/bin/hunspell") 98 | ;; 99 | ;; 5. Validate dictionary installation 100 | ;; 101 | ;; hunspell -D 102 | ;; 103 | ;; You should see your installed dictionaries under AVAILABLE DICTIONARIES. 104 | ;; 105 | ;; 6. Activate flyspell-mode when you want spell checking 106 | ;; 107 | (add-hook 'org-mode-hook (lambda () (flyspell-mode 1))) 108 | (add-hook 'markdown-mode-hook (lambda () (flyspell-mode 1))) 109 | ;; 110 | ;; 7. Finally, when you want to change the spell checking language, 111 | ;; 112 | ;; M-x ispell-change-dictionary 113 | 114 | (setq cider-repl-display-help-banner nil) 115 | (pixel-scroll-precision-mode 1) 116 | 117 | (defun teodorlu-mblog-create-olorm () 118 | "Write a new post at https://mikrobloggeriet.no/olorm/ with minimal friction!" 119 | (interactive) 120 | (projectile-run-shell-command-in-root "./mblog.sh create text/olorm")) 121 | 122 | (defun teodorlu-dir-locals () 123 | (interactive) 124 | (insert (prin1-to-string 125 | '((nil 126 | (cider-clojure-cli-aliases . "-A:dev") 127 | (cider-preferred-build-tool . clojure-cli)))))) 128 | 129 | (defun teodorlu-make () 130 | (interactive) 131 | (projectile-run-shell-command-in-root "make")) 132 | 133 | ;; Teach projectile where to find projects 134 | (setq projectile-project-search-path '(("~/repo" . 2) ; github projects 135 | ("~/p" . 2) ; learn stuff 136 | )) 137 | ;; Find more repos :: M-x projectile-discover-projects-in-search-path 138 | ;; Cleanup :: M-x projectile-cleanup-known-projects 139 | 140 | (defun teodorlu-insert-bb-edn-refer-deps-edn () 141 | (interactive) 142 | (let* ((ancestors (thread-last (f-this-file) f-dirname f-split nreverse)) 143 | (project (car ancestors)) 144 | (org (cadr ancestors)) 145 | (bb-edn-string (s-concat "{:deps {io.github." 146 | org "/" project 147 | " {:local/root \".\"}}}"))) 148 | (insert bb-edn-string))) 149 | 150 | (setq browse-at-remote-prefer-symbolic nil) 151 | (global-set-key (kbd "C-x v w") 'browse-at-remote-kill) 152 | 153 | (defun matnyttig-bb-dev () 154 | (interactive) 155 | (async-shell-command 156 | (concat "/Applications/Ghostty.app/Contents/MacOS/ghostty" 157 | " --working-directory=/Users/teodorlu/repo/Mattilsynet/matnyttig" 158 | " -e zsh -c \"bb dev\"") 159 | nil nil)) 160 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # .emacs.d 2 | 3 | An Emacs configuration for Norwegian keyboard on OSX (lol), as seen on emacsrocks.com and parens-of-the-dead.com. 4 | 5 | ## Installation 6 | 7 | Download [Emacs for Mac OSX](http://emacsformacosx.com/). 8 | 9 | ## Out of band dependencies 10 | 11 | - Spell checking: 12 | 13 | ``` 14 | brew install aspell --lang=en 15 | ``` 16 | 17 | - Static analysis for Clojure with LSP 18 | 19 | ``` 20 | brew install clojure-lsp/brew/clojure-lsp-native 21 | ``` 22 | 23 | or get the nightly: 24 | 25 | ``` 26 | bash <(curl https://raw.githubusercontent.com/clojure-lsp/clojure-lsp/master/install) --version nightly --dir ~/bin 27 | ``` 28 | 29 | (place somewhere on your path before homebrew) 30 | 31 | 32 | - Fast grepping: 33 | 34 | ``` 35 | brew install ripgrep 36 | ``` 37 | 38 | - Make dired happy, install `gls` as replacement for `ls`: 39 | 40 | ``` 41 | brew install xz coreutils 42 | ``` 43 | 44 | - Stop clojure-lsp from adding duplicate namespace declarations: 45 | 46 | Open `~/.config/clojure-lsp/config.edn` and add: 47 | 48 | ``` 49 | {:auto-add-ns-to-new-files? false} 50 | ``` 51 | 52 | This is already handled better by clj-refactor (which also inserts test 53 | declarations in relevant namespaces). 54 | 55 | - Add dependencies to the project without CIDER running: 56 | 57 | ``` 58 | brew install babashka/brew/neil 59 | ``` 60 | 61 | - Static analysis for CSS with LSP 62 | 63 | ``` 64 | npm install -g vscode-langservers-extracted 65 | ``` 66 | 67 | - On a Mac you might want to do this, to disable `C-M-d` in your OS, making it 68 | available for `paredit-forward-down`: 69 | 70 | ``` 71 | defaults write com.apple.symbolichotkeys AppleSymbolicHotKeys -dict-add 70 'enabled' 72 | ``` 73 | 74 | ## Tips for using these emacs settings 75 | 76 | If you want to use my settings straight out of the box, here are some things to note: 77 | 78 | * This is my personal emacs configuration. I am constantly tuning it to my 79 | preferences. You should consider doing the same. Maybe start with a blank emacs + 80 | [Technomancy's better-defaults package](https://git.sr.ht/~technomancy/better-defaults), 81 | and then dig through this repo for useful nuggets, instead of forking it directly. 82 | 83 | * The key bindings are optimized for a norwegian keyboard layout. 84 | 85 | * You quit emacs with `C-x r q`, mnemonic *Really Quit*. 86 | 87 | * Find file in project with `C-x p f`, in dir with `C-x C-f`, recent with `C-x f` 88 | 89 | * Switch to a project with `C-x p p` 90 | 91 | * Add your user- and project-specific stuff in .emacs.d/users/[machine name]/*.el 92 | 93 | * `C-h` is rebound to backspace, like in the shell. Get help on `F1` instead. 94 | 95 | * Autocomplete with `C-,` or `C-.`, try both to get a feel for them. (autocomplete entire lines with `C-:`) 96 | 97 | * expand-region is your friend. Find its bound key by doing `F1 f er/expand-region` 98 | 99 | * Undo with `C-_` and redo with `M-_`. Watch the undo-tree with `C-x u` 100 | 101 | * Indent and clean up white space in the entire buffer with `C-c n` 102 | 103 | * On a mac, the Meta key `M` is bound to Command. 104 | 105 | * I recommend rebinding Caps Lock to Ctrl and use that instead of the often badly placed Ctrl-key. 106 | 107 | * Watch [emacsrocks.com](http://emacsrocks.com) 108 | 109 | ## Survival guide for the first week of emacs 110 | 111 | When you start using emacs for the first time, your habits fight you every inch 112 | of the way. Your fingers long for the good old familiar keybindings. Here's an 113 | overview of the most commonly used shortcuts to get you through this pain: 114 | 115 | * `C ` Shorthand for the ctrl-key 116 | * `M ` Shorthand for the meta-key (bound to command on my mac settings) 117 | * `S ` Shorthand for the shift-key 118 | * `s ` Shorthand for the super-key (bount to option on my mac settings) 119 | 120 | ### Files 121 | 122 | * `C-x C-f` Open a file. Starts in the current directory 123 | * `C-x f ` Open a recently visited file 124 | * `C-x p f` Open a file in the current project (based on .git ++) 125 | * `C-x C-s` Save this file 126 | * `C-x C-w` Save as ... 127 | * `C-x C-j` Jump to this files' current directory 128 | * `C-x b ` Switch to another open file (buffer) 129 | * `C-x C-b` List all open files (buffers) 130 | 131 | ### Cut copy and paste 132 | 133 | * `C-space` Start marking stuff. C-g to cancel. 134 | * `C-w ` Cut (aka kill) 135 | * `C-k ` Cut till end of line 136 | * `M-w ` Copy 137 | * `C-y ` Paste (aka yank) 138 | * `M-y ` Cycle last paste through previous kills 139 | * `C-x C-y` Choose what to paste from previous kills 140 | * `C-@ ` Mark stuff quickly. Press multiple times 141 | 142 | ### General 143 | 144 | * `C-g ` Quit out of whatever mess you've gotten yourself into 145 | * `M-x ` Run a command by name 146 | * `C-. ` Autocomplete 147 | * `C-_ ` Undo 148 | * `M-_ ` Redo 149 | * `C-x u ` Show the undo-tree 150 | * `C-x m ` Open magit. It's a magical git interface for emacs 151 | 152 | ### Navigation 153 | 154 | * `C-arrow` Move past words/paragraphs 155 | * `C-a ` Go to start of line 156 | * `C-e ` Go to end of line 157 | * `M-g M-g` Go to line number 158 | * `C-x C-i` Go to symbol 159 | * `C-s ` Search forward. Press `C-s` again to go further. 160 | * `C-r ` Search backward. Press `C-r` again to go further. 161 | 162 | ### Window management 163 | 164 | * `C-x 0 ` Close this window 165 | * `C-x 1 ` Close other windows 166 | * `C-x 2 ` Split window horizontally 167 | * `C-x 3 ` Split window vertically 168 | * `S-arrow` Jump to window to the left/right/up/down 169 | 170 | ### Help 171 | 172 | * `F1 t ` Basic tutorial 173 | * `F1 k ` Help for a keybinding 174 | * `F1 r ` Emacs' extensive documentation 175 | -------------------------------------------------------------------------------- /packages/setup-magit.el: -------------------------------------------------------------------------------- 1 | (use-package magit 2 | :defer t 3 | 4 | :custom 5 | (magit-section-initial-visibility-alist '((untracked . show) 6 | (unstaged . show) 7 | (unpushed . show) 8 | (unpulled . show) 9 | (stashes . show))) 10 | (magit-diff-refine-hunk t) 11 | (magit-push-always-verify nil) 12 | (magit-revert-buffers 'silent) 13 | (magit-no-confirm '(stage-all-changes 14 | unstage-all-changes)) 15 | 16 | :bind (("C-x m" . magit-status) 17 | ("C-c p" . magit-toggle-pair-programming-mode) 18 | ("C-c P" . magit-add-pair-programming-partner) 19 | (:map git-commit-mode-map 20 | ("C-c C-p" . magit-add-pair-programming-partner)) 21 | (:map magit-status-mode-map 22 | ("q" . magit-quit))) 23 | 24 | :config 25 | (wrap-fullscreen magit-status) 26 | (wrap-fullscreen magit-init) 27 | 28 | ;; move cursor into position when entering commit message 29 | (add-hook 'git-commit-mode-hook 'my/magit-cursor-fix)) 30 | 31 | (use-package git-timemachine 32 | :defer t 33 | :bind (("C-x v t" . git-timemachine))) 34 | 35 | (defun b-a-r-k-commit () 36 | (interactive) 37 | (browse-at-remote-kill) 38 | (message "Remote was killed 💀")) 39 | 40 | (defun b-a-r-k-branch () 41 | (interactive) 42 | (setq browse-at-remote-prefer-symbolic t) 43 | (browse-at-remote-kill) 44 | (setq browse-at-remote-prefer-symbolic nil) 45 | (message "Remote was killed 🪓🪵")) 46 | 47 | (use-package browse-at-remote 48 | :defer t 49 | :custom 50 | (browse-at-remote-prefer-symbolic nil) 51 | :bind (("C-x v w" . b-a-r-k-commit) 52 | ("C-x v W" . b-a-r-k-branch))) 53 | 54 | (defun kill-magit-buffers () 55 | (let ((current (current-buffer))) 56 | (dolist (buf (magit-mode-get-buffers)) 57 | (unless (eq buf current) 58 | (kill-buffer buf))))) 59 | 60 | (defun magit-quit () 61 | "Like magit-mode-bury-buffer, but also restores the window 62 | configuration stored by magit-status-fullscreen" 63 | (interactive) 64 | (let ((prev my/previous-window-configuration)) 65 | (kill-magit-buffers) 66 | (funcall magit-bury-buffer-function 'kill-buffer) 67 | (when prev (register-val-jump-to prev nil)))) 68 | 69 | (defvar magit-pair-programming-partners nil) 70 | 71 | (defun is-commit-message-buffer? () 72 | (when (buffer-file-name) 73 | (equal (buffer-file-name-body) "COMMIT_EDITMSG"))) 74 | 75 | (defun magit-insert-pair-programming-co-authors () 76 | (dolist (partner magit-pair-programming-partners) 77 | (unless (save-excursion 78 | (goto-char (point-min)) 79 | (search-forward (concat "Co-authored-by: " (car partner)) nil t)) 80 | (apply #'git-commit-insert-header "Co-authored-by" partner)))) 81 | 82 | (defun magit-enable-pair-programming-mode (details) 83 | (add-to-list 'magit-pair-programming-partners details) 84 | (when (is-commit-message-buffer?) 85 | (magit-insert-pair-programming-co-authors)) 86 | (add-hook 'git-commit-setup-hook 'magit-insert-pair-programming-co-authors) 87 | (message "Enabled pair programming with %s" (car details))) 88 | 89 | (defun magit-disable-pair-programming-mode () 90 | (setq magit-pair-programming-partners nil) 91 | (when (is-commit-message-buffer?) 92 | (save-excursion 93 | (goto-char (point-min)) 94 | (flush-lines "Co-authored-by"))) 95 | (remove-hook 'git-commit-setup-hook 'magit-insert-pair-programming-co-authors) 96 | (message "Disabled pair programming")) 97 | 98 | (defvar my/pair-programming-usual-suspects nil) 99 | (defvar my/pair-programming-myself nil) 100 | 101 | (defun my/remove-same-name (a b) 102 | (let ((names (--map (car (s-slice-at "<" it)) b))) 103 | (-remove (lambda (elem) 104 | (--any (s-starts-with? it elem) names)) 105 | a))) 106 | 107 | (defun my/git-commit-read-ident-candidates () 108 | (let* ((users-from-git-history (my/remove-same-name (delete-dups 109 | (magit-git-lines "log" "-n9999" "--format=%aN <%ae>")) 110 | my/pair-programming-myself)) 111 | (usual-suspects-in-history (-intersection users-from-git-history 112 | my/pair-programming-usual-suspects)) 113 | (missing-usual-suspects (my/remove-same-name my/pair-programming-usual-suspects 114 | usual-suspects-in-history))) 115 | (delete-dups 116 | (-concat usual-suspects-in-history 117 | missing-usual-suspects 118 | users-from-git-history)))) 119 | 120 | (defun my/git-commit-read-ident (prompt) 121 | (let ((str (magit-completing-read 122 | prompt 123 | (my/git-commit-read-ident-candidates) 124 | nil nil nil 'git-commit-read-ident-history))) 125 | (save-match-data 126 | (if (string-match "\\`\\([^<]+\\) *<\\([^>]+\\)>\\'" str) 127 | (list (save-match-data (string-trim (match-string 1 str))) 128 | (string-trim (match-string 2 str))) 129 | (user-error "Invalid input"))))) 130 | 131 | (defun magit-toggle-pair-programming-mode () 132 | (interactive) 133 | (if magit-pair-programming-partners 134 | (magit-disable-pair-programming-mode) 135 | (magit-enable-pair-programming-mode 136 | (my/git-commit-read-ident "Pair programming with")))) 137 | 138 | (defun magit-add-pair-programming-partner () 139 | (interactive) 140 | (magit-enable-pair-programming-mode 141 | (my/git-commit-read-ident "Pair programming with"))) 142 | 143 | (defun my/magit-cursor-fix () 144 | (beginning-of-buffer) 145 | (when (looking-at "#") 146 | (while (looking-at "#") 147 | (forward-line)) 148 | (forward-line))) 149 | 150 | (provide 'setup-magit) 151 | -------------------------------------------------------------------------------- /packages/setup-cider.el: -------------------------------------------------------------------------------- 1 | ;; CIDER 2 | ;; 3 | ;; Extends Emacs with support for interactive programming in Clojure. The 4 | ;; features are centered around cider-mode, an Emacs minor-mode that complements 5 | ;; clojure-mode. While clojure-mode supports editing Clojure source files, 6 | ;; cider-mode adds support for interacting with a running Clojure process for 7 | ;; compilation, debugging, definition and documentation lookup, running tests 8 | ;; and so on. 9 | 10 | (defun nrepl-warn-when-not-connected () 11 | (interactive) 12 | (message "Oops! You're not connected to an nREPL server. Please run M-x cider or M-x cider-jack-in to connect.")) 13 | 14 | (defun my/shadow-cider-keys-with-warning () 15 | "Rebind all keys from `cider-mode-map` to `nrepl-warn-when-not-connected` in `clojure-mode-map`." 16 | (interactive) 17 | (map-keymap 18 | (lambda (key def) 19 | ;; Check if 'def' is a command or another keymap. 20 | (cond ((commandp def) 21 | ;; If 'def' is a command, rebind it in `clojure-mode-map`. 22 | (define-key clojure-mode-map (vector key) 'nrepl-warn-when-not-connected)) 23 | ((keymapp def) 24 | ;; If 'def' is another keymap, recursively apply the same process. 25 | (map-keymap 26 | (lambda (sub-key sub-def) 27 | (when (commandp sub-def) 28 | (define-key clojure-mode-map (vconcat (vector key) (vector sub-key)) 29 | 'nrepl-warn-when-not-connected))) 30 | def)))) 31 | cider-mode-map)) 32 | 33 | (use-package cider 34 | :after (clojure-mode) 35 | :diminish " CIDER" 36 | :defer t 37 | 38 | :bind ((:map cider-mode-map 39 | ([remap cider-quit] . sesman-quit)) 40 | (:map cider-repl-mode-map 41 | ([remap cider-quit] . sesman-quit))) 42 | :config 43 | ;; Warn about missing nREPL instead of doing stupid things 44 | (my/shadow-cider-keys-with-warning) 45 | 46 | ;; Show the port number when figwheel comes online 47 | (add-to-list 'cider--repl-stderr-functions #'my/cider-maybe-log-figwheel-main-port) 48 | 49 | ;; Clear CIDER repl buffer with C-c C-l 50 | (define-key cider-mode-map (kbd "C-c C-l") 'cider-find-and-clear-repl-buffer) 51 | (define-key cider-repl-mode-map (kbd "C-c C-l") 'cider-repl-clear-buffer) 52 | (define-key clojure-mode-map (kbd "C-c C-l") 'nrepl-warn-when-not-connected) 53 | 54 | ;; Keybinding to switch to repl-buffer even if it is the wrong kind 55 | (define-key clojure-mode-map (kbd "C-c z") 'my/cider-select-repl-buffer) 56 | (define-key cider-repl-mode-map (kbd "C-c z") 'my/cider-select-repl-buffer) 57 | 58 | (autoload 'cider-run-in-dev-namespace "cider-run") 59 | (define-key clojure-mode-map (kbd "s-:") 'cider-run-in-dev-namespace) 60 | (define-key cider-repl-mode-map (kbd "s-:") 'cider-run-in-dev-namespace) 61 | 62 | (define-key cider-mode-map (kbd "C-c M-w") 'my/cider-eval-to-clipboard) 63 | (define-key cider-mode-map (kbd "C-c C-M-w") 'my/cider-eval-defun-to-clipboard) 64 | 65 | :custom 66 | ;; save files when evaluating them 67 | (cider-save-file-on-load t) 68 | 69 | ;; try working around a bug where sesman is way too friendly when selecting 70 | ;; repl-s from adjacent projects 71 | ;; https://clojurians.slack.com/archives/C0617A8PQ/p1718608002044069 72 | (sesman-use-friendly-sessions nil) 73 | 74 | ;; don't pop up repl when connecting 75 | (cider-repl-pop-to-buffer-on-connect nil) 76 | 77 | ;; re-use dead buffers without asking me about it when there is only one choice 78 | (cider-reuse-dead-repls 'auto) 79 | 80 | ;; show stacktraces for everything, until https://github.com/clojure-emacs/cider/issues/3495 is solved 81 | (cider-clojure-compilation-error-phases nil) 82 | 83 | ;; always scroll output from interactive evaluations into view 84 | (cider-repl-display-output-before-window-boundaries t)) 85 | 86 | (defun my/cider-maybe-log-figwheel-main-port (buffer out) 87 | (when (string-match-p "\\[Figwheel\\] Starting Server at" out) 88 | (message (propertize out 'face 'cider-repl-stderr-face)))) 89 | 90 | (defun my/get-sessions-with-same-project-dir (session sessions) 91 | "Returns a list of SESSIONS with the same project-dir as SESSION." 92 | (--filter 93 | (s-equals? (plist-get (cider--gather-session-params session) :project-dir) 94 | (plist-get (cider--gather-session-params it) :project-dir)) 95 | sessions)) 96 | 97 | (defun my/cider-select-repl-buffer () 98 | (interactive) 99 | (pop-to-buffer 100 | (let ((buffer-names (-map #'buffer-name 101 | (or (cider--extract-connections (my/get-sessions-with-same-project-dir 102 | (sesman-current-session 'CIDER) 103 | (sesman-current-sessions 'CIDER))) 104 | (user-error "No linked %s sessions" 'CIDER))))) 105 | (if (cdr buffer-names) 106 | (completing-read "Which REPL, Sire?" buffer-names nil t) 107 | (car buffer-names))))) 108 | 109 | (defun cider-find-and-clear-repl-buffer () 110 | (interactive) 111 | (cider-find-and-clear-repl-output t)) 112 | 113 | (defun my/cider-eval-to-clipboard () 114 | "Evaluate the Clojure form at point and put the result on the clipboard." 115 | (interactive) 116 | (let ((form (cider-last-sexp))) 117 | (cider-nrepl-request:eval 118 | form 119 | (lambda (response) 120 | (when (nrepl-dict-get response "value") 121 | (let ((result (nrepl-dict-get response "value"))) 122 | (kill-new result) 123 | (message "Result copied to clipboard: %s" result)))) 124 | (cider-current-ns)))) 125 | 126 | (defun my/cider-eval-defun-to-clipboard () 127 | "Evaluate the current top-level form and copy the result to the clipboard." 128 | (interactive) 129 | (cider-nrepl-request:eval 130 | (cider-defun-at-point) 131 | (lambda (response) 132 | (when (nrepl-dict-get response "value") 133 | (let ((result (nrepl-dict-get response "value"))) 134 | (kill-new result) 135 | (message "Result copied to clipboard: %s" result)))) 136 | (cider-current-ns))) 137 | 138 | (provide 'setup-cider) 139 | -------------------------------------------------------------------------------- /settings/clj-clean-namespace.el: -------------------------------------------------------------------------------- 1 | ;; Provides basic namespace sorting functionality for clojure 2 | 3 | (require 'dash) 4 | (require 's) 5 | 6 | (defun clj-cn--goto-ns () 7 | (goto-char (point-min)) 8 | (if (re-search-forward clojure-namespace-name-regex nil t) 9 | (clj-cn--goto-toplevel) 10 | (error "No namespace declaration found"))) 11 | 12 | (defun clj-cn--search-forward-within-sexp (s &optional save-excursion) 13 | "Searches forward for S in the current sexp. 14 | 15 | if SAVE-EXCURSION is T POINT does not move." 16 | (let ((bound (save-excursion (forward-list 1) (point)))) 17 | (if save-excursion 18 | (save-excursion 19 | (re-search-forward (concat s "\\()\\| \\|$\\)") bound t)) 20 | (when-let ((result (re-search-forward (concat s "\\()\\| \\|$\\)") bound t))) 21 | (when (or (looking-back " " 1) 22 | (looking-back ")" 1)) 23 | (forward-char -1)) 24 | result)))) 25 | 26 | (defun clj-cn--comment-line? () 27 | (save-excursion 28 | (back-to-indentation) 29 | (or (looking-at ";") 30 | (looking-at "#_")))) 31 | 32 | (defun clj-cn--delete-and-extract-sexp () 33 | (let (sexp comment-before comment-after beg end) 34 | ;; start at the beginning 35 | (setq beg (point)) 36 | 37 | ;; move forward to the the first non-comment sexp 38 | (clojure-forward-logical-sexp) 39 | (while (clj-cn--comment-line?) 40 | (clojure-forward-logical-sexp)) 41 | (clojure-backward-logical-sexp) 42 | 43 | ;; extract any full-line comments before the sexp 44 | (setq comment-before (s-trim (buffer-substring-no-properties beg (point)))) 45 | 46 | ;; extract the sexp itself 47 | (let ((here (point))) 48 | (clojure-forward-logical-sexp) 49 | (setq sexp (buffer-substring-no-properties here (point)))) 50 | 51 | ;; extract any inline comments after the sexp 52 | (when (looking-at "\s*;") 53 | (let ((here (point))) 54 | (end-of-line) 55 | (setq comment-after (buffer-substring-no-properties here (point))))) 56 | 57 | (let ((contents (buffer-substring beg (point)))) 58 | (delete-region beg (point)) 59 | (list :sexp sexp 60 | :contents contents 61 | :comment-before (unless (string= "" comment-before) 62 | comment-before) 63 | :comment-after comment-after)))) 64 | 65 | (defun clj-cn--end-of-statement? () 66 | (not 67 | (ignore-errors 68 | (save-excursion (forward-sexp)) t))) 69 | 70 | (defun clj-cn--extract-ns-statements (statement-type) 71 | (clj-cn--goto-ns) 72 | (when (and (clj-cn--search-forward-within-sexp (concat "(" statement-type)) 73 | (not (clj-cn--comment-line?))) 74 | (let (statements) 75 | (while (not (clj-cn--end-of-statement?)) 76 | (push (clj-cn--delete-and-extract-sexp) 77 | statements)) 78 | statements))) 79 | 80 | (defun clj-cn--prepare-insert-in-ns (type) 81 | (clj-cn--goto-ns) 82 | (if (clj-cn--search-forward-within-sexp (concat "(" type)) 83 | (if (looking-at " *)") 84 | (progn 85 | (search-backward "(") 86 | (forward-list 1) 87 | (forward-char -1) 88 | (insert " ")) 89 | (search-backward "(") 90 | (forward-list 1) 91 | (forward-char -1) 92 | (newline-and-indent)) 93 | (forward-list 1) 94 | (forward-char -1) 95 | (newline-and-indent) 96 | (insert "(" type " )") 97 | (forward-char -1))) 98 | 99 | (defun clj-cn--goto-toplevel () 100 | (paredit-backward-up (cljr--depth-at-point)) 101 | (when (looking-back "#") 102 | (backward-char))) 103 | 104 | (defun clj-cn--extract-ns-form () 105 | (save-excursion 106 | (clj-cn--goto-ns) 107 | (let ((beg (point)) 108 | (end (progn (paredit-forward) 109 | (point)))) 110 | (buffer-substring-no-properties beg end)))) 111 | 112 | (defun clj-cn--comparator (a b) 113 | (string< (plist-get a :sexp) 114 | (plist-get b :sexp))) 115 | 116 | (defun clj-cn--remove-blank-lines (beg end) 117 | (save-excursion 118 | (goto-char beg) 119 | (while (re-search-forward "^\\s-*$" end t) 120 | (delete-char 1) 121 | (setq end (- end 1))))) 122 | 123 | (defun clj-cn--clean-up (statement-type f) 124 | (clj-cn--goto-ns) 125 | (when (and (clj-cn--search-forward-within-sexp (concat "(" statement-type)) 126 | (not (clj-cn--comment-line?))) 127 | (paredit-backward-up) 128 | (let ((beg (point)) 129 | (end (progn (paredit-forward) 130 | (point)))) 131 | (funcall f beg end)))) 132 | 133 | (defun clj-cn-sort-ns () 134 | "Sort the `ns' form." 135 | (interactive) 136 | (let ((buf-already-modified? (buffer-modified-p)) 137 | (ns-form-before (clj-cn--extract-ns-form))) 138 | (save-excursion 139 | (dolist (statement-type '(":require" ":use" ":import" ":require-macros")) 140 | (clj-cn--goto-ns) 141 | (when (clj-cn--search-forward-within-sexp (concat "(" statement-type)) 142 | (let* ((own-line? (eolp)) 143 | (statements (clj-cn--extract-ns-statements statement-type)) 144 | (sorted-statement (->> statements 145 | (nreverse) 146 | (-sort 'clj-cn--comparator) 147 | (-distinct)))) 148 | (while (not (looking-at ")")) 149 | (delete-char 1)) 150 | (dolist (it sorted-statement) 151 | (clj-cn--prepare-insert-in-ns statement-type) 152 | (when own-line? 153 | (delete-char -1) 154 | (insert "\n")) 155 | (insert (s-trim (plist-get it :contents))) 156 | (when (plist-get it :comment-after) 157 | (insert "\n"))) 158 | (clj-cn--clean-up statement-type #'clj-cn--remove-blank-lines) 159 | (clj-cn--clean-up statement-type #'indent-region))))) 160 | (when (and (not buf-already-modified?) 161 | (buffer-modified-p) 162 | (s-equals? ns-form-before 163 | (clj-cn--extract-ns-form))) 164 | (not-modified)))) 165 | 166 | (provide 'clj-clean-namespace) 167 | -------------------------------------------------------------------------------- /settings/multifiles.el: -------------------------------------------------------------------------------- 1 | ;;; multifiles.el --- View and edit parts of multiple files in one buffer 2 | 3 | ;; Copyright (C) 2011 Magnar Sveen 4 | 5 | ;; Author: Magnar Sveen 6 | ;; Keywords: multiple files 7 | 8 | ;; This program is free software; you can redistribute it and/or modify 9 | ;; it under the terms of the GNU General Public License as published by 10 | ;; the Free Software Foundation, either version 3 of the License, or 11 | ;; (at your option) any later version. 12 | 13 | ;; This program is distributed in the hope that it will be useful, 14 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ;; GNU General Public License for more details. 17 | 18 | ;; You should have received a copy of the GNU General Public License 19 | ;; along with this program. If not, see . 20 | 21 | ;;; Commentary: 22 | 23 | ;; Bind a key to `mf/mirror-region-in-multifile`, let's say `C-!`. Now 24 | ;; mark a part of the buffer and press it. A new \*multifile\* buffer pops 25 | ;; up. Mark some other part of another file, and press `C-!` again. This 26 | ;; is added to the \*multifile\*. 27 | 28 | ;; You can now edit the \*multifile\* buffer, and watch the original files change. 29 | ;; Or you can edit the original files and watch the \*multifile\* buffer change. 30 | 31 | ;; **Warning** This API and functionality is highly volatile. 32 | 33 | ;;; Code: 34 | 35 | (require 'dash) 36 | 37 | (defun mf/mirror-region-in-multifile (beg end &optional multifile-buffer) 38 | (interactive (list (region-beginning) (region-end) 39 | (when current-prefix-arg 40 | (read-buffer "Mirror into buffer: " "*multifile*")))) 41 | (deactivate-mark) 42 | (let ((buffer (current-buffer)) 43 | (mode major-mode)) 44 | (switch-to-buffer-other-window (or multifile-buffer "*multifile*")) 45 | (funcall mode) 46 | (multifiles-minor-mode 1) 47 | (mf--add-mirror buffer beg end) 48 | (switch-to-buffer-other-window buffer))) 49 | 50 | (defvar multifiles-minor-mode-map nil 51 | "Keymap for multifiles minor mode.") 52 | 53 | (unless multifiles-minor-mode-map 54 | (setq multifiles-minor-mode-map (make-sparse-keymap))) 55 | 56 | (define-key multifiles-minor-mode-map (vector 'remap 'save-buffer) 'mf/save-original-buffers) 57 | 58 | (defun mf/save-original-buffers () 59 | (interactive) 60 | (--each (mf--original-buffers) 61 | (with-current-buffer it 62 | (when buffer-file-name 63 | (save-buffer))))) 64 | 65 | (defun mf--original-buffers () 66 | (->> (overlays-in (point-min) (point-max)) 67 | (--filter (equal 'mf-mirror (overlay-get it 'type))) 68 | (--map (overlay-buffer (overlay-get it 'twin))) 69 | (-distinct))) 70 | 71 | (define-minor-mode multifiles-minor-mode 72 | "A minor mode for the *multifile* buffer." 73 | :lighter "" 74 | :keymap multifiles-minor-mode-map) 75 | 76 | (defun mf--add-mirror (buffer beg end) 77 | (let (contents original-overlay mirror-overlay) 78 | (mf--add-hook-if-necessary) 79 | (with-current-buffer buffer 80 | (mf--add-hook-if-necessary) 81 | (setq contents (buffer-substring beg end)) 82 | (setq original-overlay (create-original-overlay beg end))) 83 | (mf---insert-contents) 84 | (setq mirror-overlay (create-mirror-overlay beg end)) 85 | (overlay-put mirror-overlay 'twin original-overlay) 86 | (overlay-put original-overlay 'twin mirror-overlay))) 87 | 88 | (defun mf---insert-contents () 89 | (end-of-buffer) 90 | (newline) 91 | (setq beg (point)) 92 | (insert contents) 93 | (setq end (point)) 94 | (newline 2)) 95 | 96 | (defun mf--any-overlays-in-buffer () 97 | (--any? (memq (overlay-get it 'type) '(mf-original mf-mirror)) 98 | (overlays-in (point-min) (point-max)))) 99 | 100 | (defun mf--add-hook-if-necessary () 101 | (unless (mf--any-overlays-in-buffer) 102 | (add-hook 'post-command-hook 'mf--update-twins))) 103 | 104 | (defun mf--remove-hook-if-necessary () 105 | (unless (mf--any-overlays-in-buffer) 106 | (remove-hook 'post-command-hook 'mf--update-twins))) 107 | 108 | (defun create-original-overlay (beg end) 109 | (let ((o (make-overlay beg end nil nil t))) 110 | (overlay-put o 'type 'mf-original) 111 | (overlay-put o 'modification-hooks '(mf--on-modification)) 112 | (overlay-put o 'insert-in-front-hooks '(mf--on-modification)) 113 | (overlay-put o 'insert-behind-hooks '(mf--on-modification)) 114 | o)) 115 | 116 | (defun create-mirror-overlay (beg end) 117 | (let ((o (make-overlay beg end nil nil t))) 118 | (overlay-put o 'type 'mf-mirror) 119 | (overlay-put o 'line-prefix mf--mirror-indicator) 120 | (overlay-put o 'modification-hooks '(mf--on-modification)) 121 | (overlay-put o 'insert-in-front-hooks '(mf--on-modification)) 122 | (overlay-put o 'insert-behind-hooks '(mf--on-modification)) 123 | o)) 124 | 125 | (defvar mf--changed-overlays nil) 126 | (make-variable-buffer-local 'mf--changed-overlays) 127 | 128 | (defun mf--on-modification (o after? beg end &optional delete-length) 129 | (when (not after?) 130 | (when (mf---removed-entire-overlay o) 131 | (mf--remove-mirror o))) 132 | 133 | (when (and after? (not (null (overlay-start o)))) 134 | (add-to-list 'mf--changed-overlays o))) 135 | 136 | (defun mf---removed-entire-overlay (o) 137 | (and (<= beg (overlay-start o)) 138 | (>= end (overlay-end o)))) 139 | 140 | (defun mf--update-twins () 141 | (when mf--changed-overlays 142 | (-each mf--changed-overlays 'mf--update-twin) 143 | (setq mf--changed-overlays nil))) 144 | 145 | (defun mf--remove-mirror (o) 146 | (let* ((twin (overlay-get o 'twin)) 147 | (original (if (mf--is-original o) o twin)) 148 | (mirror (if (mf--is-original o) twin o)) 149 | (mirror-beg (overlay-start mirror)) 150 | (mirror-end (overlay-end mirror))) 151 | (with-current-buffer (overlay-buffer mirror) 152 | (save-excursion 153 | (delete-overlay mirror) 154 | (delete-region mirror-beg mirror-end) 155 | (goto-char mirror-beg) 156 | (delete-blank-lines) 157 | (mf--remove-hook-if-necessary))) 158 | (with-current-buffer (overlay-buffer original) 159 | (delete-overlay original) 160 | (mf--remove-hook-if-necessary)))) 161 | 162 | (defun mf--is-original (o) 163 | (equal 'mf-original (overlay-get o 'type))) 164 | 165 | (defun mf--update-twin (o) 166 | (let* ((beg (overlay-start o)) 167 | (end (overlay-end o)) 168 | (contents (buffer-substring beg end)) 169 | (twin (overlay-get o 'twin)) 170 | (buffer (overlay-buffer twin)) 171 | (beg (overlay-start twin)) 172 | (end (overlay-end twin))) 173 | (with-current-buffer buffer 174 | (save-excursion 175 | (goto-char beg) 176 | (insert contents) 177 | (delete-char (- end beg)) 178 | )))) 179 | 180 | (defvar mf--mirror-indicator "| ") 181 | (add-text-properties 182 | 0 1 183 | `(face (:foreground ,(format "#%02x%02x%02x" 128 128 128) 184 | :background ,(format "#%02x%02x%02x" 128 128 128))) 185 | mf--mirror-indicator) 186 | 187 | (provide 'multifiles) 188 | 189 | ;;; multifiles.el ends here 190 | -------------------------------------------------------------------------------- /settings/editing.el: -------------------------------------------------------------------------------- 1 | ;; Manipulating the contents of a buffer 2 | 3 | ;; Killing words backwards 4 | (global-set-key (kbd "C-w") 'kill-region-or-backward-word) 5 | (global-set-key (kbd "M-h") 'kill-region-or-backward-word) ;; matches C-h 6 | 7 | ;; Duplicate region 8 | (global-set-key (kbd "C-c d") 'duplicate-current-line-or-region) 9 | 10 | ;; Increase and decrease number at point 11 | (global-set-key (kbd "C-+") 'inc-number-at-point) 12 | (global-set-key (kbd "C-?") 'dec-number-at-point) 13 | 14 | ;; Clean up whitespace 15 | (global-set-key (kbd "C-c n") 'cleanup-buffer) 16 | 17 | ;; Copy to end of current line if no region 18 | (global-set-key (kbd "M-w") 'copy-region-or-current-line) 19 | 20 | ;; Completion at point 21 | (global-set-key (kbd "C-,") 'completion-at-point) 22 | 23 | ;; Use shell-like backspace C-h, rebind help to F1 24 | (define-key key-translation-map [?\C-h] [?\C-?]) 25 | (global-set-key (kbd "") 'help-command) 26 | 27 | ;; Revert entire buffer without any fuss 28 | (global-set-key (kbd "M-") (λ (revert-buffer t t))) 29 | 30 | ;; Join lines with ease 31 | (global-set-key (kbd "M-j") (λ (join-line -1))) 32 | 33 | ;; Query replace regex key binding 34 | (global-set-key (kbd "M-&") 'query-replace-regexp) 35 | 36 | ;; Delete blank lines 37 | (global-set-key (kbd "C-c C-") 'delete-blank-lines) 38 | 39 | ;; Eval emacs-lisp expressions anywhere. 40 | (global-set-key (kbd "C-c C-e") 'eval-and-replace) 41 | (global-set-key (kbd "M-s-e") 'eval-and-replace) 42 | 43 | ;; Clever newlines 44 | (global-set-key (kbd "C-o") 'open-line-and-indent) 45 | (global-set-key (kbd "") 'open-line-below) 46 | (global-set-key (kbd "") 'open-line-above) 47 | (global-set-key (kbd "") 'new-line-dwim) 48 | 49 | ;; Move whole lines 50 | (global-set-key (kbd "") 'move-text-down) 51 | (global-set-key (kbd "") 'move-text-up) 52 | 53 | ;; Sorting lines alphabetically 54 | (global-set-key (kbd "M-s l") 'sort-lines) 55 | 56 | ;; Display and edit occurances of regexp in buffer 57 | (global-set-key (kbd "C-c o") 'occur) 58 | 59 | ;;;; Implementations 60 | 61 | (use-package move-text) 62 | 63 | (defun duplicate-region (&optional num start end) 64 | "Duplicates the region bounded by START and END NUM times. 65 | If no START and END is provided, the current region-beginning and 66 | region-end is used." 67 | (interactive "p") 68 | (save-excursion 69 | (let* ((start (or start (region-beginning))) 70 | (end (or end (region-end))) 71 | (region (buffer-substring start end))) 72 | (goto-char end) 73 | (dotimes (i num) 74 | (insert region))))) 75 | 76 | (defun paredit-duplicate-current-line () 77 | (back-to-indentation) 78 | (let (kill-ring kill-ring-yank-pointer) 79 | (paredit-kill) 80 | (yank) 81 | (newline-and-indent) 82 | (yank))) 83 | 84 | (defun duplicate-current-line (&optional num) 85 | "Duplicate the current line NUM times." 86 | (interactive "p") 87 | (if (bound-and-true-p paredit-mode) 88 | (paredit-duplicate-current-line) 89 | (save-excursion 90 | (when (eq (point-at-eol) (point-max)) 91 | (goto-char (point-max)) 92 | (newline) 93 | (forward-char -1)) 94 | (duplicate-region num (point-at-bol) (1+ (point-at-eol)))))) 95 | 96 | (defun duplicate-current-line-or-region (arg) 97 | "Duplicates the current line or region ARG times. 98 | If there's no region, the current line will be duplicated." 99 | (interactive "p") 100 | (if (region-active-p) 101 | (let ((beg (region-beginning)) 102 | (end (region-end))) 103 | (duplicate-region arg beg end) 104 | (one-shot-keybinding "d" (λ (duplicate-region 1 beg end)))) 105 | (duplicate-current-line arg) 106 | (one-shot-keybinding "d" 'duplicate-current-line))) 107 | 108 | (defun incs (s &optional num) 109 | (let* ((inc (or num 1)) 110 | (new-number (number-to-string (+ inc (string-to-number s)))) 111 | (zero-padded? (s-starts-with? "0" s))) 112 | (if zero-padded? 113 | (s-pad-left (length s) "0" new-number) 114 | new-number))) 115 | 116 | (defun goto-closest-number () 117 | (interactive) 118 | (let ((closest-behind (save-excursion (search-backward-regexp "[0-9]" nil t))) 119 | (closest-ahead (save-excursion (search-forward-regexp "[0-9]" nil t)))) 120 | (push-mark) 121 | (goto-char 122 | (cond 123 | ((and (not closest-ahead) (not closest-behind)) (error "No numbers in buffer")) 124 | ((and closest-ahead (not closest-behind)) closest-ahead) 125 | ((and closest-behind (not closest-ahead)) closest-behind) 126 | ((> (- closest-ahead (point)) (- (point) closest-behind)) closest-behind) 127 | ((> (- (point) closest-behind) (- closest-ahead (point))) closest-ahead) 128 | :else closest-ahead)))) 129 | 130 | (defun inc-number-at-point (arg) 131 | (interactive "p") 132 | (unless (or (looking-at "[0-9]") 133 | (looking-back "[0-9]")) 134 | (goto-closest-number)) 135 | (save-excursion 136 | (while (looking-back "[0-9]") 137 | (forward-char -1)) 138 | (re-search-forward "[0-9]+" nil) 139 | (replace-match (incs (match-string 0) arg) nil nil))) 140 | 141 | (defun dec-number-at-point (arg) 142 | (interactive "p") 143 | (inc-number-at-point (- arg))) 144 | 145 | (defun cleanup-buffer () 146 | "Perform a bunch of operations on the whitespace content of a buffer. 147 | Including indent-buffer, which should not be called automatically on save." 148 | (interactive) 149 | (untabify (point-min) (point-max)) 150 | (delete-trailing-whitespace) 151 | (indent-region (point-min) (point-max))) 152 | 153 | (defun kill-region-or-backward-word () 154 | (interactive) 155 | (if (region-active-p) 156 | (kill-region (region-beginning) (region-end)) 157 | (backward-kill-word 1))) 158 | 159 | (defun copy-to-end-of-line () 160 | (interactive) 161 | (kill-ring-save (point) 162 | (line-end-position)) 163 | (message "Copied to end of line")) 164 | 165 | (defun copy-region-or-current-line () 166 | (interactive) 167 | (if (region-active-p) 168 | (kill-ring-save (region-beginning) (region-end)) 169 | (copy-to-end-of-line))) 170 | 171 | (defun eval-and-replace () 172 | "Replace the preceding sexp with its value." 173 | (interactive) 174 | (backward-kill-sexp) 175 | (condition-case nil 176 | (prin1 (eval (read (current-kill 0))) 177 | (current-buffer)) 178 | (error (message "Invalid expression") 179 | (insert (current-kill 0))))) 180 | 181 | (defun open-line-and-indent () 182 | (interactive) 183 | (newline-and-indent) 184 | (end-of-line 0) 185 | (indent-for-tab-command)) 186 | 187 | (defun open-line-below () 188 | (interactive) 189 | (end-of-line) 190 | (newline) 191 | (indent-for-tab-command)) 192 | 193 | (defun open-line-above () 194 | (interactive) 195 | (beginning-of-line) 196 | (newline) 197 | (forward-line -1) 198 | (indent-for-tab-command)) 199 | 200 | (defun new-line-dwim () 201 | (interactive) 202 | (let ((break-open-pair (or (and (looking-back "{" 1) (looking-at "}")) 203 | (and (looking-back ">" 1) (looking-at "<")) 204 | (and (looking-back "(" 1) (looking-at ")")) 205 | (and (looking-back "\\[" 1) (looking-at "\\]"))))) 206 | (newline) 207 | (when break-open-pair 208 | (save-excursion 209 | (newline) 210 | (indent-for-tab-command))) 211 | (indent-for-tab-command))) 212 | 213 | (provide 'editing) 214 | -------------------------------------------------------------------------------- /settings/matnyttig.el: -------------------------------------------------------------------------------- 1 | (require 'parseedn) 2 | 3 | ;; The following code is a starting point for integrating Emacs with 4 | ;; "matnyttig", our Clojure code base. 5 | ;; 6 | ;; At first, I tried cider-interactive-eval. Then, I was sad. 7 | ;; cider-interactive-eval doesn't block, and doesn't provide an easy-to-use 8 | ;; mechanism to return control to the function that called it. For example, you 9 | ;; may get multiple responses if you're running multiple REPLs. 10 | ;; 11 | ;; The next thing I want to try is to shell out to Babashka. 12 | ;; shell-command-to-string just returns the script's stdout, so it's perfect for 13 | ;; our use. 14 | 15 | (defun matnyttig-add-page () 16 | (interactive) 17 | (let* ((page-id (read-string "page-id: "))) 18 | (cider-interactive-eval (concat "(do" 19 | " (require 'matnyttig.page-admin)" 20 | " (matnyttig.page-admin/add \"" page-id "\")" 21 | ")")))) 22 | 23 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 24 | ;; Renaming a page requires listing existing pages! This is hard with CIDER, but 25 | ;; maybe easy with Babashka. My CIDER-based starting point follows. 26 | ;; 27 | ;; (defun matnyttig-list-pages () 28 | ;; ;; (interactive) 29 | ;; (let* ((pages-edn-str (cider-interactive-eval 30 | ;; (concat "(do" 31 | ;; " (require 'matnyttig.feed-admin)" 32 | ;; " (matnyttig.feed-admin/list-pages)" 33 | ;; ")")))) 34 | ;; (parseedn-read-str pages-edn-str))) 35 | 36 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 37 | ;; Helper code to develop interactive Emacs functions with a primitive UI. 38 | ;; 39 | ;; (defun demo-callback (something) 40 | ;; (message something)) 41 | ;; 42 | ;; (defun demo () 43 | ;; ;; (interactive) 44 | ;; (let* ((edn-str (cider-interactive-eval "(map (partial * 10) '(1 2 3))" 45 | ;; 'demo-callback)) 46 | ;; (elisp-values (parseedn-read-str edn-str))) 47 | ;; elisp-values)) 48 | ;; ;; then M-x demo from a buffer with a running REPL. 49 | 50 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 51 | ;; Evaluate and print with e->map 52 | 53 | (defun matnyttig-wrap-e->map (form) 54 | (format "((or (requiring-resolve 'clojure.core/e->map) identity) %s)" form)) 55 | 56 | (defun matnyttig-cider-pprint-eval-last-sexp-with-e->map (prefixed) 57 | (interactive "P") 58 | (if prefixed 59 | (cider--pprint-eval-form (cider-last-sexp)) 60 | (cider--pprint-eval-form 61 | (matnyttig-wrap-e->map (cider-last-sexp))))) 62 | 63 | (defun matnyttig-cider-pprint-eval-defun-at-point-with-e->map (prefixed) 64 | (interactive "P") 65 | (if prefixed 66 | (cider--pprint-eval-form (cider-defun-at-point)) 67 | (cider--pprint-eval-form 68 | (matnyttig-wrap-e->map (cider-defun-at-point))))) 69 | 70 | (define-key cider-mode-map (kbd "C-c C-p") #'matnyttig-cider-pprint-eval-last-sexp-with-e->map) 71 | (define-key cider-mode-map (kbd "C-c C-f") #'matnyttig-cider-pprint-eval-defun-at-point-with-e->map) 72 | 73 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 74 | ;; Find first class definitions (like feeds, etc.) 75 | 76 | ;; Patterns 77 | (defun matnyttig-collector-pattern (thing) 78 | (format "(source-definition/define-collector\n {:id %s" thing)) 79 | 80 | (defun matnyttig-refiner-pattern (thing) 81 | (format "(source-definition/define-refiner\n {:id %s" thing)) 82 | 83 | (defun matnyttig-feed-pattern (thing) 84 | (format "(source-definition/define-feed\n {:id %s" thing)) 85 | 86 | (defun matnyttig-page-pattern (thing) 87 | (format "(page-definition/define\n {:id %s" thing)) 88 | 89 | ;; Files 90 | (defun matnyttig-src-files () 91 | (directory-files-recursively 92 | (file-name-concat (projectile-project-root) "src") 93 | "\\.clj[sc]?$")) 94 | 95 | (defun matnyttig-collector-files (files) 96 | (--filter (string-match-p "/collectors/" it) files)) 97 | 98 | (defun matnyttig-refiner-files (files) 99 | (--filter (string-match-p "/refiners/" it) files)) 100 | 101 | (defun matnyttig-feed-files (files) 102 | (--filter (string-match-p "/feeds/" it) files)) 103 | 104 | (defun matnyttig-page-files (files) 105 | (--filter (string-match-p "/sider/" it) files)) 106 | 107 | ;; Effects 108 | (defun matnyttig-find-effect-definition (thing) 109 | (interactive) 110 | (let ((effects-file (file-name-concat (projectile-project-root) "src/matnyttig/imperative_shell/effects.clj")) 111 | (result nil)) 112 | (when (file-exists-p effects-file) 113 | (with-temp-buffer 114 | (insert-file-contents effects-file) 115 | (goto-char (point-min)) 116 | (when (search-forward "(case (:effect/kind effect)" nil t) 117 | (when (search-forward thing nil t) 118 | (paredit-forward-down) 119 | (when (re-search-backward 120 | (format "(defn \\(\\^{:indent 1} \\)?%s" 121 | (thing-at-point 'symbol t))) 122 | (setq result (cons effects-file (line-number-at-pos))))))) 123 | result))) 124 | 125 | ;; Logic 126 | (defun matnyttig-search-first-class-definition (files pattern) 127 | (let ((result nil)) 128 | (catch 'found 129 | (dolist (file files) 130 | (with-temp-buffer 131 | (insert-file-contents file) 132 | (goto-char (point-min)) 133 | (when (search-forward pattern nil t) 134 | (setq result (cons file (line-number-at-pos))) 135 | (throw 'found t))))) 136 | result)) 137 | 138 | (defun matnyttig-goto-first-class-definition (floc) 139 | (xref-push-marker-stack) 140 | (find-file (car floc)) 141 | (goto-line (cdr floc)) 142 | (goto-char (point-at-eol))) 143 | 144 | (defun matnyttig-find-first-class-definition () 145 | (interactive) 146 | (let ((thing (thing-at-point 'symbol t)) 147 | (matnyttig-src-files (matnyttig-src-files)) 148 | (floc nil)) 149 | (cond 150 | ((string-prefix-p ":feed/" thing) 151 | (setq floc (matnyttig-search-first-class-definition 152 | (matnyttig-feed-files matnyttig-src-files) 153 | (matnyttig-feed-pattern thing)))) 154 | 155 | ((string-prefix-p ":pages/" thing) 156 | (setq floc (matnyttig-search-first-class-definition 157 | (matnyttig-page-files matnyttig-src-files) 158 | (matnyttig-page-pattern thing)))) 159 | 160 | ((string-prefix-p ":data/" thing) 161 | (when-let ((result (matnyttig-search-first-class-definition 162 | (matnyttig-collector-files matnyttig-src-files) 163 | (matnyttig-collector-pattern thing)))) 164 | (setq floc result)) 165 | (when-let ((result (matnyttig-search-first-class-definition 166 | (matnyttig-refiner-files matnyttig-src-files) 167 | (matnyttig-refiner-pattern thing)))) 168 | (setq floc result))) 169 | 170 | ((string-prefix-p ":effects." thing) 171 | (setq floc (matnyttig-find-effect-definition thing)))) 172 | (if floc 173 | (matnyttig-goto-first-class-definition floc) 174 | (xref-find-definitions thing)))) 175 | 176 | (define-key clojure-mode-map (kbd "M-.") 'matnyttig-find-first-class-definition) 177 | 178 | ;; Ignore annoyingly abundant files that hang Emacs on Vertico analyses 179 | (with-eval-after-load 'lsp-mode 180 | (add-to-list 'lsp-file-watch-ignored-directories "[/\\\\].nats-cache\\'")) 181 | 182 | (provide 'matnyttig) 183 | -------------------------------------------------------------------------------- /packages/setup-clojure-mode.el: -------------------------------------------------------------------------------- 1 | (use-package clojure-mode 2 | :hook ((clojure-mode . setup-clojure-mode-so) 3 | (clojurescript-mode-hook . setup-clojure-mode-so) 4 | (clojurec-mode-hook . setup-clojure-mode-so)) 5 | 6 | :custom 7 | (clojure-toplevel-inside-comment-form t) 8 | 9 | :config 10 | ;; don't steal hippie-expand-lines keybinding 11 | (unbind-key (kbd "C-:") clojure-mode-map) 12 | 13 | (require 'i18n-edn) 14 | 15 | ;; After threading all forms, check if we should maybe unwind once 16 | ;; according to my tastes 17 | (defadvice clojure--thread-all (after possibly-unwind-once activate) 18 | (when (my/clojure-should-unwind-once?) 19 | (clojure-unwind))) 20 | 21 | :bind (:map clojure-mode-map 22 | ([remap paredit-forward] . clojure-forward-logical-sexp) 23 | ([remap paredit-backward] . clojure-backward-logical-sexp) 24 | ("C-\"" . clojure-toggle-keyword-string) 25 | ("C-x M-e" . my/cider-eval-including-lets) 26 | ("C-c C-M-s" . my/cider-eval-def-symbol) 27 | ("C-." . clj-hippie-expand-no-case-fold) 28 | ("C-c i 1 8 n" . i18n-edn-edit-in-multifile) 29 | ("" . cider-eval-last-sexp) 30 | ("" . cider-pprint-eval-last-sexp) 31 | ("" . delete-other-windows) 32 | ("s-" . clerk-show))) 33 | 34 | (use-package zprint-mode 35 | :defer 2) 36 | 37 | ;; Set up jumping to other file (src/test, component/scene) 38 | 39 | (require 's) 40 | (require 'significant-other) 41 | 42 | (defun setup-clojure-mode-so () 43 | (with-significant-others file-name 44 | ("/portfolio/.+/components/" (list (s-with file-name 45 | (s-replace "/portfolio/" "/src/") 46 | (s-replace "_scenes.cljs" ".cljc")))) 47 | 48 | ("/ui/src/.+/components/" (list (s-with file-name 49 | (s-replace "/src/" "/portfolio/") 50 | (s-replace ".cljc" "_scenes.cljs")) 51 | (s-with file-name 52 | (s-replace "/src/" "/test/") 53 | (s-replace ".cljc" "_test.clj")))) 54 | 55 | ("/src/.+\.cljc" (list (s-with file-name 56 | (s-replace "/src/" "/test/") 57 | (s-replace ".cljc" "_test.clj")) 58 | (s-with file-name 59 | (s-replace "/src/" "/test/") 60 | (s-replace ".cljc" "_test.cljc")))) 61 | 62 | ("/src/.+\.clj" (list (s-with file-name 63 | (s-replace "/src/" "/test/") 64 | (s-replace ".clj" "_test.clj")))) 65 | 66 | ("/dev/.+\.clj" (list (s-with file-name 67 | (s-replace "/dev/" "/test/") 68 | (s-replace ".clj" "_test.clj")))) 69 | 70 | ("/test/.+\.clj" (list 71 | (s-with file-name 72 | (s-replace "/test/" "/src/") 73 | (s-replace "_test.clj" ".clj")) 74 | (s-with file-name 75 | (s-replace "/test/" "/src/") 76 | (s-replace "_test.clj" ".cljc")) 77 | (s-with file-name 78 | (s-replace "/test/" "/dev/") 79 | (s-replace "_test.clj" ".clj")))))) 80 | 81 | ;; Set up Clojure CSS completions 82 | 83 | (use-package css-completions 84 | :after (cider projectile) 85 | :ensure nil 86 | :defer t 87 | :commands (cssc/enable-for-clojure) 88 | :init 89 | (add-hook 'clojure-mode-hook 'cssc/enable-for-clojure) 90 | (add-hook 'clojurescript-mode-hook 'cssc/enable-for-clojure) 91 | (add-hook 'clojurec-mode-hook 'cssc/enable-for-clojure) 92 | (add-hook 'mhtml-mode-hook 'cssc/enable-for-html)) 93 | 94 | ;; Don't fully unthread always 95 | 96 | (defun my/clojure-should-unwind-once? () 97 | (save-excursion 98 | (ignore-errors 99 | (when (looking-at "(") 100 | (forward-char 1) 101 | (forward-sexp 1))) 102 | (let ((forms nil)) 103 | (while (not (looking-at ")")) 104 | (clojure-forward-logical-sexp) 105 | (clojure-backward-logical-sexp) 106 | (setq forms (cons (buffer-substring-no-properties (point) (+ 1 (point))) forms)) 107 | (clojure-forward-logical-sexp)) 108 | (and (--any? (s-equals? it "(") forms) 109 | (< 2 (length forms)))))) 110 | 111 | ;; eval-current-sexp while also including surrounding lets 112 | 113 | (defun my/cider-looking-at-lets? () 114 | (or (looking-at "(let ") 115 | (looking-at "(letfn ") 116 | (looking-at "(when-let ") 117 | (looking-at "(if-let "))) 118 | 119 | (defun my/cider-collect-lets (&optional max-point) 120 | (let* ((beg-of-defun (save-excursion (beginning-of-defun) (point))) 121 | (lets nil)) 122 | (save-excursion 123 | (while (not (= (point) beg-of-defun)) 124 | (paredit-backward-up 1) 125 | (when (my/cider-looking-at-lets?) 126 | (save-excursion 127 | (let ((beg (point))) 128 | (paredit-forward-down 1) 129 | (paredit-forward 2) 130 | (when (and max-point (< max-point (point))) 131 | (goto-char max-point)) 132 | (setq lets (cons (concat (buffer-substring-no-properties beg (point)) 133 | (if max-point "]" "")) 134 | lets)))))) 135 | lets))) 136 | 137 | (defun my/inside-let-block? () 138 | (save-excursion 139 | (paredit-backward-up 2) 140 | (my/cider-looking-at-lets?))) 141 | 142 | (defun my/cider-eval-including-lets (&optional output-to-current-buffer) 143 | "Evaluates the current sexp form, wrapped in all parent lets." 144 | (interactive "P") 145 | (let* ((beg-of-sexp (save-excursion (paredit-backward 1) (point))) 146 | (code (buffer-substring-no-properties beg-of-sexp (point))) 147 | (lets (my/cider-collect-lets (when (my/inside-let-block?) 148 | (save-excursion (paredit-backward 2) (point))))) 149 | (code (concat (s-join " " lets) 150 | " " code 151 | (s-repeat (length lets) ")")))) 152 | (cider-interactive-eval code 153 | (when output-to-current-buffer 154 | (cider-eval-print-handler)) 155 | nil 156 | (cider--nrepl-pr-request-map)))) 157 | 158 | (defun my/cider-eval-def-symbol () 159 | "Evaluate and pretty-print the value of the symbol of the def at point." 160 | (interactive) 161 | (save-excursion 162 | (beginning-of-defun) 163 | (paredit-forward-down) 164 | (when (looking-at "def\\(\\w*\\)") 165 | (paredit-forward 2) 166 | (cider-pprint-eval-last-sexp)))) 167 | 168 | (defun clj-hippie-expand-no-case-fold () 169 | "Consider / as whitespace when doing hippie-expand i clojure-mode" 170 | (interactive) 171 | (let ((old-syntax (char-to-string (char-syntax ?/)))) 172 | (modify-syntax-entry ?/ " ") 173 | (hippie-expand-no-case-fold) 174 | (modify-syntax-entry ?/ old-syntax))) 175 | 176 | (defun clerk-show () 177 | (interactive) 178 | (when-let 179 | ((filename 180 | (buffer-file-name))) 181 | (save-buffer) 182 | (cider-interactive-eval 183 | (concat "(nextjournal.clerk/show! \"" filename "\")")))) 184 | 185 | (defun my/cider-unload-current-namespace-aliases () 186 | ;; A clojure.tools.namespace.refresh can leave namespaces with aliases that 187 | ;; point to nothingness! Those namespaces pointing to nothingness prevent the 188 | ;; (ns ,,,) form from evaluating. A workaround is to unload the aliases for 189 | ;; your currently open namespace. 190 | (interactive) 191 | (cider-interactive-eval "(doseq [a (keys (ns-aliases *ns*))] (ns-unalias (symbol (str *ns*)) a))")) 192 | 193 | (use-package neil :defer t) ;; M-x neil-find-clojure-package 194 | 195 | (provide 'setup-clojure-mode) 196 | -------------------------------------------------------------------------------- /packages/setup-hippie.el: -------------------------------------------------------------------------------- 1 | (require 'hippie-exp) 2 | 3 | (defvar he-search-loc-backward (make-marker)) 4 | (defvar he-search-loc-forward (make-marker)) 5 | 6 | (defun he--closest-in-this-buffer (old beg-function search-function) 7 | (let (expansion) 8 | (unless old 9 | (he-init-string (funcall beg-function) (point)) 10 | (set-marker he-search-loc-backward he-string-beg) 11 | (set-marker he-search-loc-forward he-string-end)) 12 | 13 | (if (not (equal he-search-string "")) 14 | (save-excursion 15 | (save-restriction 16 | (if hippie-expand-no-restriction 17 | (widen)) 18 | 19 | (let (forward-point 20 | backward-point 21 | forward-distance 22 | backward-distance 23 | forward-expansion 24 | backward-expansion 25 | chosen) 26 | 27 | ;; search backward 28 | (goto-char he-search-loc-backward) 29 | (setq expansion (funcall search-function he-search-string t)) 30 | 31 | (when expansion 32 | (setq backward-expansion expansion) 33 | (setq backward-point (point)) 34 | (setq backward-distance (- he-string-beg backward-point))) 35 | 36 | ;; search forward 37 | (goto-char he-search-loc-forward) 38 | (setq expansion (funcall search-function he-search-string)) 39 | 40 | (when expansion 41 | (setq forward-expansion expansion) 42 | (setq forward-point (point)) 43 | (setq forward-distance (- forward-point he-string-beg))) 44 | 45 | ;; choose depending on distance 46 | (setq chosen (cond 47 | ((and forward-point backward-point) 48 | (if (< forward-distance backward-distance) :forward :backward)) 49 | 50 | (forward-point :forward) 51 | (backward-point :backward))) 52 | 53 | (when (equal chosen :forward) 54 | (setq expansion forward-expansion) 55 | (set-marker he-search-loc-forward forward-point)) 56 | 57 | (when (equal chosen :backward) 58 | (setq expansion backward-expansion) 59 | (set-marker he-search-loc-backward backward-point)) 60 | 61 | )))) 62 | 63 | (if (not expansion) 64 | (progn 65 | (if old (he-reset-string)) 66 | nil) 67 | (progn 68 | (he-substitute-string expansion t) 69 | t)))) 70 | 71 | (defun try-expand-dabbrev-closest-first (old) 72 | "Try to expand word \"dynamically\", searching the current buffer. 73 | The argument OLD has to be nil the first call of this function, and t 74 | for subsequent calls (for further possible expansions of the same 75 | string). It returns t if a new expansion is found, nil otherwise." 76 | (he--closest-in-this-buffer old #'he-dabbrev-beg #'he-dabbrev-search)) 77 | 78 | (defun try-expand-line-closest-first (old) 79 | "Try to complete the current line to an entire line in the buffer. 80 | The argument OLD has to be nil the first call of this function, and t 81 | for subsequent calls (for further possible completions of the same 82 | string). It returns t if a new completion is found, nil otherwise." 83 | (let ((expansion ()) 84 | (strip-prompt (and (get-buffer-process (current-buffer)) 85 | comint-use-prompt-regexp 86 | comint-prompt-regexp))) 87 | (unless old 88 | (he-init-string (he-line-beg strip-prompt) (point)) 89 | (set-marker he-search-loc-backward he-string-beg) 90 | (set-marker he-search-loc-forward he-string-end)) 91 | 92 | (unless (equal he-search-string "") 93 | (save-excursion 94 | (save-restriction 95 | (when hippie-expand-no-restriction 96 | (widen)) 97 | 98 | (let (forward-point 99 | backward-point 100 | forward-distance 101 | backward-distance 102 | forward-expansion 103 | backward-expansion 104 | chosen) 105 | 106 | ;; search backward 107 | (goto-char he-search-loc-backward) 108 | (setq expansion (he-line-search he-search-string 109 | strip-prompt t)) 110 | 111 | (when expansion 112 | (setq backward-expansion expansion) 113 | (setq backward-point (point)) 114 | (setq backward-distance (- he-string-beg backward-point))) 115 | 116 | ;; search forward 117 | (goto-char he-search-loc-forward) 118 | (setq expansion (he-line-search he-search-string 119 | strip-prompt nil)) 120 | 121 | (when expansion 122 | (setq forward-expansion expansion) 123 | (setq forward-point (point)) 124 | (setq forward-distance (- forward-point he-string-beg))) 125 | 126 | ;; choose depending on distance 127 | (setq chosen (cond 128 | ((and forward-point backward-point) 129 | (if (< forward-distance backward-distance) :forward :backward)) 130 | 131 | (forward-point :forward) 132 | (backward-point :backward))) 133 | 134 | (when (equal chosen :forward) 135 | (setq expansion forward-expansion) 136 | (set-marker he-search-loc-forward forward-point)) 137 | 138 | (when (equal chosen :backward) 139 | (setq expansion backward-expansion) 140 | (set-marker he-search-loc-backward backward-point)) 141 | 142 | )))) 143 | 144 | (if (not expansion) 145 | (progn 146 | (if old (he-reset-string)) 147 | ()) 148 | (progn 149 | (he-substitute-string expansion t) 150 | t)))) 151 | 152 | (defun he-sexp-search (pattern &optional reverse limit) 153 | (when (if reverse 154 | (search-backward pattern nil t) 155 | (search-forward pattern nil t)) 156 | (ignore-errors 157 | (buffer-substring-no-properties (if reverse 158 | (point) 159 | (save-excursion 160 | (paredit-backward-up 1) 161 | (point))) 162 | (save-excursion 163 | (if reverse 164 | (paredit-forward 1) 165 | (paredit-forward-up 1)) 166 | (paredit-backward-down 1) 167 | (point)))))) 168 | 169 | (defun he-sexp-beg () 170 | (save-excursion (paredit-backward-up 1) (point))) 171 | 172 | (defun try-expand-sexp-closest-first (old) 173 | "Try to complete the current sexp to an entire sexp in the buffer. 174 | The argument OLD has to be nil the first call of this function, and t 175 | for subsequent calls (for further possible completions of the same 176 | string). It returns t if a new completion is found, nil otherwise." 177 | (he--closest-in-this-buffer old #'he-sexp-beg #'he-sexp-search)) 178 | 179 | (defun try-expand-sexp-all-buffers (old) 180 | "Try to expand sexp \"dynamically\", searching all other buffers. 181 | The argument OLD has to be nil the first call of this function, and t 182 | for subsequent calls (for further possible expansions of the same 183 | string). It returns t if a new expansion is found, nil otherwise." 184 | (he--all-buffers old #'he-sexp-beg #'he-sexp-search)) 185 | 186 | ;; Hippie expand: sometimes too hip 187 | (setq hippie-expand-try-functions-list '(try-expand-dabbrev-closest-first 188 | try-complete-file-name 189 | try-expand-dabbrev-all-buffers 190 | try-expand-dabbrev-from-kill 191 | try-expand-all-abbrevs 192 | try-complete-lisp-symbol-partially 193 | try-complete-lisp-symbol)) 194 | 195 | ;; Create own function to expand lines (C-S-.) 196 | (defun hippie-expand-lines () 197 | (interactive) 198 | (let ((hippie-expand-try-functions-list 199 | (if paredit-mode 200 | '(try-expand-sexp-closest-first 201 | try-expand-sexp-all-buffers) 202 | '(try-expand-line-closest-first 203 | try-expand-line-all-buffers)))) 204 | (unless paredit-mode (end-of-line)) 205 | (hippie-expand nil) 206 | (indent-region he-string-beg (point)))) 207 | 208 | ;; Don't case-fold when expanding with hippe 209 | (defun hippie-expand-no-case-fold () 210 | (interactive) 211 | (let ((case-fold-search nil)) 212 | (hippie-expand nil))) 213 | 214 | (global-set-key (kbd "C-.") 'hippie-expand-no-case-fold) 215 | (global-set-key (kbd "C-:") 'hippie-expand-lines) 216 | 217 | (provide 'setup-hippie) 218 | --------------------------------------------------------------------------------