From 62c90fb2c5e2c8968eee10ce512ff6672abe9d17 Mon Sep 17 00:00:00 2001
From: Jens Nolte <git@queezle.net>
Date: Fri, 18 Feb 2022 18:29:53 +0100
Subject: [PATCH] Add emacs configuration

---
 flake.lock                |  33 ++++
 modules/default.nix       |   1 +
 modules/emacs/default.nix | 351 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 385 insertions(+)
 create mode 100644 modules/emacs/default.nix

diff --git a/flake.lock b/flake.lock
index d217c26..6ca17b8 100644
--- a/flake.lock
+++ b/flake.lock
@@ -1,5 +1,36 @@
 {
   "nodes": {
+    "emacs-overlay": {
+      "locked": {
+        "lastModified": 1643481488,
+        "narHash": "sha256-6WqxMVVbwvFKuqOz+SEeNLtCfRM1NOWd0imBKe2onxs=",
+        "owner": "nix-community",
+        "repo": "emacs-overlay",
+        "rev": "db06e18bcea9fa0cd93067dab0648b8280637304",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nix-community",
+        "repo": "emacs-overlay",
+        "type": "github"
+      }
+    },
+    "emacs-term-cursor": {
+      "flake": false,
+      "locked": {
+        "lastModified": 1557873673,
+        "narHash": "sha256-pob9XWj1QSDyDGM7EuY75oHBPv1MRYZCa7WTfyRBAko=",
+        "owner": "denrat",
+        "repo": "term-cursor.el",
+        "rev": "d6c9b46c6ad73875db4ce04cac335846f86fb7e7",
+        "type": "github"
+      },
+      "original": {
+        "owner": "denrat",
+        "repo": "term-cursor.el",
+        "type": "github"
+      }
+    },
     "homemanager": {
       "inputs": {
         "nixpkgs": [
@@ -171,6 +202,8 @@
     },
     "root": {
       "inputs": {
+        "emacs-overlay": "emacs-overlay",
+        "emacs-term-cursor": "emacs-term-cursor",
         "homemanager": "homemanager",
         "matrix-homeserver": "matrix-homeserver",
         "mobile-nixos": "mobile-nixos",
diff --git a/modules/default.nix b/modules/default.nix
index 78b9ce9..197f8c3 100644
--- a/modules/default.nix
+++ b/modules/default.nix
@@ -4,6 +4,7 @@
   imports = [
     ./desktop
     ./desktop/launcher.nix
+    ./emacs
     ./sway
     ./project-manager
     ./dotfiles.nix
diff --git a/modules/emacs/default.nix b/modules/emacs/default.nix
new file mode 100644
index 0000000..e7ee880
--- /dev/null
+++ b/modules/emacs/default.nix
@@ -0,0 +1,351 @@
+{ config, lib, pkgs, flakeInputs, ... }:
+with lib;
+
+let
+  cfg = config.queezle.emacs;
+
+  customHunspell = pkgs.hunspellWithDicts [
+    pkgs.hunspellDicts.de_DE
+    pkgs.hunspellDicts.en_US-large
+  ];
+
+  extraEmacsPackages = final: prev: {
+    emacsPackagesFor = emacs: (prev.emacsPackagesFor emacs).overrideScope' (
+      efinal: eprev: {
+        term-cursor = efinal.trivialBuild ({
+          pname = "term-cursor";
+          src = flakeInputs.emacs-term-cursor;
+        });
+
+        tsc-dyn = pkgs.callPackage (
+          { lib , rustPlatform , llvmPackages }:
+          rustPlatform.buildRustPackage {
+            version = efinal.tsc.version;
+            src = efinal.tsc.src;
+        
+            pname = "tsc-dyn";
+            commit = efinal.tsc.version;
+        
+            nativeBuildInputs = [ llvmPackages.clang ];
+            sourceRoot = "source/core";
+        
+            configurePhase = ''
+              export LIBCLANG_PATH="${llvmPackages.libclang.lib}/lib"
+            '';
+        
+            postInstall = ''
+              LIB=($out/lib/libtsc_dyn.*)
+              TSC_PATH=$out/share/emacs/site-lisp/elpa/tsc-${version}
+              install -d $TSC_PATH
+              install -m444 $out/lib/libtsc_dyn.* $TSC_PATH/''${LIB/*libtsc_/tsc-}
+              echo -n $version > $TSC_PATH/DYN-VERSION
+              rm -r $out/lib
+            '';
+        
+            cargoSha256 = "sha256-JHBIOVNRjOpFcUSzLlMrO4dmOdAY9RecglXrF9c3tRg=";    
+        }) {};
+      }
+    );
+  };
+
+  emacsWithPackages = (pkgs.emacsPackagesFor pkgs.emacsPgtkGcc).withPackages;
+
+  emacs-queezle = emacsWithPackages (epkgs: [(config-queezle epkgs)]);
+
+  config-queezle = epkgs: (epkgs.trivialBuild ({
+    pname = "config-queezle";
+    src = defaultFile;
+    packageRequires = [
+      epkgs.term-cursor
+      #epkgs.tsc-dyn
+      #pkgs.notmuch   # From main packages set
+    ] ++
+    (with epkgs.melpaStablePackages; [
+      magit
+      helm-projectile
+      which-key
+      smart-mode-line
+    ]) ++ (with epkgs.melpaPackages; [
+      solaire-mode
+      doom-themes
+      gruvbox-theme
+      evil
+      evil-collection
+      evil-surround
+      evil-commentary
+      evil-easymotion
+      evil-visualstar
+      helm
+      projectile
+
+      treemacs
+      treemacs-evil
+      treemacs-projectile
+      treemacs-magit
+
+      flycheck
+      lsp-mode
+      lsp-ui
+      company
+
+      #epkgs.tree-sitter
+      # Build system is a mess
+      #epkgs.tree-sitter-langs
+
+      lsp-haskell
+      nix-mode
+      #zoom-frm       # ; increase/decrease font size for all buffers %lt;C-x C-+>
+    ]) ++ (with epkgs.elpaPackages; [
+      # <C-x u> to show the undo tree
+      undo-tree
+      #auctex         # ; LaTeX mode
+      #beacon         # ; highlight my cursor when scrolling
+      #nameless       # ; hide current package name everywhere in elisp code
+    ]);
+  }));
+
+
+
+
+  defaultFile = pkgs.writeText "default.el" init;
+  initFile = pkgs.writeText "init.el" init;
+
+  early-init = pkgs.writeText "early-init.el" ''
+    ;;; early-init: loaded before the package system and gui are initialized
+
+    ;; Makes impure packages archives unavailable
+    (setq package-archives nil)
+
+    ;; Turn off UI elements before the window is shown
+    (menu-bar-mode -1)
+    (tool-bar-mode -1)
+    (scroll-bar-mode -1)
+
+    ;; Font size (in pt*10)
+    (set-face-attribute 'default nil :height 105)
+  '';
+
+  init = ''
+    ;;; general settings
+
+    ;; Inhibit startup screen (ignored by emacs if put directly in default.el)
+    (add-hook 'after-init-hook (lambda () (setq inhibit-startup-screen t)))
+
+    ;; Inhibit startup message. Emacs _really_ wants you to personalize this variable by putting in your username manually. This is not practical for me since I use the same config across multiple users (personal, work, development sandboxes).
+    (put 'inhibit-startup-echo-area-message 'saved-value
+      (setq inhibit-startup-echo-area-message (user-login-name)))
+
+    ;; Show init time on startup
+    (add-hook 'emacs-startup-hook (lambda () (message "Initialized in %s" (emacs-init-time))))
+
+    (setq mouse-wheel-progressive-speed nil)
+    (pixel-scroll-precision-mode)
+
+    (xterm-mouse-mode)
+
+    ;; 100mb GC threshold since lsp generates a lot of garbage
+    (setq gc-cons-threshold 100000000)
+
+    ;; Read 1Mb from processes since some lsp responses are multiple megabytes large
+    (setq read-process-output-max (* 1024 1024))
+
+    ;; Don't write customizations to `.emacs`
+    (setq custom-file "~/.emacs.d/custom.el")
+
+    ;; Backup files
+    (setq backup-path "~/.emacs.d/backup")
+    (if
+      (not (file-exists-p (directory-file-name backup-path)))
+      (make-directory (directory-file-name backup-path))
+    )
+
+    (setq
+      backup-directory-alist `((".*" . ,(directory-file-name backup-path)))
+      auto-save-file-name-transforms `((".*" ,(directory-file-name backup-path) t))
+      auto-save-list-file-prefix (directory-file-name backup-path)
+    )
+
+
+    (global-display-line-numbers-mode)
+    (column-number-mode)
+
+    (setq-default show-trailing-whitespace t)
+
+    (global-hl-line-mode)
+
+
+    ;;; evil/editing
+
+    ;; has to be initialized early so evil isn't loaded as a dependency before setting up variables
+
+    (setq
+      evil-want-keybinding nil ; handled by evil-collection
+      evil-want-C-u-scroll t
+      evil-undo-system 'undo-tree
+      evil-want-C-u-delete t
+      evil-collection-setup-minibuffer t
+    )
+
+    (evil-collection-init)
+    (evil-mode 1)
+
+    (evil-set-leader 'normal (kbd "SPC"))
+
+    ;; C-SPC (set-mark) is not required with evil and could be reused
+    ;(keymap-global-unset "C-SPC")
+
+    (global-evil-surround-mode 1)
+    (evil-commentary-mode)
+    (global-evil-visualstar-mode)
+
+    (keymap-set evil-normal-state-map "C-s" #'save-buffer)
+
+    (global-undo-tree-mode)
+
+    ;; easymotion
+    (evilem-default-keybindings "<leader> SPC")
+
+    ;; easymotion change word-based commands to seek across lines
+    (with-eval-after-load 'evil-easymotion
+      (eval-when-compile (require 'evil-easymotion))
+      (evilem-make-motion evilem-motion-forward-word-begin #'evil-forward-word-begin)
+      (evilem-make-motion evilem-motion-forward-WORD-begin #'evil-forward-WORD-begin)
+      (evilem-make-motion evilem-motion-forward-word-end #'evil-forward-word-end)
+      (evilem-make-motion evilem-motion-forward-WORD-end #'evil-forward-WORD-end)
+      (evilem-make-motion evilem-motion-backward-word-begin #'evil-backward-word-begin)
+      (evilem-make-motion evilem-motion-backward-WORD-begin #'evil-backward-WORD-begin)
+      (evilem-make-motion evilem-motion-backward-word-end #'evil-backward-word-end)
+      (evilem-make-motion evilem-motion-backward-WORD-end #'evil-backward-WORD-end))
+
+
+    ;; TODO Move C-p functionality to C-b
+    ;; TODO Maybe use C-a and C-y (or C-ö/C-ä) for evil-numbers
+    ;; in/out across files
+
+    ;;; indentation
+
+    (setq-default
+      indent-tabs-mode nil
+      tab-width 2
+      evil-shift-width 2
+    )
+
+
+    ;;; theme
+
+    ;; Darker background for utility buffers
+    (solaire-global-mode +1)
+
+    (setq doom-themes-enable-bold t
+          doom-themes-enable-italic t)
+    (load-theme 'doom-gruvbox t)
+
+    ;; smart-mode-line + don't ask when loading themes
+    (setq
+      sml/no-confirm-load-theme t
+      sml/name-width '(1 . 44))
+    (sml/setup)
+
+
+    ;;; terminal
+
+    (require 'term-cursor)
+    ;; breaks lsp-ui
+    ;(global-term-cursor-mode)
+
+    ;; foot is an xterm-compatible terminal
+    ;; 24bit-colors require COLORTERM=truecolor (not sent by SSH by default)
+    (add-to-list 'term-file-aliases '("foot" . "xterm"))
+
+
+    ;;; ide
+
+    (global-flycheck-mode)
+    (setq lsp-keymap-prefix "<leader> l")
+    (setq lsp-ui-sideline-show-code-actions t)
+    (setq lsp-haskell-plugin-import-lens-code-lens-on nil)
+    (add-hook 'haskell-mode-hook #'lsp)
+    (keymap-global-set "<leader> h" #'lsp-ui-doc-show)
+
+
+    ;;; helm
+
+    (setq helm-command-prefix-key "<leader> c")
+    (keymap-global-set "M-x" #'helm-M-x)
+    (keymap-global-set "C-x r b" #'helm-filtered-bookmarks)
+    (keymap-global-set "C-x C-f" #'helm-find-files)
+    (helm-mode 1)
+
+
+    ;;; project management
+
+    (require 'projectile)
+    (keymap-set projectile-mode-map "<leader> p" #'projectile-command-map)
+    (projectile-mode +1)
+
+    (keymap-unset evil-normal-state-map "C-p" t)
+    (keymap-set projectile-mode-map "C-p" #'helm-projectile)
+
+
+    ;;; treemacs
+
+    (setq treemacs-no-png-images t)
+    (require 'treemacs-evil)
+
+    (set-face-attribute 'treemacs-root-face nil :height 1.0)
+
+    (keymap-global-set "<leader> t" #'treemacs)
+
+
+    ;;; which-key
+
+    (setq which-key-idle-delay 0.8
+      which-key-idle-secondary-delay 0.05)
+    (which-key-mode)
+    (with-eval-after-load 'lsp-mode
+      (add-hook 'lsp-mode-hook #'lsp-enable-which-key-integration))
+  '';
+
+  #tree-sitter-init = ''
+  #  ;;; tree-sitter
+
+  #  ;; Use nix-provided tree-sitter
+  #  (setq tsc-dyn-get-from nil)
+
+  #  (setq tree-sitter-load-path '("${tree-sitter-grammars}"))
+  #'';
+
+  tree-sitter-grammars = pkgs.runCommand "tree-sitter-grammars" {} ''
+      mkdir -p $out/bin
+      ${lib.concatStringsSep "\n" (lib.mapAttrsToList (name: src: "name=${name}; ln -s ${src}/parser $out/bin/\${name#tree-sitter-}.so") pkgs.tree-sitter.builtGrammars)};
+    '';
+
+in {
+  options.queezle.emacs = {
+    enable = mkEnableOption "queezles emacs configuration";
+
+    user = mkOption {
+      type = types.str;
+      default = "jens";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    nixpkgs.overlays = [ extraEmacsPackages ];
+
+    environment.systemPackages = [
+      emacs-queezle
+      customHunspell
+      pkgs.tree-sitter
+    ];
+
+    home-manager.users."${cfg.user}".home.file = {
+      ".emacs.d/early-init.el" = {
+        source = early-init;
+      };
+      #".emacs.d/init.el" = {
+      #  source = initFile;
+      #};
+    };
+  };
+}
-- 
GitLab