diff --git a/.dir-locals.el b/.dir-locals.el new file mode 100644 index 0000000000..3bcda9266b --- /dev/null +++ b/.dir-locals.el @@ -0,0 +1,5 @@ +;;; Directory Local Variables +;;; For more information see (info "(emacs) Directory Variables") + +((emacs-lisp-mode + (indent-tabs-mode . nil))) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000000..82925e0318 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +patreon: sanityinc diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..ba29cb1dad --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +version: 2 +updates: +- package-ecosystem: github-actions + directory: "/" + schedule: + interval: daily + open-pull-requests-limit: 10 + commit-message: + prefix: "chore" + include: "scope" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000000..efc86c8131 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,35 @@ +name: CI + +on: + pull_request: + push: + paths-ignore: + - '**.md' + +jobs: + build: + runs-on: ubuntu-latest + continue-on-error: ${{ matrix.experimental }} + strategy: + matrix: + emacs_version: + - 27.1 + - 27.2 + - 28.1 + - 28.2 + - 29.1 + - 29.3 + - 29.4 + - 30.1 + experimental: [false] + include: + - emacs_version: snapshot + experimental: true + steps: + - uses: purcell/setup-emacs@master + with: + version: ${{ matrix.emacs_version }} + + - uses: actions/checkout@v4 + - name: Check startup + run: ./test-startup.sh diff --git a/.gitignore b/.gitignore index a532b2beef..d93ca95c8d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ /url/cache /.emacs.desktop /auto-save-list +/network-security.data /.session /session.* /custom.el @@ -12,13 +13,13 @@ CVS .*.lock /tramp /TODO -/anything-c-adaptive-history /.org-id-locations cookies /newsticker -/ac-comphist.dat \#* +/tree-sitter /elpa +/elpa-* site-lisp/package/ /swank /.mc-lists.el @@ -43,3 +44,17 @@ site-lisp/package/ /viper /projectile-bookmarks.eld /anaconda-mode/ +/.historian +/.uptimes.el +/rake.cache +/plantuml.jar +/magithub +.cache/ +/forge-database.sqlite +/transient +/.lsp-session-v1 +/smex-items +/eln-cache/ +/projects +/forge-*.sqlite +/projectile.cache diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index b3f4a0f4c4..0000000000 --- a/.travis.yml +++ /dev/null @@ -1,31 +0,0 @@ -language: ruby - -before_install: - - git clone https://github.com/rejeep/evm.git $HOME/.evm - - export PATH=$HOME/.evm/bin:$PATH - - - evm config path /tmp - - evm install $EVM_EMACS --use --skip - -env: - - EVM_EMACS=emacs-23.4-travis - - EVM_EMACS=emacs-24.1-travis - - EVM_EMACS=emacs-24.2-travis - - EVM_EMACS=emacs-24.3-travis - - EVM_EMACS=emacs-24.4-travis - - EVM_EMACS=emacs-24.5-travis - - EVM_EMACS=emacs-git-snapshot-travis - -matrix: - allow_failures: - - env: EVM_EMACS=emacs-24.1-travis - - env: EVM_EMACS=emacs-24.2-travis - - env: EVM_EMACS=emacs-git-snapshot-travis - -script: - lsb_release -a && ./test-startup.sh - -# Local Variables: -# indent-tabs-mode: nil -# coding: utf-8 -# End: diff --git a/LICENSE b/LICENSE index 1c932c6ecd..9c161b813e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2006-2014, Steve Purcell +Copyright (c) 2006-2021, Steve Purcell All rights reserved. Redistribution and use in source and binary forms, with or without @@ -20,7 +20,3 @@ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -The views and conclusions contained in the software and documentation are those -of the authors and should not be interpreted as representing official policies, -either expressed or implied, of any organization or project. \ No newline at end of file diff --git a/README.md b/README.md index a0372533bf..485be5bc55 100644 --- a/README.md +++ b/README.md @@ -1,52 +1,59 @@ -[![Build Status](https://travis-ci.org/purcell/emacs.d.png?branch=master)](https://travis-ci.org/purcell/emacs.d) +[![Build Status](https://github.com/purcell/emacs.d/workflows/CI/badge.svg)](https://github.com/purcell/emacs.d/actions) +Support me # A reasonable Emacs config This is my emacs configuration tree, continually used and tweaked since 2000, and it may be a good starting point for other Emacs -users, especially those who are web developers. These days it's +users, especially web developers. These days it's somewhat geared towards OS X, but it is known to also work on Linux and Windows. Emacs itself comes with support for many programming languages. This -config adds improved defaults and extended support for the following: +config adds improved defaults and extended support for the following, listed +in the approximate order of how much I use them, from most to least: +* Haskell / Purescript / Elm / OCaml * Ruby / Ruby on Rails +* SQL * CSS / LESS / SASS / SCSS -* HAML / Markdown / Textile / ERB -* Clojure (with Cider and nRepl) -* Javascript / Coffeescript +* Javascript / Typescript +* HTML / HAML / Markdown / Textile / ERB +* Common Lisp (with Slime) * Python +* Rust +* Clojure (with Cider and nRepl) * PHP -* Haskell -* Elm * Erlang -* Common Lisp (with Slime) -In particular, there's a nice config for *autocompletion* with -[company](https://company-mode.github.io/), and -[flycheck](http://www.flycheck.org) is used to immediately highlight -syntax errors in Ruby, Python, Javascript, Haskell and a number of -other languages. +Included is a nice setup for in-buffer autocompletion with +[corfu](https://github.com/minad/corfu), and minibuffer completion using +[vertico](https://github.com/minad/vertico). + +`flymake` (re-using backends from [flycheck](http://www.flycheck.org)) +is used to immediately highlight syntax errors in Ruby, Python, +Javascript, Haskell and a number of other languages. + +LSP support is provided using `eglot`. + +Various popular Emacs tools are included and configured here, such as +`magit`, `docker.el`, `projectile`, `org-mode` etc., but the focus is moderate ## Supported Emacs versions -The config should run on Emacs 23.3 or greater and is designed to -degrade smoothly - see the Travis build - but note that Emacs 24 and -above is required for an increasing number of key packages, including -`magit`, `company` and `flycheck`, so to get full you should use the -latest Emacs version available to you. +Use the latest released Emacs version available to you. The author +typically uses the latest stable version. -Some Windows users might need to follow -[these instructions](http://xn--9dbdkw.se/diary/how_to_enable_GnuTLS_for_Emacs_24_on_Windows/index.en.html) -to get TLS (ie. SSL) support included in their Emacs. +The config should run on Emacs 27.1 or greater and is designed to +degrade smoothly - see the CI build - but many enhancements may be +unavailable if your Emacs is too old, and in general you should try +to use the latest stable Emacs release like I do. ## Other requirements To make the most of the programming language-specific support in this config, further programs will likely be required, particularly those -that [flycheck](https://github.com/flycheck/flycheck) uses to provide -on-the-fly syntax checking. +that flycheck or flymake use to provide on-the-fly syntax checking. ## Installation @@ -63,20 +70,11 @@ encounter any errors at that stage, try restarting Emacs, and possibly running `M-x package-refresh-contents` before doing so. - -## Important note about `ido` - -This config enables `ido-mode` completion in the minibuffer wherever -possible, which might confuse you when trying to open files using -C-x C-f, e.g. when you want to open a directory to use -`dired` -- if you get stuck, use C-f to drop into the -regular `find-file` prompt. (You might want to customize the -`ido-show-dot-for-dired` variable if this is an issue for you.) - ## Updates -Update the config with `git pull`. You'll probably also want/need to update -the third-party packages regularly too: +Update the config with `git pull`. You'll probably also want/need to +update the third-party packages regularly too, because that's what I +do, and the config assumes it: M-x package-list-packages, then U followed by x. @@ -85,10 +83,11 @@ packages so that they can take effect. Emacs should usually restore your working buffers when you restart due to this configuration's use of the `desktop` and `session` packages. -## Adding your own customization +## Changing themes and adding your own customization -To add your own customization, use M-x customize and/or -create a file `~/.emacs.d/lisp/init-local.el` which looks like this: +To add your own customization, use M-x customize, M-x +customize-themes etc. and/or create a file +`~/.emacs.d/lisp/init-local.el` which looks like this: ```el ... your code here ... @@ -101,10 +100,11 @@ you can also create an `~/.emacs.d/lisp/init-preload-local.el` file. If you plan to customize things more extensively, you should probably just fork the repo and hack away at the config to make it your own! +Remember to regularly merge in changes from this repo, so that your +config remains compatible with the latest package and Emacs versions. -## Similar configs - -You might also want to check out `emacs-starter-kit` and `prelude`. +*Please note that I cannot provide support for customised versions of +this configuration.* ## Support / issues @@ -117,10 +117,9 @@ versions (see "Updates" above). If you still experience problems, go ahead and
-[![](http://api.coderwall.com/purcell/endorsecount.png)](http://coderwall.com/purcell) -[![](http://www.linkedin.com/img/webpromo/btn_liprofile_blue_80x15.png)](http://uk.linkedin.com/in/stevepurcell) +[💝 Support this project and my other Open Source work](https://www.patreon.com/sanityinc) -[sanityinc.com](http://www.sanityinc.com/) +[💼 LinkedIn profile](https://uk.linkedin.com/in/stevepurcell) -[@sanityinc](https://twitter.com/) +[✍ sanityinc.com](http://www.sanityinc.com/) diff --git a/early-init.el b/early-init.el new file mode 100644 index 0000000000..2ad5d5c411 --- /dev/null +++ b/early-init.el @@ -0,0 +1,16 @@ +;;; early-init.el --- Emacs 27+ pre-initialisation config + +;;; Commentary: + +;; Emacs 27+ loads this file before (normally) calling +;; `package-initialize'. We use this file to suppress that automatic +;; behaviour so that startup is consistent across Emacs versions. + +;;; Code: + +(setq package-enable-at-startup nil) + +;; So we can detect this having been loaded +(provide 'early-init) + +;;; early-init.el ends here diff --git a/init.el b/init.el index 64fd9ba21f..6f93d485e1 100644 --- a/init.el +++ b/init.el @@ -1,11 +1,18 @@ +;;; init.el --- Load the full configuration -*- lexical-binding: t -*- +;;; Commentary: -;;; This file bootstraps the configuration, which is divided into -;;; a number of other files. +;; This file bootstraps the configuration, which is divided into +;; a number of other files. -(let ((minver "23.3")) - (when (version<= emacs-version "23.1") +;;; Code: + +;; Produce backtraces when errors occur: can be helpful to diagnose startup issues +;;(setq debug-on-error t) + +(let ((minver "27.1")) + (when (version< emacs-version minver) (error "Your Emacs is too old -- this config requires v%s or higher" minver))) -(when (version<= emacs-version "24") +(when (version< emacs-version "28.1") (message "Your Emacs is old, and some functionality in this config will be disabled. Please upgrade if possible.")) (add-to-list 'load-path (expand-file-name "lisp" user-emacs-directory)) @@ -14,40 +21,45 @@ (defconst *spell-check-support-enabled* nil) ;; Enable with t if you prefer (defconst *is-a-mac* (eq system-type 'darwin)) -;;---------------------------------------------------------------------------- -;; Temporarily reduce garbage collection during startup -;;---------------------------------------------------------------------------- -(defconst sanityinc/initial-gc-cons-threshold gc-cons-threshold - "Initial value of `gc-cons-threshold' at start-up time.") + +;; Adjust garbage collection threshold for early startup (see use of gcmh below) (setq gc-cons-threshold (* 128 1024 1024)) -(add-hook 'after-init-hook - (lambda () (setq gc-cons-threshold sanityinc/initial-gc-cons-threshold))) -;;---------------------------------------------------------------------------- + +;; Process performance tuning + +(setq read-process-output-max (* 4 1024 1024)) +(setq process-adaptive-read-buffering nil) + + ;; Bootstrap config -;;---------------------------------------------------------------------------- -(setq custom-file (expand-file-name "custom.el" user-emacs-directory)) -(require 'init-compat) + + +(setq custom-file (locate-user-emacs-file "custom.el")) (require 'init-utils) (require 'init-site-lisp) ;; Must come before elpa, as it may provide package.el ;; Calls (package-initialize) (require 'init-elpa) ;; Machinery for installing required packages (require 'init-exec-path) ;; Set up $PATH -;;---------------------------------------------------------------------------- + +;; General performance tuning +(when (require-package 'gcmh) + (setq gcmh-high-cons-threshold (* 128 1024 1024)) + (add-hook 'after-init-hook (lambda () + (gcmh-mode) + (diminish 'gcmh-mode)))) + +(setq jit-lock-defer-time 0) + + ;; Allow users to provide an optional "init-preload-local.el" -;;---------------------------------------------------------------------------- (require 'init-preload-local nil t) -;;---------------------------------------------------------------------------- ;; Load configs for specific features and modes -;;---------------------------------------------------------------------------- - -(require-package 'wgrep) -(require-package 'project-local-variables) (require-package 'diminish) -(require-package 'scratch) -(require-package 'mwe-log-commands) +(maybe-require-package 'scratch) +(require-package 'command-log-mode) (require 'init-frame-hooks) (require 'init-xterm) @@ -59,22 +71,19 @@ (require 'init-grep) (require 'init-uniquify) (require 'init-ibuffer) -(require 'init-flycheck) +(require 'init-flymake) +(require 'init-eglot) (require 'init-recentf) -(require 'init-smex) -(require 'init-ido) -;; (require 'init-ivy) +(require 'init-minibuffer) (require 'init-hippie-expand) -(require 'init-company) +(require 'init-corfu) (require 'init-windows) (require 'init-sessions) -(require 'init-fonts) (require 'init-mmm) (require 'init-editing-utils) (require 'init-whitespace) -(require 'init-fci) (require 'init-vc) (require 'init-darcs) @@ -96,72 +105,93 @@ (require 'init-html) (require 'init-css) (require 'init-haml) -(require 'init-python-mode) -(unless (version<= emacs-version "24.3") - (require 'init-haskell)) +(require 'init-http) +(require 'init-python) +(require 'init-haskell) (require 'init-elm) -(require 'init-ruby-mode) +(require 'init-purescript) +(require 'init-ruby) (require 'init-rails) (require 'init-sql) +(require 'init-ocaml) +(require 'init-j) +(require 'init-nim) +(require 'init-rust) +(require 'init-toml) +(require 'init-yaml) +(require 'init-docker) +(require 'init-terraform) +(require 'init-nix) +(maybe-require-package 'nginx-mode) +(maybe-require-package 'just-mode) +(when (maybe-require-package 'just-ts-mode) + ;; Undo overly-optimistic autoloading, so that things still work in + ;; Emacs 29 without treesitter + (sanityinc/remove-auto-mode 'just-ts-mode)) +(maybe-require-package 'justl) (require 'init-paredit) (require 'init-lisp) -(require 'init-slime) -(unless (version<= emacs-version "24.2") - (require 'init-clojure) - (require 'init-clojure-cider)) -(require 'init-common-lisp) +(require 'init-sly) +(require 'init-clojure) +(require 'init-clojure-cider) (when *spell-check-support-enabled* (require 'init-spelling)) (require 'init-misc) +(require 'init-folding) (require 'init-dash) + (require 'init-ledger) +(require 'init-lua) +(require 'init-uiua) +(require 'init-zig) +(require 'init-terminals) + ;; Extra packages which don't require any configuration +(require-package 'sudo-edit) (require-package 'gnuplot) -(require-package 'lua-mode) (require-package 'htmlize) -(require-package 'dsvn) (when *is-a-mac* (require-package 'osx-location)) -(require-package 'regex-tool) +(maybe-require-package 'dotenv-mode) +(maybe-require-package 'shfmt) -;;---------------------------------------------------------------------------- -;; Allow access from emacsclient -;;---------------------------------------------------------------------------- -(require 'server) -(unless (server-running-p) - (server-start)) +(when (maybe-require-package 'uptimes) + (setq-default uptimes-keep-count 200) + (add-hook 'after-init-hook (lambda () (require 'uptimes)))) +(when (fboundp 'global-eldoc-mode) + (add-hook 'after-init-hook 'global-eldoc-mode)) -;;---------------------------------------------------------------------------- -;; Variables configured via the interactive 'customize' interface -;;---------------------------------------------------------------------------- -(when (file-exists-p custom-file) - (load custom-file)) +(require 'init-direnv) +(when (and (require 'treesit nil t) + (fboundp 'treesit-available-p) + (treesit-available-p)) + (require 'init-treesitter)) -;;---------------------------------------------------------------------------- -;; Allow users to provide an optional "init-local" containing personal settings -;;---------------------------------------------------------------------------- -(when (file-exists-p (expand-file-name "init-local.el" user-emacs-directory)) - (error "Please move init-local.el to ~/.emacs.d/lisp")) -(require 'init-local nil t) + +;; Allow access from emacsclient +(add-hook 'after-init-hook + (lambda () + (require 'server) + (unless (server-running-p) + (server-start)))) + +;; Variables configured via the interactive 'customize' interface +(when (file-exists-p custom-file) + (load custom-file)) -;;---------------------------------------------------------------------------- ;; Locales (setting them earlier in this file doesn't work in X) -;;---------------------------------------------------------------------------- (require 'init-locales) -(add-hook 'after-init-hook - (lambda () - (message "init completed in %.2fms" - (sanityinc/time-subtract-millis after-init-time before-init-time)))) - +;; Allow users to provide an optional "init-local" containing personal settings +(require 'init-local nil t) (provide 'init) @@ -169,3 +199,4 @@ ;; coding: utf-8 ;; no-byte-compile: t ;; End: +;;; init.el ends here diff --git a/lisp/.dir-locals.el b/lisp/.dir-locals.el index 9639f386e0..3e3da00136 100644 --- a/lisp/.dir-locals.el +++ b/lisp/.dir-locals.el @@ -1 +1,5 @@ -((emacs-lisp-mode . ((no-byte-compile t)))) +;;; Directory Local Variables +;;; For more information see (info "(emacs) Directory Variables") + +((emacs-lisp-mode + (no-byte-compile . t))) diff --git a/lisp/init-benchmarking.el b/lisp/init-benchmarking.el index a145f7bff5..cfae65673e 100644 --- a/lisp/init-benchmarking.el +++ b/lisp/init-benchmarking.el @@ -1,23 +1,78 @@ +;;; init-benchmarking.el --- Measure startup and require times -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + (defun sanityinc/time-subtract-millis (b a) (* 1000.0 (float-time (time-subtract b a)))) (defvar sanityinc/require-times nil - "A list of (FEATURE . LOAD-DURATION). + "A list of (FEATURE LOAD-START-TIME LOAD-DURATION). LOAD-DURATION is the time taken in milliseconds to load FEATURE.") -(defadvice require (around sanityinc/build-require-times (feature &optional filename noerror) activate) +(defun sanityinc/require-times-wrapper (orig feature &rest args) "Note in `sanityinc/require-times' the time taken to require each feature." (let* ((already-loaded (memq feature features)) (require-start-time (and (not already-loaded) (current-time)))) (prog1 - ad-do-it + (apply orig feature args) (when (and (not already-loaded) (memq feature features)) (let ((time (sanityinc/time-subtract-millis (current-time) require-start-time))) (add-to-list 'sanityinc/require-times - (cons feature time) + (list feature require-start-time time) t)))))) +(advice-add 'require :around 'sanityinc/require-times-wrapper) + + +(define-derived-mode sanityinc/require-times-mode tabulated-list-mode "Require-Times" + "Show times taken to `require' packages." + (setq tabulated-list-format + [("Start time (ms)" 20 sanityinc/require-times-sort-by-start-time-pred) + ("Feature" 30 t) + ("Time (ms)" 12 sanityinc/require-times-sort-by-load-time-pred)]) + (setq tabulated-list-sort-key (cons "Start time (ms)" nil)) + ;; (setq tabulated-list-padding 2) + (setq tabulated-list-entries #'sanityinc/require-times-tabulated-list-entries) + (tabulated-list-init-header) + (when (fboundp 'tablist-minor-mode) + (tablist-minor-mode))) + +(defun sanityinc/require-times-sort-by-start-time-pred (entry1 entry2) + (< (string-to-number (elt (nth 1 entry1) 0)) + (string-to-number (elt (nth 1 entry2) 0)))) + +(defun sanityinc/require-times-sort-by-load-time-pred (entry1 entry2) + (> (string-to-number (elt (nth 1 entry1) 2)) + (string-to-number (elt (nth 1 entry2) 2)))) + +(defun sanityinc/require-times-tabulated-list-entries () + (cl-loop for (feature start-time millis) in sanityinc/require-times + with order = 0 + do (cl-incf order) + collect (list order + (vector + (format "%.3f" (sanityinc/time-subtract-millis start-time before-init-time)) + (symbol-name feature) + (format "%.3f" millis))))) + +(defun sanityinc/require-times () + "Show a tabular view of how long various libraries took to load." + (interactive) + (with-current-buffer (get-buffer-create "*Require Times*") + (sanityinc/require-times-mode) + (tabulated-list-revert) + (display-buffer (current-buffer)))) + + + + +(defun sanityinc/show-init-time () + (message "init completed in %.2fms" + (sanityinc/time-subtract-millis after-init-time before-init-time))) + +(add-hook 'after-init-hook 'sanityinc/show-init-time) (provide 'init-benchmarking) +;;; init-benchmarking.el ends here diff --git a/lisp/init-clojure-cider.el b/lisp/init-clojure-cider.el index 6e1f4d7315..db54b7ab21 100644 --- a/lisp/init-clojure-cider.el +++ b/lisp/init-clojure-cider.el @@ -1,20 +1,24 @@ +;;; init-clojure-cider.el --- Cider support for clojure -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + (require 'init-clojure) (when (maybe-require-package 'cider) (setq nrepl-popup-stacktraces nil) - (after-load 'cider - (add-hook 'cider-mode-hook 'eldoc-mode) - (add-hook 'cider-repl-mode-hook 'subword-mode) - (add-hook 'cider-repl-mode-hook 'paredit-mode) + (add-hook 'clojure-ts-mode-hook #'cider-mode) - ;; nrepl isn't based on comint - (add-hook 'cider-repl-mode-hook 'sanityinc/no-trailing-whitespace)) + (with-eval-after-load 'cider + (add-hook 'cider-repl-mode-hook 'subword-mode) + (add-hook 'cider-repl-mode-hook 'paredit-mode)) (require-package 'flycheck-clojure) - (after-load 'clojure-mode - (after-load 'flycheck - (flycheck-clojure-setup)))) + (with-eval-after-load 'clojure-mode + (with-eval-after-load 'cider + (with-eval-after-load 'flycheck + (flycheck-clojure-setup))))) (provide 'init-clojure-cider) +;;; init-clojure-cider.el ends here diff --git a/lisp/init-clojure.el b/lisp/init-clojure.el index 51fbe74833..8c5e6329c1 100644 --- a/lisp/init-clojure.el +++ b/lisp/init-clojure.el @@ -1,12 +1,18 @@ +;;; init-clojure.el --- Clojure support -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + ;; See also init-clojure-cider.el -(when (maybe-require-package 'clojure-mode) +(when (or (maybe-require-package 'clojure-ts-mode) + (maybe-require-package 'clojure-mode)) (require-package 'cljsbuild-mode) (require-package 'elein) - (after-load 'clojure-mode - (add-hook 'clojure-mode-hook 'sanityinc/lisp-setup) - (add-hook 'clojure-mode-hook 'subword-mode))) + (with-eval-after-load 'clojure-mode + (dolist (m '(clojure-mode-hook clojure-ts-mode-hook)) + (add-hook m 'sanityinc/lisp-setup)))) (provide 'init-clojure) +;;; init-clojure.el ends here diff --git a/lisp/init-common-lisp.el b/lisp/init-common-lisp.el deleted file mode 100644 index b51d06128d..0000000000 --- a/lisp/init-common-lisp.el +++ /dev/null @@ -1,47 +0,0 @@ -;; See http://bc.tech.coop/blog/070927.html -(add-auto-mode 'lisp-mode "\\.cl\\'") -(add-hook 'lisp-mode-hook (lambda () - (unless (featurep 'slime) - (require 'slime) - (normal-mode)))) - -(after-load 'slime - (when (executable-find "sbcl") - (add-to-list 'slime-lisp-implementations - '(sbcl ("sbcl") :coding-system utf-8-unix))) - (when (executable-find "lisp") - (add-to-list 'slime-lisp-implementations - '(cmucl ("lisp") :coding-system iso-latin-1-unix))) - (when (executable-find "ccl") - (add-to-list 'slime-lisp-implementations - '(ccl ("ccl") :coding-system utf-8-unix)))) - -;; From http://bc.tech.coop/blog/070515.html -(defun lispdoc () - "Searches lispdoc.com for SYMBOL, which is by default the symbol currently under the curser" - (interactive) - (let* ((word-at-point (word-at-point)) - (symbol-at-point (symbol-at-point)) - (default (symbol-name symbol-at-point)) - (inp (read-from-minibuffer - (if (or word-at-point symbol-at-point) - (concat "Symbol (default " default "): ") - "Symbol (no default): ")))) - (if (and (string= inp "") (not word-at-point) (not - symbol-at-point)) - (message "you didn't enter a symbol!") - (let ((search-type (read-from-minibuffer - "full-text (f) or basic (b) search (default b)? "))) - (browse-url (concat "http://lispdoc.com?q=" - (if (string= inp "") - default - inp) - "&search=" - (if (string-equal search-type "f") - "full+text+search" - "basic+search"))))))) - -(define-key lisp-mode-map (kbd "C-c l") 'lispdoc) - - -(provide 'init-common-lisp) diff --git a/lisp/init-company.el b/lisp/init-company.el deleted file mode 100644 index 2f4eec50a5..0000000000 --- a/lisp/init-company.el +++ /dev/null @@ -1,48 +0,0 @@ -;; WAITING: haskell-mode sets tags-table-list globally, breaks tags-completion-at-point-function -;; TODO Default sort order should place [a-z] before punctuation - -(setq tab-always-indent 'complete) ;; use 't when company is disabled -(add-to-list 'completion-styles 'initials t) -;; Stop completion-at-point from popping up completion buffers so eagerly -(setq completion-cycle-threshold 5) - - -(when (maybe-require-package 'company) - (add-hook 'after-init-hook 'global-company-mode) - (after-load 'company - (diminish 'company-mode "CMP") - (define-key company-mode-map (kbd "M-/") 'company-complete) - (define-key company-active-map (kbd "M-/") 'company-select-next) - (setq-default company-backends '((company-capf company-dabbrev-code) company-dabbrev))) - (global-set-key (kbd "M-C-/") 'company-complete) - (when (maybe-require-package 'company-quickhelp) - (add-hook 'after-init-hook 'company-quickhelp-mode)) - - (defun sanityinc/local-push-company-backend (backend) - "Add BACKEND to a buffer-local version of `company-backends'." - (set (make-local-variable 'company-backends) - (append (list backend) company-backends)))) - - -;; Suspend page-break-lines-mode while company menu is active -;; (see https://github.com/company-mode/company-mode/issues/416) -(after-load 'company - (after-load 'page-break-lines-mode - (defvar sanityinc/page-break-lines-on-p nil) - (make-variable-buffer-local 'sanityinc/page-break-lines-on-p) - - (defun sanityinc/page-break-lines-disable (&rest ignore) - (when (setq sanityinc/page-break-lines-on-p (bound-and-true-p page-break-lines-mode)) - (page-break-lines-mode -1))) - - (defun sanityinc/page-break-lines-maybe-reenable (&rest ignore) - (when sanityinc/page-break-lines-on-p - (page-break-lines-mode 1))) - - (add-hook 'company-completion-started-hook 'sanityinc/page-break-lines-disable) - (add-hook 'company-completion-finished-hook 'sanityinc/page-break-lines-maybe-reenable) - (add-hook 'company-completion-cancelled-hook 'sanityinc/page-break-lines-maybe-reenable))) - - - -(provide 'init-company) diff --git a/lisp/init-compat.el b/lisp/init-compat.el deleted file mode 100644 index b3d14ad62a..0000000000 --- a/lisp/init-compat.el +++ /dev/null @@ -1,42 +0,0 @@ -;;---------------------------------------------------------------------------- -;; Provide a version of Emacs 24's 'string-prefix-p in older emacsen -;;---------------------------------------------------------------------------- -(when (eval-when-compile (< emacs-major-version 24)) - (defun string-prefix-p (str1 str2 &optional ignore-case) - "Return non-nil if STR1 is a prefix of STR2. -If IGNORE-CASE is non-nil, the comparison is done without paying attention -to case differences." - (eq t (compare-strings str1 nil nil - str2 0 (length str1) ignore-case)))) - - -;;---------------------------------------------------------------------------- -;; Allow recent packages to safely pass an arg to 'called-interactively-p -;; in older Emacsen, including 23.1. -;;---------------------------------------------------------------------------- -(let ((fn (symbol-function 'called-interactively-p))) - (when (and (subrp fn) (zerop (cdr-safe (subr-arity fn)))) - (message "Warning: overriding called-interactively-p to support an argument.") - (fset 'sanityinc/called-interactively-p fn) - (defun called-interactively-p (&optional kind) - "Overridden; see `sanityinc/called-interactively-p' for the wrapped function." - (sanityinc/called-interactively-p)))) - -(when (eval-when-compile (< emacs-major-version 24)) - ;; Help package.el work in older Emacsen, where there's no TRASH arg - ;; for 'delete-directory - (message "Warning: overriding delete-directory to support TRASH argument.") - (fset 'sanityinc/delete-directory (symbol-function 'delete-directory)) - (defun delete-directory (directory &optional recursive trash) - "Overridden: see `sanityinc/delete-directory' for the wrapped function" - (sanityinc/delete-directory directory recursive))) - - -;;---------------------------------------------------------------------------- -;; Restore removed var alias, used by ruby-electric-brace and others -;;---------------------------------------------------------------------------- -(unless (boundp 'last-command-char) - (defvaralias 'last-command-char 'last-command-event)) - - -(provide 'init-compat) diff --git a/lisp/init-compile.el b/lisp/init-compile.el index 15aa57db24..ac196b7eed 100644 --- a/lisp/init-compile.el +++ b/lisp/init-compile.el @@ -1,3 +1,7 @@ +;;; init-compile.el --- Helpers for M-x compile -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + (setq-default compilation-scroll-output t) (require-package 'alert) @@ -6,54 +10,58 @@ (defun sanityinc/alert-after-compilation-finish (buf result) "Use `alert' to report compilation RESULT if BUF is hidden." - (unless (catch 'is-visible - (walk-windows (lambda (w) - (when (eq (window-buffer w) buf) - (throw 'is-visible t)))) - nil) - (alert (concat "Compilation " result) - :buffer buf - :category 'compilation))) - -(after-load 'compile + (when (buffer-live-p buf) + (unless (catch 'is-visible + (walk-windows (lambda (w) + (when (eq (window-buffer w) buf) + (throw 'is-visible t)))) + nil) + (alert (concat "Compilation " result) + :buffer buf + :category 'compilation)))) + +(with-eval-after-load 'compile (add-hook 'compilation-finish-functions 'sanityinc/alert-after-compilation-finish)) (defvar sanityinc/last-compilation-buffer nil "The last buffer in which compilation took place.") -(after-load 'compile - (defadvice compilation-start (after sanityinc/save-compilation-buffer activate) +(with-eval-after-load 'compile + (defun sanityinc/save-compilation-buffer (&rest _) "Save the compilation buffer to find it later." (setq sanityinc/last-compilation-buffer next-error-last-buffer)) + (advice-add 'compilation-start :after 'sanityinc/save-compilation-buffer) - (defadvice recompile (around sanityinc/find-prev-compilation (&optional edit-command) activate) + (defun sanityinc/find-prev-compilation (orig &optional edit-command) "Find the previous compilation buffer, if present, and recompile there." (if (and (null edit-command) (not (derived-mode-p 'compilation-mode)) sanityinc/last-compilation-buffer (buffer-live-p (get-buffer sanityinc/last-compilation-buffer))) (with-current-buffer sanityinc/last-compilation-buffer - ad-do-it) - ad-do-it))) + (funcall orig edit-command)) + (funcall orig edit-command))) + (advice-add 'recompile :around 'sanityinc/find-prev-compilation)) (global-set-key [f6] 'recompile) -(defadvice shell-command-on-region - (after sanityinc/shell-command-in-view-mode - (start end command &optional output-buffer &rest other-args) - activate) + +(defun sanityinc/shell-command-in-view-mode (start end command &optional output-buffer replace &rest other-args) "Put \"*Shell Command Output*\" buffers into view-mode." - (unless output-buffer + (unless (or output-buffer replace) (with-current-buffer "*Shell Command Output*" (view-mode 1)))) +(advice-add 'shell-command-on-region :after 'sanityinc/shell-command-in-view-mode) -(after-load 'compile +(with-eval-after-load 'compile (require 'ansi-color) (defun sanityinc/colourise-compilation-buffer () (when (eq major-mode 'compilation-mode) (ansi-color-apply-on-region compilation-filter-start (point-max)))) (add-hook 'compilation-filter-hook 'sanityinc/colourise-compilation-buffer)) + (provide 'init-compile) +;;; init-compile.el ends here diff --git a/lisp/init-corfu.el b/lisp/init-corfu.el new file mode 100644 index 0000000000..b127b584a4 --- /dev/null +++ b/lisp/init-corfu.el @@ -0,0 +1,39 @@ +;;; init-corfu.el --- Interactive completion in buffers -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + +;; WAITING: haskell-mode sets tags-table-list globally, breaks tags-completion-at-point-function +;; TODO Default sort order should place [a-z] before punctuation + +(setq tab-always-indent 'complete) +(when (maybe-require-package 'orderless) + (with-eval-after-load 'vertico + (require 'orderless) + (setq completion-styles '(orderless basic)))) +(setq completion-category-defaults nil + completion-category-overrides nil) +(setq completion-cycle-threshold 4) + +(when (and (version< "28.1" emacs-version) (maybe-require-package 'corfu)) + (setq-default corfu-auto t) + (with-eval-after-load 'eshell + (add-hook 'eshell-mode-hook (lambda () (setq-local corfu-auto nil)))) + (setq-default corfu-quit-no-match 'separator) + (add-hook 'after-init-hook 'global-corfu-mode) + + + + (with-eval-after-load 'corfu + (corfu-popupinfo-mode)) + + ;; Make Corfu also work in terminals, without disturbing usual behaviour in GUI + (when (maybe-require-package 'corfu-terminal) + (with-eval-after-load 'corfu + (corfu-terminal-mode))) + + ;; TODO: https://github.com/jdtsmith/kind-icon + ) + + +(provide 'init-corfu) +;;; init-corfu.el ends here diff --git a/lisp/init-crontab.el b/lisp/init-crontab.el index f3cb9c1acd..91af3d2848 100644 --- a/lisp/init-crontab.el +++ b/lisp/init-crontab.el @@ -1,4 +1,9 @@ +;;; init-crontab.el --- Working with crontabs -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + (require-package 'crontab-mode) (add-auto-mode 'crontab-mode "\\.?cron\\(tab\\)?\\'") (provide 'init-crontab) +;;; init-crontab.el ends here diff --git a/lisp/init-css.el b/lisp/init-css.el index 82ab342b20..8a34ced370 100644 --- a/lisp/init-css.el +++ b/lisp/init-css.el @@ -1,3 +1,7 @@ +;;; init-css.el --- CSS/Less/SASS/SCSS support -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + ;;; Colourise CSS colour literals (when (maybe-require-package 'rainbow-mode) (dolist (hook '(css-mode-hook html-mode-hook sass-mode-hook)) @@ -6,7 +10,7 @@ ;;; Embedding in html (require-package 'mmm-mode) -(after-load 'mmm-vars +(with-eval-after-load 'mmm-vars (mmm-add-group 'html-css '((css-cdata @@ -14,14 +18,14 @@ :face mmm-code-submode-face :front "]*>[ \t\n]*\\(//\\)?[ \t\n]*" - :insert ((?j js-tag nil @ "" @))) (css :submode css-mode :face mmm-code-submode-face :front "]*>[ \t]*\n?" :back "[ \t]*" - :insert ((?j js-tag nil @ "" @))) (css-inline :submode css-mode @@ -36,15 +40,17 @@ ;;; SASS and SCSS (require-package 'sass-mode) -(require-package 'scss-mode) +(unless (fboundp 'scss-mode) + ;; Prefer the scss-mode built into Emacs + (require-package 'scss-mode)) (setq-default scss-compile-at-save nil) ;;; LESS -(require-package 'less-css-mode) -(when (featurep 'js2-mode) - (require-package 'skewer-less)) +(unless (fboundp 'less-css-mode) + ;; Prefer the scss-mode built into Emacs + (require-package 'less-css-mode)) @@ -55,3 +61,4 @@ (provide 'init-css) +;;; init-css.el ends here diff --git a/lisp/init-csv.el b/lisp/init-csv.el index 4a9eb36791..77af1359c4 100644 --- a/lisp/init-csv.el +++ b/lisp/init-csv.el @@ -1,8 +1,11 @@ -(require-package 'csv-mode) -(require-package 'csv-nav) +;;; init-csv.el --- CSV files -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: -(add-auto-mode 'csv-mode "\\.[Cc][Ss][Vv]\\'") +(when (maybe-require-package 'csv-mode) + (add-auto-mode 'csv-mode "\\.[Cc][Ss][Vv]\\'") -(setq csv-separators '("," ";" "|" " ")) + (setq csv-separators '("," ";" "|" " "))) (provide 'init-csv) +;;; init-csv.el ends here diff --git a/lisp/init-darcs.el b/lisp/init-darcs.el index 5cf8930637..d2d1844287 100644 --- a/lisp/init-darcs.el +++ b/lisp/init-darcs.el @@ -1,4 +1,7 @@ -(require-package 'darcsum) +;;; init-darcs.el --- Support for the Darcs DVCS -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + (require-package 'vc-darcs) @@ -7,12 +10,7 @@ (autoload 'vc-darcs-find-file-hook "vc-darcs") (add-hook 'find-file-hooks 'vc-darcs-find-file-hook) -(after-load 'vc-darcs - ;; This variable was removed in an Emacs 25.x snapshot, but vc-darcs - ;; hasn't been fixed accordingly - (unless (boundp 'vc-disable-async-diff) - (setq vc-disable-async-diff nil))) - (setq darcsum-whatsnew-switches "-l") (provide 'init-darcs) +;;; init-darcs.el ends here diff --git a/lisp/init-dash.el b/lisp/init-dash.el index a6bb06a9e4..1234e4e6df 100644 --- a/lisp/init-dash.el +++ b/lisp/init-dash.el @@ -1,20 +1,12 @@ -;; Support for the http://kapeli.com/dash documentation browser - -(defun sanityinc/dash-installed-p () - "Return t if Dash is installed on this machine, or nil otherwise." - (let ((lsregister "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister")) - (and (file-executable-p lsregister) - (not (string-equal - "" - (shell-command-to-string - (concat lsregister " -dump|grep com.kapeli.dash"))))))) +;;; init-dash.el --- Integrate with the Mac app "Dash" -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: -(when (and *is-a-mac* (not (package-installed-p 'dash-at-point))) - (message "Checking whether Dash is installed") - (when (sanityinc/dash-installed-p) - (require-package 'dash-at-point))) +;; Support for the http://kapeli.com/dash documentation browser -(when (package-installed-p 'dash-at-point) +(when *is-a-mac* + (require-package 'dash-at-point) (global-set-key (kbd "C-c D") 'dash-at-point)) (provide 'init-dash) +;;; init-dash.el ends here diff --git a/lisp/init-dired.el b/lisp/init-dired.el index 828664a3d0..a5c4403582 100644 --- a/lisp/init-dired.el +++ b/lisp/init-dired.el @@ -1,25 +1,30 @@ -(require-package 'dired+) -(require-package 'dired-sort) +;;; init-dired.el --- Dired customisations -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: -(setq-default diredp-hide-details-initially-flag nil - dired-dwim-target t) +(setq-default dired-dwim-target t) ;; Prefer g-prefixed coreutils version of standard utilities when available (let ((gls (executable-find "gls"))) (when gls (setq insert-directory-program gls))) -(after-load 'dired - (require 'dired+) - (require 'dired-sort) - (when (fboundp 'global-dired-hide-details-mode) - (global-dired-hide-details-mode -1)) +(when (maybe-require-package 'diredfl) + (with-eval-after-load 'dired + (diredfl-global-mode) + (require 'dired-x))) + +;; Hook up dired-x global bindings without loading it up-front +(define-key ctl-x-map "\C-j" 'dired-jump) +(define-key ctl-x-4-map "\C-j" 'dired-jump-other-window) + +(with-eval-after-load 'dired (setq dired-recursive-deletes 'top) (define-key dired-mode-map [mouse-2] 'dired-find-file) - (add-hook 'dired-mode-hook - (lambda () (guide-key/add-local-guide-key-sequence "%")))) + (define-key dired-mode-map (kbd "C-c C-q") 'wdired-change-to-wdired-mode)) (when (maybe-require-package 'diff-hl) - (after-load 'dired + (with-eval-after-load 'dired (add-hook 'dired-mode-hook 'diff-hl-dired-mode))) (provide 'init-dired) +;;; init-dired.el ends here diff --git a/lisp/init-direnv.el b/lisp/init-direnv.el new file mode 100644 index 0000000000..a144f8d4fd --- /dev/null +++ b/lisp/init-direnv.el @@ -0,0 +1,12 @@ +;;; init-direnv.el --- Integrate with direnv -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + +(when (maybe-require-package 'envrc) + (with-eval-after-load 'envrc + (define-key envrc-mode-map (kbd "C-c e") 'envrc-command-map)) + (add-hook 'after-init-hook 'envrc-global-mode)) + +(provide 'init-direnv) + +;;; init-direnv.el ends here diff --git a/lisp/init-docker.el b/lisp/init-docker.el new file mode 100644 index 0000000000..99cfcc677d --- /dev/null +++ b/lisp/init-docker.el @@ -0,0 +1,16 @@ +;;; init-docker.el --- Work with Docker and its tools -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + +(when (maybe-require-package 'docker) + (sanityinc/fullframe-mode 'docker-image-mode) + (sanityinc/fullframe-mode 'docker-machine-mode) + (sanityinc/fullframe-mode 'docker-volume-mode) + (sanityinc/fullframe-mode 'docker-network-mode) + (sanityinc/fullframe-mode 'docker-container-mode)) +(maybe-require-package 'dockerfile-mode) +(maybe-require-package 'docker-compose-mode) + + +(provide 'init-docker) +;;; init-docker.el ends here diff --git a/lisp/init-editing-utils.el b/lisp/init-editing-utils.el index e8580c92b4..378a4b3bb3 100644 --- a/lisp/init-editing-utils.el +++ b/lisp/init-editing-utils.el @@ -1,23 +1,29 @@ +;;; init-editing-utils.el --- Day-to-day editing helpers -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + (require-package 'unfill) (when (fboundp 'electric-pair-mode) - (electric-pair-mode)) -(when (eval-when-compile (version< "24.4" emacs-version)) - (electric-indent-mode 1)) + (add-hook 'after-init-hook 'electric-pair-mode)) +(add-hook 'after-init-hook 'electric-indent-mode) + +(maybe-require-package 'list-unicode-display) + + +;;; Some basic preferences -;;---------------------------------------------------------------------------- -;; Some basic preferences -;;---------------------------------------------------------------------------- (setq-default blink-cursor-interval 0.4 - bookmark-default-file (expand-file-name ".bookmarks.el" user-emacs-directory) + bookmark-default-file (locate-user-emacs-file ".bookmarks.el") buffers-menu-max-size 30 case-fold-search t column-number-mode t - delete-selection-mode t ediff-split-window-function 'split-window-horizontally ediff-window-setup-function 'ediff-setup-windows-plain indent-tabs-mode nil + create-lockfiles nil + auto-save-default nil make-backup-files nil mouse-yank-at-point t save-interprogram-paste-before-kill t @@ -27,17 +33,42 @@ truncate-lines nil truncate-partial-width-windows nil) -(global-auto-revert-mode) +(add-hook 'after-init-hook 'delete-selection-mode) + +(add-hook 'after-init-hook 'global-auto-revert-mode) (setq global-auto-revert-non-file-buffers t auto-revert-verbose nil) +(with-eval-after-load 'autorevert + (diminish 'auto-revert-mode)) + +(add-hook 'after-init-hook 'transient-mark-mode) -(transient-mark-mode t) + +;; Huge files + +(when (fboundp 'so-long-enable) + (add-hook 'after-init-hook 'so-long-enable)) + +(require-package 'vlf) + +(defun ffap-vlf () + "Find file at point with VLF." + (interactive) + (let ((file (ffap-file-at-point))) + (unless (file-exists-p file) + (error "File does not exist: %s" file)) + (vlf file))) -;;; Newline behaviour +;;; A simple visible bell which works in all terminal types +(require-package 'mode-line-bell) +(add-hook 'after-init-hook 'mode-line-bell-mode) + + + +;;; Newline behaviour (see also electric-indent-mode, enabled above) -(global-set-key (kbd "RET") 'newline-and-indent) (defun sanityinc/newline-at-end-of-line () "Move to end of line, enter a newline, and reindent." (interactive) @@ -48,54 +79,42 @@ -(when (eval-when-compile (string< "24.3.1" emacs-version)) - ;; https://github.com/purcell/emacs.d/issues/138 - (after-load 'subword - (diminish 'subword-mode))) +(with-eval-after-load 'subword + (diminish 'subword-mode)) -(when (maybe-require-package 'indent-guide) - (add-hook 'prog-mode-hook 'indent-guide-mode) - (after-load 'indent-guide - (diminish 'indent-guide-mode))) +(when (fboundp 'display-line-numbers-mode) + (setq-default display-line-numbers-width 3) + (add-hook 'prog-mode-hook 'display-line-numbers-mode) + (add-hook 'yaml-mode-hook 'display-line-numbers-mode) + (add-hook 'yaml-ts-mode-hook 'display-line-numbers-mode)) -(require-package 'nlinum) +(when (boundp 'display-fill-column-indicator) + (setq-default indicate-buffer-boundaries 'left) + (setq-default display-fill-column-indicator-character ?┊) + (add-hook 'prog-mode-hook 'display-fill-column-indicator-mode)) + (when (require-package 'rainbow-delimiters) (add-hook 'prog-mode-hook 'rainbow-delimiters-mode)) - -(when (fboundp 'global-prettify-symbols-mode) - (global-prettify-symbols-mode)) - - -(require-package 'undo-tree) -(global-undo-tree-mode) -(diminish 'undo-tree-mode) +(when (maybe-require-package 'symbol-overlay) + (dolist (hook '(prog-mode-hook html-mode-hook yaml-mode-hook conf-mode-hook)) + (add-hook hook 'symbol-overlay-mode)) + (with-eval-after-load 'symbol-overlay + (diminish 'symbol-overlay-mode) + (define-key symbol-overlay-mode-map (kbd "M-i") 'symbol-overlay-put) + (define-key symbol-overlay-mode-map (kbd "M-I") 'symbol-overlay-remove-all) + (define-key symbol-overlay-mode-map (kbd "M-n") 'symbol-overlay-jump-next) + (define-key symbol-overlay-mode-map (kbd "M-p") 'symbol-overlay-jump-prev))) -(require-package 'highlight-symbol) -(dolist (hook '(prog-mode-hook html-mode-hook css-mode-hook)) - (add-hook hook 'highlight-symbol-mode) - (add-hook hook 'highlight-symbol-nav-mode)) -(add-hook 'org-mode-hook 'highlight-symbol-nav-mode) -(after-load 'highlight-symbol - (diminish 'highlight-symbol-mode) - (defadvice highlight-symbol-temp-highlight (around sanityinc/maybe-suppress activate) - "Suppress symbol highlighting while isearching." - (unless (or isearch-mode - (and (boundp 'multiple-cursors-mode) multiple-cursors-mode)) - ad-do-it))) - -;;---------------------------------------------------------------------------- -;; Zap *up* to char is a handy pair for zap-to-char -;;---------------------------------------------------------------------------- -(autoload 'zap-up-to-char "misc" "Kill up to, but not including ARGth occurrence of CHAR.") +;;; Zap *up* to char is a handy pair for zap-to-char (global-set-key (kbd "M-Z") 'zap-up-to-char) @@ -103,54 +122,40 @@ (require-package 'browse-kill-ring) (setq browse-kill-ring-separator "\f") (global-set-key (kbd "M-Y") 'browse-kill-ring) -(after-load 'browse-kill-ring +(with-eval-after-load 'browse-kill-ring (define-key browse-kill-ring-mode-map (kbd "C-g") 'browse-kill-ring-quit) (define-key browse-kill-ring-mode-map (kbd "M-n") 'browse-kill-ring-forward) (define-key browse-kill-ring-mode-map (kbd "M-p") 'browse-kill-ring-previous)) -(after-load 'page-break-lines - (push 'browse-kill-ring-mode page-break-lines-modes)) +(with-eval-after-load 'page-break-lines + (add-to-list 'page-break-lines-modes 'browse-kill-ring-mode)) -;;---------------------------------------------------------------------------- ;; Don't disable narrowing commands -;;---------------------------------------------------------------------------- (put 'narrow-to-region 'disabled nil) (put 'narrow-to-page 'disabled nil) (put 'narrow-to-defun 'disabled nil) - -;;---------------------------------------------------------------------------- -;; Show matching parens -;;---------------------------------------------------------------------------- -(show-paren-mode 1) - -;;---------------------------------------------------------------------------- -;; Expand region -;;---------------------------------------------------------------------------- -(require-package 'expand-region) -(global-set-key (kbd "C-=") 'er/expand-region) - - -;;---------------------------------------------------------------------------- ;; Don't disable case-change functions -;;---------------------------------------------------------------------------- (put 'upcase-region 'disabled nil) (put 'downcase-region 'disabled nil) -;;---------------------------------------------------------------------------- -;; Rectangle selections, and overwrite text when the selection is active -;;---------------------------------------------------------------------------- -(cua-selection-mode t) ; for rectangles, CUA is nice +;; Show matching parens +(add-hook 'after-init-hook 'show-paren-mode) + +(when (fboundp 'repeat-mode) + (add-hook 'after-init-hook 'repeat-mode)) + + +;;; Handy key bindings +(with-eval-after-load 'help + (define-key help-map "A" 'describe-face)) -;;---------------------------------------------------------------------------- -;; Handy key bindings -;;---------------------------------------------------------------------------- (global-set-key (kbd "C-.") 'set-mark-command) (global-set-key (kbd "C-x C-.") 'pop-global-mark) (when (maybe-require-package 'avy) - (global-set-key (kbd "C-;") 'avy-goto-word-or-subword-1)) + (global-set-key (kbd "C-;") 'avy-goto-char-timer)) (require-package 'multiple-cursors) ;; multiple-cursors @@ -158,19 +163,11 @@ (global-set-key (kbd "C->") 'mc/mark-next-like-this) (global-set-key (kbd "C-+") 'mc/mark-next-like-this) (global-set-key (kbd "C-c C-<") 'mc/mark-all-like-this) -;; From active region to multiple cursors: -(global-set-key (kbd "C-c c r") 'set-rectangular-region-anchor) -(global-set-key (kbd "C-c c c") 'mc/edit-lines) -(global-set-key (kbd "C-c c e") 'mc/edit-ends-of-lines) -(global-set-key (kbd "C-c c a") 'mc/edit-beginnings-of-lines) - ;; Train myself to use M-f and M-b instead (global-unset-key [M-left]) (global-unset-key [M-right]) - - (defun kill-back-to-indentation () "Kill from point back to the first non-whitespace character on the line." (interactive) @@ -181,106 +178,58 @@ (global-set-key (kbd "C-M-") 'kill-back-to-indentation) -;;---------------------------------------------------------------------------- -;; Page break lines -;;---------------------------------------------------------------------------- -(require-package 'page-break-lines) -(global-page-break-lines-mode) -(diminish 'page-break-lines-mode) + +;;; Page break lines + +(when (maybe-require-package 'page-break-lines) + (add-hook 'after-init-hook 'global-page-break-lines-mode) + (with-eval-after-load 'page-break-lines + (diminish 'page-break-lines-mode))) -;;---------------------------------------------------------------------------- + + ;; Shift lines up and down with M-up and M-down. When paredit is enabled, ;; it will use those keybindings. For this reason, you might prefer to ;; use M-S-up and M-S-down, which will work even in lisp modes. -;;---------------------------------------------------------------------------- + (require-package 'move-dup) -(global-set-key [M-up] 'md/move-lines-up) -(global-set-key [M-down] 'md/move-lines-down) -(global-set-key [M-S-up] 'md/move-lines-up) -(global-set-key [M-S-down] 'md/move-lines-down) - -(global-set-key (kbd "C-c d") 'md/duplicate-down) -(global-set-key (kbd "C-c D") 'md/duplicate-up) - -;;---------------------------------------------------------------------------- -;; Fix backward-up-list to understand quotes, see http://bit.ly/h7mdIL -;;---------------------------------------------------------------------------- -(defun backward-up-sexp (arg) +(global-set-key [M-S-up] 'move-dup-move-lines-up) +(global-set-key [M-S-down] 'move-dup-move-lines-down) + +(global-set-key (kbd "C-c d") 'move-dup-duplicate-down) +(global-set-key (kbd "C-c u") 'move-dup-duplicate-up) + + +;;; Fix backward-up-list to understand quotes, see http://bit.ly/h7mdIL + +(defun sanityinc/backward-up-sexp (arg) "Jump up to the start of the ARG'th enclosing sexp." (interactive "p") (let ((ppss (syntax-ppss))) (cond ((elt ppss 3) (goto-char (elt ppss 8)) - (backward-up-sexp (1- arg))) + (sanityinc/backward-up-sexp (1- arg))) ((backward-up-list arg))))) -(global-set-key [remap backward-up-list] 'backward-up-sexp) ; C-M-u, C-M-up +(global-set-key [remap backward-up-list] 'sanityinc/backward-up-sexp) ; C-M-u, C-M-up -;;---------------------------------------------------------------------------- -;; Cut/copy the current line if no region is active -;;---------------------------------------------------------------------------- + +;;; Cut/copy the current line if no region is active (require-package 'whole-line-or-region) -(whole-line-or-region-mode t) -(diminish 'whole-line-or-region-mode) -(make-variable-buffer-local 'whole-line-or-region-mode) - -(defun suspend-mode-during-cua-rect-selection (mode-name) - "Add an advice to suspend `MODE-NAME' while selecting a CUA rectangle." - (let ((flagvar (intern (format "%s-was-active-before-cua-rectangle" mode-name))) - (advice-name (intern (format "suspend-%s" mode-name)))) - (eval-after-load 'cua-rect - `(progn - (defvar ,flagvar nil) - (make-variable-buffer-local ',flagvar) - (defadvice cua--activate-rectangle (after ,advice-name activate) - (setq ,flagvar (and (boundp ',mode-name) ,mode-name)) - (when ,flagvar - (,mode-name 0))) - (defadvice cua--deactivate-rectangle (after ,advice-name activate) - (when ,flagvar - (,mode-name 1))))))) - -(suspend-mode-during-cua-rect-selection 'whole-line-or-region-mode) +(add-hook 'after-init-hook 'whole-line-or-region-global-mode) +(with-eval-after-load 'whole-line-or-region + (diminish 'whole-line-or-region-local-mode)) +;; M-^ is inconvenient, so also bind M-j +(global-set-key (kbd "M-j") 'join-line) -(defun sanityinc/open-line-with-reindent (n) - "A version of `open-line' which reindents the start and end positions. -If there is a fill prefix and/or a `left-margin', insert them -on the new line if the line would have been blank. -With arg N, insert N newlines." - (interactive "*p") - (let* ((do-fill-prefix (and fill-prefix (bolp))) - (do-left-margin (and (bolp) (> (current-left-margin) 0))) - (loc (point-marker)) - ;; Don't expand an abbrev before point. - (abbrev-mode nil)) - (delete-horizontal-space t) - (newline n) - (indent-according-to-mode) - (when (eolp) - (delete-horizontal-space t)) - (goto-char loc) - (while (> n 0) - (cond ((bolp) - (if do-left-margin (indent-to (current-left-margin))) - (if do-fill-prefix (insert-and-inherit fill-prefix)))) - (forward-line 1) - (setq n (1- n))) - (goto-char loc) - (end-of-line) - (indent-according-to-mode))) - -(global-set-key (kbd "C-o") 'sanityinc/open-line-with-reindent) - - -;;---------------------------------------------------------------------------- + ;; Random line sorting -;;---------------------------------------------------------------------------- -(defun sort-lines-random (beg end) - "Sort lines in region randomly." +(defun sanityinc/sort-lines-random (beg end) + "Sort lines in region from BEG to END randomly." (interactive "r") (save-excursion (save-restriction @@ -291,19 +240,29 @@ With arg N, insert N newlines." (sort-subr nil 'forward-line 'end-of-line nil nil (lambda (s1 s2) (eq (random 2) 0))))))) - (require-package 'highlight-escape-sequences) -(hes-mode) +(add-hook 'after-init-hook 'hes-mode) -(require-package 'guide-key) -(setq guide-key/guide-key-sequence '("C-x" "C-c" "C-x 4" "C-x 5" "C-c ;" "C-c ; f" "C-c ' f" "C-x n" "C-x C-r" "C-x r" "M-s" "C-h")) -(add-hook 'after-init-hook - (lambda () - (guide-key-mode 1) - (diminish 'guide-key-mode))) +(require-package 'which-key) +(add-hook 'after-init-hook 'which-key-mode) +(setq-default which-key-idle-delay 1.5) +(with-eval-after-load 'which-key + (diminish 'which-key-mode)) + + +(defun sanityinc/disable-features-during-macro-call (orig &rest args) + "When running a macro, disable features that might be expensive. +ORIG is the advised function, which is called with its ARGS." + (let (post-command-hook + font-lock-mode + (tab-always-indent (or (eq 'complete tab-always-indent) tab-always-indent))) + (apply orig args))) + +(advice-add 'kmacro-call-macro :around 'sanityinc/disable-features-during-macro-call) (provide 'init-editing-utils) +;;; init-editing-utils.el ends here diff --git a/lisp/init-eglot.el b/lisp/init-eglot.el new file mode 100644 index 0000000000..5b13d68e6a --- /dev/null +++ b/lisp/init-eglot.el @@ -0,0 +1,13 @@ +;;; init-eglot.el --- LSP support via eglot -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(when (maybe-require-package 'eglot) + (maybe-require-package 'consult-eglot)) + + + +(provide 'init-eglot) +;;; init-eglot.el ends here diff --git a/lisp/init-elm.el b/lisp/init-elm.el index 85cac639c1..aa1c8a5c61 100644 --- a/lisp/init-elm.el +++ b/lisp/init-elm.el @@ -1,10 +1,13 @@ +;;; init-elm.el --- Support for the Elm language -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + (when (maybe-require-package 'elm-mode) - (after-load 'elm-mode + (with-eval-after-load 'elm-mode (diminish 'elm-indent-mode) - (when (executable-find "elm-oracle") - (add-hook 'elm-mode-hook 'elm-oracle-setup-completion))) - (when (maybe-require-package 'flycheck-elm) - (after-load 'elm-mode - (flycheck-elm-setup)))) + (when (executable-find "elm-format") + (setq-default elm-format-on-save t))) + (maybe-require-package 'elm-test-runner)) (provide 'init-elm) +;;; init-elm.el ends here diff --git a/lisp/init-elpa.el b/lisp/init-elpa.el index fb39c7bc77..e0f7f65630 100644 --- a/lisp/init-elpa.el +++ b/lisp/init-elpa.el @@ -1,47 +1,30 @@ -;;; Find and load the correct package.el - -;; When switching between Emacs 23 and 24, we always use the bundled package.el in Emacs 24 -(let ((package-el-site-lisp-dir - (expand-file-name "site-lisp/package" user-emacs-directory))) - (when (and (file-directory-p package-el-site-lisp-dir) - (> emacs-major-version 23)) - (message "Removing local package.el from load-path to avoid shadowing bundled version") - (setq load-path (remove package-el-site-lisp-dir load-path)))) +;;; init-elpa.el --- Settings and helpers for package.el -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: (require 'package) - +(require 'cl-lib) -;;; Standard package repositories - -(when (< emacs-major-version 24) - ;; Mainly for ruby-mode - (add-to-list 'package-archives '("marmalade" . "http://marmalade-repo.org/packages/"))) +;;; Install into separate package dirs for each Emacs version, to prevent bytecode incompatibility +(setq package-user-dir + (expand-file-name (format "elpa-%s.%s" emacs-major-version emacs-minor-version) + user-emacs-directory)) -;; We include the org repository for completeness, but don't normally -;; use it. -(add-to-list 'package-archives '("org" . "http://orgmode.org/elpa/")) -(when (< emacs-major-version 24) - (add-to-list 'package-archives '("gnu" . "http://elpa.gnu.org/packages/"))) + +;;; Standard package repositories -;;; Also use Melpa for most packages -(add-to-list 'package-archives `("melpa" . ,(if (< emacs-major-version 24) - "http://melpa.org/packages/" - "https://melpa.org/packages/"))) +(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t) +(add-to-list 'package-unsigned-archives "melpa") +;; Official MELPA Mirror, in case necessary. +;;(add-to-list 'package-archives (cons "melpa-mirror" (concat proto "://www.mirrorservice.org/sites/melpa.org/packages/")) t) -;; If gpg cannot be found, signature checking will fail, so we -;; conditionally enable it according to whether gpg is available. We -;; re-run this check once $PATH has been configured -(defun sanityinc/package-maybe-enable-signatures () - (setq package-check-signature (when (executable-find "gpg") 'allow-unsigned))) - -(sanityinc/package-maybe-enable-signatures) -(after-load 'init-exec-path - (sanityinc/package-maybe-enable-signatures)) - +;; Work-around for https://debbugs.gnu.org/cgi/bugreport.cgi?bug=34341 +(when (and (version< emacs-version "26.3") (boundp 'libgnutls-version) (>= libgnutls-version 30604)) + (setq gnutls-algorithm-priority "NORMAL:-VERS-TLS1.3")) ;;; On-demand installation of packages @@ -50,17 +33,20 @@ "Install given PACKAGE, optionally requiring MIN-VERSION. If NO-REFRESH is non-nil, the available package lists will not be re-downloaded in order to locate PACKAGE." - (if (package-installed-p package min-version) - t - (if (or (assoc package package-archive-contents) no-refresh) - (if (boundp 'package-selected-packages) - ;; Record this as a package the user installed explicitly - (package-install package nil) - (package-install package)) - (progn - (package-refresh-contents) - (require-package package min-version t))))) - + (when (stringp min-version) + (setq min-version (version-to-list min-version))) + (or (package-installed-p package min-version) + (let* ((known (cdr (assoc package package-archive-contents))) + (best (car (sort known (lambda (a b) + (version-list-<= (package-desc-version b) + (package-desc-version a))))))) + (if (and best (version-list-<= min-version (package-desc-version best))) + (package-install best) + (if no-refresh + (error "No version of %s >= %S is available" package min-version) + (package-refresh-contents) + (require-package package min-version t))) + (package-installed-p package min-version)))) (defun maybe-require-package (package &optional min-version no-refresh) "Try to install PACKAGE, and return non-nil if successful. @@ -71,24 +57,64 @@ locate PACKAGE." (condition-case err (require-package package min-version no-refresh) (error - (message "Couldn't install package `%s': %S" package err) + (message "Couldn't install optional package `%s': %S" package err) nil))) ;;; Fire up package.el (setq package-enable-at-startup nil) +(setq package-native-compile t) (package-initialize) + +;; package.el updates the saved version of package-selected-packages correctly only +;; after custom-file has been loaded, which is a bug. We work around this by adding +;; the required packages to package-selected-packages after startup is complete. + +(defvar sanityinc/required-packages nil) + +(defun sanityinc/note-selected-package (oldfun package &rest args) + "If OLDFUN reports PACKAGE was successfully installed, note that fact. +The package name is noted by adding it to +`sanityinc/required-packages'. This function is used as an +advice for `require-package', to which ARGS are passed." + (let ((available (apply oldfun package args))) + (prog1 + available + (when available + (add-to-list 'sanityinc/required-packages package))))) + +(advice-add 'require-package :around 'sanityinc/note-selected-package) -(require-package 'fullframe) -(fullframe list-packages quit-window) +;; Work around an issue in Emacs 29 where seq gets implicitly +;; reinstalled via the rg -> transient dependency chain, but fails to +;; reload cleanly due to not finding seq-25.el, breaking first-time +;; start-up +;; See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=67025 +(when (string= "29.1" emacs-version) + (defun sanityinc/reload-previously-loaded-with-load-path-updated (orig pkg-desc) + (let ((load-path (cons (package-desc-dir pkg-desc) load-path))) + (funcall orig pkg-desc))) + + (advice-add 'package--reload-previously-loaded :around + 'sanityinc/reload-previously-loaded-with-load-path-updated)) -(require-package 'cl-lib) -(require 'cl-lib) +(when (fboundp 'package--save-selected-packages) + (require-package 'seq) + (add-hook 'after-init-hook + (lambda () + (package--save-selected-packages + (seq-uniq (append sanityinc/required-packages package-selected-packages)))))) + + +(let ((package-check-signature nil)) + (require-package 'gnu-elpa-keyring-update)) + + (defun sanityinc/set-tabulated-list-column-width (col-name width) "Set any column with name COL-NAME to the given WIDTH." (when (> width (length col-name)) @@ -107,3 +133,4 @@ locate PACKAGE." (provide 'init-elpa) +;;; init-elpa.el ends here diff --git a/lisp/init-erlang.el b/lisp/init-erlang.el index 02f2ef4bc1..7b5755a1eb 100644 --- a/lisp/init-erlang.el +++ b/lisp/init-erlang.el @@ -1,8 +1,10 @@ -(ignore-errors - (require-package 'erlang)) +;;; init-erlang.el --- Support for the Erlang language -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: -(when (package-installed-p 'erlang) +(when (maybe-require-package 'erlang) (require 'erlang-start)) (provide 'init-erlang) +;;; init-erlang.el ends here diff --git a/lisp/init-exec-path.el b/lisp/init-exec-path.el index 763c5291c4..494b4c486a 100644 --- a/lisp/init-exec-path.el +++ b/lisp/init-exec-path.el @@ -1,11 +1,18 @@ +;;; init-exec-path.el --- Set up exec-path to help Emacs find programs -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + (require-package 'exec-path-from-shell) -(after-load 'exec-path-from-shell - (dolist (var '("SSH_AUTH_SOCK" "SSH_AGENT_PID" "GPG_AGENT_INFO" "LANG" "LC_CTYPE")) +(with-eval-after-load 'exec-path-from-shell + (dolist (var '("SSH_AUTH_SOCK" "SSH_AGENT_PID" "GPG_AGENT_INFO" "LANG" "LC_CTYPE" "NIX_SSL_CERT_FILE" "NIX_PATH")) (add-to-list 'exec-path-from-shell-variables var))) -(when (memq window-system '(mac ns x)) +(when (or (memq window-system '(mac ns x pgtk)) + (unless (memq system-type '(ms-dos windows-nt)) + (daemonp))) (exec-path-from-shell-initialize)) (provide 'init-exec-path) +;;; init-exec-path.el ends here diff --git a/lisp/init-fci.el b/lisp/init-fci.el deleted file mode 100644 index 9c0c9f6910..0000000000 --- a/lisp/init-fci.el +++ /dev/null @@ -1,39 +0,0 @@ -;; Fill column indicator -(when (eval-when-compile (> emacs-major-version 23)) - (require-package 'fill-column-indicator) - (defun sanityinc/prog-mode-fci-settings () - (turn-on-fci-mode) - (when show-trailing-whitespace - (set (make-local-variable 'whitespace-style) '(face trailing)) - (whitespace-mode 1))) - - ;;(add-hook 'prog-mode-hook 'sanityinc/prog-mode-fci-settings) - - (defun sanityinc/fci-enabled-p () - (bound-and-true-p fci-mode)) - - (defvar sanityinc/fci-mode-suppressed nil) - (make-variable-buffer-local 'sanityinc/fci-mode-suppressed) - - (defadvice popup-create (before suppress-fci-mode activate) - "Suspend fci-mode while popups are visible" - (let ((fci-enabled (sanityinc/fci-enabled-p))) - (when fci-enabled - (setq sanityinc/fci-mode-suppressed fci-enabled) - (turn-off-fci-mode)))) - (defadvice popup-delete (after restore-fci-mode activate) - "Restore fci-mode when all popups have closed" - (when (and sanityinc/fci-mode-suppressed - (null popup-instances)) - (setq sanityinc/fci-mode-suppressed nil) - (turn-on-fci-mode))) - - ;; Regenerate fci-mode line images after switching themes - (defadvice enable-theme (after recompute-fci-face activate) - (dolist (buffer (buffer-list)) - (with-current-buffer buffer - (when (sanityinc/fci-enabled-p) - (turn-on-fci-mode)))))) - - -(provide 'init-fci) diff --git a/lisp/init-flycheck.el b/lisp/init-flycheck.el deleted file mode 100644 index 3d5a398e65..0000000000 --- a/lisp/init-flycheck.el +++ /dev/null @@ -1,6 +0,0 @@ -(when (maybe-require-package 'flycheck) - (add-hook 'after-init-hook 'global-flycheck-mode) - (setq flycheck-display-errors-function #'flycheck-display-error-messages-unless-error-list)) - - -(provide 'init-flycheck) diff --git a/lisp/init-flymake.el b/lisp/init-flymake.el new file mode 100644 index 0000000000..a8b3a45c24 --- /dev/null +++ b/lisp/init-flymake.el @@ -0,0 +1,35 @@ +;;; init-flymake.el --- Configure Flymake global behaviour -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + +(maybe-require-package 'flymake "1.2.1") + +;; Use flycheck checkers with flymake, to extend its coverage +(when (maybe-require-package 'flymake-flycheck) + ;; Disable flycheck checkers for which we have flymake equivalents + (with-eval-after-load 'flycheck + (setq-default + flycheck-disabled-checkers + (append (default-value 'flycheck-disabled-checkers) + '(emacs-lisp emacs-lisp-checkdoc emacs-lisp-package sh-shellcheck)))) + + (add-hook 'flymake-mode-hook 'flymake-flycheck-auto) + (add-hook 'prog-mode-hook 'flymake-mode) + (add-hook 'text-mode-hook 'flymake-mode)) + +(with-eval-after-load 'flymake + ;; Provide some flycheck-like bindings in flymake mode to ease transition + (define-key flymake-mode-map (kbd "C-c ! l") 'flymake-show-buffer-diagnostics) + (define-key flymake-mode-map (kbd "C-c ! n") 'flymake-goto-next-error) + (define-key flymake-mode-map (kbd "C-c ! p") 'flymake-goto-prev-error) + (define-key flymake-mode-map (kbd "C-c ! c") 'flymake-start)) + +(unless (version< emacs-version "28.1") + (setq eldoc-documentation-function 'eldoc-documentation-compose) + + (add-hook 'flymake-mode-hook + (lambda () + (add-hook 'eldoc-documentation-functions 'flymake-eldoc-function nil t)))) + +(provide 'init-flymake) +;;; init-flymake.el ends here diff --git a/lisp/init-flyspell.el b/lisp/init-flyspell.el deleted file mode 100644 index 80a695c3db..0000000000 --- a/lisp/init-flyspell.el +++ /dev/null @@ -1,29 +0,0 @@ -;;---------------------------------------------------------------------------- -;; Add spell-checking in comments for all programming language modes -;;---------------------------------------------------------------------------- -(if (fboundp 'prog-mode) - (add-hook 'prog-mode-hook 'flyspell-prog-mode) - (dolist (hook '(lisp-mode-hook - emacs-lisp-mode-hook - scheme-mode-hook - clojure-mode-hook - ruby-mode-hook - yaml-mode - python-mode-hook - shell-mode-hook - php-mode-hook - css-mode-hook - haskell-mode-hook - caml-mode-hook - nxml-mode-hook - crontab-mode-hook - perl-mode-hook - tcl-mode-hook - javascript-mode-hook)) - (add-hook hook 'flyspell-prog-mode))) - -(after-load 'flyspell - (add-to-list 'flyspell-prog-text-faces 'nxml-text-face)) - - -(provide 'init-flyspell) diff --git a/lisp/init-folding.el b/lisp/init-folding.el new file mode 100644 index 0000000000..6448f59d77 --- /dev/null +++ b/lisp/init-folding.el @@ -0,0 +1,12 @@ +;;; init-folding.el --- Support code and region folding -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + +(when (maybe-require-package 'origami) + (with-eval-after-load 'origami + (define-key origami-mode-map (kbd "C-c f") 'origami-recursively-toggle-node) + (define-key origami-mode-map (kbd "C-c F") 'origami-toggle-all-nodes))) + + +(provide 'init-folding) +;;; init-folding.el ends here diff --git a/lisp/init-fonts.el b/lisp/init-fonts.el deleted file mode 100644 index b5f0498360..0000000000 --- a/lisp/init-fonts.el +++ /dev/null @@ -1,24 +0,0 @@ -;;; Character sets - - -;;; Changing font sizes - -(require-package 'default-text-scale) -(global-set-key (kbd "C-M-=") 'default-text-scale-increase) -(global-set-key (kbd "C-M--") 'default-text-scale-decrease) - - -(defun sanityinc/maybe-adjust-visual-fill-column () - "Readjust visual fill column when the global font size is modified. -This is helpful for writeroom-mode, in particular." - ;; TODO: submit as patch - (if visual-fill-column-mode - (add-hook 'after-setting-font-hook 'visual-fill-column--adjust-window nil t) - (remove-hook 'after-setting-font-hook 'visual-fill-column--adjust-window t))) - -(add-hook 'visual-fill-column-mode-hook - 'sanityinc/maybe-adjust-visual-fill-column) - - - -(provide 'init-fonts) diff --git a/lisp/init-frame-hooks.el b/lisp/init-frame-hooks.el index 556976de25..ea7208b745 100644 --- a/lisp/init-frame-hooks.el +++ b/lisp/init-frame-hooks.el @@ -1,3 +1,7 @@ +;;; init-frame-hooks.el --- Provide specific hooks for GUI/TTY frame creation -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + (defvar after-make-console-frame-hooks '() "Hooks to run after creating a new TTY frame") (defvar after-make-window-system-frame-hooks '() @@ -23,3 +27,4 @@ Selectively runs either `after-make-console-frame-hooks' or (provide 'init-frame-hooks) +;;; init-frame-hooks.el ends here diff --git a/lisp/init-git.el b/lisp/init-git.el index a44399e0a6..0a14da3aa6 100644 --- a/lisp/init-git.el +++ b/lisp/init-git.el @@ -1,43 +1,53 @@ +;;; init-git.el --- Git SCM support -*- lexical-binding: t -*- +;;; Commentary: + +;; See also init-github.el. + +;;; Code: + ;; TODO: link commits from vc-log to magit-show-commit ;; TODO: smerge-mode -(require-package 'git-blame) -(require-package 'gitignore-mode) -(require-package 'gitconfig-mode) -(require-package 'git-messenger) ;; Though see also vc-annotate's "n" & "p" bindings -(maybe-require-package 'git-timemachine) +(require-package 'git-modes) +(when (maybe-require-package 'git-timemachine) + (global-set-key (kbd "C-x v t") 'git-timemachine-toggle)) +(require-package 'git-link) (when (maybe-require-package 'magit) - (setq-default - magit-process-popup-time 10 - magit-diff-refine-hunk t) + (setq-default magit-diff-refine-hunk 'all) - ;; Hint: customize `magit-repo-dirs' so that you can use C-u M-F12 to + (sanityinc/fullframe-mode 'magit-status-mode) + + ;; Hint: customize `magit-repository-directories' so that you can use C-u M-F12 to ;; quickly open magit on any one of your projects. (global-set-key [(meta f12)] 'magit-status) (global-set-key (kbd "C-x g") 'magit-status) - (global-set-key (kbd "C-x M-g") 'magit-dispatch-popup)) + (global-set-key (kbd "C-x M-g") 'magit-dispatch) -(after-load 'magit - (define-key magit-status-mode-map (kbd "C-M-") 'magit-section-up) - (add-hook 'magit-popup-mode-hook 'sanityinc/no-trailing-whitespace)) + (defun sanityinc/magit-or-vc-log-file (&optional prompt) + (interactive "P") + (if (and (buffer-file-name) + (eq 'Git (vc-backend (buffer-file-name)))) + (if prompt + (magit-log-buffer-file-popup) + (magit-log-buffer-file t)) + (vc-print-log))) -(require-package 'fullframe) -(after-load 'magit - (fullframe magit-status magit-mode-quit-window)) + (with-eval-after-load 'vc + (define-key vc-prefix-map (kbd "l") 'sanityinc/magit-or-vc-log-file))) -(when (maybe-require-package 'git-commit) - (add-hook 'git-commit-mode-hook 'goto-address-mode)) - -(when *is-a-mac* - (after-load 'magit - (add-hook 'magit-mode-hook (lambda () (local-unset-key [(meta h)]))))) +(with-eval-after-load 'magit + (define-key magit-status-mode-map (kbd "C-M-") 'magit-section-up)) +(maybe-require-package 'magit-todos) + +(add-hook 'git-commit-mode-hook 'goto-address-mode) ;; Convenient binding for vc-git-grep -(global-set-key (kbd "C-x v f") 'vc-git-grep) +(with-eval-after-load 'vc + (define-key vc-prefix-map (kbd "f") 'vc-git-grep)) @@ -51,7 +61,7 @@ ;; (magit-svn-mode))) ;; (add-hook 'magit-status-mode-hook #'sanityinc/maybe-enable-magit-svn-mode)) -(after-load 'compile +(with-eval-after-load 'compile (dolist (defn (list '(git-svn-updated "^\t[A-Z]\t\\(.*\\)$" 1 nil nil 0 1) '(git-svn-needs-update "^\\(.*\\): needs update$" 1 nil nil 2 1))) (add-to-list 'compilation-error-regexp-alist-alist defn) @@ -65,6 +75,8 @@ "^ \\([a-z\\-]+\\) +" (shell-command-to-string "git svn help") 1)))) +(autoload 'vc-git-root "vc-git") + (defun git-svn (dir command) "Run a git svn subcommand in DIR." (interactive (list (read-directory-name "Directory: ") @@ -73,9 +85,6 @@ (compilation-buffer-name-function (lambda (major-mode-name) "*git-svn*"))) (compile (concat "git svn " command)))) - -(require-package 'git-messenger) -(global-set-key (kbd "C-x v p") #'git-messenger:popup-message) - (provide 'init-git) +;;; init-git.el ends here diff --git a/lisp/init-github.el b/lisp/init-github.el index b1fbf113bc..e82e2b5c1d 100644 --- a/lisp/init-github.el +++ b/lisp/init-github.el @@ -1,3 +1,7 @@ +;;; init-github.el --- Github integration -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + (require 'init-git) (maybe-require-package 'yagist) @@ -5,9 +9,11 @@ (add-hook 'prog-mode-hook 'bug-reference-prog-mode) (maybe-require-package 'github-clone) -(maybe-require-package 'github-issues) -(maybe-require-package 'magit-gh-pulls) - +(maybe-require-package 'forge) +(maybe-require-package 'github-review) +(when (maybe-require-package 'flymake-actionlint) + (add-hook 'yaml-mode-hook 'flymake-actionlint-action-load-when-actions-file)) (provide 'init-github) +;;; init-github.el ends here diff --git a/lisp/init-grep.el b/lisp/init-grep.el index 86b09f7a68..7ff1954d63 100644 --- a/lisp/init-grep.el +++ b/lisp/init-grep.el @@ -1,14 +1,28 @@ +;;; init-grep.el --- Settings for grep and grep-like tools -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + (setq-default grep-highlight-matches t grep-scroll-output t) (when *is-a-mac* (setq-default locate-command "mdfind")) -(when (executable-find "ag") - (require-package 'ag) +(require-package 'wgrep) +(with-eval-after-load 'grep + (dolist (key (list (kbd "C-c C-q") (kbd "w"))) + (define-key grep-mode-map key 'wgrep-change-to-wgrep-mode))) + +(when (and (executable-find "ag") + (maybe-require-package 'ag)) (require-package 'wgrep-ag) (setq-default ag-highlight-search t) (global-set-key (kbd "M-?") 'ag-project)) +(when (and (executable-find "rg") + (maybe-require-package 'rg)) + (global-set-key (kbd "M-?") 'rg-project)) + (provide 'init-grep) +;;; init-grep.el ends here diff --git a/lisp/init-gui-frames.el b/lisp/init-gui-frames.el index 4198f7ce42..5fb5f937ea 100644 --- a/lisp/init-gui-frames.el +++ b/lisp/init-gui-frames.el @@ -1,6 +1,10 @@ -;;---------------------------------------------------------------------------- +;;; init-gui-frames.el --- Behaviour specific to non-TTY frames -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + + ;; Stop C-z from minimizing windows under OS X -;;---------------------------------------------------------------------------- + (defun sanityinc/maybe-suspend-frame () (interactive) (unless (and *is-a-mac* window-system) @@ -9,29 +13,28 @@ (global-set-key (kbd "C-z") 'sanityinc/maybe-suspend-frame) -;;---------------------------------------------------------------------------- + ;; Suppress GUI features -;;---------------------------------------------------------------------------- + (setq use-file-dialog nil) (setq use-dialog-box nil) (setq inhibit-startup-screen t) -(setq inhibit-startup-echo-area-message t) -;;---------------------------------------------------------------------------- -;; Show a marker in the left fringe for lines not in the buffer -;;---------------------------------------------------------------------------- -(setq indicate-empty-lines t) + +;; Window size and features +(setq-default + window-resize-pixelwise t + frame-resize-pixelwise t) -;;---------------------------------------------------------------------------- -;; Window size and features -;;---------------------------------------------------------------------------- (when (fboundp 'tool-bar-mode) (tool-bar-mode -1)) (when (fboundp 'set-scroll-bar-mode) (set-scroll-bar-mode nil)) +(menu-bar-mode -1) + (let ((no-border '(internal-border-width . 0))) (add-to-list 'default-frame-alist no-border) (add-to-list 'initial-frame-alist no-border)) @@ -56,13 +59,13 @@ ;; TODO: use seethru package instead? (global-set-key (kbd "M-C-8") (lambda () (interactive) (sanityinc/adjust-opacity nil -2))) (global-set-key (kbd "M-C-9") (lambda () (interactive) (sanityinc/adjust-opacity nil 2))) -(global-set-key (kbd "M-C-0") (lambda () (interactive) (modify-frame-parameters nil `((alpha . 100))))) +(global-set-key (kbd "M-C-7") (lambda () (interactive) (modify-frame-parameters nil `((alpha . 100))))) + + +(when *is-a-mac* + (when (maybe-require-package 'ns-auto-titlebar) + (ns-auto-titlebar-mode))) -(add-hook 'after-make-frame-functions - (lambda (frame) - (with-selected-frame frame - (unless window-system - (set-frame-parameter nil 'menu-bar-lines 0))))) (setq frame-title-format '((:eval (if (buffer-file-name) @@ -75,8 +78,20 @@ (lambda () (setq line-spacing 0))) + +;; Change global font size easily + +(require-package 'default-text-scale) +(add-hook 'after-init-hook 'default-text-scale-mode) + + (require-package 'disable-mouse) + +(when (fboundp 'pixel-scroll-precision-mode) + (pixel-scroll-precision-mode)) + (provide 'init-gui-frames) +;;; init-gui-frames.el ends here diff --git a/lisp/init-haml.el b/lisp/init-haml.el index de09b71548..924d2d9e0f 100644 --- a/lisp/init-haml.el +++ b/lisp/init-haml.el @@ -1,6 +1,11 @@ +;;; init-haml.el --- Haml template support -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + (require-package 'haml-mode) -(after-load 'haml-mode +(with-eval-after-load 'haml-mode (define-key haml-mode-map (kbd "C-o") 'open-line)) (provide 'init-haml) +;;; init-haml.el ends here diff --git a/lisp/init-haskell.el b/lisp/init-haskell.el index c5297d8145..1a061bb8b9 100644 --- a/lisp/init-haskell.el +++ b/lisp/init-haskell.el @@ -1,55 +1,48 @@ -;; TODO: https://wunki.org/posts/2014-05-17-haskell-packages-development.html -;; https://github.com/chrisdone/chrisdone-emacs/blob/master/config/haskell.el -;; TODO: ghci-ng -;; TODO: don't pop up *Warnings* if haskell-stylish-on-save fails -;; TODO: purescript-mode -(require-package 'haskell-mode) +;;; init-haskell.el --- Support the Haskell language -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: - -;; Use intero for completion and flycheck +(when (maybe-require-package 'haskell-mode) + (add-hook 'haskell-mode-hook 'subword-mode) + (add-hook 'haskell-cabal-mode 'subword-mode) -(when (maybe-require-package 'intero) - (after-load 'haskell-mode - (add-hook 'haskell-mode-hook 'intero-mode) - ) - (after-load 'intero - (after-load 'flycheck - (flycheck-add-next-checker 'intero - '(warning . haskell-hlint))))) + (add-hook 'haskell-mode-hook 'interactive-haskell-mode) + (add-auto-mode 'haskell-mode "\\.ghci\\'") -(add-auto-mode 'haskell-mode "\\.ghci\\'") + ;; Indentation + (add-hook 'haskell-mode-hook 'turn-on-haskell-indentation) - -;; Indentation -(add-hook 'haskell-mode-hook 'turn-on-haskell-indentation) + ;; Source code helpers - -;; Source code helpers + (add-hook 'haskell-mode-hook 'haskell-auto-insert-module-template) -(add-hook 'haskell-mode-hook 'haskell-auto-insert-module-template) + (when (maybe-require-package 'reformatter) + (reformatter-define hindent + :program "hindent" + :lighter " Hin") -(setq-default haskell-stylish-on-save t) + (defalias 'hindent-mode 'hindent-on-save-mode) -(when (maybe-require-package 'hindent) - (add-hook 'haskell-mode-hook 'hindent-mode) - (after-load 'haskell-mode - (define-key haskell-mode-map (kbd "M-q") 'hindent-reformat-decl-or-fill))) + (reformatter-define ormolu + :program "ormolu" + :lighter " Orm")) -(maybe-require-package 'hayoo) -(after-load 'haskell-mode - (define-key haskell-mode-map (kbd "C-c h") 'hoogle) - (define-key haskell-mode-map (kbd "C-o") 'open-line)) + (with-eval-after-load 'haskell-mode + (define-key haskell-mode-map (kbd "C-c h") 'hoogle) + (define-key haskell-mode-map (kbd "C-o") 'open-line)) -(after-load 'page-break-lines - (push 'haskell-mode page-break-lines-modes)) + (with-eval-after-load 'page-break-lines + (add-to-list 'page-break-lines-modes 'haskell-mode))) + + + +(maybe-require-package 'dhall-mode) -(after-load 'haskell - (define-key interactive-haskell-mode-map (kbd "M-N") 'haskell-goto-next-error) - (define-key interactive-haskell-mode-map (kbd "M-P") 'haskell-goto-prev-error)) (provide 'init-haskell) +;;; init-haskell.el ends here diff --git a/lisp/init-hippie-expand.el b/lisp/init-hippie-expand.el index db5a2a6a78..156819a1c5 100644 --- a/lisp/init-hippie-expand.el +++ b/lisp/init-hippie-expand.el @@ -1,3 +1,7 @@ +;;; init-hippie-expand.el --- Settings for hippie-expand -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + (global-set-key (kbd "M-/") 'hippie-expand) (setq hippie-expand-try-functions-list @@ -7,4 +11,5 @@ try-expand-dabbrev-all-buffers try-expand-dabbrev-from-kill)) -(provide 'init-hippie-expand) \ No newline at end of file +(provide 'init-hippie-expand) +;;; init-hippie-expand.el ends here diff --git a/lisp/init-html.el b/lisp/init-html.el index 047a3fc580..518c84379d 100644 --- a/lisp/init-html.el +++ b/lisp/init-html.el @@ -1,13 +1,19 @@ -(require-package 'tidy) -(add-hook 'html-mode-hook (lambda () (tidy-build-menu html-mode-map))) +;;; init-html.el --- Editing HTML -*- lexical-binding: t -*- +;;; Commentary: + +;; ERB is configured separately in init-ruby + +;;; Code: (require-package 'tagedit) -(after-load 'sgml-mode +(with-eval-after-load 'sgml-mode (tagedit-add-paredit-like-keybindings) + (define-key tagedit-mode-map (kbd "M-?") nil) + (define-key tagedit-mode-map (kbd "M-s") nil) (add-hook 'sgml-mode-hook (lambda () (tagedit-mode 1)))) (add-auto-mode 'html-mode "\\.\\(jsp\\|tmpl\\)\\'") -;; Note: ERB is configured in init-ruby-mode (provide 'init-html) +;;; init-html.el ends here diff --git a/lisp/init-http.el b/lisp/init-http.el new file mode 100644 index 0000000000..bacc852a67 --- /dev/null +++ b/lisp/init-http.el @@ -0,0 +1,26 @@ +;;; init-http.el --- Work with HTTP APIs -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + +(maybe-require-package 'httprepl) +(with-eval-after-load 'httprepl + (push '("image" . image) httprepl-content-type-alist) + (push '(image . ((lambda (b) (with-current-buffer b + (image-mode) + b)))) + httprepl-content-type-middleware-alist)) + + +(when (maybe-require-package 'restclient) + (add-auto-mode 'restclient-mode "\\.rest\\'") + + (defun sanityinc/restclient () + "Work with `rest' in the *restclient* buffer." + (interactive) + (with-current-buffer (get-buffer-create "*restclient*") + (restclient-mode) + (pop-to-buffer (current-buffer))))) + + +(provide 'init-http) +;;; init-http.el ends here diff --git a/lisp/init-ibuffer.el b/lisp/init-ibuffer.el index adc7c6dcc8..acc2960f5e 100644 --- a/lisp/init-ibuffer.el +++ b/lisp/init-ibuffer.el @@ -1,9 +1,10 @@ +;;; init-ibuffer.el --- ibuffer settings -*- lexical-binding: t -*- +;;; Commentary: + ;; TODO: enhance ibuffer-fontification-alist ;; See http://www.reddit.com/r/emacs/comments/21fjpn/fontifying_buffer_list_for_emacs_243/ -(require-package 'fullframe) -(after-load 'ibuffer - (fullframe ibuffer ibuffer-quit)) +;;; Code: (require-package 'ibuffer-vc) @@ -16,45 +17,40 @@ (setq-default ibuffer-show-empty-filter-groups nil) +(sanityinc/fullframe-mode 'ibuffer-mode) + -(after-load 'ibuffer +(with-eval-after-load 'ibuffer ;; Use human readable Size column instead of original one (define-ibuffer-column size-h (:name "Size" :inline t) - (cond - ((> (buffer-size) 1000000) (format "%7.1fM" (/ (buffer-size) 1000000.0))) - ((> (buffer-size) 1000) (format "%7.1fk" (/ (buffer-size) 1000.0))) - (t (format "%8d" (buffer-size)))))) - + (file-size-human-readable (buffer-size)))) -;; Explicitly require ibuffer-vc to get its column definitions, which -;; can't be autoloaded -(after-load 'ibuffer - (require 'ibuffer-vc)) ;; Modify the default ibuffer-formats (toggle with `) (setq ibuffer-formats '((mark modified read-only vc-status-mini " " - (name 18 18 :left :elide) + (name 22 22 :left :elide) " " (size-h 9 -1 :right) " " - (mode 16 16 :left :elide) + (mode 12 12 :left :elide) " " - filename-and-process) + vc-relative-file) (mark modified read-only vc-status-mini " " - (name 18 18 :left :elide) + (name 22 22 :left :elide) " " (size-h 9 -1 :right) " " - (mode 16 16 :left :elide) + (mode 14 14 :left :elide) " " - (vc-status 16 16 :left) + (vc-status 12 12 :left) " " - filename-and-process))) + vc-relative-file))) (setq ibuffer-filter-group-name-face 'font-lock-doc-face) (global-set-key (kbd "C-x C-b") 'ibuffer) (provide 'init-ibuffer) +;;; init-ibuffer.el ends here diff --git a/lisp/init-ido.el b/lisp/init-ido.el deleted file mode 100644 index a15c364739..0000000000 --- a/lisp/init-ido.el +++ /dev/null @@ -1,25 +0,0 @@ -;; Use C-f during file selection to switch to regular find-file -(ido-mode t) -(ido-everywhere t) -(setq ido-enable-flex-matching t) -(setq ido-use-filename-at-point nil) -(setq ido-auto-merge-work-directories-length -1) -(setq ido-use-virtual-buffers t) -(setq-default org-completion-use-ido t) -(setq-default magit-completing-read-function 'magit-ido-completing-read) - -(when (maybe-require-package 'ido-ubiquitous) - (ido-ubiquitous-mode t)) - -(require-package 'idomenu) - -;; Allow the same buffer to be open in different frames -(setq ido-default-buffer-method 'selected-window) - -;; http://www.reddit.com/r/emacs/comments/21a4p9/use_recentf_and_ido_together/cgbprem -(add-hook 'ido-setup-hook (lambda () (define-key ido-completion-map [up] 'previous-history-element))) - - - - -(provide 'init-ido) diff --git a/lisp/init-isearch.el b/lisp/init-isearch.el index 0bac4898b5..0c083ebdcd 100644 --- a/lisp/init-isearch.el +++ b/lisp/init-isearch.el @@ -1,26 +1,43 @@ +;;; init-isearch.el --- isearch settings -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + ;; Show number of matches while searching (when (maybe-require-package 'anzu) - (global-anzu-mode t) + (add-hook 'after-init-hook 'global-anzu-mode) (setq anzu-mode-lighter "") (global-set-key [remap query-replace-regexp] 'anzu-query-replace-regexp) (global-set-key [remap query-replace] 'anzu-query-replace)) -;; Activate occur easily inside isearch -(define-key isearch-mode-map (kbd "C-o") 'isearch-occur) +(with-eval-after-load 'isearch + ;; DEL during isearch should edit the search string, not jump back to the previous result + (define-key isearch-mode-map [remap isearch-delete-char] 'isearch-del-char) + + (defun sanityinc/isearch-occur () + "Invoke `consult-line' from isearch." + (interactive) + (let ((query (if isearch-regexp + isearch-string + (regexp-quote isearch-string)))) + (isearch-update-ring isearch-string isearch-regexp) + (let (search-nonincremental-instead) + (ignore-errors (isearch-done t t))) + (consult-line query))) + + (define-key isearch-mode-map (kbd "C-o") 'sanityinc/isearch-occur) + (define-key isearch-mode-map (kbd "C-c C-o") 'sanityinc/isearch-occur)) -;; DEL during isearch should edit the search string, not jump back to the previous result -(define-key isearch-mode-map [remap isearch-delete-char] 'isearch-del-char) ;; Search back/forth for the symbol at point ;; See http://www.emacswiki.org/emacs/SearchAtPoint (defun isearch-yank-symbol () "*Put symbol at current point into search string." (interactive) - (let ((sym (symbol-at-point))) + (let ((sym (thing-at-point 'symbol))) (if sym (progn (setq isearch-regexp t - isearch-string (concat "\\_<" (regexp-quote (symbol-name sym)) "\\_>") + isearch-string (concat "\\_<" (regexp-quote sym) "\\_>") isearch-message (mapconcat 'isearch-text-char-description isearch-string "") isearch-yank-flag t)) (ding))) @@ -29,46 +46,15 @@ (define-key isearch-mode-map "\C-\M-w" 'isearch-yank-symbol) -;; http://www.emacswiki.org/emacs/ZapToISearch -(defun zap-to-isearch (rbeg rend) - "Kill the region between the mark and the closest portion of -the isearch match string. The behaviour is meant to be analogous -to zap-to-char; let's call it zap-to-isearch. The deleted region -does not include the isearch word. This is meant to be bound only -in isearch mode. The point of this function is that oftentimes -you want to delete some portion of text, one end of which happens -to be an active isearch word. The observation to make is that if -you use isearch a lot to move the cursor around (as you should, -it is much more efficient than using the arrows), it happens a -lot that you could just delete the active region between the mark -and the point, not include the isearch word." - (interactive "r") - (when (not mark-active) - (error "Mark is not active")) - (let* ((isearch-bounds (list isearch-other-end (point))) - (ismin (apply 'min isearch-bounds)) - (ismax (apply 'max isearch-bounds)) - ) - (if (< (mark) ismin) - (kill-region (mark) ismin) - (if (> (mark) ismax) - (kill-region ismax (mark)) - (error "Internal error in isearch kill function."))) - (isearch-exit) - )) - -(define-key isearch-mode-map [(meta z)] 'zap-to-isearch) - - -;; http://www.emacswiki.org/emacs/ZapToISearch -(defun isearch-exit-other-end (rbeg rend) +(defun sanityinc/isearch-exit-other-end () "Exit isearch, but at the other end of the search string. This is useful when followed by an immediate kill." - (interactive "r") + (interactive) (isearch-exit) (goto-char isearch-other-end)) -(define-key isearch-mode-map [(control return)] 'isearch-exit-other-end) +(define-key isearch-mode-map [(control return)] 'sanityinc/isearch-exit-other-end) (provide 'init-isearch) +;;; init-isearch.el ends here diff --git a/lisp/init-ivy.el b/lisp/init-ivy.el deleted file mode 100644 index d134f7af3d..0000000000 --- a/lisp/init-ivy.el +++ /dev/null @@ -1,33 +0,0 @@ -(when (maybe-require-package 'ivy) - (after-load 'ivy - (setq-default ivy-use-virtual-buffers t - ivy-count-format "") - ;; IDO-style directory navigation - (define-key ivy-minibuffer-map (kbd "C-j") #'ivy-immediate-done) - (define-key ivy-minibuffer-map (kbd "RET") #'ivy-alt-done) - (when (maybe-require-package 'diminish) - (diminish 'ivy-mode))) - - (add-hook 'after-init-hook - (lambda () - (when (bound-and-true-p ido-ubiquitous-mode) - (ido-ubiquitous-mode -1) - (ido-mode -1)) - (ivy-mode 1)))) - - -(when (maybe-require-package 'counsel) - (setq-default counsel-mode-override-describe-bindings t) - (when (maybe-require-package 'diminish) - (after-load 'counsel - (diminish 'counsel-mode))) - (add-hook 'after-init-hook 'counsel-mode)) - - -;;(when (maybe-require-package 'swiper) -;; (after-load 'ivy -;; (define-key ivy-mode-map (kbd "C-s") 'swiper))) - - - -(provide 'init-ivy) diff --git a/lisp/init-j.el b/lisp/init-j.el new file mode 100644 index 0000000000..68e2c49bdd --- /dev/null +++ b/lisp/init-j.el @@ -0,0 +1,12 @@ +;;; init-j.el --- Basic support for programming in J -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + +(require-package 'j-mode) + +(setq-default j-console-cmd "jconsole") +(add-hook 'inferior-j-mode-hook (lambda () (electric-pair-mode -1))) + + +(provide 'init-j) +;;; init-j.el ends here diff --git a/lisp/init-javascript.el b/lisp/init-javascript.el index 8e871b2383..efb678c4d1 100644 --- a/lisp/init-javascript.el +++ b/lisp/init-javascript.el @@ -1,103 +1,89 @@ +;;; init-javascript.el --- Support for Javascript and derivatives -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + (maybe-require-package 'json-mode) (maybe-require-package 'js2-mode) -(maybe-require-package 'coffee-mode) +(maybe-require-package 'typescript-mode) +(maybe-require-package 'prettier-js) + + +;;; Basic js-mode setup -(defcustom preferred-javascript-mode - (first (remove-if-not #'fboundp '(js2-mode js-mode))) - "Javascript mode to use for .js files." - :type 'symbol - :group 'programming - :options '(js2-mode js-mode)) +(add-to-list 'auto-mode-alist '("\\.\\(js\\|es6\\)\\(\\.erb\\)?\\'" . js-mode)) -(defconst preferred-javascript-indent-level 2) +(with-eval-after-load 'js + (sanityinc/major-mode-lighter 'js-mode "JS") + (sanityinc/major-mode-lighter 'js-jsx-mode "JSX")) -;; Need to first remove from list if present, since elpa adds entries too, which -;; may be in an arbitrary order -(eval-when-compile (require 'cl)) -(setq auto-mode-alist (cons `("\\.\\(js\\|es6\\)\\(\\.erb\\)?\\'" . ,preferred-javascript-mode) - (loop for entry in auto-mode-alist - unless (eq preferred-javascript-mode (cdr entry)) - collect entry))) +(setq-default js-indent-level 2) + ;; js2-mode ;; Change some defaults: customize them to override -(setq-default js2-basic-offset 2 - js2-bounce-indent-p nil) -(after-load 'js2-mode +(setq-default js2-bounce-indent-p nil) +(with-eval-after-load 'js2-mode ;; Disable js2 mode's syntax error highlighting by default... (setq-default js2-mode-show-parse-errors nil js2-mode-show-strict-warnings nil) ;; ... but enable it if flycheck can't handle javascript (autoload 'flycheck-get-checker-for-buffer "flycheck") - (defun sanityinc/disable-js2-checks-if-flycheck-active () + (defun sanityinc/enable-js2-checks-if-flycheck-inactive () (unless (flycheck-get-checker-for-buffer) - (set (make-local-variable 'js2-mode-show-parse-errors) t) - (set (make-local-variable 'js2-mode-show-strict-warnings) t))) - (add-hook 'js2-mode-hook 'sanityinc/disable-js2-checks-if-flycheck-active) - - (add-hook 'js2-mode-hook (lambda () (setq mode-name "JS2"))) - - (after-load 'js2-mode - (js2-imenu-extras-setup))) + (setq-local js2-mode-show-parse-errors t) + (setq-local js2-mode-show-strict-warnings t) + (when (derived-mode-p 'js-mode) + (js2-minor-mode 1)))) + (add-hook 'js-mode-hook 'sanityinc/enable-js2-checks-if-flycheck-inactive) + (add-hook 'js2-mode-hook 'sanityinc/enable-js2-checks-if-flycheck-inactive) -;; js-mode -(setq-default js-indent-level preferred-javascript-indent-level) + (js2-imenu-extras-setup)) +(add-to-list 'interpreter-mode-alist (cons "node" 'js2-mode)) -(add-to-list 'interpreter-mode-alist (cons "node" preferred-javascript-mode)) +(with-eval-after-load 'js2-mode + (sanityinc/major-mode-lighter 'js2-mode "JS2") + (sanityinc/major-mode-lighter 'js2-jsx-mode "JSX2")) -;; Javascript nests {} and () a lot, so I find this helpful - -(when (and (executable-find "ag") +(require 'derived) +(when (and (or (executable-find "rg") (executable-find "ag")) (maybe-require-package 'xref-js2)) - (after-load 'js2-mode - (define-key js2-mode-map (kbd "M-.") nil) - (add-hook 'js2-mode-hook - (lambda () (add-hook 'xref-backend-functions #'xref-js2-xref-backend nil t))))) - + (when (executable-find "rg") + (setq-default xref-js2-search-program 'rg)) - -;;; Coffeescript + (defun sanityinc/enable-xref-js2 () + (add-hook 'xref-backend-functions #'xref-js2-xref-backend nil t)) -(after-load 'coffee-mode - (setq coffee-js-mode preferred-javascript-mode - coffee-tab-width preferred-javascript-indent-level)) + (let ((base-mode (if (fboundp 'js-base-mode) 'js-base-mode 'js-mode))) + (with-eval-after-load 'js + (add-hook (derived-mode-hook-name base-mode) 'sanityinc/enable-xref-js2) + (define-key js-mode-map (kbd "M-.") nil) + (when (boundp 'js-ts-mode-map) + (define-key js-ts-mode-map (kbd "M-.") nil)))) + (with-eval-after-load 'js2-mode + (define-key js2-mode-map (kbd "M-.") nil))) -(when (fboundp 'coffee-mode) - (add-to-list 'auto-mode-alist '("\\.coffee\\.erb\\'" . coffee-mode))) -;; --------------------------------------------------------------------------- + ;; Run and interact with an inferior JS via js-comint.el -;; --------------------------------------------------------------------------- (when (maybe-require-package 'js-comint) - (setq inferior-js-program-command "js") + (setq js-comint-program-command "node") (defvar inferior-js-minor-mode-map (make-sparse-keymap)) (define-key inferior-js-minor-mode-map "\C-x\C-e" 'js-send-last-sexp) - (define-key inferior-js-minor-mode-map "\C-\M-x" 'js-send-last-sexp-and-go) (define-key inferior-js-minor-mode-map "\C-cb" 'js-send-buffer) - (define-key inferior-js-minor-mode-map "\C-c\C-b" 'js-send-buffer-and-go) - (define-key inferior-js-minor-mode-map "\C-cl" 'js-load-file-and-go) (define-minor-mode inferior-js-keys-mode "Bindings for communicating with an inferior js interpreter." - nil " InfJS" inferior-js-minor-mode-map) + :init-value nil :lighter " InfJS" :keymap inferior-js-minor-mode-map) (dolist (hook '(js2-mode-hook js-mode-hook)) (add-hook hook 'inferior-js-keys-mode))) -;; --------------------------------------------------------------------------- -;; Alternatively, use skewer-mode -;; --------------------------------------------------------------------------- - -(when (maybe-require-package 'skewer-mode) - (after-load 'skewer-mode - (add-hook 'skewer-mode-hook - (lambda () (inferior-js-keys-mode -1))))) - (provide 'init-javascript) +;;; init-javascript.el ends here diff --git a/lisp/init-ledger.el b/lisp/init-ledger.el index 70680005e0..80a0163ab6 100644 --- a/lisp/init-ledger.el +++ b/lisp/init-ledger.el @@ -1,23 +1,26 @@ -(require-package 'ledger-mode) +;;; init-ledger.el --- Support for the ledger CLI accounting tool -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: -(add-to-list 'auto-mode-alist '("\\.ledger$" . ledger-mode)) +(when (maybe-require-package 'ledger-mode) + (when (maybe-require-package 'flycheck-ledger) + (with-eval-after-load 'flycheck + (with-eval-after-load 'ledger-mode + (require 'flycheck-ledger)))) -(when (maybe-require-package 'flycheck-ledger) - (after-load 'flycheck - (after-load 'ledger-mode - (require 'flycheck-ledger)))) + (with-eval-after-load 'ledger-mode + (define-key ledger-mode-map (kbd "RET") 'newline) + (define-key ledger-mode-map (kbd "C-o") 'open-line)) -(after-load 'ledger-mode - (define-key ledger-mode-map (kbd "RET") 'newline) - (define-key ledger-mode-map (kbd "C-o") 'open-line)) + (setq ledger-highlight-xact-under-point nil + ledger-use-iso-dates nil + ledger-mode-should-check-version nil) -(setq ledger-highlight-xact-under-point nil - ledger-use-iso-dates nil) + (with-eval-after-load 'ledger-mode + (when (memq window-system '(mac ns)) + (exec-path-from-shell-copy-env "LEDGER_FILE"))) -(after-load 'ledger-mode - (when (memq window-system '(mac ns)) - (exec-path-from-shell-copy-env "LEDGER_FILE"))) - -(add-hook 'ledger-mode-hook 'goto-address-prog-mode) + (add-hook 'ledger-mode-hook 'goto-address-prog-mode)) (provide 'init-ledger) +;;; init-ledger.el ends here diff --git a/lisp/init-lisp.el b/lisp/init-lisp.el index 2c80297d83..250969cdc0 100644 --- a/lisp/init-lisp.el +++ b/lisp/init-lisp.el @@ -1,14 +1,32 @@ +;;; init-lisp.el --- Emacs lisp settings, and common config for other lisps -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + +(setq-default debugger-bury-or-kill 'kill) + (require-package 'elisp-slime-nav) (dolist (hook '(emacs-lisp-mode-hook ielm-mode-hook)) (add-hook hook 'turn-on-elisp-slime-nav-mode)) (add-hook 'emacs-lisp-mode-hook (lambda () (setq mode-name "ELisp"))) -(require-package 'lively) - (setq-default initial-scratch-message (concat ";; Happy hacking, " user-login-name " - Emacs ♥ you!\n\n")) +(defun sanityinc/headerise-elisp () + "Add minimal header and footer to an elisp buffer in order to placate flycheck." + (interactive) + (let ((fname (if (buffer-file-name) + (file-name-nondirectory (buffer-file-name)) + (error "This buffer is not visiting a file")))) + (save-excursion + (goto-char (point-min)) + (insert ";;; " fname " --- Insert description here -*- lexical-binding: t -*-\n" + ";;; Commentary:\n" + ";;; Code:\n\n") + (goto-char (point-max)) + (insert ";;; " fname " ends here\n")))) + ;; Make C-x C-e run 'eval-region if the region is active @@ -19,32 +37,42 @@ (eval-region (min (point) (mark)) (max (point) (mark))) (pp-eval-last-sexp prefix))) -(after-load 'lisp-mode - (define-key emacs-lisp-mode-map (kbd "C-x C-e") 'sanityinc/eval-last-sexp-or-region)) +(global-set-key [remap eval-expression] 'pp-eval-expression) -(require-package 'ipretty) -(ipretty-mode 1) +(with-eval-after-load 'lisp-mode + (define-key emacs-lisp-mode-map (kbd "C-x C-e") 'sanityinc/eval-last-sexp-or-region) + (define-key emacs-lisp-mode-map (kbd "C-c C-e") 'pp-eval-expression)) +(when (maybe-require-package 'ipretty) + (add-hook 'after-init-hook 'ipretty-mode)) -(defadvice pp-display-expression (after sanityinc/make-read-only (expression out-buffer-name) activate) + +(defun sanityinc/make-read-only (_expression out-buffer-name &rest _) "Enable `view-mode' in the output buffer - if any - so it can be closed with `\"q\"." (when (get-buffer out-buffer-name) (with-current-buffer out-buffer-name (view-mode 1)))) +(advice-add 'pp-display-expression :after 'sanityinc/make-read-only) -;; Enhanced M-: - -(when (maybe-require-package 'eval-expr) - (global-set-key [remap eval-expression] 'eval-expr) - (setq eval-expr-print-function 'pp - eval-expr-print-level 20 - eval-expr-print-length 100) - (after-load 'eval-expr - (defun eval-expr-minibuffer-setup () - (set-syntax-table emacs-lisp-mode-syntax-table) - (paredit-mode)))) +(defun sanityinc/load-this-file () + "Load the current file or buffer. +The current directory is temporarily added to `load-path'. When +there is no current file, eval the current buffer." + (interactive) + (let ((load-path (cons default-directory load-path)) + (file (buffer-file-name))) + (if file + (progn + (save-some-buffers nil (apply-partially 'derived-mode-p 'emacs-lisp-mode)) + (load-file (buffer-file-name)) + (message "Loaded %s" file)) + (eval-buffer) + (message "Evaluated %s" (current-buffer))))) + +(with-eval-after-load 'lisp-mode + (define-key emacs-lisp-mode-map (kbd "C-c C-l") 'sanityinc/load-this-file)) @@ -61,9 +89,8 @@ ;; Use C-c C-z to toggle between elisp files and an ielm session ;; I might generalise this to ruby etc., or even just adopt the repl-toggle package. -(defvar sanityinc/repl-original-buffer nil +(defvar-local sanityinc/repl-original-buffer nil "Buffer from which we jumped to this REPL.") -(make-variable-buffer-local 'sanityinc/repl-original-buffer) (defvar sanityinc/repl-switch-function 'switch-to-buffer-other-window) @@ -80,49 +107,37 @@ (interactive) (if sanityinc/repl-original-buffer (funcall sanityinc/repl-switch-function sanityinc/repl-original-buffer) - (error "No original buffer."))) + (error "No original buffer"))) -(after-load 'lisp-mode +(with-eval-after-load 'elisp-mode (define-key emacs-lisp-mode-map (kbd "C-c C-z") 'sanityinc/switch-to-ielm)) -(after-load 'ielm +(with-eval-after-load 'ielm (define-key ielm-map (kbd "C-c C-z") 'sanityinc/repl-switch-back)) -;; ---------------------------------------------------------------------------- + ;; Hippie-expand -;; ---------------------------------------------------------------------------- (defun set-up-hippie-expand-for-elisp () "Locally set `hippie-expand' completion functions for use with Emacs Lisp." (make-local-variable 'hippie-expand-try-functions-list) (add-to-list 'hippie-expand-try-functions-list 'try-complete-lisp-symbol t) - (add-to-list 'hippie-expand-try-functions-list 'try-complete-lisp-symbol-partially t) - (add-to-list 'hippie-expand-try-functions-list 'my/try-complete-lisp-symbol-without-namespace t)) + (add-to-list 'hippie-expand-try-functions-list 'try-complete-lisp-symbol-partially t)) -;; ---------------------------------------------------------------------------- + ;; Automatic byte compilation -;; ---------------------------------------------------------------------------- + (when (maybe-require-package 'auto-compile) - (auto-compile-on-save-mode 1) - (auto-compile-on-load-mode 1)) + (setq auto-compile-delete-stray-dest nil) + (add-hook 'after-init-hook 'auto-compile-on-save-mode) + (add-hook 'after-init-hook 'auto-compile-on-load-mode)) -;; ---------------------------------------------------------------------------- + ;; Load .el if newer than corresponding .elc -;; ---------------------------------------------------------------------------- -(setq load-prefer-newer t) - -;; ---------------------------------------------------------------------------- -;; Highlight current sexp -;; ---------------------------------------------------------------------------- -(require-package 'hl-sexp) - -;; Prevent flickery behaviour due to hl-sexp-mode unhighlighting before each command -(after-load 'hl-sexp - (defadvice hl-sexp-mode (after unflicker (&optional turn-on) activate) - (when turn-on - (remove-hook 'pre-command-hook #'hl-sexp-unhighlight)))) +(setq load-prefer-newer t) + (require-package 'immortal-scratch) (add-hook 'after-init-hook 'immortal-scratch-mode) @@ -144,26 +159,15 @@ " "))))) -;; ---------------------------------------------------------------------------- + ;; Enable desired features for all lisp modes -;; ---------------------------------------------------------------------------- -(require-package 'redshank) -(after-load 'redshank - (diminish 'redshank-mode)) (defun sanityinc/enable-check-parens-on-save () "Run `check-parens' when the current buffer is saved." (add-hook 'after-save-hook #'check-parens nil t)) -(defun sanityinc/disable-indent-guide () - (when (bound-and-true-p indent-guide-mode) - (indent-guide-mode -1))) - (defvar sanityinc/lispy-modes-hook '(enable-paredit-mode - turn-on-eldoc-mode - redshank-mode - sanityinc/disable-indent-guide sanityinc/enable-check-parens-on-save) "Hook run in all Lisp modes.") @@ -175,43 +179,20 @@ "Enable features useful in any Lisp mode." (run-hooks 'sanityinc/lispy-modes-hook)) -(defun sanityinc/emacs-lisp-setup () - "Enable features useful when working with elisp." - (set-up-hippie-expand-for-elisp)) - -(defconst sanityinc/elispy-modes - '(emacs-lisp-mode ielm-mode) - "Major modes relating to elisp.") - -(defconst sanityinc/lispy-modes - (append sanityinc/elispy-modes - '(lisp-mode inferior-lisp-mode lisp-interaction-mode)) - "All lispy major modes.") - (require 'derived) -(dolist (hook (mapcar #'derived-mode-hook-name sanityinc/lispy-modes)) - (add-hook hook 'sanityinc/lisp-setup)) - -(dolist (hook (mapcar #'derived-mode-hook-name sanityinc/elispy-modes)) - (add-hook hook 'sanityinc/emacs-lisp-setup)) +(dolist (mode '(emacs-lisp-mode ielm-mode lisp-mode inferior-lisp-mode lisp-interaction-mode)) + (add-hook (derived-mode-hook-name mode) 'sanityinc/lisp-setup)) -(if (boundp 'eval-expression-minibuffer-setup-hook) - (add-hook 'eval-expression-minibuffer-setup-hook #'eldoc-mode) - (require-package 'eldoc-eval) - (require 'eldoc-eval) - (eldoc-in-minibuffer-mode 1)) +(when (boundp 'eval-expression-minibuffer-setup-hook) + (add-hook 'eval-expression-minibuffer-setup-hook #'eldoc-mode)) (add-to-list 'auto-mode-alist '("\\.emacs-project\\'" . emacs-lisp-mode)) (add-to-list 'auto-mode-alist '("archive-contents\\'" . emacs-lisp-mode)) -(require-package 'cl-lib-highlight) -(after-load 'lisp-mode - (cl-lib-highlight-initialize)) -;; ---------------------------------------------------------------------------- + ;; Delete .elc files when reverting the .el from VC or magit -;; ---------------------------------------------------------------------------- ;; When .el files are open, we can intercept when they are modified ;; by VC or magit in order to remove .elc files that are likely to @@ -225,7 +206,7 @@ (defvar sanityinc/vc-reverting nil "Whether or not VC or Magit is currently reverting buffers.") -(defadvice revert-buffer (after sanityinc/maybe-remove-elc activate) +(defun sanityinc/maybe-remove-elc (&rest _) "If reverting from VC, delete any .elc file that will now be out of sync." (when sanityinc/vc-reverting (when (and (eq 'emacs-lisp-mode major-mode) @@ -235,20 +216,20 @@ (when (file-exists-p elc) (message "Removing out-of-sync elc file %s" (file-name-nondirectory elc)) (delete-file elc)))))) +(advice-add 'revert-buffer :after 'sanityinc/maybe-remove-elc) -(defadvice magit-revert-buffers (around sanityinc/reverting activate) - (let ((sanityinc/vc-reverting t)) - ad-do-it)) -(defadvice vc-revert-buffer-internal (around sanityinc/reverting activate) +(defun sanityinc/reverting (orig &rest args) (let ((sanityinc/vc-reverting t)) - ad-do-it)) + (apply orig args))) +(advice-add 'magit-revert-buffers :around 'sanityinc/reverting) +(advice-add 'vc-revert-buffer-internal :around 'sanityinc/reverting) (require-package 'macrostep) -(after-load 'lisp-mode - (define-key emacs-lisp-mode-map (kbd "C-c e") 'macrostep-expand)) +(with-eval-after-load 'lisp-mode + (define-key emacs-lisp-mode-map (kbd "C-c x") 'macrostep-expand)) @@ -258,23 +239,14 @@ ;; Extras for theme editing - -(defvar sanityinc/theme-mode-hook nil - "Hook triggered when editing a theme file.") - -(defun sanityinc/run-theme-mode-hooks-if-theme () - "Run `sanityinc/theme-mode-hook' if this appears to a theme." - (when (string-match "\\(color-theme-\\|-theme\\.el\\)" (buffer-name)) - (run-hooks 'sanityinc/theme-mode-hook))) - -(add-hook 'emacs-lisp-mode-hook 'sanityinc/run-theme-mode-hooks-if-theme t) - (when (maybe-require-package 'rainbow-mode) - (add-hook 'sanityinc/theme-mode-hook 'rainbow-mode)) - -(when (maybe-require-package 'aggressive-indent) - ;; Can be prohibitively slow with very long forms - (add-to-list 'sanityinc/theme-mode-hook (lambda () (aggressive-indent-mode -1)) t)) + (defun sanityinc/enable-rainbow-mode-if-theme () + (when (and (buffer-file-name) (string-match-p "\\(color-theme-\\|-theme\\.el\\)" (buffer-file-name))) + (rainbow-mode))) + (add-hook 'emacs-lisp-mode-hook 'sanityinc/enable-rainbow-mode-if-theme) + (add-hook 'help-mode-hook 'rainbow-mode) + (with-eval-after-load 'rainbow-mode + (diminish 'rainbow-mode))) @@ -282,44 +254,24 @@ (add-hook 'emacs-lisp-mode-hook 'highlight-quoted-mode)) -(when (maybe-require-package 'flycheck) - (require-package 'flycheck-package) - (after-load 'flycheck - (flycheck-package-setup))) +(when (maybe-require-package 'package-lint-flymake) + (add-hook 'emacs-lisp-mode-hook #'package-lint-flymake-setup)) ;; ERT -(after-load 'ert +(with-eval-after-load 'ert (define-key ert-results-mode-map (kbd "g") 'ert-results-rerun-all-tests)) -(defun sanityinc/cl-libify-next () - "Find next symbol from 'cl and replace it with the 'cl-lib equivalent." - (interactive) - (let ((case-fold-search nil)) - (re-search-forward - (concat - "(" - (regexp-opt - ;; Not an exhaustive list - '("loop" "incf" "plusp" "first" "decf" "minusp" "assert" - "case" "destructuring-bind" "second" "third" "defun*" - "defmacro*" "return-from" "labels" "cadar" "fourth" - "cadadr") t) - "\\_>"))) - (let ((form (match-string 1))) - (backward-sexp) - (cond - ((string-match "^\\(defun\\|defmacro\\)\\*$") - (kill-sexp) - (insert (concat "cl-" (match-string 1)))) - (t - (insert "cl-"))) - (when (fboundp 'aggressive-indent-indent-defun) - (aggressive-indent-indent-defun)))) +(maybe-require-package 'cl-libify) + + +(maybe-require-package 'flycheck-relint) + (maybe-require-package 'cask-mode) (provide 'init-lisp) +;;; init-lisp.el ends here diff --git a/lisp/init-locales.el b/lisp/init-locales.el index 3b4c9f2774..6e35194b64 100644 --- a/lisp/init-locales.el +++ b/lisp/init-locales.el @@ -1,20 +1,26 @@ -(defun sanityinc/utf8-locale-p (v) - "Return whether locale string V relates to a UTF-8 locale." - (and v (string-match "UTF-8" v))) +;;; init-locales.el --- Configure default locale -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: -(defun sanityinc/locale-is-utf8-p () - "Return t iff the \"locale\" command or environment variables prefer UTF-8." - (or (sanityinc/utf8-locale-p (and (executable-find "locale") (shell-command-to-string "locale"))) - (sanityinc/utf8-locale-p (getenv "LC_ALL")) - (sanityinc/utf8-locale-p (getenv "LC_CTYPE")) - (sanityinc/utf8-locale-p (getenv "LANG")))) +(defun sanityinc/locale-var-encoding (v) + "Return the encoding portion of the locale string V, or nil if missing." + (when v + (save-match-data + (let ((case-fold-search t)) + (when (string-match "\\.\\([^.]*\\)\\'" v) + (intern (downcase (match-string 1 v)))))))) -(when (or window-system (sanityinc/locale-is-utf8-p)) - (set-language-environment 'utf-8) - (setq locale-coding-system 'utf-8) - (set-default-coding-systems 'utf-8) - (set-terminal-coding-system 'utf-8) - (set-selection-coding-system (if (eq system-type 'windows-nt) 'utf-16-le 'utf-8)) - (prefer-coding-system 'utf-8)) +(dolist (varname '("LC_ALL" "LANG" "LC_CTYPE")) + (let ((encoding (sanityinc/locale-var-encoding (getenv varname)))) + (unless (memq encoding '(nil utf8 utf-8)) + (message "Warning: non-UTF8 encoding in environment variable %s may cause interop problems with this Emacs configuration." varname)))) + +(when (fboundp 'set-charset-priority) + (set-charset-priority 'unicode)) +(prefer-coding-system 'utf-8) +(setq locale-coding-system 'utf-8) +(unless (eq system-type 'windows-nt) + (set-selection-coding-system 'utf-8)) (provide 'init-locales) +;;; init-locales.el ends here diff --git a/lisp/init-lua.el b/lisp/init-lua.el new file mode 100644 index 0000000000..60b7f7492d --- /dev/null +++ b/lisp/init-lua.el @@ -0,0 +1,16 @@ +;;; init-lua.el --- Support for Lua programming -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + +(require-package 'lua-mode) + +(setq-default lua-indent-level 2) + +(reformatter-define lua-format + :program "lua-format" + :args '("--indent-width=2" "--no-use-tab") + :lighter "LuaFmt ") + + +(provide 'init-lua) +;;; init-lua.el ends here diff --git a/lisp/init-markdown.el b/lisp/init-markdown.el index abd1c92a5d..aa476cbb80 100644 --- a/lisp/init-markdown.el +++ b/lisp/init-markdown.el @@ -1,6 +1,12 @@ +;;; init-markdown.el --- Markdown support -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + (when (maybe-require-package 'markdown-mode) - (after-load 'whitespace-cleanup-mode - (push 'markdown-mode whitespace-cleanup-mode-ignore-modes))) + (add-auto-mode 'markdown-mode "\\.md\\.html\\'") + (with-eval-after-load 'whitespace-cleanup-mode + (add-to-list 'whitespace-cleanup-mode-ignore-modes 'markdown-mode))) (provide 'init-markdown) +;;; init-markdown.el ends here diff --git a/lisp/init-minibuffer.el b/lisp/init-minibuffer.el new file mode 100644 index 0000000000..9081ea355f --- /dev/null +++ b/lisp/init-minibuffer.el @@ -0,0 +1,45 @@ +;;; init-minibuffer.el --- Config for minibuffer completion -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + + +(when (maybe-require-package 'vertico) + (add-hook 'after-init-hook 'vertico-mode) + + (when (maybe-require-package 'embark) + (with-eval-after-load 'vertico + (define-key vertico-map (kbd "C-c C-o") 'embark-export) + (define-key vertico-map (kbd "C-c C-c") 'embark-act))) + + (when (maybe-require-package 'consult) + (defmacro sanityinc/no-consult-preview (&rest cmds) + `(with-eval-after-load 'consult + (consult-customize ,@cmds :preview-key "M-P"))) + + (sanityinc/no-consult-preview + consult-ripgrep + consult-git-grep consult-grep + consult-bookmark consult-recent-file consult-xref + consult--source-recent-file consult--source-project-recent-file consult--source-bookmark) + + (when (and (executable-find "rg")) + (defun sanityinc/consult-ripgrep-at-point (&optional dir initial) + (interactive (list current-prefix-arg (when-let ((s (symbol-at-point))) + (symbol-name s)))) + (consult-ripgrep dir initial)) + (sanityinc/no-consult-preview sanityinc/consult-ripgrep-at-point) + (global-set-key (kbd "M-?") 'sanityinc/consult-ripgrep-at-point)) + + (global-set-key [remap switch-to-buffer] 'consult-buffer) + (global-set-key [remap switch-to-buffer-other-window] 'consult-buffer-other-window) + (global-set-key [remap switch-to-buffer-other-frame] 'consult-buffer-other-frame) + (global-set-key [remap goto-line] 'consult-goto-line) + + (maybe-require-package 'embark-consult))) + +(when (maybe-require-package 'marginalia) + (add-hook 'after-init-hook 'marginalia-mode)) + + +(provide 'init-minibuffer) +;;; init-minibuffer.el ends here diff --git a/lisp/init-misc.el b/lisp/init-misc.el index 15089a52f4..7f9f17e22e 100644 --- a/lisp/init-misc.el +++ b/lisp/init-misc.el @@ -1,19 +1,57 @@ -;;---------------------------------------------------------------------------- +;;; init-misc.el --- Miscellaneous config -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + + ;; Misc config - yet to be placed in separate files -;;---------------------------------------------------------------------------- -(add-auto-mode 'tcl-mode "Portfile\\'") -(fset 'yes-or-no-p 'y-or-n-p) - -(dolist (hook (if (fboundp 'prog-mode) - '(prog-mode-hook ruby-mode-hook) - '(find-file-hooks))) - (add-hook hook 'goto-address-prog-mode)) -(add-hook 'after-save-hook 'executable-make-buffer-file-executable-if-script-p) + +(add-auto-mode 'tcl-mode "^Portfile\\'") + +(if (boundp 'use-short-answers) + (setq use-short-answers t) + (fset 'yes-or-no-p 'y-or-n-p)) + +(add-hook 'prog-mode-hook 'goto-address-prog-mode) +(add-hook 'conf-mode-hook 'goto-address-prog-mode) (setq goto-address-mail-face 'link) -(setq-default regex-tool-backend 'perl) +(add-hook 'after-save-hook 'executable-make-buffer-file-executable-if-script-p) +(add-hook 'after-save-hook 'sanityinc/set-mode-for-new-scripts) + +(defun sanityinc/set-mode-for-new-scripts () + "Invoke `normal-mode' if this file is a script and in `fundamental-mode'." + (and + (eq major-mode 'fundamental-mode) + (>= (buffer-size) 2) + (save-restriction + (widen) + (string= "#!" (buffer-substring (point-min) (+ 2 (point-min))))) + (normal-mode))) + + +(when (maybe-require-package 'info-colors) + (with-eval-after-load 'info + (add-hook 'Info-selection-hook 'info-colors-fontify-node))) + + +;; Handle the prompt pattern for the 1password command-line interface +(with-eval-after-load 'comint + (setq comint-password-prompt-regexp + (concat + comint-password-prompt-regexp + "\\|^Please enter your password for user .*?:\\s *\\'"))) + + + +(when (maybe-require-package 'regex-tool) + (setq-default regex-tool-backend 'perl)) + +(with-eval-after-load 're-builder + ;; Support a slightly more idiomatic quit binding in re-builder + (define-key reb-mode-map (kbd "C-c C-k") 'reb-quit)) -(add-auto-mode 'conf-mode "Procfile") +(add-auto-mode 'conf-mode "^Procfile\\'") (provide 'init-misc) +;;; init-misc.el ends here diff --git a/lisp/init-mmm.el b/lisp/init-mmm.el index 01e71b0caf..4cd592fe0d 100644 --- a/lisp/init-mmm.el +++ b/lisp/init-mmm.el @@ -1,6 +1,7 @@ -;;---------------------------------------------------------------------------- -;; Multiple major modes -;;---------------------------------------------------------------------------- +;;; init-mmm.el --- Multiple Major Modes support -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + (require-package 'mmm-mode) (require 'mmm-auto) (setq mmm-global-mode 'buffers-with-submode-classes) @@ -8,3 +9,4 @@ (provide 'init-mmm) +;;; init-mmm.el ends here diff --git a/lisp/init-nim.el b/lisp/init-nim.el new file mode 100644 index 0000000000..667db1ffa7 --- /dev/null +++ b/lisp/init-nim.el @@ -0,0 +1,13 @@ +;;; init-nim.el --- Nim programming support -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + +(when (maybe-require-package 'nim-mode) + (when (maybe-require-package 'flycheck-nim) + (with-eval-after-load 'nim-mode + (with-eval-after-load 'flycheck + (require 'flycheck-nim))))) + +(provide 'init-nim) + +;;; init-nim.el ends here diff --git a/lisp/init-nix.el b/lisp/init-nix.el new file mode 100644 index 0000000000..2272f8a662 --- /dev/null +++ b/lisp/init-nix.el @@ -0,0 +1,20 @@ +;;; init-nix.el --- Support for the Nix package manager -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + +(if (maybe-require-package 'nix-ts-mode) + ;; If the TS mode is installed, then the non-TS mode is not, so + ;; nobody will have added an auto-mode-alist entry + (add-to-list 'auto-mode-alist '("\\.nix\\'" . nix-ts-mode)) + (maybe-require-package 'nix-mode)) + +(with-eval-after-load 'eglot + ;; Prefer nixd to nil, and enable in nix-ts-mode too + (add-to-list 'eglot-server-programs + `((nix-mode nix-ts-mode) . ,(eglot-alternatives '("nixd" "nil"))))) + +(maybe-require-package 'nixpkgs-fmt) +(maybe-require-package 'nixfmt) + +(provide 'init-nix) +;;; init-nix.el ends here diff --git a/lisp/init-nxml.el b/lisp/init-nxml.el index 45d73685e8..78b3449eda 100644 --- a/lisp/init-nxml.el +++ b/lisp/init-nxml.el @@ -1,3 +1,7 @@ +;;; init-nxml.el --- Support for editing XML with NXML -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + (add-auto-mode 'nxml-mode (concat "\\." @@ -7,8 +11,6 @@ "\\'")) (setq magic-mode-alist (cons '("<\\?xml " . nxml-mode) magic-mode-alist)) (fset 'xml-mode 'nxml-mode) -(add-hook 'nxml-mode-hook (lambda () - (set (make-local-variable 'ido-use-filename-at-point) nil))) (setq nxml-slash-auto-complete-flag t) @@ -23,20 +25,17 @@ indentation rules." (setq beg (point-min) end (point-max))) ;; Use markers because our changes will move END - (setq beg (set-marker (make-marker) begin) + (setq beg (set-marker (make-marker) beg) end (set-marker (make-marker) end)) (save-excursion (goto-char beg) (while (search-forward-regexp "\>[ \\t]*\<" end t) (backward-char) (insert "\n")) (nxml-mode) - (indent-region begin end))) + (indent-region beg end))) -;;---------------------------------------------------------------------------- + ;; Integration with tidy for html + xml -;;---------------------------------------------------------------------------- -(require-package 'tidy) -(add-hook 'nxml-mode-hook (lambda () (tidy-build-menu nxml-mode-map))) (defun sanityinc/tidy-buffer-xml (beg end) "Run \"tidy -xml\" on the region from BEG to END, or whole buffer." @@ -48,3 +47,4 @@ indentation rules." (provide 'init-nxml) +;;; init-nxml.el ends here diff --git a/lisp/init-ocaml.el b/lisp/init-ocaml.el new file mode 100644 index 0000000000..35115e95cf --- /dev/null +++ b/lisp/init-ocaml.el @@ -0,0 +1,47 @@ +;;; init-ocaml.el --- Support the OCaml language -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + +(when (maybe-require-package 'tuareg) + (with-eval-after-load 'tuareg + (defvar-local tuareg-previous-tuareg-buffer nil + "Buffer from which we jumped to the REPL.") + + (defun sanityinc/tuareg-repl-switch () + (interactive) + (let ((last-tuareg-buf (when (derived-mode-p 'tuareg-mode) + (current-buffer)))) + (tuareg-run-ocaml) + (pop-to-buffer tuareg-interactive-buffer-name) + (when last-tuareg-buf + (setq-local tuareg-previous-tuareg-buffer last-tuareg-buf)))) + + (defun sanityinc/tuareg-repl-switch-back () + (interactive) + (when tuareg-previous-tuareg-buffer + (pop-to-buffer tuareg-previous-tuareg-buffer))) + + (define-key tuareg-mode-map (kbd "C-c C-z") 'sanityinc/tuareg-repl-switch) + (define-key tuareg-interactive-mode-map (kbd "C-c C-z") 'sanityinc/tuareg-repl-switch-back))) + +(when (and (fboundp 'treesit-available-p) (treesit-available-p)) + (require-package 'ocaml-ts-mode) + (with-eval-after-load 'eglot + (add-to-list 'eglot-server-programs '(((ocaml-ts-mode :language-id "ocaml")) "ocamllsp")))) + +(when (maybe-require-package 'dune) + (maybe-require-package 'dune-format)) + +;; Add my own lightweight ocp-indent reformatter, instead of the clunky upstream package +(when (maybe-require-package 'reformatter) + (defcustom ocp-indent-args nil + "Arguments for \"ocp-indent\" invocation.") + + (reformatter-define ocp-indent + :program "ocp-indent" + :args ocp-indent-args + :lighter " OCP")) + + +(provide 'init-ocaml) +;;; init-ocaml.el ends here diff --git a/lisp/init-org.el b/lisp/init-org.el index c704dbfd13..b9bac92361 100644 --- a/lisp/init-org.el +++ b/lisp/init-org.el @@ -1,17 +1,48 @@ -(when (< emacs-major-version 24) - (require-package 'org)) -(require-package 'org-fstree) +;;; init-org.el --- Org-mode config -*- lexical-binding: t -*- +;;; Commentary: + +;; Among settings for many aspects of `org-mode', this code includes +;; an opinionated setup for the Getting Things Done (GTD) system based +;; around the Org Agenda. I have an "inbox.org" file with a header +;; including + +;; #+CATEGORY: Inbox +;; #+FILETAGS: INBOX + +;; and then set this file as `org-default-notes-file'. Captured org +;; items will then go into this file with the file-level tag, and can +;; be refiled to other locations as necessary. + +;; Those other locations are generally other org files, which should +;; be added to `org-agenda-files-list' (along with "inbox.org" org). +;; With that done, there's then an agenda view, accessible via the +;; `org-agenda' command, which gives a convenient overview. +;; `org-todo-keywords' is customised here to provide corresponding +;; TODO states, which should make sense to GTD adherents. + +;;; Code: + (when *is-a-mac* - (maybe-require-package 'grab-mac-link) - (require-package 'org-mac-iCal)) + (maybe-require-package 'grab-mac-link)) + +(maybe-require-package 'org-cliplink) (define-key global-map (kbd "C-c l") 'org-store-link) (define-key global-map (kbd "C-c a") 'org-agenda) +(defvar sanityinc/org-global-prefix-map (make-sparse-keymap) + "A keymap for handy global access to org helpers, particularly clocking.") + +(define-key sanityinc/org-global-prefix-map (kbd "j") 'org-clock-goto) +(define-key sanityinc/org-global-prefix-map (kbd "l") 'org-clock-in-last) +(define-key sanityinc/org-global-prefix-map (kbd "i") 'org-clock-in) +(define-key sanityinc/org-global-prefix-map (kbd "o") 'org-clock-out) +(define-key global-map (kbd "C-c o") sanityinc/org-global-prefix-map) + + ;; Various preferences (setq org-log-done t org-edit-timestamp-down-means-later t - org-archive-mark-done nil org-hide-emphasis-markers t org-catch-invisible-edits 'show org-export-coding-system 'utf-8 @@ -23,40 +54,26 @@ ;; Lots of stuff from http://doc.norang.ca/org-mode.html -(defun sanityinc/grab-ditaa (url jar-name) - "Download URL and extract JAR-NAME as `org-ditaa-jar-path'." - ;; TODO: handle errors - (message "Grabbing " jar-name " for org.") - (let ((zip-temp (make-temp-name "emacs-ditaa"))) - (unwind-protect - (progn - (when (executable-find "unzip") - (url-copy-file url zip-temp) - (shell-command (concat "unzip -p " (shell-quote-argument zip-temp) - " " (shell-quote-argument jar-name) " > " - (shell-quote-argument org-ditaa-jar-path))))) - (when (file-exists-p zip-temp) - (delete-file zip-temp))))) - -(after-load 'ob-ditaa - (unless (and (boundp 'org-ditaa-jar-path) - (file-exists-p org-ditaa-jar-path)) - (let ((jar-name "ditaa0_9.jar") - (url "http://jaist.dl.sourceforge.net/project/ditaa/ditaa/0.9/ditaa0_9.zip")) - (setq org-ditaa-jar-path (expand-file-name jar-name (file-name-directory user-init-file))) - (unless (file-exists-p org-ditaa-jar-path) - (sanityinc/grab-ditaa url jar-name))))) +;; Re-align tags when window shape changes +(with-eval-after-load 'org-agenda + (add-hook 'org-agenda-mode-hook + (lambda () (add-hook 'window-configuration-change-hook 'org-agenda-align-tags nil t)))) + +(maybe-require-package 'writeroom-mode) + (define-minor-mode prose-mode "Set up a buffer for prose editing. This enables or modifies a number of settings so that the experience of editing prose is a little more like that of a typical word processor." - nil " Prose" nil + :init-value nil :lighter " Prose" :keymap nil (if prose-mode (progn + (when (fboundp 'writeroom-mode) + (writeroom-mode 1)) (setq truncate-lines nil) (setq word-wrap t) (setq cursor-type 'bar) @@ -64,20 +81,25 @@ typical word processor." (kill-local-variable 'buffer-face-mode-face)) (buffer-face-mode 1) ;;(delete-selection-mode 1) - (set (make-local-variable 'blink-cursor-interval) 0.6) - (set (make-local-variable 'show-trailing-whitespace) nil) - (flyspell-mode 1) - (when (fboundp 'visual-line-mode) - (visual-line-mode 1))) + (setq-local blink-cursor-interval 0.6) + (setq-local show-trailing-whitespace nil) + (setq-local line-spacing 0.2) + (setq-local electric-pair-mode nil) + (ignore-errors (flyspell-mode 1)) + (visual-line-mode 1)) (kill-local-variable 'truncate-lines) (kill-local-variable 'word-wrap) (kill-local-variable 'cursor-type) + (kill-local-variable 'blink-cursor-interval) (kill-local-variable 'show-trailing-whitespace) + (kill-local-variable 'line-spacing) + (kill-local-variable 'electric-pair-mode) (buffer-face-mode -1) ;; (delete-selection-mode -1) (flyspell-mode -1) - (when (fboundp 'visual-line-mode) - (visual-line-mode -1)))) + (visual-line-mode -1) + (when (fboundp 'writeroom-mode) + (writeroom-mode 0)))) ;;(add-hook 'org-mode-hook 'buffer-face-mode) @@ -89,7 +111,7 @@ typical word processor." (global-set-key (kbd "C-c c") 'org-capture) (setq org-capture-templates - `(("t" "todo" entry (file "") ; "" => org-default-notes-file + `(("t" "todo" entry (file "") ; "" => `org-default-notes-file' "* NEXT %?\n%U\n" :clock-resume t) ("n" "note" entry (file "") "* %? :NOTE:\n%U\n%a\n" :clock-resume t) @@ -101,12 +123,14 @@ typical word processor." (setq org-refile-use-cache nil) -; Targets include this file and any file contributing to the agenda - up to 5 levels deep +;; Targets include this file and any file contributing to the agenda - up to 5 levels deep (setq org-refile-targets '((nil :maxlevel . 5) (org-agenda-files :maxlevel . 5))) -(after-load 'org-agenda +(with-eval-after-load 'org-agenda (add-to-list 'org-agenda-after-show-hook 'org-show-entry)) +(advice-add 'org-refile :after (lambda (&rest _) (org-save-all-org-buffers))) + ;; Exclude DONE state tasks from refile targets (defun sanityinc/verify-refile-target () "Exclude todo keywords with a done state from refile targets." @@ -114,11 +138,17 @@ typical word processor." (setq org-refile-target-verify-function 'sanityinc/verify-refile-target) (defun sanityinc/org-refile-anywhere (&optional goto default-buffer rfloc msg) - "A version of `org-refile' which suppresses `org-refile-target-verify-function'." + "A version of `org-refile' which allows refiling to any subtree." (interactive "P") (let ((org-refile-target-verify-function)) (org-refile goto default-buffer rfloc msg))) +(defun sanityinc/org-agenda-refile-anywhere (&optional goto rfloc no-update) + "A version of `org-agenda-refile' which allows refiling to any subtree." + (interactive "P") + (let ((org-refile-target-verify-function)) + (org-agenda-refile goto rfloc no-update))) + ;; Targets start with the file name - allows creating level 1 tasks ;;(setq org-refile-use-outline-path (quote file)) (setq org-refile-use-outline-path t) @@ -177,11 +207,14 @@ typical word processor." (org-agenda-tags-todo-honor-ignore-options t) (org-tags-match-list-sublevels t) (org-agenda-todo-ignore-scheduled 'future))) - (tags-todo "-INBOX/NEXT" + (tags-todo "-INBOX" ((org-agenda-overriding-header "Next Actions") (org-agenda-tags-todo-honor-ignore-options t) (org-agenda-todo-ignore-scheduled 'future) - ;; TODO: skip if a parent is WAITING or HOLD + (org-agenda-skip-function + '(lambda () + (or (org-agenda-skip-subtree-if 'todo '("HOLD" "WAITING")) + (org-agenda-skip-entry-if 'nottodo '("NEXT"))))) (org-tags-match-list-sublevels t) (org-agenda-sorting-strategy '(todo-state-down effort-up category-keep)))) @@ -194,7 +227,6 @@ typical word processor." ((org-agenda-overriding-header "Orphaned Tasks") (org-agenda-tags-todo-honor-ignore-options t) (org-agenda-todo-ignore-scheduled 'future) - ;; TODO: skip if a parent is a project (org-agenda-skip-function '(lambda () (or (org-agenda-skip-subtree-if 'todo '("PROJECT" "HOLD" "WAITING" "DELEGATED")) @@ -214,9 +246,12 @@ typical word processor." (org-agenda-todo-ignore-scheduled 'future) (org-agenda-sorting-strategy '(category-keep)))) - (tags-todo "-INBOX/HOLD" + (tags-todo "-INBOX" ((org-agenda-overriding-header "On Hold") - ;; TODO: skip if a parent is WAITING or HOLD + (org-agenda-skip-function + '(lambda () + (or (org-agenda-skip-subtree-if 'todo '("WAITING")) + (org-agenda-skip-entry-if 'nottodo '("HOLD"))))) (org-tags-match-list-sublevels nil) (org-agenda-sorting-strategy '(category-keep)))) @@ -232,7 +267,7 @@ typical word processor." ;;; Org clock ;; Save the running clock and all clock history when exiting Emacs, load it on startup -(after-load 'org +(with-eval-after-load 'org (org-clock-persistence-insinuate)) (setq org-clock-persist t) (setq org-clock-in-resume t) @@ -261,7 +296,7 @@ typical word processor." (add-hook 'org-clock-out-hook 'sanityinc/hide-org-clock-from-header-line) (add-hook 'org-clock-cancel-hook 'sanityinc/hide-org-clock-from-header-line) -(after-load 'org-clock +(with-eval-after-load 'org-clock (define-key org-clock-mode-line-map [header-line mouse-2] 'org-clock-goto) (define-key org-clock-mode-line-map [header-line mouse-1] 'org-clock-menu)) @@ -277,18 +312,6 @@ typical word processor." -;; Remove empty LOGBOOK drawers on clock out -(defun sanityinc/remove-empty-drawer-on-clock-out () - (interactive) - (save-excursion - (beginning-of-line 0) - (org-remove-empty-drawer-at "LOGBOOK" (point)))) - -(after-load 'org-clock - (add-hook 'org-clock-out-hook 'sanityinc/remove-empty-drawer-on-clock-out 'append)) - - - ;; TODO: warn about inconsistent items, e.g. TODO inside non-PROJECT ;; TODO: nested projects! @@ -305,7 +328,7 @@ typical word processor." (require-package 'org-pomodoro) (setq org-pomodoro-keep-killed-pomodoro-time t) -(after-load 'org-agenda +(with-eval-after-load 'org-agenda (define-key org-agenda-mode-map (kbd "P") 'org-pomodoro)) @@ -330,31 +353,37 @@ typical word processor." ;; (insert (match-string 0)))))) -(after-load 'org +(with-eval-after-load 'org (define-key org-mode-map (kbd "C-M-") 'org-up-element) (when *is-a-mac* (define-key org-mode-map (kbd "M-h") nil) - (define-key org-mode-map (kbd "C-c g") 'org-mac-grab-link))) + (define-key org-mode-map (kbd "C-c g") 'grab-mac-link))) -(after-load 'org +(with-eval-after-load 'org (org-babel-do-load-languages 'org-babel-load-languages - `((R . t) - (ditaa . t) - (dot . t) - (emacs-lisp . t) - (gnuplot . t) - (haskell . nil) - (latex . t) - (ledger . t) - (ocaml . nil) - (octave . t) - (python . t) - (ruby . t) - (screen . nil) - (,(if (locate-library "ob-sh") 'sh 'shell) . t) - (sql . nil) - (sqlite . t)))) + (seq-filter + (lambda (pair) + (locate-library (concat "ob-" (symbol-name (car pair))))) + '((R . t) + (ditaa . t) + (dot . t) + (emacs-lisp . t) + (gnuplot . t) + (haskell . nil) + (latex . t) + (ledger . t) + (ocaml . nil) + (octave . t) + (plantuml . t) + (python . t) + (ruby . t) + (screen . nil) + (sh . t) ;; obsolete + (shell . t) + (sql . t) + (sqlite . t))))) (provide 'init-org) +;;; init-org.el ends here diff --git a/lisp/init-osx-keys.el b/lisp/init-osx-keys.el index 9cea45056f..e6bec59b19 100644 --- a/lisp/init-osx-keys.el +++ b/lisp/init-osx-keys.el @@ -1,7 +1,10 @@ +;;; init-osx-keys.el --- Configure keys specific to MacOS -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + (when *is-a-mac* (setq mac-command-modifier 'meta) (setq mac-option-modifier 'none) - (setq-default default-input-method "MacOSX") ;; Make mouse wheel / trackpad scrolling less jerky (setq mouse-wheel-scroll-amount '(1 ((shift) . 5) @@ -12,10 +15,11 @@ (global-set-key (kbd "M-`") 'ns-next-frame) (global-set-key (kbd "M-h") 'ns-do-hide-emacs) (global-set-key (kbd "M-˙") 'ns-do-hide-others) - (after-load 'nxml-mode + (with-eval-after-load 'nxml-mode (define-key nxml-mode-map (kbd "M-h") nil)) (global-set-key (kbd "M-ˍ") 'ns-do-hide-others) ;; what describe-key reports for cmd-option-h ) (provide 'init-osx-keys) +;;; init-osx-keys.el ends here diff --git a/lisp/init-paredit.el b/lisp/init-paredit.el index 80134e77d1..4c2712b378 100644 --- a/lisp/init-paredit.el +++ b/lisp/init-paredit.el @@ -1,30 +1,31 @@ +;;; init-paredit.el --- Configure paredit structured editing -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + (require-package 'paredit) -(autoload 'enable-paredit-mode "paredit") -(defun maybe-map-paredit-newline () - (unless (or (memq major-mode '(inferior-emacs-lisp-mode cider-repl-mode)) + +(defun sanityinc/maybe-map-paredit-newline () + (unless (or (derived-mode-p 'inferior-emacs-lisp-mode 'cider-repl-mode) (minibufferp)) (local-set-key (kbd "RET") 'paredit-newline))) -(add-hook 'paredit-mode-hook 'maybe-map-paredit-newline) +(add-hook 'paredit-mode-hook 'sanityinc/maybe-map-paredit-newline) -(after-load 'paredit +(with-eval-after-load 'paredit (diminish 'paredit-mode " Par") ;; Suppress certain paredit keybindings to avoid clashes, including ;; my global binding of M-? - (dolist (binding '("C-" "C-" "C-M-" "C-M-" "M-s" "M-?")) - (define-key paredit-mode-map (read-kbd-macro binding) nil))) - + (dolist (binding '("RET" "C-" "C-" "C-M-" "C-M-" "M-s" "M-?")) + (define-key paredit-mode-map (read-kbd-macro binding) nil)) + (define-key paredit-mode-map (kbd "M-") 'paredit-splice-sexp-killing-backward)) -;; Compatibility with other modes - -(suspend-mode-during-cua-rect-selection 'paredit-mode) ;; Use paredit in the minibuffer ;; TODO: break out into separate package ;; http://emacsredux.com/blog/2013/04/18/evaluate-emacs-lisp-in-the-minibuffer/ -(add-hook 'minibuffer-setup-hook 'conditionally-enable-paredit-mode) +(add-hook 'minibuffer-setup-hook 'sanityinc/conditionally-enable-paredit-mode) (defvar paredit-minibuffer-commands '(eval-expression pp-eval-expression @@ -33,19 +34,26 @@ ibuffer-do-view-and-eval) "Interactive commands for which paredit should be enabled in the minibuffer.") -(defun conditionally-enable-paredit-mode () +(defun sanityinc/conditionally-enable-paredit-mode () "Enable paredit during lisp-related minibuffer commands." - (if (memq this-command paredit-minibuffer-commands) - (enable-paredit-mode))) + (when (memq this-command paredit-minibuffer-commands) + (enable-paredit-mode))) + +(add-hook 'sanityinc/lispy-modes-hook 'enable-paredit-mode) + +(when (maybe-require-package 'puni) + ;;(add-hook 'prog-mode-hook 'puni-mode) + (add-hook 'sanityinc/lispy-modes-hook (lambda () (puni-mode -1))) + (with-eval-after-load 'puni + (define-key puni-mode-map (kbd "M-(") 'puni-wrap-round) + (define-key puni-mode-map (kbd "C-(") 'puni-slurp-backward) + (define-key puni-mode-map (kbd "C-)") 'puni-slurp-forward) + (define-key puni-mode-map (kbd "C-}") 'puni-barf-forward) + (define-key puni-mode-map (kbd "C-{") 'puni-barf-backward) + (define-key puni-mode-map (kbd "M-") 'puni-splice-killing-backward) + (define-key puni-mode-map (kbd "C-w") nil))) -;; ---------------------------------------------------------------------------- -;; Enable some handy paredit functions in all prog modes -;; ---------------------------------------------------------------------------- -(require-package 'paredit-everywhere) -(after-load 'paredit-everywhere - (define-key paredit-everywhere-mode-map (kbd "M-s") nil)) -(add-hook 'prog-mode-hook 'paredit-everywhere-mode) -(add-hook 'css-mode-hook 'paredit-everywhere-mode) (provide 'init-paredit) +;;; init-paredit.el ends here diff --git a/lisp/init-php.el b/lisp/init-php.el index a9534615ed..e0aceef697 100644 --- a/lisp/init-php.el +++ b/lisp/init-php.el @@ -1,4 +1,9 @@ +;;; init-php.el --- Support for working with PHP -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + (when (maybe-require-package 'php-mode) (maybe-require-package 'smarty-mode)) (provide 'init-php) +;;; init-php.el ends here diff --git a/lisp/init-projectile.el b/lisp/init-projectile.el index 3e48c4bc50..61c8a9d6fe 100644 --- a/lisp/init-projectile.el +++ b/lisp/init-projectile.el @@ -1,18 +1,21 @@ -(when (maybe-require-package 'projectile) - (add-hook 'after-init-hook 'projectile-global-mode) +;;; init-projectile.el --- Use Projectile for navigation within projects -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: - ;; The following code means you get a menu if you hit "C-c p" and wait - (after-load 'guide-key - (add-to-list 'guide-key/guide-key-sequence "C-c p")) +(when (maybe-require-package 'projectile) + (add-hook 'after-init-hook 'projectile-mode) ;; Shorter modeline - (after-load 'projectile - (setq-default - projectile-mode-line - '(:eval - (if (file-remote-p default-directory) - " Pr" - (format " Pr[%s]" (projectile-project-name))))))) + (setq-default projectile-mode-line-prefix " Proj") + + (when (executable-find "rg") + (setq-default projectile-generic-command "rg --files --hidden -0")) + + (with-eval-after-load 'projectile + (define-key projectile-mode-map (kbd "C-c p") 'projectile-command-map)) + + (maybe-require-package 'ibuffer-projectile)) (provide 'init-projectile) +;;; init-projectile.el ends here diff --git a/lisp/init-purescript.el b/lisp/init-purescript.el new file mode 100644 index 0000000000..cbe3f7f0c3 --- /dev/null +++ b/lisp/init-purescript.el @@ -0,0 +1,34 @@ +;;; init-purescript.el --- Support the Purescript language -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + +(when (maybe-require-package 'purescript-mode) + (add-hook 'purescript-mode-hook 'turn-on-purescript-indentation) + + (add-hook 'purescript-mode-hook + (lambda () + (add-hook 'before-save-hook 'purescript-sort-imports nil t))) + + (add-hook 'purescript-mode-hook (apply-partially 'prettify-symbols-mode -1)) + + (with-eval-after-load 'purescript-mode + (define-key purescript-mode-map (kbd "C-o") 'open-line)) + + (when (maybe-require-package 'reformatter) + (reformatter-define purty + :program "purty" :lighter " purty")) + + (when (maybe-require-package 'psci) + (add-hook 'purescript-mode-hook 'inferior-psci-mode)) + + (when (maybe-require-package 'add-node-modules-path) + (with-eval-after-load 'purescript-mode + (add-hook 'purescript-mode-hook 'add-node-modules-path)) + (with-eval-after-load 'psci + (advice-add 'psci :around (lambda (oldfun &rest args) + (let ((psci/purs-path (or (executable-find "purs") + psci/purs-path))) + (apply oldfun args))))))) + +(provide 'init-purescript) +;;; init-purescript.el ends here diff --git a/lisp/init-python-mode.el b/lisp/init-python-mode.el deleted file mode 100644 index cce97603e8..0000000000 --- a/lisp/init-python-mode.el +++ /dev/null @@ -1,18 +0,0 @@ -(setq auto-mode-alist - (append '(("SConstruct\\'" . python-mode) - ("SConscript\\'" . python-mode)) - auto-mode-alist)) - -(require-package 'pip-requirements) - -(when (maybe-require-package 'anaconda-mode) - (after-load 'python - (add-hook 'python-mode-hook 'anaconda-mode) - (add-hook 'python-mode-hook 'anaconda-eldoc-mode)) - (when (maybe-require-package 'company-anaconda) - (after-load 'company - (add-hook 'python-mode-hook - (lambda () (sanityinc/local-push-company-backend 'company-anaconda)))))) - - -(provide 'init-python-mode) diff --git a/lisp/init-python.el b/lisp/init-python.el new file mode 100644 index 0000000000..c52cd6352d --- /dev/null +++ b/lisp/init-python.el @@ -0,0 +1,34 @@ +;;; init-python.el --- Python editing -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + +;; I use nix + direnv instead of virtualenv/pyenv/pyvenv, and it is an +;; approach which extends to other languages too. I recorded a +;; screencast about this: https://www.youtube.com/watch?v=TbIHRHy7_JM + + +(setq auto-mode-alist + (append '(("SConstruct\\'" . python-mode) + ("SConscript\\'" . python-mode)) + auto-mode-alist)) + +(setq python-shell-interpreter "python3") + +(require-package 'pip-requirements) + +(when (maybe-require-package 'flymake-ruff) + (defun sanityinc/flymake-ruff-maybe-enable () + (when (executable-find "ruff") + (flymake-ruff-load))) + (add-hook 'python-mode-hook 'sanityinc/flymake-ruff-maybe-enable)) + +(maybe-require-package 'ruff-format) + +(when (maybe-require-package 'toml-mode) + (add-to-list 'auto-mode-alist '("poetry\\.lock\\'" . toml-mode))) + +(when (maybe-require-package 'reformatter) + (reformatter-define black :program "black" :args '("-"))) + +(provide 'init-python) +;;; init-python.el ends here diff --git a/lisp/init-rails.el b/lisp/init-rails.el index 6c4c338abd..0ac624485f 100644 --- a/lisp/init-rails.el +++ b/lisp/init-rails.el @@ -1,18 +1,11 @@ -(require-package 'rinari) -(after-load 'rinari - (diminish 'rinari-minor-mode "Rin")) -(global-rinari-mode) - -(defun update-rails-ctags () - (interactive) - (let ((default-directory (or (rinari-root) default-directory))) - (shell-command (concat "ctags -a -e -f " rinari-tags-file-name " --tag-relative -R app lib vendor test")))) +;;; init-rails.el --- Ruby on Rails support -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: (when (maybe-require-package 'projectile-rails) - (after-load 'projectile - (add-hook 'projectile-mode-hook 'projectile-rails-on) - (after-load 'guide-key - (add-to-list 'guide-key/guide-key-sequence "C-c r")))) + (add-hook 'projectile-mode-hook + (lambda () (projectile-rails-global-mode projectile-mode)))) (provide 'init-rails) +;;; init-rails.el ends here diff --git a/lisp/init-recentf.el b/lisp/init-recentf.el index d778ebd791..983fc89c03 100644 --- a/lisp/init-recentf.el +++ b/lisp/init-recentf.el @@ -1,7 +1,12 @@ -(recentf-mode 1) +;;; init-recentf.el --- Settings for tracking recent files -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + +(add-hook 'after-init-hook 'recentf-mode) (setq-default recentf-max-saved-items 1000 - recentf-exclude '("/tmp/" "/ssh:")) + recentf-exclude `("/tmp/" "/ssh:" ,(concat package-user-dir "/.*-autoloads\\.el\\'"))) (provide 'init-recentf) +;;; init-recentf.el ends here diff --git a/lisp/init-ruby-mode.el b/lisp/init-ruby-mode.el deleted file mode 100644 index 0b41113b68..0000000000 --- a/lisp/init-ruby-mode.el +++ /dev/null @@ -1,141 +0,0 @@ -;;; Basic ruby setup -(require-package 'ruby-mode) -(require-package 'ruby-hash-syntax) - -(add-auto-mode 'ruby-mode - "Rakefile\\'" "\\.rake\\'" "\\.rxml\\'" - "\\.rjs\\'" "\\.irbrc\\'" "\\.pryrc\\'" "\\.builder\\'" "\\.ru\\'" - "\\.gemspec\\'" "Gemfile\\'" "Kirkfile\\'") -(add-auto-mode 'conf-mode "Gemfile\\.lock\\'") - -(setq ruby-use-encoding-map nil) - -(after-load 'ruby-mode - (define-key ruby-mode-map (kbd "TAB") 'indent-for-tab-command) - - ;; Stupidly the non-bundled ruby-mode isn't a derived mode of - ;; prog-mode: we run the latter's hooks anyway in that case. - (add-hook 'ruby-mode-hook - (lambda () - (unless (derived-mode-p 'prog-mode) - (run-hooks 'prog-mode-hook))))) - -(add-hook 'ruby-mode-hook 'subword-mode) - -(after-load 'page-break-lines - (push 'ruby-mode page-break-lines-modes)) - -(require-package 'rspec-mode) - - -;;; Inferior ruby -(require-package 'inf-ruby) - - - -;;; Ruby compilation -(require-package 'ruby-compilation) - -(after-load 'ruby-mode - (let ((m ruby-mode-map)) - (define-key m [S-f7] 'ruby-compilation-this-buffer) - (define-key m [f7] 'ruby-compilation-this-test))) - -(after-load 'ruby-compilation - (defalias 'rake 'ruby-compilation-rake)) - - - -;;; Robe -(require-package 'robe) -(after-load 'ruby-mode - (add-hook 'ruby-mode-hook 'robe-mode)) -(after-load 'company - (dolist (hook '(ruby-mode-hook inf-ruby-mode-hook html-erb-mode-hook haml-mode)) - (add-hook hook - (lambda () (sanityinc/local-push-company-backend 'company-robe))))) - - - -;; Customise highlight-symbol to not highlight do/end/class/def etc. -(defun sanityinc/suppress-ruby-mode-keyword-highlights () - "Suppress highlight-symbol for do/end etc." - (set (make-local-variable 'highlight-symbol-ignore-list) - (list (concat "\\_<" (regexp-opt '("do" "end")) "\\_>")))) -(add-hook 'ruby-mode-hook 'sanityinc/suppress-ruby-mode-keyword-highlights) - - - -;;; ri support -(require-package 'yari) -(defalias 'ri 'yari) - - - -(require-package 'goto-gem) - - -(require-package 'bundler) - - -;;; YAML - -(when (maybe-require-package 'yaml-mode) - (add-auto-mode 'yaml-mode "\\.yml\\.erb\\'")) - - - -;;; ERB -(require-package 'mmm-mode) -(defun sanityinc/ensure-mmm-erb-loaded () - (require 'mmm-erb)) - -(require 'derived) - -(defun sanityinc/set-up-mode-for-erb (mode) - (add-hook (derived-mode-hook-name mode) 'sanityinc/ensure-mmm-erb-loaded) - (mmm-add-mode-ext-class mode "\\.erb\\'" 'erb)) - -(let ((html-erb-modes '(html-mode html-erb-mode nxml-mode))) - (dolist (mode html-erb-modes) - (sanityinc/set-up-mode-for-erb mode) - (mmm-add-mode-ext-class mode "\\.r?html\\(\\.erb\\)?\\'" 'html-js) - (mmm-add-mode-ext-class mode "\\.r?html\\(\\.erb\\)?\\'" 'html-css))) - -(mapc 'sanityinc/set-up-mode-for-erb - '(coffee-mode js-mode js2-mode js3-mode markdown-mode textile-mode)) - -(mmm-add-mode-ext-class 'html-erb-mode "\\.jst\\.ejs\\'" 'ejs) - -(add-auto-mode 'html-erb-mode "\\.rhtml\\'" "\\.html\\.erb\\'") -(add-to-list 'auto-mode-alist '("\\.jst\\.ejs\\'" . html-erb-mode)) -(mmm-add-mode-ext-class 'yaml-mode "\\.yaml\\(\\.erb\\)?\\'" 'erb) - -(dolist (mode (list 'js-mode 'js2-mode 'js3-mode)) - (mmm-add-mode-ext-class mode "\\.js\\.erb\\'" 'erb)) - - -;;---------------------------------------------------------------------------- -;; Ruby - my convention for heredocs containing SQL -;;---------------------------------------------------------------------------- - -;; Needs to run after rinari to avoid clobbering font-lock-keywords? - -;; (require-package 'mmm-mode) -;; (eval-after-load 'mmm-mode -;; '(progn -;; (mmm-add-classes -;; '((ruby-heredoc-sql -;; :submode sql-mode -;; :front "<<-?[\'\"]?\\(end_sql\\)[\'\"]?" -;; :save-matches 1 -;; :front-offset (end-of-line 1) -;; :back "^[ \t]*~1$" -;; :delimiter-mode nil))) -;; (mmm-add-mode-ext-class 'ruby-mode "\\.rb\\'" 'ruby-heredoc-sql))) - -;(add-to-list 'mmm-set-file-name-for-modes 'ruby-mode) - - - -(provide 'init-ruby-mode) diff --git a/lisp/init-ruby.el b/lisp/init-ruby.el new file mode 100644 index 0000000000..f7b5dfaaec --- /dev/null +++ b/lisp/init-ruby.el @@ -0,0 +1,129 @@ +;;; init-ruby.el --- Support for the Ruby language -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + +;;; Basic ruby setup +(require-package 'ruby-hash-syntax) + +(add-auto-mode 'ruby-mode + "\\.rxml\\'" + "\\.rjs\\'" "\\.irbrc\\'" "\\.pryrc\\'" "\\.builder\\'" + "\\.gemspec\\'" "Kirkfile\\'") +(add-auto-mode 'conf-mode "Gemfile\\.lock\\'") + +(setq-default + ruby-use-encoding-map nil + ruby-insert-encoding-magic-comment nil) + +(add-hook 'ruby-mode-hook 'subword-mode) + +(with-eval-after-load 'page-break-lines + (add-to-list 'page-break-lines-modes 'ruby-mode)) + +(require-package 'rspec-mode) + + +(define-derived-mode brewfile-mode ruby-mode "Brewfile" + "A major mode for Brewfiles, used by homebrew-bundle on MacOS.") + +(add-auto-mode 'brewfile-mode "Brewfile\\'") + + +;;; Inferior ruby +(require-package 'inf-ruby) +(with-eval-after-load 'inf-ruby + (defun sanityinc/ruby-load-file (&optional choose-file) + (interactive "P") + (if (or choose-file (not buffer-file-name)) + (call-interactively 'ruby-load-file) + (save-some-buffers) + (ruby-load-file buffer-file-name))) + (define-key inf-ruby-minor-mode-map [remap ruby-load-file] 'sanityinc/ruby-load-file)) + + + +;;; Ruby compilation +(require-package 'ruby-compilation) + +(with-eval-after-load 'ruby-mode + (define-key ruby-mode-map [S-f7] 'ruby-compilation-this-buffer) + (define-key ruby-mode-map [f7] 'ruby-compilation-this-test)) + +(with-eval-after-load 'ruby-compilation + (defalias 'rake 'ruby-compilation-rake)) + + + +;;; Robe +(when (maybe-require-package 'robe) + (with-eval-after-load 'ruby-mode + (add-hook 'ruby-mode-hook 'robe-mode))) + + + +;;; ri support +(require-package 'yari) +(defalias 'ri 'yari) + + + +(require-package 'bundler) + + +(when (maybe-require-package 'yard-mode) + (add-hook 'ruby-mode-hook 'yard-mode) + (with-eval-after-load 'yard-mode + (diminish 'yard-mode))) + + +;;; ERB +(require-package 'mmm-mode) + +(require 'derived) + +(defun sanityinc/set-up-mode-for-erb (mode) + (add-hook (derived-mode-hook-name mode) (lambda () (require 'mmm-erb))) + (mmm-add-mode-ext-class mode "\\.erb\\'" 'erb)) + +(dolist (mode '(html-mode html-erb-mode nxml-mode)) + (sanityinc/set-up-mode-for-erb mode) + (mmm-add-mode-ext-class mode "\\.r?html\\(\\.erb\\)?\\'" 'html-js) + (mmm-add-mode-ext-class mode "\\.r?html\\(\\.erb\\)?\\'" 'html-css)) + +(mapc 'sanityinc/set-up-mode-for-erb + '(coffee-mode js-mode js2-mode js3-mode markdown-mode textile-mode)) + +(mmm-add-mode-ext-class 'html-erb-mode "\\.jst\\.ejs\\'" 'ejs) + +(add-auto-mode 'html-erb-mode "\\.rhtml\\'" "\\.html\\.erb\\'") +(add-to-list 'auto-mode-alist '("\\.jst\\.ejs\\'" . html-erb-mode)) + +(mmm-add-mode-ext-class 'yaml-mode "\\.yaml\\(\\.erb\\)?\\'" 'erb) +(sanityinc/set-up-mode-for-erb 'yaml-mode) + +(dolist (mode (list 'js-mode 'js2-mode 'js3-mode)) + (mmm-add-mode-ext-class mode "\\.js\\.erb\\'" 'erb)) + + + +;; Ruby - my convention for heredocs containing SQL + +;; (require-package 'mmm-mode) +;; (eval-after-load 'mmm-mode +;; '(progn +;; (mmm-add-classes +;; '((ruby-heredoc-sql +;; :submode sql-mode +;; :front "<<-?[\'\"]?\\(end_sql\\)[\'\"]?" +;; :save-matches 1 +;; :front-offset (end-of-line 1) +;; :back "^[ \t]*~1$" +;; :delimiter-mode nil))) +;; (mmm-add-mode-ext-class 'ruby-mode "\\.rb\\'" 'ruby-heredoc-sql))) + +;; (add-to-list 'mmm-set-file-name-for-modes 'ruby-mode) + + + +(provide 'init-ruby) +;;; init-ruby.el ends here diff --git a/lisp/init-rust.el b/lisp/init-rust.el new file mode 100644 index 0000000000..ddc8681489 --- /dev/null +++ b/lisp/init-rust.el @@ -0,0 +1,11 @@ +;;; init-rust.el --- Support for the Rust language -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + +(when (maybe-require-package 'rust-mode) + (when (maybe-require-package 'flycheck-rust) + (with-eval-after-load 'rust-mode + (add-hook 'flycheck-mode-hook #'flycheck-rust-setup)))) + +(provide 'init-rust) +;;; init-rust.el ends here diff --git a/lisp/init-sessions.el b/lisp/init-sessions.el index d05b570ba8..1ae7fd59ff 100644 --- a/lisp/init-sessions.el +++ b/lisp/init-sessions.el @@ -1,77 +1,74 @@ +;;; init-sessions.el --- Save and restore editor sessions between restarts -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + ;; save a list of open files in ~/.emacs.d/.emacs.desktop (setq desktop-path (list user-emacs-directory) desktop-auto-save-timeout 600) (desktop-save-mode 1) -(defadvice desktop-read (around time-restore activate) - (let ((start-time (current-time))) - (prog1 - ad-do-it - (message "Desktop restored in %.2fms" - (sanityinc/time-subtract-millis (current-time) - start-time))))) +(defun sanityinc/desktop-time-restore (orig &rest args) + (let ((start-time (current-time))) + (prog1 + (apply orig args) + (message "Desktop restored in %.2fms" + (sanityinc/time-subtract-millis (current-time) + start-time))))) +(advice-add 'desktop-read :around 'sanityinc/desktop-time-restore) -(defadvice desktop-create-buffer (around time-create activate) - (let ((start-time (current-time)) - (filename (ad-get-arg 1))) +(defun sanityinc/desktop-time-buffer-create (orig ver filename &rest args) + (let ((start-time (current-time))) (prog1 - ad-do-it + (apply orig ver filename args) (message "Desktop: %.2fms to restore %s" (sanityinc/time-subtract-millis (current-time) start-time) (when filename - (abbreviate-file-name filename)))))) + (abbreviate-file-name filename)))))) +(advice-add 'desktop-create-buffer :around 'sanityinc/desktop-time-buffer-create) -;;---------------------------------------------------------------------------- + ;; Restore histories and registers after saving -;;---------------------------------------------------------------------------- + (setq-default history-length 1000) -(savehist-mode t) +(add-hook 'after-init-hook 'savehist-mode) (require-package 'session) -(setq session-save-file (expand-file-name ".session" user-emacs-directory)) +(setq session-save-file (locate-user-emacs-file ".session")) (setq session-name-disable-regexp "\\(?:\\`'/tmp\\|\\.git/[A-Z_]+\\'\\)") +(setq session-save-file-coding-system 'utf-8) + (add-hook 'after-init-hook 'session-initialize) ;; save a bunch of variables to the desktop file ;; for lists specify the len of the maximal saved data also (setq desktop-globals-to-save - (append '((comint-input-ring . 50) - (compile-history . 30) - desktop-missing-file-warning - (dired-regexp-history . 20) - (extended-command-history . 30) - (face-name-history . 20) - (file-name-history . 100) - (grep-find-history . 30) - (grep-history . 30) - (ido-buffer-history . 100) - (ido-last-directory-list . 100) - (ido-work-directory-list . 100) - (ido-work-file-list . 100) - (ivy-history . 100) - (magit-read-rev-history . 50) - (minibuffer-history . 50) - (org-clock-history . 50) - (org-refile-history . 50) - (org-tags-history . 50) - (query-replace-history . 60) - (read-expression-history . 60) - (regexp-history . 60) - (regexp-search-ring . 20) - register-alist - (search-ring . 20) - (shell-command-history . 50) - tags-file-name - tags-table-list))) - -(when (eval-when-compile (and (>= emacs-major-version 24) - (version< emacs-version "24.3.50") - )) - (unless (boundp 'desktop-restore-frames) - (require-package 'frame-restore) - (frame-restore))) + '((comint-input-ring . 50) + (compile-history . 30) + desktop-missing-file-warning + (dired-regexp-history . 20) + (extended-command-history . 30) + (face-name-history . 20) + (file-name-history . 100) + (grep-find-history . 30) + (grep-history . 30) + (ivy-history . 100) + (magit-revision-history . 50) + (minibuffer-history . 50) + (org-clock-history . 50) + (org-refile-history . 50) + (org-tags-history . 50) + (query-replace-history . 60) + (read-expression-history . 60) + (regexp-history . 60) + (regexp-search-ring . 20) + register-alist + (search-ring . 20) + (shell-command-history . 50) + tags-file-name + tags-table-list)) (provide 'init-sessions) +;;; init-sessions.el ends here diff --git a/lisp/init-site-lisp.el b/lisp/init-site-lisp.el index 81de0a0814..8b190abbe4 100644 --- a/lisp/init-site-lisp.el +++ b/lisp/init-site-lisp.el @@ -1,19 +1,25 @@ +;;; init-site-lisp.el --- Support elisp manually installed in the site-lisp dir -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + ;;; Set load path -(eval-when-compile (require 'cl)) +(require 'cl-lib) + (defun sanityinc/add-subdirs-to-load-path (parent-dir) - "Adds every non-hidden subdir of PARENT-DIR to `load-path'." - (let* ((default-directory parent-dir)) - (progn - (setq load-path - (append - (remove-if-not - (lambda (dir) (file-directory-p dir)) - (directory-files (expand-file-name parent-dir) t "^[^\\.]")) - load-path))))) - -(sanityinc/add-subdirs-to-load-path - (expand-file-name "site-lisp/" user-emacs-directory)) + "Add every non-hidden subdir of PARENT-DIR to `load-path'." + (let ((default-directory parent-dir)) + (setq load-path + (append + (cl-remove-if-not + #'file-directory-p + (directory-files (expand-file-name parent-dir) t "^[^\\.]")) + load-path)))) + +;; Add both site-lisp and its immediate subdirs to `load-path' +(let ((site-lisp-dir (expand-file-name "site-lisp/" user-emacs-directory))) + (push site-lisp-dir load-path) + (sanityinc/add-subdirs-to-load-path site-lisp-dir)) ;;; Utilities for grabbing upstream libs @@ -44,13 +50,5 @@ source file under ~/.emacs.d/site-lisp/name/" (and f (string-prefix-p (file-name-as-directory (site-lisp-dir-for name)) f)))) - -;; Download these upstream libs - -(unless (> emacs-major-version 23) - (ensure-lib-from-url - 'package - "http://repo.or.cz/w/emacs.git/blob_plain/ba08b24186711eaeb3748f3d1f23e2c2d9ed0d09:/lisp/emacs-lisp/package.el")) - - (provide 'init-site-lisp) +;;; init-site-lisp.el ends here diff --git a/lisp/init-slime.el b/lisp/init-slime.el deleted file mode 100644 index fa24d18ba2..0000000000 --- a/lisp/init-slime.el +++ /dev/null @@ -1,48 +0,0 @@ -(require-package 'slime) -;; package.el compiles the contrib subdir, but the compilation order -;; causes problems, so we remove the .elc files there. See -;; http://lists.common-lisp.net/pipermail/slime-devel/2012-February/018470.html -(mapc #'delete-file - (file-expand-wildcards (concat user-emacs-directory "elpa/slime-2*/contrib/*.elc"))) - -(require-package 'hippie-expand-slime) -(maybe-require-package 'slime-company) - - -;;; Lisp buffers - -(defun sanityinc/slime-setup () - "Mode setup function for slime lisp buffers." - (set-up-slime-hippie-expand)) - -(after-load 'slime - (setq slime-protocol-version 'ignore) - (setq slime-net-coding-system 'utf-8-unix) - (let ((extras (when (require 'slime-company nil t) - '(slime-company)))) - (slime-setup (append '(slime-repl slime-fuzzy) extras))) - (setq slime-complete-symbol*-fancy t) - (setq slime-complete-symbol-function 'slime-fuzzy-complete-symbol) - (add-hook 'slime-mode-hook 'sanityinc/slime-setup)) - - -;;; REPL - -(defun sanityinc/slime-repl-setup () - "Mode setup function for slime REPL." - (sanityinc/lisp-setup) - (set-up-slime-hippie-expand) - (setq show-trailing-whitespace nil)) - -(after-load 'slime-repl - ;; Stop SLIME's REPL from grabbing DEL, which is annoying when backspacing over a '(' - (after-load 'paredit - (define-key slime-repl-mode-map (read-kbd-macro paredit-backward-delete-key) nil)) - - ;; Bind TAB to `indent-for-tab-command', as in regular Slime buffers. - (define-key slime-repl-mode-map (kbd "TAB") 'indent-for-tab-command) - - (add-hook 'slime-repl-mode-hook 'sanityinc/slime-repl-setup)) - - -(provide 'init-slime) diff --git a/lisp/init-sly.el b/lisp/init-sly.el new file mode 100644 index 0000000000..f504498c40 --- /dev/null +++ b/lisp/init-sly.el @@ -0,0 +1,36 @@ +;;; init-sly.el --- Sly support for Common Lisp -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + +(require-package 'sly) +(require-package 'sly-asdf) +(require-package 'sly-macrostep) +(require-package 'sly-repl-ansi-color) + + +;;; Lisp buffers + +(with-eval-after-load 'sly + (setq sly-protocol-version 'ignore) + (setq sly-net-coding-system 'utf-8-unix) + (let ((features '(sly-fancy))) + ;; (when (require 'sly-company nil t) + ;; (push 'sly-company features)) + (sly-setup features))) + + +;;; REPL + +(with-eval-after-load 'sly-repl + ;; Stop SLY's REPL from grabbing DEL, which is annoying when backspacing over a '(' + (with-eval-after-load 'paredit + (define-key sly-repl-mode-map (read-kbd-macro paredit-backward-delete-key) nil)) + + ;; Bind TAB to `indent-for-tab-command', as in regular Sly buffers. + (define-key sly-repl-mode-map (kbd "TAB") 'indent-for-tab-command) + + (add-hook 'sly-repl-mode-hook 'sanityinc/lisp-setup)) + + +(provide 'init-sly) +;;; init-sly.el ends here diff --git a/lisp/init-smex.el b/lisp/init-smex.el deleted file mode 100644 index de41b1c288..0000000000 --- a/lisp/init-smex.el +++ /dev/null @@ -1,7 +0,0 @@ -;; Use smex to handle M-x -(when (maybe-require-package 'smex) - ;; Change path for ~/.smex-items - (setq-default smex-save-file (expand-file-name ".smex-items" user-emacs-directory)) - (global-set-key [remap execute-extended-command] 'smex)) - -(provide 'init-smex) diff --git a/lisp/init-spelling.el b/lisp/init-spelling.el index 0c79bd304d..6e11ad0232 100644 --- a/lisp/init-spelling.el +++ b/lisp/init-spelling.el @@ -1,6 +1,16 @@ +;;; init-spelling.el --- Spell check settings -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + (require 'ispell) (when (executable-find ispell-program-name) - (require 'init-flyspell)) + ;; Add spell-checking in comments for all programming language modes + (add-hook 'prog-mode-hook 'flyspell-prog-mode) + + (with-eval-after-load 'flyspell + (define-key flyspell-mode-map (kbd "C-;") nil) + (add-to-list 'flyspell-prog-text-faces 'nxml-text-face))) (provide 'init-spelling) +;;; init-spelling.el ends here diff --git a/lisp/init-sql.el b/lisp/init-sql.el index 9004b2260b..c27b70dbc4 100644 --- a/lisp/init-sql.el +++ b/lisp/init-sql.el @@ -1,15 +1,15 @@ -(require-package 'sql-indent) -(after-load 'sql - (require 'sql-indent)) +;;; init-sql.el --- Support for SQL -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: -(after-load 'sql +(with-eval-after-load 'sql ;; sql-mode pretty much requires your psql to be uncustomised from stock settings - (push "--no-psqlrc" sql-postgres-options)) + (add-to-list 'sql-postgres-options "--no-psqlrc")) (defun sanityinc/pop-to-sqli-buffer () "Switch to the corresponding sqli buffer." (interactive) - (if sql-buffer + (if (and sql-buffer (buffer-live-p sql-buffer)) (progn (pop-to-buffer sql-buffer) (goto-char (point-max))) @@ -17,20 +17,19 @@ (when sql-buffer (sanityinc/pop-to-sqli-buffer)))) -(after-load 'sql +(with-eval-after-load 'sql (define-key sql-mode-map (kbd "C-c C-z") 'sanityinc/pop-to-sqli-buffer) (when (package-installed-p 'dash-at-point) - (defun sanityinc/maybe-set-dash-db-docset () + (defun sanityinc/maybe-set-dash-db-docset (&rest _) (when (eq sql-product 'postgres) - (set (make-local-variable 'dash-at-point-docset) "psql"))) + (setq-local dash-at-point-docset "psql"))) (add-hook 'sql-mode-hook 'sanityinc/maybe-set-dash-db-docset) (add-hook 'sql-interactive-mode-hook 'sanityinc/maybe-set-dash-db-docset) - (defadvice sql-set-product (after set-dash-docset activate) - (sanityinc/maybe-set-dash-db-docset)))) + (advice-add 'sql-set-product :after 'sanityinc/maybe-set-dash-db-docset))) (setq-default sql-input-ring-file-name - (expand-file-name ".sqli_history" user-emacs-directory)) + (locate-user-emacs-file ".sqli_history")) ;; See my answer to https://emacs.stackexchange.com/questions/657/why-do-sql-mode-and-sql-interactive-mode-not-highlight-strings-the-same-way/673 (defun sanityinc/font-lock-everything-in-sql-interactive-mode () @@ -39,7 +38,71 @@ (add-hook 'sql-interactive-mode-hook 'sanityinc/font-lock-everything-in-sql-interactive-mode) -(after-load 'page-break-lines - (push 'sql-mode page-break-lines-modes)) +(require-package 'sqlformat) +(with-eval-after-load 'sql + (define-key sql-mode-map (kbd "C-c C-f") 'sqlformat)) + +;; Package ideas: +;; - PEV +(defun sanityinc/sql-explain-region-as-json (beg end &optional copy) + "Explain the SQL between BEG and END in detailed JSON format. +This is suitable for pasting into tools such as +https://explain.dalibo.com/. + +When the prefix argument COPY is non-nil, do not display the +resulting JSON, but instead copy it to the kill ring. + +If the region is not active, uses the current paragraph, as per +`sql-send-paragraph'. + +Connection information is taken from the special sql-* variables +set in the current buffer, so you will usually want to start a +SQLi session first, or otherwise set `sql-database' etc. + +This command currently blocks the UI, sorry." + (interactive "rP") + (unless (eq sql-product 'postgres) + (user-error "This command is for PostgreSQL only")) + (unless (use-region-p) + (setq beg (save-excursion (backward-paragraph) (point)) + end (save-excursion (forward-paragraph) (point)))) + (let ((query (buffer-substring-no-properties beg end))) + (with-current-buffer (if (sql-buffer-live-p sql-buffer) + sql-buffer + (current-buffer)) + (let* ((process-environment + (append (list (concat "PGDATABASE=" sql-database) + (concat "PGHOST=" sql-server) + (concat "PGUSER=" sql-user)) + process-environment)) + (args (list "--no-psqlrc" + "-qAt" + "-w" ; Never prompt for password + "-E" + "-c" (concat "EXPLAIN (ANALYZE, COSTS, VERBOSE, BUFFERS, FORMAT JSON) " query ";") + )) + (err-file (make-temp-file "sql-explain-json"))) + (with-current-buffer (get-buffer-create "*sql-explain-json*") + (setq buffer-read-only nil) + (delete-region (point-min) (point-max)) + (let ((retcode (apply 'call-process sql-postgres-program nil (list (current-buffer) err-file) nil args))) + (if (zerop retcode) + (progn + (json-mode) + (read-only-mode 1) + (if copy + (progn + (kill-ring-save (buffer-substring-no-properties (point-min) (point-max))) + (message "EXPLAIN output copied to kill-ring.")) + (display-buffer (current-buffer)))) + (with-current-buffer (get-buffer-create "*sql-explain-errors*") + (let ((inhibit-read-only t)) + (insert-file-contents err-file nil nil nil t)) + (display-buffer (current-buffer)) + (user-error "EXPLAIN failed"))))))))) + +(with-eval-after-load 'page-break-lines + (add-to-list 'page-break-lines-modes 'sql-mode)) (provide 'init-sql) +;;; init-sql.el ends here diff --git a/lisp/init-terminals.el b/lisp/init-terminals.el new file mode 100644 index 0000000000..a6829179b9 --- /dev/null +++ b/lisp/init-terminals.el @@ -0,0 +1,32 @@ +;;; init-terminals.el --- Terminal emulators -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(when (maybe-require-package 'eat) + (defun sanityinc/on-eat-exit (process) + (when (zerop (process-exit-status process)) + (kill-buffer) + (unless (eq (selected-window) (next-window)) + (delete-window)))) + (add-hook 'eat-exit-hook 'sanityinc/on-eat-exit) + + (with-eval-after-load 'eat + (custom-set-variables + `(eat-semi-char-non-bound-keys + (quote ,(cons [?\e ?w] (cl-remove [?\e ?w] eat-semi-char-non-bound-keys :test 'equal)))))) + + (defcustom sanityinc/eat-map + (let ((map (make-sparse-keymap))) + (define-key map (kbd "t") 'eat-other-window) + map) + "Prefix map for commands that create and manipulate eat buffers.") + (fset 'sanityinc/eat-map sanityinc/eat-map) + + (global-set-key (kbd "C-c t") 'sanityinc/eat-map)) + + + +(provide 'init-terminals) +;;; init-terminals.el ends here diff --git a/lisp/init-terraform.el b/lisp/init-terraform.el new file mode 100644 index 0000000000..6bc7758058 --- /dev/null +++ b/lisp/init-terraform.el @@ -0,0 +1,17 @@ +;;; init-terraform.el --- Work with Terraform configurations -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + +;;; Terraform + +(when (maybe-require-package 'terraform-mode) + ;; TODO: find/write a replacement for company-terraform + (with-eval-after-load 'terraform-mode + ;; I find formatters based on "reformatter" to be more reliable + ;; so I redefine `terraform-format-on-save-mode' here. + (when (maybe-require-package 'reformatter) + (reformatter-define terraform-format + :program "terraform" :args '("fmt" "-"))))) + +(provide 'init-terraform) +;;; init-terraform.el ends here diff --git a/lisp/init-textile.el b/lisp/init-textile.el index 7146641cad..6e8d7e092c 100644 --- a/lisp/init-textile.el +++ b/lisp/init-textile.el @@ -1,3 +1,7 @@ +;;; init-textile.el --- Edit Textile markup -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + (require-package 'textile-mode) (setq auto-mode-alist @@ -5,3 +9,4 @@ (provide 'init-textile) +;;; init-textile.el ends here diff --git a/lisp/init-themes.el b/lisp/init-themes.el index edbae85740..1ac94c5d8a 100644 --- a/lisp/init-themes.el +++ b/lisp/init-themes.el @@ -1,8 +1,16 @@ +;;; init-themes.el --- Defaults for themes -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + (require-package 'color-theme-sanityinc-solarized) (require-package 'color-theme-sanityinc-tomorrow) +;; Don't prompt to confirm theme safety. This avoids problems with +;; first-time startup on Emacs > 26.3. +(setq custom-safe-themes t) + ;; If you don't customize it, this is the theme you get. -(setq-default custom-enabled-themes '(sanityinc-solarized-light)) +(setq-default custom-enabled-themes '(sanityinc-tomorrow-bright)) ;; Ensure that themes will be applied even if they have not been customized (defun reapply-themes () @@ -15,18 +23,36 @@ (add-hook 'after-init-hook 'reapply-themes) -;;------------------------------------------------------------------------------ + ;; Toggle between light and dark -;;------------------------------------------------------------------------------ + (defun light () "Activate a light color theme." (interactive) - (color-theme-sanityinc-solarized-light)) + (setq custom-enabled-themes '(sanityinc-tomorrow-day)) + (reapply-themes)) (defun dark () "Activate a dark color theme." (interactive) - (color-theme-sanityinc-solarized-dark)) + (setq custom-enabled-themes '(sanityinc-tomorrow-bright)) + (reapply-themes)) + + +(when (maybe-require-package 'dimmer) + (setq-default dimmer-fraction 0.15) + (add-hook 'after-init-hook 'dimmer-mode) + (with-eval-after-load 'dimmer + ;; TODO: file upstream as a PR + (advice-add 'frame-set-background-mode :after (lambda (&rest args) (dimmer-process-all)))) + (with-eval-after-load 'dimmer + ;; Don't dim in terminal windows. Even with 256 colours it can + ;; lead to poor contrast. Better would be to vary dimmer-fraction + ;; according to frame type. + (defun sanityinc/display-non-graphic-p () + (not (display-graphic-p))) + (add-to-list 'dimmer-exclusion-predicates 'sanityinc/display-non-graphic-p))) (provide 'init-themes) +;;; init-themes.el ends here diff --git a/lisp/init-toml.el b/lisp/init-toml.el new file mode 100644 index 0000000000..81212846ba --- /dev/null +++ b/lisp/init-toml.el @@ -0,0 +1,9 @@ +;;; init-toml.el --- Support TOML files -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + +(maybe-require-package 'toml-mode) + + +(provide 'init-toml) +;;; init-toml.el ends here diff --git a/lisp/init-treesitter.el b/lisp/init-treesitter.el new file mode 100644 index 0000000000..04c607a9af --- /dev/null +++ b/lisp/init-treesitter.el @@ -0,0 +1,77 @@ +;;; init-treesitter.el --- Enable Treesitter-based major modes -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + +;; You can download per-architecture pre-compiled release from +;; https://github.com/emacs-tree-sitter/tree-sitter-langs Rename +;; contained grammars to add prefix "libtree-sitter-", place in +;; ~/.emacs.d/tree-sitter. +;; +;; Nix users can pre-install all grammars alongside their Emacs, see +;; https://github.com/nix-community/emacs-overlay/issues/341 +;; +;; Note that grammar files from different sources can be differently +;; named and configured, so there could be different results. Some +;; common remappings are included below. + + +;;; Enable built-in and pre-installed TS modes if the grammars are available + +(defun sanityinc/auto-configure-treesitter () + "Find and configure installed grammars, remap to matching -ts-modes if present. +Return a list of languages seen along the way." + (let ((grammar-name-to-emacs-lang '(("c-sharp" . "csharp") + ("cpp" . "c++") + ("gomod" . "go-mod") + ("javascript" . "js"))) + seen-grammars) + (dolist (dir (cons (expand-file-name "tree-sitter" user-emacs-directory) + treesit-extra-load-path)) + (when (file-directory-p dir) + (dolist (file (directory-files dir)) + (let ((fname (file-name-sans-extension (file-name-nondirectory file)))) + (when (string-match "libtree-sitter-\\(.*\\)" fname) + (let* ((file-lang (match-string 1 fname)) + (emacs-lang (or (cdr (assoc-string file-lang grammar-name-to-emacs-lang)) file-lang))) + ;; Override library if its filename doesn't match the Emacs name + (unless (or (memq (intern emacs-lang) seen-grammars) + (string-equal file-lang emacs-lang)) + (let ((libname (concat "tree_sitter_" (replace-regexp-in-string "-" "_" file-lang)))) + (add-to-list 'treesit-load-name-override-list + (list (intern emacs-lang) fname libname)))) + ;; If there's a corresponding -ts mode, remap the standard mode to it + (let ((ts-mode-name (intern (concat emacs-lang "-ts-mode"))) + (regular-mode-name (intern (concat emacs-lang "-mode")))) + (when (fboundp ts-mode-name) + (message "init-treesitter: using %s in place of %s" ts-mode-name regular-mode-name) + (add-to-list 'major-mode-remap-alist + (cons regular-mode-name ts-mode-name)))) + ;; Remember we saw this language so we don't squash its config when we + ;; find another lib later in the treesit load path + (push (intern emacs-lang) seen-grammars))))))) + seen-grammars)) + +(sanityinc/auto-configure-treesitter) + + +;;; Support remapping of additional libraries + +(defun sanityinc/remap-ts-mode (non-ts-mode ts-mode grammar) + "Explicitly remap NON-TS-MODE to TS-MODE if GRAMMAR is available." + (when (and (fboundp 'treesit-ready-p) + (treesit-ready-p grammar t) + (fboundp ts-mode)) + (add-to-list 'major-mode-remap-alist (cons non-ts-mode ts-mode)))) + +;; When there's js-ts-mode, we also prefer it to js2-mode +(sanityinc/remap-ts-mode 'js2-mode 'js-ts-mode 'javascript) +(sanityinc/remap-ts-mode 'clojurescript-mode 'clojurescript-ts-mode 'clojure) + + +;; Default +(setq treesit-font-lock-level 4) + + + +(provide 'init-treesitter) +;;; init-treesitter.el ends here diff --git a/lisp/init-uiua.el b/lisp/init-uiua.el new file mode 100644 index 0000000000..7181e13430 --- /dev/null +++ b/lisp/init-uiua.el @@ -0,0 +1,14 @@ +;;; init-uiua.el --- Support for the Uiua programming language -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + +(or (maybe-require-package 'uiua-ts-mode) + (maybe-require-package 'uiua-mode)) + +(with-eval-after-load 'eglot + (add-to-list 'eglot-server-programs '((uiua-mode uiua-ts-mode) . ("uiua" "lsp")))) + +(maybe-require-package 'nixpkgs-fmt) + +(provide 'init-uiua) +;;; init-uiua.el ends here diff --git a/lisp/init-uniquify.el b/lisp/init-uniquify.el index ba3c6b5b0a..eb528e054a 100644 --- a/lisp/init-uniquify.el +++ b/lisp/init-uniquify.el @@ -1,6 +1,8 @@ -;;---------------------------------------------------------------------------- +;;; init-uniquify.el --- Configure uniquification of buffer names -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + ;; Nicer naming of buffers for files with identical names -;;---------------------------------------------------------------------------- (require 'uniquify) (setq uniquify-buffer-name-style 'reverse) @@ -9,4 +11,5 @@ (setq uniquify-ignore-buffers-re "^\\*") -(provide 'init-uniquify) \ No newline at end of file +(provide 'init-uniquify) +;;; init-uniquify.el ends here diff --git a/lisp/init-utils.el b/lisp/init-utils.el index 0bae031364..e5cb5cb839 100644 --- a/lisp/init-utils.el +++ b/lisp/init-utils.el @@ -1,24 +1,59 @@ -(if (fboundp 'with-eval-after-load) - (defalias 'after-load 'with-eval-after-load) - (defmacro after-load (feature &rest body) - "After FEATURE is loaded, evaluate BODY." - (declare (indent defun)) - `(eval-after-load ,feature - '(progn ,@body)))) +;;; init-utils.el --- Elisp helper functions and commands -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: +(defun sanityinc/display-buffer-full-frame (buffer alist) + "If it's not visible, display buffer full-frame, saving the prior window config. +The saved config will be restored when the window is quit later. +BUFFER and ALIST are as for `display-buffer-full-frame'." + (let ((initial-window-configuration (current-window-configuration))) + (or (display-buffer-reuse-window buffer alist) + (let ((full-window (display-buffer-full-frame buffer alist))) + (prog1 + full-window + (set-window-parameter full-window 'sanityinc/previous-config initial-window-configuration)))))) -;;---------------------------------------------------------------------------- +(defun sanityinc/maybe-restore-window-configuration (orig &optional kill window) + (let* ((window (or window (selected-window))) + (to-restore (window-parameter window 'sanityinc/previous-config))) + (set-window-parameter window 'sanityinc/previous-config nil) + (funcall orig kill window) + (when to-restore + (set-window-configuration to-restore)))) + +(advice-add 'quit-window :around 'sanityinc/maybe-restore-window-configuration) + +(defmacro sanityinc/fullframe-mode (mode) + "Configure buffers that open in MODE to display in full-frame." + `(add-to-list 'display-buffer-alist + (cons (cons 'major-mode ,mode) + (list 'sanityinc/display-buffer-full-frame)))) + +(sanityinc/fullframe-mode 'package-menu-mode) + + ;; Handier way to add modes to auto-mode-alist -;;---------------------------------------------------------------------------- (defun add-auto-mode (mode &rest patterns) "Add entries to `auto-mode-alist' to use `MODE' for all given file `PATTERNS'." (dolist (pattern patterns) (add-to-list 'auto-mode-alist (cons pattern mode)))) +(defun sanityinc/remove-auto-mode (mode) + "Remove entries from `auto-mode-alist' that are for `MODE'." + (setq auto-mode-alist (seq-remove (lambda (x) (eq mode (cdr x))) auto-mode-alist))) -;;---------------------------------------------------------------------------- +;; Like diminish, but for major modes +(defun sanityinc/set-major-mode-name (name) + "Override the major mode NAME in this buffer." + (setq-local mode-name name)) + +(defun sanityinc/major-mode-lighter (mode name) + (add-hook (derived-mode-hook-name mode) + (apply-partially 'sanityinc/set-major-mode-name name))) + + ;; String utilities missing from core emacs -;;---------------------------------------------------------------------------- + (defun sanityinc/string-all-matches (regex str &optional group) "Find all matches for `REGEX' within `STR', returning the full match string or group `GROUP'." (let ((result nil) @@ -29,52 +64,42 @@ (setq pos (match-end group))) result)) -(defun sanityinc/string-rtrim (str) - "Remove trailing whitespace from `STR'." - (replace-regexp-in-string "[ \t\n]+$" "" str)) - -;;---------------------------------------------------------------------------- -;; Find the directory containing a given library -;;---------------------------------------------------------------------------- -(autoload 'find-library-name "find-func") -(defun sanityinc/directory-of-library (library-name) - "Return the directory in which the `LIBRARY-NAME' load file is found." - (file-name-as-directory (file-name-directory (find-library-name library-name)))) - - -;;---------------------------------------------------------------------------- + ;; Delete the current file -;;---------------------------------------------------------------------------- + (defun delete-this-file () "Delete the current file, and kill the buffer." (interactive) - (or (buffer-file-name) (error "No file is currently being edited")) + (unless (buffer-file-name) + (error "No file is currently being edited")) (when (yes-or-no-p (format "Really delete '%s'?" (file-name-nondirectory buffer-file-name))) (delete-file (buffer-file-name)) (kill-this-buffer))) -;;---------------------------------------------------------------------------- + ;; Rename the current file -;;---------------------------------------------------------------------------- -(defun rename-this-file-and-buffer (new-name) - "Renames both current buffer and file it's visiting to NEW-NAME." - (interactive "sNew name: ") - (let ((name (buffer-name)) - (filename (buffer-file-name))) - (unless filename - (error "Buffer '%s' is not visiting a file!" name)) - (progn - (when (file-exists-p filename) - (rename-file filename new-name 1)) - (set-visited-file-name new-name) - (rename-buffer new-name)))) - -;;---------------------------------------------------------------------------- + +(if (fboundp 'rename-visited-file) + (defalias 'rename-this-file-and-buffer 'rename-visited-file) + (defun rename-this-file-and-buffer (new-name) + "Renames both current buffer and file it's visiting to NEW-NAME." + (interactive "sNew name: ") + (let ((name (buffer-name)) + (filename (buffer-file-name))) + (unless filename + (error "Buffer '%s' is not visiting a file!" name)) + (progn + (when (file-exists-p filename) + (rename-file filename new-name 1)) + (set-visited-file-name new-name) + (rename-buffer new-name))))) + + ;; Browse current HTML file -;;---------------------------------------------------------------------------- + (defun browse-current-file () "Open the current file as a URL using `browse-url'." (interactive) @@ -86,3 +111,4 @@ (provide 'init-utils) +;;; init-utils.el ends here diff --git a/lisp/init-vc.el b/lisp/init-vc.el index 1e0ec3f545..7417d8cb98 100644 --- a/lisp/init-vc.el +++ b/lisp/init-vc.el @@ -1,7 +1,19 @@ -(require-package 'diff-hl) -(add-hook 'prog-mode-hook 'turn-on-diff-hl-mode) -(add-hook 'vc-dir-mode-hook 'turn-on-diff-hl-mode) +;;; init-vc.el --- Version control support -*- lexical-binding: t -*- +;;; Commentary: -(maybe-require-package 'browse-at-remote) +;; Most version control packages are configured separately: see +;; init-git.el, for example. + +;;; Code: + +(when (maybe-require-package 'diff-hl) + (add-hook 'magit-post-refresh-hook 'diff-hl-magit-post-refresh) + (add-hook 'after-init-hook 'global-diff-hl-mode) + + (with-eval-after-load 'diff-hl + (define-key diff-hl-mode-map (kbd " ") 'diff-hl-diff-goto-hunk) + (define-key diff-hl-mode-map (kbd "M-C-]") 'diff-hl-next-hunk) + (define-key diff-hl-mode-map (kbd "M-C-[") 'diff-hl-previous-hunk))) (provide 'init-vc) +;;; init-vc.el ends here diff --git a/lisp/init-whitespace.el b/lisp/init-whitespace.el index d0df5c5251..b208215500 100644 --- a/lisp/init-whitespace.el +++ b/lisp/init-whitespace.el @@ -1,28 +1,27 @@ -(setq-default show-trailing-whitespace t) +;;; init-whitespace.el --- Special handling for whitespace -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + +(setq-default show-trailing-whitespace nil) ;;; Whitespace -(defun sanityinc/no-trailing-whitespace () - "Turn off display of trailing whitespace in this buffer." - (setq show-trailing-whitespace nil)) +(defun sanityinc/show-trailing-whitespace () + "Enable display of trailing whitespace in this buffer." + (setq-local show-trailing-whitespace t)) -;; But don't show trailing whitespace in SQLi, inf-ruby etc. -(dolist (hook '(special-mode-hook - Info-mode-hook - eww-mode-hook - term-mode-hook - comint-mode-hook - compilation-mode-hook - twittering-mode-hook - minibuffer-setup-hook)) - (add-hook hook #'sanityinc/no-trailing-whitespace)) +(dolist (hook '(prog-mode-hook text-mode-hook conf-mode-hook)) + (add-hook hook 'sanityinc/show-trailing-whitespace)) (require-package 'whitespace-cleanup-mode) -(global-whitespace-cleanup-mode t) +(add-hook 'after-init-hook 'global-whitespace-cleanup-mode) +(with-eval-after-load 'whitespace-cleanup-mode + (diminish 'whitespace-cleanup-mode)) (global-set-key [remap just-one-space] 'cycle-spacing) (provide 'init-whitespace) +;;; init-whitespace.el ends here diff --git a/lisp/init-windows.el b/lisp/init-windows.el index c5bffc0eb2..47e8e299b0 100644 --- a/lisp/init-windows.el +++ b/lisp/init-windows.el @@ -1,31 +1,36 @@ -;;---------------------------------------------------------------------------- +;;; init-windows.el --- Working with windows within frames -*- lexical-binding: t -*- +;;; Commentary: + +;; This is not about the "Windows" OS, but rather Emacs's "windows" +;; concept: these are the panels within an Emacs frame which contain +;; buffers. + +;;; Code: + ;; Navigate window layouts with "C-c " and "C-c " -;;---------------------------------------------------------------------------- -(winner-mode 1) +(add-hook 'after-init-hook 'winner-mode) ;; Make "C-x o" prompt for a target window when there are more than 2 (require-package 'switch-window) -(require 'switch-window) -(setq-default switch-window-shortcut-style 'alphabet) +(setq-default switch-window-shortcut-style 'qwerty) (setq-default switch-window-timeout nil) (global-set-key (kbd "C-x o") 'switch-window) -;;---------------------------------------------------------------------------- + ;; When splitting window, show (other-buffer) in the new window -;;---------------------------------------------------------------------------- + (defun split-window-func-with-other-buffer (split-function) - (lexical-let ((s-f split-function)) - (lambda (&optional arg) - "Split this window and switch to the new window unless ARG is provided." - (interactive "P") - (funcall s-f) - (let ((target-window (next-window))) - (set-window-buffer target-window (other-buffer)) - (unless arg - (select-window target-window)))))) + (lambda (&optional arg) + "Split this window and switch to the new window unless ARG is provided." + (interactive "P") + (funcall split-function) + (let ((target-window (next-window))) + (set-window-buffer target-window (other-buffer)) + (unless arg + (select-window target-window))))) (global-set-key (kbd "C-x 2") (split-window-func-with-other-buffer 'split-window-vertically)) (global-set-key (kbd "C-x 3") (split-window-func-with-other-buffer 'split-window-horizontally)) @@ -40,26 +45,33 @@ (global-set-key (kbd "C-x 1") 'sanityinc/toggle-delete-other-windows) -;;---------------------------------------------------------------------------- + ;; Rearrange split windows -;;---------------------------------------------------------------------------- + (defun split-window-horizontally-instead () + "Kill any other windows and re-split such that the current window is on the top half of the frame." (interactive) - (save-excursion + (let ((other-buffer (and (next-window) (window-buffer (next-window))))) (delete-other-windows) - (funcall (split-window-func-with-other-buffer 'split-window-horizontally)))) + (split-window-horizontally) + (when other-buffer + (set-window-buffer (next-window) other-buffer)))) (defun split-window-vertically-instead () + "Kill any other windows and re-split such that the current window is on the left half of the frame." (interactive) - (save-excursion + (let ((other-buffer (and (next-window) (window-buffer (next-window))))) (delete-other-windows) - (funcall (split-window-func-with-other-buffer 'split-window-vertically)))) + (split-window-vertically) + (when other-buffer + (set-window-buffer (next-window) other-buffer)))) (global-set-key (kbd "C-x |") 'split-window-horizontally-instead) (global-set-key (kbd "C-x _") 'split-window-vertically-instead) ;; Borrowed from http://postmomentum.ch/blog/201304/blog-on-emacs + (defun sanityinc/split-window() "Split the window to see the most recent buffer in the other window. Call a second time to restore the original window configuration." @@ -75,6 +87,7 @@ Call a second time to restore the original window configuration." + (defun sanityinc/toggle-current-window-dedication () "Toggle whether the current window is dedicated to its current buffer." (interactive) @@ -89,8 +102,12 @@ Call a second time to restore the original window configuration." + (unless (memq window-system '(nt w32)) - (windmove-default-keybindings 'control)) + (require-package 'windswap) + (add-hook 'after-init-hook (apply-partially 'windmove-default-keybindings 'control)) + (add-hook 'after-init-hook (apply-partially 'windswap-default-keybindings 'shift 'control))) (provide 'init-windows) +;;; init-windows.el ends here diff --git a/lisp/init-xterm.el b/lisp/init-xterm.el index d8ed6c7e0a..1b2f7ce943 100644 --- a/lisp/init-xterm.el +++ b/lisp/init-xterm.el @@ -1,30 +1,21 @@ -(require 'init-frame-hooks) +;;; init-xterm.el --- Integrate with terminals such as xterm -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: -(defun fix-up-xterm-control-arrows () - (let ((map (if (boundp 'input-decode-map) - input-decode-map - function-key-map))) - (define-key map "\e[1;5A" [C-up]) - (define-key map "\e[1;5B" [C-down]) - (define-key map "\e[1;5C" [C-right]) - (define-key map "\e[1;5D" [C-left]) - (define-key map "\e[5A" [C-up]) - (define-key map "\e[5B" [C-down]) - (define-key map "\e[5C" [C-right]) - (define-key map "\e[5D" [C-left]))) +(require 'init-frame-hooks) (global-set-key [mouse-4] (lambda () (interactive) (scroll-down 1))) (global-set-key [mouse-5] (lambda () (interactive) (scroll-up 1))) +(autoload 'mwheel-install "mwheel") + (defun sanityinc/console-frame-setup () - (when (< emacs-major-version 23) - (fix-up-xterm-control-arrows)) (xterm-mouse-mode 1) ; Mouse in a terminal (Use shift to paste with middle button) - (when (fboundp 'mwheel-install) - (mwheel-install))) + (mwheel-install)) (add-hook 'after-make-console-frame-hooks 'sanityinc/console-frame-setup) (provide 'init-xterm) +;;; init-xterm.el ends here diff --git a/lisp/init-yaml.el b/lisp/init-yaml.el new file mode 100644 index 0000000000..4a9d1697d3 --- /dev/null +++ b/lisp/init-yaml.el @@ -0,0 +1,11 @@ +;;; init-yaml.el --- Support Yaml files -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + +(when (maybe-require-package 'yaml-mode) + (add-auto-mode 'yaml-mode "\\.yml\\.erb\\'") + (add-hook 'yaml-mode-hook 'goto-address-prog-mode)) + + +(provide 'init-yaml) +;;; init-yaml.el ends here diff --git a/lisp/init-zig.el b/lisp/init-zig.el new file mode 100644 index 0000000000..0fa4af72ab --- /dev/null +++ b/lisp/init-zig.el @@ -0,0 +1,15 @@ +;;; init-zig.el --- Support for the Zig language -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: + +(if (and (maybe-require-package 'zig-ts-mode) + (fboundp 'treesit-ready-p) (treesit-ready-p 'zig)) + (progn + (add-to-list 'auto-mode-alist '("\\.\\(zig\\|zon\\)\\'" . zig-ts-mode)) + (with-eval-after-load 'eglot + (add-to-list 'eglot-server-programs '(zig-ts-mode . ("zls"))))) + (require-package 'zig-mode)) + + +(provide 'init-zig) +;;; init-zig.el ends here diff --git a/site-lisp/README b/site-lisp/README index 41662772c5..4f84600e2d 100644 --- a/site-lisp/README +++ b/site-lisp/README @@ -1,6 +1,3 @@ This is a place for 3rd party code which isn't available in MELPA or other package repositories. This directory and its immediate subdirectories will be added to load-path at start-up time. - -Notably, in Emacs 23.x, a backported package.el is automatically -downloaded and installed here. \ No newline at end of file diff --git a/test-startup.sh b/test-startup.sh index 13351c2d12..6c55d91585 100755 --- a/test-startup.sh +++ b/test-startup.sh @@ -1,17 +1,14 @@ #!/bin/sh -e -if [ -n "$TRAVIS" ]; then - # Make it look like this is ~/.emacs.d (needed for Emacs 24.3, at least) - export HOME=$PWD/.. - ln -s emacs.d ../.emacs.d -fi echo "Attempting startup..." ${EMACS:=emacs} -nw --batch \ - --eval '(let ((debug-on-error t) + --eval '(progn + (defvar url-show-status) + (let ((debug-on-error t) (url-show-status nil) (user-emacs-directory default-directory) - (package-user-dir (expand-file-name (concat "elpa-" emacs-version))) (user-init-file (expand-file-name "init.el")) (load-path (delq default-directory load-path))) + (setq package-check-signature nil) (load-file user-init-file) - (run-hooks (quote after-init-hook)))' + (run-hooks (quote after-init-hook))))' echo "Startup successful"